@cortexkit/pi-magic-context 0.34.0 → 0.34.1

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.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,
@@ -361,7 +362,7 @@ import {
361
362
  withMigrationLanguageDirective,
362
363
  withRawMessageProvider,
363
364
  withRawSessionMessageCache
364
- } from "./index-0k3j1tc3.js";
365
+ } from "./index-ekzg3w5y.js";
365
366
  import {
366
367
  pushNotification
367
368
  } from "./index-hw34xmzk.js";
@@ -443,6 +444,16 @@ var CANONICAL_DREAM_TASKS = [
443
444
  "promote-primers",
444
445
  "refresh-primers"
445
446
  ];
447
+ function formatDreamTaskBacklogs(backlogs, tasks = CANONICAL_DREAM_TASKS) {
448
+ return tasks.filter((task) => backlogs[task] !== undefined).map((task) => {
449
+ const backlog = backlogs[task];
450
+ return `- ${task}: ${backlog?.pending ?? 0} pending / ${backlog?.total ?? 0} total`;
451
+ }).join(`
452
+ `);
453
+ }
454
+ function processedDreamTaskItems(startPending, endPending) {
455
+ return Math.max(0, startPending - endPending);
456
+ }
446
457
  var MEMORY_DOMAIN_TASKS = [
447
458
  "map-memories",
448
459
  "verify",
@@ -580,7 +591,7 @@ function formatFailClosedBlockingMessage(reason) {
580
591
  }
581
592
  if (reason.kind === "schema_fence") {
582
593
  return [
583
- `Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (database schema v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
594
+ `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
595
  `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
585
596
  ].join(" ");
586
597
  }
@@ -3072,6 +3083,394 @@ ${sidekickText}
3072
3083
  });
3073
3084
  }
3074
3085
 
3086
+ // ../plugin/src/features/magic-context/smart-notes/types.ts
3087
+ var SMART_NOTE_CHECK_POLICY_VERSION = 1;
3088
+ var SMART_NOTE_CHECK_FLOOR_MS = 5 * 60 * 1000;
3089
+ var SMART_NOTE_CHECK_CEILING_MS = 24 * 60 * 60 * 1000;
3090
+ var SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
3091
+ var SMART_NOTE_CHECK_MAX_STALENESS_MS = 7 * 24 * 60 * 60 * 1000;
3092
+ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
3093
+
3094
+ class SmartNoteNetworkError extends Error {
3095
+ isSmartNoteNetworkError = true;
3096
+ terminal;
3097
+ constructor(message, options = {}) {
3098
+ super(message);
3099
+ this.name = "SmartNoteNetworkError";
3100
+ this.terminal = options.terminal ?? false;
3101
+ }
3102
+ }
3103
+
3104
+ class SmartNoteSecurityError extends Error {
3105
+ isSmartNoteSecurityError = true;
3106
+ constructor(message) {
3107
+ super(message);
3108
+ this.name = "SmartNoteSecurityError";
3109
+ }
3110
+ }
3111
+ function isSmartNoteNetworkError(error) {
3112
+ return error instanceof SmartNoteNetworkError || error instanceof Error && (error.name === "SmartNoteNetworkError" || error.message.includes("SmartNoteNetworkError") || error.message.includes("SMART_NOTE_NETWORK"));
3113
+ }
3114
+ function isTerminalSmartNoteNetworkError(error) {
3115
+ return error instanceof SmartNoteNetworkError && error.terminal;
3116
+ }
3117
+ function parseSmartNoteManifest(json) {
3118
+ if (!json)
3119
+ return { capabilities: [] };
3120
+ try {
3121
+ const parsed = JSON.parse(json);
3122
+ const capabilities = Array.isArray(parsed.capabilities) ? parsed.capabilities.filter((c) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(c))) : [];
3123
+ return {
3124
+ capabilities,
3125
+ readFiles: stringArray(parsed.readFiles),
3126
+ hosts: stringArray(parsed.hosts),
3127
+ urls: stringArray(parsed.urls),
3128
+ signals: stringArray(parsed.signals),
3129
+ summary: typeof parsed.summary === "string" ? parsed.summary : undefined
3130
+ };
3131
+ } catch {
3132
+ return { capabilities: [] };
3133
+ }
3134
+ }
3135
+ function stringArray(value) {
3136
+ if (!Array.isArray(value))
3137
+ return;
3138
+ const arr = value.filter((item) => typeof item === "string");
3139
+ return arr.length > 0 ? arr : undefined;
3140
+ }
3141
+
3142
+ // ../plugin/src/features/magic-context/smart-notes/storage.ts
3143
+ function toSmartNote(note) {
3144
+ return {
3145
+ ...note,
3146
+ checkStatus: note.checkStatus ?? "uncompiled",
3147
+ checkFailureCount: note.checkFailureCount ?? 0,
3148
+ checkNetworkFailureCount: note.checkNetworkFailureCount ?? 0,
3149
+ policyVersion: note.policyVersion ?? 0
3150
+ };
3151
+ }
3152
+ function commitSmartNoteState(db, args) {
3153
+ db.exec("BEGIN IMMEDIATE");
3154
+ let leaseLost = false;
3155
+ let committed = false;
3156
+ try {
3157
+ if (args.leaseHeld && !args.leaseHeld()) {
3158
+ leaseLost = true;
3159
+ } else if (claimExpectedState(db, args.expected)) {
3160
+ args.write();
3161
+ committed = true;
3162
+ }
3163
+ db.exec("COMMIT");
3164
+ } catch (error) {
3165
+ try {
3166
+ db.exec("ROLLBACK");
3167
+ } catch {}
3168
+ throw error;
3169
+ }
3170
+ if (leaseLost) {
3171
+ throw new Error(`Dream lease lost during smart-note ${args.phase} commit`);
3172
+ }
3173
+ if (!committed) {
3174
+ log(`[debug] smart note #${args.expected.noteId}: discarded stale ${args.phase} result`);
3175
+ }
3176
+ return committed;
3177
+ }
3178
+ function claimExpectedState(db, expected) {
3179
+ if (expected.kind === "compiled-check") {
3180
+ return db.prepare(`UPDATE notes SET id = id
3181
+ WHERE id = ? AND type = 'smart' AND status = 'pending'
3182
+ AND check_status = 'compiled' AND compiled_check = ?
3183
+ AND check_hash IS ? AND check_compiled_at IS ?`).run(expected.noteId, expected.compiledCheck, expected.checkHash, expected.checkCompiledAt).changes > 0;
3184
+ }
3185
+ const statusClause = expected.checkStatus ? " AND check_status = ?" : "";
3186
+ const params = [
3187
+ expected.noteId,
3188
+ expected.content,
3189
+ expected.surfaceCondition,
3190
+ expected.updatedAt
3191
+ ];
3192
+ if (expected.checkStatus)
3193
+ params.push(expected.checkStatus);
3194
+ return db.prepare(`UPDATE notes SET id = id
3195
+ WHERE id = ? AND type = 'smart' AND status = 'pending'
3196
+ AND content = ? AND surface_condition IS ? AND updated_at = ?${statusClause}`).run(...params).changes > 0;
3197
+ }
3198
+ function getDueCompiledSmartNoteChecks(db, projectPath, now, limit) {
3199
+ 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));
3200
+ }
3201
+ function getSmartNotesNeedingCompilation(db, projectPath, now, limit) {
3202
+ 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));
3203
+ }
3204
+ function getStaleCompiledSmartNotes(db, projectPath, now, limit) {
3205
+ const staleBefore = now - SMART_NOTE_CHECK_MAX_STALENESS_MS;
3206
+ const livenessBefore = now - SMART_NOTE_CHECK_LIVENESS_RECHECK_MS;
3207
+ 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));
3208
+ }
3209
+ function storeCompiledSmartNoteCheck(db, args) {
3210
+ db.prepare(`UPDATE notes
3211
+ SET compiled_check = ?,
3212
+ manifest_json = ?,
3213
+ check_hash = ?,
3214
+ check_cron = ?,
3215
+ check_version = 1,
3216
+ check_status = 'compiled',
3217
+ check_failure_count = 0,
3218
+ check_network_failure_count = 0,
3219
+ check_quarantined_until = NULL,
3220
+ check_next_due_at = ?,
3221
+ check_compiled_at = ?,
3222
+ check_false_since_at = COALESCE(check_false_since_at, ?),
3223
+ check_last_liveness_at = NULL,
3224
+ policy_version = ?,
3225
+ updated_at = ?
3226
+ 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);
3227
+ }
3228
+ function markCompiledCheckFalse(db, noteId, nextDueAt, now) {
3229
+ db.prepare(`UPDATE notes
3230
+ SET last_checked_at = ?,
3231
+ updated_at = ?,
3232
+ check_next_due_at = ?,
3233
+ check_failure_count = 0,
3234
+ check_network_failure_count = 0,
3235
+ check_false_since_at = COALESCE(check_false_since_at, ?)
3236
+ WHERE id = ? AND type = 'smart'`).run(now, now, nextDueAt, now, noteId);
3237
+ }
3238
+ function markCompiledCheckLogicFailure(db, noteId, now, maxFailures) {
3239
+ const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
3240
+ const status = failureCount >= maxFailures ? "failing" : "compiled";
3241
+ db.prepare(`UPDATE notes
3242
+ SET check_failure_count = ?,
3243
+ check_status = ?,
3244
+ check_next_due_at = ?,
3245
+ updated_at = ?
3246
+ WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
3247
+ }
3248
+ function markCompiledCheckNetworkFailure(db, noteId, now, maxFailures) {
3249
+ const failureCount = readFailureCount(db, noteId, "check_network_failure_count") + 1;
3250
+ const quarantinedUntil = now + backoffMs(failureCount);
3251
+ const status = failureCount >= maxFailures ? "failing" : "compiled";
3252
+ db.prepare(`UPDATE notes
3253
+ SET check_network_failure_count = ?,
3254
+ check_status = ?,
3255
+ check_next_due_at = ?,
3256
+ check_quarantined_until = ?,
3257
+ updated_at = ?
3258
+ WHERE id = ? AND type = 'smart'`).run(failureCount, status, quarantinedUntil, quarantinedUntil, now, noteId);
3259
+ }
3260
+ function markSmartNoteLivenessChecked(db, noteId, now) {
3261
+ db.prepare(`UPDATE notes
3262
+ SET check_last_liveness_at = ?, updated_at = ?
3263
+ WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
3264
+ }
3265
+ function markSmartNoteCheckStatus(db, noteId, status, now) {
3266
+ db.prepare(`UPDATE notes SET check_status = ?, updated_at = ? WHERE id = ? AND type = 'smart'`).run(status, now, noteId);
3267
+ }
3268
+ function markSmartNoteCompilationFailure(db, noteId, now, maxFailures) {
3269
+ const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
3270
+ const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
3271
+ db.prepare(`UPDATE notes
3272
+ SET check_failure_count = ?,
3273
+ check_status = ?,
3274
+ check_next_due_at = ?,
3275
+ updated_at = ?
3276
+ WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
3277
+ }
3278
+ function readFailureCount(db, noteId, column) {
3279
+ if (column !== "check_failure_count" && column !== "check_network_failure_count")
3280
+ return 0;
3281
+ const row = db.prepare(`SELECT ${column} AS count FROM notes WHERE id = ?`).get(noteId);
3282
+ return row?.count ?? 0;
3283
+ }
3284
+ function backoffMs(failureCount) {
3285
+ const minutes = Math.min(24 * 60, 5 * 2 ** Math.max(0, failureCount - 1));
3286
+ return minutes * 60 * 1000;
3287
+ }
3288
+
3289
+ // ../plugin/src/features/magic-context/dreamer/task-gates.ts
3290
+ function countActiveMemories(db, projectPath) {
3291
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
3292
+ return row?.cnt ?? 0;
3293
+ }
3294
+ function countUnmappedActiveMemories(db, projectPath) {
3295
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
3296
+ FROM memories m
3297
+ WHERE m.project_path = ?
3298
+ AND m.status IN ('active','permanent')
3299
+ AND NOT EXISTS (
3300
+ SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
3301
+ )`).get(projectPath);
3302
+ return row?.cnt ?? 0;
3303
+ }
3304
+ function countCompartmentsSince(db, projectPath, since) {
3305
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
3306
+ FROM compartments c
3307
+ JOIN session_projects sp ON sp.session_id = c.session_id
3308
+ WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
3309
+ return row?.cnt ?? 0;
3310
+ }
3311
+ function countProjectSessionsSince(db, projectPath, since) {
3312
+ 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);
3313
+ return row?.cnt ?? 0;
3314
+ }
3315
+ function countMappedMemories(db, projectPath) {
3316
+ const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
3317
+ FROM memories m
3318
+ JOIN memory_verifications v ON v.memory_id = m.id
3319
+ WHERE m.project_path = ?
3320
+ AND m.status IN ('active','permanent')
3321
+ AND v.file_path <> ''`).get(projectPath);
3322
+ return row?.cnt ?? 0;
3323
+ }
3324
+ function countUnverifiedMappedMemories(db, projectPath) {
3325
+ const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
3326
+ FROM memories m
3327
+ JOIN memory_verifications v ON v.memory_id = m.id
3328
+ WHERE m.project_path = ?
3329
+ AND m.status IN ('active','permanent')
3330
+ AND v.file_path <> ''
3331
+ AND v.verified_at = 0`).get(projectPath);
3332
+ return row?.cnt ?? 0;
3333
+ }
3334
+ function countCueCandidates(db, projectPath) {
3335
+ if (!hasMuralCueColumns(db))
3336
+ return countActiveMemories(db, projectPath);
3337
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
3338
+ FROM memories
3339
+ WHERE project_path = ?
3340
+ AND status IN ('active','permanent')
3341
+ AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
3342
+ return row?.cnt ?? 0;
3343
+ }
3344
+ function countStalePrimers(db, projectPath) {
3345
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
3346
+ FROM primers
3347
+ WHERE project_path = ?
3348
+ AND status = 'active'
3349
+ AND (answer IS NULL OR TRIM(answer) = '' OR answer_refreshed_at IS NULL
3350
+ OR last_observed_at > answer_refreshed_at)`).get(projectPath);
3351
+ return row?.cnt ?? 0;
3352
+ }
3353
+ function countUnclassifiedActiveMemories(db, projectPath) {
3354
+ if (!hasMemoryClassifiedAtColumn(db))
3355
+ return countActiveMemories(db, projectPath);
3356
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
3357
+ FROM memories
3358
+ WHERE project_path = ?
3359
+ AND status IN ('active','permanent')
3360
+ AND classified_at IS NULL`).get(projectPath);
3361
+ return row?.cnt ?? 0;
3362
+ }
3363
+ function countPendingSmartNotes(db, projectPath) {
3364
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
3365
+ return row?.cnt ?? 0;
3366
+ }
3367
+ function countUserMemoryCandidates(db) {
3368
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM user_memory_candidates").get();
3369
+ return row?.cnt ?? 0;
3370
+ }
3371
+ function countActivePrimers(db, projectPath) {
3372
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM primers WHERE project_path = ? AND status = 'active'").get(projectPath);
3373
+ return row?.cnt ?? 0;
3374
+ }
3375
+ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
3376
+ switch (task) {
3377
+ case "map-memories": {
3378
+ const total = countActiveMemories(db, projectPath);
3379
+ return { pending: countUnmappedActiveMemories(db, projectPath), total };
3380
+ }
3381
+ case "verify": {
3382
+ return {
3383
+ pending: countUnverifiedMappedMemories(db, projectPath),
3384
+ total: countMappedMemories(db, projectPath)
3385
+ };
3386
+ }
3387
+ case "verify-broad": {
3388
+ const total = countMappedMemories(db, projectPath);
3389
+ return { pending: total, total };
3390
+ }
3391
+ case "curate": {
3392
+ const total = countActiveMemories(db, projectPath);
3393
+ return { pending: total, total };
3394
+ }
3395
+ case "compress-cues": {
3396
+ const total = countActiveMemories(db, projectPath);
3397
+ return { pending: countCueCandidates(db, projectPath), total };
3398
+ }
3399
+ case "classify-memories": {
3400
+ const total = countActiveMemories(db, projectPath);
3401
+ return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
3402
+ }
3403
+ case "retrospective": {
3404
+ const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
3405
+ return { pending, total: pending };
3406
+ }
3407
+ case "maintain-docs": {
3408
+ const total = countCompartmentsSince(db, projectPath, 0);
3409
+ const pending = countCompartmentsSince(db, projectPath, options.lastRunAt ?? 0);
3410
+ return { pending, total };
3411
+ }
3412
+ case "evaluate-smart-notes": {
3413
+ const pending = countPendingSmartNotes(db, projectPath);
3414
+ return { pending, total: pending };
3415
+ }
3416
+ case "review-user-memories": {
3417
+ const pending = countUserMemoryCandidates(db);
3418
+ return { pending, total: pending };
3419
+ }
3420
+ case "promote-primers": {
3421
+ const pending = countPrimerCandidatesForProject(db, projectPath);
3422
+ return { pending, total: pending };
3423
+ }
3424
+ case "refresh-primers": {
3425
+ const total = countActivePrimers(db, projectPath);
3426
+ return { pending: countStalePrimers(db, projectPath), total };
3427
+ }
3428
+ default: {
3429
+ const _exhaustive = task;
3430
+ return _exhaustive;
3431
+ }
3432
+ }
3433
+ }
3434
+ function getDreamTaskBacklogs(db, projectPath, tasks = CANONICAL_DREAM_TASKS, options = {}) {
3435
+ const result = {};
3436
+ for (const task of tasks)
3437
+ result[task] = getDreamTaskBacklog(db, projectPath, task, options);
3438
+ return result;
3439
+ }
3440
+ function evaluateTaskGate(task, ctx) {
3441
+ const { db, projectIdentity: project, lastRunAt } = ctx;
3442
+ switch (task) {
3443
+ case "map-memories":
3444
+ return countUnmappedActiveMemories(db, project) > 0;
3445
+ case "verify":
3446
+ return countActiveMemories(db, project) > 0;
3447
+ case "verify-broad":
3448
+ return countActiveMemories(db, project) > 0;
3449
+ case "curate":
3450
+ return countActiveMemories(db, project) > 0;
3451
+ case "compress-cues":
3452
+ return countActiveMemories(db, project) > 0;
3453
+ case "classify-memories":
3454
+ return countActiveMemories(db, project) > 0;
3455
+ case "retrospective":
3456
+ return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
3457
+ case "maintain-docs":
3458
+ return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
3459
+ case "evaluate-smart-notes":
3460
+ 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");
3461
+ case "review-user-memories":
3462
+ return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
3463
+ case "promote-primers":
3464
+ return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
3465
+ case "refresh-primers":
3466
+ return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
3467
+ default: {
3468
+ const _exhaustive = task;
3469
+ return Boolean(_exhaustive);
3470
+ }
3471
+ }
3472
+ }
3473
+
3075
3474
  // ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
3076
3475
  import { existsSync as existsSync4 } from "node:fs";
3077
3476
  function openOpenCodeDb() {
@@ -4059,6 +4458,7 @@ async function runCompressCues(args) {
4059
4458
  result.skipped += outcome.skipped;
4060
4459
  result.remaining -= outcome.compressed;
4061
4460
  result.chunks += 1;
4461
+ args.onProgress?.(result.compressed + result.skipped);
4062
4462
  if (outcome.failure?.class === "timeout") {
4063
4463
  consecutiveTimeouts += 1;
4064
4464
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
@@ -5154,6 +5554,7 @@ async function runClassify(args) {
5154
5554
  result.changed += counts.changed;
5155
5555
  result.remaining -= counts.classified;
5156
5556
  result.chunks += 1;
5557
+ args.onProgress?.(result.classified);
5157
5558
  }
5158
5559
  result.complete = result.remaining === 0;
5159
5560
  log(`[dreamer] classify: stage=${stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining} complete=${result.complete}`);
@@ -5383,95 +5784,37 @@ function applyClassifications(args, chunk, manifestText) {
5383
5784
  });
5384
5785
  return { classified, changed };
5385
5786
  }
5386
- function recordInvocation(args, startedAt, params) {
5387
- if (!args.parentSessionId)
5388
- return;
5389
- recordChildInvocation({
5390
- db: args.db,
5391
- parentSessionId: args.parentSessionId,
5392
- harness: "opencode",
5393
- subagent: "dreamer",
5394
- task: "classify-memories",
5395
- startedAt,
5396
- status: params.status,
5397
- messages: params.messages,
5398
- error: params.error
5399
- });
5400
- }
5401
-
5402
- // ../plugin/src/agents/smart-note-compiler.ts
5403
- var SMART_NOTE_COMPILER_AGENT = "smart-note-compiler";
5404
-
5405
- // ../plugin/src/features/magic-context/smart-notes/capabilities.ts
5406
- import { execFile } from "node:child_process";
5407
- import { constants as fsConstants } from "node:fs";
5408
- import { lstat, open, realpath } from "node:fs/promises";
5409
- import path3 from "node:path";
5410
- import { promisify } from "node:util";
5411
-
5412
- // ../plugin/src/features/magic-context/smart-notes/ssrf-guard.ts
5413
- import { lookup as dnsLookup } from "node:dns/promises";
5414
- import * as https from "node:https";
5415
- import { isIP } from "node:net";
5416
- 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))
5787
+ function recordInvocation(args, startedAt, params) {
5788
+ if (!args.parentSessionId)
5469
5789
  return;
5470
- const arr = value.filter((item) => typeof item === "string");
5471
- return arr.length > 0 ? arr : undefined;
5790
+ recordChildInvocation({
5791
+ db: args.db,
5792
+ parentSessionId: args.parentSessionId,
5793
+ harness: "opencode",
5794
+ subagent: "dreamer",
5795
+ task: "classify-memories",
5796
+ startedAt,
5797
+ status: params.status,
5798
+ messages: params.messages,
5799
+ error: params.error
5800
+ });
5472
5801
  }
5473
5802
 
5803
+ // ../plugin/src/agents/smart-note-compiler.ts
5804
+ var SMART_NOTE_COMPILER_AGENT = "smart-note-compiler";
5805
+
5806
+ // ../plugin/src/features/magic-context/smart-notes/capabilities.ts
5807
+ import { execFile } from "node:child_process";
5808
+ import { constants as fsConstants } from "node:fs";
5809
+ import { lstat, open, realpath } from "node:fs/promises";
5810
+ import path3 from "node:path";
5811
+ import { promisify } from "node:util";
5812
+
5474
5813
  // ../plugin/src/features/magic-context/smart-notes/ssrf-guard.ts
5814
+ import { lookup as dnsLookup } from "node:dns/promises";
5815
+ import * as https from "node:https";
5816
+ import { isIP } from "node:net";
5817
+ import { domainToASCII } from "node:url";
5475
5818
  var DNS_TIMEOUT_MS = 3000;
5476
5819
  var DEFAULT_HTTP_TIMEOUT_MS = 5000;
5477
5820
  var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
@@ -6457,153 +6800,6 @@ function deterministicJitterMs(intervalMs, noteId, hash) {
6457
6800
  return unsigned % (max * 2 + 1) - max;
6458
6801
  }
6459
6802
 
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);
6565
- }
6566
- function markCompiledCheckNetworkFailure(db, noteId, now, maxFailures) {
6567
- const failureCount = readFailureCount(db, noteId, "check_network_failure_count") + 1;
6568
- const quarantinedUntil = now + backoffMs(failureCount);
6569
- const status = failureCount >= maxFailures ? "failing" : "compiled";
6570
- db.prepare(`UPDATE notes
6571
- SET check_network_failure_count = ?,
6572
- check_status = ?,
6573
- check_next_due_at = ?,
6574
- check_quarantined_until = ?,
6575
- updated_at = ?
6576
- WHERE id = ? AND type = 'smart'`).run(failureCount, status, quarantinedUntil, quarantinedUntil, now, noteId);
6577
- }
6578
- function markSmartNoteLivenessChecked(db, noteId, now) {
6579
- db.prepare(`UPDATE notes
6580
- SET check_last_liveness_at = ?, updated_at = ?
6581
- WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
6582
- }
6583
- function markSmartNoteCheckStatus(db, noteId, status, now) {
6584
- db.prepare(`UPDATE notes SET check_status = ?, updated_at = ? WHERE id = ? AND type = 'smart'`).run(status, now, noteId);
6585
- }
6586
- function markSmartNoteCompilationFailure(db, noteId, now, maxFailures) {
6587
- const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
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);
6595
- }
6596
- function readFailureCount(db, noteId, column) {
6597
- if (column !== "check_failure_count" && column !== "check_network_failure_count")
6598
- return 0;
6599
- const row = db.prepare(`SELECT ${column} AS count FROM notes WHERE id = ?`).get(noteId);
6600
- return row?.count ?? 0;
6601
- }
6602
- function backoffMs(failureCount) {
6603
- const minutes = Math.min(24 * 60, 5 * 2 ** Math.max(0, failureCount - 1));
6604
- return minutes * 60 * 1000;
6605
- }
6606
-
6607
6803
  // ../plugin/src/features/magic-context/smart-notes/runner.ts
