@cortexkit/pi-magic-context 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-ekzg3w5y.js → index-xbk9epe8.js} +1029 -164
- package/dist/index.js +626 -701
- package/dist/subagent-entry.js +6 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
buildCompartmentSummaryFallbackText,
|
|
41
41
|
buildPrimaryLanguageDirective,
|
|
42
42
|
buildSyntheticTodoPart,
|
|
43
|
+
buildToolArcs,
|
|
44
|
+
buildTrueRawTokenIndex,
|
|
43
45
|
bumpEpochsForWorkspaceMembers,
|
|
44
46
|
bumpProjectUserProfileVersion,
|
|
45
47
|
byteSize,
|
|
@@ -67,8 +69,10 @@ import {
|
|
|
67
69
|
clearPersistedTodoSyntheticAnchor,
|
|
68
70
|
clearRecompStaging,
|
|
69
71
|
closeQuietly,
|
|
72
|
+
completedToolArcCrossesBoundary,
|
|
70
73
|
computeCueContentHash,
|
|
71
74
|
computeNormalizedHash,
|
|
75
|
+
computeRawRangeFingerprint,
|
|
72
76
|
computeWorkspaceEpochFingerprint,
|
|
73
77
|
contentSha256,
|
|
74
78
|
convertEntriesToRawMessages,
|
|
@@ -76,6 +80,8 @@ import {
|
|
|
76
80
|
cosineSimilarity,
|
|
77
81
|
countPrimerCandidatesForProject,
|
|
78
82
|
createPrimer,
|
|
83
|
+
createPromptSurfaceGuidanceEpochCache,
|
|
84
|
+
createPromptSurfaceRuntime,
|
|
79
85
|
decodeCachedM0UpgradeIdentity,
|
|
80
86
|
deleteMemory,
|
|
81
87
|
deleteUserMemoryCandidates,
|
|
@@ -98,10 +104,12 @@ import {
|
|
|
98
104
|
escapeXmlAttr,
|
|
99
105
|
escapeXmlContent,
|
|
100
106
|
estimateTokens,
|
|
107
|
+
estimateTrueRawMessageTokens,
|
|
101
108
|
expandWorkspaceIdentitySetWithAliases,
|
|
102
109
|
extractM0Block,
|
|
103
110
|
extractTexts,
|
|
104
111
|
extractToolCallSummaries,
|
|
112
|
+
fenceBoundaryForToolArcs,
|
|
105
113
|
findAdoptableFallbackTags,
|
|
106
114
|
findLastModelKeyFromBranch,
|
|
107
115
|
findPiFallbackToolOwnerTags,
|
|
@@ -213,6 +221,7 @@ import {
|
|
|
213
221
|
insertUserMemory,
|
|
214
222
|
insertUserMemoryCandidates,
|
|
215
223
|
invalidateMemory,
|
|
224
|
+
invalidateTrueRawTokenCache,
|
|
216
225
|
isCompactionEnabled,
|
|
217
226
|
isCompartmentLeaseHeld,
|
|
218
227
|
isDreamerRunnable,
|
|
@@ -253,6 +262,7 @@ import {
|
|
|
253
262
|
primerOccurrenceKey,
|
|
254
263
|
primerOccurrenceUtcDay,
|
|
255
264
|
promoteSessionFactsDurable,
|
|
265
|
+
promptSurfaceHashMaterial,
|
|
256
266
|
pruneAutoSearchHintDecisions,
|
|
257
267
|
pruneExpiredUserMemoryCandidates,
|
|
258
268
|
pruneNoteNudgeAnchors,
|
|
@@ -270,6 +280,7 @@ import {
|
|
|
270
280
|
recordHistorianDrainFailure,
|
|
271
281
|
recordMemoryMapping,
|
|
272
282
|
recordMemoryVerifications,
|
|
283
|
+
recordMuralCueRejection,
|
|
273
284
|
recordOverflowDetected,
|
|
274
285
|
recordProtectedTailNoEligibleHead,
|
|
275
286
|
recordProtectedTailPublicationFloor,
|
|
@@ -297,6 +308,7 @@ import {
|
|
|
297
308
|
resolveCompactionModeRecord,
|
|
298
309
|
resolveGitTopLevel,
|
|
299
310
|
resolveLimit,
|
|
311
|
+
resolveModelConfigOrDefault,
|
|
300
312
|
resolveModelRefForPi,
|
|
301
313
|
resolveMuralWire,
|
|
302
314
|
resolvePiStableId,
|
|
@@ -330,7 +342,6 @@ import {
|
|
|
330
342
|
setStoragePrivatePermissionEnforcement,
|
|
331
343
|
setTodoSnapshot,
|
|
332
344
|
sourceNameForMemory,
|
|
333
|
-
stableStringify,
|
|
334
345
|
stripMemoryMuralBlock,
|
|
335
346
|
stripPersistedAssistantText,
|
|
336
347
|
stripTagPrefix,
|
|
@@ -362,7 +373,7 @@ import {
|
|
|
362
373
|
withMigrationLanguageDirective,
|
|
363
374
|
withRawMessageProvider,
|
|
364
375
|
withRawSessionMessageCache
|
|
365
|
-
} from "./index-
|
|
376
|
+
} from "./index-xbk9epe8.js";
|
|
366
377
|
import {
|
|
367
378
|
pushNotification
|
|
368
379
|
} from "./index-hw34xmzk.js";
|
|
@@ -583,6 +594,15 @@ function formatFailClosedBlockingProcesses(processes) {
|
|
|
583
594
|
}
|
|
584
595
|
function formatFailClosedBlockingMessage(reason) {
|
|
585
596
|
if (reason.kind === "migration_guard") {
|
|
597
|
+
if (reason.unreadableFile) {
|
|
598
|
+
const arm = reason.unreadableArm ?? "io";
|
|
599
|
+
const recovery = arm === "io" ? `If no OpenCode server is running, it is safe to delete ${reason.unreadableFile} and retry.` : `The file may be a recent incomplete write; retry after the file is older than the ten-minute grace window, or stop OpenCode before deleting it.`;
|
|
600
|
+
return [
|
|
601
|
+
`Magic Context cannot migrate the shared database because RPC discovery file ${reason.unreadableFile} is uncertain (${arm} arm), so the absence of a live OpenCode server cannot be proven.`,
|
|
602
|
+
recovery,
|
|
603
|
+
`Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
|
|
604
|
+
].join(" ");
|
|
605
|
+
}
|
|
586
606
|
return [
|
|
587
607
|
`Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
|
|
588
608
|
"Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
|
|
@@ -1478,16 +1498,7 @@ function resolveCacheTtl(cacheTtl, modelKey) {
|
|
|
1478
1498
|
if (typeof cacheTtl === "string") {
|
|
1479
1499
|
return cacheTtl;
|
|
1480
1500
|
}
|
|
1481
|
-
|
|
1482
|
-
return cacheTtl[modelKey];
|
|
1483
|
-
}
|
|
1484
|
-
if (modelKey) {
|
|
1485
|
-
const bareModelId = modelKey.split("/").slice(1).join("/");
|
|
1486
|
-
if (bareModelId && typeof cacheTtl[bareModelId] === "string") {
|
|
1487
|
-
return cacheTtl[bareModelId];
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
return cacheTtl.default ?? "5m";
|
|
1501
|
+
return resolveModelConfigOrDefault(cacheTtl, modelKey, cacheTtl.default ?? "5m");
|
|
1491
1502
|
}
|
|
1492
1503
|
var clampWarnSeen = new Set;
|
|
1493
1504
|
function isFinitePositive(v) {
|
|
@@ -3286,6 +3297,70 @@ function backoffMs(failureCount) {
|
|
|
3286
3297
|
return minutes * 60 * 1000;
|
|
3287
3298
|
}
|
|
3288
3299
|
|
|
3300
|
+
// ../plugin/src/features/magic-context/dreamer/storage-task-schedule.ts
|
|
3301
|
+
function toRow(r) {
|
|
3302
|
+
return {
|
|
3303
|
+
projectPath: r.project_path,
|
|
3304
|
+
task: r.task,
|
|
3305
|
+
lastRunAt: r.last_run_at,
|
|
3306
|
+
nextDueAt: r.next_due_at,
|
|
3307
|
+
schedule: r.schedule ?? null,
|
|
3308
|
+
lastStatus: r.last_status ?? null,
|
|
3309
|
+
lastError: r.last_error,
|
|
3310
|
+
retryCount: r.retry_count ?? 0,
|
|
3311
|
+
lastCheckedCommit: r.last_checked_commit ?? null,
|
|
3312
|
+
lastBroadRunAt: r.last_broad_run_at ?? null,
|
|
3313
|
+
retrospectiveWatermarkMs: r.retrospective_watermark_ms ?? null
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
var SELECT_COLUMNS = "project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count, last_checked_commit, last_broad_run_at, retrospective_watermark_ms";
|
|
3317
|
+
function getTaskScheduleState(db, projectPath, task) {
|
|
3318
|
+
const row = db.prepare(`SELECT ${SELECT_COLUMNS} FROM task_schedule_state WHERE project_path = ? AND task = ?`).get(projectPath, task);
|
|
3319
|
+
return row ? toRow(row) : null;
|
|
3320
|
+
}
|
|
3321
|
+
function getMostRecentTaskRunAt(db, projectPath) {
|
|
3322
|
+
const row = db.prepare("SELECT MAX(last_run_at) AS max_at FROM task_schedule_state WHERE project_path = ?").get(projectPath);
|
|
3323
|
+
const value = row?.max_at ?? null;
|
|
3324
|
+
return typeof value === "number" && value > 0 ? value : null;
|
|
3325
|
+
}
|
|
3326
|
+
function pruneNonCanonicalTaskRows(db, projectPath, canonicalTasks) {
|
|
3327
|
+
if (canonicalTasks.length === 0)
|
|
3328
|
+
return 0;
|
|
3329
|
+
const placeholders = canonicalTasks.map(() => "?").join(", ");
|
|
3330
|
+
const result = db.prepare(`DELETE FROM task_schedule_state WHERE project_path = ? AND task NOT IN (${placeholders})`).run(projectPath, ...canonicalTasks);
|
|
3331
|
+
return Number(result.changes ?? 0);
|
|
3332
|
+
}
|
|
3333
|
+
function deleteTaskScheduleRowsForProject(db, projectPath) {
|
|
3334
|
+
const result = db.prepare("DELETE FROM task_schedule_state WHERE project_path = ?").run(projectPath);
|
|
3335
|
+
return Number(result.changes ?? 0);
|
|
3336
|
+
}
|
|
3337
|
+
function seedTaskScheduleState(db, projectPath, task, nextDueAt, lastRunAt, schedule) {
|
|
3338
|
+
db.prepare("INSERT INTO task_schedule_state (project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count) VALUES (?, ?, ?, ?, ?, NULL, NULL, 0) ON CONFLICT(project_path, task) DO NOTHING").run(projectPath, task, lastRunAt, nextDueAt, schedule);
|
|
3339
|
+
}
|
|
3340
|
+
function writeTaskScheduleState(db, row) {
|
|
3341
|
+
const broadCycleUpdate = row.lastBroadRunAt === undefined ? "last_broad_run_at = task_schedule_state.last_broad_run_at" : "last_broad_run_at = excluded.last_broad_run_at";
|
|
3342
|
+
db.prepare(`INSERT INTO task_schedule_state
|
|
3343
|
+
(project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count, last_checked_commit, last_broad_run_at, retrospective_watermark_ms)
|
|
3344
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3345
|
+
ON CONFLICT(project_path, task) DO UPDATE SET
|
|
3346
|
+
last_run_at = excluded.last_run_at,
|
|
3347
|
+
next_due_at = excluded.next_due_at,
|
|
3348
|
+
schedule = excluded.schedule,
|
|
3349
|
+
last_status = excluded.last_status,
|
|
3350
|
+
last_error = excluded.last_error,
|
|
3351
|
+
retry_count = excluded.retry_count,
|
|
3352
|
+
last_checked_commit = COALESCE(excluded.last_checked_commit, task_schedule_state.last_checked_commit),
|
|
3353
|
+
${broadCycleUpdate},
|
|
3354
|
+
retrospective_watermark_ms = COALESCE(excluded.retrospective_watermark_ms, task_schedule_state.retrospective_watermark_ms)`).run(row.projectPath, row.task, row.lastRunAt, row.nextDueAt, row.schedule, row.lastStatus, row.lastError, row.retryCount, row.lastCheckedCommit ?? null, row.lastBroadRunAt ?? null, row.retrospectiveWatermarkMs ?? null);
|
|
3355
|
+
}
|
|
3356
|
+
function isRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
3357
|
+
const row = db.prepare("SELECT 1 AS one FROM retrospective_processed_windows WHERE project_path = ? AND window_key = ?").get(projectPath, windowKey);
|
|
3358
|
+
return row != null;
|
|
3359
|
+
}
|
|
3360
|
+
function recordRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
3361
|
+
db.prepare("INSERT INTO retrospective_processed_windows (project_path, window_key, processed_at) VALUES (?, ?, ?) ON CONFLICT(project_path, window_key) DO NOTHING").run(projectPath, windowKey, Date.now());
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3289
3364
|
// ../plugin/src/features/magic-context/dreamer/task-gates.ts
|
|
3290
3365
|
function countActiveMemories(db, projectPath) {
|
|
3291
3366
|
const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
|
|
@@ -3331,6 +3406,19 @@ function countUnverifiedMappedMemories(db, projectPath) {
|
|
|
3331
3406
|
AND v.verified_at = 0`).get(projectPath);
|
|
3332
3407
|
return row?.cnt ?? 0;
|
|
3333
3408
|
}
|
|
3409
|
+
function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
|
|
3410
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3411
|
+
FROM memories m
|
|
3412
|
+
WHERE m.project_path = ?
|
|
3413
|
+
AND m.status IN ('active','permanent')
|
|
3414
|
+
AND (
|
|
3415
|
+
SELECT MAX(v.verified_at)
|
|
3416
|
+
FROM memory_verifications v
|
|
3417
|
+
WHERE v.memory_id = m.id
|
|
3418
|
+
AND v.file_path <> ''
|
|
3419
|
+
) < ?`).get(projectPath, cycleStartAt);
|
|
3420
|
+
return row?.cnt ?? 0;
|
|
3421
|
+
}
|
|
3334
3422
|
function countCueCandidates(db, projectPath) {
|
|
3335
3423
|
if (!hasMuralCueColumns(db))
|
|
3336
3424
|
return countActiveMemories(db, projectPath);
|
|
@@ -3386,7 +3474,9 @@ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
|
|
|
3386
3474
|
}
|
|
3387
3475
|
case "verify-broad": {
|
|
3388
3476
|
const total = countMappedMemories(db, projectPath);
|
|
3389
|
-
|
|
3477
|
+
const cycleStartAt = getTaskScheduleState(db, projectPath, "verify-broad")?.lastBroadRunAt;
|
|
3478
|
+
const pending = cycleStartAt == null ? total : countBroadCycleCandidates(db, projectPath, cycleStartAt);
|
|
3479
|
+
return { pending, total };
|
|
3390
3480
|
}
|
|
3391
3481
|
case "curate": {
|
|
3392
3482
|
const total = countActiveMemories(db, projectPath);
|
|
@@ -3445,7 +3535,7 @@ function evaluateTaskGate(task, ctx) {
|
|
|
3445
3535
|
case "verify":
|
|
3446
3536
|
return countActiveMemories(db, project) > 0;
|
|
3447
3537
|
case "verify-broad":
|
|
3448
|
-
return countActiveMemories(db, project) > 0;
|
|
3538
|
+
return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countActiveMemories(db, project) > 0;
|
|
3449
3539
|
case "curate":
|
|
3450
3540
|
return countActiveMemories(db, project) > 0;
|
|
3451
3541
|
case "compress-cues":
|
|
@@ -3490,7 +3580,7 @@ function openOpenCodeDb() {
|
|
|
3490
3580
|
}
|
|
3491
3581
|
|
|
3492
3582
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
3493
|
-
import { createHash as
|
|
3583
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
3494
3584
|
import { existsSync as existsSync7 } from "node:fs";
|
|
3495
3585
|
|
|
3496
3586
|
// ../plugin/src/agents/dreamer.ts
|
|
@@ -4075,6 +4165,9 @@ function parseFields(json) {
|
|
|
4075
4165
|
return {};
|
|
4076
4166
|
}
|
|
4077
4167
|
|
|
4168
|
+
// ../plugin/src/features/magic-context/mural/compress-cues.ts
|
|
4169
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
4170
|
+
|
|
4078
4171
|
// ../plugin/src/features/magic-context/dreamer/storage-dream-state.ts
|
|
4079
4172
|
var getDreamStateStatements = new WeakMap;
|
|
4080
4173
|
var setDreamStateStatements = new WeakMap;
|
|
@@ -4299,6 +4392,64 @@ function assertManifestCoversExactly(ids, expectedIds, rootName) {
|
|
|
4299
4392
|
}
|
|
4300
4393
|
}
|
|
4301
4394
|
|
|
4395
|
+
// ../plugin/src/features/magic-context/dreamer/module-apply.ts
|
|
4396
|
+
class DreamerModuleFailureError extends Error {
|
|
4397
|
+
transient = true;
|
|
4398
|
+
constructor(operation, cause) {
|
|
4399
|
+
super(`Rust dreamer ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
4400
|
+
this.name = "DreamerModuleFailureError";
|
|
4401
|
+
this.cause = cause;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
async function resolveDreamerModuleRoute(args) {
|
|
4405
|
+
const transport = args.transformMode === "ts" ? undefined : args.moduleClient;
|
|
4406
|
+
if (!transport?.authorityStatus)
|
|
4407
|
+
return;
|
|
4408
|
+
const contextStoreUuid = getContextStoreUuid(args.db);
|
|
4409
|
+
if (!contextStoreUuid)
|
|
4410
|
+
throw new Error("Rust dreamer requires a context store identity");
|
|
4411
|
+
const result = await transport.authorityStatus({
|
|
4412
|
+
context_store_uuid: contextStoreUuid,
|
|
4413
|
+
project: args.projectIdentity,
|
|
4414
|
+
projectRoot: args.projectRoot,
|
|
4415
|
+
domain: "memories"
|
|
4416
|
+
});
|
|
4417
|
+
if (result.authority?.state !== "MODULE")
|
|
4418
|
+
return;
|
|
4419
|
+
const generation = result.authority.generation;
|
|
4420
|
+
if (typeof generation !== "number")
|
|
4421
|
+
throw new Error("Rust authority status omitted generation");
|
|
4422
|
+
return {
|
|
4423
|
+
moduleClient: transport,
|
|
4424
|
+
moduleSessionId: args.projectIdentity,
|
|
4425
|
+
moduleProjectRoot: args.projectRoot,
|
|
4426
|
+
moduleContextStoreUuid: contextStoreUuid,
|
|
4427
|
+
moduleAuthorityGeneration: generation,
|
|
4428
|
+
moduleCommandId: args.commandId
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4431
|
+
function getModuleMemoryIdentities(db, projectIdentity, contextIds) {
|
|
4432
|
+
if (contextIds.length === 0)
|
|
4433
|
+
return new Map;
|
|
4434
|
+
const placeholders = contextIds.map(() => "?").join(", ");
|
|
4435
|
+
const rows = db.prepare(`SELECT identity.context_row_id, identity.module_row_id, live.normalized_hash
|
|
4436
|
+
FROM mirror_identity identity
|
|
4437
|
+
LEFT JOIN mirror_live_memory_rows live
|
|
4438
|
+
ON live.module_project = identity.module_project
|
|
4439
|
+
AND live.module_row_id = identity.module_row_id
|
|
4440
|
+
WHERE identity.domain = 'memories' AND identity.module_project = ?
|
|
4441
|
+
AND identity.context_row_id IN (${placeholders})`).all(projectIdentity, ...contextIds);
|
|
4442
|
+
return new Map(rows.flatMap((row) => Number.isInteger(row.context_row_id) && Number.isInteger(row.module_row_id) && typeof row.normalized_hash === "string" ? [
|
|
4443
|
+
[
|
|
4444
|
+
row.context_row_id,
|
|
4445
|
+
{
|
|
4446
|
+
moduleId: row.module_row_id,
|
|
4447
|
+
normalizedHash: row.normalized_hash
|
|
4448
|
+
}
|
|
4449
|
+
]
|
|
4450
|
+
] : []));
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4302
4453
|
// ../plugin/src/features/magic-context/mural/compress-cues-prompt.ts
|
|
4303
4454
|
var CUE_BUDGET_HIGH = 90;
|
|
4304
4455
|
var CUE_BUDGET_LOW = 50;
|
|
@@ -4370,7 +4521,7 @@ function hasBalancedParentheses(cue) {
|
|
|
4370
4521
|
}
|
|
4371
4522
|
return depth === 0;
|
|
4372
4523
|
}
|
|
4373
|
-
function validateCue(cue, importance) {
|
|
4524
|
+
function validateCue(cue, importance, ownId) {
|
|
4374
4525
|
const trimmed = cue.trim();
|
|
4375
4526
|
if (trimmed.length === 0)
|
|
4376
4527
|
return { reason: "empty" };
|
|
@@ -4378,8 +4529,9 @@ function validateCue(cue, importance) {
|
|
|
4378
4529
|
const length = [...trimmed].length;
|
|
4379
4530
|
if (length > budget)
|
|
4380
4531
|
return { reason: `over-budget ${length}>${budget}` };
|
|
4381
|
-
if (
|
|
4532
|
+
if (ownId !== undefined && new RegExp(`#${ownId}\\b`).test(trimmed)) {
|
|
4382
4533
|
return { reason: "leaked-id" };
|
|
4534
|
+
}
|
|
4383
4535
|
if (!hasBalancedParentheses(trimmed))
|
|
4384
4536
|
return { reason: "unbalanced-parens" };
|
|
4385
4537
|
const markers = trimmed.split("⊘").length - 1;
|
|
@@ -4395,6 +4547,7 @@ function validateCue(cue, importance) {
|
|
|
4395
4547
|
// ../plugin/src/features/magic-context/mural/compress-cues.ts
|
|
4396
4548
|
var COMPRESS_CUES_CHUNK_SIZE = 40;
|
|
4397
4549
|
var CHUNK_TIMEOUT_FLOOR_MS = 240000;
|
|
4550
|
+
var CUE_REJECTION_LATCH_THRESHOLD = 3;
|
|
4398
4551
|
var CONSECUTIVE_TIMEOUT_LIMIT = 2;
|
|
4399
4552
|
function toPromptMemory(candidate) {
|
|
4400
4553
|
const memory = candidate.memory;
|
|
@@ -4405,6 +4558,38 @@ function toPromptMemory(candidate) {
|
|
|
4405
4558
|
content: memory.content
|
|
4406
4559
|
};
|
|
4407
4560
|
}
|
|
4561
|
+
function stripOwnIdToken(value, ownId) {
|
|
4562
|
+
return value.replace(new RegExp(`#${ownId}\\b`, "g"), "");
|
|
4563
|
+
}
|
|
4564
|
+
function truncateCue(value, budget) {
|
|
4565
|
+
const trimmed = value.trim();
|
|
4566
|
+
const codepoints = [...trimmed];
|
|
4567
|
+
if (codepoints.length <= budget)
|
|
4568
|
+
return trimmed;
|
|
4569
|
+
const prefix = codepoints.slice(0, budget).join("");
|
|
4570
|
+
const boundary = prefix.search(/\s+\S*$/);
|
|
4571
|
+
return (boundary > 0 ? prefix.slice(0, boundary) : prefix).trim();
|
|
4572
|
+
}
|
|
4573
|
+
function sanitizeCue(value, candidate) {
|
|
4574
|
+
return truncateCue(stripOwnIdToken(value, candidate.memory.id), cueBudgetFor(candidate.memory.importance ?? 50));
|
|
4575
|
+
}
|
|
4576
|
+
function deterministicFallbackCue(candidate, lastCandidate) {
|
|
4577
|
+
const importance = candidate.memory.importance ?? 50;
|
|
4578
|
+
const budget = cueBudgetFor(importance);
|
|
4579
|
+
const sanitizedCandidate = sanitizeCue(lastCandidate, candidate);
|
|
4580
|
+
if (validateCue(sanitizedCandidate, importance, candidate.memory.id) === null) {
|
|
4581
|
+
return sanitizedCandidate;
|
|
4582
|
+
}
|
|
4583
|
+
const sourceSlice = sanitizeCue(candidate.memory.content, candidate);
|
|
4584
|
+
if (validateCue(sourceSlice, importance, candidate.memory.id) === null) {
|
|
4585
|
+
return sourceSlice;
|
|
4586
|
+
}
|
|
4587
|
+
const grammarSafe = truncateCue(sourceSlice.replaceAll("⊘", "").replace(/[()]/g, "").replace(/\b(?:must not|never|without|instead of|exclude|excludes)\b/gi, "").replace(/\s+/g, " "), budget);
|
|
4588
|
+
if (validateCue(grammarSafe, importance, candidate.memory.id) === null) {
|
|
4589
|
+
return grammarSafe;
|
|
4590
|
+
}
|
|
4591
|
+
return "memory";
|
|
4592
|
+
}
|
|
4408
4593
|
function selectCandidates(db, projectIdentity) {
|
|
4409
4594
|
const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
|
|
4410
4595
|
const cueState = getMuralCueState(db, memories.map((memory) => memory.id));
|
|
@@ -4536,7 +4721,7 @@ async function compressOneChunk(args, chunk, sliceMs, signal) {
|
|
|
4536
4721
|
return text;
|
|
4537
4722
|
}
|
|
4538
4723
|
});
|
|
4539
|
-
return applyCues(args, chunk, run.validated);
|
|
4724
|
+
return args.moduleRoute ? await applyCuesThroughModule(args, chunk, run.validated, signal) : applyCues(args, chunk, run.validated);
|
|
4540
4725
|
} catch (error) {
|
|
4541
4726
|
const desc = describeError(error);
|
|
4542
4727
|
log(`[dreamer] compress-cues chunk failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
|
|
@@ -4573,10 +4758,19 @@ function applyCues(args, chunk, manifestText) {
|
|
|
4573
4758
|
const candidate = byId.get(entry.id);
|
|
4574
4759
|
if (!candidate)
|
|
4575
4760
|
throw new Error(`cues manifest contains unknown id ${entry.id}`);
|
|
4576
|
-
const
|
|
4761
|
+
const importance = candidate.memory.importance ?? 50;
|
|
4762
|
+
const failure = validateCue(entry.cue, importance, candidate.memory.id);
|
|
4577
4763
|
if (failure) {
|
|
4764
|
+
const rejectionCount = recordMuralCueRejection(args.db, args.projectIdentity, entry.id, candidate.contentHash);
|
|
4765
|
+
if (rejectionCount >= CUE_REJECTION_LATCH_THRESHOLD) {
|
|
4766
|
+
const fallback = deterministicFallbackCue(candidate, entry.cue);
|
|
4767
|
+
setMuralCue(args.db, args.projectIdentity, entry.id, fallback, candidate.contentHash);
|
|
4768
|
+
compressed += 1;
|
|
4769
|
+
log(`[dreamer] compress-cues: fallback cue for memory ${entry.id} (${failure.reason}; ${rejectionCount} rejections; fallback)`);
|
|
4770
|
+
continue;
|
|
4771
|
+
}
|
|
4578
4772
|
skipped += 1;
|
|
4579
|
-
log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason})`);
|
|
4773
|
+
log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason}; rejection ${rejectionCount}/${CUE_REJECTION_LATCH_THRESHOLD})`);
|
|
4580
4774
|
continue;
|
|
4581
4775
|
}
|
|
4582
4776
|
setMuralCue(args.db, args.projectIdentity, entry.id, entry.cue.trim(), candidate.contentHash);
|
|
@@ -4585,6 +4779,111 @@ function applyCues(args, chunk, manifestText) {
|
|
|
4585
4779
|
});
|
|
4586
4780
|
return { compressed, skipped };
|
|
4587
4781
|
}
|
|
4782
|
+
async function applyCuesThroughModule(args, chunk, manifestText, signal) {
|
|
4783
|
+
const route = args.moduleRoute;
|
|
4784
|
+
if (!route)
|
|
4785
|
+
throw new Error("module cue apply called without a module route");
|
|
4786
|
+
const byId = new Map(chunk.map((candidate) => [candidate.memory.id, candidate]));
|
|
4787
|
+
const parsed = parseCuesManifest(manifestText);
|
|
4788
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), new Set(byId.keys()), "cues");
|
|
4789
|
+
const state2 = getMuralCueState(args.db, chunk.map((candidate) => candidate.memory.id));
|
|
4790
|
+
const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, chunk.map((candidate) => candidate.memory.id));
|
|
4791
|
+
const updates = [];
|
|
4792
|
+
for (const entry of parsed) {
|
|
4793
|
+
const candidate = byId.get(entry.id);
|
|
4794
|
+
if (!candidate)
|
|
4795
|
+
throw new Error(`cues manifest contains unknown id ${entry.id}`);
|
|
4796
|
+
const identity = identities.get(candidate.memory.id);
|
|
4797
|
+
if (!identity) {
|
|
4798
|
+
throw new Error(`module mirror identity missing for memory ${candidate.memory.id}`);
|
|
4799
|
+
}
|
|
4800
|
+
const failure = validateCue(entry.cue, candidate.memory.importance ?? 50, candidate.memory.id);
|
|
4801
|
+
if (!failure) {
|
|
4802
|
+
updates.push({
|
|
4803
|
+
contextId: candidate.memory.id,
|
|
4804
|
+
moduleId: identity.moduleId,
|
|
4805
|
+
contentHash: candidate.contentHash,
|
|
4806
|
+
cue: entry.cue.trim(),
|
|
4807
|
+
rejectionCount: 0,
|
|
4808
|
+
kind: "compressed"
|
|
4809
|
+
});
|
|
4810
|
+
continue;
|
|
4811
|
+
}
|
|
4812
|
+
const previous = state2.get(candidate.memory.id);
|
|
4813
|
+
const rejectionCount = previous?.hash === candidate.contentHash ? (previous.rejectionCount ?? 0) + 1 : 1;
|
|
4814
|
+
if (rejectionCount >= CUE_REJECTION_LATCH_THRESHOLD) {
|
|
4815
|
+
updates.push({
|
|
4816
|
+
contextId: candidate.memory.id,
|
|
4817
|
+
moduleId: identity.moduleId,
|
|
4818
|
+
contentHash: candidate.contentHash,
|
|
4819
|
+
cue: deterministicFallbackCue(candidate, entry.cue),
|
|
4820
|
+
rejectionCount: 0,
|
|
4821
|
+
kind: "compressed"
|
|
4822
|
+
});
|
|
4823
|
+
log(`[dreamer] compress-cues: fallback cue for memory ${entry.id} (${failure.reason}; ${rejectionCount} rejections; fallback)`);
|
|
4824
|
+
} else {
|
|
4825
|
+
updates.push({
|
|
4826
|
+
contextId: candidate.memory.id,
|
|
4827
|
+
moduleId: identity.moduleId,
|
|
4828
|
+
contentHash: candidate.contentHash,
|
|
4829
|
+
cue: null,
|
|
4830
|
+
rejectionCount,
|
|
4831
|
+
kind: "skipped"
|
|
4832
|
+
});
|
|
4833
|
+
log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason}; rejection ${rejectionCount}/${CUE_REJECTION_LATCH_THRESHOLD})`);
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
const commandId = `mural-cues:${route.moduleCommandId}:${createHash2("sha256").update(chunk.map((candidate) => candidate.memory.id).join(",")).digest("hex").slice(0, 24)}`;
|
|
4837
|
+
let response;
|
|
4838
|
+
try {
|
|
4839
|
+
response = await route.moduleClient.call({
|
|
4840
|
+
sessionId: route.moduleSessionId,
|
|
4841
|
+
projectRoot: route.moduleProjectRoot,
|
|
4842
|
+
method: "memory.set_mural_cue",
|
|
4843
|
+
body: {
|
|
4844
|
+
name: "memory.set_mural_cue",
|
|
4845
|
+
arguments: {
|
|
4846
|
+
memory_project: args.projectIdentity,
|
|
4847
|
+
context_store_uuid: route.moduleContextStoreUuid,
|
|
4848
|
+
authority_generation: route.moduleAuthorityGeneration,
|
|
4849
|
+
command_id: commandId,
|
|
4850
|
+
rows: updates.map((update) => ({
|
|
4851
|
+
memory_id: update.moduleId,
|
|
4852
|
+
content_hash_at_prompt: update.contentHash,
|
|
4853
|
+
cue: update.cue,
|
|
4854
|
+
rejection_count: update.rejectionCount
|
|
4855
|
+
}))
|
|
4856
|
+
}
|
|
4857
|
+
},
|
|
4858
|
+
signal
|
|
4859
|
+
});
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
throw new DreamerModuleFailureError("mural cue apply", error);
|
|
4862
|
+
}
|
|
4863
|
+
const result = response?.result ?? response;
|
|
4864
|
+
if (!result || typeof result !== "object") {
|
|
4865
|
+
throw new Error("module returned invalid mural cue apply result");
|
|
4866
|
+
}
|
|
4867
|
+
const accepted = result.accepted;
|
|
4868
|
+
if (!Array.isArray(accepted) || !accepted.every((id) => Number.isInteger(id))) {
|
|
4869
|
+
throw new Error("module returned no mural cue acceptance list");
|
|
4870
|
+
}
|
|
4871
|
+
const acceptedIds = new Set(accepted);
|
|
4872
|
+
const rejected = result.rejected;
|
|
4873
|
+
const rejectedReasons = new Map;
|
|
4874
|
+
for (const row of Array.isArray(rejected) ? rejected : []) {
|
|
4875
|
+
const reason = row && typeof row === "object" && typeof row.reason === "string" ? row.reason : "unknown";
|
|
4876
|
+
rejectedReasons.set(reason, (rejectedReasons.get(reason) ?? 0) + 1);
|
|
4877
|
+
}
|
|
4878
|
+
if ([...rejectedReasons].some(([reason]) => reason !== "stale")) {
|
|
4879
|
+
throw new Error(`module rejected mural cues (${[...rejectedReasons].map(([reason, count]) => `${reason}=${count}`).join(", ")})`);
|
|
4880
|
+
}
|
|
4881
|
+
return updates.reduce((counts, update) => {
|
|
4882
|
+
if (acceptedIds.has(update.moduleId))
|
|
4883
|
+
counts[update.kind] += 1;
|
|
4884
|
+
return counts;
|
|
4885
|
+
}, { compressed: 0, skipped: 0 });
|
|
4886
|
+
}
|
|
4588
4887
|
|
|
4589
4888
|
// ../plugin/src/features/magic-context/dreamer/task-prompts.ts
|
|
4590
4889
|
var PROJECT_MEMORY_TAXONOMY = `## Memory taxonomy (5 categories)
|
|
@@ -5099,7 +5398,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
|
|
|
5099
5398
|
}
|
|
5100
5399
|
|
|
5101
5400
|
// ../plugin/src/features/magic-context/dreamer/classify.ts
|
|
5102
|
-
import { createHash as
|
|
5401
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
5103
5402
|
|
|
5104
5403
|
// ../plugin/src/plugin/rust-tool-backends.ts
|
|
5105
5404
|
function isRustAuthorityDrainingError(error) {
|
|
@@ -5367,64 +5666,6 @@ function parseClassifyManifest(text) {
|
|
|
5367
5666
|
return out;
|
|
5368
5667
|
}
|
|
5369
5668
|
|
|
5370
|
-
// ../plugin/src/features/magic-context/dreamer/module-apply.ts
|
|
5371
|
-
class DreamerModuleFailureError extends Error {
|
|
5372
|
-
transient = true;
|
|
5373
|
-
constructor(operation, cause) {
|
|
5374
|
-
super(`Rust dreamer ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
5375
|
-
this.name = "DreamerModuleFailureError";
|
|
5376
|
-
this.cause = cause;
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
|
-
async function resolveDreamerModuleRoute(args) {
|
|
5380
|
-
const transport = args.transformMode === "ts" ? undefined : args.moduleClient;
|
|
5381
|
-
if (!transport?.authorityStatus)
|
|
5382
|
-
return;
|
|
5383
|
-
const contextStoreUuid = getContextStoreUuid(args.db);
|
|
5384
|
-
if (!contextStoreUuid)
|
|
5385
|
-
throw new Error("Rust dreamer requires a context store identity");
|
|
5386
|
-
const result = await transport.authorityStatus({
|
|
5387
|
-
context_store_uuid: contextStoreUuid,
|
|
5388
|
-
project: args.projectIdentity,
|
|
5389
|
-
projectRoot: args.projectRoot,
|
|
5390
|
-
domain: "memories"
|
|
5391
|
-
});
|
|
5392
|
-
if (result.authority?.state !== "MODULE")
|
|
5393
|
-
return;
|
|
5394
|
-
const generation = result.authority.generation;
|
|
5395
|
-
if (typeof generation !== "number")
|
|
5396
|
-
throw new Error("Rust authority status omitted generation");
|
|
5397
|
-
return {
|
|
5398
|
-
moduleClient: transport,
|
|
5399
|
-
moduleSessionId: args.projectIdentity,
|
|
5400
|
-
moduleProjectRoot: args.projectRoot,
|
|
5401
|
-
moduleContextStoreUuid: contextStoreUuid,
|
|
5402
|
-
moduleAuthorityGeneration: generation,
|
|
5403
|
-
moduleCommandId: args.commandId
|
|
5404
|
-
};
|
|
5405
|
-
}
|
|
5406
|
-
function getModuleMemoryIdentities(db, projectIdentity, contextIds) {
|
|
5407
|
-
if (contextIds.length === 0)
|
|
5408
|
-
return new Map;
|
|
5409
|
-
const placeholders = contextIds.map(() => "?").join(", ");
|
|
5410
|
-
const rows = db.prepare(`SELECT identity.context_row_id, identity.module_row_id, live.normalized_hash
|
|
5411
|
-
FROM mirror_identity identity
|
|
5412
|
-
LEFT JOIN mirror_live_memory_rows live
|
|
5413
|
-
ON live.module_project = identity.module_project
|
|
5414
|
-
AND live.module_row_id = identity.module_row_id
|
|
5415
|
-
WHERE identity.domain = 'memories' AND identity.module_project = ?
|
|
5416
|
-
AND identity.context_row_id IN (${placeholders})`).all(projectIdentity, ...contextIds);
|
|
5417
|
-
return new Map(rows.flatMap((row) => Number.isInteger(row.context_row_id) && Number.isInteger(row.module_row_id) && typeof row.normalized_hash === "string" ? [
|
|
5418
|
-
[
|
|
5419
|
-
row.context_row_id,
|
|
5420
|
-
{
|
|
5421
|
-
moduleId: row.module_row_id,
|
|
5422
|
-
normalizedHash: row.normalized_hash
|
|
5423
|
-
}
|
|
5424
|
-
]
|
|
5425
|
-
] : []));
|
|
5426
|
-
}
|
|
5427
|
-
|
|
5428
5669
|
// ../plugin/src/features/magic-context/dreamer/classify.ts
|
|
5429
5670
|
var MIN_POOL_TO_CLASSIFY = 10;
|
|
5430
5671
|
var FULL_POOL_CEILING = 100;
|
|
@@ -5661,7 +5902,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
|
|
|
5661
5902
|
v: 1,
|
|
5662
5903
|
session_id: args.moduleSessionId,
|
|
5663
5904
|
task: "classify",
|
|
5664
|
-
command_id: `classify:${args.moduleCommandId ?? Date.now()}:${
|
|
5905
|
+
command_id: `classify:${args.moduleCommandId ?? Date.now()}:${createHash3("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`,
|
|
5665
5906
|
authority_generation: args.moduleAuthorityGeneration,
|
|
5666
5907
|
payload: {
|
|
5667
5908
|
prompt_body: prompt,
|
|
@@ -6299,7 +6540,7 @@ function throwIfAborted2(signal) {
|
|
|
6299
6540
|
}
|
|
6300
6541
|
|
|
6301
6542
|
// ../plugin/src/features/magic-context/smart-notes/compiler.ts
|
|
6302
|
-
import { createHash as
|
|
6543
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
6303
6544
|
|
|
6304
6545
|
// ../plugin/src/features/magic-context/smart-notes/compiler-prompt.ts
|
|
6305
6546
|
var SMART_NOTE_COMPILER_SYSTEM_PROMPT = `You are the Magic Context smart-note compiler for the magic-context system.
|
|
@@ -6723,7 +6964,7 @@ function manifestAdvisoryWarnings(code, manifest) {
|
|
|
6723
6964
|
return warnings;
|
|
6724
6965
|
}
|
|
6725
6966
|
function hashCheck(surfaceCondition, compiledCheck, manifest, checkCron) {
|
|
6726
|
-
return
|
|
6967
|
+
return createHash4("sha256").update(surfaceCondition ?? "").update("\x00").update(compiledCheck).update("\x00").update(JSON.stringify(manifest)).update("\x00").update(checkCron).digest("hex");
|
|
6727
6968
|
}
|
|
6728
6969
|
function extractJsonObject(output) {
|
|
6729
6970
|
const fenced = output.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
@@ -7403,7 +7644,7 @@ function enforceMaintainDocsProtectedRegions(args) {
|
|
|
7403
7644
|
}
|
|
7404
7645
|
|
|
7405
7646
|
// ../plugin/src/features/magic-context/dreamer/map-memories.ts
|
|
7406
|
-
import { createHash as
|
|
7647
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
7407
7648
|
|
|
7408
7649
|
// ../plugin/src/features/magic-context/dreamer/map-memories-prompt.ts
|
|
7409
7650
|
import { existsSync as existsSync6, statSync } from "node:fs";
|
|
@@ -7664,7 +7905,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
7664
7905
|
memory_project: args.projectIdentity,
|
|
7665
7906
|
context_store_uuid: args.moduleRoute.moduleContextStoreUuid,
|
|
7666
7907
|
authority_generation: args.moduleRoute.moduleAuthorityGeneration,
|
|
7667
|
-
command_id: `${args.moduleRoute.moduleCommandId}:${
|
|
7908
|
+
command_id: `${args.moduleRoute.moduleCommandId}:${createHash5("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
|
|
7668
7909
|
rows
|
|
7669
7910
|
}
|
|
7670
7911
|
}
|
|
@@ -8721,77 +8962,100 @@ function insertDreamRun(db, run) {
|
|
|
8721
8962
|
getInsertDreamRunStatement(db).run(run.projectPath, run.startedAt, run.finishedAt, run.holderId, JSON.stringify(run.tasks), run.tasksSucceeded, run.tasksFailed, run.smartNotesSurfaced, run.smartNotesPending, run.memoryChanges ? JSON.stringify(run.memoryChanges) : null, run.parentSessionId ?? null);
|
|
8722
8963
|
}
|
|
8723
8964
|
|
|
8724
|
-
// ../plugin/src/features/magic-context/dreamer/
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
}
|
|
8745
|
-
function getMostRecentTaskRunAt(db, projectPath) {
|
|
8746
|
-
const row = db.prepare("SELECT MAX(last_run_at) AS max_at FROM task_schedule_state WHERE project_path = ?").get(projectPath);
|
|
8747
|
-
const value = row?.max_at ?? null;
|
|
8748
|
-
return typeof value === "number" && value > 0 ? value : null;
|
|
8749
|
-
}
|
|
8750
|
-
function pruneNonCanonicalTaskRows(db, projectPath, canonicalTasks) {
|
|
8751
|
-
if (canonicalTasks.length === 0)
|
|
8752
|
-
return 0;
|
|
8753
|
-
const placeholders = canonicalTasks.map(() => "?").join(", ");
|
|
8754
|
-
const result = db.prepare(`DELETE FROM task_schedule_state WHERE project_path = ? AND task NOT IN (${placeholders})`).run(projectPath, ...canonicalTasks);
|
|
8755
|
-
return Number(result.changes ?? 0);
|
|
8756
|
-
}
|
|
8757
|
-
function deleteTaskScheduleRowsForProject(db, projectPath) {
|
|
8758
|
-
const result = db.prepare("DELETE FROM task_schedule_state WHERE project_path = ?").run(projectPath);
|
|
8759
|
-
return Number(result.changes ?? 0);
|
|
8760
|
-
}
|
|
8761
|
-
function seedTaskScheduleState(db, projectPath, task, nextDueAt, lastRunAt, schedule) {
|
|
8762
|
-
db.prepare("INSERT INTO task_schedule_state (project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count) VALUES (?, ?, ?, ?, ?, NULL, NULL, 0) ON CONFLICT(project_path, task) DO NOTHING").run(projectPath, task, lastRunAt, nextDueAt, schedule);
|
|
8763
|
-
}
|
|
8764
|
-
function writeTaskScheduleState(db, row) {
|
|
8765
|
-
db.prepare(`INSERT INTO task_schedule_state
|
|
8766
|
-
(project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count, last_checked_commit, last_broad_run_at, retrospective_watermark_ms)
|
|
8767
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8768
|
-
ON CONFLICT(project_path, task) DO UPDATE SET
|
|
8769
|
-
last_run_at = excluded.last_run_at,
|
|
8770
|
-
next_due_at = excluded.next_due_at,
|
|
8771
|
-
schedule = excluded.schedule,
|
|
8772
|
-
last_status = excluded.last_status,
|
|
8773
|
-
last_error = excluded.last_error,
|
|
8774
|
-
retry_count = excluded.retry_count,
|
|
8775
|
-
last_checked_commit = COALESCE(excluded.last_checked_commit, task_schedule_state.last_checked_commit),
|
|
8776
|
-
last_broad_run_at = COALESCE(excluded.last_broad_run_at, task_schedule_state.last_broad_run_at),
|
|
8777
|
-
retrospective_watermark_ms = COALESCE(excluded.retrospective_watermark_ms, task_schedule_state.retrospective_watermark_ms)`).run(row.projectPath, row.task, row.lastRunAt, row.nextDueAt, row.schedule, row.lastStatus, row.lastError, row.retryCount, row.lastCheckedCommit ?? null, row.lastBroadRunAt ?? null, row.retrospectiveWatermarkMs ?? null);
|
|
8965
|
+
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
8966
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
8967
|
+
|
|
8968
|
+
// ../plugin/src/features/magic-context/dreamer/provider-output-failure.ts
|
|
8969
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
8970
|
+
var MAX_NEAR_ZERO_OUTPUT_TOKENS = 32;
|
|
8971
|
+
|
|
8972
|
+
class DreamerProviderOutputFailureError extends Error {
|
|
8973
|
+
fingerprint;
|
|
8974
|
+
outputTokens;
|
|
8975
|
+
reasoningTokens;
|
|
8976
|
+
transient = true;
|
|
8977
|
+
constructor(fingerprint, outputTokens, reasoningTokens, responseText) {
|
|
8978
|
+
const preview = responseText.trim().replace(/\s+/g, " ").slice(0, 160);
|
|
8979
|
+
super(`verify provider-outage completion (output_tokens=${outputTokens}, reasoning_tokens=${reasoningTokens}): ${JSON.stringify(preview)}`);
|
|
8980
|
+
this.fingerprint = fingerprint;
|
|
8981
|
+
this.outputTokens = outputTokens;
|
|
8982
|
+
this.reasoningTokens = reasoningTokens;
|
|
8983
|
+
this.name = "DreamerProviderOutputFailureError";
|
|
8984
|
+
}
|
|
8778
8985
|
}
|
|
8779
|
-
function
|
|
8780
|
-
|
|
8781
|
-
return row != null;
|
|
8986
|
+
function finiteTokenCount(value) {
|
|
8987
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
8782
8988
|
}
|
|
8783
|
-
function
|
|
8784
|
-
|
|
8989
|
+
function completionShape(value) {
|
|
8990
|
+
if (!isRecord(value))
|
|
8991
|
+
return null;
|
|
8992
|
+
const info = isRecord(value.info) ? value.info : value;
|
|
8993
|
+
if (info.role !== "assistant")
|
|
8994
|
+
return null;
|
|
8995
|
+
const time = isRecord(info.time) ? info.time : null;
|
|
8996
|
+
const tokens = isRecord(info.tokens) ? info.tokens : null;
|
|
8997
|
+
return {
|
|
8998
|
+
createdAt: typeof time?.created === "number" ? time.created : 0,
|
|
8999
|
+
finish: typeof info.finish === "string" ? info.finish : typeof info.finish_reason === "string" ? info.finish_reason : typeof info.finishReason === "string" ? info.finishReason : null,
|
|
9000
|
+
error: info.error,
|
|
9001
|
+
outputTokens: finiteTokenCount(tokens?.output),
|
|
9002
|
+
reasoningTokens: finiteTokenCount(tokens?.reasoning)
|
|
9003
|
+
};
|
|
9004
|
+
}
|
|
9005
|
+
function latestAssistantCompletion(messages) {
|
|
9006
|
+
if (!Array.isArray(messages))
|
|
9007
|
+
return null;
|
|
9008
|
+
let latest = null;
|
|
9009
|
+
for (const message of messages) {
|
|
9010
|
+
const completion = completionShape(message);
|
|
9011
|
+
if (completion && (!latest || completion.createdAt >= latest.createdAt))
|
|
9012
|
+
latest = completion;
|
|
9013
|
+
}
|
|
9014
|
+
return latest;
|
|
9015
|
+
}
|
|
9016
|
+
function providerOutputFailureFromInvalidManifest(messages, responseText) {
|
|
9017
|
+
const completion = latestAssistantCompletion(messages);
|
|
9018
|
+
if (completion?.finish?.toLowerCase() !== "stop")
|
|
9019
|
+
return null;
|
|
9020
|
+
if (completion.error != null || completion.outputTokens === null || completion.outputTokens > MAX_NEAR_ZERO_OUTPUT_TOKENS || completion.reasoningTokens !== 0) {
|
|
9021
|
+
return null;
|
|
9022
|
+
}
|
|
9023
|
+
const normalized = responseText.trim().replace(/\s+/g, " ").toLowerCase();
|
|
9024
|
+
if (!normalized)
|
|
9025
|
+
return null;
|
|
9026
|
+
const fingerprint = createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
9027
|
+
return new DreamerProviderOutputFailureError(fingerprint, completion.outputTokens, completion.reasoningTokens, responseText);
|
|
8785
9028
|
}
|
|
8786
|
-
|
|
8787
|
-
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
8788
|
-
import { createHash as createHash5 } from "node:crypto";
|
|
8789
9029
|
|
|
8790
9030
|
// ../plugin/src/features/magic-context/dreamer/verify-gate.ts
|
|
8791
9031
|
import path5 from "node:path";
|
|
8792
9032
|
function minOf(values) {
|
|
8793
9033
|
return values.reduce((acc, v) => v < acc ? v : acc, Number.POSITIVE_INFINITY);
|
|
8794
9034
|
}
|
|
9035
|
+
function ensureBroadCycleStart(args) {
|
|
9036
|
+
const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
9037
|
+
if (current?.lastBroadRunAt != null && current.lastBroadRunAt > 0) {
|
|
9038
|
+
return current.lastBroadRunAt;
|
|
9039
|
+
}
|
|
9040
|
+
if (!current)
|
|
9041
|
+
return args.runStartedAt;
|
|
9042
|
+
if (!args.holderId || !args.leaseKey) {
|
|
9043
|
+
throw new Error("verify-broad cycle opening requires the task lease");
|
|
9044
|
+
}
|
|
9045
|
+
return runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
9046
|
+
const latest = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
9047
|
+
if (!latest)
|
|
9048
|
+
return args.runStartedAt;
|
|
9049
|
+
if (latest.lastBroadRunAt != null && latest.lastBroadRunAt > 0) {
|
|
9050
|
+
return latest.lastBroadRunAt;
|
|
9051
|
+
}
|
|
9052
|
+
writeTaskScheduleState(args.db, {
|
|
9053
|
+
...latest,
|
|
9054
|
+
lastBroadRunAt: args.runStartedAt
|
|
9055
|
+
});
|
|
9056
|
+
return args.runStartedAt;
|
|
9057
|
+
});
|
|
9058
|
+
}
|
|
8795
9059
|
async function partitionVerifyScope(args) {
|
|
8796
9060
|
const runStartedAt = args.now ?? Date.now();
|
|
8797
9061
|
const active = getMemoriesByProject(args.db, args.projectIdentity);
|
|
@@ -8803,6 +9067,29 @@ async function partitionVerifyScope(args) {
|
|
|
8803
9067
|
content: m.content,
|
|
8804
9068
|
mappedFiles: verById.get(m.id)?.files ?? []
|
|
8805
9069
|
});
|
|
9070
|
+
if (args.forceBroad) {
|
|
9071
|
+
const broadCycleStartAt = ensureBroadCycleStart({
|
|
9072
|
+
db: args.db,
|
|
9073
|
+
projectIdentity: args.projectIdentity,
|
|
9074
|
+
holderId: args.holderId,
|
|
9075
|
+
leaseKey: args.leaseKey,
|
|
9076
|
+
runStartedAt
|
|
9077
|
+
});
|
|
9078
|
+
const broadCandidates = candidates.filter((m) => (verById.get(m.id)?.verifiedAt ?? 0) < broadCycleStartAt).sort((a, b) => {
|
|
9079
|
+
const verifiedAtA = verById.get(a.id)?.verifiedAt ?? 0;
|
|
9080
|
+
const verifiedAtB = verById.get(b.id)?.verifiedAt ?? 0;
|
|
9081
|
+
return verifiedAtA - verifiedAtB || a.id - b.id;
|
|
9082
|
+
});
|
|
9083
|
+
return {
|
|
9084
|
+
runStartedAt,
|
|
9085
|
+
mode: "broad",
|
|
9086
|
+
inScope: broadCandidates.map(toPrompt),
|
|
9087
|
+
inScopeIds: broadCandidates.map((m) => m.id),
|
|
9088
|
+
skippedIds: candidates.filter((m) => !broadCandidates.some((candidate) => candidate.id === m.id)).map((m) => m.id),
|
|
9089
|
+
broadCycleStartAt,
|
|
9090
|
+
reason: `broad cycle (${broadCandidates.length} remain; started ${broadCycleStartAt})`
|
|
9091
|
+
};
|
|
9092
|
+
}
|
|
8806
9093
|
if (candidates.length === 0) {
|
|
8807
9094
|
return {
|
|
8808
9095
|
runStartedAt,
|
|
@@ -8813,16 +9100,6 @@ async function partitionVerifyScope(args) {
|
|
|
8813
9100
|
reason: "no file-mapped memories in scope"
|
|
8814
9101
|
};
|
|
8815
9102
|
}
|
|
8816
|
-
if (args.forceBroad) {
|
|
8817
|
-
return {
|
|
8818
|
-
runStartedAt,
|
|
8819
|
-
mode: "broad",
|
|
8820
|
-
inScope: candidates.map(toPrompt),
|
|
8821
|
-
inScopeIds: candidates.map((m) => m.id),
|
|
8822
|
-
skippedIds: [],
|
|
8823
|
-
reason: "broad full-pool verification of file-mapped memories"
|
|
8824
|
-
};
|
|
8825
|
-
}
|
|
8826
9103
|
const allInScope = (mode, reason) => ({
|
|
8827
9104
|
runStartedAt,
|
|
8828
9105
|
mode,
|
|
@@ -8941,7 +9218,26 @@ function parseVerifyManifest(text) {
|
|
|
8941
9218
|
|
|
8942
9219
|
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
8943
9220
|
var VERIFY_BATCH_SIZE = 50;
|
|
9221
|
+
var IDENTICAL_PROVIDER_FAILURE_BATCH_LIMIT = 2;
|
|
9222
|
+
function closeBroadCycle(args, cycleStartAt) {
|
|
9223
|
+
if (!args.forceBroad || cycleStartAt === undefined)
|
|
9224
|
+
return;
|
|
9225
|
+
if (!getTaskScheduleState(args.db, args.projectIdentity, "verify-broad"))
|
|
9226
|
+
return;
|
|
9227
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
9228
|
+
const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
9229
|
+
if (current?.lastBroadRunAt !== cycleStartAt)
|
|
9230
|
+
return;
|
|
9231
|
+
if (!current)
|
|
9232
|
+
return;
|
|
9233
|
+
writeTaskScheduleState(args.db, {
|
|
9234
|
+
...current,
|
|
9235
|
+
lastBroadRunAt: null
|
|
9236
|
+
});
|
|
9237
|
+
});
|
|
9238
|
+
}
|
|
8944
9239
|
async function runVerify(args) {
|
|
9240
|
+
const runStartedAt = Date.now();
|
|
8945
9241
|
const result = {
|
|
8946
9242
|
verified: 0,
|
|
8947
9243
|
updated: 0,
|
|
@@ -8956,20 +9252,29 @@ async function runVerify(args) {
|
|
|
8956
9252
|
db: args.db,
|
|
8957
9253
|
projectIdentity: args.projectIdentity,
|
|
8958
9254
|
projectDirectory: args.sessionDirectory,
|
|
8959
|
-
forceBroad: args.forceBroad
|
|
9255
|
+
forceBroad: args.forceBroad,
|
|
9256
|
+
now: runStartedAt,
|
|
9257
|
+
holderId: args.holderId,
|
|
9258
|
+
leaseKey: args.leaseKey
|
|
8960
9259
|
});
|
|
8961
9260
|
result.mode = gate.mode;
|
|
9261
|
+
result.broadCycleStartAt = gate.broadCycleStartAt;
|
|
8962
9262
|
result.inScope = gate.inScope.length;
|
|
8963
9263
|
result.remaining = gate.inScope.length;
|
|
8964
9264
|
log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"} gate: mode=${gate.mode} in_scope=${gate.inScope.length} skipped=${gate.skippedIds.length} reason=${gate.reason}`);
|
|
8965
|
-
if (gate.inScope.length === 0)
|
|
9265
|
+
if (gate.inScope.length === 0) {
|
|
9266
|
+
closeBroadCycle(args, gate.broadCycleStartAt);
|
|
8966
9267
|
return result;
|
|
9268
|
+
}
|
|
8967
9269
|
const batches = [];
|
|
8968
9270
|
for (let i = 0;i < gate.inScope.length; i += VERIFY_BATCH_SIZE) {
|
|
8969
9271
|
batches.push(gate.inScope.slice(i, i + VERIFY_BATCH_SIZE));
|
|
8970
9272
|
}
|
|
8971
9273
|
const abortController = new AbortController;
|
|
8972
9274
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, () => abortController.abort());
|
|
9275
|
+
let consecutiveProviderFailures = 0;
|
|
9276
|
+
let priorProviderFailureFingerprint = null;
|
|
9277
|
+
let lastProviderFailure = null;
|
|
8973
9278
|
try {
|
|
8974
9279
|
for (let i = 0;i < batches.length; i += 1) {
|
|
8975
9280
|
const remainingMs = Math.max(0, args.deadline - Date.now());
|
|
@@ -8984,8 +9289,28 @@ async function runVerify(args) {
|
|
|
8984
9289
|
result.remaining -= counts.verified + counts.updated + counts.archived;
|
|
8985
9290
|
result.batches += 1;
|
|
8986
9291
|
args.onProgress?.(result.verified + result.updated + result.archived);
|
|
9292
|
+
if (counts.providerFailure) {
|
|
9293
|
+
lastProviderFailure = counts.providerFailure;
|
|
9294
|
+
if (counts.providerFailure.fingerprint === priorProviderFailureFingerprint) {
|
|
9295
|
+
consecutiveProviderFailures += 1;
|
|
9296
|
+
} else {
|
|
9297
|
+
priorProviderFailureFingerprint = counts.providerFailure.fingerprint;
|
|
9298
|
+
consecutiveProviderFailures = 1;
|
|
9299
|
+
}
|
|
9300
|
+
if (consecutiveProviderFailures >= IDENTICAL_PROVIDER_FAILURE_BATCH_LIMIT) {
|
|
9301
|
+
log(`[dreamer] verify run aborting after ${consecutiveProviderFailures} identical provider-failure batches`);
|
|
9302
|
+
throw counts.providerFailure;
|
|
9303
|
+
}
|
|
9304
|
+
} else {
|
|
9305
|
+
priorProviderFailureFingerprint = null;
|
|
9306
|
+
consecutiveProviderFailures = 0;
|
|
9307
|
+
}
|
|
8987
9308
|
}
|
|
9309
|
+
if (lastProviderFailure)
|
|
9310
|
+
throw lastProviderFailure;
|
|
8988
9311
|
result.complete = result.remaining === 0;
|
|
9312
|
+
if (result.complete)
|
|
9313
|
+
closeBroadCycle(args, gate.broadCycleStartAt);
|
|
8989
9314
|
log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: verified=${result.verified} updated=${result.updated} archived=${result.archived} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
|
|
8990
9315
|
return result;
|
|
8991
9316
|
} finally {
|
|
@@ -9040,7 +9365,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
9040
9365
|
const text = extractLatestAssistantText(messages);
|
|
9041
9366
|
if (!text)
|
|
9042
9367
|
throw new Error("verify returned no output");
|
|
9043
|
-
|
|
9368
|
+
try {
|
|
9369
|
+
parseVerifyManifest(text);
|
|
9370
|
+
} catch (error) {
|
|
9371
|
+
const providerFailure = providerOutputFailureFromInvalidManifest(messages, text);
|
|
9372
|
+
if (providerFailure)
|
|
9373
|
+
throw providerFailure;
|
|
9374
|
+
throw error;
|
|
9375
|
+
}
|
|
9044
9376
|
return text;
|
|
9045
9377
|
}
|
|
9046
9378
|
});
|
|
@@ -9048,11 +9380,12 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
9048
9380
|
return await applyVerifyManifest(args, batch, run.validated);
|
|
9049
9381
|
} catch (error) {
|
|
9050
9382
|
const desc = describeError(error);
|
|
9051
|
-
|
|
9383
|
+
const providerFailure = error instanceof DreamerProviderOutputFailureError ? error : undefined;
|
|
9384
|
+
log(`[dreamer] verify batch ${providerFailure ? "provider failure" : "failed"}: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
|
|
9052
9385
|
recordInvocation4(args, startedAt, { status: "failed", error });
|
|
9053
9386
|
if (error instanceof DreamerModuleFailureError || signal.aborted)
|
|
9054
9387
|
throw error;
|
|
9055
|
-
return { verified: 0, updated: 0, archived: 0 };
|
|
9388
|
+
return { verified: 0, updated: 0, archived: 0, providerFailure };
|
|
9056
9389
|
} finally {
|
|
9057
9390
|
if (agentSessionId && !shouldKeepSubagents()) {
|
|
9058
9391
|
await args.client.session.delete({
|
|
@@ -9124,7 +9457,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
9124
9457
|
memory_project: args.projectIdentity,
|
|
9125
9458
|
context_store_uuid: args.moduleRoute.moduleContextStoreUuid,
|
|
9126
9459
|
authority_generation: args.moduleRoute.moduleAuthorityGeneration,
|
|
9127
|
-
command_id: `${args.moduleRoute.moduleCommandId}:${
|
|
9460
|
+
command_id: `${args.moduleRoute.moduleCommandId}:${createHash7("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
|
|
9128
9461
|
rows
|
|
9129
9462
|
}
|
|
9130
9463
|
}
|
|
@@ -9319,7 +9652,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
9319
9652
|
resultChars: 0,
|
|
9320
9653
|
...error ? { error } : {},
|
|
9321
9654
|
backlog: (() => {
|
|
9322
|
-
const end = getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9655
|
+
const end = extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9323
9656
|
const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
|
|
9324
9657
|
const value = {
|
|
9325
9658
|
pendingAtStart: backlogAtStart.pending,
|
|
@@ -9368,12 +9701,6 @@ function createDreamTaskExecutor(deps) {
|
|
|
9368
9701
|
recordRun("completed", null);
|
|
9369
9702
|
return { status: "completed" };
|
|
9370
9703
|
}
|
|
9371
|
-
if (moduleRoute) {
|
|
9372
|
-
const reason = "compress-cues parked: MODULE memory authority has no cue write facade";
|
|
9373
|
-
log(`[dreamer] compress-cues: skipped (${reason})`);
|
|
9374
|
-
recordRun("failed", reason);
|
|
9375
|
-
return { status: "failed", transient: true, error: reason };
|
|
9376
|
-
}
|
|
9377
9704
|
const result = await runCompressCues({
|
|
9378
9705
|
db,
|
|
9379
9706
|
client: deps.client,
|
|
@@ -9385,6 +9712,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
9385
9712
|
deadline,
|
|
9386
9713
|
model: config.model ?? deps.mural.model ?? deps.dreamerModel,
|
|
9387
9714
|
fallbackModels: config.fallbackModels,
|
|
9715
|
+
moduleRoute,
|
|
9388
9716
|
onProgress: (processed) => reportProgress(processed)
|
|
9389
9717
|
});
|
|
9390
9718
|
log(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
|
|
@@ -9454,19 +9782,31 @@ function createDreamTaskExecutor(deps) {
|
|
|
9454
9782
|
fallbackModels: config.fallbackModels,
|
|
9455
9783
|
language: config.language ?? deps.language,
|
|
9456
9784
|
moduleRoute,
|
|
9457
|
-
onProgress: (
|
|
9785
|
+
onProgress: (processed2) => reportProgress(processed2)
|
|
9458
9786
|
});
|
|
9787
|
+
const processed = result.verified + result.updated + result.archived;
|
|
9788
|
+
const broadProgress = config.task === "verify-broad" ? `verify-broad cycle ${result.broadCycleStartAt ?? "open"}: verified ${processed}, ${result.remaining} remain` : null;
|
|
9789
|
+
const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
|
|
9459
9790
|
if (!result.complete) {
|
|
9791
|
+
if (broadProgress && processed > 0) {
|
|
9792
|
+
recordRun("completed", broadProgress, {
|
|
9793
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9794
|
+
backlogAfter
|
|
9795
|
+
});
|
|
9796
|
+
return { status: "completed", error: broadProgress };
|
|
9797
|
+
}
|
|
9460
9798
|
const error = incompleteMessage(result.remaining);
|
|
9461
9799
|
recordRun("failed", error, {
|
|
9462
|
-
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
9800
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9801
|
+
backlogAfter
|
|
9463
9802
|
});
|
|
9464
9803
|
return { status: "failed", transient: true, error };
|
|
9465
9804
|
}
|
|
9466
|
-
recordRun("completed",
|
|
9467
|
-
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
9805
|
+
recordRun("completed", broadProgress, {
|
|
9806
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9807
|
+
backlogAfter
|
|
9468
9808
|
});
|
|
9469
|
-
return { status: "completed" };
|
|
9809
|
+
return broadProgress ? { status: "completed", error: broadProgress } : { status: "completed" };
|
|
9470
9810
|
}
|
|
9471
9811
|
if (config.task === "classify-memories") {
|
|
9472
9812
|
let moduleArgs;
|
|
@@ -9654,7 +9994,7 @@ function parseFrictionGateVerdict(verdict) {
|
|
|
9654
9994
|
}
|
|
9655
9995
|
function computeRetrospectiveWindowKey(flagged) {
|
|
9656
9996
|
const anchors = flagged.map((message) => `${message.sessionId}:${message.ts}`).sort().join("|");
|
|
9657
|
-
return
|
|
9997
|
+
return createHash8("sha256").update(anchors).digest("hex").slice(0, 32);
|
|
9658
9998
|
}
|
|
9659
9999
|
function renderFrictionWindow(messages, flaggedOrdinals, radius = 2) {
|
|
9660
10000
|
const flagged = new Set(flaggedOrdinals);
|
|
@@ -12757,513 +13097,6 @@ function replayCavemanCompression(sessionId, db, targets, tags) {
|
|
|
12757
13097
|
return replayed;
|
|
12758
13098
|
}
|
|
12759
13099
|
|
|
12760
|
-
// ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
|
|
12761
|
-
var MAX_MESSAGE_CACHE_ENTRIES = 1e5;
|
|
12762
|
-
var MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
|
|
12763
|
-
var FNV1A_32_OFFSET = 2166136261;
|
|
12764
|
-
var FNV1A_32_PRIME = 16777619;
|
|
12765
|
-
var messageEstimateCache = new Map;
|
|
12766
|
-
var messageEstimateCacheBytes = 0;
|
|
12767
|
-
var EMPTY_BREAKDOWN = {
|
|
12768
|
-
text: 0,
|
|
12769
|
-
reasoning: 0,
|
|
12770
|
-
toolInput: 0,
|
|
12771
|
-
toolOutput: 0,
|
|
12772
|
-
image: 0,
|
|
12773
|
-
other: 0,
|
|
12774
|
-
total: 0
|
|
12775
|
-
};
|
|
12776
|
-
function addBreakdown(target, kind, value) {
|
|
12777
|
-
if (kind === "total")
|
|
12778
|
-
return;
|
|
12779
|
-
const safeValue = Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
|
|
12780
|
-
target[kind] += safeValue;
|
|
12781
|
-
target.total += safeValue;
|
|
12782
|
-
}
|
|
12783
|
-
function estimateStructured(value) {
|
|
12784
|
-
if (typeof value === "string")
|
|
12785
|
-
return estimateTokens(value);
|
|
12786
|
-
if (value === undefined || value === null)
|
|
12787
|
-
return 0;
|
|
12788
|
-
return estimateTokens(stableStringify(value));
|
|
12789
|
-
}
|
|
12790
|
-
function firstStringField(record, fields) {
|
|
12791
|
-
for (const field of fields) {
|
|
12792
|
-
const value = record[field];
|
|
12793
|
-
if (typeof value === "string" && value.length > 0)
|
|
12794
|
-
return value;
|
|
12795
|
-
}
|
|
12796
|
-
return null;
|
|
12797
|
-
}
|
|
12798
|
-
function stringValue(value) {
|
|
12799
|
-
if (typeof value === "string")
|
|
12800
|
-
return value;
|
|
12801
|
-
if (value === undefined || value === null)
|
|
12802
|
-
return "";
|
|
12803
|
-
return stableStringify(value);
|
|
12804
|
-
}
|
|
12805
|
-
function textFromToolResultContent(content) {
|
|
12806
|
-
if (typeof content === "string")
|
|
12807
|
-
return content;
|
|
12808
|
-
if (Array.isArray(content)) {
|
|
12809
|
-
const pieces = [];
|
|
12810
|
-
for (const entry of content) {
|
|
12811
|
-
if (typeof entry === "string") {
|
|
12812
|
-
pieces.push(entry);
|
|
12813
|
-
} else if (isRecord(entry)) {
|
|
12814
|
-
const text = firstStringField(entry, ["text", "content", "value"]);
|
|
12815
|
-
pieces.push(text ?? stableStringify(entry));
|
|
12816
|
-
} else if (entry !== null && entry !== undefined) {
|
|
12817
|
-
pieces.push(String(entry));
|
|
12818
|
-
}
|
|
12819
|
-
}
|
|
12820
|
-
return pieces.join(`
|
|
12821
|
-
`);
|
|
12822
|
-
}
|
|
12823
|
-
return stringValue(content);
|
|
12824
|
-
}
|
|
12825
|
-
function looksImageLike(part) {
|
|
12826
|
-
const type = typeof part.type === "string" ? part.type.toLowerCase() : "";
|
|
12827
|
-
const mime = typeof part.mime === "string" ? part.mime.toLowerCase() : "";
|
|
12828
|
-
const mediaType = typeof part.mediaType === "string" ? part.mediaType.toLowerCase() : "";
|
|
12829
|
-
return type.includes("image") || mime.startsWith("image/") || mediaType.startsWith("image/") || part.image_url !== undefined || part.imageUrl !== undefined || part.image !== undefined;
|
|
12830
|
-
}
|
|
12831
|
-
function defaultImageTokenHeuristic(part) {
|
|
12832
|
-
if (isRecord(part)) {
|
|
12833
|
-
const width = part.width;
|
|
12834
|
-
const height = part.height;
|
|
12835
|
-
if (typeof width === "number" && typeof height === "number" && width > 0 && height > 0) {
|
|
12836
|
-
return Math.max(256, Math.min(4096, Math.ceil(width * height / 750)));
|
|
12837
|
-
}
|
|
12838
|
-
}
|
|
12839
|
-
return 1024;
|
|
12840
|
-
}
|
|
12841
|
-
function partType(part) {
|
|
12842
|
-
return typeof part.type === "string" ? part.type : "";
|
|
12843
|
-
}
|
|
12844
|
-
function hasOwn(record, key) {
|
|
12845
|
-
return Object.hasOwn(record, key);
|
|
12846
|
-
}
|
|
12847
|
-
function recursiveByteLength(value) {
|
|
12848
|
-
if (value === null || value === undefined)
|
|
12849
|
-
return 0;
|
|
12850
|
-
if (typeof value === "string")
|
|
12851
|
-
return value.length;
|
|
12852
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
12853
|
-
return String(value).length;
|
|
12854
|
-
}
|
|
12855
|
-
if (Array.isArray(value)) {
|
|
12856
|
-
return value.reduce((sum, item) => sum + recursiveByteLength(item), value.length);
|
|
12857
|
-
}
|
|
12858
|
-
if (isRecord(value)) {
|
|
12859
|
-
let total = Object.keys(value).length;
|
|
12860
|
-
for (const [key, child] of Object.entries(value)) {
|
|
12861
|
-
total += key.length + recursiveByteLength(child);
|
|
12862
|
-
}
|
|
12863
|
-
return total;
|
|
12864
|
-
}
|
|
12865
|
-
return String(value).length;
|
|
12866
|
-
}
|
|
12867
|
-
function updateFnv1a32(hash, text) {
|
|
12868
|
-
let next = hash;
|
|
12869
|
-
for (let index = 0;index < text.length; index += 1) {
|
|
12870
|
-
next ^= text.charCodeAt(index);
|
|
12871
|
-
next = Math.imul(next, FNV1A_32_PRIME) >>> 0;
|
|
12872
|
-
}
|
|
12873
|
-
return next;
|
|
12874
|
-
}
|
|
12875
|
-
function contentStringsHash(fields) {
|
|
12876
|
-
let hash = FNV1A_32_OFFSET;
|
|
12877
|
-
for (const field of fields) {
|
|
12878
|
-
hash = updateFnv1a32(hash, `${field.length}:`);
|
|
12879
|
-
hash = updateFnv1a32(hash, field);
|
|
12880
|
-
hash = updateFnv1a32(hash, "\x00");
|
|
12881
|
-
}
|
|
12882
|
-
return hash.toString(16).padStart(8, "0");
|
|
12883
|
-
}
|
|
12884
|
-
function rawPartVersion(part) {
|
|
12885
|
-
return part.__magicContextPartUpdatedAt ?? part.updated_at ?? part.updatedAt ?? part.version ?? part.revision ?? "";
|
|
12886
|
-
}
|
|
12887
|
-
function callIdFromPart(part) {
|
|
12888
|
-
const direct = firstStringField(part, ["callID", "callId", "toolCallId", "tool_call_id", "id"]);
|
|
12889
|
-
if (direct)
|
|
12890
|
-
return direct;
|
|
12891
|
-
const state2 = isRecord(part.state) ? part.state : null;
|
|
12892
|
-
return state2 ? firstStringField(state2, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
|
|
12893
|
-
}
|
|
12894
|
-
function toolSignalFromPart(part) {
|
|
12895
|
-
if (!isRecord(part))
|
|
12896
|
-
return null;
|
|
12897
|
-
const type = partType(part);
|
|
12898
|
-
const state2 = isRecord(part.state) ? part.state : null;
|
|
12899
|
-
const callId = callIdFromPart(part);
|
|
12900
|
-
if (!callId && type !== "tool")
|
|
12901
|
-
return null;
|
|
12902
|
-
if (type === "tool") {
|
|
12903
|
-
const hasInput = state2 !== null && hasOwn(state2, "input");
|
|
12904
|
-
const outputKey = state2 ? hasOwn(state2, "output") ? "output" : hasOwn(state2, "error") ? "error" : hasOwn(state2, "result") ? "result" : null : null;
|
|
12905
|
-
const hasOutput = outputKey !== null;
|
|
12906
|
-
const outputValue = outputKey && state2 ? state2[outputKey] : undefined;
|
|
12907
|
-
const providerExecuted = part.providerExecuted === true;
|
|
12908
|
-
const openInvocation = !providerExecuted && !hasOutput;
|
|
12909
|
-
return {
|
|
12910
|
-
callId,
|
|
12911
|
-
hasInput: hasInput || openInvocation,
|
|
12912
|
-
hasOutput,
|
|
12913
|
-
inputText: hasInput && state2 ? stringValue(state2.input) : "",
|
|
12914
|
-
outputText: hasOutput ? stringValue(outputValue) : ""
|
|
12915
|
-
};
|
|
12916
|
-
}
|
|
12917
|
-
if (type === "tool-invocation") {
|
|
12918
|
-
const args = part.args ?? part.input;
|
|
12919
|
-
return {
|
|
12920
|
-
callId,
|
|
12921
|
-
hasInput: args !== undefined,
|
|
12922
|
-
hasOutput: false,
|
|
12923
|
-
inputText: args !== undefined ? stringValue(args) : "",
|
|
12924
|
-
outputText: ""
|
|
12925
|
-
};
|
|
12926
|
-
}
|
|
12927
|
-
if (type === "tool_use") {
|
|
12928
|
-
const input = part.input;
|
|
12929
|
-
return {
|
|
12930
|
-
callId,
|
|
12931
|
-
hasInput: input !== undefined,
|
|
12932
|
-
hasOutput: false,
|
|
12933
|
-
inputText: input !== undefined ? stringValue(input) : "",
|
|
12934
|
-
outputText: ""
|
|
12935
|
-
};
|
|
12936
|
-
}
|
|
12937
|
-
if (type === "tool_result") {
|
|
12938
|
-
const content = part.content ?? part.output ?? part.result;
|
|
12939
|
-
return {
|
|
12940
|
-
callId,
|
|
12941
|
-
hasInput: false,
|
|
12942
|
-
hasOutput: content !== undefined,
|
|
12943
|
-
inputText: "",
|
|
12944
|
-
outputText: content !== undefined ? textFromToolResultContent(content) : ""
|
|
12945
|
-
};
|
|
12946
|
-
}
|
|
12947
|
-
return null;
|
|
12948
|
-
}
|
|
12949
|
-
function partCheapFingerprint(part) {
|
|
12950
|
-
if (!isRecord(part))
|
|
12951
|
-
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
12952
|
-
const version = rawPartVersion(part);
|
|
12953
|
-
const type = typeof part.type === "string" ? part.type : "";
|
|
12954
|
-
return `${type}:${String(version)}:${recursiveByteLength(part)}`;
|
|
12955
|
-
}
|
|
12956
|
-
function messageCacheKey(message, options) {
|
|
12957
|
-
const namespace = "cacheNamespace" in options ? options.cacheNamespace : "estimate";
|
|
12958
|
-
const cheapFingerprint = message.parts.map(partCheapFingerprint).join("|");
|
|
12959
|
-
return [
|
|
12960
|
-
namespace,
|
|
12961
|
-
options.providerShapeVersion,
|
|
12962
|
-
message.id || `ordinal:${message.ordinal}`,
|
|
12963
|
-
message.role,
|
|
12964
|
-
message.parts.length,
|
|
12965
|
-
cheapFingerprint
|
|
12966
|
-
].join("\x00");
|
|
12967
|
-
}
|
|
12968
|
-
function setCachedEstimate(key, breakdown) {
|
|
12969
|
-
const keyEstimateBytes = key.length * 2 + 64;
|
|
12970
|
-
const existing = messageEstimateCache.get(key);
|
|
12971
|
-
if (existing)
|
|
12972
|
-
messageEstimateCacheBytes -= existing.keyEstimateBytes;
|
|
12973
|
-
messageEstimateCache.set(key, { breakdown, keyEstimateBytes });
|
|
12974
|
-
messageEstimateCacheBytes += keyEstimateBytes;
|
|
12975
|
-
while (messageEstimateCache.size > MAX_MESSAGE_CACHE_ENTRIES || messageEstimateCacheBytes > MAX_MESSAGE_CACHE_KEY_BYTES) {
|
|
12976
|
-
const first = messageEstimateCache.keys().next().value;
|
|
12977
|
-
if (typeof first !== "string")
|
|
12978
|
-
break;
|
|
12979
|
-
const removed = messageEstimateCache.get(first);
|
|
12980
|
-
if (removed)
|
|
12981
|
-
messageEstimateCacheBytes -= removed.keyEstimateBytes;
|
|
12982
|
-
messageEstimateCache.delete(first);
|
|
12983
|
-
}
|
|
12984
|
-
}
|
|
12985
|
-
function cloneBreakdown(value) {
|
|
12986
|
-
return { ...value };
|
|
12987
|
-
}
|
|
12988
|
-
function estimateNonToolPart(part, options, breakdown) {
|
|
12989
|
-
if (!isRecord(part)) {
|
|
12990
|
-
if (part !== null && part !== undefined)
|
|
12991
|
-
addBreakdown(breakdown, "other", estimateStructured(part));
|
|
12992
|
-
return true;
|
|
12993
|
-
}
|
|
12994
|
-
const type = partType(part);
|
|
12995
|
-
if (type === "step-start" || type === "step-finish" || type === "meta" && Object.keys(part).length <= 1) {
|
|
12996
|
-
return true;
|
|
12997
|
-
}
|
|
12998
|
-
if (type === "text") {
|
|
12999
|
-
const text = firstStringField(part, ["text", "content"]);
|
|
13000
|
-
if (text)
|
|
13001
|
-
addBreakdown(breakdown, "text", estimateTokens(text));
|
|
13002
|
-
return true;
|
|
13003
|
-
}
|
|
13004
|
-
if (type === "reasoning" || type === "thinking" || type === "redacted_thinking") {
|
|
13005
|
-
const text = firstStringField(part, ["thinking", "text", "content", "reasoning"]);
|
|
13006
|
-
if (text) {
|
|
13007
|
-
addBreakdown(breakdown, "reasoning", estimateTokens(text));
|
|
13008
|
-
} else {
|
|
13009
|
-
addBreakdown(breakdown, "other", estimateStructured(part));
|
|
13010
|
-
}
|
|
13011
|
-
return true;
|
|
13012
|
-
}
|
|
13013
|
-
const reasoningText = firstStringField(part, ["thinking", "reasoning"]);
|
|
13014
|
-
if (reasoningText && type.length === 0) {
|
|
13015
|
-
addBreakdown(breakdown, "reasoning", estimateTokens(reasoningText));
|
|
13016
|
-
return true;
|
|
13017
|
-
}
|
|
13018
|
-
if (looksImageLike(part)) {
|
|
13019
|
-
addBreakdown(breakdown, "image", options.imageTokenHeuristic?.(part) ?? defaultImageTokenHeuristic(part));
|
|
13020
|
-
const altText = firstStringField(part, ["alt", "text", "description"]);
|
|
13021
|
-
if (altText)
|
|
13022
|
-
addBreakdown(breakdown, "text", estimateTokens(altText));
|
|
13023
|
-
return true;
|
|
13024
|
-
}
|
|
13025
|
-
if (type.includes("file") || type === "source") {
|
|
13026
|
-
const content = firstStringField(part, ["content", "text", "source"]);
|
|
13027
|
-
if (content)
|
|
13028
|
-
addBreakdown(breakdown, "text", estimateTokens(content));
|
|
13029
|
-
else
|
|
13030
|
-
addBreakdown(breakdown, "other", estimateStructured(part));
|
|
13031
|
-
return true;
|
|
13032
|
-
}
|
|
13033
|
-
return false;
|
|
13034
|
-
}
|
|
13035
|
-
function estimateTrueRawMessageTokens(message, options) {
|
|
13036
|
-
const breakdown = cloneBreakdown(EMPTY_BREAKDOWN);
|
|
13037
|
-
const countedInput = new Set;
|
|
13038
|
-
const countedOutput = new Set;
|
|
13039
|
-
let ordinalToolIndex = 0;
|
|
13040
|
-
for (const part of message.parts) {
|
|
13041
|
-
const signal = toolSignalFromPart(part);
|
|
13042
|
-
if (signal) {
|
|
13043
|
-
const localKey = `${signal.callId || "tool"}:${message.ordinal}:${ordinalToolIndex}`;
|
|
13044
|
-
ordinalToolIndex += 1;
|
|
13045
|
-
if (signal.hasInput) {
|
|
13046
|
-
const key = `${signal.callId}:input:${message.ordinal}`;
|
|
13047
|
-
if (!countedInput.has(key)) {
|
|
13048
|
-
countedInput.add(key);
|
|
13049
|
-
addBreakdown(breakdown, "toolInput", estimateTokens(signal.inputText));
|
|
13050
|
-
}
|
|
13051
|
-
}
|
|
13052
|
-
if (signal.hasOutput) {
|
|
13053
|
-
const key = `${signal.callId}:output:${message.ordinal}:${localKey}`;
|
|
13054
|
-
if (!countedOutput.has(key)) {
|
|
13055
|
-
countedOutput.add(key);
|
|
13056
|
-
addBreakdown(breakdown, "toolOutput", estimateTokens(signal.outputText));
|
|
13057
|
-
}
|
|
13058
|
-
}
|
|
13059
|
-
continue;
|
|
13060
|
-
}
|
|
13061
|
-
if (!estimateNonToolPart(part, options, breakdown)) {
|
|
13062
|
-
addBreakdown(breakdown, "other", estimateStructured(part));
|
|
13063
|
-
}
|
|
13064
|
-
}
|
|
13065
|
-
return breakdown;
|
|
13066
|
-
}
|
|
13067
|
-
function buildToolArcs(messages) {
|
|
13068
|
-
const openQueues = new Map;
|
|
13069
|
-
const arcs = [];
|
|
13070
|
-
for (const message of messages) {
|
|
13071
|
-
for (const part of message.parts) {
|
|
13072
|
-
const signal = toolSignalFromPart(part);
|
|
13073
|
-
if (!signal || signal.callId.length === 0)
|
|
13074
|
-
continue;
|
|
13075
|
-
if (signal.hasInput && signal.hasOutput) {
|
|
13076
|
-
arcs.push({
|
|
13077
|
-
callId: signal.callId,
|
|
13078
|
-
invOrdinal: message.ordinal,
|
|
13079
|
-
resOrdinal: message.ordinal
|
|
13080
|
-
});
|
|
13081
|
-
continue;
|
|
13082
|
-
}
|
|
13083
|
-
if (signal.hasInput) {
|
|
13084
|
-
const queue = openQueues.get(signal.callId) ?? [];
|
|
13085
|
-
queue.push(message.ordinal);
|
|
13086
|
-
openQueues.set(signal.callId, queue);
|
|
13087
|
-
continue;
|
|
13088
|
-
}
|
|
13089
|
-
if (signal.hasOutput) {
|
|
13090
|
-
const queue = openQueues.get(signal.callId) ?? [];
|
|
13091
|
-
const invOrdinal = queue.shift();
|
|
13092
|
-
if (queue.length === 0)
|
|
13093
|
-
openQueues.delete(signal.callId);
|
|
13094
|
-
else
|
|
13095
|
-
openQueues.set(signal.callId, queue);
|
|
13096
|
-
if (invOrdinal !== undefined) {
|
|
13097
|
-
arcs.push({ callId: signal.callId, invOrdinal, resOrdinal: message.ordinal });
|
|
13098
|
-
}
|
|
13099
|
-
}
|
|
13100
|
-
}
|
|
13101
|
-
}
|
|
13102
|
-
for (const [callId, queue] of openQueues) {
|
|
13103
|
-
for (const invOrdinal of queue) {
|
|
13104
|
-
arcs.push({ callId, invOrdinal, resOrdinal: null });
|
|
13105
|
-
}
|
|
13106
|
-
}
|
|
13107
|
-
return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
|
|
13108
|
-
}
|
|
13109
|
-
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
|
|
13110
|
-
let boundary = candidate;
|
|
13111
|
-
for (const arc of arcs) {
|
|
13112
|
-
if (arc.resOrdinal !== null) {
|
|
13113
|
-
if (arc.invOrdinal < boundary && boundary <= arc.resOrdinal) {
|
|
13114
|
-
boundary = arc.resOrdinal + 1;
|
|
13115
|
-
}
|
|
13116
|
-
continue;
|
|
13117
|
-
}
|
|
13118
|
-
if (arc.invOrdinal < recentOpenArcCutoff)
|
|
13119
|
-
continue;
|
|
13120
|
-
if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
|
|
13121
|
-
return arc.invOrdinal;
|
|
13122
|
-
}
|
|
13123
|
-
if (arc.invOrdinal >= boundary) {
|
|
13124
|
-
return arc.invOrdinal;
|
|
13125
|
-
}
|
|
13126
|
-
}
|
|
13127
|
-
return boundary;
|
|
13128
|
-
}
|
|
13129
|
-
function tokenForMessage(message, options) {
|
|
13130
|
-
const key = messageCacheKey(message, options);
|
|
13131
|
-
const cached = messageEstimateCache.get(key);
|
|
13132
|
-
if (cached)
|
|
13133
|
-
return cloneBreakdown(cached.breakdown);
|
|
13134
|
-
const breakdown = estimateTrueRawMessageTokens(message, options);
|
|
13135
|
-
setCachedEstimate(key, breakdown);
|
|
13136
|
-
return cloneBreakdown(breakdown);
|
|
13137
|
-
}
|
|
13138
|
-
function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
13139
|
-
const ordered = [...messages].sort((a, b) => a.ordinal - b.ordinal);
|
|
13140
|
-
const sliceCount = ordered.length;
|
|
13141
|
-
const firstOrdinal = ordered.length > 0 ? ordered[0].ordinal : 1;
|
|
13142
|
-
const terminalOrdinal = ordered.length > 0 ? ordered[ordered.length - 1].ordinal : 0;
|
|
13143
|
-
const rawMessageCount = Math.max(sliceCount, terminalOrdinal, options.absoluteMessageCount ?? sliceCount);
|
|
13144
|
-
const ordinalSpan = terminalOrdinal >= firstOrdinal ? terminalOrdinal - firstOrdinal + 1 : 0;
|
|
13145
|
-
const tokensByOrdinal = new Map;
|
|
13146
|
-
const idsByOrdinal = new Map;
|
|
13147
|
-
const prefix = new Array(ordinalSpan + 1).fill(0);
|
|
13148
|
-
for (const message of ordered) {
|
|
13149
|
-
const stored = options.storedTotalForMessage?.(message);
|
|
13150
|
-
const total = stored !== undefined && stored !== null ? stored : tokenForMessage(message, options).total;
|
|
13151
|
-
tokensByOrdinal.set(message.ordinal, total);
|
|
13152
|
-
idsByOrdinal.set(message.ordinal, message.id);
|
|
13153
|
-
const relative = message.ordinal - firstOrdinal + 1;
|
|
13154
|
-
if (relative >= 1 && relative <= ordinalSpan) {
|
|
13155
|
-
prefix[relative] = total;
|
|
13156
|
-
}
|
|
13157
|
-
}
|
|
13158
|
-
for (let k = 1;k <= ordinalSpan; k += 1) {
|
|
13159
|
-
prefix[k] += prefix[k - 1];
|
|
13160
|
-
}
|
|
13161
|
-
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(ordinalSpan, ordinal - firstOrdinal));
|
|
13162
|
-
return {
|
|
13163
|
-
sessionId,
|
|
13164
|
-
providerShapeVersion: options.providerShapeVersion,
|
|
13165
|
-
rawMessageCount,
|
|
13166
|
-
tokenForOrdinal(ordinal) {
|
|
13167
|
-
return tokensByOrdinal.get(ordinal) ?? 0;
|
|
13168
|
-
},
|
|
13169
|
-
messageIdAtOrdinal(ordinal) {
|
|
13170
|
-
return idsByOrdinal.get(ordinal) ?? null;
|
|
13171
|
-
},
|
|
13172
|
-
suffixTokensFromOrdinal(ordinal) {
|
|
13173
|
-
if (ordinal <= firstOrdinal)
|
|
13174
|
-
return prefix[ordinalSpan];
|
|
13175
|
-
if (ordinal > terminalOrdinal)
|
|
13176
|
-
return 0;
|
|
13177
|
-
return prefix[ordinalSpan] - prefix[ordinalToIndex(ordinal)];
|
|
13178
|
-
},
|
|
13179
|
-
rangeTokens(startInclusive, endExclusive) {
|
|
13180
|
-
const start = Math.max(firstOrdinal, startInclusive);
|
|
13181
|
-
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
13182
|
-
return prefix[end - firstOrdinal] - prefix[start - firstOrdinal];
|
|
13183
|
-
},
|
|
13184
|
-
findSuffixStartForTokens(tokens) {
|
|
13185
|
-
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
13186
|
-
return terminalOrdinal + 1;
|
|
13187
|
-
const target = Math.max(0, Math.floor(tokens));
|
|
13188
|
-
const total = prefix[ordinalSpan];
|
|
13189
|
-
if (total < target)
|
|
13190
|
-
return firstOrdinal;
|
|
13191
|
-
const cut = total - target;
|
|
13192
|
-
let lo = 0;
|
|
13193
|
-
let hi = ordinalSpan;
|
|
13194
|
-
let best = 0;
|
|
13195
|
-
while (lo <= hi) {
|
|
13196
|
-
const mid = lo + hi >> 1;
|
|
13197
|
-
if (prefix[mid] <= cut) {
|
|
13198
|
-
best = mid;
|
|
13199
|
-
lo = mid + 1;
|
|
13200
|
-
} else {
|
|
13201
|
-
hi = mid - 1;
|
|
13202
|
-
}
|
|
13203
|
-
}
|
|
13204
|
-
return firstOrdinal + best;
|
|
13205
|
-
},
|
|
13206
|
-
findHeadEndForCap(startInclusive, endExclusive, capTokens) {
|
|
13207
|
-
const start = Math.max(firstOrdinal, Math.min(terminalOrdinal + 1, startInclusive));
|
|
13208
|
-
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
13209
|
-
if (!Number.isFinite(capTokens) || capTokens <= 0)
|
|
13210
|
-
return start;
|
|
13211
|
-
const startIndex = start - firstOrdinal;
|
|
13212
|
-
const endIndex = end - firstOrdinal;
|
|
13213
|
-
const cut = prefix[startIndex] + Math.floor(capTokens);
|
|
13214
|
-
let lo = startIndex + 1;
|
|
13215
|
-
let hi = endIndex;
|
|
13216
|
-
let bestEndIndex = startIndex;
|
|
13217
|
-
while (lo <= hi) {
|
|
13218
|
-
const mid = lo + hi >> 1;
|
|
13219
|
-
if (prefix[mid] <= cut) {
|
|
13220
|
-
bestEndIndex = mid;
|
|
13221
|
-
lo = mid + 1;
|
|
13222
|
-
} else {
|
|
13223
|
-
hi = mid - 1;
|
|
13224
|
-
}
|
|
13225
|
-
}
|
|
13226
|
-
let bestEnd = firstOrdinal + bestEndIndex;
|
|
13227
|
-
if (bestEnd === start && start < end)
|
|
13228
|
-
bestEnd = start + 1;
|
|
13229
|
-
return Math.min(bestEnd, end);
|
|
13230
|
-
}
|
|
13231
|
-
};
|
|
13232
|
-
}
|
|
13233
|
-
function partContentFingerprint(part) {
|
|
13234
|
-
if (!isRecord(part))
|
|
13235
|
-
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
13236
|
-
const tool = toolSignalFromPart(part);
|
|
13237
|
-
if (tool) {
|
|
13238
|
-
return contentStringsHash([tool.inputText, tool.outputText]);
|
|
13239
|
-
}
|
|
13240
|
-
const text = firstStringField(part, ["text", "thinking", "reasoning", "content", "url"]) ?? "";
|
|
13241
|
-
return contentStringsHash([text]);
|
|
13242
|
-
}
|
|
13243
|
-
function computeRawRangeFingerprint(messages, startInclusive, endExclusive) {
|
|
13244
|
-
const pieces = [];
|
|
13245
|
-
for (const message of messages) {
|
|
13246
|
-
if (message.ordinal < startInclusive || message.ordinal >= endExclusive)
|
|
13247
|
-
continue;
|
|
13248
|
-
const partFingerprint = message.parts.map(partContentFingerprint).join(",");
|
|
13249
|
-
pieces.push(`${message.ordinal}:${message.id}:${message.parts.length}:${partFingerprint}`);
|
|
13250
|
-
}
|
|
13251
|
-
return pieces.join("|");
|
|
13252
|
-
}
|
|
13253
|
-
function invalidateTrueRawTokenCache(args) {
|
|
13254
|
-
const sessionNeedle = args.sessionId ? `${args.sessionId}` : null;
|
|
13255
|
-
const messageNeedle = args.messageId ? `\x00${args.messageId}\x00` : null;
|
|
13256
|
-
for (const [key, value] of messageEstimateCache) {
|
|
13257
|
-
const sessionMatches = sessionNeedle === null || key.includes(sessionNeedle);
|
|
13258
|
-
const messageMatches = messageNeedle === null || key.includes(messageNeedle);
|
|
13259
|
-
if (sessionMatches && messageMatches) {
|
|
13260
|
-
messageEstimateCache.delete(key);
|
|
13261
|
-
messageEstimateCacheBytes -= value.keyEstimateBytes;
|
|
13262
|
-
}
|
|
13263
|
-
}
|
|
13264
|
-
args.reason;
|
|
13265
|
-
}
|
|
13266
|
-
|
|
13267
13100
|
// ../plugin/src/hooks/magic-context/protected-tail-boundary.ts
|
|
13268
13101
|
var ALPHA = 0.3;
|
|
13269
13102
|
var FLOOR_RATIO = 0.08;
|
|
@@ -13384,7 +13217,7 @@ function fenceWrapupBoundaryForToolArcs(args) {
|
|
|
13384
13217
|
if (arc.resOrdinal === null) {
|
|
13385
13218
|
continue;
|
|
13386
13219
|
}
|
|
13387
|
-
if (arc.invOrdinal >= args.lastCompartmentEndOrdinal + 1 && arc.invOrdinal
|
|
13220
|
+
if (arc.invOrdinal >= args.lastCompartmentEndOrdinal + 1 && completedToolArcCrossesBoundary(arc.invOrdinal, arc.resOrdinal, next)) {
|
|
13388
13221
|
next = arc.invOrdinal;
|
|
13389
13222
|
}
|
|
13390
13223
|
}
|
|
@@ -14401,7 +14234,7 @@ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
|
14401
14234
|
}
|
|
14402
14235
|
|
|
14403
14236
|
// ../plugin/src/shared/tag-transcript.ts
|
|
14404
|
-
import { createHash as
|
|
14237
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
14405
14238
|
|
|
14406
14239
|
// ../plugin/src/hooks/magic-context/image-token-estimate.ts
|
|
14407
14240
|
var IMAGE_TOKEN_DIVISOR = 750;
|
|
@@ -14556,7 +14389,7 @@ function readUint32BE(b, offset) {
|
|
|
14556
14389
|
// ../plugin/src/shared/tag-transcript.ts
|
|
14557
14390
|
var TEXT_TAG_IDENTITY_MARKER = ":mc-text-v1:";
|
|
14558
14391
|
function textIdentityDigest(value) {
|
|
14559
|
-
return
|
|
14392
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
14560
14393
|
}
|
|
14561
14394
|
function buildContentDerivedTextIds(messageId, parts) {
|
|
14562
14395
|
const sources = parts.filter((part) => part.kind === "text").map((part) => stripTagPrefix(part.getText() ?? ""));
|
|
@@ -18443,6 +18276,7 @@ function mapParsedCompartmentsToChunk(compartments, chunk, sequenceOffset) {
|
|
|
18443
18276
|
|
|
18444
18277
|
// ../plugin/src/hooks/magic-context/compartment-runner-validation.ts
|
|
18445
18278
|
var MIN_RECOMP_CHUNK_TOKEN_BUDGET = 20;
|
|
18279
|
+
var HISTORIAN_BOUNDARY_HEALING_SLACK = 2;
|
|
18446
18280
|
function healCompartmentGaps(compartments, toolOnlyRanges = []) {
|
|
18447
18281
|
for (let i = 1;i < compartments.length; i++) {
|
|
18448
18282
|
const prev = compartments[i - 1];
|
|
@@ -18458,6 +18292,36 @@ function healCompartmentGaps(compartments, toolOnlyRanges = []) {
|
|
|
18458
18292
|
}
|
|
18459
18293
|
}
|
|
18460
18294
|
}
|
|
18295
|
+
function boundarySplitsCompletedToolArc(boundary, arcs = []) {
|
|
18296
|
+
return arcs.some((arc) => completedToolArcCrossesBoundary(arc.start, arc.end, boundary));
|
|
18297
|
+
}
|
|
18298
|
+
function healTerminalCompletedToolArc(compartments, unprocessedFrom, arcs = [], chunkEnd) {
|
|
18299
|
+
const last = compartments[compartments.length - 1];
|
|
18300
|
+
if (!last)
|
|
18301
|
+
return unprocessedFrom;
|
|
18302
|
+
const originalEnd = last.endMessage;
|
|
18303
|
+
for (let pass = 0;pass <= arcs.length; pass += 1) {
|
|
18304
|
+
const boundary = last.endMessage + 1;
|
|
18305
|
+
let nextEnd = last.endMessage;
|
|
18306
|
+
for (const arc of arcs) {
|
|
18307
|
+
if (arc.end <= chunkEnd && completedToolArcCrossesBoundary(arc.start, arc.end, boundary)) {
|
|
18308
|
+
nextEnd = Math.max(nextEnd, arc.end);
|
|
18309
|
+
}
|
|
18310
|
+
}
|
|
18311
|
+
if (nextEnd === last.endMessage)
|
|
18312
|
+
break;
|
|
18313
|
+
last.endMessage = nextEnd;
|
|
18314
|
+
}
|
|
18315
|
+
return last.endMessage !== originalEnd && unprocessedFrom !== null ? last.endMessage + 1 : unprocessedFrom;
|
|
18316
|
+
}
|
|
18317
|
+
function shouldDiscardLastHistorianCompartment(compartments, chunk) {
|
|
18318
|
+
if (compartments.length < 2)
|
|
18319
|
+
return false;
|
|
18320
|
+
const last = compartments[compartments.length - 1];
|
|
18321
|
+
const previous = compartments[compartments.length - 2];
|
|
18322
|
+
const lookaheadMargin = chunk.endIndex - last.endMessage;
|
|
18323
|
+
return lookaheadMargin <= HISTORIAN_BOUNDARY_HEALING_SLACK && !boundarySplitsCompletedToolArc(previous.endMessage + 1, chunk.completedToolArcs);
|
|
18324
|
+
}
|
|
18461
18325
|
function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, sequenceOffset) {
|
|
18462
18326
|
const parsed = parseCompartmentOutput(text);
|
|
18463
18327
|
if (parsed.compartments.length === 0) {
|
|
@@ -18467,6 +18331,7 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
|
|
|
18467
18331
|
};
|
|
18468
18332
|
}
|
|
18469
18333
|
healCompartmentGaps(parsed.compartments, chunk.toolOnlyRanges);
|
|
18334
|
+
parsed.unprocessedFrom = healTerminalCompletedToolArc(parsed.compartments, parsed.unprocessedFrom, chunk.completedToolArcs, chunk.endIndex);
|
|
18470
18335
|
const mapped = mapParsedCompartmentsToChunk(parsed.compartments, chunk, sequenceOffset);
|
|
18471
18336
|
if (!mapped.ok) {
|
|
18472
18337
|
return {
|
|
@@ -18481,6 +18346,13 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
|
|
|
18481
18346
|
error: `Historian returned invalid compartment output: ${parsedValidationError}`
|
|
18482
18347
|
};
|
|
18483
18348
|
}
|
|
18349
|
+
const last = parsed.compartments[parsed.compartments.length - 1];
|
|
18350
|
+
if (last && boundarySplitsCompletedToolArc(last.endMessage + 1, chunk.completedToolArcs)) {
|
|
18351
|
+
return {
|
|
18352
|
+
ok: false,
|
|
18353
|
+
error: "Historian terminal boundary splits a completed tool invocation/result arc"
|
|
18354
|
+
};
|
|
18355
|
+
}
|
|
18484
18356
|
return {
|
|
18485
18357
|
ok: true,
|
|
18486
18358
|
compartments: mapped.compartments,
|
|
@@ -20910,17 +20782,14 @@ ${chunkText}`,
|
|
|
20910
20782
|
}
|
|
20911
20783
|
}
|
|
20912
20784
|
}
|
|
20913
|
-
const BOUNDARY_HEALING_SLACK = 2;
|
|
20914
20785
|
const inEmergency = getOverflowState(db, sessionId).needsEmergencyRecovery;
|
|
20915
20786
|
const emittedCompartments = validatedPass.compartments;
|
|
20916
20787
|
let newCompartments = emittedCompartments;
|
|
20917
|
-
if (!inEmergency && !forceKeepLastCompartmentForChunk && emittedCompartments
|
|
20788
|
+
if (!inEmergency && !forceKeepLastCompartmentForChunk && shouldDiscardLastHistorianCompartment(emittedCompartments, chunk)) {
|
|
20918
20789
|
const lastEmitted = emittedCompartments[emittedCompartments.length - 1];
|
|
20919
20790
|
const lookaheadMargin = chunk.endIndex - lastEmitted.endMessage;
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
sessionLog(sessionId, `historian discard-last: dropped provisional compartment ${lastEmitted.startMessage}-${lastEmitted.endMessage} (lookaheadMargin=${lookaheadMargin} <= ${BOUNDARY_HEALING_SLACK}); will re-derive next run`);
|
|
20923
|
-
}
|
|
20791
|
+
newCompartments = emittedCompartments.slice(0, -1);
|
|
20792
|
+
sessionLog(sessionId, `historian discard-last: dropped provisional compartment ${lastEmitted.startMessage}-${lastEmitted.endMessage} (lookaheadMargin=${lookaheadMargin} <= ${HISTORIAN_BOUNDARY_HEALING_SLACK}); will re-derive next run`);
|
|
20924
20793
|
}
|
|
20925
20794
|
const lastNewEnd = newCompartments[newCompartments.length - 1]?.endMessage ?? 0;
|
|
20926
20795
|
if (lastNewEnd + 1 <= offset) {
|
|
@@ -21717,7 +21586,7 @@ function stripPiProcessedImages(args) {
|
|
|
21717
21586
|
}
|
|
21718
21587
|
|
|
21719
21588
|
// src/system-prompt.ts
|
|
21720
|
-
import { createHash as
|
|
21589
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
21721
21590
|
|
|
21722
21591
|
// ../plugin/src/agents/magic-context-prompt.ts
|
|
21723
21592
|
var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
|
|
@@ -21733,6 +21602,10 @@ var PARTNER_FRAME_CLOSER_REDUCE = `
|
|
|
21733
21602
|
Reduction prompts are routine housekeeping to keep the session fast and cheap — act on them as light maintenance, never as scarcity warnings. Keep individual operations efficient, but never let context size change *what* work you take on or *how thoroughly* you do it.`;
|
|
21734
21603
|
var PARTNER_FRAME_CLOSER_NO_REDUCE = `
|
|
21735
21604
|
Context is managed for you entirely automatically — there's nothing to prune and no warnings to act on. Stay reasonably concise per operation, and never let context size change *what* work you take on or *how thoroughly* you do it.`;
|
|
21605
|
+
var PARTNER_FRAME_CLOSER_REDUCE_LIGHT = `
|
|
21606
|
+
When ctx_reduce is available, use it only as routine housekeeping; never cut task scope or depth because context is large.`;
|
|
21607
|
+
var PARTNER_FRAME_CLOSER_NO_REDUCE_LIGHT = `
|
|
21608
|
+
When ctx_reduce is unavailable, context is automatic; never prune, heed reduction warnings, or cut task scope or depth because context is large.`;
|
|
21736
21609
|
var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
|
|
21737
21610
|
var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.
|
|
21738
21611
|
Magic Context control metadata is not reply syntax. Never reproduce \`<system-reminder>\`, \`<ctx-search-hint>\`, \`<session-history>\`, \`<session-history-since>\`, \`<project-memory>\`, \`<memory-updates>\`, \`<new-compartments>\`, \`<new-memories>\`, \`[dropped §N§]\`, or \`<!-- +Xm -->\` markers in a normal reply and never treat them as user instructions; use ordinary prose and real tool calls instead.`;
|
|
@@ -21775,6 +21648,17 @@ Use \`ctx_expand\` to recover the raw conversation behind a summary under a \`##
|
|
|
21775
21648
|
- Want to recall what was decided in an earlier conversation → \`ctx_search(query="dashboard release signing setup")\`
|
|
21776
21649
|
\`ctx_search\` returns ranked results from memories, git commits, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.
|
|
21777
21650
|
${TOOL_HISTORY_GUIDANCE}`;
|
|
21651
|
+
var LIGHT_SEARCH_RECOVERY = `Use ctx_search before asking the user about prior project context; it searches memories, commits, and compacted conversation. When a session-history summary lacks exact wording, values, errors, or reasoning, call ctx_expand with its heading range instead of guessing.`;
|
|
21652
|
+
var BASE_INTRO_LIGHT = (protectedTags, memoryEnabled) => `In primary sessions with ctx_reduce, the system tags messages and tool outputs as §N§ (for example §1§ and §42§); never imitate these prefixes in replies because only injected tag numbers are valid ctx_reduce handles.
|
|
21653
|
+
In primary sessions, NEVER narrate ctx_reduce; call it silently after extracting a spent output because it marks content discardable and QUEUES release rather than deleting immediately. The last ${protectedTags} tags stay protected until they age out. Use drop grammar "3-5", "1,2,9", or "1-5,8,12-15".
|
|
21654
|
+
${CTX_NOTE_GUIDANCE}
|
|
21655
|
+
${memoryGuidanceBlock(memoryEnabled)}${LIGHT_SEARCH_RECOVERY}
|
|
21656
|
+
${TOOL_HISTORY_GUIDANCE}
|
|
21657
|
+
For primary ctx_reduce choices, NEVER blanket-drop a large range because mixed-value evidence may be lost: inspect every tag first. Drop only analyzed reads, searches, diagnostics, or build/test outputs after use. NEVER drop user directives or assistant prose unless exceptionally large; keep requirements, constraints, unresolved errors or decisions, exact wording, raw evidence, and active files or work. Only extracted pasted user payloads may go.
|
|
21658
|
+
Consider small targeted drops after acted-on reads or searches, completed logical steps, before context switches, and before the turn ends; this keeps the working set tidy without changing task scope.`;
|
|
21659
|
+
var BASE_INTRO_NO_REDUCE_LIGHT = (memoryEnabled) => `${CTX_NOTE_GUIDANCE}
|
|
21660
|
+
${memoryGuidanceBlock(memoryEnabled)}${LIGHT_SEARCH_RECOVERY}
|
|
21661
|
+
${TOOL_HISTORY_GUIDANCE}`;
|
|
21778
21662
|
var GENERIC_SECTION = `
|
|
21779
21663
|
### Reduction Triggers
|
|
21780
21664
|
- After reading files or search results you already acted on — drop raw outputs.
|
|
@@ -21791,21 +21675,27 @@ var GENERIC_SECTION = `
|
|
|
21791
21675
|
- Your current task requirements and constraints.
|
|
21792
21676
|
- Recent errors and unresolved decisions.
|
|
21793
21677
|
- Active work context and files being edited.`;
|
|
21678
|
+
var SMART_NOTE_GUIDANCE_LIGHT = `
|
|
21679
|
+
surface_condition creates a smart note checked nightly against external signals on ctx_note write.`;
|
|
21794
21680
|
var TEMPORAL_AWARENESS_GUIDANCE = `
|
|
21795
21681
|
**Temporal awareness**: User messages may be preceded by HTML comments like \`<!-- +12m -->\`, \`<!-- +2h 15m -->\`, or \`<!-- +3d 4h -->\` indicating time elapsed since the previous message's completion. Compartments in \`<session-history>\` carry \`start-date\` and \`end-date\` attributes (YYYY-MM-DD) showing real-time boundaries. Use these when reasoning about workflow pacing, log durations, build times, or how long ago something happened.`;
|
|
21796
21682
|
var SUBAGENT_REDUCE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
|
|
21797
21683
|
Use \`ctx_reduce\` to drop tool outputs you have already finished with, keeping your working context lean. Syntax: "3-5", "1,2,9", or "1-5,8,12-15". The last ${protectedTags} tags are protected.
|
|
21798
21684
|
Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-50"); review each tag first. Do not drop user or assistant text messages — only large tool outputs are worth dropping.
|
|
21799
21685
|
Older tool calls may show \`[dropped §N§]\` sentinels; that is normal context management, not a pattern to copy. ALWAYS make fresh real tool calls when you need data again; never fabricate or inline tool output.`;
|
|
21686
|
+
var SUBAGENT_REDUCE_INTRO_LIGHT = (protectedTags) => `In bounded subagent sessions, the system tags messages and tool outputs as §N§; use only those IDs in ctx_reduce drop ranges such as "3-5", "1,2,9", or "1-5,8,12-15". The last ${protectedTags} tags stay protected.
|
|
21687
|
+
When dropping, do it silently and NEVER choose a large range before reviewing every tag; drop only finished large tool outputs, never user or assistant messages.
|
|
21688
|
+
If older calls show [dropped §N§], never copy that system sentinel because it is not reply syntax; make a fresh real tool call and never fabricate or inline output.`;
|
|
21800
21689
|
var CAVEMAN_COMPRESSION_WARNING = `
|
|
21801
21690
|
**BEWARE**: History compression is on; older user AND assistant text — including your own earlier responses — has been deterministically rewritten in a terse caveman style (dropped articles, missing auxiliaries, \`//\` instead of connectives like \`because\`). This is automatic context compression that runs after the fact, not your actual prior wording or the user's. **DO NOT mimic this style in new turns.** Write fresh responses in normal prose. If you notice your output drifting into caveman cadence, that drift is in-context-learning bleeding from the compressed history — consciously revert to full sentences.`;
|
|
21802
|
-
function buildMagicContextSection(_agent, protectedTags, ctxReduceCallable = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language, memoryEnabled = true) {
|
|
21691
|
+
function buildMagicContextSection(_agent, protectedTags, ctxReduceCallable = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language, memoryEnabled = true, preset = "full", primaryOverride) {
|
|
21803
21692
|
if (subagentMode) {
|
|
21693
|
+
const intro = preset === "light" ? SUBAGENT_REDUCE_INTRO_LIGHT(protectedTags) : SUBAGENT_REDUCE_INTRO(protectedTags);
|
|
21804
21694
|
return `## Magic Context
|
|
21805
21695
|
|
|
21806
|
-
${
|
|
21696
|
+
${intro}`;
|
|
21807
21697
|
}
|
|
21808
|
-
const smartNoteGuidance = dreamerEnabled ? `
|
|
21698
|
+
const smartNoteGuidance = dreamerEnabled ? preset === "light" ? SMART_NOTE_GUIDANCE_LIGHT : `
|
|
21809
21699
|
When \`surface_condition\` is provided with \`write\`, the note becomes a project-scoped smart note.
|
|
21810
21700
|
The dreamer evaluates smart note conditions during nightly runs and surfaces them when conditions are met.
|
|
21811
21701
|
Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
|
|
@@ -21815,13 +21705,32 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
|
|
|
21815
21705
|
const languageGuidance = languageDirective ? `
|
|
21816
21706
|
|
|
21817
21707
|
${languageDirective}` : "";
|
|
21708
|
+
if (primaryOverride !== undefined) {
|
|
21709
|
+
return `${primaryOverride}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
|
|
21710
|
+
}
|
|
21818
21711
|
if (!ctxReduceCallable) {
|
|
21712
|
+
if (preset === "light") {
|
|
21713
|
+
return `## Magic Context
|
|
21714
|
+
|
|
21715
|
+
${LONG_TERM_PARTNER_FRAME}
|
|
21716
|
+
${PARTNER_FRAME_CLOSER_NO_REDUCE_LIGHT}
|
|
21717
|
+
|
|
21718
|
+
${BASE_INTRO_NO_REDUCE_LIGHT(memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
|
|
21719
|
+
}
|
|
21819
21720
|
return `## Magic Context
|
|
21820
21721
|
|
|
21821
21722
|
${LONG_TERM_PARTNER_FRAME}
|
|
21822
21723
|
${PARTNER_FRAME_CLOSER_NO_REDUCE}
|
|
21823
21724
|
|
|
21824
21725
|
${BASE_INTRO_NO_REDUCE(memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
|
|
21726
|
+
}
|
|
21727
|
+
if (preset === "light") {
|
|
21728
|
+
return `## Magic Context
|
|
21729
|
+
|
|
21730
|
+
${LONG_TERM_PARTNER_FRAME}
|
|
21731
|
+
${PARTNER_FRAME_CLOSER_REDUCE_LIGHT}
|
|
21732
|
+
|
|
21733
|
+
${BASE_INTRO_LIGHT(protectedTags, memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
|
|
21825
21734
|
}
|
|
21826
21735
|
return `## Magic Context
|
|
21827
21736
|
|
|
@@ -21842,7 +21751,7 @@ function buildMagicContextBlock(opts) {
|
|
|
21842
21751
|
const includeGuidance = (opts.includeGuidance ?? true) && !existing.includes(MAGIC_CONTEXT_MARKER);
|
|
21843
21752
|
if (!includeGuidance)
|
|
21844
21753
|
return null;
|
|
21845
|
-
return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceCallable ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false, false, opts.language, opts.memoryEnabled !== false);
|
|
21754
|
+
return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceCallable ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false, false, opts.language, opts.memoryEnabled !== false, opts.promptSurfacePreset, opts.primaryGuidanceOverride);
|
|
21846
21755
|
}
|
|
21847
21756
|
var DATE_PATTERN = /Today's date: .+/;
|
|
21848
21757
|
function processSystemPromptForCache(args) {
|
|
@@ -21870,7 +21779,7 @@ function processSystemPromptForCache(args) {
|
|
|
21870
21779
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
21871
21780
|
}
|
|
21872
21781
|
}
|
|
21873
|
-
const currentHash =
|
|
21782
|
+
const currentHash = createHash10("md5").update(promptSurfaceHashMaterial(frozenPrompt, args.promptSurfacePreset)).digest("hex");
|
|
21874
21783
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
21875
21784
|
if (hashChanged) {
|
|
21876
21785
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -27539,7 +27448,7 @@ ${c.content}
|
|
|
27539
27448
|
// package.json
|
|
27540
27449
|
var package_default = {
|
|
27541
27450
|
name: "@cortexkit/pi-magic-context",
|
|
27542
|
-
version: "0.
|
|
27451
|
+
version: "0.35.0",
|
|
27543
27452
|
type: "module",
|
|
27544
27453
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
27545
27454
|
main: "dist/index.js",
|
|
@@ -28995,9 +28904,15 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28995
28904
|
const projectDir = process.cwd();
|
|
28996
28905
|
const seenDreamerProjectIdentities = new Set;
|
|
28997
28906
|
ensureConfigLocationsMigrated(projectDir);
|
|
28998
|
-
const { config, warnings, loadedFromPaths } = loadPiConfig({
|
|
28907
|
+
const { config, warnings, loadedFromPaths, registrationPromptSurface } = loadPiConfig({
|
|
28999
28908
|
cwd: projectDir
|
|
29000
28909
|
});
|
|
28910
|
+
const promptSurfaceRuntime = createPromptSurfaceRuntime({
|
|
28911
|
+
harness: "pi",
|
|
28912
|
+
directory: projectDir,
|
|
28913
|
+
warn: (message) => warn(`config: ${message}`)
|
|
28914
|
+
});
|
|
28915
|
+
const promptSurfaceGuidanceEpochs = createPromptSurfaceGuidanceEpochCache(promptSurfaceRuntime);
|
|
29001
28916
|
const projectIdentity = resolveProjectIdentityForSession(projectDir, config.allow_home_project) ?? "";
|
|
29002
28917
|
if (projectIdentity)
|
|
29003
28918
|
seenDreamerProjectIdentities.add(projectIdentity);
|
|
@@ -29143,7 +29058,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
29143
29058
|
dreamerEnabled: isDreamerRunnable(config),
|
|
29144
29059
|
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
29145
29060
|
todowriteEnabled,
|
|
29146
|
-
compactionOff
|
|
29061
|
+
compactionOff,
|
|
29062
|
+
promptSurface: registrationPromptSurface,
|
|
29063
|
+
promptSurfaceRuntime
|
|
29147
29064
|
});
|
|
29148
29065
|
info(compactionOff ? todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite; registered /todos (ctx_reduce unavailable in compaction-off mode)" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand (ctx_reduce unavailable in compaction-off mode; todowrite disabled)" : todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite, ctx_reduce; registered /todos" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, ctx_reduce (todowrite disabled)");
|
|
29149
29066
|
pi.on("session_start", async (event, ctx) => {
|
|
@@ -29449,6 +29366,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
29449
29366
|
return;
|
|
29450
29367
|
}
|
|
29451
29368
|
const isCacheBusting = sessionId ? hasSystemPromptRefresh(sessionId) : true;
|
|
29369
|
+
const promptSurfaceModel = ctx.model;
|
|
29370
|
+
const promptSurfaceModelKey = typeof promptSurfaceModel?.provider === "string" && promptSurfaceModel.provider.length > 0 && typeof promptSurfaceModel.id === "string" && promptSurfaceModel.id.length > 0 ? `${promptSurfaceModel.provider}/${promptSurfaceModel.id}` : undefined;
|
|
29371
|
+
const promptSurface = sessionId ? promptSurfaceGuidanceEpochs.resolve(sessionId, effectiveConfig.prompt_surface, promptSurfaceModelKey) : promptSurfaceRuntime.resolveGuidance(effectiveConfig.prompt_surface, promptSurfaceModelKey);
|
|
29452
29372
|
const block = buildMagicContextBlock({
|
|
29453
29373
|
db,
|
|
29454
29374
|
cwd: currentProject.projectDir,
|
|
@@ -29461,6 +29381,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
29461
29381
|
temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
|
|
29462
29382
|
cavemanTextCompressionEnabled: effectiveConfig.caveman_text_compression?.enabled === true,
|
|
29463
29383
|
language: effectiveConfig.language,
|
|
29384
|
+
promptSurfacePreset: promptSurface.preset,
|
|
29385
|
+
primaryGuidanceOverride: promptSurface.primaryOverride,
|
|
29464
29386
|
userMemoriesEnabled: userMemoryCollectionEnabled(effectiveConfig.dreamer),
|
|
29465
29387
|
isCacheBusting,
|
|
29466
29388
|
existingSystemPrompt: event.systemPrompt
|
|
@@ -29477,7 +29399,8 @@ ${block}` : event.systemPrompt;
|
|
|
29477
29399
|
db,
|
|
29478
29400
|
sessionId,
|
|
29479
29401
|
systemPrompt: composedPrompt,
|
|
29480
|
-
isCacheBusting
|
|
29402
|
+
isCacheBusting,
|
|
29403
|
+
promptSurfacePreset: promptSurface.preset
|
|
29481
29404
|
});
|
|
29482
29405
|
if (result.hashChanged) {
|
|
29483
29406
|
signalPiHistoryRefresh(sessionId);
|
|
@@ -29691,6 +29614,7 @@ ${block}` : event.systemPrompt;
|
|
|
29691
29614
|
const sessionId = typeof sm?.getSessionId === "function" ? sm.getSessionId() : undefined;
|
|
29692
29615
|
if (typeof sessionId === "string" && sessionId.length > 0) {
|
|
29693
29616
|
clearPiSystemPromptSession(sessionId);
|
|
29617
|
+
promptSurfaceGuidanceEpochs.clear(sessionId);
|
|
29694
29618
|
clearContextHandlerSession(sessionId);
|
|
29695
29619
|
}
|
|
29696
29620
|
} catch {}
|
|
@@ -29702,6 +29626,7 @@ ${block}` : event.systemPrompt;
|
|
|
29702
29626
|
const outgoingSessionId = typeof sm?.getSessionId === "function" ? sm.getSessionId() : undefined;
|
|
29703
29627
|
if (typeof outgoingSessionId === "string" && outgoingSessionId.length > 0) {
|
|
29704
29628
|
clearPiSystemPromptSession(outgoingSessionId);
|
|
29629
|
+
promptSurfaceGuidanceEpochs.clear(outgoingSessionId);
|
|
29705
29630
|
clearContextHandlerSession(outgoingSessionId);
|
|
29706
29631
|
}
|
|
29707
29632
|
} catch {}
|