@cortexkit/opencode-magic-context 0.43.1 → 0.43.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.
Files changed (51) hide show
  1. package/dist/{conflict-warning-hook-956fkedc.js → conflict-warning-hook-hyk9y29x.js} +3 -3
  2. package/dist/features/magic-context/dreamer/classify.d.ts +12 -0
  3. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  4. package/dist/features/magic-context/store-generation-rebase.d.ts +62 -1
  5. package/dist/features/magic-context/store-generation-rebase.d.ts.map +1 -1
  6. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  7. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts +6 -0
  8. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  9. package/dist/hooks/magic-context/compartment-runner-validation.d.ts +7 -0
  10. package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
  11. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +60 -8
  12. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
  13. package/dist/hooks/magic-context/hook.d.ts +3 -1
  14. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  15. package/dist/hooks/magic-context/system-prompt-hash.d.ts +9 -0
  16. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  17. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  19. package/dist/{index-kpmmr8s0.js → index-6j8th2hd.js} +1 -1
  20. package/dist/{index-v8qr6tt2.js → index-bmzt6xgy.js} +2 -2
  21. package/dist/{index-7wpwr2e2.js → index-g6hpfkqa.js} +250 -36
  22. package/dist/{index-yc81txp4.js → index-jgnag7sa.js} +1 -1
  23. package/dist/{index-1gtwrdc4.js → index-pa67brzn.js} +2 -2
  24. package/dist/{index-j7cnyvq1.js → index-tdrv6nd0.js} +1 -1
  25. package/dist/index.js +238 -28
  26. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  27. package/dist/{read-session-chunk-z7fv0qjv.js → read-session-chunk-53a7qbrh.js} +2 -2
  28. package/dist/{recomp-orchestrator-r00aa5t3.js → recomp-orchestrator-adsgqgse.js} +4 -4
  29. package/dist/{send-session-notification-3warjyjp.js → send-session-notification-e28r4xx2.js} +2 -2
  30. package/dist/shared/opencode-db-path.d.ts.map +1 -1
  31. package/dist/shared/test-fake-executable.d.ts +38 -0
  32. package/dist/shared/test-fake-executable.d.ts.map +1 -0
  33. package/dist/shared/user-facing-codes.d.ts +10 -0
  34. package/dist/shared/user-facing-codes.d.ts.map +1 -1
  35. package/dist/v2/hooks/context.d.ts.map +1 -1
  36. package/dist/v2/hooks/refusal.d.ts +9 -1
  37. package/dist/v2/hooks/refusal.d.ts.map +1 -1
  38. package/dist/v2/hooks/restore-rows.d.ts +29 -0
  39. package/dist/v2/hooks/restore-rows.d.ts.map +1 -0
  40. package/dist/v2/hooks/update-check.d.ts +8 -0
  41. package/dist/v2/hooks/update-check.d.ts.map +1 -1
  42. package/dist/v2/server.js +470 -61
  43. package/dist/v2/store-reader.d.ts +8 -0
  44. package/dist/v2/store-reader.d.ts.map +1 -1
  45. package/dist/{wrapup-orchestrator-y0bs7t5a.js → wrapup-orchestrator-8xzbpvm8.js} +5 -5
  46. package/package.json +1 -1
  47. package/src/shared/opencode-db-path.test.ts +18 -0
  48. package/src/shared/opencode-db-path.ts +5 -2
  49. package/src/shared/test-fake-executable.test.ts +54 -0
  50. package/src/shared/test-fake-executable.ts +110 -0
  51. package/src/shared/user-facing-codes.ts +11 -0
package/dist/v2/server.js CHANGED
@@ -15449,7 +15449,7 @@ var HOST_UNSERVED_ROW = "hostUnservedRow";
15449
15449
  // src/shared/opencode-db-path.ts
15450
15450
  import { existsSync as existsSync8, readdirSync, statSync as statSync7 } from "node:fs";
15451
15451
  import { homedir as homedir8 } from "node:os";