6608
6804
  function inferEvaluateSmartNotesLeaseHeld(db, projectIdentity) {
6609
6805
  const leaseKey = leaseKeyFor("evaluate-smart-notes", projectIdentity);
@@ -7337,6 +7533,7 @@ async function mapMemories(args) {
7337
7533
  result.independent += counts.independent;
7338
7534
  result.remaining -= counts.mapped + counts.independent;
7339
7535
  result.batches += 1;
7536
+ args.onProgress?.(result.mapped + result.independent);
7340
7537
  }
7341
7538
  result.complete = result.remaining === 0;
7342
7539
  log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
@@ -7938,6 +8135,7 @@ async function refreshPrimers(args) {
7938
8135
  result.refreshed += 1;
7939
8136
  else
7940
8137
  result.skipped += 1;
8138
+ args.onProgress?.(result.refreshed + result.skipped);
7941
8139
  }
7942
8140
  log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
7943
8141
  return result;
@@ -8785,6 +8983,7 @@ async function runVerify(args) {
8785
8983
  result.archived += counts.archived;
8786
8984
  result.remaining -= counts.verified + counts.updated + counts.archived;
8787
8985
  result.batches += 1;
8986
+ args.onProgress?.(result.verified + result.updated + result.archived);
8788
8987
  }
8789
8988
  result.complete = result.remaining === 0;
8790
8989
  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}`);
@@ -9076,6 +9275,20 @@ function createDreamTaskExecutor(deps) {
9076
9275
  const { db, projectIdentity, holderId, leaseKey } = ctx;
9077
9276
  const startedAt = Date.now();
9078
9277
  const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
9278
+ const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
9279
+ const reportProgress = (processed) => {
9280
+ deps.onProgress?.({
9281
+ task: config.task,
9282
+ processed: Math.max(0, processed),
9283
+ total: backlogAtStart.pending,
9284
+ startedAt
9285
+ });
9286
+ };
9287
+ reportProgress(0);
9288
+ const incompleteMessage = (remaining) => {
9289
+ const processed = processedDreamTaskItems(backlogAtStart.pending, remaining);
9290
+ return `${config.task} incomplete: ${remaining} remain (was ${backlogAtStart.pending} at run start; processed ${processed} this run)`;
9291
+ };
9079
9292
  const parent = await resolveParentSessionId();
9080
9293
  let moduleRoute;
9081
9294
  if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
@@ -9104,7 +9317,19 @@ function createDreamTaskExecutor(deps) {
9104
9317
  name: config.task,
9105
9318
  durationMs: Date.now() - startedAt,
9106
9319
  resultChars: 0,
9107
- ...error ? { error } : {}
9320
+ ...error ? { error } : {},
9321
+ backlog: (() => {
9322
+ const end = getDreamTaskBacklog(db, projectIdentity, config.task);
9323
+ const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
9324
+ const value = {
9325
+ pendingAtStart: backlogAtStart.pending,
9326
+ totalAtStart: backlogAtStart.total,
9327
+ pendingAtEnd: end.pending,
9328
+ totalAtEnd: end.total,
9329
+ processed
9330
+ };
9331
+ return value;
9332
+ })()
9108
9333
  }
9109
9334
  ],
9110
9335
  tasksSucceeded: status === "completed" ? 1 : 0,
@@ -9138,8 +9363,8 @@ function createDreamTaskExecutor(deps) {
9138
9363
  }
9139
9364
  try {
9140
9365
  if (config.task === "compress-cues") {
9141
- if (deps.experimentalMural?.enabled !== true) {
9142
- log("[dreamer] compress-cues: skipped (experimental.mural is not enabled)");
9366
+ if (deps.mural?.enabled !== true) {
9367
+ log("[dreamer] compress-cues: skipped (mural is not enabled)");
9143
9368
  recordRun("completed", null);
9144
9369
  return { status: "completed" };
9145
9370
  }
@@ -9158,12 +9383,13 @@ function createDreamTaskExecutor(deps) {
9158
9383
  holderId,
9159
9384
  leaseKey,
9160
9385
  deadline,
9161
- model: config.model ?? deps.experimentalMural.model ?? deps.dreamerModel,
9162
- fallbackModels: config.fallbackModels
9386
+ model: config.model ?? deps.mural.model ?? deps.dreamerModel,
9387
+ fallbackModels: config.fallbackModels,
9388
+ onProgress: (processed) => reportProgress(processed)
9163
9389
  });
9164
9390
  log(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
9165
9391
  if (!result.complete) {
9166
- const error = `compress-cues incomplete: ${result.remaining} selected memories remain`;
9392
+ const error = incompleteMessage(result.remaining);
9167
9393
  recordRun("failed", error);
9168
9394
  return { status: "failed", transient: true, error };
9169
9395
  }
@@ -9200,11 +9426,12 @@ function createDreamTaskExecutor(deps) {
9200
9426
  deadline,
9201
9427
  model: config.model,
9202
9428
  fallbackModels: config.fallbackModels,
9203
- moduleRoute
9429
+ moduleRoute,
9430
+ onProgress: (processed) => reportProgress(processed)
9204
9431
  });
9205
9432
  log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining}`);
