@cortexkit/pi-magic-context 0.34.0 → 0.34.2
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-0k3j1tc3.js → index-fw42dv6t.js} +272 -65
- package/dist/index.js +782 -422
- package/dist/subagent-entry.js +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -200,6 +200,7 @@ import {
|
|
|
200
200
|
hasMeaningfulUserText,
|
|
201
201
|
hasMemoryClassifiedAtColumn,
|
|
202
202
|
hasMemoryShareableColumn,
|
|
203
|
+
hasMuralCueColumns,
|
|
203
204
|
hasPiFallbackMessageTags,
|
|
204
205
|
hasPiFallbackToolOwnerTags,
|
|
205
206
|
incrementHistorianFailure,
|
|
@@ -269,6 +270,7 @@ import {
|
|
|
269
270
|
recordHistorianDrainFailure,
|
|
270
271
|
recordMemoryMapping,
|
|
271
272
|
recordMemoryVerifications,
|
|
273
|
+
recordMuralCueRejection,
|
|
272
274
|
recordOverflowDetected,
|
|
273
275
|
recordProtectedTailNoEligibleHead,
|
|
274
276
|
recordProtectedTailPublicationFloor,
|
|
@@ -361,7 +363,7 @@ import {
|
|
|
361
363
|
withMigrationLanguageDirective,
|
|
362
364
|
withRawMessageProvider,
|
|
363
365
|
withRawSessionMessageCache
|
|
364
|
-
} from "./index-
|
|
366
|
+
} from "./index-fw42dv6t.js";
|
|
365
367
|
import {
|
|
366
368
|
pushNotification
|
|
367
369
|
} from "./index-hw34xmzk.js";
|
|
@@ -443,6 +445,16 @@ var CANONICAL_DREAM_TASKS = [
|
|
|
443
445
|
"promote-primers",
|
|
444
446
|
"refresh-primers"
|
|
445
447
|
];
|
|
448
|
+
function formatDreamTaskBacklogs(backlogs, tasks = CANONICAL_DREAM_TASKS) {
|
|
449
|
+
return tasks.filter((task) => backlogs[task] !== undefined).map((task) => {
|
|
450
|
+
const backlog = backlogs[task];
|
|
451
|
+
return `- ${task}: ${backlog?.pending ?? 0} pending / ${backlog?.total ?? 0} total`;
|
|
452
|
+
}).join(`
|
|
453
|
+
`);
|
|
454
|
+
}
|
|
455
|
+
function processedDreamTaskItems(startPending, endPending) {
|
|
456
|
+
return Math.max(0, startPending - endPending);
|
|
457
|
+
}
|
|
446
458
|
var MEMORY_DOMAIN_TASKS = [
|
|
447
459
|
"map-memories",
|
|
448
460
|
"verify",
|
|
@@ -572,6 +584,15 @@ function formatFailClosedBlockingProcesses(processes) {
|
|
|
572
584
|
}
|
|
573
585
|
function formatFailClosedBlockingMessage(reason) {
|
|
574
586
|
if (reason.kind === "migration_guard") {
|
|
587
|
+
if (reason.unreadableFile) {
|
|
588
|
+
const arm = reason.unreadableArm ?? "io";
|
|
589
|
+
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.`;
|
|
590
|
+
return [
|
|
591
|
+
`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.`,
|
|
592
|
+
recovery,
|
|
593
|
+
`Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
|
|
594
|
+
].join(" ");
|
|
595
|
+
}
|
|
575
596
|
return [
|
|
576
597
|
`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.`,
|
|
577
598
|
"Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
|
|
@@ -580,7 +601,7 @@ function formatFailClosedBlockingMessage(reason) {
|
|
|
580
601
|
}
|
|
581
602
|
if (reason.kind === "schema_fence") {
|
|
582
603
|
return [
|
|
583
|
-
`Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (
|
|
604
|
+
`Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (upstream migration lane v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
|
|
584
605
|
`Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
|
|
585
606
|
].join(" ");
|
|
586
607
|
}
|
|
@@ -3069,7 +3090,474 @@ ${sidekickText}
|
|
|
3069
3090
|
</sidekick-augmentation>`;
|
|
3070
3091
|
pi.sendUserMessage(augmentedPrompt);
|
|
3071
3092
|
}
|
|
3072
|
-
});
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
// ../plugin/src/features/magic-context/smart-notes/types.ts
|
|
3097
|
+
var SMART_NOTE_CHECK_POLICY_VERSION = 1;
|
|
3098
|
+
var SMART_NOTE_CHECK_FLOOR_MS = 5 * 60 * 1000;
|
|
3099
|
+
var SMART_NOTE_CHECK_CEILING_MS = 24 * 60 * 60 * 1000;
|
|
3100
|
+
var SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
3101
|
+
var SMART_NOTE_CHECK_MAX_STALENESS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
3102
|
+
var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
3103
|
+
|
|
3104
|
+
class SmartNoteNetworkError extends Error {
|
|
3105
|
+
isSmartNoteNetworkError = true;
|
|
3106
|
+
terminal;
|
|
3107
|
+
constructor(message, options = {}) {
|
|
3108
|
+
super(message);
|
|
3109
|
+
this.name = "SmartNoteNetworkError";
|
|
3110
|
+
this.terminal = options.terminal ?? false;
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
class SmartNoteSecurityError extends Error {
|
|
3115
|
+
isSmartNoteSecurityError = true;
|
|
3116
|
+
constructor(message) {
|
|
3117
|
+
super(message);
|
|
3118
|
+
this.name = "SmartNoteSecurityError";
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
function isSmartNoteNetworkError(error) {
|
|
3122
|
+
return error instanceof SmartNoteNetworkError || error instanceof Error && (error.name === "SmartNoteNetworkError" || error.message.includes("SmartNoteNetworkError") || error.message.includes("SMART_NOTE_NETWORK"));
|
|
3123
|
+
}
|
|
3124
|
+
function isTerminalSmartNoteNetworkError(error) {
|
|
3125
|
+
return error instanceof SmartNoteNetworkError && error.terminal;
|
|
3126
|
+
}
|
|
3127
|
+
function parseSmartNoteManifest(json) {
|
|
3128
|
+
if (!json)
|
|
3129
|
+
return { capabilities: [] };
|
|
3130
|
+
try {
|
|
3131
|
+
const parsed = JSON.parse(json);
|
|
3132
|
+
const capabilities = Array.isArray(parsed.capabilities) ? parsed.capabilities.filter((c) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(c))) : [];
|
|
3133
|
+
return {
|
|
3134
|
+
capabilities,
|
|
3135
|
+
readFiles: stringArray(parsed.readFiles),
|
|
3136
|
+
hosts: stringArray(parsed.hosts),
|
|
3137
|
+
urls: stringArray(parsed.urls),
|
|
3138
|
+
signals: stringArray(parsed.signals),
|
|
3139
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : undefined
|
|
3140
|
+
};
|
|
3141
|
+
} catch {
|
|
3142
|
+
return { capabilities: [] };
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
function stringArray(value) {
|
|
3146
|
+
if (!Array.isArray(value))
|
|
3147
|
+
return;
|
|
3148
|
+
const arr = value.filter((item) => typeof item === "string");
|
|
3149
|
+
return arr.length > 0 ? arr : undefined;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
// ../plugin/src/features/magic-context/smart-notes/storage.ts
|
|
3153
|
+
function toSmartNote(note) {
|
|
3154
|
+
return {
|
|
3155
|
+
...note,
|
|
3156
|
+
checkStatus: note.checkStatus ?? "uncompiled",
|
|
3157
|
+
checkFailureCount: note.checkFailureCount ?? 0,
|
|
3158
|
+
checkNetworkFailureCount: note.checkNetworkFailureCount ?? 0,
|
|
3159
|
+
policyVersion: note.policyVersion ?? 0
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
function commitSmartNoteState(db, args) {
|
|
3163
|
+
db.exec("BEGIN IMMEDIATE");
|
|
3164
|
+
let leaseLost = false;
|
|
3165
|
+
let committed = false;
|
|
3166
|
+
try {
|
|
3167
|
+
if (args.leaseHeld && !args.leaseHeld()) {
|
|
3168
|
+
leaseLost = true;
|
|
3169
|
+
} else if (claimExpectedState(db, args.expected)) {
|
|
3170
|
+
args.write();
|
|
3171
|
+
committed = true;
|
|
3172
|
+
}
|
|
3173
|
+
db.exec("COMMIT");
|
|
3174
|
+
} catch (error) {
|
|
3175
|
+
try {
|
|
3176
|
+
db.exec("ROLLBACK");
|
|
3177
|
+
} catch {}
|
|
3178
|
+
throw error;
|
|
3179
|
+
}
|
|
3180
|
+
if (leaseLost) {
|
|
3181
|
+
throw new Error(`Dream lease lost during smart-note ${args.phase} commit`);
|
|
3182
|
+
}
|
|
3183
|
+
if (!committed) {
|
|
3184
|
+
log(`[debug] smart note #${args.expected.noteId}: discarded stale ${args.phase} result`);
|
|
3185
|
+
}
|
|
3186
|
+
return committed;
|
|
3187
|
+
}
|
|
3188
|
+
function claimExpectedState(db, expected) {
|
|
3189
|
+
if (expected.kind === "compiled-check") {
|
|
3190
|
+
return db.prepare(`UPDATE notes SET id = id
|
|
3191
|
+
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
3192
|
+
AND check_status = 'compiled' AND compiled_check = ?
|
|
3193
|
+
AND check_hash IS ? AND check_compiled_at IS ?`).run(expected.noteId, expected.compiledCheck, expected.checkHash, expected.checkCompiledAt).changes > 0;
|
|
3194
|
+
}
|
|
3195
|
+
const statusClause = expected.checkStatus ? " AND check_status = ?" : "";
|
|
3196
|
+
const params = [
|
|
3197
|
+
expected.noteId,
|
|
3198
|
+
expected.content,
|
|
3199
|
+
expected.surfaceCondition,
|
|
3200
|
+
expected.updatedAt
|
|
3201
|
+
];
|
|
3202
|
+
if (expected.checkStatus)
|
|
3203
|
+
params.push(expected.checkStatus);
|
|
3204
|
+
return db.prepare(`UPDATE notes SET id = id
|
|
3205
|
+
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
3206
|
+
AND content = ? AND surface_condition IS ? AND updated_at = ?${statusClause}`).run(...params).changes > 0;
|
|
3207
|
+
}
|
|
3208
|
+
function getDueCompiledSmartNoteChecks(db, projectPath, now, limit) {
|
|
3209
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && (note.checkQuarantinedUntil === null || note.checkQuarantinedUntil <= now) && (note.checkNextDueAt === null || note.checkNextDueAt <= now)).sort((a, b) => (a.checkNextDueAt ?? 0) - (b.checkNextDueAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3210
|
+
}
|
|
3211
|
+
function getSmartNotesNeedingCompilation(db, projectPath, now, limit) {
|
|
3212
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => (note.checkNextDueAt === null || note.checkNextDueAt <= now) && (note.checkStatus === "uncompiled" || note.checkStatus === "failing" || note.compiledCheck === null || note.policyVersion !== SMART_NOTE_CHECK_POLICY_VERSION)).sort((a, b) => a.createdAt - b.createdAt || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3213
|
+
}
|
|
3214
|
+
function getStaleCompiledSmartNotes(db, projectPath, now, limit) {
|
|
3215
|
+
const staleBefore = now - SMART_NOTE_CHECK_MAX_STALENESS_MS;
|
|
3216
|
+
const livenessBefore = now - SMART_NOTE_CHECK_LIVENESS_RECHECK_MS;
|
|
3217
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && note.checkFalseSinceAt !== null && note.checkFalseSinceAt <= staleBefore && (note.checkLastLivenessAt === null || note.checkLastLivenessAt <= livenessBefore)).sort((a, b) => (a.checkFalseSinceAt ?? 0) - (b.checkFalseSinceAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3218
|
+
}
|
|
3219
|
+
function storeCompiledSmartNoteCheck(db, args) {
|
|
3220
|
+
db.prepare(`UPDATE notes
|
|
3221
|
+
SET compiled_check = ?,
|
|
3222
|
+
manifest_json = ?,
|
|
3223
|
+
check_hash = ?,
|
|
3224
|
+
check_cron = ?,
|
|
3225
|
+
check_version = 1,
|
|
3226
|
+
check_status = 'compiled',
|
|
3227
|
+
check_failure_count = 0,
|
|
3228
|
+
check_network_failure_count = 0,
|
|
3229
|
+
check_quarantined_until = NULL,
|
|
3230
|
+
check_next_due_at = ?,
|
|
3231
|
+
check_compiled_at = ?,
|
|
3232
|
+
check_false_since_at = COALESCE(check_false_since_at, ?),
|
|
3233
|
+
check_last_liveness_at = NULL,
|
|
3234
|
+
policy_version = ?,
|
|
3235
|
+
updated_at = ?
|
|
3236
|
+
WHERE id = ? AND type = 'smart'`).run(args.compiledCheck, JSON.stringify(args.manifest), args.checkHash, args.checkCron, args.nextDueAt, args.now, args.now, SMART_NOTE_CHECK_POLICY_VERSION, args.now, args.noteId);
|
|
3237
|
+
}
|
|
3238
|
+
function markCompiledCheckFalse(db, noteId, nextDueAt, now) {
|
|
3239
|
+
db.prepare(`UPDATE notes
|
|
3240
|
+
SET last_checked_at = ?,
|
|
3241
|
+
updated_at = ?,
|
|
3242
|
+
check_next_due_at = ?,
|
|
3243
|
+
check_failure_count = 0,
|
|
3244
|
+
check_network_failure_count = 0,
|
|
3245
|
+
check_false_since_at = COALESCE(check_false_since_at, ?)
|
|
3246
|
+
WHERE id = ? AND type = 'smart'`).run(now, now, nextDueAt, now, noteId);
|
|
3247
|
+
}
|
|
3248
|
+
function markCompiledCheckLogicFailure(db, noteId, now, maxFailures) {
|
|
3249
|
+
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
3250
|
+
const status = failureCount >= maxFailures ? "failing" : "compiled";
|
|
3251
|
+
db.prepare(`UPDATE notes
|
|
3252
|
+
SET check_failure_count = ?,
|
|
3253
|
+
check_status = ?,
|
|
3254
|
+
check_next_due_at = ?,
|
|
3255
|
+
updated_at = ?
|
|
3256
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
3257
|
+
}
|
|
3258
|
+
function markCompiledCheckNetworkFailure(db, noteId, now, maxFailures) {
|
|
3259
|
+
const failureCount = readFailureCount(db, noteId, "check_network_failure_count") + 1;
|
|
3260
|
+
const quarantinedUntil = now + backoffMs(failureCount);
|
|
3261
|
+
const status = failureCount >= maxFailures ? "failing" : "compiled";
|
|
3262
|
+
db.prepare(`UPDATE notes
|
|
3263
|
+
SET check_network_failure_count = ?,
|
|
3264
|
+
check_status = ?,
|
|
3265
|
+
check_next_due_at = ?,
|
|
3266
|
+
check_quarantined_until = ?,
|
|
3267
|
+
updated_at = ?
|
|
3268
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, quarantinedUntil, quarantinedUntil, now, noteId);
|
|
3269
|
+
}
|
|
3270
|
+
function markSmartNoteLivenessChecked(db, noteId, now) {
|
|
3271
|
+
db.prepare(`UPDATE notes
|
|
3272
|
+
SET check_last_liveness_at = ?, updated_at = ?
|
|
3273
|
+
WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
|
|
3274
|
+
}
|
|
3275
|
+
function markSmartNoteCheckStatus(db, noteId, status, now) {
|
|
3276
|
+
db.prepare(`UPDATE notes SET check_status = ?, updated_at = ? WHERE id = ? AND type = 'smart'`).run(status, now, noteId);
|
|
3277
|
+
}
|
|
3278
|
+
function markSmartNoteCompilationFailure(db, noteId, now, maxFailures) {
|
|
3279
|
+
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
3280
|
+
const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
3281
|
+
db.prepare(`UPDATE notes
|
|
3282
|
+
SET check_failure_count = ?,
|
|
3283
|
+
check_status = ?,
|
|
3284
|
+
check_next_due_at = ?,
|
|
3285
|
+
updated_at = ?
|
|
3286
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
3287
|
+
}
|
|
3288
|
+
function readFailureCount(db, noteId, column) {
|
|
3289
|
+
if (column !== "check_failure_count" && column !== "check_network_failure_count")
|
|
3290
|
+
return 0;
|
|
3291
|
+
const row = db.prepare(`SELECT ${column} AS count FROM notes WHERE id = ?`).get(noteId);
|
|
3292
|
+
return row?.count ?? 0;
|
|
3293
|
+
}
|
|
3294
|
+
function backoffMs(failureCount) {
|
|
3295
|
+
const minutes = Math.min(24 * 60, 5 * 2 ** Math.max(0, failureCount - 1));
|
|
3296
|
+
return minutes * 60 * 1000;
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
// ../plugin/src/features/magic-context/dreamer/storage-task-schedule.ts
|
|
3300
|
+
function toRow(r) {
|
|
3301
|
+
return {
|
|
3302
|
+
projectPath: r.project_path,
|
|
3303
|
+
task: r.task,
|
|
3304
|
+
lastRunAt: r.last_run_at,
|
|
3305
|
+
nextDueAt: r.next_due_at,
|
|
3306
|
+
schedule: r.schedule ?? null,
|
|
3307
|
+
lastStatus: r.last_status ?? null,
|
|
3308
|
+
lastError: r.last_error,
|
|
3309
|
+
retryCount: r.retry_count ?? 0,
|
|
3310
|
+
lastCheckedCommit: r.last_checked_commit ?? null,
|
|
3311
|
+
lastBroadRunAt: r.last_broad_run_at ?? null,
|
|
3312
|
+
retrospectiveWatermarkMs: r.retrospective_watermark_ms ?? null
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
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";
|
|
3316
|
+
function getTaskScheduleState(db, projectPath, task) {
|
|
3317
|
+
const row = db.prepare(`SELECT ${SELECT_COLUMNS} FROM task_schedule_state WHERE project_path = ? AND task = ?`).get(projectPath, task);
|
|
3318
|
+
return row ? toRow(row) : null;
|
|
3319
|
+
}
|
|
3320
|
+
function getMostRecentTaskRunAt(db, projectPath) {
|
|
3321
|
+
const row = db.prepare("SELECT MAX(last_run_at) AS max_at FROM task_schedule_state WHERE project_path = ?").get(projectPath);
|
|
3322
|
+
const value = row?.max_at ?? null;
|
|
3323
|
+
return typeof value === "number" && value > 0 ? value : null;
|
|
3324
|
+
}
|
|
3325
|
+
function pruneNonCanonicalTaskRows(db, projectPath, canonicalTasks) {
|
|
3326
|
+
if (canonicalTasks.length === 0)
|
|
3327
|
+
return 0;
|
|
3328
|
+
const placeholders = canonicalTasks.map(() => "?").join(", ");
|
|
3329
|
+
const result = db.prepare(`DELETE FROM task_schedule_state WHERE project_path = ? AND task NOT IN (${placeholders})`).run(projectPath, ...canonicalTasks);
|
|
3330
|
+
return Number(result.changes ?? 0);
|
|
3331
|
+
}
|
|
3332
|
+
function deleteTaskScheduleRowsForProject(db, projectPath) {
|
|
3333
|
+
const result = db.prepare("DELETE FROM task_schedule_state WHERE project_path = ?").run(projectPath);
|
|
3334
|
+
return Number(result.changes ?? 0);
|
|
3335
|
+
}
|
|
3336
|
+
function seedTaskScheduleState(db, projectPath, task, nextDueAt, lastRunAt, schedule) {
|
|
3337
|
+
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);
|
|
3338
|
+
}
|
|
3339
|
+
function writeTaskScheduleState(db, row) {
|
|
3340
|
+
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";
|
|
3341
|
+
db.prepare(`INSERT INTO task_schedule_state
|
|
3342
|
+
(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)
|
|
3343
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3344
|
+
ON CONFLICT(project_path, task) DO UPDATE SET
|
|
3345
|
+
last_run_at = excluded.last_run_at,
|
|
3346
|
+
next_due_at = excluded.next_due_at,
|
|
3347
|
+
schedule = excluded.schedule,
|
|
3348
|
+
last_status = excluded.last_status,
|
|
3349
|
+
last_error = excluded.last_error,
|
|
3350
|
+
retry_count = excluded.retry_count,
|
|
3351
|
+
last_checked_commit = COALESCE(excluded.last_checked_commit, task_schedule_state.last_checked_commit),
|
|
3352
|
+
${broadCycleUpdate},
|
|
3353
|
+
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);
|
|
3354
|
+
}
|
|
3355
|
+
function isRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
3356
|
+
const row = db.prepare("SELECT 1 AS one FROM retrospective_processed_windows WHERE project_path = ? AND window_key = ?").get(projectPath, windowKey);
|
|
3357
|
+
return row != null;
|
|
3358
|
+
}
|
|
3359
|
+
function recordRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
3360
|
+
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());
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
// ../plugin/src/features/magic-context/dreamer/task-gates.ts
|
|
3364
|
+
function countActiveMemories(db, projectPath) {
|
|
3365
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
|
|
3366
|
+
return row?.cnt ?? 0;
|
|
3367
|
+
}
|
|
3368
|
+
function countUnmappedActiveMemories(db, projectPath) {
|
|
3369
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3370
|
+
FROM memories m
|
|
3371
|
+
WHERE m.project_path = ?
|
|
3372
|
+
AND m.status IN ('active','permanent')
|
|
3373
|
+
AND NOT EXISTS (
|
|
3374
|
+
SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
|
|
3375
|
+
)`).get(projectPath);
|
|
3376
|
+
return row?.cnt ?? 0;
|
|
3377
|
+
}
|
|
3378
|
+
function countCompartmentsSince(db, projectPath, since) {
|
|
3379
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3380
|
+
FROM compartments c
|
|
3381
|
+
JOIN session_projects sp ON sp.session_id = c.session_id
|
|
3382
|
+
WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
|
|
3383
|
+
return row?.cnt ?? 0;
|
|
3384
|
+
}
|
|
3385
|
+
function countProjectSessionsSince(db, projectPath, since) {
|
|
3386
|
+
const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
|
|
3387
|
+
return row?.cnt ?? 0;
|
|
3388
|
+
}
|
|
3389
|
+
function countMappedMemories(db, projectPath) {
|
|
3390
|
+
const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
|
|
3391
|
+
FROM memories m
|
|
3392
|
+
JOIN memory_verifications v ON v.memory_id = m.id
|
|
3393
|
+
WHERE m.project_path = ?
|
|
3394
|
+
AND m.status IN ('active','permanent')
|
|
3395
|
+
AND v.file_path <> ''`).get(projectPath);
|
|
3396
|
+
return row?.cnt ?? 0;
|
|
3397
|
+
}
|
|
3398
|
+
function countUnverifiedMappedMemories(db, projectPath) {
|
|
3399
|
+
const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
|
|
3400
|
+
FROM memories m
|
|
3401
|
+
JOIN memory_verifications v ON v.memory_id = m.id
|
|
3402
|
+
WHERE m.project_path = ?
|
|
3403
|
+
AND m.status IN ('active','permanent')
|
|
3404
|
+
AND v.file_path <> ''
|
|
3405
|
+
AND v.verified_at = 0`).get(projectPath);
|
|
3406
|
+
return row?.cnt ?? 0;
|
|
3407
|
+
}
|
|
3408
|
+
function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
|
|
3409
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3410
|
+
FROM memories m
|
|
3411
|
+
WHERE m.project_path = ?
|
|
3412
|
+
AND m.status IN ('active','permanent')
|
|
3413
|
+
AND (
|
|
3414
|
+
SELECT MAX(v.verified_at)
|
|
3415
|
+
FROM memory_verifications v
|
|
3416
|
+
WHERE v.memory_id = m.id
|
|
3417
|
+
AND v.file_path <> ''
|
|
3418
|
+
) < ?`).get(projectPath, cycleStartAt);
|
|
3419
|
+
return row?.cnt ?? 0;
|
|
3420
|
+
}
|
|
3421
|
+
function countCueCandidates(db, projectPath) {
|
|
3422
|
+
if (!hasMuralCueColumns(db))
|
|
3423
|
+
return countActiveMemories(db, projectPath);
|
|
3424
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3425
|
+
FROM memories
|
|
3426
|
+
WHERE project_path = ?
|
|
3427
|
+
AND status IN ('active','permanent')
|
|
3428
|
+
AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
|
|
3429
|
+
return row?.cnt ?? 0;
|
|
3430
|
+
}
|
|
3431
|
+
function countStalePrimers(db, projectPath) {
|
|
3432
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3433
|
+
FROM primers
|
|
3434
|
+
WHERE project_path = ?
|
|
3435
|
+
AND status = 'active'
|
|
3436
|
+
AND (answer IS NULL OR TRIM(answer) = '' OR answer_refreshed_at IS NULL
|
|
3437
|
+
OR last_observed_at > answer_refreshed_at)`).get(projectPath);
|
|
3438
|
+
return row?.cnt ?? 0;
|
|
3439
|
+
}
|
|
3440
|
+
function countUnclassifiedActiveMemories(db, projectPath) {
|
|
3441
|
+
if (!hasMemoryClassifiedAtColumn(db))
|
|
3442
|
+
return countActiveMemories(db, projectPath);
|
|
3443
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3444
|
+
FROM memories
|
|
3445
|
+
WHERE project_path = ?
|
|
3446
|
+
AND status IN ('active','permanent')
|
|
3447
|
+
AND classified_at IS NULL`).get(projectPath);
|
|
3448
|
+
return row?.cnt ?? 0;
|
|
3449
|
+
}
|
|
3450
|
+
function countPendingSmartNotes(db, projectPath) {
|
|
3451
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
|
|
3452
|
+
return row?.cnt ?? 0;
|
|
3453
|
+
}
|
|
3454
|
+
function countUserMemoryCandidates(db) {
|
|
3455
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM user_memory_candidates").get();
|
|
3456
|
+
return row?.cnt ?? 0;
|
|
3457
|
+
}
|
|
3458
|
+
function countActivePrimers(db, projectPath) {
|
|
3459
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM primers WHERE project_path = ? AND status = 'active'").get(projectPath);
|
|
3460
|
+
return row?.cnt ?? 0;
|
|
3461
|
+
}
|
|
3462
|
+
function getDreamTaskBacklog(db, projectPath, task, options = {}) {
|
|
3463
|
+
switch (task) {
|
|
3464
|
+
case "map-memories": {
|
|
3465
|
+
const total = countActiveMemories(db, projectPath);
|
|
3466
|
+
return { pending: countUnmappedActiveMemories(db, projectPath), total };
|
|
3467
|
+
}
|
|
3468
|
+
case "verify": {
|
|
3469
|
+
return {
|
|
3470
|
+
pending: countUnverifiedMappedMemories(db, projectPath),
|
|
3471
|
+
total: countMappedMemories(db, projectPath)
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
case "verify-broad": {
|
|
3475
|
+
const total = countMappedMemories(db, projectPath);
|
|
3476
|
+
const cycleStartAt = getTaskScheduleState(db, projectPath, "verify-broad")?.lastBroadRunAt;
|
|
3477
|
+
const pending = cycleStartAt == null ? total : countBroadCycleCandidates(db, projectPath, cycleStartAt);
|
|
3478
|
+
return { pending, total };
|
|
3479
|
+
}
|
|
3480
|
+
case "curate": {
|
|
3481
|
+
const total = countActiveMemories(db, projectPath);
|
|
3482
|
+
return { pending: total, total };
|
|
3483
|
+
}
|
|
3484
|
+
case "compress-cues": {
|
|
3485
|
+
const total = countActiveMemories(db, projectPath);
|
|
3486
|
+
return { pending: countCueCandidates(db, projectPath), total };
|
|
3487
|
+
}
|
|
3488
|
+
case "classify-memories": {
|
|
3489
|
+
const total = countActiveMemories(db, projectPath);
|
|
3490
|
+
return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
|
|
3491
|
+
}
|
|
3492
|
+
case "retrospective": {
|
|
3493
|
+
const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
|
|
3494
|
+
return { pending, total: pending };
|
|
3495
|
+
}
|
|
3496
|
+
case "maintain-docs": {
|
|
3497
|
+
const total = countCompartmentsSince(db, projectPath, 0);
|
|
3498
|
+
const pending = countCompartmentsSince(db, projectPath, options.lastRunAt ?? 0);
|
|
3499
|
+
return { pending, total };
|
|
3500
|
+
}
|
|
3501
|
+
case "evaluate-smart-notes": {
|
|
3502
|
+
const pending = countPendingSmartNotes(db, projectPath);
|
|
3503
|
+
return { pending, total: pending };
|
|
3504
|
+
}
|
|
3505
|
+
case "review-user-memories": {
|
|
3506
|
+
const pending = countUserMemoryCandidates(db);
|
|
3507
|
+
return { pending, total: pending };
|
|
3508
|
+
}
|
|
3509
|
+
case "promote-primers": {
|
|
3510
|
+
const pending = countPrimerCandidatesForProject(db, projectPath);
|
|
3511
|
+
return { pending, total: pending };
|
|
3512
|
+
}
|
|
3513
|
+
case "refresh-primers": {
|
|
3514
|
+
const total = countActivePrimers(db, projectPath);
|
|
3515
|
+
return { pending: countStalePrimers(db, projectPath), total };
|
|
3516
|
+
}
|
|
3517
|
+
default: {
|
|
3518
|
+
const _exhaustive = task;
|
|
3519
|
+
return _exhaustive;
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
function getDreamTaskBacklogs(db, projectPath, tasks = CANONICAL_DREAM_TASKS, options = {}) {
|
|
3524
|
+
const result = {};
|
|
3525
|
+
for (const task of tasks)
|
|
3526
|
+
result[task] = getDreamTaskBacklog(db, projectPath, task, options);
|
|
3527
|
+
return result;
|
|
3528
|
+
}
|
|
3529
|
+
function evaluateTaskGate(task, ctx) {
|
|
3530
|
+
const { db, projectIdentity: project, lastRunAt } = ctx;
|
|
3531
|
+
switch (task) {
|
|
3532
|
+
case "map-memories":
|
|
3533
|
+
return countUnmappedActiveMemories(db, project) > 0;
|
|
3534
|
+
case "verify":
|
|
3535
|
+
return countActiveMemories(db, project) > 0;
|
|
3536
|
+
case "verify-broad":
|
|
3537
|
+
return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countActiveMemories(db, project) > 0;
|
|
3538
|
+
case "curate":
|
|
3539
|
+
return countActiveMemories(db, project) > 0;
|
|
3540
|
+
case "compress-cues":
|
|
3541
|
+
return countActiveMemories(db, project) > 0;
|
|
3542
|
+
case "classify-memories":
|
|
3543
|
+
return countActiveMemories(db, project) > 0;
|
|
3544
|
+
case "retrospective":
|
|
3545
|
+
return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
|
|
3546
|
+
case "maintain-docs":
|
|
3547
|
+
return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
|
|
3548
|
+
case "evaluate-smart-notes":
|
|
3549
|
+
return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
|
|
3550
|
+
case "review-user-memories":
|
|
3551
|
+
return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
|
|
3552
|
+
case "promote-primers":
|
|
3553
|
+
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
|
|
3554
|
+
case "refresh-primers":
|
|
3555
|
+
return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
|
|
3556
|
+
default: {
|
|
3557
|
+
const _exhaustive = task;
|
|
3558
|
+
return Boolean(_exhaustive);
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3073
3561
|
}
|
|
3074
3562
|
|
|
3075
3563
|
// ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
|
|
@@ -3971,7 +4459,7 @@ function hasBalancedParentheses(cue) {
|
|
|
3971
4459
|
}
|
|
3972
4460
|
return depth === 0;
|
|
3973
4461
|
}
|
|
3974
|
-
function validateCue(cue, importance) {
|
|
4462
|
+
function validateCue(cue, importance, ownId) {
|
|
3975
4463
|
const trimmed = cue.trim();
|
|
3976
4464
|
if (trimmed.length === 0)
|
|
3977
4465
|
return { reason: "empty" };
|
|
@@ -3979,8 +4467,9 @@ function validateCue(cue, importance) {
|
|
|
3979
4467
|
const length = [...trimmed].length;
|
|
3980
4468
|
if (length > budget)
|
|
3981
4469
|
return { reason: `over-budget ${length}>${budget}` };
|
|
3982
|
-
if (
|
|
4470
|
+
if (ownId !== undefined && new RegExp(`#${ownId}\\b`).test(trimmed)) {
|
|
3983
4471
|
return { reason: "leaked-id" };
|
|
4472
|
+
}
|
|
3984
4473
|
if (!hasBalancedParentheses(trimmed))
|
|
3985
4474
|
return { reason: "unbalanced-parens" };
|
|
3986
4475
|
const markers = trimmed.split("⊘").length - 1;
|
|
@@ -3996,6 +4485,7 @@ function validateCue(cue, importance) {
|
|
|
3996
4485
|
// ../plugin/src/features/magic-context/mural/compress-cues.ts
|
|
3997
4486
|
var COMPRESS_CUES_CHUNK_SIZE = 40;
|
|
3998
4487
|
var CHUNK_TIMEOUT_FLOOR_MS = 240000;
|
|
4488
|
+
var CUE_REJECTION_LATCH_THRESHOLD = 3;
|
|
3999
4489
|
var CONSECUTIVE_TIMEOUT_LIMIT = 2;
|
|
4000
4490
|
function toPromptMemory(candidate) {
|
|
4001
4491
|
const memory = candidate.memory;
|
|
@@ -4006,6 +4496,38 @@ function toPromptMemory(candidate) {
|
|
|
4006
4496
|
content: memory.content
|
|
4007
4497
|
};
|
|
4008
4498
|
}
|
|
4499
|
+
function stripOwnIdToken(value, ownId) {
|
|
4500
|
+
return value.replace(new RegExp(`#${ownId}\\b`, "g"), "");
|
|
4501
|
+
}
|
|
4502
|
+
function truncateCue(value, budget) {
|
|
4503
|
+
const trimmed = value.trim();
|
|
4504
|
+
const codepoints = [...trimmed];
|
|
4505
|
+
if (codepoints.length <= budget)
|
|
4506
|
+
return trimmed;
|
|
4507
|
+
const prefix = codepoints.slice(0, budget).join("");
|
|
4508
|
+
const boundary = prefix.search(/\s+\S*$/);
|
|
4509
|
+
return (boundary > 0 ? prefix.slice(0, boundary) : prefix).trim();
|
|
4510
|
+
}
|
|
4511
|
+
function sanitizeCue(value, candidate) {
|
|
4512
|
+
return truncateCue(stripOwnIdToken(value, candidate.memory.id), cueBudgetFor(candidate.memory.importance ?? 50));
|
|
4513
|
+
}
|
|
4514
|
+
function deterministicFallbackCue(candidate, lastCandidate) {
|
|
4515
|
+
const importance = candidate.memory.importance ?? 50;
|
|
4516
|
+
const budget = cueBudgetFor(importance);
|
|
4517
|
+
const sanitizedCandidate = sanitizeCue(lastCandidate, candidate);
|
|
4518
|
+
if (validateCue(sanitizedCandidate, importance, candidate.memory.id) === null) {
|
|
4519
|
+
return sanitizedCandidate;
|
|
4520
|
+
}
|
|
4521
|
+
const sourceSlice = sanitizeCue(candidate.memory.content, candidate);
|
|
4522
|
+
if (validateCue(sourceSlice, importance, candidate.memory.id) === null) {
|
|
4523
|
+
return sourceSlice;
|
|
4524
|
+
}
|
|
4525
|
+
const grammarSafe = truncateCue(sourceSlice.replaceAll("⊘", "").replace(/[()]/g, "").replace(/\b(?:must not|never|without|instead of|exclude|excludes)\b/gi, "").replace(/\s+/g, " "), budget);
|
|
4526
|
+
if (validateCue(grammarSafe, importance, candidate.memory.id) === null) {
|
|
4527
|
+
return grammarSafe;
|
|
4528
|
+
}
|
|
4529
|
+
return "memory";
|
|
4530
|
+
}
|
|
4009
4531
|
function selectCandidates(db, projectIdentity) {
|
|
4010
4532
|
const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
|
|
4011
4533
|
const cueState = getMuralCueState(db, memories.map((memory) => memory.id));
|
|
@@ -4059,6 +4581,7 @@ async function runCompressCues(args) {
|
|
|
4059
4581
|
result.skipped += outcome.skipped;
|
|
4060
4582
|
result.remaining -= outcome.compressed;
|
|
4061
4583
|
result.chunks += 1;
|
|
4584
|
+
args.onProgress?.(result.compressed + result.skipped);
|
|
4062
4585
|
if (outcome.failure?.class === "timeout") {
|
|
4063
4586
|
consecutiveTimeouts += 1;
|
|
4064
4587
|
timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
|
|
@@ -4173,10 +4696,19 @@ function applyCues(args, chunk, manifestText) {
|
|
|
4173
4696
|
const candidate = byId.get(entry.id);
|
|
4174
4697
|
if (!candidate)
|
|
4175
4698
|
throw new Error(`cues manifest contains unknown id ${entry.id}`);
|
|
4176
|
-
const
|
|
4699
|
+
const importance = candidate.memory.importance ?? 50;
|
|
4700
|
+
const failure = validateCue(entry.cue, importance, candidate.memory.id);
|
|
4177
4701
|
if (failure) {
|
|
4702
|
+
const rejectionCount = recordMuralCueRejection(args.db, args.projectIdentity, entry.id, candidate.contentHash);
|
|
4703
|
+
if (rejectionCount >= CUE_REJECTION_LATCH_THRESHOLD) {
|
|
4704
|
+
const fallback = deterministicFallbackCue(candidate, entry.cue);
|
|
4705
|
+
setMuralCue(args.db, args.projectIdentity, entry.id, fallback, candidate.contentHash);
|
|
4706
|
+
compressed += 1;
|
|
4707
|
+
log(`[dreamer] compress-cues: fallback cue for memory ${entry.id} (${failure.reason}; ${rejectionCount} rejections; fallback)`);
|
|
4708
|
+
continue;
|
|
4709
|
+
}
|
|
4178
4710
|
skipped += 1;
|
|
4179
|
-
log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason})`);
|
|
4711
|
+
log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason}; rejection ${rejectionCount}/${CUE_REJECTION_LATCH_THRESHOLD})`);
|
|
4180
4712
|
continue;
|
|
4181
4713
|
}
|
|
4182
4714
|
setMuralCue(args.db, args.projectIdentity, entry.id, entry.cue.trim(), candidate.contentHash);
|
|
@@ -5154,6 +5686,7 @@ async function runClassify(args) {
|
|
|
5154
5686
|
result.changed += counts.changed;
|
|
5155
5687
|
result.remaining -= counts.classified;
|
|
5156
5688
|
result.chunks += 1;
|
|
5689
|
+
args.onProgress?.(result.classified);
|
|
5157
5690
|
}
|
|
5158
5691
|
result.complete = result.remaining === 0;
|
|
5159
5692
|
log(`[dreamer] classify: stage=${stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining} complete=${result.complete}`);
|
|
@@ -5414,64 +5947,6 @@ import { lookup as dnsLookup } from "node:dns/promises";
|
|
|
5414
5947
|
import * as https from "node:https";
|
|
5415
5948
|
import { isIP } from "node:net";
|
|
5416
5949
|
import { domainToASCII } from "node:url";
|
|
5417
|
-
|
|
5418
|
-
// ../plugin/src/features/magic-context/smart-notes/types.ts
|
|
5419
|
-
var SMART_NOTE_CHECK_POLICY_VERSION = 1;
|
|
5420
|
-
var SMART_NOTE_CHECK_FLOOR_MS = 5 * 60 * 1000;
|
|
5421
|
-
var SMART_NOTE_CHECK_CEILING_MS = 24 * 60 * 60 * 1000;
|
|
5422
|
-
var SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
5423
|
-
var SMART_NOTE_CHECK_MAX_STALENESS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
5424
|
-
var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
5425
|
-
|
|
5426
|
-
class SmartNoteNetworkError extends Error {
|
|
5427
|
-
isSmartNoteNetworkError = true;
|
|
5428
|
-
terminal;
|
|
5429
|
-
constructor(message, options = {}) {
|
|
5430
|
-
super(message);
|
|
5431
|
-
this.name = "SmartNoteNetworkError";
|
|
5432
|
-
this.terminal = options.terminal ?? false;
|
|
5433
|
-
}
|
|
5434
|
-
}
|
|
5435
|
-
|
|
5436
|
-
class SmartNoteSecurityError extends Error {
|
|
5437
|
-
isSmartNoteSecurityError = true;
|
|
5438
|
-
constructor(message) {
|
|
5439
|
-
super(message);
|
|
5440
|
-
this.name = "SmartNoteSecurityError";
|
|
5441
|
-
}
|
|
5442
|
-
}
|
|
5443
|
-
function isSmartNoteNetworkError(error) {
|
|
5444
|
-
return error instanceof SmartNoteNetworkError || error instanceof Error && (error.name === "SmartNoteNetworkError" || error.message.includes("SmartNoteNetworkError") || error.message.includes("SMART_NOTE_NETWORK"));
|
|
5445
|
-
}
|
|
5446
|
-
function isTerminalSmartNoteNetworkError(error) {
|
|
5447
|
-
return error instanceof SmartNoteNetworkError && error.terminal;
|
|
5448
|
-
}
|
|
5449
|
-
function parseSmartNoteManifest(json) {
|
|
5450
|
-
if (!json)
|
|
5451
|
-
return { capabilities: [] };
|
|
5452
|
-
try {
|
|
5453
|
-
const parsed = JSON.parse(json);
|
|
5454
|
-
const capabilities = Array.isArray(parsed.capabilities) ? parsed.capabilities.filter((c) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(c))) : [];
|
|
5455
|
-
return {
|
|
5456
|
-
capabilities,
|
|
5457
|
-
readFiles: stringArray(parsed.readFiles),
|
|
5458
|
-
hosts: stringArray(parsed.hosts),
|
|
5459
|
-
urls: stringArray(parsed.urls),
|
|
5460
|
-
signals: stringArray(parsed.signals),
|
|
5461
|
-
summary: typeof parsed.summary === "string" ? parsed.summary : undefined
|
|
5462
|
-
};
|
|
5463
|
-
} catch {
|
|
5464
|
-
return { capabilities: [] };
|
|
5465
|
-
}
|
|
5466
|
-
}
|
|
5467
|
-
function stringArray(value) {
|
|
5468
|
-
if (!Array.isArray(value))
|
|
5469
|
-
return;
|
|
5470
|
-
const arr = value.filter((item) => typeof item === "string");
|
|
5471
|
-
return arr.length > 0 ? arr : undefined;
|
|
5472
|
-
}
|
|
5473
|
-
|
|
5474
|
-
// ../plugin/src/features/magic-context/smart-notes/ssrf-guard.ts
|
|
5475
5950
|
var DNS_TIMEOUT_MS = 3000;
|
|
5476
5951
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
5477
5952
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -6403,205 +6878,58 @@ function literalCalls(code, method) {
|
|
|
6403
6878
|
const values = [];
|
|
6404
6879
|
for (const match of code.matchAll(regex)) {
|
|
6405
6880
|
values.push(match[2].replace(/\\([\\"'])/g, "$1"));
|
|
6406
|
-
}
|
|
6407
|
-
return values;
|
|
6408
|
-
}
|
|
6409
|
-
function normalizeCron(cron) {
|
|
6410
|
-
if (cron.length > MAX_CRON_CHARS)
|
|
6411
|
-
throw new Error("check_cron exceeds 256 characters");
|
|
6412
|
-
const normalized = cron.trim() || "0 * * * *";
|
|
6413
|
-
const parsed = parseCron(normalized);
|
|
6414
|
-
if (!parsed.ok)
|
|
6415
|
-
throw new Error(`invalid check_cron: ${parsed.error}`);
|
|
6416
|
-
const next = nextOccurrence(parsed.cron, new Date, undefined, SMART_NOTE_CHECK_CEILING_MS);
|
|
6417
|
-
if (!next)
|
|
6418
|
-
throw new Error("check_cron has no occurrence within the scheduling ceiling");
|
|
6419
|
-
return normalized;
|
|
6420
|
-
}
|
|
6421
|
-
function unique(items) {
|
|
6422
|
-
return [...new Set(items)];
|
|
6423
|
-
}
|
|
6424
|
-
function uniqueStrings(items, normalize = (value) => value) {
|
|
6425
|
-
if (!Array.isArray(items))
|
|
6426
|
-
return;
|
|
6427
|
-
const values = unique(items.slice(0, MAX_MANIFEST_ENTRIES).filter((item) => typeof item === "string" && item.length > 0).map(normalize));
|
|
6428
|
-
return values.length > 0 ? values : undefined;
|
|
6429
|
-
}
|
|
6430
|
-
function boundedError(error) {
|
|
6431
|
-
return error.slice(0, MAX_COMPILER_ERROR_CHARS);
|
|
6432
|
-
}
|
|
6433
|
-
|
|
6434
|
-
// ../plugin/src/features/magic-context/smart-notes/schedule.ts
|
|
6435
|
-
function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
6436
|
-
const now = options.now ?? Date.now();
|
|
6437
|
-
const floorMs = options.floorMs ?? SMART_NOTE_CHECK_FLOOR_MS;
|
|
6438
|
-
const ceilingMs = options.ceilingMs ?? SMART_NOTE_CHECK_CEILING_MS;
|
|
6439
|
-
const rawNext = cron?.trim() ? nextDueAtMs(cron, now, undefined, ceilingMs) : null;
|
|
6440
|
-
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
6441
|
-
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
6442
|
-
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
6443
|
-
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
6444
|
-
return now + bounded;
|
|
6445
|
-
}
|
|
6446
|
-
function deterministicJitterMs(intervalMs, noteId, hash) {
|
|
6447
|
-
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
6448
|
-
if (max <= 0)
|
|
6449
|
-
return 0;
|
|
6450
|
-
const seed = `${noteId ?? 0}:${hash ?? ""}`;
|
|
6451
|
-
let h = 2166136261;
|
|
6452
|
-
for (let i = 0;i < seed.length; i++) {
|
|
6453
|
-
h ^= seed.charCodeAt(i);
|
|
6454
|
-
h = Math.imul(h, 16777619);
|
|
6455
|
-
}
|
|
6456
|
-
const unsigned = h >>> 0;
|
|
6457
|
-
return unsigned % (max * 2 + 1) - max;
|
|
6458
|
-
}
|
|
6459
|
-
|
|
6460
|
-
// ../plugin/src/features/magic-context/smart-notes/storage.ts
|
|
6461
|
-
function toSmartNote(note) {
|
|
6462
|
-
return {
|
|
6463
|
-
...note,
|
|
6464
|
-
checkStatus: note.checkStatus ?? "uncompiled",
|
|
6465
|
-
checkFailureCount: note.checkFailureCount ?? 0,
|
|
6466
|
-
checkNetworkFailureCount: note.checkNetworkFailureCount ?? 0,
|
|
6467
|
-
policyVersion: note.policyVersion ?? 0
|
|
6468
|
-
};
|
|
6469
|
-
}
|
|
6470
|
-
function commitSmartNoteState(db, args) {
|
|
6471
|
-
db.exec("BEGIN IMMEDIATE");
|
|
6472
|
-
let leaseLost = false;
|
|
6473
|
-
let committed = false;
|
|
6474
|
-
try {
|
|
6475
|
-
if (args.leaseHeld && !args.leaseHeld()) {
|
|
6476
|
-
leaseLost = true;
|
|
6477
|
-
} else if (claimExpectedState(db, args.expected)) {
|
|
6478
|
-
args.write();
|
|
6479
|
-
committed = true;
|
|
6480
|
-
}
|
|
6481
|
-
db.exec("COMMIT");
|
|
6482
|
-
} catch (error) {
|
|
6483
|
-
try {
|
|
6484
|
-
db.exec("ROLLBACK");
|
|
6485
|
-
} catch {}
|
|
6486
|
-
throw error;
|
|
6487
|
-
}
|
|
6488
|
-
if (leaseLost) {
|
|
6489
|
-
throw new Error(`Dream lease lost during smart-note ${args.phase} commit`);
|
|
6490
|
-
}
|
|
6491
|
-
if (!committed) {
|
|
6492
|
-
log(`[debug] smart note #${args.expected.noteId}: discarded stale ${args.phase} result`);
|
|
6493
|
-
}
|
|
6494
|
-
return committed;
|
|
6495
|
-
}
|
|
6496
|
-
function claimExpectedState(db, expected) {
|
|
6497
|
-
if (expected.kind === "compiled-check") {
|
|
6498
|
-
return db.prepare(`UPDATE notes SET id = id
|
|
6499
|
-
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
6500
|
-
AND check_status = 'compiled' AND compiled_check = ?
|
|
6501
|
-
AND check_hash IS ? AND check_compiled_at IS ?`).run(expected.noteId, expected.compiledCheck, expected.checkHash, expected.checkCompiledAt).changes > 0;
|
|
6502
|
-
}
|
|
6503
|
-
const statusClause = expected.checkStatus ? " AND check_status = ?" : "";
|
|
6504
|
-
const params = [
|
|
6505
|
-
expected.noteId,
|
|
6506
|
-
expected.content,
|
|
6507
|
-
expected.surfaceCondition,
|
|
6508
|
-
expected.updatedAt
|
|
6509
|
-
];
|
|
6510
|
-
if (expected.checkStatus)
|
|
6511
|
-
params.push(expected.checkStatus);
|
|
6512
|
-
return db.prepare(`UPDATE notes SET id = id
|
|
6513
|
-
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
6514
|
-
AND content = ? AND surface_condition IS ? AND updated_at = ?${statusClause}`).run(...params).changes > 0;
|
|
6515
|
-
}
|
|
6516
|
-
function getDueCompiledSmartNoteChecks(db, projectPath, now, limit) {
|
|
6517
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && (note.checkQuarantinedUntil === null || note.checkQuarantinedUntil <= now) && (note.checkNextDueAt === null || note.checkNextDueAt <= now)).sort((a, b) => (a.checkNextDueAt ?? 0) - (b.checkNextDueAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6518
|
-
}
|
|
6519
|
-
function getSmartNotesNeedingCompilation(db, projectPath, now, limit) {
|
|
6520
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => (note.checkNextDueAt === null || note.checkNextDueAt <= now) && (note.checkStatus === "uncompiled" || note.checkStatus === "failing" || note.compiledCheck === null || note.policyVersion !== SMART_NOTE_CHECK_POLICY_VERSION)).sort((a, b) => a.createdAt - b.createdAt || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6521
|
-
}
|
|
6522
|
-
function getStaleCompiledSmartNotes(db, projectPath, now, limit) {
|
|
6523
|
-
const staleBefore = now - SMART_NOTE_CHECK_MAX_STALENESS_MS;
|
|
6524
|
-
const livenessBefore = now - SMART_NOTE_CHECK_LIVENESS_RECHECK_MS;
|
|
6525
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && note.checkFalseSinceAt !== null && note.checkFalseSinceAt <= staleBefore && (note.checkLastLivenessAt === null || note.checkLastLivenessAt <= livenessBefore)).sort((a, b) => (a.checkFalseSinceAt ?? 0) - (b.checkFalseSinceAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6526
|
-
}
|
|
6527
|
-
function storeCompiledSmartNoteCheck(db, args) {
|
|
6528
|
-
db.prepare(`UPDATE notes
|
|
6529
|
-
SET compiled_check = ?,
|
|
6530
|
-
manifest_json = ?,
|
|
6531
|
-
check_hash = ?,
|
|
6532
|
-
check_cron = ?,
|
|
6533
|
-
check_version = 1,
|
|
6534
|
-
check_status = 'compiled',
|
|
6535
|
-
check_failure_count = 0,
|
|
6536
|
-
check_network_failure_count = 0,
|
|
6537
|
-
check_quarantined_until = NULL,
|
|
6538
|
-
check_next_due_at = ?,
|
|
6539
|
-
check_compiled_at = ?,
|
|
6540
|
-
check_false_since_at = COALESCE(check_false_since_at, ?),
|
|
6541
|
-
check_last_liveness_at = NULL,
|
|
6542
|
-
policy_version = ?,
|
|
6543
|
-
updated_at = ?
|
|
6544
|
-
WHERE id = ? AND type = 'smart'`).run(args.compiledCheck, JSON.stringify(args.manifest), args.checkHash, args.checkCron, args.nextDueAt, args.now, args.now, SMART_NOTE_CHECK_POLICY_VERSION, args.now, args.noteId);
|
|
6545
|
-
}
|
|
6546
|
-
function markCompiledCheckFalse(db, noteId, nextDueAt, now) {
|
|
6547
|
-
db.prepare(`UPDATE notes
|
|
6548
|
-
SET last_checked_at = ?,
|
|
6549
|
-
updated_at = ?,
|
|
6550
|
-
check_next_due_at = ?,
|
|
6551
|
-
check_failure_count = 0,
|
|
6552
|
-
check_network_failure_count = 0,
|
|
6553
|
-
check_false_since_at = COALESCE(check_false_since_at, ?)
|
|
6554
|
-
WHERE id = ? AND type = 'smart'`).run(now, now, nextDueAt, now, noteId);
|
|
6555
|
-
}
|
|
6556
|
-
function markCompiledCheckLogicFailure(db, noteId, now, maxFailures) {
|
|
6557
|
-
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
6558
|
-
const status = failureCount >= maxFailures ? "failing" : "compiled";
|
|
6559
|
-
db.prepare(`UPDATE notes
|
|
6560
|
-
SET check_failure_count = ?,
|
|
6561
|
-
check_status = ?,
|
|
6562
|
-
check_next_due_at = ?,
|
|
6563
|
-
updated_at = ?
|
|
6564
|
-
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
6881
|
+
}
|
|
6882
|
+
return values;
|
|
6565
6883
|
}
|
|
6566
|
-
function
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
const
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6884
|
+
function normalizeCron(cron) {
|
|
6885
|
+
if (cron.length > MAX_CRON_CHARS)
|
|
6886
|
+
throw new Error("check_cron exceeds 256 characters");
|
|
6887
|
+
const normalized = cron.trim() || "0 * * * *";
|
|
6888
|
+
const parsed = parseCron(normalized);
|
|
6889
|
+
if (!parsed.ok)
|
|
6890
|
+
throw new Error(`invalid check_cron: ${parsed.error}`);
|
|
6891
|
+
const next = nextOccurrence(parsed.cron, new Date, undefined, SMART_NOTE_CHECK_CEILING_MS);
|
|
6892
|
+
if (!next)
|
|
6893
|
+
throw new Error("check_cron has no occurrence within the scheduling ceiling");
|
|
6894
|
+
return normalized;
|
|
6577
6895
|
}
|
|
6578
|
-
function
|
|
6579
|
-
|
|
6580
|
-
SET check_last_liveness_at = ?, updated_at = ?
|
|
6581
|
-
WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
|
|
6896
|
+
function unique(items) {
|
|
6897
|
+
return [...new Set(items)];
|
|
6582
6898
|
}
|
|
6583
|
-
function
|
|
6584
|
-
|
|
6899
|
+
function uniqueStrings(items, normalize = (value) => value) {
|
|
6900
|
+
if (!Array.isArray(items))
|
|
6901
|
+
return;
|
|
6902
|
+
const values = unique(items.slice(0, MAX_MANIFEST_ENTRIES).filter((item) => typeof item === "string" && item.length > 0).map(normalize));
|
|
6903
|
+
return values.length > 0 ? values : undefined;
|
|
6585
6904
|
}
|
|
6586
|
-
function
|
|
6587
|
-
|
|
6588
|
-
const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
6589
|
-
db.prepare(`UPDATE notes
|
|
6590
|
-
SET check_failure_count = ?,
|
|
6591
|
-
check_status = ?,
|
|
6592
|
-
check_next_due_at = ?,
|
|
6593
|
-
updated_at = ?
|
|
6594
|
-
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
6905
|
+
function boundedError(error) {
|
|
6906
|
+
return error.slice(0, MAX_COMPILER_ERROR_CHARS);
|
|
6595
6907
|
}
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
const
|
|
6600
|
-
|
|
6908
|
+
|
|
6909
|
+
// ../plugin/src/features/magic-context/smart-notes/schedule.ts
|
|
6910
|
+
function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
6911
|
+
const now = options.now ?? Date.now();
|
|
6912
|
+
const floorMs = options.floorMs ?? SMART_NOTE_CHECK_FLOOR_MS;
|
|
6913
|
+
const ceilingMs = options.ceilingMs ?? SMART_NOTE_CHECK_CEILING_MS;
|
|
6914
|
+
const rawNext = cron?.trim() ? nextDueAtMs(cron, now, undefined, ceilingMs) : null;
|
|
6915
|
+
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
6916
|
+
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
6917
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
6918
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
6919
|
+
return now + bounded;
|
|
6601
6920
|
}
|
|
6602
|
-
function
|
|
6603
|
-
const
|
|
6604
|
-
|
|
6921
|
+
function deterministicJitterMs(intervalMs, noteId, hash) {
|
|
6922
|
+
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
6923
|
+
if (max <= 0)
|
|
6924
|
+
return 0;
|
|
6925
|
+
const seed = `${noteId ?? 0}:${hash ?? ""}`;
|
|
6926
|
+
let h = 2166136261;
|
|
6927
|
+
for (let i = 0;i < seed.length; i++) {
|
|
6928
|
+
h ^= seed.charCodeAt(i);
|
|
6929
|
+
h = Math.imul(h, 16777619);
|
|
6930
|
+
}
|
|
6931
|
+
const unsigned = h >>> 0;
|
|
6932
|
+
return unsigned % (max * 2 + 1) - max;
|
|
6605
6933
|
}
|
|
6606
6934
|
|
|
6607
6935
|
// ../plugin/src/features/magic-context/smart-notes/runner.ts
|
|
@@ -7337,6 +7665,7 @@ async function mapMemories(args) {
|
|
|
7337
7665
|
result.independent += counts.independent;
|
|
7338
7666
|
result.remaining -= counts.mapped + counts.independent;
|
|
7339
7667
|
result.batches += 1;
|
|
7668
|
+
args.onProgress?.(result.mapped + result.independent);
|
|
7340
7669
|
}
|
|
7341
7670
|
result.complete = result.remaining === 0;
|
|
7342
7671
|
log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
|
|
@@ -7938,6 +8267,7 @@ async function refreshPrimers(args) {
|
|
|
7938
8267
|
result.refreshed += 1;
|
|
7939
8268
|
else
|
|
7940
8269
|
result.skipped += 1;
|
|
8270
|
+
args.onProgress?.(result.refreshed + result.skipped);
|
|
7941
8271
|
}
|
|
7942
8272
|
log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
|
|
7943
8273
|
return result;
|
|
@@ -8523,69 +8853,6 @@ function insertDreamRun(db, run) {
|
|
|
8523
8853
|
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);
|
|
8524
8854
|
}
|
|
8525
8855
|
|
|
8526
|
-
// ../plugin/src/features/magic-context/dreamer/storage-task-schedule.ts
|
|
8527
|
-
function toRow(r) {
|
|
8528
|
-
return {
|
|
8529
|
-
projectPath: r.project_path,
|
|
8530
|
-
task: r.task,
|
|
8531
|
-
lastRunAt: r.last_run_at,
|
|
8532
|
-
nextDueAt: r.next_due_at,
|
|
8533
|
-
schedule: r.schedule ?? null,
|
|
8534
|
-
lastStatus: r.last_status ?? null,
|
|
8535
|
-
lastError: r.last_error,
|
|
8536
|
-
retryCount: r.retry_count ?? 0,
|
|
8537
|
-
lastCheckedCommit: r.last_checked_commit ?? null,
|
|
8538
|
-
lastBroadRunAt: r.last_broad_run_at ?? null,
|
|
8539
|
-
retrospectiveWatermarkMs: r.retrospective_watermark_ms ?? null
|
|
8540
|
-
};
|
|
8541
|
-
}
|
|
8542
|
-
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";
|
|
8543
|
-
function getTaskScheduleState(db, projectPath, task) {
|
|
8544
|
-
const row = db.prepare(`SELECT ${SELECT_COLUMNS} FROM task_schedule_state WHERE project_path = ? AND task = ?`).get(projectPath, task);
|
|
8545
|
-
return row ? toRow(row) : null;
|
|
8546
|
-
}
|
|
8547
|
-
function getMostRecentTaskRunAt(db, projectPath) {
|
|
8548
|
-
const row = db.prepare("SELECT MAX(last_run_at) AS max_at FROM task_schedule_state WHERE project_path = ?").get(projectPath);
|
|
8549
|
-
const value = row?.max_at ?? null;
|
|
8550
|
-
return typeof value === "number" && value > 0 ? value : null;
|
|
8551
|
-
}
|
|
8552
|
-
function pruneNonCanonicalTaskRows(db, projectPath, canonicalTasks) {
|
|
8553
|
-
if (canonicalTasks.length === 0)
|
|
8554
|
-
return 0;
|
|
8555
|
-
const placeholders = canonicalTasks.map(() => "?").join(", ");
|
|
8556
|
-
const result = db.prepare(`DELETE FROM task_schedule_state WHERE project_path = ? AND task NOT IN (${placeholders})`).run(projectPath, ...canonicalTasks);
|
|
8557
|
-
return Number(result.changes ?? 0);
|
|
8558
|
-
}
|
|
8559
|
-
function deleteTaskScheduleRowsForProject(db, projectPath) {
|
|
8560
|
-
const result = db.prepare("DELETE FROM task_schedule_state WHERE project_path = ?").run(projectPath);
|
|
8561
|
-
return Number(result.changes ?? 0);
|
|
8562
|
-
}
|
|
8563
|
-
function seedTaskScheduleState(db, projectPath, task, nextDueAt, lastRunAt, schedule) {
|
|
8564
|
-
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);
|
|
8565
|
-
}
|
|
8566
|
-
function writeTaskScheduleState(db, row) {
|
|
8567
|
-
db.prepare(`INSERT INTO task_schedule_state
|
|
8568
|
-
(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)
|
|
8569
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8570
|
-
ON CONFLICT(project_path, task) DO UPDATE SET
|
|
8571
|
-
last_run_at = excluded.last_run_at,
|
|
8572
|
-
next_due_at = excluded.next_due_at,
|
|
8573
|
-
schedule = excluded.schedule,
|
|
8574
|
-
last_status = excluded.last_status,
|
|
8575
|
-
last_error = excluded.last_error,
|
|
8576
|
-
retry_count = excluded.retry_count,
|
|
8577
|
-
last_checked_commit = COALESCE(excluded.last_checked_commit, task_schedule_state.last_checked_commit),
|
|
8578
|
-
last_broad_run_at = COALESCE(excluded.last_broad_run_at, task_schedule_state.last_broad_run_at),
|
|
8579
|
-
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);
|
|
8580
|
-
}
|
|
8581
|
-
function isRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
8582
|
-
const row = db.prepare("SELECT 1 AS one FROM retrospective_processed_windows WHERE project_path = ? AND window_key = ?").get(projectPath, windowKey);
|
|
8583
|
-
return row != null;
|
|
8584
|
-
}
|
|
8585
|
-
function recordRetrospectiveWindowProcessed(db, projectPath, windowKey) {
|
|
8586
|
-
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());
|
|
8587
|
-
}
|
|
8588
|
-
|
|
8589
8856
|
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
8590
8857
|
import { createHash as createHash5 } from "node:crypto";
|
|
8591
8858
|
|
|
@@ -8594,6 +8861,30 @@ import path5 from "node:path";
|
|
|
8594
8861
|
function minOf(values) {
|
|
8595
8862
|
return values.reduce((acc, v) => v < acc ? v : acc, Number.POSITIVE_INFINITY);
|
|
8596
8863
|
}
|
|
8864
|
+
function ensureBroadCycleStart(args) {
|
|
8865
|
+
const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
8866
|
+
if (current?.lastBroadRunAt != null && current.lastBroadRunAt > 0) {
|
|
8867
|
+
return current.lastBroadRunAt;
|
|
8868
|
+
}
|
|
8869
|
+
if (!current)
|
|
8870
|
+
return args.runStartedAt;
|
|
8871
|
+
if (!args.holderId || !args.leaseKey) {
|
|
8872
|
+
throw new Error("verify-broad cycle opening requires the task lease");
|
|
8873
|
+
}
|
|
8874
|
+
return runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
8875
|
+
const latest = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
8876
|
+
if (!latest)
|
|
8877
|
+
return args.runStartedAt;
|
|
8878
|
+
if (latest.lastBroadRunAt != null && latest.lastBroadRunAt > 0) {
|
|
8879
|
+
return latest.lastBroadRunAt;
|
|
8880
|
+
}
|
|
8881
|
+
writeTaskScheduleState(args.db, {
|
|
8882
|
+
...latest,
|
|
8883
|
+
lastBroadRunAt: args.runStartedAt
|
|
8884
|
+
});
|
|
8885
|
+
return args.runStartedAt;
|
|
8886
|
+
});
|
|
8887
|
+
}
|
|
8597
8888
|
async function partitionVerifyScope(args) {
|
|
8598
8889
|
const runStartedAt = args.now ?? Date.now();
|
|
8599
8890
|
const active = getMemoriesByProject(args.db, args.projectIdentity);
|
|
@@ -8605,6 +8896,29 @@ async function partitionVerifyScope(args) {
|
|
|
8605
8896
|
content: m.content,
|
|
8606
8897
|
mappedFiles: verById.get(m.id)?.files ?? []
|
|
8607
8898
|
});
|
|
8899
|
+
if (args.forceBroad) {
|
|
8900
|
+
const broadCycleStartAt = ensureBroadCycleStart({
|
|
8901
|
+
db: args.db,
|
|
8902
|
+
projectIdentity: args.projectIdentity,
|
|
8903
|
+
holderId: args.holderId,
|
|
8904
|
+
leaseKey: args.leaseKey,
|
|
8905
|
+
runStartedAt
|
|
8906
|
+
});
|
|
8907
|
+
const broadCandidates = candidates.filter((m) => (verById.get(m.id)?.verifiedAt ?? 0) < broadCycleStartAt).sort((a, b) => {
|
|
8908
|
+
const verifiedAtA = verById.get(a.id)?.verifiedAt ?? 0;
|
|
8909
|
+
const verifiedAtB = verById.get(b.id)?.verifiedAt ?? 0;
|
|
8910
|
+
return verifiedAtA - verifiedAtB || a.id - b.id;
|
|
8911
|
+
});
|
|
8912
|
+
return {
|
|
8913
|
+
runStartedAt,
|
|
8914
|
+
mode: "broad",
|
|
8915
|
+
inScope: broadCandidates.map(toPrompt),
|
|
8916
|
+
inScopeIds: broadCandidates.map((m) => m.id),
|
|
8917
|
+
skippedIds: candidates.filter((m) => !broadCandidates.some((candidate) => candidate.id === m.id)).map((m) => m.id),
|
|
8918
|
+
broadCycleStartAt,
|
|
8919
|
+
reason: `broad cycle (${broadCandidates.length} remain; started ${broadCycleStartAt})`
|
|
8920
|
+
};
|
|
8921
|
+
}
|
|
8608
8922
|
if (candidates.length === 0) {
|
|
8609
8923
|
return {
|
|
8610
8924
|
runStartedAt,
|
|
@@ -8615,16 +8929,6 @@ async function partitionVerifyScope(args) {
|
|
|
8615
8929
|
reason: "no file-mapped memories in scope"
|
|
8616
8930
|
};
|
|
8617
8931
|
}
|
|
8618
|
-
if (args.forceBroad) {
|
|
8619
|
-
return {
|
|
8620
|
-
runStartedAt,
|
|
8621
|
-
mode: "broad",
|
|
8622
|
-
inScope: candidates.map(toPrompt),
|
|
8623
|
-
inScopeIds: candidates.map((m) => m.id),
|
|
8624
|
-
skippedIds: [],
|
|
8625
|
-
reason: "broad full-pool verification of file-mapped memories"
|
|
8626
|
-
};
|
|
8627
|
-
}
|
|
8628
8932
|
const allInScope = (mode, reason) => ({
|
|
8629
8933
|
runStartedAt,
|
|
8630
8934
|
mode,
|
|
@@ -8743,7 +9047,25 @@ function parseVerifyManifest(text) {
|
|
|
8743
9047
|
|
|
8744
9048
|
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
8745
9049
|
var VERIFY_BATCH_SIZE = 50;
|
|
9050
|
+
function closeBroadCycle(args, cycleStartAt) {
|
|
9051
|
+
if (!args.forceBroad || cycleStartAt === undefined)
|
|
9052
|
+
return;
|
|
9053
|
+
if (!getTaskScheduleState(args.db, args.projectIdentity, "verify-broad"))
|
|
9054
|
+
return;
|
|
9055
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
9056
|
+
const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
|
|
9057
|
+
if (current?.lastBroadRunAt !== cycleStartAt)
|
|
9058
|
+
return;
|
|
9059
|
+
if (!current)
|
|
9060
|
+
return;
|
|
9061
|
+
writeTaskScheduleState(args.db, {
|
|
9062
|
+
...current,
|
|
9063
|
+
lastBroadRunAt: null
|
|
9064
|
+
});
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
8746
9067
|
async function runVerify(args) {
|
|
9068
|
+
const runStartedAt = Date.now();
|
|
8747
9069
|
const result = {
|
|
8748
9070
|
verified: 0,
|
|
8749
9071
|
updated: 0,
|
|
@@ -8758,14 +9080,20 @@ async function runVerify(args) {
|
|
|
8758
9080
|
db: args.db,
|
|
8759
9081
|
projectIdentity: args.projectIdentity,
|
|
8760
9082
|
projectDirectory: args.sessionDirectory,
|
|
8761
|
-
forceBroad: args.forceBroad
|
|
9083
|
+
forceBroad: args.forceBroad,
|
|
9084
|
+
now: runStartedAt,
|
|
9085
|
+
holderId: args.holderId,
|
|
9086
|
+
leaseKey: args.leaseKey
|
|
8762
9087
|
});
|
|
8763
9088
|
result.mode = gate.mode;
|
|
9089
|
+
result.broadCycleStartAt = gate.broadCycleStartAt;
|
|
8764
9090
|
result.inScope = gate.inScope.length;
|
|
8765
9091
|
result.remaining = gate.inScope.length;
|
|
8766
9092
|
log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"} gate: mode=${gate.mode} in_scope=${gate.inScope.length} skipped=${gate.skippedIds.length} reason=${gate.reason}`);
|
|
8767
|
-
if (gate.inScope.length === 0)
|
|
9093
|
+
if (gate.inScope.length === 0) {
|
|
9094
|
+
closeBroadCycle(args, gate.broadCycleStartAt);
|
|
8768
9095
|
return result;
|
|
9096
|
+
}
|
|
8769
9097
|
const batches = [];
|
|
8770
9098
|
for (let i = 0;i < gate.inScope.length; i += VERIFY_BATCH_SIZE) {
|
|
8771
9099
|
batches.push(gate.inScope.slice(i, i + VERIFY_BATCH_SIZE));
|
|
@@ -8785,8 +9113,11 @@ async function runVerify(args) {
|
|
|
8785
9113
|
result.archived += counts.archived;
|
|
8786
9114
|
result.remaining -= counts.verified + counts.updated + counts.archived;
|
|
8787
9115
|
result.batches += 1;
|
|
9116
|
+
args.onProgress?.(result.verified + result.updated + result.archived);
|
|
8788
9117
|
}
|
|
8789
9118
|
result.complete = result.remaining === 0;
|
|
9119
|
+
if (result.complete)
|
|
9120
|
+
closeBroadCycle(args, gate.broadCycleStartAt);
|
|
8790
9121
|
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}`);
|
|
8791
9122
|
return result;
|
|
8792
9123
|
} finally {
|
|
@@ -9076,6 +9407,20 @@ function createDreamTaskExecutor(deps) {
|
|
|
9076
9407
|
const { db, projectIdentity, holderId, leaseKey } = ctx;
|
|
9077
9408
|
const startedAt = Date.now();
|
|
9078
9409
|
const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
|
|
9410
|
+
const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9411
|
+
const reportProgress = (processed) => {
|
|
9412
|
+
deps.onProgress?.({
|
|
9413
|
+
task: config.task,
|
|
9414
|
+
processed: Math.max(0, processed),
|
|
9415
|
+
total: backlogAtStart.pending,
|
|
9416
|
+
startedAt
|
|
9417
|
+
});
|
|
9418
|
+
};
|
|
9419
|
+
reportProgress(0);
|
|
9420
|
+
const incompleteMessage = (remaining) => {
|
|
9421
|
+
const processed = processedDreamTaskItems(backlogAtStart.pending, remaining);
|
|
9422
|
+
return `${config.task} incomplete: ${remaining} remain (was ${backlogAtStart.pending} at run start; processed ${processed} this run)`;
|
|
9423
|
+
};
|
|
9079
9424
|
const parent = await resolveParentSessionId();
|
|
9080
9425
|
let moduleRoute;
|
|
9081
9426
|
if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
|
|
@@ -9104,7 +9449,19 @@ function createDreamTaskExecutor(deps) {
|
|
|
9104
9449
|
name: config.task,
|
|
9105
9450
|
durationMs: Date.now() - startedAt,
|
|
9106
9451
|
resultChars: 0,
|
|
9107
|
-
...error ? { error } : {}
|
|
9452
|
+
...error ? { error } : {},
|
|
9453
|
+
backlog: (() => {
|
|
9454
|
+
const end = extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9455
|
+
const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
|
|
9456
|
+
const value = {
|
|
9457
|
+
pendingAtStart: backlogAtStart.pending,
|
|
9458
|
+
totalAtStart: backlogAtStart.total,
|
|
9459
|
+
pendingAtEnd: end.pending,
|
|
9460
|
+
totalAtEnd: end.total,
|
|
9461
|
+
processed
|
|
9462
|
+
};
|
|
9463
|
+
return value;
|
|
9464
|
+
})()
|
|
9108
9465
|
}
|
|
9109
9466
|
],
|
|
9110
9467
|
tasksSucceeded: status === "completed" ? 1 : 0,
|
|
@@ -9138,8 +9495,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
9138
9495
|
}
|
|
9139
9496
|
try {
|
|
9140
9497
|
if (config.task === "compress-cues") {
|
|
9141
|
-
if (deps.
|
|
9142
|
-
log("[dreamer] compress-cues: skipped (
|
|
9498
|
+
if (deps.mural?.enabled !== true) {
|
|
9499
|
+
log("[dreamer] compress-cues: skipped (mural is not enabled)");
|
|
9143
9500
|
recordRun("completed", null);
|
|
9144
9501
|
return { status: "completed" };
|
|
9145
9502
|
}
|
|
@@ -9158,12 +9515,13 @@ function createDreamTaskExecutor(deps) {
|
|
|
9158
9515
|
holderId,
|
|
9159
9516
|
leaseKey,
|
|
9160
9517
|
deadline,
|
|
9161
|
-
model: config.model ?? deps.
|
|
9162
|
-
fallbackModels: config.fallbackModels
|
|
9518
|
+
model: config.model ?? deps.mural.model ?? deps.dreamerModel,
|
|
9519
|
+
fallbackModels: config.fallbackModels,
|
|
9520
|
+
onProgress: (processed) => reportProgress(processed)
|
|
9163
9521
|
});
|
|
9164
9522
|
log(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
|
|
9165
9523
|
if (!result.complete) {
|
|
9166
|
-
const error =
|
|
9524
|
+
const error = incompleteMessage(result.remaining);
|
|
9167
9525
|
recordRun("failed", error);
|
|
9168
9526
|
return { status: "failed", transient: true, error };
|
|
9169
9527
|
}
|
|
@@ -9200,11 +9558,12 @@ function createDreamTaskExecutor(deps) {
|
|
|
9200
9558
|
deadline,
|
|
9201
9559
|
model: config.model,
|
|
9202
9560
|
fallbackModels: config.fallbackModels,
|
|
9203
|
-
moduleRoute
|
|
9561
|
+
moduleRoute,
|
|
9562
|
+
onProgress: (processed) => reportProgress(processed)
|
|
9204
9563
|
});
|
|
9205
9564
|
log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining}`);
|
|
9206
9565
|
if (!result.complete) {
|
|
9207
|
-
const error =
|
|
9566
|
+
const error = incompleteMessage(result.remaining);
|
|
9208
9567
|
recordRun("failed", error);
|
|
9209
9568
|
return { status: "failed", transient: true, error };
|
|
9210
9569
|
}
|
|
@@ -9226,19 +9585,32 @@ function createDreamTaskExecutor(deps) {
|
|
|
9226
9585
|
model: config.model,
|
|
9227
9586
|
fallbackModels: config.fallbackModels,
|
|
9228
9587
|
language: config.language ?? deps.language,
|
|
9229
|
-
moduleRoute
|
|
9588
|
+
moduleRoute,
|
|
9589
|
+
onProgress: (processed2) => reportProgress(processed2)
|
|
9230
9590
|
});
|
|
9591
|
+
const processed = result.verified + result.updated + result.archived;
|
|
9592
|
+
const broadProgress = config.task === "verify-broad" ? `verify-broad cycle ${result.broadCycleStartAt ?? "open"}: verified ${processed}, ${result.remaining} remain` : null;
|
|
9593
|
+
const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
|
|
9231
9594
|
if (!result.complete) {
|
|
9232
|
-
|
|
9595
|
+
if (broadProgress && processed > 0) {
|
|
9596
|
+
recordRun("completed", broadProgress, {
|
|
9597
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9598
|
+
backlogAfter
|
|
9599
|
+
});
|
|
9600
|
+
return { status: "completed", error: broadProgress };
|
|
9601
|
+
}
|
|
9602
|
+
const error = incompleteMessage(result.remaining);
|
|
9233
9603
|
recordRun("failed", error, {
|
|
9234
|
-
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
9604
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9605
|
+
backlogAfter
|
|
9235
9606
|
});
|
|
9236
9607
|
return { status: "failed", transient: true, error };
|
|
9237
9608
|
}
|
|
9238
|
-
recordRun("completed",
|
|
9239
|
-
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
9609
|
+
recordRun("completed", broadProgress, {
|
|
9610
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
9611
|
+
backlogAfter
|
|
9240
9612
|
});
|
|
9241
|
-
return { status: "completed" };
|
|
9613
|
+
return broadProgress ? { status: "completed", error: broadProgress } : { status: "completed" };
|
|
9242
9614
|
}
|
|
9243
9615
|
if (config.task === "classify-memories") {
|
|
9244
9616
|
let moduleArgs;
|
|
@@ -9288,11 +9660,12 @@ function createDreamTaskExecutor(deps) {
|
|
|
9288
9660
|
deadline,
|
|
9289
9661
|
model: config.model,
|
|
9290
9662
|
fallbackModels: config.fallbackModels,
|
|
9291
|
-
...moduleArgs
|
|
9663
|
+
...moduleArgs,
|
|
9664
|
+
onProgress: (processed) => reportProgress(processed)
|
|
9292
9665
|
});
|
|
9293
9666
|
log(`[dreamer] classify-memories: stage=${result.stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining}`);
|
|
9294
9667
|
if (!result.complete) {
|
|
9295
|
-
const error =
|
|
9668
|
+
const error = incompleteMessage(result.remaining);
|
|
9296
9669
|
recordRun("failed", error);
|
|
9297
9670
|
return { status: "failed", transient: true, error };
|
|
9298
9671
|
}
|
|
@@ -9328,7 +9701,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
9328
9701
|
model: config.model,
|
|
9329
9702
|
fallbackModels: config.fallbackModels,
|
|
9330
9703
|
language: config.language ?? deps.language,
|
|
9331
|
-
rawProviderFactory: deps.primerRawProviderFactory
|
|
9704
|
+
rawProviderFactory: deps.primerRawProviderFactory,
|
|
9705
|
+
onProgress: (processed) => reportProgress(processed)
|
|
9332
9706
|
});
|
|
9333
9707
|
recordRun("completed", null);
|
|
9334
9708
|
log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
|
|
@@ -9382,6 +9756,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
9382
9756
|
recordRun("failed", brief);
|
|
9383
9757
|
log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
|
|
9384
9758
|
return { status: "failed", transient, error: brief };
|
|
9759
|
+
} finally {
|
|
9760
|
+
deps.onProgress?.(null, config.task);
|
|
9385
9761
|
}
|
|
9386
9762
|
};
|
|
9387
9763
|
}
|
|
@@ -9757,66 +10133,6 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
9757
10133
|
}
|
|
9758
10134
|
}
|
|
9759
10135
|
|
|
9760
|
-
// ../plugin/src/features/magic-context/dreamer/task-gates.ts
|
|
9761
|
-
function countActiveMemories(db, projectPath) {
|
|
9762
|
-
const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
|
|
9763
|
-
return row?.cnt ?? 0;
|
|
9764
|
-
}
|
|
9765
|
-
function countUnmappedActiveMemories(db, projectPath) {
|
|
9766
|
-
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
9767
|
-
FROM memories m
|
|
9768
|
-
WHERE m.project_path = ?
|
|
9769
|
-
AND m.status IN ('active','permanent')
|
|
9770
|
-
AND NOT EXISTS (
|
|
9771
|
-
SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
|
|
9772
|
-
)`).get(projectPath);
|
|
9773
|
-
return row?.cnt ?? 0;
|
|
9774
|
-
}
|
|
9775
|
-
function countCompartmentsSince(db, projectPath, since) {
|
|
9776
|
-
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
9777
|
-
FROM compartments c
|
|
9778
|
-
JOIN session_projects sp ON sp.session_id = c.session_id
|
|
9779
|
-
WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
|
|
9780
|
-
return row?.cnt ?? 0;
|
|
9781
|
-
}
|
|
9782
|
-
function countProjectSessionsSince(db, projectPath, since) {
|
|
9783
|
-
const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
|
|
9784
|
-
return row?.cnt ?? 0;
|
|
9785
|
-
}
|
|
9786
|
-
function evaluateTaskGate(task, ctx) {
|
|
9787
|
-
const { db, projectIdentity: project, lastRunAt } = ctx;
|
|
9788
|
-
switch (task) {
|
|
9789
|
-
case "map-memories":
|
|
9790
|
-
return countUnmappedActiveMemories(db, project) > 0;
|
|
9791
|
-
case "verify":
|
|
9792
|
-
return countActiveMemories(db, project) > 0;
|
|
9793
|
-
case "verify-broad":
|
|
9794
|
-
return countActiveMemories(db, project) > 0;
|
|
9795
|
-
case "curate":
|
|
9796
|
-
return countActiveMemories(db, project) > 0;
|
|
9797
|
-
case "compress-cues":
|
|
9798
|
-
return countActiveMemories(db, project) > 0;
|
|
9799
|
-
case "classify-memories":
|
|
9800
|
-
return countActiveMemories(db, project) > 0;
|
|
9801
|
-
case "retrospective":
|
|
9802
|
-
return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
|
|
9803
|
-
case "maintain-docs":
|
|
9804
|
-
return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
|
|
9805
|
-
case "evaluate-smart-notes":
|
|
9806
|
-
return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
|
|
9807
|
-
case "review-user-memories":
|
|
9808
|
-
return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
|
|
9809
|
-
case "promote-primers":
|
|
9810
|
-
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
|
|
9811
|
-
case "refresh-primers":
|
|
9812
|
-
return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
|
|
9813
|
-
default: {
|
|
9814
|
-
const _exhaustive = task;
|
|
9815
|
-
return Boolean(_exhaustive);
|
|
9816
|
-
}
|
|
9817
|
-
}
|
|
9818
|
-
}
|
|
9819
|
-
|
|
9820
10136
|
// ../plugin/src/features/magic-context/dreamer/task-scheduler.ts
|
|
9821
10137
|
var MAX_TASK_RETRIES = 3;
|
|
9822
10138
|
function ensureSeeded(db, projectIdentity, config, now) {
|
|
@@ -9964,10 +10280,10 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
9964
10280
|
cb?.onRan?.(due.config.task);
|
|
9965
10281
|
} else if (outcome.transient) {
|
|
9966
10282
|
recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
|
|
9967
|
-
cb?.onFailed?.(due.config.task);
|
|
10283
|
+
cb?.onFailed?.(due.config.task, outcome.error);
|
|
9968
10284
|
} else {
|
|
9969
10285
|
advanceAfterRun(db, projectIdentity, due, finishedAt, "failed", outcome.error ?? null);
|
|
9970
|
-
cb?.onFailed?.(due.config.task);
|
|
10286
|
+
cb?.onFailed?.(due.config.task, outcome.error);
|
|
9971
10287
|
}
|
|
9972
10288
|
}
|
|
9973
10289
|
} finally {
|
|
@@ -9976,7 +10292,15 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
9976
10292
|
}
|
|
9977
10293
|
async function runManualDream(deps) {
|
|
9978
10294
|
const now = Date.now();
|
|
9979
|
-
const result = {
|
|
10295
|
+
const result = {
|
|
10296
|
+
ran: [],
|
|
10297
|
+
skippedNoWork: [],
|
|
10298
|
+
deferredBusy: [],
|
|
10299
|
+
failed: [],
|
|
10300
|
+
failureDetails: [],
|
|
10301
|
+
backlogBefore: {},
|
|
10302
|
+
backlogAfter: {}
|
|
10303
|
+
};
|
|
9980
10304
|
let selected;
|
|
9981
10305
|
let forceGate = false;
|
|
9982
10306
|
if (deps.task) {
|
|
@@ -9990,6 +10314,9 @@ async function runManualDream(deps) {
|
|
|
9990
10314
|
}
|
|
9991
10315
|
if (selected.length === 0)
|
|
9992
10316
|
return result;
|
|
10317
|
+
const selectedTaskNames = selected.map((config) => config.task);
|
|
10318
|
+
result.backlogBefore = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
10319
|
+
result.backlogAfter = { ...result.backlogBefore };
|
|
9993
10320
|
for (const cfg of selected)
|
|
9994
10321
|
ensureSeeded(deps.db, deps.projectIdentity, cfg, now);
|
|
9995
10322
|
const dueAll = selected.map((config) => ({ config, scheduledAt: now }));
|
|
@@ -10011,8 +10338,10 @@ async function runManualDream(deps) {
|
|
|
10011
10338
|
else
|
|
10012
10339
|
result.skippedNoWork.push(d.config.task);
|
|
10013
10340
|
}
|
|
10014
|
-
if (gated.length === 0)
|
|
10341
|
+
if (gated.length === 0) {
|
|
10342
|
+
result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
10015
10343
|
return result;
|
|
10344
|
+
}
|
|
10016
10345
|
const groups = new Map;
|
|
10017
10346
|
for (const d of gated) {
|
|
10018
10347
|
const kind = leaseKindFor(d.config.task);
|
|
@@ -10024,9 +10353,14 @@ async function runManualDream(deps) {
|
|
|
10024
10353
|
forceGate,
|
|
10025
10354
|
leaseWaitMs: MANUAL_RUN_LEASE_WAIT_MS,
|
|
10026
10355
|
onRan: (t) => result.ran.push(t),
|
|
10027
|
-
onFailed: (
|
|
10356
|
+
onFailed: (task, error) => {
|
|
10357
|
+
result.failed.push(task);
|
|
10358
|
+
if (error)
|
|
10359
|
+
result.failureDetails?.push(`${task}: ${error}`);
|
|
10360
|
+
},
|
|
10028
10361
|
onBusy: (t) => result.deferredBusy.push(t)
|
|
10029
10362
|
})));
|
|
10363
|
+
result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
10030
10364
|
return result;
|
|
10031
10365
|
}
|
|
10032
10366
|
async function runDueTasksForProject(deps) {
|
|
@@ -10341,10 +10675,11 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
|
|
|
10341
10675
|
ensureProjectRegistered: reg.ensureRegistered,
|
|
10342
10676
|
language: reg.language,
|
|
10343
10677
|
dreamerModel: dreamerConfig.model,
|
|
10344
|
-
|
|
10678
|
+
mural: reg.mural,
|
|
10345
10679
|
memoryInjectionBudgetTokens: reg.memoryInjectionBudgetTokens,
|
|
10346
10680
|
transformMode: reg.transformMode,
|
|
10347
|
-
moduleClient: reg.moduleClient
|
|
10681
|
+
moduleClient: reg.moduleClient,
|
|
10682
|
+
onProgress: (progress, completedTask) => reg.onDreamerProgress?.(progress, completedTask)
|
|
10348
10683
|
});
|
|
10349
10684
|
const ran = await runDueTasksForProject({
|
|
10350
10685
|
db,
|
|
@@ -11014,13 +11349,18 @@ Unknown task "${requested}".`,
|
|
|
11014
11349
|
});
|
|
11015
11350
|
return;
|
|
11016
11351
|
}
|
|
11352
|
+
const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
|
|
11353
|
+
const backlogBefore = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
|
|
11017
11354
|
sendCtxStatusMessage(pi, {
|
|
11018
11355
|
title: "/ctx-dream",
|
|
11019
11356
|
text: [
|
|
11020
11357
|
"## /ctx-dream",
|
|
11021
11358
|
"",
|
|
11022
11359
|
task ? `Running dream task "${task}" for ${project.projectIdentity}…` : `Starting dream run for ${project.projectIdentity}…`,
|
|
11023
|
-
`Project directory: ${project.projectDir}
|
|
11360
|
+
`Project directory: ${project.projectDir}`,
|
|
11361
|
+
"",
|
|
11362
|
+
"Backlog before starting:",
|
|
11363
|
+
formatDreamTaskBacklogs(backlogBefore, backlogTasks)
|
|
11024
11364
|
].join(`
|
|
11025
11365
|
`),
|
|
11026
11366
|
level: "info"
|
|
@@ -11035,10 +11375,16 @@ Unknown task "${requested}".`,
|
|
|
11035
11375
|
lines.push(`Ran: ${result.ran.join(", ")}`);
|
|
11036
11376
|
if (result.failed.length > 0)
|
|
11037
11377
|
lines.push(`Failed: ${result.failed.join(", ")}`);
|
|
11378
|
+
if ((result.failureDetails?.length ?? 0) > 0) {
|
|
11379
|
+
lines.push("Failure details:", ...(result.failureDetails ?? []).map((detail) => `- ${detail}`));
|
|
11380
|
+
}
|
|
11038
11381
|
if (result.skippedNoWork.length > 0)
|
|
11039
11382
|
lines.push(`Skipped (no work): ${result.skippedNoWork.join(", ")}`);
|
|
11040
11383
|
if (result.deferredBusy.length > 0)
|
|
11041
11384
|
lines.push(`Busy: ${result.deferredBusy.join(", ")} — another dream task holds this domain's lease; retry in a minute`);
|
|
11385
|
+
if (Object.keys(result.backlogAfter ?? {}).length > 0) {
|
|
11386
|
+
lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(result.backlogAfter));
|
|
11387
|
+
}
|
|
11042
11388
|
if (lines.length === 0)
|
|
11043
11389
|
lines.push("No enabled dream tasks to run.");
|
|
11044
11390
|
sendCtxStatusMessage(pi, {
|
|
@@ -27175,7 +27521,7 @@ function formatExecuteThreshold(detail, contextLimit) {
|
|
|
27175
27521
|
}
|
|
27176
27522
|
return `${percentage}%${clampNote}`;
|
|
27177
27523
|
}
|
|
27178
|
-
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
|
|
27524
|
+
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer) {
|
|
27179
27525
|
const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
|
|
27180
27526
|
tokensConfig: executeThresholdTokens,
|
|
27181
27527
|
contextLimit,
|
|
@@ -27230,6 +27576,11 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
27230
27576
|
`**Protected tags:** ${protectedTags}`,
|
|
27231
27577
|
`**Subagent session:** ${meta.isSubagent}`
|
|
27232
27578
|
];
|
|
27579
|
+
if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
|
|
27580
|
+
lines.push("", "### Dreamer", ...dreamer.progress ? [
|
|
27581
|
+
`- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
|
|
27582
|
+
] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
|
|
27583
|
+
}
|
|
27233
27584
|
if (meta.lastContextPercentage > 0 || meta.lastInputTokens > 0) {
|
|
27234
27585
|
lines.push("", "### Context Usage", `- Last percentage: ${meta.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, `- Proactive compartment evaluation: ${proactiveCompartmentTrigger}%`, `- Post-drop target for historian: ${(executeThresholdPercentage * POST_DROP_TARGET_RATIO).toFixed(0)}% (${executeThresholdPercentage}% * ${POST_DROP_TARGET_RATIO})`, `- Commit cluster trigger: ${commitClusterTrigger?.enabled !== false ? `enabled (min ${commitClusterTrigger?.min_clusters ?? 3} clusters)` : "disabled"}, tail-size trigger: > 3x compartment budget`);
|
|
27235
27586
|
}
|
|
@@ -27252,6 +27603,12 @@ ${c.content}
|
|
|
27252
27603
|
lines.push(`- §${op.tagId}§ → ${op.operation}`);
|
|
27253
27604
|
}
|
|
27254
27605
|
}
|
|
27606
|
+
if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
|
|
27607
|
+
lines.push("", "### Dreamer Backlog", formatDreamTaskBacklogs(dreamer.backlog));
|
|
27608
|
+
}
|
|
27609
|
+
if (dreamer?.progress) {
|
|
27610
|
+
lines.push("", "### Dreamer Progress", `- ${dreamer.progress.task}: ${dreamer.progress.processed}/${dreamer.progress.total} processed this run`);
|
|
27611
|
+
}
|
|
27255
27612
|
return lines.join(`
|
|
27256
27613
|
`);
|
|
27257
27614
|
} catch (error) {
|
|
@@ -27326,7 +27683,7 @@ ${c.content}
|
|
|
27326
27683
|
// package.json
|
|
27327
27684
|
var package_default = {
|
|
27328
27685
|
name: "@cortexkit/pi-magic-context",
|
|
27329
|
-
version: "0.34.
|
|
27686
|
+
version: "0.34.2",
|
|
27330
27687
|
type: "module",
|
|
27331
27688
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
27332
27689
|
main: "dist/index.js",
|
|
@@ -27834,7 +28191,9 @@ No active Pi session is available.`,
|
|
|
27834
28191
|
model: ctx.model,
|
|
27835
28192
|
detectedContextLimit
|
|
27836
28193
|
});
|
|
27837
|
-
const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit
|
|
28194
|
+
const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
|
|
28195
|
+
backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
|
|
28196
|
+
});
|
|
27838
28197
|
const details = buildStatusDetails(currentDeps, sessionId);
|
|
27839
28198
|
sendCtxStatusMessage(pi, { title: "/ctx-status", text: statusText, level: "info" }, details);
|
|
27840
28199
|
} catch (error) {
|
|
@@ -27876,6 +28235,7 @@ function buildStatusDetails(deps, sessionId) {
|
|
|
27876
28235
|
dreamer: {
|
|
27877
28236
|
enabled: deps.dreamer?.runnable === true,
|
|
27878
28237
|
scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
|
|
28238
|
+
backlog: getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS),
|
|
27879
28239
|
lastRunAt: getMostRecentTaskRunAt(deps.db, deps.projectIdentity)
|
|
27880
28240
|
},
|
|
27881
28241
|
historian: readHistorianState(deps.db, sessionId, meta)
|
|
@@ -28838,7 +29198,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28838
29198
|
injectDocs: cfg.dreamer?.inject_docs !== false,
|
|
28839
29199
|
injectionBudgetTokens: cfg.memory.injection_budget_tokens,
|
|
28840
29200
|
temporalAwareness: cfg.temporal_awareness === true,
|
|
28841
|
-
muralEnabled: cfg.
|
|
29201
|
+
muralEnabled: cfg.mural.enabled === true
|
|
28842
29202
|
},
|
|
28843
29203
|
scheduler: {
|
|
28844
29204
|
executeThresholdPercentage: cfg.execute_threshold_percentage,
|