15452
- import { isAbsolute as isAbsolute4, join as join8 } from "node:path";
15452
+ import { isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "node:path";
15453
15453
  function openCodeDataDir(env = process.env, dataHome) {
15454
15454
  return join8(dataHome ?? env.XDG_DATA_HOME ?? join8(homedir8(), ".local", "share"), "opencode");
15455
15455
  }
@@ -15529,7 +15529,7 @@ function resolveV2Fresh(dataDir, channel, env) {
15529
15529
  const filename = sourceOpenCodeDatabaseFilename("v2", channel, env);
15530
15530
  const explicit = env.OPENCODE_DB !== undefined;
15531
15531
  return {
15532
- path: filename === ":memory:" ? filename : join8(dataDir, filename),
15532
+ path: filename === ":memory:" ? filename : resolve4(dataDir, filename),
15533
15533
  source: explicit ? "OPENCODE_DB" : env.OPENCODE_CHANNEL ? "channel" : "default",
15534
15534
  channel: explicit ? null : channel
15535
15535
  };
@@ -26133,7 +26133,7 @@ import {
26133
26133
  statSync as statSync9,
26134
26134
  unlinkSync as unlinkSync2
26135
26135
  } from "node:fs";
26136
- import { basename as basename3, dirname as dirname6, join as join9, resolve as resolve4 } from "node:path";
26136
+ import { basename as basename3, dirname as dirname6, join as join9, resolve as resolve5 } from "node:path";
26137
26137
  function getSchemaFenceRejection() {
26138
26138
  return lastSchemaFenceRejection;
26139
26139
  }
@@ -26450,7 +26450,7 @@ function isDefaultSharedDatabasePath(dbPath) {
26450
26450
  if (!process.env.XDG_DATA_HOME && (process.env.MAGIC_CONTEXT_TEST_DATA_DIR || false)) {
26451
26451
  return false;
26452
26452
  }
26453
- return resolve4(dbPath) === resolve4(join9(getMagicContextStorageDir(), "context.db"));
26453
+ return resolve5(dbPath) === resolve5(join9(getMagicContextStorageDir(), "context.db"));
26454
26454
  }
26455
26455
  function migrationBlockingPiPids(dbPath, discovery, discoveredPiPids) {
26456
26456
  if (isDefaultSharedDatabasePath(dbPath))
@@ -29016,6 +29016,7 @@ function emptyOutcome(status, generation, previousGeneration) {
29016
29016
  previousGeneration,
29017
29017
  compartmentsRebased: 0,
29018
29018
  compartmentsUnresolved: 0,
29019
+ compartmentsDerived: 0,
29019
29020
  compartmentsResolvedAgain: 0,
29020
29021
  recompCompartmentsRebased: 0,
29021
29022
  recompCompartmentsUnresolved: 0,
@@ -29069,6 +29070,12 @@ function readCoordinateRebaseNotice(db, sessionId) {
29069
29070
  if (record.generation !== "v1" && record.generation !== "v2")
29070
29071
  return null;
29071
29072
  const count = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
29073
+ const ranges = (value) => Array.isArray(value) ? value.flatMap((entry) => {
29074
+ if (!entry || typeof entry !== "object")
29075
+ return [];
29076
+ const { sequence, start, end } = entry;
29077
+ return typeof sequence === "number" && typeof start === "number" && typeof end === "number" ? [{ sequence, start, end }] : [];
29078
+ }) : [];
29072
29079
  return {
29073
29080
  generation: record.generation,
29074
29081
  previousGeneration: record.previousGeneration === "v1" || record.previousGeneration === "v2" ? record.previousGeneration : null,
@@ -29077,7 +29084,10 @@ function readCoordinateRebaseNotice(db, sessionId) {
29077
29084
  discardedReductions: count(record.discardedReductions),
29078
29085
  droppedDepthRows: count(record.droppedDepthRows),
29079
29086
  healedGaps: count(record.healedGaps),
29080
- narrativeGaps: count(record.narrativeGaps)
29087
+ narrativeGaps: count(record.narrativeGaps),
29088
+ derivedCompartments: ranges(record.derivedCompartments),
29089
+ unresolvedCompartmentRanges: ranges(record.unresolvedCompartmentRanges),
29090
+ neighbourRecoveryAt: typeof record.neighbourRecoveryAt === "number" && Number.isFinite(record.neighbourRecoveryAt) ? record.neighbourRecoveryAt : null
29081
29091
  };
29082
29092
  } catch {
29083
29093
  return null;
@@ -29114,36 +29124,62 @@ function readCompartmentRows(db, table, sessionId) {
29114
29124
  return db.prepare(`SELECT id, sequence, start_message, end_message, start_message_id, end_message_id, rebase_status
29115
29125
  FROM ${table} WHERE session_id = ? ORDER BY sequence ASC`).all(sessionId).filter(isCompartmentCoordinateRow);
29116
29126
  }
29127
+ function resolveAnchor(messageId, resolveOrdinal) {
29128
+ return messageId && messageId.length > 0 ? resolveOrdinal(messageId) : undefined;
29129
+ }
29117
29130
  function planCompartments(rows, table, projection) {
29118
- return rows.map((row) => {
29119
- const previousStatus = row.rebase_status === "unresolved" ? "unresolved" : "ok";
29120
- const startId = row.start_message_id ?? "";
29121
- const endId = row.end_message_id ?? "";
29122
- const start = startId.length > 0 ? projection.ordinalById.get(startId) : undefined;
29123
- const end = endId.length > 0 ? projection.ordinalById.get(endId) : undefined;
29124
- if (start === undefined || end === undefined) {
29125
- return {
29126
- table,
29127
- id: row.id,
29128
- previousStart: row.start_message,
29129
- previousEnd: row.end_message,
29130
- start: row.start_message,
29131
- end: row.end_message,
29132
- status: "unresolved",
29133
- previousStatus
29134
- };
29135
- }
29136
- return {
29131
+ const resolveOrdinal = (messageId) => projection.ordinalById.get(messageId);
29132
+ return recoverFromNeighbours(rows, table, rows.map((row) => ({
29133
+ start: resolveAnchor(row.start_message_id, resolveOrdinal),
29134
+ end: resolveAnchor(row.end_message_id, resolveOrdinal)
29135
+ })));
29136
+ }
29137
+ function recoverFromNeighbours(rows, table, known) {
29138
+ const plans = rows.map((row, index) => {
29139
+ const own = known[index] ?? { start: undefined, end: undefined };
29140
+ const previousEnd = index === 0 ? 0 : known[index - 1]?.end;
29141
+ const nextStart = index === rows.length - 1 ? undefined : known[index + 1]?.start;
29142
+ const start = own.start ?? (previousEnd === undefined ? undefined : previousEnd + 1);
29143
+ const end = own.end ?? (nextStart === undefined ? undefined : nextStart - 1);
29144
+ const base = {
29137
29145
  table,
29138
29146
  id: row.id,
29147
+ sequence: row.sequence,
29139
29148
  previousStart: row.start_message,
29140
29149
  previousEnd: row.end_message,
29141
- start,
29142
- end,
29143
- status: "ok",
29144
- previousStatus
29150
+ previousStatus: row.rebase_status === "unresolved" ? "unresolved" : "ok"
29151
+ };
29152
+ if (start !== undefined && end !== undefined && start <= end) {
29153
+ return {
29154
+ ...base,
29155
+ start,
29156
+ end,
29157
+ status: "ok",
29158
+ derived: own.start === undefined || own.end === undefined
29159
+ };
29160
+ }
29161
+ return {
29162
+ ...base,
29163
+ start: start ?? row.start_message,
29164
+ end: end ?? row.end_message,
29165
+ status: "unresolved",
29166
+ derived: false
29145
29167
  };
29146
29168
  });
29169
+ for (let index = 0;index < plans.length; index += 1) {
29170
+ const plan = plans[index];
29171
+ if (plan?.status !== "unresolved")
29172
+ continue;
29173
+ const previous = plans[index - 1];
29174
+ const next = plans[index + 1];
29175
+ if (previous?.status === "ok" && plan.start <= previous.end) {
29176
+ plan.start = previous.end + 1;
29177
+ }
29178
+ if (next?.status === "ok" && plan.end >= next.start) {
29179
+ plan.end = next.start - 1;
29180
+ }
29181
+ }
29182
+ return plans;
29147
29183
  }
29148
29184
  function healNonNarrativeCompartmentGaps(plans, projection) {
29149
29185
  let healed = 0;
@@ -29174,7 +29210,10 @@ function healNonNarrativeCompartmentGaps(plans, projection) {
29174
29210
  return { healed, narrative };
29175
29211
  }
29176
29212
  function compartmentPlanChanges(plan) {
29177
- return plan.status !== plan.previousStatus || plan.status === "ok" && (plan.start !== plan.previousStart || plan.end !== plan.previousEnd);
29213
+ return plan.status !== plan.previousStatus || plan.start !== plan.previousStart || plan.end !== plan.previousEnd;
29214
+ }
29215
+ function rangeNotes(plans, predicate) {
29216
+ return plans.filter((plan) => plan.table === "compartments" && predicate(plan)).map((plan) => ({ sequence: plan.sequence, start: plan.start, end: plan.end }));
29178
29217
  }
29179
29218
  function planNotes(db, sessionId, projection) {
29180
29219
  const rows = db.prepare("SELECT id, anchor_ordinal, anchor_block_id FROM notes WHERE session_id = ? AND anchor_ordinal IS NOT NULL").all(sessionId);
@@ -29260,7 +29299,14 @@ function rebaseSessionCoordinates(args) {
29260
29299
  }
29261
29300
  const previousGeneration = readCoordinateGeneration(db, sessionId);
29262
29301
  if (previousGeneration === generation) {
29263
- return emptyOutcome("unchanged", generation, previousGeneration);
29302
+ const outcome = emptyOutcome("unchanged", generation, previousGeneration);
29303
+ const repair = repairStampedSessionOnce(db, sessionId, args.readMessages);
29304
+ if (repair !== null && repair.rowsRewritten > 0) {
29305
+ outcome.status = "repaired";
29306
+ outcome.compartmentsDerived = repair.derived.length;
29307
+ outcome.compartmentsUnresolved = repair.unresolved.length;
29308
+ }
29309
+ return outcome;
29264
29310
  }
29265
29311
  const impliedGeneration = previousGeneration ?? generationImpliedByHarness(db, sessionId) ?? generation;
29266
29312
  if (impliedGeneration === generation) {
@@ -29324,6 +29370,8 @@ function rebaseSessionCoordinates(args) {
29324
29370
  if (plan.table === "compartments") {
29325
29371
  outcome.compartmentsRebased += rebased;
29326
29372
  outcome.compartmentsUnresolved += unresolved;
29373
+ if (rebased === 1 && plan.derived)
29374
+ outcome.compartmentsDerived += 1;
29327
29375
  if (rebased === 1 && plan.previousStatus === "unresolved")
29328
29376
  outcome.compartmentsResolvedAgain += 1;
29329
29377
  } else {
@@ -29380,7 +29428,10 @@ function rebaseSessionCoordinates(args) {
29380
29428
  discardedReductions: outcome.queuedReductionsDiscarded,
29381
29429
  droppedDepthRows: outcome.compressionDepthRowsDropped,
29382
29430
  healedGaps: outcome.healedGaps,
29383
- narrativeGaps: outcome.narrativeGaps
29431
+ narrativeGaps: outcome.narrativeGaps,
29432
+ derivedCompartments: rangeNotes(compartmentPlans, (plan) => plan.status === "ok" && plan.derived),
29433
+ unresolvedCompartmentRanges: rangeNotes(compartmentPlans, (plan) => plan.status === "unresolved"),
29434
+ neighbourRecoveryAt: Date.now()
29384
29435
  });
29385
29436
  db.exec("COMMIT");
29386
29437
  committed = true;
@@ -29398,10 +29449,114 @@ function rebaseSessionCoordinates(args) {
29398
29449
  sessionLog(sessionId, formatRebaseLogLine(outcome, performance.now() - startedAt));
29399
29450
  return outcome;
29400
29451
  }
29452
+ function recoverUnresolvedCompartments(args) {
29453
+ const { db, sessionId } = args;
29454
+ const rows = readCompartmentRows(db, "compartments", sessionId);
29455
+ const result = { rowsRewritten: 0, derived: [], unresolved: [] };
29456
+ if (!rows.some((row) => row.rebase_status === "unresolved"))
29457
+ return result;
29458
+ const known = rows.map((row) => {
29459
+ if (row.rebase_status !== "unresolved") {
29460
+ return { start: row.start_message, end: row.end_message };
29461
+ }
29462
+ return {
29463
+ start: resolveAnchor(row.start_message_id, args.resolveOrdinal),
29464
+ end: resolveAnchor(row.end_message_id, args.resolveOrdinal)
29465
+ };
29466
+ });
29467
+ const plans = recoverFromNeighbours(rows, "compartments", known);
29468
+ const wasUnresolved = (plan) => plan.previousStatus === "unresolved";
29469
+ result.derived = rangeNotes(plans, (plan) => wasUnresolved(plan) && plan.status === "ok" && plan.derived);
29470
+ result.unresolved = rangeNotes(plans, (plan) => plan.status === "unresolved");
29471
+ const changed = plans.filter(compartmentPlanChanges);
29472
+ const now = Date.now();
29473
+ db.exec("BEGIN IMMEDIATE");
29474
+ let committed = false;
29475
+ try {
29476
+ for (const plan of changed) {
29477
+ db.prepare("UPDATE compartments SET start_message = ?, end_message = ?, rebase_status = ? WHERE id = ?").run(plan.start, plan.end, plan.status, plan.id);
29478
+ }
29479
+ const previous = readCoordinateRebaseNotice(db, sessionId);
29480
+ const generation = previous?.generation ?? readCoordinateGeneration(db, sessionId);
29481
+ if (generation !== null) {
29482
+ const derivedSequences = new Set(result.derived.map((note) => note.sequence));
29483
+ const notice = {
29484
+ generation,
29485
+ previousGeneration: previous?.previousGeneration ?? null,
29486
+ at: previous?.at ?? now,
29487
+ unresolvedCompartments: result.unresolved.length,
29488
+ discardedReductions: previous?.discardedReductions ?? 0,
29489
+ droppedDepthRows: previous?.droppedDepthRows ?? 0,
29490
+ healedGaps: previous?.healedGaps ?? 0,
29491
+ narrativeGaps: previous?.narrativeGaps ?? 0,
29492
+ derivedCompartments: [
29493
+ ...(previous?.derivedCompartments ?? []).filter((note) => !derivedSequences.has(note.sequence)),
29494
+ ...result.derived
29495
+ ],
29496
+ unresolvedCompartmentRanges: result.unresolved,
29497
+ neighbourRecoveryAt: now
29498
+ };
29499
+ ensureSessionMetaRow(db, sessionId);
29500
+ db.prepare("UPDATE session_meta SET coordinate_rebase_notice = ? WHERE session_id = ?").run(JSON.stringify(notice), sessionId);
29501
+ }
29502
+ db.exec("COMMIT");
29503
+ committed = true;
29504
+ } finally {
29505
+ if (!committed) {
29506
+ try {
29507
+ db.exec("ROLLBACK");
29508
+ } catch {}
29509
+ }
29510
+ }
29511
+ result.rowsRewritten = changed.length;
29512
+ sessionLog(sessionId, `INFO compartment neighbour recovery (${args.reason}) rows_rewritten=${changed.length} ` + `derived=${result.derived.map((note) => `${note.start}-${note.end}`).join(",") || "none"} ` + `unresolved=${result.unresolved.map((note) => `${note.start}-${note.end}`).join(",") || "none"}`);
29513
+ return result;
29514
+ }
29515
+ function unresolvedRowBreaksTiling(rows) {
29516
+ let expectedStart = 1;
29517
+ for (let index = 0;index < rows.length; index += 1) {
29518
+ const row = rows[index];
29519
+ if (!row)
29520
+ continue;
29521
+ const broken = row.start_message !== expectedStart || row.end_message < row.start_message;
29522
+ const touchesUnresolved = row.rebase_status === "unresolved" || rows[index - 1]?.rebase_status === "unresolved";
29523
+ if (broken && touchesUnresolved)
29524
+ return true;
29525
+ expectedStart = row.end_message + 1;
29526
+ }
29527
+ return false;
29528
+ }
29529
+ function repairStampedSessionOnce(db, sessionId, readMessages) {
29530
+ let checked = repairCheckedSessions.get(db);
29531
+ if (!checked) {
29532
+ checked = new Set;
29533
+ repairCheckedSessions.set(db, checked);
29534
+ }
29535
+ if (checked.has(sessionId))
29536
+ return null;
29537
+ checked.add(sessionId);
29538
+ const rows = readCompartmentRows(db, "compartments", sessionId);
29539
+ if (!rows.some((row) => row.rebase_status === "unresolved"))
29540
+ return null;
29541
+ if (readCoordinateRebaseNotice(db, sessionId)?.neighbourRecoveryAt != null)
29542
+ return null;
29543
+ if (!unresolvedRowBreaksTiling(rows))
29544
+ return null;
29545
+ let ordinalById = null;
29546
+ return recoverUnresolvedCompartments({
29547
+ db,
29548
+ sessionId,
29549
+ resolveOrdinal: (messageId) => {
29550
+ ordinalById ??= buildProjection(readMessages(sessionId)).ordinalById;
29551
+ return ordinalById.get(messageId);
29552
+ },
29553
+ reason: "stamped-session repair"
29554
+ });
29555
+ }
29401
29556
  function formatRebaseLogLine(outcome, elapsedMs) {
29402
29557
  const rowsRewritten = outcome.compartmentsRebased + outcome.recompCompartmentsRebased + outcome.notesRebased + outcome.notesCleared + (outcome.priorBoundaryRebased ? 1 : 0) + (outcome.recompPartialRangeRebased ? 1 : 0);
29403
29558
  const unresolved = outcome.compartmentsUnresolved + outcome.recompCompartmentsUnresolved;
29404
- return `INFO store-generation-rebase ${outcome.previousGeneration ?? "unrecorded"}->${outcome.generation} ` + `rows_rewritten=${rowsRewritten} unresolved=${unresolved} ` + `index_rows_rebuilt=${outcome.indexRowsRebuilt} drops_discarded=${outcome.queuedReductionsDiscarded} ` + `healed_gaps=${outcome.healedGaps} narrative_gaps=${outcome.narrativeGaps} ` + `ms=${Math.round(elapsedMs)} ` + `(chunk_windows_deleted=${outcome.chunkEmbeddingsDeleted} depth_rows_dropped=${outcome.compressionDepthRowsDropped} ` + `part_tags_folded=${outcome.partTagsFolded} lkg_slots_dropped=${outcome.lkgSlotsDropped} ` + `frozen_part_entries_dropped=${outcome.frozenPartEntriesDropped})`;
29559
+ return `INFO store-generation-rebase ${outcome.previousGeneration ?? "unrecorded"}->${outcome.generation} ` + `rows_rewritten=${rowsRewritten} unresolved=${unresolved} derived=${outcome.compartmentsDerived} ` + `index_rows_rebuilt=${outcome.indexRowsRebuilt} drops_discarded=${outcome.queuedReductionsDiscarded} ` + `healed_gaps=${outcome.healedGaps} narrative_gaps=${outcome.narrativeGaps} ` + `ms=${Math.round(elapsedMs)} ` + `(chunk_windows_deleted=${outcome.chunkEmbeddingsDeleted} depth_rows_dropped=${outcome.compressionDepthRowsDropped} ` + `part_tags_folded=${outcome.partTagsFolded} lkg_slots_dropped=${outcome.lkgSlotsDropped} ` + `frozen_part_entries_dropped=${outcome.frozenPartEntriesDropped})`;
29405
29560
  }
29406
29561
  function planPriorBoundary(db, sessionId, plans) {
29407
29562
  const row = db.prepare("SELECT prior_boundary_ordinal FROM session_meta WHERE session_id = ?").get(sessionId);
@@ -29445,11 +29600,13 @@ function indexDisagreesWithProjection(db, sessionId, projection) {
29445
29600
  )`, sessionId);
29446
29601
  return orphanedMapRows > 0;
29447
29602
  }
29603
+ var repairCheckedSessions;
29448
29604
  var init_store_generation_rebase = __esm(async () => {
29449
29605
  init_logger();
29450
29606
  init_storage_meta_shared();
29451
29607
  init_storage_tags();
29452
29608
  await init_message_index();
29609
+ repairCheckedSessions = new WeakMap;
29453
29610
  });
29454
29611
 
29455
29612
  // src/hooks/magic-context/derive-budgets.ts
@@ -75573,14 +75730,14 @@ var CTX_REDUCE_LIGHT_DESCRIPTION = `Stamp an item as no longer needed for the wo
75573
75730
 
75574
75731
  // src/shared/prompt-surface-runtime.ts
75575
75732
  import { existsSync as existsSync12, readFileSync as readFileSync12, statSync as statSync12 } from "node:fs";
75576
- import { dirname as dirname8, isAbsolute as isAbsolute5, resolve as resolve5 } from "node:path";
75733
+ import { dirname as dirname8, isAbsolute as isAbsolute5, resolve as resolve6 } from "node:path";
75577
75734
  function promptSurfaceHashMaterial(systemContent, preset = "full") {
75578
75735
  return preset === "full" ? systemContent : `${systemContent}
75579
75736
  \x00magic-context-prompt-surface:${preset}`;
75580
75737
  }
75581
75738
  function resolveUserConfigDirectory(options) {
75582
75739
  if (options.userConfigDirectory)
75583
- return resolve5(options.userConfigDirectory);
75740
+ return resolve6(options.userConfigDirectory);
75584
75741
  const sharedBase = cortexKitUserConfigBasePath();
75585
75742
  const shared = detectConfigFile(sharedBase);
75586
75743
  if (shared.format !== "none")
@@ -75607,7 +75764,7 @@ function createPromptSurfaceRuntime(options) {
75607
75764
  const readGuidanceOverride = (configuredPath) => {
75608
75765
  if (!configuredPath)
75609
75766
  return;
75610
- const path = isAbsolute5(configuredPath) ? configuredPath : resolve5(userConfigDirectory, configuredPath);
75767
+ const path = isAbsolute5(configuredPath) ? configuredPath : resolve6(userConfigDirectory, configuredPath);
75611
75768
  let content;
75612
75769
  try {
75613
75770
  if (!statSync12(path).isFile()) {
@@ -75757,6 +75914,11 @@ var init_user_facing_codes = __esm(() => {
75757
75914
  sentence: "Some finished background sessions could not be removed from this host.",
75758
75915
  action: "They are removed automatically the next time Magic Context runs inside a registered OpenCode service."
75759
75916
  },
75917
+ historian_saved_history_misaligned: {
75918
+ code: "MC-H03",
75919
+ sentence: "History compression is paused because this session's saved summaries no longer line up with its messages.",
75920
+ action: "Run /ctx-recomp to rebuild them."
75921
+ },
75760
75922
  recomp_unavailable: {
75761
75923
  code: "MC-R01",
75762
75924
  sentence: "History compression could not be rebuilt.",
@@ -75927,6 +76089,11 @@ var init_user_facing_codes = __esm(() => {
75927
76089
  sentence: "History compression is paused while the engine syncs.",
75928
76090
  action: "Retry in a moment."
75929
76091
  },
76092
+ history_compression_needs_message: {
76093
+ code: "MC-C12",
76094
+ sentence: "History compression has not seen this session since Magic Context reconnected.",
76095
+ action: "Send a message in this session first, then run /ctx-wrapup again."
76096
+ },
75930
76097
  context_service_unavailable: {
75931
76098
  code: "MC-C10",
75932
76099
  sentence: "Magic Context is temporarily unavailable.",
@@ -78056,6 +78223,14 @@ function buildHistorianFailureNotice(failureCount, _lastError) {
78056
78223
  return [heading, "", renderUserFacingFailure("historian_unavailable")].join(`
78057
78224
  `);
78058
78225
  }
78226
+ function buildStoredCompartmentsInvalidNotice() {
78227
+ return [
78228
+ "## Magic Context — History compression",
78229
+ "",
78230
+ renderUserFacingFailure("historian_saved_history_misaligned")
78231
+ ].join(`
78232
+ `);
78233
+ }
78059
78234
  function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
78060
78235
  const prompt = [
78061
78236
  originalPrompt,
@@ -84739,6 +84914,12 @@ function v2NonNarrativeStoredGapRanges(db, sessionId, compartments) {
84739
84914
  }
84740
84915
  return safeRanges;
84741
84916
  }
84917
+ function storedCompartmentFailureBackingOff(db, sessionId, validationError, now = Date.now()) {
84918
+ if (getHistorianFailureState(db, sessionId).lastError !== validationError)
84919
+ return false;
84920
+ const failedAt = loadProtectedTailMeta(db, sessionId).historianDrainFailureAt;
84921
+ return failedAt > 0 && failedAt <= now && now - failedAt < STORED_COMPARTMENT_FAILURE_BACKOFF_MS;
84922
+ }
84742
84923
  function shouldSuppressHistorianAlert(sessionId) {
84743
84924
  const lastAlert = lastHistorianAlertBySession.get(sessionId);
84744
84925
  if (lastAlert && Date.now() - lastAlert < HISTORIAN_ALERT_COOLDOWN_MS) {
@@ -84861,13 +85042,37 @@ async function runCompartmentAgent(deps) {
84861
85042
  }
84862
85043
  return;
84863
85044
  }
84864
- const priorCompartments = getCompartments(db, sessionId);
84865
- const existingValidationError = validateStoredCompartments(priorCompartments, v2NonNarrativeStoredGapRanges(db, sessionId, priorCompartments));
85045
+ let priorCompartments = getCompartments(db, sessionId);
85046
+ let existingValidationError = validateStoredCompartments(priorCompartments, v2NonNarrativeStoredGapRanges(db, sessionId, priorCompartments));
85047
+ const unresolvedInvolved = existingValidationError !== null && priorCompartments.some((compartment) => compartment.rebaseStatus === "unresolved");
85048
+ if (unresolvedInvolved && existingValidationError && storedCompartmentFailureBackingOff(db, sessionId, existingValidationError)) {
85049
+ sessionLog(sessionId, `historian no-op: stored compartments still invalid ("${existingValidationError}"); backing off`);
85050
+ telemetry.status = "noop";
85051
+ telemetry.failureReason = `existing-validation backoff: ${existingValidationError}`;
85052
+ return;
85053
+ }
85054
+ if (unresolvedInvolved && existingValidationError) {
85055
+ const recovery = recoverUnresolvedCompartments({
85056
+ db,
85057
+ sessionId,
85058
+ resolveOrdinal: (messageId) => readRawSessionMessageOrdinalById(sessionId, messageId) ?? undefined,
85059
+ reason: `historian pre-run check failed: ${existingValidationError}`
85060
+ });
85061
+ if (recovery.rowsRewritten > 0) {
85062
+ priorCompartments = getCompartments(db, sessionId);
85063
+ existingValidationError = validateStoredCompartments(priorCompartments, v2NonNarrativeStoredGapRanges(db, sessionId, priorCompartments));
85064
+ }
85065
+ }
84866
85066
  if (existingValidationError) {
84867
85067
  sessionLog(sessionId, `historian failure: source=existing-validation reason="${existingValidationError}"`);
84868
85068
  const failCount = incrementHistorianFailure(db, sessionId, existingValidationError);
84869
85069
  telemetry.failureReason = `existing-validation: ${existingValidationError}`;
84870
- await notifyHistorianIssue(buildHistorianFailureNotice(failCount, existingValidationError));
85070
+ if (unresolvedInvolved) {
85071
+ retainDrainReservationForRetryThrottle = true;
85072
+ await notifyHistorianIssue(buildStoredCompartmentsInvalidNotice());
85073
+ } else {
85074
+ await notifyHistorianIssue(buildHistorianFailureNotice(failCount, existingValidationError));
85075
+ }
84871
85076
  return;
84872
85077
  }
84873
85078
  const offset = priorCompartments.length > 0 ? priorCompartments[priorCompartments.length - 1].endMessage + 1 : 1;
@@ -85350,7 +85555,7 @@ ${chunkText}`,
85350
85555
  cleanupHistorianStateFile(stateFilePath);
85351
85556
  }
85352
85557
  }
85353
- var inconsistentProducerWindows, HISTORIAN_ALERT_COOLDOWN_MS, lastHistorianAlertBySession;
85558
+ var inconsistentProducerWindows, HISTORIAN_ALERT_COOLDOWN_MS, lastHistorianAlertBySession, STORED_COMPARTMENT_FAILURE_BACKOFF_MS;
85354
85559
  var init_compartment_runner_incremental = __esm(async () => {
85355
85560
  init_compartment_embedding();
85356
85561
  init_compartment_events();
@@ -85393,6 +85598,7 @@ var init_compartment_runner_incremental = __esm(async () => {
85393
85598
  inconsistentProducerWindows = new Set;
85394
85599
  HISTORIAN_ALERT_COOLDOWN_MS = 60 * 1000;
85395
85600
  lastHistorianAlertBySession = new Map;
85601
+ STORED_COMPARTMENT_FAILURE_BACKOFF_MS = 30 * 60 * 1000;
85396
85602
  });
85397
85603
 
85398
85604
  // src/hooks/magic-context/compartment-runner-recomp.ts
@@ -106994,6 +107200,8 @@ var TODOWRITE_TOOL = "todowrite";
106994
107200
  var availabilityBySession = new BoundedSessionMap(1000);
106995
107201
  var permissionDeniedBySession = new BoundedSessionMap(2000);
106996
107202
  var ctxReducePermissionDenyLogged = new BoundedSessionMap(1000);
107203
+ var ctxReduceSpawnPermissionDenied = new BoundedSessionMap(1000);
107204
+ var ctxReduceSpawnPermissionReads = new Map;
106997
107205
  function permissionCacheKey(toolName, sessionId) {
106998
107206
  return `${toolName}\x00${sessionId}`;
106999
107207
  }
@@ -107012,6 +107220,12 @@ function verdictFromToolsMap(tools, toolName) {
107012
107220
  return false;
107013
107221
  return null;
107014
107222
  }
107223
+ function unfrozenVerdict(sessionId, toolName, mapVerdict) {
107224
+ if (toolName === CTX_REDUCE_TOOL && ctxReduceSpawnPermissionDenied.get(sessionId) === true) {
107225
+ return false;
107226
+ }
107227
+ return mapVerdict ?? true;
107228
+ }
107015
107229
  function resolveToolAvailabilityFromMessages(sessionId, toolName, messages) {
107016
107230
  if (toolName === CTX_REDUCE_TOOL && !ctxReduceRegisteredGlobally) {
107017
107231
  return { callable: false, frozen: true };
@@ -107023,7 +107237,7 @@ function resolveToolAvailabilityFromMessages(sessionId, toolName, messages) {
107023
107237
  for (const message of messages) {
107024
107238
  if (message.info?.role !== "user")
107025
107239
  continue;
107026
- const verdict = verdictFromToolsMap(message.info.tools, toolName) ?? true;
107240
+ const verdict = unfrozenVerdict(sessionId, toolName, verdictFromToolsMap(message.info.tools, toolName));
107027
107241
  availabilityBySession.set(key, verdict);
107028
107242
  return { callable: verdict, frozen: true };
107029
107243
  }
@@ -107046,7 +107260,7 @@ function resolveToolAvailability(sessionId, toolName) {
107046
107260
  if (!row)
107047
107261
  return { callable: true, frozen: false };
107048
107262
  const verdict = row.tools === null ? null : verdictFromToolsMap(JSON.parse(row.tools), toolName);
107049
- const resolved = verdict ?? true;
107263
+ const resolved = unfrozenVerdict(sessionId, toolName, verdict);
107050
107264
  availabilityBySession.set(key, resolved);
107051
107265
  return { callable: resolved, frozen: true };
107052
107266
  } catch (error) {
@@ -107162,6 +107376,58 @@ async function resolveToolPermissionDenied(client, sessionId, toolName, activeAg
107162
107376
  permissionDeniedBySession.set(permissionCacheKey(toolName, sessionId), denied);
107163
107377
  return denied;
107164
107378
  }
107379
+ function primeCtxReduceSpawnPermission(client, sessionId, spawnAgent) {
107380
+ if (!ctxReduceSpawnPermissionReadNeeded(sessionId))
107381
+ return Promise.resolve();
107382
+ const inFlight = ctxReduceSpawnPermissionReads.get(sessionId);
107383
+ if (inFlight)
107384
+ return inFlight;
107385
+ const read = (async () => {
107386
+ try {
107387
+ const denied = await resolveToolPermissionDenied(client, sessionId, CTX_REDUCE_TOOL, spawnAgent);
107388
+ ctxReduceSpawnPermissionDenied.set(sessionId, denied);
107389
+ if (denied) {
107390
+ sessionLog(sessionId, `ctx_reduce permission is denied for agent=${spawnAgent ?? "(session default)"}; session freezes without ctx_reduce tags, guidance, or nudges`);
107391
+ }
107392
+ } catch (error) {
107393
+ sessionLog(sessionId, "ctx_reduce permission read before freeze failed (fail-open):", error);
107394
+ } finally {
107395
+ ctxReduceSpawnPermissionReads.delete(sessionId);
107396
+ }
107397
+ })();
107398
+ ctxReduceSpawnPermissionReads.set(sessionId, read);
107399
+ return read;
107400
+ }
107401
+ function ctxReduceSpawnPermissionReadNeeded(sessionId) {
107402
+ return ctxReduceRegisteredGlobally && availabilityBySession.get(cacheKey2(CTX_REDUCE_TOOL, sessionId)) === undefined && ctxReduceSpawnPermissionDenied.get(sessionId) === undefined;
107403
+ }
107404
+ function spawnAgentFromMessages(messages) {
107405
+ for (const message of messages) {
107406
+ if (message.info?.role !== "user")
107407
+ continue;
107408
+ const agent = message.info.agent;
107409
+ return typeof agent === "string" && agent.length > 0 ? agent : undefined;
107410
+ }
107411
+ return;
107412
+ }
107413
+ function spawnAgentFromOpenCodeDb(sessionId) {
107414
+ if (!openCodeDbExists())
107415
+ return { persisted: false };
107416
+ try {
107417
+ const row = withReadOnlySessionDb((db) => db.prepare(`SELECT json_extract(data, '$.agent') AS agent FROM message
107418
+ WHERE session_id = ? AND json_extract(data, '$.role') = 'user'
107419
+ ORDER BY time_created ASC LIMIT 1`).get(sessionId));
107420
+ if (!row)
107421
+ return { persisted: false };
107422
+ return {
107423
+ persisted: true,
107424
+ agent: typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined
107425
+ };
107426
+ } catch (error) {
107427
+ sessionLog(sessionId, "ctx_reduce spawn agent read failed:", error);
107428
+ return { persisted: false };
107429
+ }
107430
+ }
107165
107431
  function todowritePermissionDenied(client, sessionId, activeAgent) {
107166
107432
  return resolveToolPermissionDenied(client, sessionId, TODOWRITE_TOOL, activeAgent);
107167
107433
  }
@@ -108930,6 +109196,12 @@ function createSystemPromptHashHandler(deps) {
108930
109196
  sessionLog(sessionId, "system-prompt-hash session meta load failed:", error);
108931
109197
  }
108932
109198
  const isSubagentSession = sessionMetaEarly?.isSubagent === true;
109199
+ if (deps.client && ctxReduceSpawnPermissionReadNeeded(sessionId)) {
109200
+ const spawn = spawnAgentFromOpenCodeDb(sessionId);
109201
+ if (spawn.persisted) {
109202
+ await primeCtxReduceSpawnPermission(deps.client, sessionId, spawn.agent);
109203
+ }
109204
+ }
108933
109205
  const availability = resolveCtxReduceAvailability(sessionId);
108934
109206
  const ctxReduceCallable = availability.callable;
108935
109207
  const subagentReduceMode = isSubagentSession && ctxReduceCallable;
@@ -118754,6 +119026,9 @@ function createTransform(deps) {
118754
119026
  passOutcome.record("compaction-mode-transition-failure");
118755
119027
  sessionLog(sessionId, "compaction mode transition failed (retrying next pass):", error);
118756
119028
  }
119029
+ if (deps.client !== undefined && !compactionOff) {
119030
+ await primeCtxReduceSpawnPermission(deps.client, sessionId, spawnAgentFromMessages(messages));
119031
+ }
118757
119032
  if (deps.transformMode === "rust") {
118758
119033
  if (!rustModeTransform) {
118759
119034
  sessionLog(sessionId, "rust transform unavailable; using raw passthrough");
@@ -121778,7 +122053,7 @@ import { join as join15 } from "node:path";
121778
122053
 
121779
122054
  // src/shared/opencode-config-dir.ts
121780
122055
  import { homedir as homedir9 } from "node:os";
121781
- import { join as join14, resolve as resolve6 } from "node:path";
122056
+ import { join as join14, resolve as resolve7 } from "node:path";
121782
122057
  function resolveHomeDir() {
121783
122058
  return process.env.HOME?.trim() || homedir9();
121784
122059
  }
@@ -121790,7 +122065,7 @@ function getOpenCodeGlobalConfigDir() {
121790
122065
  }
121791
122066
  function getEnvConfigDir() {
121792
122067
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
121793
- return envConfigDir ? resolve6(envConfigDir) : undefined;
122068
+ return envConfigDir ? resolve7(envConfigDir) : undefined;
121794
122069
  }
121795
122070
  function getOpenCodeConfigDirs() {
121796
122071
  const dirs = [getOpenCodeGlobalConfigDir(), join14(resolveHomeDir(), ".opencode")];
@@ -124025,6 +124300,17 @@ class V2StoreReader {
124025
124300
  range(sessionID, after, through) {
124026
124301
  return trackDecodeOperation("range", () => through <= after ? [] : this.all(sessionID, after, undefined, through));
124027
124302
  }
124303
+ spanRowStamps(sessionID, after, through) {
124304
+ if (!Number.isSafeInteger(after) || !Number.isSafeInteger(through))
124305
+ throw new Error("Invalid seq span");
124306
+ const rows = this.db.prepare(`SELECT seq, id, time_updated AS updated, length(CAST(data AS BLOB)) AS bytes
124307
+ FROM session_message WHERE session_id = ? AND seq > ? AND seq <= ?
124308
+ ORDER BY seq ASC`).all(sessionID, after, through);
124309
+ const stamps = new Map;
124310
+ for (const row of rows)
124311
+ stamps.set(row.seq, `${row.id}\x00${row.updated ?? ""}\x00${row.bytes ?? ""}`);
124312
+ return stamps;
124313
+ }
124028
124314
  rawRowsThrough(sessionID, throughSeq, limit) {
124029
124315
  return trackDecodeOperation("rawRowsThrough", () => {
124030
124316
  if (!Number.isSafeInteger(throughSeq))
@@ -126040,8 +126326,11 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
126040
126326
  anchors
126041
126327
  });
126042
126328
  if (moduleRoute) {
126043
- const run = await runClassifyThroughModule(args, chunk, anchors, signal);
126044
- recordInvocation(args, startedAt, { status: "completed" });
126329
+ const { accounting, ...run } = await runClassifyThroughModule(args, chunk, anchors, signal);
126330
+ recordInvocation(args, startedAt, {
126331
+ status: "completed",
126332
+ moduleAccounting: accounting
126333
+ });
126045
126334
  return run;
126046
126335
  }
126047
126336
  const executor = resolveHiddenCompletionExecutor(args.hiddenCompletionExecutor, args.client, args.db, args.sessionDirectory, "classify-memories");
@@ -126143,7 +126432,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
126143
126432
  task: "classify",
126144
126433
  command_id: `classify:${args.moduleCommandId ?? Date.now()}:${createHash29("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`,
126145
126434
  authority_generation: args.moduleAuthorityGeneration,
126146
- ...resolvedModelChain.length > 0 ? { model_chain: resolvedModelChain } : {},
126435
+ model_chain: resolvedModelChain,
126147
126436
  payload: {
126148
126437
  prompt_body: prompt,
126149
126438
  items: chunk.map((candidate) => ({
@@ -126164,6 +126453,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
126164
126453
  if (result.truncated === true) {
126165
126454
  throw new Error("classify returned length-capped output");
126166
126455
  }
126456
+ const accounting = moduleClassifyAccounting(result);
126167
126457
  const parsed = validateClassifyManifest(manifestText, new Set(chunk.map((candidate) => candidate.id)));
126168
126458
  const rows = parsed.map((entry) => {
126169
126459
  const candidate = chunk.find((item) => item.id === entry.id);
@@ -126236,7 +126526,39 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
126236
126526
  throw new Error(`module accepted unknown memory ${moduleId}`);
126237
126527
  return candidate.contextMemory.id;
126238
126528
  });
126239
- return { classified: acceptedContextIds.length, changed: acceptedContextIds.length };
126529
+ return {
126530
+ classified: acceptedContextIds.length,
126531
+ changed: acceptedContextIds.length,
126532
+ accounting
126533
+ };
126534
+ }
126535
+ function nonNegativeCount(value) {
126536
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
126537
+ }
126538
+ function moduleClassifyAccounting(result) {
126539
+ const accounting = {};
126540
+ const usage = result.usage;
126541
+ if (usage && typeof usage === "object") {
126542
+ const record = usage;
126543
+ accounting.tokens = {
126544
+ input: nonNegativeCount(record.input),
126545
+ output: nonNegativeCount(record.output),
126546
+ cacheRead: nonNegativeCount(record.cache_read),
126547
+ cacheWrite: nonNegativeCount(record.cache_write)
126548
+ };
126549
+ }
126550
+ const diagnostics = result.diagnostics;
126551
+ const model = diagnostics && typeof diagnostics === "object" ? diagnostics.model : undefined;
126552
+ if (typeof model === "string") {
126553
+ const parsed = parseProviderModel(model);
126554
+ if (parsed) {
126555
+ accounting.providerId = parsed.providerID;
126556
+ accounting.modelId = parsed.modelID;
126557
+ } else if (model.trim()) {
126558
+ accounting.modelId = model.trim();
126559
+ }
126560
+ }
126561
+ return accounting;
126240
126562
  }
126241
126563
  function applyClassifications(args, chunk, manifestText) {
126242
126564
  const byId = new Map(chunk.map((m) => [m.id, m]));
@@ -126281,6 +126603,11 @@ function recordInvocation(args, startedAt, params) {
126281
126603
  providerId: params.completion.providerId,
126282
126604
  modelId: params.completion.modelId
126283
126605
  } : {},
126606
+ ...params.moduleAccounting ? {
126607
+ tokens: params.moduleAccounting.tokens,
126608
+ providerId: params.moduleAccounting.providerId,
126609
+ modelId: params.moduleAccounting.modelId
126610
+ } : {},
126284
126611
  error: params.error
126285
126612
  });
126286
126613
  }
@@ -132253,13 +132580,18 @@ class V2ContextRefusal extends Error {
132253
132580
  this.name = "V2ContextRefusal";
132254
132581
  }
132255
132582
  }
132256
- async function interruptBeforeProvider(session, sessionID) {
132583
+ var INTERRUPT_CONFIRMATION_TIMEOUT_MS = 2000;
132584
+ var systemTimers = {
132585
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
132586
+ clearTimeout: (handle) => clearTimeout(handle)
132587
+ };
132588
+ async function interruptBeforeProvider(session, sessionID, timers = systemTimers) {
132257
132589
  let timer;
132258
132590
  try {
132259
132591
  const result = await Promise.race([
132260
132592
  session.interrupt({ sessionID }),
132261
132593
  new Promise((_, reject) => {
132262
- timer = setTimeout(() => reject(new Error("interrupt exceeded 2000 ms")), 2000);
132594
+ timer = timers.setTimeout(() => reject(new Error(`interrupt exceeded ${INTERRUPT_CONFIRMATION_TIMEOUT_MS} ms`)), INTERRUPT_CONFIRMATION_TIMEOUT_MS);
132263
132595
  })
132264
132596
  ]);
132265
132597
  if (result.interrupted !== true)
@@ -132267,10 +132599,80 @@ async function interruptBeforeProvider(session, sessionID) {
132267
132599
  } catch (cause) {
132268
132600
  throw new V2ContextRefusal(undefined, { cause });
132269
132601
  } finally {
132270
- clearTimeout(timer);
132602
+ if (timer !== undefined)
132603
+ timers.clearTimeout(timer);
132271
132604
  }
132272
132605
  }
132273
132606
 
132607
+ // src/v2/hooks/restore-rows.ts
132608
+ class RestoredRowCache {
132609
+ capacity;
132610
+ sessions = new Map;
132611
+ constructor(capacity = 16) {
132612
+ this.capacity = capacity;
132613
+ }
132614
+ rows(reader, sessionID, after, through) {
132615
+ if (through <= after)
132616
+ return [];
132617
+ const kept = this.sessions.get(sessionID) ?? new Map;
132618
+ const stamps = reader.spanRowStamps(sessionID, after, through);
132619
+ const runs = [];
132620
+ let previous = after;
132621
+ let open;
132622
+ for (const [seq, stamp] of stamps) {
132623
+ if (kept.get(seq)?.stamp === stamp) {
132624
+ open = undefined;
132625
+ } else if (open) {
132626
+ open.through = seq;
132627
+ } else {
132628
+ open = { after: previous, through: seq };
132629
+ runs.push(open);
132630
+ }
132631
+ previous = seq;
132632
+ }
132633
+ const fresh = new Map;
132634
+ for (const run of runs)
132635
+ for (const row of readPages(reader, sessionID, run.after, run.through))
132636
+ fresh.set(row.seq, row);
132637
+ const next = new Map;
132638
+ const rows = [];
132639
+ for (const [seq, stamp] of stamps) {
132640
+ const row = fresh.get(seq) ?? kept.get(seq)?.row;
132641
+ if (!row)
132642
+ continue;
132643
+ next.set(seq, { stamp, row });
132644
+ rows.push(row);
132645
+ }
132646
+ this.sessions.delete(sessionID);
132647
+ this.sessions.set(sessionID, next);
132648
+ while (this.sessions.size > this.capacity) {
132649
+ const oldest = this.sessions.keys().next().value;
132650
+ if (oldest === undefined)
132651
+ break;
132652
+ this.sessions.delete(oldest);
132653
+ }
132654
+ return structuredClone(rows);
132655
+ }
132656
+ forget(sessionID) {
132657
+ this.sessions.delete(sessionID);
132658
+ }
132659
+ clear() {
132660
+ this.sessions.clear();
132661
+ }
132662
+ }
132663
+ function readPages(reader, sessionID, after, through) {
132664
+ const rows = [];
132665
+ let cursor = after;
132666
+ while (cursor < through) {
132667
+ const page = reader.page(sessionID, { after: cursor, through, limit: 100 });
132668
+ rows.push(...page.rows);
132669
+ if (page.rows.length < 100 || page.cursor === undefined)
132670
+ break;
132671
+ cursor = page.cursor;
132672
+ }
132673
+ return rows;
132674
+ }
132675
+
132274
132676
  // src/hooks/magic-context/live-session-state.ts
132275
132677
  function createLiveSessionState() {
132276
132678
  return {
@@ -146107,7 +146509,7 @@ import { isAbsolute as isAbsolute7 } from "node:path";
146107
146509
  // ../retina-local-fs/src/path-fence.ts
146108
146510
  import { lstat as lstat2, readlink, realpath as realpath2 } from "node:fs/promises";
146109
146511
  import { homedir as homedir12 } from "node:os";
146110
- import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute6, join as join20, relative, resolve as resolve7, sep } from "node:path";
146512
+ import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute6, join as join20, relative, resolve as resolve8, sep } from "node:path";
146111
146513
 
146112
146514
  // ../retina-local-fs/src/errors.ts
146113
146515
  class ProviderError extends Error {
@@ -146123,7 +146525,7 @@ class ProviderError extends Error {
146123
146525
  async function resolveAndFenceProviderPath(configuredPath, options) {
146124
146526
  const { home, dataDirectory } = await resolveFenceRoots(options);
146125
146527
  const expanded = configuredPath.startsWith("~/") ? join20(home, configuredPath.slice(2)) : configuredPath === "~" ? home : configuredPath;
146126
- const absolute = isAbsolute6(expanded) ? resolve7(expanded) : resolve7(options.cwd ?? process.cwd(), expanded);
146528
+ const absolute = isAbsolute6(expanded) ? resolve8(expanded) : resolve8(options.cwd ?? process.cwd(), expanded);
146127
146529
  const canonical = await canonicalPath(absolute, options.allowMissing);
146128
146530
  if (isFencedPath(canonical, home, dataDirectory)) {
146129
146531
  throw new ProviderError("fenced_path", `Refusing fenced path: ${canonical}`);
@@ -146131,14 +146533,14 @@ async function resolveAndFenceProviderPath(configuredPath, options) {
146131
146533
  return canonical;
146132
146534
  }
146133
146535
  async function resolveFenceRoots(options) {
146134
- const configuredHomePath = resolve7(options.homeDirectory ?? process.env.HOME ?? homedir12());
146536
+ const configuredHomePath = resolve8(options.homeDirectory ?? process.env.HOME ?? homedir12());
146135
146537
  let home;
146136
146538
  try {
146137
146539
  home = await realpath2(configuredHomePath);
146138
146540
  } catch (error) {
146139
146541
  throw fsError(configuredHomePath, error);
146140
146542
  }
146141
- const configuredDataDirectory = resolve7(options.dataDirectory ?? process.env.XDG_DATA_HOME ?? join20(home, ".local", "share"));
146543
+ const configuredDataDirectory = resolve8(options.dataDirectory ?? process.env.XDG_DATA_HOME ?? join20(home, ".local", "share"));
146142
146544
  const dataDirectory = await canonicalPath(configuredDataDirectory, true);
146143
146545
  return { home, dataDirectory };
146144
146546
  }
@@ -146156,7 +146558,7 @@ async function canonicalPath(path, allowMissing) {
146156
146558
  const metadata = await lstat2(candidate);
146157
146559
  if (metadata.isSymbolicLink()) {
146158
146560
  const target = await readlink(candidate);
146159
- const resolvedTarget = resolve7(dirname10(candidate), target);
146561
+ const resolvedTarget = resolve8(dirname10(candidate), target);
146160
146562
  return canonicalPath(join20(resolvedTarget, ...suffix), true);
146161
146563
  }
146162
146564
  } catch (candidateError) {
@@ -146180,8 +146582,8 @@ async function canonicalPath(path, allowMissing) {
146180
146582
  }
146181
146583
  }
146182
146584
  }
146183
- function isFencedPath(canonicalPath, homeDirectory, dataDirectory = process.env.XDG_DATA_HOME ?? join20(resolve7(homeDirectory), ".local", "share")) {
146184
- const cortexkitRoot = join20(resolve7(dataDirectory), "cortexkit");
146585
+ function isFencedPath(canonicalPath, homeDirectory, dataDirectory = process.env.XDG_DATA_HOME ?? join20(resolve8(homeDirectory), ".local", "share")) {
146586
+ const cortexkitRoot = join20(resolve8(dataDirectory), "cortexkit");
146185
146587
  const relativeToCortexkit = relative(cortexkitRoot, canonicalPath);
146186
146588
  const insideCortexkit = relativeToCortexkit !== "" && relativeToCortexkit !== ".." && !relativeToCortexkit.startsWith(`..${sep}`) && !isAbsolute6(relativeToCortexkit);
146187
146589
  const parts = insideCortexkit ? relativeToCortexkit.split(sep) : [];
@@ -147654,6 +148056,7 @@ async function registerContext(context) {
147654
148056
  const historyRefreshSessions = new Set;
147655
148057
  const pendingMaterializationSessions = new Set;
147656
148058
  const lastHeuristicsTurnId = new Map;
148059
+ const restoredRows = new RestoredRowCache;
147657
148060
  const rawProviders = new Map;
147658
148061
  let passDuties;
147659
148062
  let toolDuties;
@@ -147814,6 +148217,7 @@ async function registerContext(context) {
147814
148217
  historyRefreshSessions.delete(sessionID);
147815
148218
  pendingMaterializationSessions.delete(sessionID);
147816
148219
  lastHeuristicsTurnId.delete(sessionID);
148220
+ restoredRows.forget(sessionID);
147817
148221
  systemPromptRefreshSessions.delete(sessionID);
147818
148222
  systemPrompt?.clearSession(sessionID);
147819
148223
  tagger.cleanup(sessionID);
@@ -148038,7 +148442,7 @@ async function registerContext(context) {
148038
148442
  const servedBoundary = boundaryID ? servedBoundaryRow(reader, draft.sessionID, boundaryID) : null;
148039
148443
  const boundary = servedBoundary?.seq ?? reader.sequenceForId(draft.sessionID, boundaryID) ?? (reader.earliestSequence(draft.sessionID) ?? 0) - 1;
148040
148444
  const present = new Set(draft.messages.map((message) => message.id));
148041
- const restored = reader.range(draft.sessionID, boundary, cut.seq).filter((row) => !present.has(row.id)).flatMap((row) => restoreRow(row, draft.model, hostUsesMediaAssets() ? {
148445
+ const restored = restoredRows.rows(reader, draft.sessionID, boundary, cut.seq).filter((row) => !present.has(row.id)).flatMap((row) => restoreRow(row, draft.model, hostUsesMediaAssets() ? {
148042
148446
  asset: hostMediaAsset,
148043
148447
  unavailable: (detail) => sessionLog(draft.sessionID, `v2 restore: attachment replaced by a note row=${detail.rowID} name=${JSON.stringify(detail.name ?? null)} mediaType=${detail.mediaType} reason=${JSON.stringify(detail.reason)}`)
148044
148448
  } : undefined));
@@ -148191,6 +148595,7 @@ ${baseline}
148191
148595
  for (const release of rawProviders.values())
148192
148596
  release();
148193
148597
  rawProviders.clear();
148598
+ restoredRows.clear();
148194
148599
  }
148195
148600
  };
148196
148601
  }
@@ -148291,6 +148696,9 @@ function packageInfo() {
148291
148696
  directory = parent;
148292
148697
  }
148293
148698
  }
148699
+ function formatUpdateAvailableMessage(latest, current) {
148700
+ return `Magic Context ${latest} is available (running ${current}). To install it, open /plugins, select Magic Context and press ctrl+u, or run \`opencode plugin update\`. A version-pinned plugin entry must be changed in your OpenCode config instead.`;
148701
+ }
148294
148702
  function startUpdateChecks(context, check = (signal) => {
148295
148703
  return packageInfo().development ? Promise.resolve(null) : getLatestVersion("latest", { signal });
148296
148704
  }) {
@@ -148305,10 +148713,11 @@ function startUpdateChecks(context, check = (signal) => {
148305
148713
  continue;
148306
148714
  await context.storage.set("version-check-at", Date.now());
148307
148715
  const latest = await check(controller.signal);
148308
- const comparison = latest ? compareSemverCore(latest, packageInfo().version) : null;
148309
- if (!controller.signal.aborted && comparison !== null && comparison > 0) {
148716
+ const current = packageInfo().version;
148717
+ const comparison = latest ? compareSemverCore(latest, current) : null;
148718
+ if (latest && !controller.signal.aborted && comparison !== null && comparison > 0) {
148310
148719
  pushNotification("toast", {
148311
- message: `Magic Context ${latest} is available. Update the plugin to install it.`,
148720
+ message: formatUpdateAvailableMessage(latest, current),
148312
148721
  variant: "info"
148313
148722
  });
148314
148723
  }