9206
9433
  if (!result.complete) {
9207
- const error = `map-memories incomplete: ${result.remaining} selected memories remain`;
9434
+ const error = incompleteMessage(result.remaining);
9208
9435
  recordRun("failed", error);
9209
9436
  return { status: "failed", transient: true, error };
9210
9437
  }
@@ -9226,10 +9453,11 @@ function createDreamTaskExecutor(deps) {
9226
9453
  model: config.model,
9227
9454
  fallbackModels: config.fallbackModels,
9228
9455
  language: config.language ?? deps.language,
9229
- moduleRoute
9456
+ moduleRoute,
9457
+ onProgress: (processed) => reportProgress(processed)
9230
9458
  });
9231
9459
  if (!result.complete) {
9232
- const error = `${config.task} incomplete: ${result.remaining} selected memories remain`;
9460
+ const error = incompleteMessage(result.remaining);
9233
9461
  recordRun("failed", error, {
9234
9462
  memoryChanges: computeMemoryDelta(memoryBefore)
9235
9463
  });
@@ -9288,11 +9516,12 @@ function createDreamTaskExecutor(deps) {
9288
9516
  deadline,
9289
9517
  model: config.model,
9290
9518
  fallbackModels: config.fallbackModels,
9291
- ...moduleArgs
9519
+ ...moduleArgs,
9520
+ onProgress: (processed) => reportProgress(processed)
9292
9521
  });
9293
9522
  log(`[dreamer] classify-memories: stage=${result.stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining}`);
9294
9523
  if (!result.complete) {
9295
- const error = `classify-memories incomplete: ${result.remaining} selected memories remain`;
9524
+ const error = incompleteMessage(result.remaining);
9296
9525
  recordRun("failed", error);
9297
9526
  return { status: "failed", transient: true, error };
9298
9527
  }
@@ -9328,7 +9557,8 @@ function createDreamTaskExecutor(deps) {
9328
9557
  model: config.model,
9329
9558
  fallbackModels: config.fallbackModels,
9330
9559
  language: config.language ?? deps.language,
9331
- rawProviderFactory: deps.primerRawProviderFactory
9560
+ rawProviderFactory: deps.primerRawProviderFactory,
9561
+ onProgress: (processed) => reportProgress(processed)
9332
9562
  });
9333
9563
  recordRun("completed", null);
9334
9564
  log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
@@ -9382,6 +9612,8 @@ function createDreamTaskExecutor(deps) {
9382
9612
  recordRun("failed", brief);
9383
9613
  log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
9384
9614
  return { status: "failed", transient, error: brief };
9615
+ } finally {
9616
+ deps.onProgress?.(null, config.task);
9385
9617
  }
9386
9618
  };
9387
9619
  }
@@ -9757,66 +9989,6 @@ async function runAgenticTask(config, ctx, helpers) {
9757
9989
  }
9758
9990
  }
9759
9991
 
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
9992
  // ../plugin/src/features/magic-context/dreamer/task-scheduler.ts
9821
9993
  var MAX_TASK_RETRIES = 3;
9822
9994
  function ensureSeeded(db, projectIdentity, config, now) {
@@ -9964,10 +10136,10 @@ async function runDomainGroup(deps, group, cb) {
9964
10136
  cb?.onRan?.(due.config.task);
9965
10137
  } else if (outcome.transient) {
9966
10138
  recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
9967
- cb?.onFailed?.(due.config.task);
10139
+ cb?.onFailed?.(due.config.task, outcome.error);
9968
10140
  } else {
9969
10141
  advanceAfterRun(db, projectIdentity, due, finishedAt, "failed", outcome.error ?? null);
9970
- cb?.onFailed?.(due.config.task);
10142
+ cb?.onFailed?.(due.config.task, outcome.error);
9971
10143
  }
9972
10144
  }
9973
10145
  } finally {
@@ -9976,7 +10148,15 @@ async function runDomainGroup(deps, group, cb) {
9976
10148
  }
9977
10149
  async function runManualDream(deps) {
9978
10150
  const now = Date.now();
9979
- const result = { ran: [], skippedNoWork: [], deferredBusy: [], failed: [] };
10151
+ const result = {
10152
+ ran: [],
10153
+ skippedNoWork: [],
10154
+ deferredBusy: [],
10155
+ failed: [],
10156
+ failureDetails: [],
10157
+ backlogBefore: {},
10158
+ backlogAfter: {}
10159
+ };
9980
10160
  let selected;
9981
10161
  let forceGate = false;
9982
10162
  if (deps.task) {
@@ -9990,6 +10170,9 @@ async function runManualDream(deps) {
9990
10170
  }
9991
10171
  if (selected.length === 0)
9992
10172
  return result;
10173
+ const selectedTaskNames = selected.map((config) => config.task);
10174
+ result.backlogBefore = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
10175
+ result.backlogAfter = { ...result.backlogBefore };
9993
10176
  for (const cfg of selected)
9994
10177
  ensureSeeded(deps.db, deps.projectIdentity, cfg, now);
9995
10178
  const dueAll = selected.map((config) => ({ config, scheduledAt: now }));
@@ -10011,8 +10194,10 @@ async function runManualDream(deps) {
10011
10194
  else
10012
10195
  result.skippedNoWork.push(d.config.task);
10013
10196
  }
10014
- if (gated.length === 0)
10197
+ if (gated.length === 0) {
10198
+ result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
10015
10199
  return result;
10200
+ }
10016
10201
  const groups = new Map;
10017
10202
  for (const d of gated) {
10018
10203
  const kind = leaseKindFor(d.config.task);
@@ -10024,9 +10209,14 @@ async function runManualDream(deps) {
10024
10209
  forceGate,
10025
10210
  leaseWaitMs: MANUAL_RUN_LEASE_WAIT_MS,
10026
10211
  onRan: (t) => result.ran.push(t),
10027
- onFailed: (t) => result.failed.push(t),
10212
+ onFailed: (task, error) => {
10213
+ result.failed.push(task);
10214
+ if (error)
10215
+ result.failureDetails?.push(`${task}: ${error}`);
10216
+ },
10028
10217
  onBusy: (t) => result.deferredBusy.push(t)
10029
10218
  })));
10219
+ result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
10030
10220
  return result;
10031
10221
  }
10032
10222
  async function runDueTasksForProject(deps) {
@@ -10341,10 +10531,11 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
10341
10531
  ensureProjectRegistered: reg.ensureRegistered,
10342
10532
  language: reg.language,
10343
10533
  dreamerModel: dreamerConfig.model,
10344
- experimentalMural: reg.experimentalMural,
10534
+ mural: reg.mural,
10345
10535
  memoryInjectionBudgetTokens: reg.memoryInjectionBudgetTokens,
10346
10536
  transformMode: reg.transformMode,
10347
- moduleClient: reg.moduleClient
10537
+ moduleClient: reg.moduleClient,
10538
+ onProgress: (progress, completedTask) => reg.onDreamerProgress?.(progress, completedTask)
10348
10539
  });
10349
10540
  const ran = await runDueTasksForProject({
10350
10541
  db,
@@ -11014,13 +11205,18 @@ Unknown task "${requested}".`,
11014
11205
  });
11015
11206
  return;
11016
11207
  }
11208
+ const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
11209
+ const backlogBefore = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
11017
11210
  sendCtxStatusMessage(pi, {
11018
11211
  title: "/ctx-dream",
11019
11212
  text: [
11020
11213
  "## /ctx-dream",
11021
11214
  "",
11022
11215
  task ? `Running dream task "${task}" for ${project.projectIdentity}…` : `Starting dream run for ${project.projectIdentity}…`,
11023
- `Project directory: ${project.projectDir}`
11216
+ `Project directory: ${project.projectDir}`,
11217
+ "",
11218
+ "Backlog before starting:",
11219
+ formatDreamTaskBacklogs(backlogBefore, backlogTasks)
11024
11220
  ].join(`
11025
11221
  `),
11026
11222
  level: "info"
@@ -11035,10 +11231,16 @@ Unknown task "${requested}".`,
11035
11231
  lines.push(`Ran: ${result.ran.join(", ")}`);
11036
11232
  if (result.failed.length > 0)
11037
11233
  lines.push(`Failed: ${result.failed.join(", ")}`);
11234
+ if ((result.failureDetails?.length ?? 0) > 0) {
11235
+ lines.push("Failure details:", ...(result.failureDetails ?? []).map((detail) => `- ${detail}`));
11236
+ }
11038
11237
  if (result.skippedNoWork.length > 0)
11039
11238
  lines.push(`Skipped (no work): ${result.skippedNoWork.join(", ")}`);
11040
11239
  if (result.deferredBusy.length > 0)
11041
11240
  lines.push(`Busy: ${result.deferredBusy.join(", ")} — another dream task holds this domain's lease; retry in a minute`);
11241
+ if (Object.keys(result.backlogAfter ?? {}).length > 0) {
11242
+ lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(result.backlogAfter));
11243
+ }
11042
11244
  if (lines.length === 0)
11043
11245
  lines.push("No enabled dream tasks to run.");
11044
11246
  sendCtxStatusMessage(pi, {
@@ -27175,7 +27377,7 @@ function formatExecuteThreshold(detail, contextLimit) {
27175
27377
  }
27176
27378
  return `${percentage}%${clampNote}`;
27177
27379
  }
27178
- function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
27380
+ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer) {
27179
27381
  const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
27180
27382
  tokensConfig: executeThresholdTokens,
27181
27383
  contextLimit,
@@ -27230,6 +27432,11 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
27230
27432
  `**Protected tags:** ${protectedTags}`,
27231
27433
  `**Subagent session:** ${meta.isSubagent}`
27232
27434
  ];
27435
+ if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
27436
+ lines.push("", "### Dreamer", ...dreamer.progress ? [
27437
+ `- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
27438
+ ] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
27439
+ }
27233
27440
  if (meta.lastContextPercentage > 0 || meta.lastInputTokens > 0) {
27234
27441
  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
27442
  }
@@ -27252,6 +27459,12 @@ ${c.content}
27252
27459
  lines.push(`- §${op.tagId}§ → ${op.operation}`);
27253
27460
  }
27254
27461
  }
27462
+ if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
27463
+ lines.push("", "### Dreamer Backlog", formatDreamTaskBacklogs(dreamer.backlog));
27464
+ }
27465
+ if (dreamer?.progress) {
27466
+ lines.push("", "### Dreamer Progress", `- ${dreamer.progress.task}: ${dreamer.progress.processed}/${dreamer.progress.total} processed this run`);
27467
+ }
27255
27468
  return lines.join(`
27256
27469
  `);
27257
27470
  } catch (error) {
@@ -27326,7 +27539,7 @@ ${c.content}
27326
27539
  // package.json
27327
27540
  var package_default = {
27328
27541
  name: "@cortexkit/pi-magic-context",
27329
- version: "0.34.0",
27542
+ version: "0.34.1",
27330
27543
  type: "module",
27331
27544
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
27332
27545
  main: "dist/index.js",
@@ -27834,7 +28047,9 @@ No active Pi session is available.`,
27834
28047
  model: ctx.model,
27835
28048
  detectedContextLimit
27836
28049
  });
27837
- const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit);
28050
+ const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
28051
+ backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
28052
+ });
27838
28053
  const details = buildStatusDetails(currentDeps, sessionId);
27839
28054
  sendCtxStatusMessage(pi, { title: "/ctx-status", text: statusText, level: "info" }, details);
27840
28055
  } catch (error) {
@@ -27876,6 +28091,7 @@ function buildStatusDetails(deps, sessionId) {
27876
28091
  dreamer: {
27877
28092
  enabled: deps.dreamer?.runnable === true,
27878
28093
  scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
28094
+ backlog: getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS),
27879
28095
  lastRunAt: getMostRecentTaskRunAt(deps.db, deps.projectIdentity)
27880
28096
  },
27881
28097
  historian: readHistorianState(deps.db, sessionId, meta)
@@ -28838,7 +29054,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
28838
29054
  injectDocs: cfg.dreamer?.inject_docs !== false,
28839
29055
  injectionBudgetTokens: cfg.memory.injection_budget_tokens,
28840
29056
  temporalAwareness: cfg.temporal_awareness === true,
28841
- muralEnabled: cfg.experimental?.mural?.enabled === true
29057
+ muralEnabled: cfg.mural.enabled === true
28842
29058
  },
28843
29059
  scheduler: {
28844
29060
  executeThresholdPercentage: cfg.execute_threshold_percentage,