@cortexkit/opencode-magic-context 0.30.6 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +1 -0
  2. package/dist/agents/magic-context-prompt.d.ts +1 -1
  3. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  4. package/dist/config/schema/magic-context.d.ts +5 -10
  5. package/dist/config/schema/magic-context.d.ts.map +1 -1
  6. package/dist/features/builtin-commands/commands.d.ts.map +1 -1
  7. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/open-opencode-db.d.ts +3 -1
  9. package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -1
  10. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  11. package/dist/features/magic-context/memory/project-identity.d.ts +18 -5
  12. package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
  13. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  14. package/dist/features/magic-context/search.d.ts +13 -2
  15. package/dist/features/magic-context/search.d.ts.map +1 -1
  16. package/dist/features/magic-context/session-project-backfill.d.ts +31 -0
  17. package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -0
  18. package/dist/features/magic-context/storage-db.d.ts +1 -1
  19. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  20. package/dist/features/magic-context/storage-meta-persisted.d.ts +26 -1
  21. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  22. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  23. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  24. package/dist/features/magic-context/storage-schema-helpers.d.ts +1 -1
  25. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  26. package/dist/features/magic-context/storage-v22-backfill-failures.d.ts +3 -1
  27. package/dist/features/magic-context/storage-v22-backfill-failures.d.ts.map +1 -1
  28. package/dist/features/magic-context/storage.d.ts +1 -1
  29. package/dist/features/magic-context/storage.d.ts.map +1 -1
  30. package/dist/features/magic-context/transform-decision-log.d.ts +1 -1
  31. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  32. package/dist/features/magic-context/v22-deferred-backfill.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/auto-search-hint.d.ts.map +1 -1
  34. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -1
  35. package/dist/hooks/magic-context/command-handler.d.ts +11 -0
  36. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/compartment-runner-types.d.ts +10 -2
  40. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/compartment-runner.d.ts +2 -1
  42. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +11 -1
  44. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -3
  46. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/hook-handlers.d.ts +0 -2
  48. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/hook.d.ts +0 -1
  50. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/inject-compartments.d.ts +2 -0
  52. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +16 -1
  54. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +1 -1
  56. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -3
  58. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/tag-messages.d.ts +3 -4
  60. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +3 -3
  62. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  63. package/dist/hooks/magic-context/transform.d.ts +8 -12
  64. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts +13 -0
  66. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +2021 -660
  69. package/dist/plugin/hooks/create-session-hooks.d.ts +0 -1
  70. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  71. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  72. package/dist/plugin/tool-registry.d.ts.map +1 -1
  73. package/dist/shared/announcement.d.ts +1 -1
  74. package/dist/shared/announcement.d.ts.map +1 -1
  75. package/dist/shared/rpc-types.d.ts +1 -2
  76. package/dist/shared/rpc-types.d.ts.map +1 -1
  77. package/dist/shared/tag-transcript.d.ts +3 -4
  78. package/dist/shared/tag-transcript.d.ts.map +1 -1
  79. package/dist/tools/ctx-search/constants.d.ts +1 -1
  80. package/dist/tools/ctx-search/constants.d.ts.map +1 -1
  81. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  82. package/dist/tools/ctx-search/types.d.ts +1 -1
  83. package/dist/tools/ctx-search/types.d.ts.map +1 -1
  84. package/dist/tui/data/context-db.d.ts.map +1 -1
  85. package/package.json +1 -1
  86. package/src/shared/announcement.ts +5 -4
  87. package/src/shared/rpc-types.ts +1 -2
  88. package/src/shared/tag-transcript.ts +3 -4
  89. package/src/tui/data/context-db.ts +0 -1
  90. package/src/tui/index.tsx +9 -1
  91. package/src/tui/slots/sidebar-content.tsx +6 -7
package/dist/index.js CHANGED
@@ -15372,7 +15372,6 @@ var init_magic_context = __esm(() => {
15372
15372
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
15373
15373
  auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
15374
15374
  language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
15375
- ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
15376
15375
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
15377
15376
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
15378
15377
  cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m") or per-model object ({ default: "5m", "model-id": "10m" })'),
@@ -15411,9 +15410,9 @@ var init_magic_context = __esm(() => {
15411
15410
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
15412
15411
  smart_drops: exports_external.boolean().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
15413
15412
  caveman_text_compression: exports_external.object({
15414
- enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
15413
+ enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Active for primary sessions when enabled; never for subagents. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
15415
15414
  min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
15416
- }).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Only active when ctx_reduce_enabled is false. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
15415
+ }).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Active for primary sessions when enabled; never for subagents. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
15417
15416
  memory: exports_external.object({
15418
15417
  enabled: exports_external.boolean().default(true).describe("Enable cross-session memory (default: true)"),
15419
15418
  injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
@@ -15446,10 +15445,344 @@ var init_magic_context = __esm(() => {
15446
15445
  });
15447
15446
  });
15448
15447
 
15448
+ // src/shared/error-message.ts
15449
+ function getErrorMessage(error51) {
15450
+ return error51 instanceof Error ? error51.message : String(error51);
15451
+ }
15452
+ function readString(value) {
15453
+ if (typeof value === "string" && value.length > 0)
15454
+ return value;
15455
+ if (typeof value === "number")
15456
+ return String(value);
15457
+ return;
15458
+ }
15459
+ function clip(value, max) {
15460
+ if (value.length <= max)
15461
+ return value;
15462
+ return `${value.slice(0, max)}…`;
15463
+ }
15464
+ function describeError(error51) {
15465
+ const stringForm = clip(safeString(error51), 400);
15466
+ if (!(error51 instanceof Error) && !(error51 && typeof error51 === "object")) {
15467
+ return {
15468
+ name: typeof error51,
15469
+ message: "",
15470
+ stringForm,
15471
+ brief: stringForm || "<empty>"
15472
+ };
15473
+ }
15474
+ const obj = error51;
15475
+ const nameFromField = readString(obj.name);
15476
+ const nameFromCtor = error51?.constructor?.name;
15477
+ const name = nameFromField ?? nameFromCtor ?? "Error";
15478
+ const message = readString(obj.message) ?? "";
15479
+ const status = readString(obj.status) ?? readString(obj.statusCode);
15480
+ const code = readString(obj.code);
15481
+ let causeName;
15482
+ const cause = obj.cause;
15483
+ if (cause && typeof cause === "object") {
15484
+ const causeRecord = cause;
15485
+ causeName = readString(causeRecord.name) ?? cause.constructor?.name;
15486
+ }
15487
+ const stack = readString(obj.stack);
15488
+ const stackHead = stack ? stack.split(`
15489
+ `).slice(0, 4).map((l) => l.trim()).filter((l) => l.length > 0).join(" | ") : undefined;
15490
+ const briefParts = [];
15491
+ if (name)
15492
+ briefParts.push(name);
15493
+ if (message)
15494
+ briefParts.push(`message="${clip(message, 200)}"`);
15495
+ if (status)
15496
+ briefParts.push(`status=${status}`);
15497
+ if (code)
15498
+ briefParts.push(`code=${code}`);
15499
+ if (causeName)
15500
+ briefParts.push(`cause=${causeName}`);
15501
+ if (!message && stringForm && stringForm !== name) {
15502
+ briefParts.push(`str="${clip(stringForm, 200)}"`);
15503
+ }
15504
+ const brief = briefParts.join(" ") || stringForm || name;
15505
+ return {
15506
+ name,
15507
+ message,
15508
+ ...status ? { status } : {},
15509
+ ...code ? { code } : {},
15510
+ ...causeName ? { causeName } : {},
15511
+ ...stackHead ? { stackHead } : {},
15512
+ stringForm,
15513
+ brief
15514
+ };
15515
+ }
15516
+ function safeString(value) {
15517
+ try {
15518
+ return String(value);
15519
+ } catch {
15520
+ return "<unstringifiable>";
15521
+ }
15522
+ }
15523
+
15524
+ // src/shared/sqlite.ts
15525
+ function buildNodeSqliteDatabaseClass(DatabaseSync) {
15526
+ const SAVEPOINT = "mc_tx_sp";
15527
+
15528
+ class NodeSqliteDatabase extends DatabaseSync {
15529
+ constructor(filename, options) {
15530
+ const translated = { ...options };
15531
+ if (options && "readonly" in options) {
15532
+ translated.readOnly = options.readonly;
15533
+ delete translated.readonly;
15534
+ }
15535
+ super(typeof filename === "string" ? filename : ":memory:", translated);
15536
+ }
15537
+ prepare(sql) {
15538
+ const stmt = super.prepare(sql);
15539
+ for (const method of ["run", "get", "all"]) {
15540
+ const original = stmt[method].bind(stmt);
15541
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
15542
+ }
15543
+ return stmt;
15544
+ }
15545
+ transaction(fn) {
15546
+ const self2 = this;
15547
+ const wrapped = function(...args) {
15548
+ const nested = self2.isTransaction === true;
15549
+ self2.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : "BEGIN");
15550
+ try {
15551
+ const result = fn.apply(this, args);
15552
+ self2.exec(nested ? `RELEASE ${SAVEPOINT}` : "COMMIT");
15553
+ return result;
15554
+ } catch (error51) {
15555
+ if (nested) {
15556
+ self2.exec(`ROLLBACK TO ${SAVEPOINT}`);
15557
+ self2.exec(`RELEASE ${SAVEPOINT}`);
15558
+ } else {
15559
+ self2.exec("ROLLBACK");
15560
+ }
15561
+ throw error51;
15562
+ }
15563
+ };
15564
+ return wrapped;
15565
+ }
15566
+ }
15567
+ return NodeSqliteDatabase;
15568
+ }
15569
+ var isBun, bunSpec, nodeSpec, sqliteModule, DatabaseImpl, Database;
15570
+ var init_sqlite = __esm(async () => {
15571
+ isBun = typeof process !== "undefined" && typeof process.versions?.bun === "string";
15572
+ bunSpec = "bun:" + "sqlite";
15573
+ nodeSpec = "node:" + "sqlite";
15574
+ sqliteModule = isBun ? await import(bunSpec) : await import(nodeSpec);
15575
+ DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
15576
+ Database = DatabaseImpl;
15577
+ });
15578
+
15579
+ // src/shared/sqlite-helpers.ts
15580
+ function closeQuietly(db) {
15581
+ if (!db)
15582
+ return;
15583
+ try {
15584
+ db.close();
15585
+ } catch {}
15586
+ }
15587
+
15588
+ // src/features/magic-context/compaction-marker.ts
15589
+ import { existsSync as existsSync6 } from "node:fs";
15590
+ import { join as join3 } from "node:path";
15591
+ function randomBase62(length) {
15592
+ const chars = [];
15593
+ for (let i = 0;i < length; i++) {
15594
+ chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
15595
+ }
15596
+ return chars.join("");
15597
+ }
15598
+ function generateId(prefix, timestampMs, counter = 0n) {
15599
+ const encoded = BigInt(timestampMs) * 0x1000n + counter;
15600
+ const hex3 = encoded.toString(16).padStart(14, "0");
15601
+ return `${prefix}_${hex3}${randomBase62(14)}`;
15602
+ }
15603
+ function generateMessageId(timestampMs, counter = 0n) {
15604
+ return generateId("msg", timestampMs, counter);
15605
+ }
15606
+ function generatePartId(timestampMs, counter = 0n) {
15607
+ return generateId("prt", timestampMs, counter);
15608
+ }
15609
+ function getOpenCodeDbPath() {
15610
+ return join3(getDataDir(), "opencode", "opencode.db");
15611
+ }
15612
+ function isOpenCodeSchemaCompatible(db, dbPath) {
15613
+ if (cachedSchemaCompatible?.path === dbPath) {
15614
+ return cachedSchemaCompatible.compatible;
15615
+ }
15616
+ try {
15617
+ const messageCols = new Set(db.prepare("PRAGMA table_info(message)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
15618
+ const partCols = new Set(db.prepare("PRAGMA table_info(part)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
15619
+ const missingMessage = REQUIRED_MESSAGE_COLUMNS.filter((c) => !messageCols.has(c));
15620
+ const missingPart = REQUIRED_PART_COLUMNS.filter((c) => !partCols.has(c));
15621
+ if (missingMessage.length > 0 || missingPart.length > 0) {
15622
+ log(`[magic-context] compaction-marker: OpenCode DB schema missing required columns ` + `(message: [${missingMessage.join(", ")}], part: [${missingPart.join(", ")}]). ` + `Marker injection disabled for this process. ` + `This usually means OpenCode was updated and magic-context is out of date.`);
15623
+ cachedSchemaCompatible = { path: dbPath, compatible: false };
15624
+ return false;
15625
+ }
15626
+ cachedSchemaCompatible = { path: dbPath, compatible: true };
15627
+ return true;
15628
+ } catch (error51) {
15629
+ log(`[magic-context] compaction-marker: schema probe failed: ${error51 instanceof Error ? error51.message : String(error51)}. ` + `Marker injection disabled until next process restart.`);
15630
+ cachedSchemaCompatible = { path: dbPath, compatible: false };
15631
+ return false;
15632
+ }
15633
+ }
15634
+ function getWritableOpenCodeDb() {
15635
+ const dbPath = getOpenCodeDbPath();
15636
+ if (cachedWriteDb?.path === dbPath) {
15637
+ return cachedWriteDb.db;
15638
+ }
15639
+ if (cachedWriteDb) {
15640
+ try {
15641
+ closeQuietly(cachedWriteDb.db);
15642
+ } catch {}
15643
+ }
15644
+ if (!existsSync6(dbPath)) {
15645
+ throw new Error(`OpenCode database not found at ${dbPath} (is OpenCode installed?)`);
15646
+ }
15647
+ const db = new Database(dbPath);
15648
+ db.exec("PRAGMA busy_timeout=5000");
15649
+ db.exec("PRAGMA journal_mode=WAL");
15650
+ cachedWriteDb = { path: dbPath, db };
15651
+ return db;
15652
+ }
15653
+ function getNonSummaryMessageSortKey(sessionId, messageId) {
15654
+ const db = getWritableOpenCodeDb();
15655
+ const row = db.prepare(`SELECT time_created, id
15656
+ FROM message
15657
+ WHERE session_id = ?
15658
+ AND id = ?
15659
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
15660
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
15661
+ LIMIT 1`).get(sessionId, messageId);
15662
+ if (typeof row?.time_created !== "number" || typeof row.id !== "string") {
15663
+ return null;
15664
+ }
15665
+ return { id: row.id, timeCreated: row.time_created };
15666
+ }
15667
+ function findBoundaryUserMessage(sessionId, endMessageId) {
15668
+ const db = getWritableOpenCodeDb();
15669
+ const target = getNonSummaryMessageSortKey(sessionId, endMessageId);
15670
+ if (!target)
15671
+ return null;
15672
+ const boundary = db.prepare(`SELECT id, time_created, data
15673
+ FROM message
15674
+ WHERE session_id = ?
15675
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
15676
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
15677
+ AND COALESCE(json_extract(data, '$.role'), '') = 'user'
15678
+ AND (time_created < ? OR (time_created = ? AND id <= ?))
15679
+ ORDER BY time_created DESC, id DESC
15680
+ LIMIT 1`).get(sessionId, target.timeCreated, target.timeCreated, target.id);
15681
+ if (typeof boundary?.id !== "string" || typeof boundary.time_created !== "number") {
15682
+ return null;
15683
+ }
15684
+ return { id: boundary.id, timeCreated: boundary.time_created };
15685
+ }
15686
+ function compareOpenCodeMessagesByCanonicalOrder(sessionId, leftMessageId, rightMessageId) {
15687
+ const left = getNonSummaryMessageSortKey(sessionId, leftMessageId);
15688
+ const right = getNonSummaryMessageSortKey(sessionId, rightMessageId);
15689
+ if (!left || !right)
15690
+ return null;
15691
+ if (left.timeCreated < right.timeCreated)
15692
+ return -1;
15693
+ if (left.timeCreated > right.timeCreated)
15694
+ return 1;
15695
+ if (left.id < right.id)
15696
+ return -1;
15697
+ if (left.id > right.id)
15698
+ return 1;
15699
+ return 0;
15700
+ }
15701
+ function getOpenCodeMessageById(sessionId, messageId) {
15702
+ const db = getWritableOpenCodeDb();
15703
+ const row = db.prepare(`SELECT id FROM message WHERE session_id = ? AND id = ? LIMIT 1`).get(sessionId, messageId);
15704
+ return row ?? null;
15705
+ }
15706
+ function injectCompactionMarker(args) {
15707
+ const db = getWritableOpenCodeDb();
15708
+ if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath())) {
15709
+ return null;
15710
+ }
15711
+ const boundary = args.resolvedBoundary ?? findBoundaryUserMessage(args.sessionId, args.endMessageId);
15712
+ if (!boundary) {
15713
+ log(`[magic-context] compaction-marker: no user message found at or before endMessageId ${args.endMessageId} (ordinal ${args.endOrdinal})`);
15714
+ return null;
15715
+ }
15716
+ const boundaryTime = boundary.timeCreated;
15717
+ const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
15718
+ const compactionPartId = generatePartId(boundaryTime, 1n);
15719
+ const summaryPartId = generatePartId(boundaryTime + 1, 2n);
15720
+ const summaryMsgData = JSON.stringify({
15721
+ role: "assistant",
15722
+ parentID: boundary.id,
15723
+ summary: true,
15724
+ finish: "stop",
15725
+ mode: "compaction",
15726
+ agent: "compaction",
15727
+ path: { cwd: args.directory, root: args.directory },
15728
+ cost: 0,
15729
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
15730
+ modelID: "magic-context",
15731
+ providerID: "magic-context",
15732
+ time: { created: boundaryTime + 1 }
15733
+ });
15734
+ try {
15735
+ db.transaction(() => {
15736
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
15737
+ db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
15738
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
15739
+ })();
15740
+ log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
15741
+ return {
15742
+ boundaryMessageId: boundary.id,
15743
+ summaryMessageId: summaryMsgId,
15744
+ compactionPartId,
15745
+ summaryPartId
15746
+ };
15747
+ } catch (error51) {
15748
+ log(`[magic-context] compaction-marker: injection failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
15749
+ return null;
15750
+ }
15751
+ }
15752
+ function removeCompactionMarker(state) {
15753
+ try {
15754
+ const db = getWritableOpenCodeDb();
15755
+ db.transaction(() => {
15756
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
15757
+ db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
15758
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
15759
+ })();
15760
+ return true;
15761
+ } catch (error51) {
15762
+ log(`[magic-context] compaction-marker: removal failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
15763
+ return false;
15764
+ }
15765
+ }
15766
+ var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null, REQUIRED_MESSAGE_COLUMNS, REQUIRED_PART_COLUMNS, cachedSchemaCompatible = null;
15767
+ var init_compaction_marker = __esm(async () => {
15768
+ init_data_path();
15769
+ init_logger();
15770
+ await init_sqlite();
15771
+ REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
15772
+ REQUIRED_PART_COLUMNS = [
15773
+ "id",
15774
+ "message_id",
15775
+ "session_id",
15776
+ "time_created",
15777
+ "time_updated",
15778
+ "data"
15779
+ ];
15780
+ });
15781
+
15449
15782
  // src/features/magic-context/memory/project-identity.ts
15450
15783
  import { execFileSync } from "node:child_process";
15451
15784
  import { createHash } from "node:crypto";
15452
- import { statSync as statSync2 } from "node:fs";
15785
+ import { existsSync as existsSync8, realpathSync, statSync as statSync2 } from "node:fs";
15453
15786
  import path3 from "node:path";
15454
15787
  function asError(error51) {
15455
15788
  return error51 instanceof Error ? error51 : undefined;
@@ -15526,6 +15859,9 @@ function classifyGitError(error51, rawDirectory) {
15526
15859
  return new ProjectIdentityError("permission_denied", rawDirectory, "Permission denied while spawning git", asError(error51));
15527
15860
  }
15528
15861
  const stderr = getErrorStderr(error51).toLowerCase();
15862
+ if (stderr.includes("detected dubious ownership")) {
15863
+ return new ProjectIdentityError("dubious_ownership", rawDirectory, "git refused to read the repository because it detected dubious ownership", asError(error51));
15864
+ }
15529
15865
  if (stderr.includes("not a git repository") || stderr.includes("does not have any commits yet") || stderr.includes("ambiguous argument 'head'") || stderr.includes("unknown revision or path")) {
15530
15866
  return new ProjectIdentityError("not_git_repo", rawDirectory, "Directory has no git root commit; caller may use directory fallback", asError(error51));
15531
15867
  }
@@ -15538,9 +15874,12 @@ function resolveProjectIdentityStrict(directory) {
15538
15874
  return cached2;
15539
15875
  }
15540
15876
  assertDirectoryUsable(canonical, directory);
15877
+ if (!hasGitDir(canonical)) {
15878
+ throw new ProjectIdentityError("not_git_repo", directory, "Directory has no git metadata; caller may use directory fallback");
15879
+ }
15541
15880
  let output;
15542
15881
  try {
15543
- output = execFileSync("git", ["rev-list", "--max-parents=0", "HEAD"], {
15882
+ output = execFileSyncForIdentity("git", ["rev-list", "--max-parents=0", "HEAD"], {
15544
15883
  cwd: canonical,
15545
15884
  encoding: "utf8",
15546
15885
  env: { ...process.env, LC_ALL: "C", LANG: "C" },
@@ -15558,10 +15897,82 @@ function resolveProjectIdentityStrict(directory) {
15558
15897
  }
15559
15898
  const identity = `git:${rootCommit}`;
15560
15899
  identityCache.set(canonical, identity);
15900
+ lastKnownGitIdentityCache.set(canonical, identity);
15901
+ transientFailureCooldown.delete(canonical);
15902
+ dubiousOwnershipFallbackDirectories.delete(canonical);
15903
+ transientGitIdentityReuseLoggedDirectories.delete(canonical);
15561
15904
  return identity;
15562
15905
  }
15563
15906
  function shouldUseDirectoryFallback(error51) {
15564
- return error51.errorClass === "not_git_repo" || error51.errorClass === "unknown" && error51.message.startsWith("Unable to access project directory:");
15907
+ return error51.errorClass !== "permission_denied";
15908
+ }
15909
+ function getActiveCooldown(canonical) {
15910
+ const until = transientFailureCooldown.get(canonical);
15911
+ if (until === undefined)
15912
+ return;
15913
+ if (nowMs() < until)
15914
+ return until;
15915
+ transientFailureCooldown.delete(canonical);
15916
+ return;
15917
+ }
15918
+ function lastKnownGitIdentity(canonical) {
15919
+ return lastKnownGitIdentityCache.get(canonical) ?? identityCache.get(canonical);
15920
+ }
15921
+ function nearestLastKnownGitIdentity(canonical) {
15922
+ const visited = new Set;
15923
+ const walk = (start) => {
15924
+ let current = start;
15925
+ while (!visited.has(current)) {
15926
+ visited.add(current);
15927
+ const cached2 = lastKnownGitIdentity(current);
15928
+ if (cached2 !== undefined)
15929
+ return { identity: cached2, source: current };
15930
+ const parent = path3.dirname(current);
15931
+ if (parent === current)
15932
+ break;
15933
+ current = parent;
15934
+ }
15935
+ return;
15936
+ };
15937
+ const exactOrAncestor = walk(canonical);
15938
+ if (exactOrAncestor)
15939
+ return exactOrAncestor;
15940
+ try {
15941
+ const realCanonical = realpathSync.native(canonical);
15942
+ if (realCanonical !== canonical)
15943
+ return walk(realCanonical);
15944
+ } catch {}
15945
+ return;
15946
+ }
15947
+ function reuseLastKnownGitIdentity(canonical) {
15948
+ const cached2 = nearestLastKnownGitIdentity(canonical);
15949
+ if (cached2 === undefined)
15950
+ return;
15951
+ if (!transientGitIdentityReuseLoggedDirectories.has(canonical)) {
15952
+ transientGitIdentityReuseLoggedDirectories.add(canonical);
15953
+ const sourceNote = cached2.source === canonical ? "" : ` from ancestor ${cached2.source}`;
15954
+ log(`[magic-context] git identity resolution is temporarily unavailable for ${canonical}; reusing the last successful project identity${sourceNote} to avoid splitting project-scoped memory`);
15955
+ }
15956
+ return cached2.identity;
15957
+ }
15958
+ function formatDubiousOwnershipWarning(canonical) {
15959
+ return `Magic Context: git refused to read ${canonical} (dubious ownership — the repo is owned by a different user). Using a directory-based project identity for now, which keeps memory separate from this repo's normal identity. Fix: git config --global --add safe.directory ${canonical}`;
15960
+ }
15961
+ function recordDubiousOwnershipFallback(canonical) {
15962
+ dubiousOwnershipFallbackDirectories.add(canonical);
15963
+ if (dubiousOwnershipLoggedDirectories.has(canonical))
15964
+ return;
15965
+ dubiousOwnershipLoggedDirectories.add(canonical);
15966
+ log(`[magic-context] ${formatDubiousOwnershipWarning(canonical)}`);
15967
+ }
15968
+ function takeDubiousOwnershipProjectIdentityWarning(directory) {
15969
+ const canonical = path3.resolve(directory);
15970
+ if (!dubiousOwnershipFallbackDirectories.has(canonical))
15971
+ return null;
15972
+ if (dubiousOwnershipWarnedDirectories.has(canonical))
15973
+ return null;
15974
+ dubiousOwnershipWarnedDirectories.add(canonical);
15975
+ return formatDubiousOwnershipWarning(canonical);
15565
15976
  }
15566
15977
  function resolveProjectIdentity(directory) {
15567
15978
  const canonical = path3.resolve(directory);
@@ -15572,27 +15983,74 @@ function resolveProjectIdentity(directory) {
15572
15983
  }
15573
15984
  directoryFallbackCache.delete(canonical);
15574
15985
  }
15986
+ if (getActiveCooldown(canonical) !== undefined) {
15987
+ if (hasGitDir(canonical)) {
15988
+ const cachedGitIdentity = reuseLastKnownGitIdentity(canonical);
15989
+ if (cachedGitIdentity !== undefined) {
15990
+ return cachedGitIdentity;
15991
+ }
15992
+ }
15993
+ return directoryFallback(canonical);
15994
+ }
15575
15995
  try {
15576
15996
  return resolveProjectIdentityStrict(directory);
15577
15997
  } catch (error51) {
15578
15998
  if (error51 instanceof ProjectIdentityError && shouldUseDirectoryFallback(error51)) {
15579
15999
  const fallback = directoryFallback(canonical);
15580
- if (!hasGitDir(canonical)) {
16000
+ const hasGitMetadata = hasGitDir(canonical);
16001
+ if (!hasGitMetadata) {
15581
16002
  directoryFallbackCache.set(canonical, fallback);
16003
+ transientFailureCooldown.delete(canonical);
16004
+ } else {
16005
+ transientFailureCooldown.set(canonical, nowMs() + TRANSIENT_FAILURE_COOLDOWN_MS);
16006
+ const cachedGitIdentity = reuseLastKnownGitIdentity(canonical);
16007
+ if (cachedGitIdentity !== undefined) {
16008
+ return cachedGitIdentity;
16009
+ }
16010
+ }
16011
+ if (error51.errorClass === "dubious_ownership") {
16012
+ recordDubiousOwnershipFallback(canonical);
15582
16013
  }
15583
16014
  return fallback;
15584
16015
  }
15585
16016
  throw error51;
15586
16017
  }
15587
16018
  }
15588
- function hasGitDir(canonical) {
16019
+ function resolveProjectIdentityOrFallback(directory) {
15589
16020
  try {
15590
- statSync2(path3.join(canonical, ".git"));
16021
+ return resolveProjectIdentity(directory);
16022
+ } catch (error51) {
16023
+ const canonical = path3.resolve(directory);
16024
+ const fallback = directoryFallback(canonical);
16025
+ const message = error51 instanceof Error ? error51.message : String(error51);
16026
+ log(`[magic-context] project identity resolution failed for ${canonical}; using directory fallback ${fallback}: ${message}`);
16027
+ return fallback;
16028
+ }
16029
+ }
16030
+ function hasGitDir(canonical) {
16031
+ if (hasGitDirInAncestorChain(canonical)) {
15591
16032
  return true;
16033
+ }
16034
+ try {
16035
+ const realCanonical = realpathSync.native(canonical);
16036
+ return realCanonical !== canonical && hasGitDirInAncestorChain(realCanonical);
15592
16037
  } catch {
15593
16038
  return false;
15594
16039
  }
15595
16040
  }
16041
+ function hasGitDirInAncestorChain(startDirectory) {
16042
+ let current = startDirectory;
16043
+ while (true) {
16044
+ if (existsSync8(path3.join(current, ".git"))) {
16045
+ return true;
16046
+ }
16047
+ const parent = path3.dirname(current);
16048
+ if (parent === current) {
16049
+ return false;
16050
+ }
16051
+ current = parent;
16052
+ }
16053
+ }
15596
16054
  function normalizeStoredProjectPath(rawOrStored) {
15597
16055
  if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
15598
16056
  return rawOrStored;
@@ -15606,10 +16064,19 @@ function normalizeStoredProjectPath(rawOrStored) {
15606
16064
  function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
15607
16065
  return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
15608
16066
  }
15609
- var GIT_TIMEOUT_MS = 5000, identityCache, directoryFallbackCache, ProjectIdentityError;
16067
+ var GIT_TIMEOUT_MS = 5000, TRANSIENT_FAILURE_COOLDOWN_MS, identityCache, lastKnownGitIdentityCache, directoryFallbackCache, transientFailureCooldown, dubiousOwnershipFallbackDirectories, dubiousOwnershipLoggedDirectories, dubiousOwnershipWarnedDirectories, transientGitIdentityReuseLoggedDirectories, execFileSyncForIdentity, nowMs = () => Date.now(), ProjectIdentityError;
15610
16068
  var init_project_identity = __esm(() => {
16069
+ init_logger();
16070
+ TRANSIENT_FAILURE_COOLDOWN_MS = 5 * 60 * 1000;
15611
16071
  identityCache = new Map;
16072
+ lastKnownGitIdentityCache = new Map;
15612
16073
  directoryFallbackCache = new Map;
16074
+ transientFailureCooldown = new Map;
16075
+ dubiousOwnershipFallbackDirectories = new Set;
16076
+ dubiousOwnershipLoggedDirectories = new Set;
16077
+ dubiousOwnershipWarnedDirectories = new Set;
16078
+ transientGitIdentityReuseLoggedDirectories = new Set;
16079
+ execFileSyncForIdentity = execFileSync;
15613
16080
  ProjectIdentityError = class ProjectIdentityError extends Error {
15614
16081
  errorClass;
15615
16082
  rawDirectory;
@@ -15625,6 +16092,80 @@ var init_project_identity = __esm(() => {
15625
16092
  };
15626
16093
  });
15627
16094
 
16095
+ // src/features/magic-context/session-project-storage.ts
16096
+ function getUpsertSessionProjectStatement(db) {
16097
+ let stmt = upsertSessionProjectStatements.get(db);
16098
+ if (!stmt) {
16099
+ stmt = db.prepare(`INSERT INTO session_projects (session_id, harness, project_path, updated_at)
16100
+ VALUES (?, ?, ?, ?)
16101
+ ON CONFLICT(session_id, harness) DO UPDATE SET
16102
+ project_path = excluded.project_path,
16103
+ updated_at = excluded.updated_at
16104
+ WHERE session_projects.project_path <> excluded.project_path`);
16105
+ upsertSessionProjectStatements.set(db, stmt);
16106
+ }
16107
+ return stmt;
16108
+ }
16109
+ function getRepairSessionChunkProjectStatement(db) {
16110
+ let stmt = repairSessionChunkProjectStatements.get(db);
16111
+ if (!stmt) {
16112
+ stmt = db.prepare(`UPDATE compartment_chunk_embeddings
16113
+ SET project_path = ?
16114
+ WHERE session_id = ?
16115
+ AND harness = ?
16116
+ AND project_path <> ?`);
16117
+ repairSessionChunkProjectStatements.set(db, stmt);
16118
+ }
16119
+ return stmt;
16120
+ }
16121
+ function getRepairProjectChunkProjectStatement(db) {
16122
+ let stmt = repairProjectChunkProjectStatements.get(db);
16123
+ if (!stmt) {
16124
+ stmt = db.prepare(`UPDATE compartment_chunk_embeddings
16125
+ SET project_path = (
16126
+ SELECT sp.project_path
16127
+ FROM session_projects sp
16128
+ WHERE sp.session_id = compartment_chunk_embeddings.session_id
16129
+ AND sp.harness = compartment_chunk_embeddings.harness
16130
+ LIMIT 1
16131
+ )
16132
+ WHERE EXISTS (
16133
+ SELECT 1
16134
+ FROM session_projects sp
16135
+ WHERE sp.session_id = compartment_chunk_embeddings.session_id
16136
+ AND sp.harness = compartment_chunk_embeddings.harness
16137
+ AND sp.project_path <> compartment_chunk_embeddings.project_path
16138
+ AND (
16139
+ sp.project_path = ?
16140
+ OR compartment_chunk_embeddings.project_path = ?
16141
+ )
16142
+ )`);
16143
+ repairProjectChunkProjectStatements.set(db, stmt);
16144
+ }
16145
+ return stmt;
16146
+ }
16147
+ function recordSessionProjectIdentity(db, sessionId, projectPath) {
16148
+ if (!sessionId || !projectPath)
16149
+ return;
16150
+ const harness = getHarness();
16151
+ const now = Date.now();
16152
+ db.transaction(() => {
16153
+ getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
16154
+ getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
16155
+ })();
16156
+ }
16157
+ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
16158
+ if (!projectPath)
16159
+ return 0;
16160
+ return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
16161
+ }
16162
+ var upsertSessionProjectStatements, repairSessionChunkProjectStatements, repairProjectChunkProjectStatements;
16163
+ var init_session_project_storage = __esm(() => {
16164
+ upsertSessionProjectStatements = new WeakMap;
16165
+ repairSessionChunkProjectStatements = new WeakMap;
16166
+ repairProjectChunkProjectStatements = new WeakMap;
16167
+ });
16168
+
15628
16169
  // src/features/magic-context/overflow-detection.ts
15629
16170
  function extractErrorMessage(error51) {
15630
16171
  if (!error51)
@@ -16735,78 +17276,14 @@ function removeSystemReminders(text) {
16735
17276
  }
16736
17277
  var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
16737
17278
 
16738
- // src/shared/sqlite.ts
16739
- function buildNodeSqliteDatabaseClass(DatabaseSync) {
16740
- const SAVEPOINT = "mc_tx_sp";
16741
-
16742
- class NodeSqliteDatabase extends DatabaseSync {
16743
- constructor(filename, options) {
16744
- const translated = { ...options };
16745
- if (options && "readonly" in options) {
16746
- translated.readOnly = options.readonly;
16747
- delete translated.readonly;
16748
- }
16749
- super(typeof filename === "string" ? filename : ":memory:", translated);
16750
- }
16751
- prepare(sql) {
16752
- const stmt = super.prepare(sql);
16753
- for (const method of ["run", "get", "all"]) {
16754
- const original = stmt[method].bind(stmt);
16755
- stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
16756
- }
16757
- return stmt;
16758
- }
16759
- transaction(fn) {
16760
- const self2 = this;
16761
- const wrapped = function(...args) {
16762
- const nested = self2.isTransaction === true;
16763
- self2.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : "BEGIN");
16764
- try {
16765
- const result = fn.apply(this, args);
16766
- self2.exec(nested ? `RELEASE ${SAVEPOINT}` : "COMMIT");
16767
- return result;
16768
- } catch (error51) {
16769
- if (nested) {
16770
- self2.exec(`ROLLBACK TO ${SAVEPOINT}`);
16771
- self2.exec(`RELEASE ${SAVEPOINT}`);
16772
- } else {
16773
- self2.exec("ROLLBACK");
16774
- }
16775
- throw error51;
16776
- }
16777
- };
16778
- return wrapped;
16779
- }
16780
- }
16781
- return NodeSqliteDatabase;
16782
- }
16783
- var isBun, bunSpec, nodeSpec, sqliteModule, DatabaseImpl, Database;
16784
- var init_sqlite = __esm(async () => {
16785
- isBun = typeof process !== "undefined" && typeof process.versions?.bun === "string";
16786
- bunSpec = "bun:" + "sqlite";
16787
- nodeSpec = "node:" + "sqlite";
16788
- sqliteModule = isBun ? await import(bunSpec) : await import(nodeSpec);
16789
- DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
16790
- Database = DatabaseImpl;
16791
- });
16792
-
16793
- // src/shared/sqlite-helpers.ts
16794
- function closeQuietly(db) {
16795
- if (!db)
16796
- return;
16797
- try {
16798
- db.close();
16799
- } catch {}
16800
- }
16801
-
16802
17279
  // src/hooks/magic-context/read-session-db.ts
16803
- import { existsSync as existsSync6 } from "node:fs";
16804
- import { join as join3 } from "node:path";
16805
- function getOpenCodeDbPath() {
16806
- return join3(getDataDir(), "opencode", "opencode.db");
17280
+ import { existsSync as existsSync10 } from "node:fs";
17281
+ import { join as join4 } from "node:path";
17282
+ function getOpenCodeDbPath2() {
17283
+ return join4(getDataDir(), "opencode", "opencode.db");
16807
17284
  }
16808
17285
  function openCodeDbExists() {
16809
- return existsSync6(getOpenCodeDbPath());
17286
+ return existsSync10(getOpenCodeDbPath2());
16810
17287
  }
16811
17288
  function closeCachedReadOnlyDb() {
16812
17289
  if (!cachedReadOnlyDb) {
@@ -16821,7 +17298,7 @@ function closeCachedReadOnlyDb() {
16821
17298
  }
16822
17299
  }
16823
17300
  function getReadOnlySessionDb() {
16824
- const dbPath = getOpenCodeDbPath();
17301
+ const dbPath = getOpenCodeDbPath2();
16825
17302
  if (cachedReadOnlyDb?.path === dbPath) {
16826
17303
  return cachedReadOnlyDb.db;
16827
17304
  }
@@ -152420,6 +152897,15 @@ var init_migrations = __esm(() => {
152420
152897
  );
152421
152898
  `);
152422
152899
  }
152900
+ },
152901
+ {
152902
+ version: 50,
152903
+ description: "add durable ctx-wrapup session marker",
152904
+ up(db) {
152905
+ if (tableExists2(db, "session_meta")) {
152906
+ ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
152907
+ }
152908
+ }
152423
152909
  }
152424
152910
  ];
152425
152911
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -152555,82 +153041,6 @@ var init_project_docs_hash = __esm(() => {
152555
153041
  docsCache = new Map;
152556
153042
  });
152557
153043
 
152558
- // src/shared/error-message.ts
152559
- function getErrorMessage(error51) {
152560
- return error51 instanceof Error ? error51.message : String(error51);
152561
- }
152562
- function readString(value) {
152563
- if (typeof value === "string" && value.length > 0)
152564
- return value;
152565
- if (typeof value === "number")
152566
- return String(value);
152567
- return;
152568
- }
152569
- function clip(value, max) {
152570
- if (value.length <= max)
152571
- return value;
152572
- return `${value.slice(0, max)}…`;
152573
- }
152574
- function describeError(error51) {
152575
- const stringForm = clip(safeString(error51), 400);
152576
- if (!(error51 instanceof Error) && !(error51 && typeof error51 === "object")) {
152577
- return {
152578
- name: typeof error51,
152579
- message: "",
152580
- stringForm,
152581
- brief: stringForm || "<empty>"
152582
- };
152583
- }
152584
- const obj = error51;
152585
- const nameFromField = readString(obj.name);
152586
- const nameFromCtor = error51?.constructor?.name;
152587
- const name2 = nameFromField ?? nameFromCtor ?? "Error";
152588
- const message = readString(obj.message) ?? "";
152589
- const status = readString(obj.status) ?? readString(obj.statusCode);
152590
- const code = readString(obj.code);
152591
- let causeName;
152592
- const cause = obj.cause;
152593
- if (cause && typeof cause === "object") {
152594
- const causeRecord = cause;
152595
- causeName = readString(causeRecord.name) ?? cause.constructor?.name;
152596
- }
152597
- const stack = readString(obj.stack);
152598
- const stackHead = stack ? stack.split(`
152599
- `).slice(0, 4).map((l) => l.trim()).filter((l) => l.length > 0).join(" | ") : undefined;
152600
- const briefParts = [];
152601
- if (name2)
152602
- briefParts.push(name2);
152603
- if (message)
152604
- briefParts.push(`message="${clip(message, 200)}"`);
152605
- if (status)
152606
- briefParts.push(`status=${status}`);
152607
- if (code)
152608
- briefParts.push(`code=${code}`);
152609
- if (causeName)
152610
- briefParts.push(`cause=${causeName}`);
152611
- if (!message && stringForm && stringForm !== name2) {
152612
- briefParts.push(`str="${clip(stringForm, 200)}"`);
152613
- }
152614
- const brief = briefParts.join(" ") || stringForm || name2;
152615
- return {
152616
- name: name2,
152617
- message,
152618
- ...status ? { status } : {},
152619
- ...code ? { code } : {},
152620
- ...causeName ? { causeName } : {},
152621
- ...stackHead ? { stackHead } : {},
152622
- stringForm,
152623
- brief
152624
- };
152625
- }
152626
- function safeString(value) {
152627
- try {
152628
- return String(value);
152629
- } catch {
152630
- return "<unstringifiable>";
152631
- }
152632
- }
152633
-
152634
153044
  // src/shared/stable-json.ts
152635
153045
  function stableStringify(value, seen = new WeakSet) {
152636
153046
  if (value === undefined)
@@ -152742,12 +153152,12 @@ var init_tool_definition_tokens = __esm(() => {
152742
153152
  });
152743
153153
 
152744
153154
  // src/features/magic-context/tool-owner-backfill.ts
152745
- import { existsSync as existsSync7 } from "node:fs";
152746
- import { join as join4 } from "node:path";
153155
+ import { existsSync as existsSync11 } from "node:fs";
153156
+ import { join as join5 } from "node:path";
152747
153157
  function resolveOpencodeDbPath() {
152748
- return join4(getDataDir(), "opencode", "opencode.db");
153158
+ return join5(getDataDir(), "opencode", "opencode.db");
152749
153159
  }
152750
- function ensureBackfillStateTable(db) {
153160
+ function ensureBackfillStateTable2(db) {
152751
153161
  db.exec(`
152752
153162
  CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
152753
153163
  session_id TEXT PRIMARY KEY,
@@ -152763,7 +153173,7 @@ function ensureBackfillStateTable(db) {
152763
153173
  }
152764
153174
  function runToolOwnerBackfill(db) {
152765
153175
  const startedAt = performance.now();
152766
- ensureBackfillStateTable(db);
153176
+ ensureBackfillStateTable2(db);
152767
153177
  const result = {
152768
153178
  sessionsProcessed: 0,
152769
153179
  sessionsSkippedNoOcDb: 0,
@@ -152780,7 +153190,7 @@ function runToolOwnerBackfill(db) {
152780
153190
  return result;
152781
153191
  }
152782
153192
  const opencodeDbPath = resolveOpencodeDbPath();
152783
- if (!existsSync7(opencodeDbPath)) {
153193
+ if (!existsSync11(opencodeDbPath)) {
152784
153194
  log(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
152785
153195
  markAllUnbackfilledSessionsSkipped(db);
152786
153196
  result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
@@ -152803,7 +153213,7 @@ function runToolOwnerBackfill(db) {
152803
153213
  return result;
152804
153214
  }
152805
153215
  function isToolOwnerBackfillNeeded(db) {
152806
- ensureBackfillStateTable(db);
153216
+ ensureBackfillStateTable2(db);
152807
153217
  const row = db.prepare(`SELECT 1 AS hit
152808
153218
  FROM tags
152809
153219
  WHERE type = 'tool' AND tool_owner_message_id IS NULL
@@ -153004,8 +153414,8 @@ var init_tool_owner_backfill = __esm(() => {
153004
153414
  });
153005
153415
 
153006
153416
  // src/features/magic-context/storage-db.ts
153007
- import { chmodSync, copyFileSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, mkdtempSync } from "node:fs";
153008
- import { dirname as dirname4, join as join5 } from "node:path";
153417
+ import { chmodSync, copyFileSync, cpSync, existsSync as existsSync12, mkdirSync as mkdirSync4, mkdtempSync } from "node:fs";
153418
+ import { dirname as dirname4, join as join6 } from "node:path";
153009
153419
  function getSchemaFenceRejection() {
153010
153420
  return lastSchemaFenceRejection;
153011
153421
  }
@@ -153024,7 +153434,7 @@ function restrictDatabaseFilePermissions(dbPath) {
153024
153434
  return;
153025
153435
  for (const suffix of ["", "-wal", "-shm"]) {
153026
153436
  const file2 = `${dbPath}${suffix}`;
153027
- if (!existsSync8(file2))
153437
+ if (!existsSync12(file2))
153028
153438
  continue;
153029
153439
  try {
153030
153440
  chmodSync(file2, 384);
@@ -153039,22 +153449,22 @@ function resolveDatabasePath(dbPathOverride) {
153039
153449
  }
153040
153450
  const testDataDir = process.env.MAGIC_CONTEXT_TEST_DATA_DIR;
153041
153451
  if (testDataDir && !process.env.XDG_DATA_HOME) {
153042
- const dbDir2 = join5(testDataDir, "cortexkit", "magic-context");
153043
- return { dbDir: dbDir2, dbPath: join5(dbDir2, "context.db") };
153452
+ const dbDir2 = join6(testDataDir, "cortexkit", "magic-context");
153453
+ return { dbDir: dbDir2, dbPath: join6(dbDir2, "context.db") };
153044
153454
  }
153045
153455
  if (false) {}
153046
153456
  const dbDir = getMagicContextStorageDir();
153047
- return { dbDir, dbPath: join5(dbDir, "context.db") };
153457
+ return { dbDir, dbPath: join6(dbDir, "context.db") };
153048
153458
  }
153049
153459
  function getDatabasePath(db) {
153050
153460
  return pathByDatabase.get(db) ?? null;
153051
153461
  }
153052
153462
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
153053
- if (existsSync8(targetDbPath))
153463
+ if (existsSync12(targetDbPath))
153054
153464
  return;
153055
153465
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
153056
- const legacyDbPath = join5(legacyDir, "context.db");
153057
- if (!existsSync8(legacyDbPath))
153466
+ const legacyDbPath = join6(legacyDir, "context.db");
153467
+ if (!existsSync12(legacyDbPath))
153058
153468
  return;
153059
153469
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
153060
153470
  ensureSecureStorageDir(targetDbDir);
@@ -153070,8 +153480,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
153070
153480
  }
153071
153481
  for (const suffix of ["", "-wal", "-shm"]) {
153072
153482
  const src = `${legacyDbPath}${suffix}`;
153073
- const dst = join5(targetDbDir, `context.db${suffix}`);
153074
- if (existsSync8(src)) {
153483
+ const dst = join6(targetDbDir, `context.db${suffix}`);
153484
+ if (existsSync12(src)) {
153075
153485
  try {
153076
153486
  copyFileSync(src, dst);
153077
153487
  } catch (error51) {
@@ -153079,9 +153489,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
153079
153489
  }
153080
153490
  }
153081
153491
  }
153082
- const legacyModelsDir = join5(legacyDir, "models");
153083
- const targetModelsDir = join5(targetDbDir, "models");
153084
- if (existsSync8(legacyModelsDir) && !existsSync8(targetModelsDir)) {
153492
+ const legacyModelsDir = join6(legacyDir, "models");
153493
+ const targetModelsDir = join6(targetDbDir, "models");
153494
+ if (existsSync12(legacyModelsDir) && !existsSync12(targetModelsDir)) {
153085
153495
  try {
153086
153496
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
153087
153497
  } catch (error51) {
@@ -153681,6 +154091,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
153681
154091
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
153682
154092
  emergency_drain_active INTEGER NOT NULL DEFAULT 0,
153683
154093
  historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
154094
+ wrapup_in_progress_state TEXT,
153684
154095
  cached_m0_materialized_at INTEGER,
153685
154096
  cached_m0_session_facts_version INTEGER,
153686
154097
  cached_m0_upgrade_state TEXT,
@@ -153969,6 +154380,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
153969
154380
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
153970
154381
  ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
153971
154382
  ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
154383
+ ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
153972
154384
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
153973
154385
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
153974
154386
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -154147,7 +154559,7 @@ function getDatabasePersistenceError(db) {
154147
154559
  return null;
154148
154560
  return persistenceErrorByDatabase.get(db) ?? null;
154149
154561
  }
154150
- var databases, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 49, PERMISSIONS_ENFORCEABLE, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
154562
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 50, PERMISSIONS_ENFORCEABLE, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
154151
154563
  var init_storage_db = __esm(async () => {
154152
154564
  init_data_path();
154153
154565
  init_logger();
@@ -154487,6 +154899,147 @@ function resetProtectedTailNoEligibleHead(db, sessionId) {
154487
154899
  db.prepare("UPDATE session_meta SET recovery_no_eligible_head_count = 0 WHERE session_id = ?").run(sessionId);
154488
154900
  })();
154489
154901
  }
154902
+ function parseWrapupState(value) {
154903
+ if (typeof value !== "string" || value.trim().length === 0)
154904
+ return null;
154905
+ try {
154906
+ const parsed = JSON.parse(value);
154907
+ if (!parsed || typeof parsed !== "object")
154908
+ return null;
154909
+ if (typeof parsed.holderId !== "string" || parsed.holderId.length === 0)
154910
+ return null;
154911
+ const numberFields = [
154912
+ "acquiredAt",
154913
+ "expiresAt",
154914
+ "messagesToKeep",
154915
+ "anchorRawMessageCount",
154916
+ "targetEligibleEndOrdinal",
154917
+ "lastCompartmentEnd",
154918
+ "chunkIndex",
154919
+ "expectedChunks",
154920
+ "updatedAt"
154921
+ ];
154922
+ for (const field of numberFields) {
154923
+ if (typeof parsed[field] !== "number" || !Number.isFinite(parsed[field]))
154924
+ return null;
154925
+ }
154926
+ return parsed;
154927
+ } catch {
154928
+ return null;
154929
+ }
154930
+ }
154931
+ function readRawWrapupState(db, sessionId) {
154932
+ const row = db.prepare("SELECT wrapup_in_progress_state FROM session_meta WHERE session_id = ?").get(sessionId);
154933
+ return parseWrapupState(row?.wrapup_in_progress_state);
154934
+ }
154935
+ function getWrapupInProgressState(db, sessionId, now = Date.now()) {
154936
+ const state = readRawWrapupState(db, sessionId);
154937
+ if (!state)
154938
+ return null;
154939
+ if (state.expiresAt > now)
154940
+ return state;
154941
+ try {
154942
+ db.exec("BEGIN IMMEDIATE");
154943
+ } catch {
154944
+ return null;
154945
+ }
154946
+ let finished = false;
154947
+ try {
154948
+ const current = readRawWrapupState(db, sessionId);
154949
+ if (current && current.expiresAt <= now) {
154950
+ db.prepare("UPDATE session_meta SET wrapup_in_progress_state = NULL WHERE session_id = ?").run(sessionId);
154951
+ }
154952
+ db.exec("COMMIT");
154953
+ finished = true;
154954
+ } finally {
154955
+ if (!finished) {
154956
+ try {
154957
+ db.exec("ROLLBACK");
154958
+ } catch {}
154959
+ }
154960
+ }
154961
+ return null;
154962
+ }
154963
+ function isWrapupInProgress(db, sessionId, now = Date.now()) {
154964
+ return getWrapupInProgressState(db, sessionId, now) !== null;
154965
+ }
154966
+ function acquireWrapupInProgress(db, sessionId, state, now = Date.now()) {
154967
+ const acquiredAt = now;
154968
+ const next = {
154969
+ ...state,
154970
+ acquiredAt,
154971
+ expiresAt: acquiredAt + WRAPUP_IN_PROGRESS_TTL_MS,
154972
+ updatedAt: acquiredAt
154973
+ };
154974
+ db.exec("BEGIN IMMEDIATE");
154975
+ let finished = false;
154976
+ try {
154977
+ ensureSessionMetaRow(db, sessionId);
154978
+ const current = readRawWrapupState(db, sessionId);
154979
+ if (current && current.expiresAt > now && current.holderId !== state.holderId) {
154980
+ db.exec("COMMIT");
154981
+ finished = true;
154982
+ return { ok: false, state: current };
154983
+ }
154984
+ db.prepare("UPDATE session_meta SET wrapup_in_progress_state = ? WHERE session_id = ?").run(stableStringify(next), sessionId);
154985
+ db.exec("COMMIT");
154986
+ finished = true;
154987
+ return { ok: true, state: next };
154988
+ } finally {
154989
+ if (!finished) {
154990
+ try {
154991
+ db.exec("ROLLBACK");
154992
+ } catch {}
154993
+ }
154994
+ }
154995
+ }
154996
+ function updateWrapupInProgress(db, sessionId, holderId, updates, now = Date.now()) {
154997
+ db.exec("BEGIN IMMEDIATE");
154998
+ let finished = false;
154999
+ try {
155000
+ const current = readRawWrapupState(db, sessionId);
155001
+ if (!current || current.holderId !== holderId || current.expiresAt <= now) {
155002
+ db.exec("ROLLBACK");
155003
+ finished = true;
155004
+ return null;
155005
+ }
155006
+ const next = {
155007
+ ...current,
155008
+ ...updates,
155009
+ holderId,
155010
+ expiresAt: now + WRAPUP_IN_PROGRESS_TTL_MS,
155011
+ updatedAt: now
155012
+ };
155013
+ db.prepare("UPDATE session_meta SET wrapup_in_progress_state = ? WHERE session_id = ?").run(stableStringify(next), sessionId);
155014
+ db.exec("COMMIT");
155015
+ finished = true;
155016
+ return next;
155017
+ } finally {
155018
+ if (!finished) {
155019
+ try {
155020
+ db.exec("ROLLBACK");
155021
+ } catch {}
155022
+ }
155023
+ }
155024
+ }
155025
+ function releaseWrapupInProgress(db, sessionId, holderId) {
155026
+ db.exec("BEGIN IMMEDIATE");
155027
+ let finished = false;
155028
+ try {
155029
+ const current = readRawWrapupState(db, sessionId);
155030
+ if (current?.holderId === holderId) {
155031
+ db.prepare("UPDATE session_meta SET wrapup_in_progress_state = NULL WHERE session_id = ?").run(sessionId);
155032
+ }
155033
+ db.exec("COMMIT");
155034
+ finished = true;
155035
+ } finally {
155036
+ if (!finished) {
155037
+ try {
155038
+ db.exec("ROLLBACK");
155039
+ } catch {}
155040
+ }
155041
+ }
155042
+ }
154490
155043
  function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
154491
155044
  if (usagePercentage >= 95)
154492
155045
  return Math.min(1e6, Math.max(4 * perRunCap, Math.round(0.5 * usable)));
@@ -155164,7 +155717,7 @@ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
155164
155717
  SET new_work_tokens = ?, total_input_tokens = ?
155165
155718
  WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
155166
155719
  }
155167
- var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS, EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95, EMERGENCY_DRAIN_EXIT_MARGIN = 10, EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55, EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000, EMERGENCY_DRAIN_MAX_LATCH_MS;
155720
+ var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS, WRAPUP_IN_PROGRESS_TTL_MS, EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95, EMERGENCY_DRAIN_EXIT_MARGIN = 10, EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55, EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000, EMERGENCY_DRAIN_MAX_LATCH_MS;
155168
155721
  var init_storage_meta_persisted = __esm(() => {
155169
155722
  init_logger();
155170
155723
  init_storage_meta_shared();
@@ -155188,6 +155741,7 @@ var init_storage_meta_persisted = __esm(() => {
155188
155741
  historianDrainFailureAt: 0
155189
155742
  };
155190
155743
  DRAIN_WINDOW_MS = 10 * 60 * 1000;
155744
+ WRAPUP_IN_PROGRESS_TTL_MS = 5 * 60 * 1000;
155191
155745
  EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
155192
155746
  });
155193
155747
 
@@ -164181,10 +164735,10 @@ var require_stringify = __commonJS((exports, module) => {
164181
164735
  replacer = null;
164182
164736
  indent = EMPTY;
164183
164737
  };
164184
- var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two)), gap) : EMPTY;
164738
+ var join7 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two)), gap) : EMPTY;
164185
164739
  var join_content = (inside, value, gap) => {
164186
164740
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
164187
- return join6(comment, inside, gap);
164741
+ return join7(comment, inside, gap);
164188
164742
  };
164189
164743
  var stringify_string = (holder, key, value) => {
164190
164744
  const raw = get_raw_string_literal(holder, key);
@@ -164206,13 +164760,13 @@ var require_stringify = __commonJS((exports, module) => {
164206
164760
  if (i !== 0) {
164207
164761
  inside += COMMA;
164208
164762
  }
164209
- const before = join6(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
164763
+ const before = join7(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
164210
164764
  inside += before || LF + deeper_gap;
164211
164765
  inside += stringify(i, value, deeper_gap) || STR_NULL;
164212
164766
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
164213
164767
  after_comma = process_comments(value, AFTER(i), deeper_gap);
164214
164768
  }
164215
- inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
164769
+ inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
164216
164770
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
164217
164771
  };
164218
164772
  var object_stringify = (value, gap) => {
@@ -164233,13 +164787,13 @@ var require_stringify = __commonJS((exports, module) => {
164233
164787
  inside += COMMA;
164234
164788
  }
164235
164789
  first = false;
164236
- const before = join6(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
164790
+ const before = join7(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
164237
164791
  inside += before || LF + deeper_gap;
164238
164792
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
164239
164793
  after_comma = process_comments(value, AFTER(key), deeper_gap);
164240
164794
  };
164241
164795
  keys.forEach(iteratee);
164242
- inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
164796
+ inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
164243
164797
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
164244
164798
  };
164245
164799
  function stringify(key, holder, gap) {
@@ -165239,16 +165793,16 @@ var init_compartment_prompt = () => {};
165239
165793
 
165240
165794
  // src/shared/opencode-config-dir.ts
165241
165795
  import { homedir as homedir6 } from "node:os";
165242
- import { join as join10, resolve as resolve3 } from "node:path";
165796
+ import { join as join11, resolve as resolve3 } from "node:path";
165243
165797
  function getCliConfigDir() {
165244
165798
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
165245
165799
  if (envConfigDir) {
165246
165800
  return resolve3(envConfigDir);
165247
165801
  }
165248
165802
  if (process.platform === "win32") {
165249
- return join10(homedir6(), ".config", "opencode");
165803
+ return join11(homedir6(), ".config", "opencode");
165250
165804
  }
165251
- return join10(process.env.XDG_CONFIG_HOME || join10(homedir6(), ".config"), "opencode");
165805
+ return join11(process.env.XDG_CONFIG_HOME || join11(homedir6(), ".config"), "opencode");
165252
165806
  }
165253
165807
  function getOpenCodeConfigDir(_options) {
165254
165808
  return getCliConfigDir();
@@ -165257,16 +165811,16 @@ function getOpenCodeConfigPaths(options) {
165257
165811
  const configDir = getOpenCodeConfigDir(options);
165258
165812
  return {
165259
165813
  configDir,
165260
- configJson: join10(configDir, "opencode.json"),
165261
- configJsonc: join10(configDir, "opencode.jsonc"),
165262
- packageJson: join10(configDir, "package.json"),
165263
- omoConfig: join10(configDir, "magic-context.jsonc")
165814
+ configJson: join11(configDir, "opencode.json"),
165815
+ configJsonc: join11(configDir, "opencode.jsonc"),
165816
+ packageJson: join11(configDir, "package.json"),
165817
+ omoConfig: join11(configDir, "magic-context.jsonc")
165264
165818
  };
165265
165819
  }
165266
165820
  var init_opencode_config_dir = () => {};
165267
165821
 
165268
165822
  // src/shared/conflict-detector.ts
165269
- import { join as join11 } from "node:path";
165823
+ import { join as join12 } from "node:path";
165270
165824
  function detectConflicts(directory) {
165271
165825
  const conflicts = {
165272
165826
  compactionAuto: false,
@@ -165323,8 +165877,8 @@ function checkCompaction(directory) {
165323
165877
  return { auto: true, prune: false };
165324
165878
  }
165325
165879
  function readProjectCompaction(directory) {
165326
- const dotOcJsonc = join11(directory, ".opencode", "opencode.jsonc");
165327
- const dotOcJson = join11(directory, ".opencode", "opencode.json");
165880
+ const dotOcJsonc = join12(directory, ".opencode", "opencode.jsonc");
165881
+ const dotOcJson = join12(directory, ".opencode", "opencode.json");
165328
165882
  const dotOcConfig = readJsoncFile(dotOcJsonc) ?? readJsoncFile(dotOcJson);
165329
165883
  if (dotOcConfig?.compaction) {
165330
165884
  const c = dotOcConfig.compaction;
@@ -165332,8 +165886,8 @@ function readProjectCompaction(directory) {
165332
165886
  return { auto: c.auto === true, prune: c.prune === true, resolved: true };
165333
165887
  }
165334
165888
  }
165335
- const rootJsonc = join11(directory, "opencode.jsonc");
165336
- const rootJson = join11(directory, "opencode.json");
165889
+ const rootJsonc = join12(directory, "opencode.jsonc");
165890
+ const rootJson = join12(directory, "opencode.json");
165337
165891
  const rootConfig = readJsoncFile(rootJsonc) ?? readJsoncFile(rootJson);
165338
165892
  if (rootConfig?.compaction) {
165339
165893
  const c = rootConfig.compaction;
@@ -165387,10 +165941,10 @@ function collectPluginEntries(directory) {
165387
165941
  }
165388
165942
  };
165389
165943
  for (const configPath of [
165390
- join11(directory, ".opencode", "opencode.jsonc"),
165391
- join11(directory, ".opencode", "opencode.json"),
165392
- join11(directory, "opencode.jsonc"),
165393
- join11(directory, "opencode.json")
165944
+ join12(directory, ".opencode", "opencode.jsonc"),
165945
+ join12(directory, ".opencode", "opencode.json"),
165946
+ join12(directory, "opencode.jsonc"),
165947
+ join12(directory, "opencode.json")
165394
165948
  ]) {
165395
165949
  const config2 = readJsoncFile(configPath);
165396
165950
  pushFrom(config2?.plugin);
@@ -165431,7 +165985,7 @@ function readOmoDisabledHooks(directory) {
165431
165985
  try {
165432
165986
  const paths = getOpenCodeConfigPaths({ binary: "opencode" });
165433
165987
  for (const name2 of configNames) {
165434
- const configPath = join11(paths.configDir, name2);
165988
+ const configPath = join12(paths.configDir, name2);
165435
165989
  const config2 = readJsoncFile(configPath);
165436
165990
  if (config2?.disabled_hooks) {
165437
165991
  for (const hook of config2.disabled_hooks) {
@@ -165441,7 +165995,7 @@ function readOmoDisabledHooks(directory) {
165441
165995
  }
165442
165996
  } catch {}
165443
165997
  for (const name2 of configNames) {
165444
- const config2 = readJsoncFile(join11(directory, name2));
165998
+ const config2 = readJsoncFile(join12(directory, name2));
165445
165999
  if (config2?.disabled_hooks) {
165446
166000
  for (const hook of config2.disabled_hooks) {
165447
166001
  disabled.add(hook);
@@ -165592,28 +166146,28 @@ __export(exports_conflict_warning_hook, {
165592
166146
  sendConflictWarning: () => sendConflictWarning,
165593
166147
  cleanupConflictWarnings: () => cleanupConflictWarnings
165594
166148
  });
165595
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
166149
+ import { existsSync as existsSync16, readFileSync as readFileSync10 } from "node:fs";
165596
166150
  import { homedir as homedir7, platform as platform2 } from "node:os";
165597
- import { join as join12 } from "node:path";
166151
+ import { join as join13 } from "node:path";
165598
166152
  function getDesktopStatePath() {
165599
166153
  const os2 = platform2();
165600
166154
  const home = homedir7();
165601
166155
  if (os2 === "darwin") {
165602
- return join12(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
166156
+ return join13(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
165603
166157
  }
165604
166158
  if (os2 === "linux") {
165605
- const xdgConfig = process.env.XDG_CONFIG_HOME || join12(home, ".config");
165606
- return join12(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
166159
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join13(home, ".config");
166160
+ return join13(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
165607
166161
  }
165608
166162
  if (os2 === "win32") {
165609
- const appData = process.env.APPDATA || join12(home, "AppData", "Roaming");
165610
- return join12(appData, "ai.opencode.desktop", "opencode.global.dat");
166163
+ const appData = process.env.APPDATA || join13(home, "AppData", "Roaming");
166164
+ return join13(appData, "ai.opencode.desktop", "opencode.global.dat");
165611
166165
  }
165612
166166
  return null;
165613
166167
  }
165614
166168
  function readDesktopState(directory) {
165615
166169
  const statePath = getDesktopStatePath();
165616
- if (!statePath || !existsSync12(statePath)) {
166170
+ if (!statePath || !existsSync16(statePath)) {
165617
166171
  log(`[magic-context] conflict-warning: Desktop state file not found at ${statePath}`);
165618
166172
  return { sessionId: null, sidecarUrl: null };
165619
166173
  }
@@ -165915,196 +166469,6 @@ var init_conflict_warning_hook = __esm(() => {
165915
166469
  cachedDesktopStateByDir = new Map;
165916
166470
  });
165917
166471
 
165918
- // src/features/magic-context/compaction-marker.ts
165919
- import { join as join13 } from "node:path";
165920
- function randomBase62(length) {
165921
- const chars = [];
165922
- for (let i = 0;i < length; i++) {
165923
- chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
165924
- }
165925
- return chars.join("");
165926
- }
165927
- function generateId(prefix, timestampMs, counter = 0n) {
165928
- const encoded = BigInt(timestampMs) * 0x1000n + counter;
165929
- const hex3 = encoded.toString(16).padStart(14, "0");
165930
- return `${prefix}_${hex3}${randomBase62(14)}`;
165931
- }
165932
- function generateMessageId(timestampMs, counter = 0n) {
165933
- return generateId("msg", timestampMs, counter);
165934
- }
165935
- function generatePartId(timestampMs, counter = 0n) {
165936
- return generateId("prt", timestampMs, counter);
165937
- }
165938
- function getOpenCodeDbPath2() {
165939
- return join13(getDataDir(), "opencode", "opencode.db");
165940
- }
165941
- function isOpenCodeSchemaCompatible(db, dbPath) {
165942
- if (cachedSchemaCompatible?.path === dbPath) {
165943
- return cachedSchemaCompatible.compatible;
165944
- }
165945
- try {
165946
- const messageCols = new Set(db.prepare("PRAGMA table_info(message)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
165947
- const partCols = new Set(db.prepare("PRAGMA table_info(part)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
165948
- const missingMessage = REQUIRED_MESSAGE_COLUMNS.filter((c) => !messageCols.has(c));
165949
- const missingPart = REQUIRED_PART_COLUMNS.filter((c) => !partCols.has(c));
165950
- if (missingMessage.length > 0 || missingPart.length > 0) {
165951
- log(`[magic-context] compaction-marker: OpenCode DB schema missing required columns ` + `(message: [${missingMessage.join(", ")}], part: [${missingPart.join(", ")}]). ` + `Marker injection disabled for this process. ` + `This usually means OpenCode was updated and magic-context is out of date.`);
165952
- cachedSchemaCompatible = { path: dbPath, compatible: false };
165953
- return false;
165954
- }
165955
- cachedSchemaCompatible = { path: dbPath, compatible: true };
165956
- return true;
165957
- } catch (error51) {
165958
- log(`[magic-context] compaction-marker: schema probe failed: ${error51 instanceof Error ? error51.message : String(error51)}. ` + `Marker injection disabled until next process restart.`);
165959
- cachedSchemaCompatible = { path: dbPath, compatible: false };
165960
- return false;
165961
- }
165962
- }
165963
- function getWritableOpenCodeDb() {
165964
- const dbPath = getOpenCodeDbPath2();
165965
- if (cachedWriteDb?.path === dbPath) {
165966
- return cachedWriteDb.db;
165967
- }
165968
- if (cachedWriteDb) {
165969
- try {
165970
- closeQuietly(cachedWriteDb.db);
165971
- } catch {}
165972
- }
165973
- const db = new Database(dbPath);
165974
- db.exec("PRAGMA busy_timeout=5000");
165975
- db.exec("PRAGMA journal_mode=WAL");
165976
- cachedWriteDb = { path: dbPath, db };
165977
- return db;
165978
- }
165979
- function getNonSummaryMessageSortKey(sessionId, messageId) {
165980
- const db = getWritableOpenCodeDb();
165981
- const row = db.prepare(`SELECT time_created, id
165982
- FROM message
165983
- WHERE session_id = ?
165984
- AND id = ?
165985
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
165986
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
165987
- LIMIT 1`).get(sessionId, messageId);
165988
- if (typeof row?.time_created !== "number" || typeof row.id !== "string") {
165989
- return null;
165990
- }
165991
- return { id: row.id, timeCreated: row.time_created };
165992
- }
165993
- function findBoundaryUserMessage(sessionId, endMessageId) {
165994
- const db = getWritableOpenCodeDb();
165995
- const target = getNonSummaryMessageSortKey(sessionId, endMessageId);
165996
- if (!target)
165997
- return null;
165998
- const boundary = db.prepare(`SELECT id, time_created, data
165999
- FROM message
166000
- WHERE session_id = ?
166001
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
166002
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
166003
- AND COALESCE(json_extract(data, '$.role'), '') = 'user'
166004
- AND (time_created < ? OR (time_created = ? AND id <= ?))
166005
- ORDER BY time_created DESC, id DESC
166006
- LIMIT 1`).get(sessionId, target.timeCreated, target.timeCreated, target.id);
166007
- if (typeof boundary?.id !== "string" || typeof boundary.time_created !== "number") {
166008
- return null;
166009
- }
166010
- return { id: boundary.id, timeCreated: boundary.time_created };
166011
- }
166012
- function compareOpenCodeMessagesByCanonicalOrder(sessionId, leftMessageId, rightMessageId) {
166013
- const left = getNonSummaryMessageSortKey(sessionId, leftMessageId);
166014
- const right = getNonSummaryMessageSortKey(sessionId, rightMessageId);
166015
- if (!left || !right)
166016
- return null;
166017
- if (left.timeCreated < right.timeCreated)
166018
- return -1;
166019
- if (left.timeCreated > right.timeCreated)
166020
- return 1;
166021
- if (left.id < right.id)
166022
- return -1;
166023
- if (left.id > right.id)
166024
- return 1;
166025
- return 0;
166026
- }
166027
- function getOpenCodeMessageById(sessionId, messageId) {
166028
- const db = getWritableOpenCodeDb();
166029
- const row = db.prepare(`SELECT id FROM message WHERE session_id = ? AND id = ? LIMIT 1`).get(sessionId, messageId);
166030
- return row ?? null;
166031
- }
166032
- function injectCompactionMarker(args) {
166033
- const db = getWritableOpenCodeDb();
166034
- if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath2())) {
166035
- return null;
166036
- }
166037
- const boundary = args.resolvedBoundary ?? findBoundaryUserMessage(args.sessionId, args.endMessageId);
166038
- if (!boundary) {
166039
- log(`[magic-context] compaction-marker: no user message found at or before endMessageId ${args.endMessageId} (ordinal ${args.endOrdinal})`);
166040
- return null;
166041
- }
166042
- const boundaryTime = boundary.timeCreated;
166043
- const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
166044
- const compactionPartId = generatePartId(boundaryTime, 1n);
166045
- const summaryPartId = generatePartId(boundaryTime + 1, 2n);
166046
- const summaryMsgData = JSON.stringify({
166047
- role: "assistant",
166048
- parentID: boundary.id,
166049
- summary: true,
166050
- finish: "stop",
166051
- mode: "compaction",
166052
- agent: "compaction",
166053
- path: { cwd: args.directory, root: args.directory },
166054
- cost: 0,
166055
- tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
166056
- modelID: "magic-context",
166057
- providerID: "magic-context",
166058
- time: { created: boundaryTime + 1 }
166059
- });
166060
- try {
166061
- db.transaction(() => {
166062
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
166063
- db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
166064
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
166065
- })();
166066
- log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
166067
- return {
166068
- boundaryMessageId: boundary.id,
166069
- summaryMessageId: summaryMsgId,
166070
- compactionPartId,
166071
- summaryPartId
166072
- };
166073
- } catch (error51) {
166074
- log(`[magic-context] compaction-marker: injection failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
166075
- return null;
166076
- }
166077
- }
166078
- function removeCompactionMarker(state) {
166079
- try {
166080
- const db = getWritableOpenCodeDb();
166081
- db.transaction(() => {
166082
- db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
166083
- db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
166084
- db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
166085
- })();
166086
- return true;
166087
- } catch (error51) {
166088
- log(`[magic-context] compaction-marker: removal failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
166089
- return false;
166090
- }
166091
- }
166092
- var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null, REQUIRED_MESSAGE_COLUMNS, REQUIRED_PART_COLUMNS, cachedSchemaCompatible = null;
166093
- var init_compaction_marker = __esm(async () => {
166094
- init_data_path();
166095
- init_logger();
166096
- await init_sqlite();
166097
- REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
166098
- REQUIRED_PART_COLUMNS = [
166099
- "id",
166100
- "message_id",
166101
- "session_id",
166102
- "time_created",
166103
- "time_updated",
166104
- "data"
166105
- ];
166106
- });
166107
-
166108
166472
  // src/features/magic-context/dreamer/task-config.ts
166109
166473
  var exports_task_config = {};
166110
166474
  __export(exports_task_config, {
@@ -166979,7 +167343,8 @@ function isNativeRuntimeMissingError(error51) {
166979
167343
  if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
166980
167344
  return true;
166981
167345
  }
166982
- if (!lower.includes("onnxruntime-node"))
167346
+ const mentionsNativeRuntime = lower.includes("onnxruntime-node") || lower.includes("onnxruntime_binding");
167347
+ if (!mentionsNativeRuntime)
166983
167348
  return false;
166984
167349
  return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
166985
167350
  }
@@ -167124,7 +167489,7 @@ class LocalEmbeddingProvider {
167124
167489
  } catch (error51) {
167125
167490
  if (isNativeRuntimeMissingError(error51)) {
167126
167491
  nativeRuntimeMissing = true;
167127
- log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @cortexkit/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
167492
+ log("[magic-context] local embeddings are disabled because the onnxruntime-node native binding is missing or failed to load. Run `npx @cortexkit/magic-context@latest doctor` for repair guidance (use `doctor --force` to reinstall cached plugin packages), or configure an `openai-compatible` embedding HTTP endpoint. Existing memories are unaffected.");
167128
167493
  } else {
167129
167494
  log("[magic-context] embedding model failed to load:", error51);
167130
167495
  }
@@ -168072,80 +168437,6 @@ var init_embedding_cache = __esm(() => {
168072
168437
  embeddingCacheTtlMs = DEFAULT_EMBEDDING_CACHE_TTL_MS;
168073
168438
  });
168074
168439
 
168075
- // src/features/magic-context/session-project-storage.ts
168076
- function getUpsertSessionProjectStatement(db) {
168077
- let stmt = upsertSessionProjectStatements.get(db);
168078
- if (!stmt) {
168079
- stmt = db.prepare(`INSERT INTO session_projects (session_id, harness, project_path, updated_at)
168080
- VALUES (?, ?, ?, ?)
168081
- ON CONFLICT(session_id, harness) DO UPDATE SET
168082
- project_path = excluded.project_path,
168083
- updated_at = excluded.updated_at
168084
- WHERE session_projects.project_path <> excluded.project_path`);
168085
- upsertSessionProjectStatements.set(db, stmt);
168086
- }
168087
- return stmt;
168088
- }
168089
- function getRepairSessionChunkProjectStatement(db) {
168090
- let stmt = repairSessionChunkProjectStatements.get(db);
168091
- if (!stmt) {
168092
- stmt = db.prepare(`UPDATE compartment_chunk_embeddings
168093
- SET project_path = ?
168094
- WHERE session_id = ?
168095
- AND harness = ?
168096
- AND project_path <> ?`);
168097
- repairSessionChunkProjectStatements.set(db, stmt);
168098
- }
168099
- return stmt;
168100
- }
168101
- function getRepairProjectChunkProjectStatement(db) {
168102
- let stmt = repairProjectChunkProjectStatements.get(db);
168103
- if (!stmt) {
168104
- stmt = db.prepare(`UPDATE compartment_chunk_embeddings
168105
- SET project_path = (
168106
- SELECT sp.project_path
168107
- FROM session_projects sp
168108
- WHERE sp.session_id = compartment_chunk_embeddings.session_id
168109
- AND sp.harness = compartment_chunk_embeddings.harness
168110
- LIMIT 1
168111
- )
168112
- WHERE EXISTS (
168113
- SELECT 1
168114
- FROM session_projects sp
168115
- WHERE sp.session_id = compartment_chunk_embeddings.session_id
168116
- AND sp.harness = compartment_chunk_embeddings.harness
168117
- AND sp.project_path <> compartment_chunk_embeddings.project_path
168118
- AND (
168119
- sp.project_path = ?
168120
- OR compartment_chunk_embeddings.project_path = ?
168121
- )
168122
- )`);
168123
- repairProjectChunkProjectStatements.set(db, stmt);
168124
- }
168125
- return stmt;
168126
- }
168127
- function recordSessionProjectIdentity(db, sessionId, projectPath) {
168128
- if (!sessionId || !projectPath)
168129
- return;
168130
- const harness = getHarness();
168131
- const now = Date.now();
168132
- db.transaction(() => {
168133
- getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
168134
- getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
168135
- })();
168136
- }
168137
- function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
168138
- if (!projectPath)
168139
- return 0;
168140
- return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
168141
- }
168142
- var upsertSessionProjectStatements, repairSessionChunkProjectStatements, repairProjectChunkProjectStatements;
168143
- var init_session_project_storage = __esm(() => {
168144
- upsertSessionProjectStatements = new WeakMap;
168145
- repairSessionChunkProjectStatements = new WeakMap;
168146
- repairProjectChunkProjectStatements = new WeakMap;
168147
- });
168148
-
168149
168440
  // src/features/magic-context/project-embedding-registry.ts
168150
168441
  import { createHash as createHash8, randomUUID } from "node:crypto";
168151
168442
  function markProjectLoadUntrusted(projectIdentity) {
@@ -169782,7 +170073,7 @@ function getMemoryVerifications(db, memoryIds) {
169782
170073
  var MEMORY_VERIFICATION_SENTINEL = "";
169783
170074
  // src/features/magic-context/memory/verification-paths.ts
169784
170075
  import { execFile } from "node:child_process";
169785
- import { existsSync as existsSync14, realpathSync as realpathSync2, statSync as statSync5 } from "node:fs";
170076
+ import { existsSync as existsSync17, realpathSync as realpathSync3, statSync as statSync5 } from "node:fs";
169786
170077
  import path6 from "node:path";
169787
170078
  import { promisify } from "node:util";
169788
170079
  async function runGit(cwd, args) {
@@ -169807,7 +170098,7 @@ function isWithin(root, candidate) {
169807
170098
  }
169808
170099
  function safeRealpath(value) {
169809
170100
  try {
169810
- return realpathSync2.native(value);
170101
+ return realpathSync3.native(value);
169811
170102
  } catch {
169812
170103
  return null;
169813
170104
  }
@@ -169885,7 +170176,7 @@ function verificationFileExists(baseRoot, filePath) {
169885
170176
  return false;
169886
170177
  const root = path6.resolve(baseRoot);
169887
170178
  const candidate = path6.resolve(root, filePath);
169888
- return isWithin(root, candidate) && existsSync14(candidate);
170179
+ return isWithin(root, candidate) && existsSync17(candidate);
169889
170180
  }
169890
170181
  async function normalizeVerificationFiles(args) {
169891
170182
  const cwd = path6.resolve(args.cwd);
@@ -169918,7 +170209,7 @@ async function normalizeVerificationFiles(args) {
169918
170209
  warnings.push(`Skipped verification path "${value}" because it is the repo/project root.`);
169919
170210
  continue;
169920
170211
  }
169921
- if (existsSync14(candidate)) {
170212
+ if (existsSync17(candidate)) {
169922
170213
  try {
169923
170214
  if (statSync5(candidate).isDirectory()) {
169924
170215
  warnings.push(`Skipped verification path "${value}" because it is a directory.`);
@@ -169949,7 +170240,7 @@ async function normalizeVerificationFiles(args) {
169949
170240
  }
169950
170241
  normalized.push(tracked);
169951
170242
  } else {
169952
- if (!existsSync14(candidate)) {
170243
+ if (!existsSync17(candidate)) {
169953
170244
  warnings.push(`Skipped verification path "${value}" because it does not exist.`);
169954
170245
  continue;
169955
170246
  }
@@ -177293,6 +177584,9 @@ function applyDeferredCompactionMarker(db, sessionId, pending, directory) {
177293
177584
  }
177294
177585
  }
177295
177586
  function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
177587
+ if (getHarness() !== "opencode") {
177588
+ return true;
177589
+ }
177296
177590
  const targetEndMessageId = getCompartmentEndMessageIdForOrdinal(db, sessionId, lastCompartmentEnd);
177297
177591
  if (!targetEndMessageId) {
177298
177592
  sessionLog(sessionId, `compaction-marker: no compartment endMessageId for ordinal ${lastCompartmentEnd}; preserving existing marker`);
@@ -178934,7 +179228,8 @@ function readCurrentM0SnapshotMarkers(args) {
178934
179228
  sessionFactsVersion: getSessionFactsVersion(args.db, args.sessionId),
178935
179229
  upgradeState: getUpgradeState(args.db, args.sessionId),
178936
179230
  systemHash: hard.systemHash,
178937
- modelKey: hard.modelKey
179231
+ modelKey: hard.modelKey,
179232
+ projectIdentity: args.projectPath ?? null
178938
179233
  };
178939
179234
  }
178940
179235
  function snapshotMarkersFromCachedM0(state) {
@@ -178967,7 +179262,8 @@ function snapshotMarkersFromCachedM0(state) {
178967
179262
  sessionFactsVersion: state.cachedM0SessionFactsVersion,
178968
179263
  upgradeState: state.cachedM0UpgradeState,
178969
179264
  systemHash: state.cachedM0SystemHash ?? "",
178970
- modelKey: state.cachedM0ModelKey ?? ""
179265
+ modelKey: state.cachedM0ModelKey ?? "",
179266
+ projectIdentity: state.cachedM0ProjectIdentity ?? null
178971
179267
  };
178972
179268
  }
178973
179269
  function mustMaterialize(args) {
@@ -178986,6 +179282,15 @@ function mustMaterialize(args) {
178986
179282
  if (hard.cacheExpired && hard.lastResponseTime > 0 && hard.lastResponseTime > (args.state.cachedM0MaterializedAt ?? 0)) {
178987
179283
  return { value: true, reason: "ttl_idle" };
178988
179284
  }
179285
+ if (current.projectIdentity !== null) {
179286
+ const cachedProjectIdentity = args.state.cachedM0ProjectIdentity ?? null;
179287
+ if (cachedProjectIdentity === null) {
179288
+ args.state.cachedM0ProjectIdentity = current.projectIdentity;
179289
+ args.db.prepare("UPDATE session_meta SET cached_m0_project_identity = ? WHERE session_id = ?").run(current.projectIdentity, args.sessionId);
179290
+ } else if (cachedProjectIdentity !== current.projectIdentity) {
179291
+ return { value: true, reason: "project_change" };
179292
+ }
179293
+ }
178989
179294
  if (current.workspaceFingerprint !== null || (args.state.cachedM0WorkspaceFingerprint ?? null) !== null) {
178990
179295
  if ((args.state.cachedM0WorkspaceFingerprint ?? null) !== current.workspaceFingerprint) {
178991
179296
  return { value: true, reason: "project_memory_epoch" };
@@ -179226,6 +179531,7 @@ function applyMarkersToState(state, m0Bytes, markers, m1Bytes) {
179226
179531
  state.cachedM0UpgradeState = markers.upgradeState;
179227
179532
  state.cachedM0SystemHash = markers.systemHash;
179228
179533
  state.cachedM0ModelKey = markers.modelKey;
179534
+ state.cachedM0ProjectIdentity = markers.projectIdentity;
179229
179535
  state.snapshotMarkers = markers;
179230
179536
  }
179231
179537
  function historySliceTokens(m0Text) {
@@ -179349,10 +179655,11 @@ function materializeM0(options) {
179349
179655
  sessionFactsVersion: getSessionFactsVersion(options.db, options.sessionId),
179350
179656
  upgradeState: getUpgradeState(options.db, options.sessionId),
179351
179657
  systemHash: snapshotMarkers.systemHash,
179352
- modelKey: snapshotMarkers.modelKey
179658
+ modelKey: snapshotMarkers.modelKey,
179659
+ projectIdentity: projectPath ?? null
179353
179660
  };
179354
179661
  const memoryEpochStale = current.workspaceFingerprint !== null || snapshotMarkers.workspaceFingerprint !== null ? current.workspaceFingerprint !== snapshotMarkers.workspaceFingerprint : current.projectMemoryEpoch !== snapshotMarkers.projectMemoryEpoch;
179355
- const stale = memoryEpochStale || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState;
179662
+ const stale = memoryEpochStale || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState || (current.projectIdentity ?? null) !== (snapshotMarkers.projectIdentity ?? null);
179356
179663
  if (stale) {
179357
179664
  options.db.exec("ROLLBACK");
179358
179665
  throw new MaterializeContentionError({ reason: "snapshot changed before Phase 3" });
@@ -179381,7 +179688,8 @@ function materializeM0(options) {
179381
179688
  sessionFactsVersion: snapshotMarkers.sessionFactsVersion,
179382
179689
  upgradeState: snapshotMarkers.upgradeState,
179383
179690
  systemHash: snapshotMarkers.systemHash,
179384
- modelKey: snapshotMarkers.modelKey
179691
+ modelKey: snapshotMarkers.modelKey,
179692
+ projectIdentity: snapshotMarkers.projectIdentity
179385
179693
  });
179386
179694
  options.db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), options.sessionId);
179387
179695
  const baselineEndMessageId = lastCompartmentBoundaryId(compartments);
@@ -179539,6 +179847,7 @@ function readCachedM0M1Row(db, sessionId) {
179539
179847
  cached_m0_upgrade_state,
179540
179848
  cached_m0_system_hash,
179541
179849
  cached_m0_model_key,
179850
+ cached_m0_project_identity,
179542
179851
  memory_block_ids
179543
179852
  FROM session_meta
179544
179853
  WHERE session_id = ?`).get(sessionId);
@@ -179573,11 +179882,12 @@ function markersFromCachedRow(row) {
179573
179882
  sessionFactsVersion: row.cached_m0_session_facts_version,
179574
179883
  upgradeState: row.cached_m0_upgrade_state,
179575
179884
  systemHash: row.cached_m0_system_hash ?? "",
179576
- modelKey: row.cached_m0_model_key ?? ""
179885
+ modelKey: row.cached_m0_model_key ?? "",
179886
+ projectIdentity: row.cached_m0_project_identity ?? null
179577
179887
  };
179578
179888
  }
179579
179889
  function cachedRowMatchesState(row, state) {
179580
- return bufferEqualsNullable(row.cached_m0_bytes, state.cachedM0Bytes) && row.cached_m0_project_memory_epoch === state.cachedM0ProjectMemoryEpoch && (row.cached_m0_workspace_fingerprint ?? null) === (state.cachedM0WorkspaceFingerprint ?? null) && row.cached_m0_project_user_profile_version === state.cachedM0ProjectUserProfileVersion && row.cached_m0_max_compartment_seq === state.cachedM0MaxCompartmentSeq && row.cached_m0_max_memory_id === state.cachedM0MaxMemoryId && row.cached_m0_max_mutation_id === state.cachedM0MaxMutationId && row.cached_m0_max_memory_mutation_id === state.cachedM0MaxMemoryMutationId && row.cached_m0_materialized_at === state.cachedM0MaterializedAt && row.cached_m0_session_facts_version === state.cachedM0SessionFactsVersion && (row.cached_m0_upgrade_state ?? null) === (state.cachedM0UpgradeState ?? null) && (row.cached_m0_system_hash ?? "") === (state.cachedM0SystemHash ?? "") && (row.cached_m0_model_key ?? "") === (state.cachedM0ModelKey ?? "");
179890
+ return bufferEqualsNullable(row.cached_m0_bytes, state.cachedM0Bytes) && row.cached_m0_project_memory_epoch === state.cachedM0ProjectMemoryEpoch && (row.cached_m0_workspace_fingerprint ?? null) === (state.cachedM0WorkspaceFingerprint ?? null) && row.cached_m0_project_user_profile_version === state.cachedM0ProjectUserProfileVersion && row.cached_m0_max_compartment_seq === state.cachedM0MaxCompartmentSeq && row.cached_m0_max_memory_id === state.cachedM0MaxMemoryId && row.cached_m0_max_mutation_id === state.cachedM0MaxMutationId && row.cached_m0_max_memory_mutation_id === state.cachedM0MaxMemoryMutationId && row.cached_m0_materialized_at === state.cachedM0MaterializedAt && row.cached_m0_session_facts_version === state.cachedM0SessionFactsVersion && (row.cached_m0_upgrade_state ?? null) === (state.cachedM0UpgradeState ?? null) && (row.cached_m0_system_hash ?? "") === (state.cachedM0SystemHash ?? "") && (row.cached_m0_model_key ?? "") === (state.cachedM0ModelKey ?? "") && (row.cached_m0_project_identity ?? null) === (state.cachedM0ProjectIdentity ?? null);
179581
179891
  }
179582
179892
  function applyCachedRowToState(state, row) {
179583
179893
  const markers = markersFromCachedRow(row);
@@ -179599,6 +179909,7 @@ function applyCachedRowToState(state, row) {
179599
179909
  state.cachedM0UpgradeState = markers.upgradeState;
179600
179910
  state.cachedM0SystemHash = markers.systemHash;
179601
179911
  state.cachedM0ModelKey = markers.modelKey;
179912
+ state.cachedM0ProjectIdentity = markers.projectIdentity;
179602
179913
  state.snapshotMarkers = markers;
179603
179914
  }
179604
179915
  function replayCachedM1(state) {
@@ -180593,6 +180904,43 @@ function semanticSnapBoundary(args) {
180593
180904
  }
180594
180905
  return snapped;
180595
180906
  }
180907
+ function snapWrapupBoundaryToUser(args) {
180908
+ const { messages, index, candidate, offset, triggerBudget } = args;
180909
+ if (candidate <= offset)
180910
+ return candidate;
180911
+ const snapTokenLimit = Math.min(Math.max(triggerBudget, 2000), 48000);
180912
+ for (let ordinal = candidate;ordinal >= offset; ordinal -= 1) {
180913
+ const message = messages.find((m) => m.ordinal === ordinal);
180914
+ if (!message)
180915
+ continue;
180916
+ if (message.role !== "user" || !hasMeaningfulUserText(message.parts))
180917
+ continue;
180918
+ const extraTokens = index.rangeTokens(ordinal, candidate);
180919
+ if (extraTokens <= snapTokenLimit)
180920
+ return ordinal;
180921
+ return candidate;
180922
+ }
180923
+ return candidate;
180924
+ }
180925
+ function fenceWrapupBoundaryForToolArcs(args) {
180926
+ let boundary = args.candidate;
180927
+ const maxPasses = args.arcs.length + 1;
180928
+ for (let pass = 0;pass < maxPasses; pass += 1) {
180929
+ let next = boundary;
180930
+ for (const arc of args.arcs) {
180931
+ if (arc.resOrdinal === null) {
180932
+ continue;
180933
+ }
180934
+ if (arc.invOrdinal >= args.lastCompartmentEndOrdinal + 1 && arc.invOrdinal < next && next <= arc.resOrdinal) {
180935
+ next = arc.invOrdinal;
180936
+ }
180937
+ }
180938
+ if (next === boundary)
180939
+ return boundary;
180940
+ boundary = next;
180941
+ }
180942
+ return boundary;
180943
+ }
180596
180944
  function applyHeadCap(args) {
180597
180945
  const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
180598
180946
  if (offset >= protectedTailStart)
@@ -180848,6 +181196,124 @@ function resolveBoundaryContext(args) {
180848
181196
  function resolveOpenCodeProtectedTailBoundary(args) {
180849
181197
  return resolveProtectedTailBoundary(resolveBoundaryContext(args));
180850
181198
  }
181199
+ function resolveWrapupProtectedTailBoundary(args) {
181200
+ const ctx = resolveBoundaryContext({ ...args, mode: "manual-wrapup" });
181201
+ const createdAt = ctx.createdAt ?? Date.now();
181202
+ const messages = readRawSessionMessages(ctx.sessionId);
181203
+ const absoluteMessageCount = getCachedAbsoluteMessageCount(ctx.sessionId) ?? undefined;
181204
+ const index = buildTrueRawTokenIndex(ctx.sessionId, messages, {
181205
+ providerShapeVersion: ctx.providerShapeVersion,
181206
+ cacheNamespace: ctx.cacheNamespace,
181207
+ absoluteMessageCount,
181208
+ storedTotalForMessage: ctx.storedTokenTotals ? (m) => {
181209
+ const value = ctx.storedTokenTotals?.get(m.id);
181210
+ return value === undefined ? null : value;
181211
+ } : undefined
181212
+ });
181213
+ const rawMessageCount = index.rawMessageCount;
181214
+ const offset = Math.max(1, ctx.lastCompartmentEndOrdinal + 1);
181215
+ const anchorRawMessageCount = Math.max(0, Math.min(rawMessageCount, Math.floor(args.anchorRawMessageCount ?? rawMessageCount)));
181216
+ const usagePercentage = clampPercentage(ctx.usage?.percentage ?? 0);
181217
+ const usageInputTokens = Math.max(0, Math.round(ctx.usage?.inputTokens ?? 0));
181218
+ const rawMessagesAboveLastCompartment = Math.max(0, anchorRawMessageCount - offset + 1);
181219
+ const keep = Math.max(1, Math.floor(args.messagesToKeep));
181220
+ let targetProtectedTailStart = offset;
181221
+ let boundaryReason = "manual-wrapup-empty";
181222
+ if (rawMessageCount === 0 || rawMessagesAboveLastCompartment <= keep) {
181223
+ targetProtectedTailStart = offset;
181224
+ boundaryReason = rawMessageCount === 0 ? "manual-wrapup-empty" : "manual-wrapup-within-keep";
181225
+ } else {
181226
+ targetProtectedTailStart = anchorRawMessageCount - keep + 1;
181227
+ boundaryReason = "manual-wrapup-keep-watermark";
181228
+ const arcs = buildToolArcs(messages);
181229
+ const fenced = fenceWrapupBoundaryForToolArcs({
181230
+ candidate: targetProtectedTailStart,
181231
+ arcs,
181232
+ lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal
181233
+ });
181234
+ if (fenced !== targetProtectedTailStart)
181235
+ boundaryReason = "manual-wrapup-tool-arc";
181236
+ targetProtectedTailStart = fenced;
181237
+ const snapped = snapWrapupBoundaryToUser({
181238
+ messages,
181239
+ index,
181240
+ candidate: targetProtectedTailStart,
181241
+ offset,
181242
+ triggerBudget: ctx.triggerBudget
181243
+ });
181244
+ if (snapped !== targetProtectedTailStart)
181245
+ boundaryReason = "manual-wrapup-user-snap";
181246
+ targetProtectedTailStart = snapped;
181247
+ const refenced = fenceWrapupBoundaryForToolArcs({
181248
+ candidate: targetProtectedTailStart,
181249
+ arcs,
181250
+ lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal
181251
+ });
181252
+ if (refenced !== targetProtectedTailStart)
181253
+ boundaryReason = "manual-wrapup-tool-arc";
181254
+ targetProtectedTailStart = refenced;
181255
+ }
181256
+ targetProtectedTailStart = clampOrdinal(targetProtectedTailStart, rawMessageCount);
181257
+ const target = deriveProtectedTailTokenTarget({
181258
+ contextLimit: ctx.contextLimit,
181259
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
181260
+ usagePercentage,
181261
+ triggerBudget: ctx.triggerBudget
181262
+ });
181263
+ const perRunCap = selectPerRunCap({
181264
+ usagePercentage,
181265
+ N: target.N,
181266
+ contextLimit: ctx.contextLimit,
181267
+ executeThresholdPercentage: ctx.executeThresholdPercentage
181268
+ });
181269
+ const head = applyHeadCap({
181270
+ index,
181271
+ protectedTailStart: targetProtectedTailStart,
181272
+ offset,
181273
+ arcs: buildToolArcs(messages),
181274
+ lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
181275
+ capTokens: perRunCap,
181276
+ recentOpenArcCutoff: targetProtectedTailStart
181277
+ });
181278
+ const eligibleEndOrdinal = Math.min(head.eligibleEndOrdinal, targetProtectedTailStart);
181279
+ const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, eligibleEndOrdinal);
181280
+ const snapshot = {
181281
+ sessionId: ctx.sessionId,
181282
+ mode: "manual-wrapup",
181283
+ offset,
181284
+ offsetMessageId: boundaryMessageId(index, offset),
181285
+ protectedTailStart: targetProtectedTailStart,
181286
+ protectedTailStartMessageId: boundaryMessageId(index, targetProtectedTailStart),
181287
+ eligibleEndOrdinal,
181288
+ eligibleEndMessageId: boundaryMessageId(index, eligibleEndOrdinal - 1),
181289
+ rawMessageCountAtTrigger: rawMessageCount,
181290
+ rawLastMessageIdAtTrigger: boundaryMessageId(index, rawMessageCount),
181291
+ N: keep,
181292
+ usagePercentage,
181293
+ usageInputTokens,
181294
+ usageSource: ctx.usageSource,
181295
+ contextLimit: ctx.contextLimit,
181296
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
181297
+ triggerBudget: ctx.triggerBudget,
181298
+ priorBoundaryOrdinal: ctx.priorBoundaryOrdinal,
181299
+ migrationFloorActive: ctx.migrationFloorActive,
181300
+ emergencyTailScale: ctx.emergencyTailScale,
181301
+ providerShapeVersion: ctx.providerShapeVersion,
181302
+ cacheNamespace: ctx.cacheNamespace,
181303
+ createdAt,
181304
+ rawRangeFingerprint,
181305
+ trueRawEligibleTokens: index.rangeTokens(offset, targetProtectedTailStart),
181306
+ oversizeAtomicUnit: head.oversizeAtomicUnit,
181307
+ boundaryReason
181308
+ };
181309
+ return {
181310
+ snapshot,
181311
+ rawMessagesAboveLastCompartment,
181312
+ anchorRawMessageCount,
181313
+ targetProtectedTailStart,
181314
+ targetEligibleEndOrdinal: targetProtectedTailStart
181315
+ };
181316
+ }
180851
181317
  function getRawHistoryEligibility(db, sessionId) {
180852
181318
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
180853
181319
  const offset = Math.max(1, lastCompartmentEnd + 1);
@@ -183075,7 +183541,7 @@ async function runCompartmentAgent(deps) {
183075
183541
  currentContextLimit: deps.currentContextLimit ?? boundarySnapshot.contextLimit
183076
183542
  }) : { ok: true };
183077
183543
  if (!validation.ok && validation.reason === "stale_snapshot") {
183078
- const refreshed = resolveOpenCodeProtectedTailBoundary({
183544
+ const refreshed = deps.refreshBoundarySnapshot ? deps.refreshBoundarySnapshot(boundarySnapshot, validation) : resolveOpenCodeProtectedTailBoundary({
183079
183545
  db,
183080
183546
  sessionId,
183081
183547
  mode: "incremental-runner",
@@ -183088,7 +183554,7 @@ async function runCompartmentAgent(deps) {
183088
183554
  usageSource: boundarySnapshot.usageSource,
183089
183555
  emergencyTailScale: boundarySnapshot.emergencyTailScale
183090
183556
  });
183091
- if (hasRunnableCompartmentWindow(refreshed)) {
183557
+ if (refreshed && hasRunnableCompartmentWindow(refreshed)) {
183092
183558
  sessionLog(sessionId, `historian: refreshed stale protected-tail snapshot at run time (was: ${validation.detail ?? "stale"}) — eligible head ${refreshed.offset}-${refreshed.eligibleEndOrdinal - 1}`);
183093
183559
  boundarySnapshot = refreshed;
183094
183560
  validation = { ok: true };
@@ -183106,7 +183572,8 @@ async function runCompartmentAgent(deps) {
183106
183572
  if (protectedTailStart <= offset || eligibleEndOrdinal <= offset) {
183107
183573
  sessionLog(sessionId, `historian no-op: protectedTailStart=${protectedTailStart} eligibleEnd=${eligibleEndOrdinal} <= offset=${offset} — nothing to compact`);
183108
183574
  if (boundarySnapshot.usagePercentage < 80 && !boundarySnapshot.emergencyTailScale) {
183109
- clearEmergencyRecovery(db, sessionId);
183575
+ if (!isWrapupInProgress(db, sessionId))
183576
+ clearEmergencyRecovery(db, sessionId);
183110
183577
  } else {
183111
183578
  const count = recordHighPressureNoEligibleHead(db, boundarySnapshot);
183112
183579
  sessionLog(sessionId, `historian high-pressure no-op: recovery remains armed (noEligibleHeadCount=${count})`);
@@ -183119,7 +183586,7 @@ async function runCompartmentAgent(deps) {
183119
183586
  }
183120
183587
  const perRunCap = selectPerRunCap(boundarySnapshot);
183121
183588
  const usable = Math.max(1, Math.round(boundarySnapshot.contextLimit * boundarySnapshot.executeThresholdPercentage / 100));
183122
- const reserve = reserveProtectedTailDrainTokens({
183589
+ const reserve = deps.forceDrainQuota ? { ok: true, reservation: null } : reserveProtectedTailDrainTokens({
183123
183590
  db,
183124
183591
  sessionId,
183125
183592
  runId: crypto.randomUUID(),
@@ -183137,12 +183604,14 @@ async function runCompartmentAgent(deps) {
183137
183604
  }
183138
183605
  drainReservation = reserve.reservation;
183139
183606
  const chunk = readSessionChunk(sessionId, historianChunkTokens, offset, eligibleEndOrdinal);
183607
+ const forceKeepLastCompartmentForChunk = deps.forceKeepLastCompartment === true && !chunk.hasMore;
183140
183608
  telemetry.chunkStartOrdinal = chunk.startIndex;
183141
183609
  telemetry.chunkEndOrdinal = chunk.endIndex;
183142
183610
  if (!chunk.text || chunk.messageCount === 0) {
183143
183611
  sessionLog(sessionId, `historian no-op: chunk empty after filtering (messageCount=${chunk.messageCount}, textLen=${chunk.text?.length ?? 0}) range=${offset}-${eligibleEndOrdinal - 1}`);
183144
183612
  if (boundarySnapshot.usagePercentage < 80 && !boundarySnapshot.emergencyTailScale) {
183145
- clearEmergencyRecovery(db, sessionId);
183613
+ if (!isWrapupInProgress(db, sessionId))
183614
+ clearEmergencyRecovery(db, sessionId);
183146
183615
  } else {
183147
183616
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
183148
183617
  }
@@ -183216,7 +183685,7 @@ ${chunkText}`,
183216
183685
  const BOUNDARY_HEALING_SLACK = 2;
183217
183686
  const inEmergency = getOverflowState(db, sessionId).needsEmergencyRecovery;
183218
183687
  let persistedCompartments = emittedCompartments;
183219
- if (!inEmergency && emittedCompartments.length >= 2) {
183688
+ if (!inEmergency && !forceKeepLastCompartmentForChunk && emittedCompartments.length >= 2) {
183220
183689
  const lastEmitted = emittedCompartments[emittedCompartments.length - 1];
183221
183690
  const lookaheadMargin = chunk.endIndex - lastEmitted.endMessage;
183222
183691
  if (lookaheadMargin <= BOUNDARY_HEALING_SLACK) {
@@ -183240,10 +183709,21 @@ ${chunkText}`,
183240
183709
  const lastNewEndMessageId = newCompartments[newCompartments.length - 1]?.endMessageId;
183241
183710
  const promotionDirectory = sessionDirectory || deps.directory;
183242
183711
  const discardedLast = persistedCompartments.length < emittedCompartments.length;
183712
+ const weakLookaheadFinalCompartment = forceKeepLastCompartmentForChunk;
183713
+ const skipUnanchoredPromotion = discardedLast || weakLookaheadFinalCompartment;
183243
183714
  const embeddingActive = !!promotionDirectory && deps.memoryEnabled !== false;
183244
183715
  const promotionActive = embeddingActive && deps.autoPromote !== false;
183245
183716
  const promotionProjectIdentity = promotionDirectory ? resolveProjectIdentity(promotionDirectory) : "";
183246
- const publishableEvents = (validatedPass.events ?? []).filter((e) => e.atCompartment == null || e.atCompartment <= persistedCompartments.length);
183717
+ const publishableEvents = (validatedPass.events ?? []).filter((e) => {
183718
+ if (typeof e.atCompartment !== "number")
183719
+ return !weakLookaheadFinalCompartment;
183720
+ if (e.atCompartment > persistedCompartments.length)
183721
+ return false;
183722
+ if (weakLookaheadFinalCompartment && e.atCompartment >= emittedCompartments.length) {
183723
+ return false;
183724
+ }
183725
+ return true;
183726
+ });
183247
183727
  let promotedFactRefs = [];
183248
183728
  let persistedIds = [];
183249
183729
  const holderId = deps.compartmentLeaseHolderId;
@@ -183263,7 +183743,7 @@ ${chunkText}`,
183263
183743
  }
183264
183744
  appendCompartments(db, sessionId, persistedCompartments);
183265
183745
  persistedIds = getCompartments(db, sessionId).slice(-persistedCompartments.length).map((c) => c.id);
183266
- if (promotionActive && !discardedLast) {
183746
+ if (promotionActive && !skipUnanchoredPromotion) {
183267
183747
  promotedFactRefs = promoteSessionFactsDurable(db, sessionId, promotionProjectIdentity, validatedPass.facts ?? []);
183268
183748
  }
183269
183749
  if (publishableEvents.length > 0) {
@@ -183278,7 +183758,8 @@ ${chunkText}`,
183278
183758
  clearHistorianFailureState(db, sessionId);
183279
183759
  clearHistorianDrainFailure(db, sessionId);
183280
183760
  recordProtectedTailPublicationFloor(db, sessionId, lastCompartmentEnd + 1);
183281
- clearEmergencyRecovery(db, sessionId);
183761
+ if (!isWrapupInProgress(db, sessionId))
183762
+ clearEmergencyRecovery(db, sessionId);
183282
183763
  drainReservation = null;
183283
183764
  if (deferMarkerApplication && lastNewEndMessageId) {
183284
183765
  setPendingCompactionMarkerState(db, sessionId, {
@@ -183350,7 +183831,7 @@ ${chunkText}`,
183350
183831
  }
183351
183832
  })();
183352
183833
  }
183353
- if (deps.experimentalUserMemories === true && !discardedLast && validatedPass.userObservations && validatedPass.userObservations.length > 0) {
183834
+ if (deps.experimentalUserMemories === true && !skipUnanchoredPromotion && validatedPass.userObservations && validatedPass.userObservations.length > 0) {
183354
183835
  try {
183355
183836
  const lastNew = newCompartments[newCompartments.length - 1];
183356
183837
  insertUserMemoryCandidates(db, validatedPass.userObservations.map((obs) => ({
@@ -183364,7 +183845,7 @@ ${chunkText}`,
183364
183845
  sessionLog(sessionId, "failed to store user memory candidates:", error51);
183365
183846
  }
183366
183847
  }
183367
- if (!discardedLast && promotionProjectIdentity && validatedPass.primerCandidates && validatedPass.primerCandidates.length > 0) {
183848
+ if (!skipUnanchoredPromotion && promotionProjectIdentity && validatedPass.primerCandidates && validatedPass.primerCandidates.length > 0) {
183368
183849
  try {
183369
183850
  const firstNew = newCompartments[0];
183370
183851
  const lastNew = newCompartments[newCompartments.length - 1];
@@ -184231,6 +184712,21 @@ function markActiveCompartmentRunPublished(sessionId) {
184231
184712
  if (activeRun)
184232
184713
  activeRun.published = true;
184233
184714
  }
184715
+ function registerActiveCompartmentRun(sessionId, promise2, kind = "other") {
184716
+ const activeRun = {
184717
+ promise: Promise.resolve(),
184718
+ published: false,
184719
+ kind
184720
+ };
184721
+ const wrapped = promise2.finally(() => {
184722
+ if (activeRuns.get(sessionId)?.promise === wrapped) {
184723
+ activeRuns.delete(sessionId);
184724
+ }
184725
+ });
184726
+ activeRun.promise = wrapped;
184727
+ activeRuns.set(sessionId, activeRun);
184728
+ return activeRun;
184729
+ }
184234
184730
  function withPublishedCallback(deps) {
184235
184731
  return {
184236
184732
  ...deps,
@@ -184242,8 +184738,12 @@ function withPublishedCallback(deps) {
184242
184738
  }
184243
184739
  function startLeaseRenewal(deps, holderId) {
184244
184740
  return setInterval(() => {
184245
- if (!renewCompartmentLease(deps.db, deps.sessionId, holderId)) {
184246
- sessionLog(deps.sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
184741
+ try {
184742
+ if (!renewCompartmentLease(deps.db, deps.sessionId, holderId)) {
184743
+ sessionLog(deps.sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
184744
+ }
184745
+ } catch (err) {
184746
+ sessionLog(deps.sessionId, `compartment lease renewal threw; publish will be skipped if holder is stale (${err instanceof Error ? err.message : String(err)})`);
184247
184747
  }
184248
184748
  }, COMPARTMENT_LEASE_RENEWAL_MS);
184249
184749
  }
@@ -184252,6 +184752,11 @@ function startCompartmentAgent(deps) {
184252
184752
  if (existing) {
184253
184753
  return;
184254
184754
  }
184755
+ if (isWrapupInProgress(deps.db, deps.sessionId)) {
184756
+ sessionLog(deps.sessionId, "compartment agent skipped: /ctx-wrapup is active");
184757
+ updateSessionMeta(deps.db, deps.sessionId, { compartmentInProgress: false });
184758
+ return;
184759
+ }
184255
184760
  const holderId = crypto.randomUUID();
184256
184761
  const lease = acquireCompartmentLease(deps.db, deps.sessionId, holderId);
184257
184762
  if (!lease) {
@@ -184259,6 +184764,12 @@ function startCompartmentAgent(deps) {
184259
184764
  updateSessionMeta(deps.db, deps.sessionId, { compartmentInProgress: false });
184260
184765
  return;
184261
184766
  }
184767
+ if (isWrapupInProgress(deps.db, deps.sessionId)) {
184768
+ sessionLog(deps.sessionId, "compartment agent skipped: /ctx-wrapup became active");
184769
+ releaseCompartmentLease(deps.db, deps.sessionId, holderId);
184770
+ updateSessionMeta(deps.db, deps.sessionId, { compartmentInProgress: false });
184771
+ return;
184772
+ }
184262
184773
  const renewal = startLeaseRenewal(deps, holderId);
184263
184774
  let realRunStarted = false;
184264
184775
  const runnerDeps = withPublishedCallback({
@@ -184280,13 +184791,19 @@ function startCompartmentAgent(deps) {
184280
184791
  activeRuns.delete(deps.sessionId);
184281
184792
  }
184282
184793
  });
184283
- activeRuns.set(deps.sessionId, { promise: promise2, published: false });
184794
+ activeRuns.set(deps.sessionId, { promise: promise2, published: false, kind: "incremental" });
184284
184795
  if (!realRunStarted && activeRuns.get(deps.sessionId)?.promise === promise2) {
184285
184796
  activeRuns.delete(deps.sessionId);
184286
184797
  }
184287
184798
  }
184288
184799
  async function executeContextRecompWithResult(deps, options = {}) {
184289
184800
  const { sessionId } = deps;
184801
+ if (isWrapupInProgress(deps.db, sessionId)) {
184802
+ return {
184803
+ message: "## Magic Recomp — Skipped\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
184804
+ published: false
184805
+ };
184806
+ }
184290
184807
  if (activeRuns.has(sessionId)) {
184291
184808
  return {
184292
184809
  message: "## Magic Recomp — Skipped\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.",
@@ -184310,7 +184827,7 @@ async function executeContextRecompWithResult(deps, options = {}) {
184310
184827
  }).catch((err) => {
184311
184828
  sessionLog(sessionId, "compartment agent: recomp unhandled rejection:", err);
184312
184829
  });
184313
- activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
184830
+ activeRuns.set(sessionId, { promise: wrappedPromise, published: false, kind: "recomp" });
184314
184831
  try {
184315
184832
  const message = await promise2;
184316
184833
  const published = activeRuns.get(sessionId)?.published === true;
@@ -184738,6 +185255,13 @@ Recomp crashed: ${String(error51)}`;
184738
185255
  }
184739
185256
  }
184740
185257
  async function runManagedUpgrade(ctx, sessionId) {
185258
+ if (isWrapupInProgress(ctx.db, sessionId)) {
185259
+ const message = "/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-session-upgrade` again.";
185260
+ setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message);
185261
+ return `## Session Upgrade — Skipped
185262
+
185263
+ ${message}`;
185264
+ }
184741
185265
  setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
184742
185266
  try {
184743
185267
  const compartments = getCompartments(ctx.db, sessionId);
@@ -184892,13 +185416,14 @@ function shouldShowAnnouncement() {
184892
185416
  }
184893
185417
  return ordering > 0;
184894
185418
  }
184895
- var ANNOUNCEMENT_VERSION = "0.30.4", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
185419
+ var ANNOUNCEMENT_VERSION = "0.31.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
184896
185420
  var init_announcement = __esm(() => {
184897
185421
  init_data_path();
184898
185422
  ANNOUNCEMENT_FEATURES = [
184899
- "Pi: fixed historian and dreamer failing when your model comes from a provider extension (Antigravity, Anthropic OAuth, etc.). Subagents now load your extensions.",
184900
- "Pi: fixed the retrospective and refresh-primers dreamer tasks reading sessions through the wrong API.",
184901
- "OpenCode: the plugin no longer re-adds its TUI sidebar entry on startup, so removing it from tui.jsonc now sticks."
185423
+ "New /ctx-wrapup command: compact older history on demand, keeping the newest N messages raw. Run it before switching to a smaller-context model.",
185424
+ "ctx_search now also searches your session notes and smart notes.",
185425
+ "Project identity now survives transient git failures (slow disks, dubious ownership) without splitting your project memory.",
185426
+ "Removed the ctx_reduce_enabled setting; agent-controlled reduction is always on. Caveman text compression is now independent (caveman_text_compression.enabled)."
184902
185427
  ];
184903
185428
  });
184904
185429
 
@@ -186326,6 +186851,10 @@ function getMagicContextBuiltinCommands() {
186326
186851
  template: "ctx-recomp",
186327
186852
  description: "Rebuild compartments and facts from raw history (full or <start>-<end> range)"
186328
186853
  },
186854
+ "ctx-wrapup": {
186855
+ template: "ctx-wrapup",
186856
+ description: "Compact older live history while keeping the newest messages raw"
186857
+ },
186329
186858
  "ctx-session-upgrade": {
186330
186859
  template: "ctx-session-upgrade",
186331
186860
  description: "Upgrade this session to the latest history format: rebuild compartments and migrate project memories"
@@ -186349,6 +186878,29 @@ function getMagicContextBuiltinCommands() {
186349
186878
  };
186350
186879
  }
186351
186880
 
186881
+ // src/features/magic-context/dreamer/open-opencode-db.ts
186882
+ init_logger();
186883
+ await __promiseAll([
186884
+ init_sqlite(),
186885
+ init_compaction_marker()
186886
+ ]);
186887
+ import { existsSync as existsSync7 } from "node:fs";
186888
+ function openOpenCodeDb() {
186889
+ const dbPath = getOpenCodeDbPath();
186890
+ if (!existsSync7(dbPath)) {
186891
+ log(`[dreamer] OpenCode DB not found at ${dbPath} — skipping OpenCode history scan`);
186892
+ return null;
186893
+ }
186894
+ try {
186895
+ const db = new Database(dbPath, { readonly: true });
186896
+ db.exec("PRAGMA busy_timeout = 5000");
186897
+ return db;
186898
+ } catch (error51) {
186899
+ log(`[dreamer] failed to open OpenCode DB at ${dbPath}: ${getErrorMessage(error51)}`);
186900
+ return null;
186901
+ }
186902
+ }
186903
+
186352
186904
  // src/features/magic-context/dreamer/task-prompts.ts
186353
186905
  var DREAMER_SYSTEM_PROMPT = `You are a background maintenance agent for the magic-context system, running during a scheduled dream window. Your task and its full instructions arrive in the message below. Never read or quote secrets from .env, credentials, or key files, and never commit — the user handles git.`;
186354
186906
  var PROJECT_MEMORY_TAXONOMY = `## Memory taxonomy (5 categories)
@@ -186658,6 +187210,192 @@ function buildDreamTaskPrompt(task, args) {
186658
187210
  // src/index.ts
186659
187211
  init_project_identity();
186660
187212
 
187213
+ // src/features/magic-context/session-project-backfill.ts
187214
+ init_logger();
187215
+ init_project_identity();
187216
+ init_session_project_storage();
187217
+ import { existsSync as existsSync9 } from "node:fs";
187218
+ var LEASE_TTL_MS = 10 * 60 * 1000;
187219
+ var SESSION_QUERY_CHUNK_SIZE = 250;
187220
+ var YIELD_EVERY_NEW_DIRECTORIES = 10;
187221
+ function ensureBackfillStateTable(db) {
187222
+ db.exec(`
187223
+ CREATE TABLE IF NOT EXISTS session_project_backfill_state (
187224
+ harness TEXT PRIMARY KEY,
187225
+ status TEXT NOT NULL CHECK (status IN ('running', 'completed')),
187226
+ started_at INTEGER,
187227
+ lease_expires_at INTEGER,
187228
+ completed_at INTEGER
187229
+ );
187230
+ `);
187231
+ }
187232
+ function withImmediateTransaction(db, fn) {
187233
+ db.exec("BEGIN IMMEDIATE");
187234
+ try {
187235
+ const result = fn();
187236
+ db.exec("COMMIT");
187237
+ return result;
187238
+ } catch (error51) {
187239
+ try {
187240
+ db.exec("ROLLBACK");
187241
+ } catch {}
187242
+ throw error51;
187243
+ }
187244
+ }
187245
+ function defaultYieldFn() {
187246
+ return new Promise((resolve2) => setTimeout(resolve2, 0));
187247
+ }
187248
+ function acquireBackfillLease(db, harness, now) {
187249
+ ensureBackfillStateTable(db);
187250
+ return withImmediateTransaction(db, () => {
187251
+ const current = db.prepare(`SELECT harness, status, started_at, lease_expires_at, completed_at
187252
+ FROM session_project_backfill_state
187253
+ WHERE harness = ?`).get(harness);
187254
+ if (current?.status === "completed") {
187255
+ return "already_completed";
187256
+ }
187257
+ if (current?.status === "running" && current.lease_expires_at !== null && current.lease_expires_at > now) {
187258
+ return "blocked_by_lease";
187259
+ }
187260
+ db.prepare(`INSERT INTO session_project_backfill_state(
187261
+ harness,
187262
+ status,
187263
+ started_at,
187264
+ lease_expires_at,
187265
+ completed_at
187266
+ )
187267
+ VALUES (?, 'running', ?, ?, NULL)
187268
+ ON CONFLICT(harness) DO UPDATE SET
187269
+ status = 'running',
187270
+ started_at = excluded.started_at,
187271
+ lease_expires_at = excluded.lease_expires_at,
187272
+ completed_at = NULL`).run(harness, now, now + LEASE_TTL_MS);
187273
+ return "completed";
187274
+ });
187275
+ }
187276
+ function renewBackfillLease(db, harness, now) {
187277
+ ensureBackfillStateTable(db);
187278
+ db.prepare(`UPDATE session_project_backfill_state
187279
+ SET lease_expires_at = ?
187280
+ WHERE harness = ? AND status = 'running'`).run(now + LEASE_TTL_MS, harness);
187281
+ }
187282
+ function markBackfillCompleted(db, harness, now) {
187283
+ ensureBackfillStateTable(db);
187284
+ db.prepare(`INSERT INTO session_project_backfill_state(
187285
+ harness,
187286
+ status,
187287
+ started_at,
187288
+ lease_expires_at,
187289
+ completed_at
187290
+ )
187291
+ VALUES (?, 'completed', ?, NULL, ?)
187292
+ ON CONFLICT(harness) DO UPDATE SET
187293
+ status = 'completed',
187294
+ lease_expires_at = NULL,
187295
+ completed_at = excluded.completed_at`).run(harness, now, now);
187296
+ }
187297
+ function dedupeSessions(sessions) {
187298
+ const sessionsById = new Map;
187299
+ for (const session of sessions) {
187300
+ if (!session.sessionId)
187301
+ continue;
187302
+ const existing = sessionsById.get(session.sessionId);
187303
+ if (!existing) {
187304
+ sessionsById.set(session.sessionId, {
187305
+ sessionId: session.sessionId,
187306
+ directory: session.directory
187307
+ });
187308
+ continue;
187309
+ }
187310
+ if (!existing.directory && session.directory) {
187311
+ sessionsById.set(session.sessionId, {
187312
+ sessionId: session.sessionId,
187313
+ directory: session.directory
187314
+ });
187315
+ }
187316
+ }
187317
+ return [...sessionsById.values()];
187318
+ }
187319
+ function getMappedSessionIds(db, harness, sessions) {
187320
+ const mapped = new Set;
187321
+ for (let index = 0;index < sessions.length; index += SESSION_QUERY_CHUNK_SIZE) {
187322
+ const chunk = sessions.slice(index, index + SESSION_QUERY_CHUNK_SIZE);
187323
+ if (chunk.length === 0)
187324
+ continue;
187325
+ const placeholders = chunk.map(() => "?").join(", ");
187326
+ const rows = db.prepare(`SELECT session_id
187327
+ FROM session_projects
187328
+ WHERE harness = ?
187329
+ AND session_id IN (${placeholders})`).all(harness, ...chunk.map((session) => session.sessionId));
187330
+ for (const row of rows) {
187331
+ mapped.add(row.session_id);
187332
+ }
187333
+ }
187334
+ return mapped;
187335
+ }
187336
+ async function runSessionProjectBackfill(db, sessions, options = {}) {
187337
+ const startedAt = performance.now();
187338
+ const harness = getHarness();
187339
+ const now = options.now ?? Date.now;
187340
+ const resolveIdentity = options.resolveIdentity ?? resolveProjectIdentity;
187341
+ const yieldFn = options.yieldFn ?? defaultYieldFn;
187342
+ const dedupedSessions = dedupeSessions(sessions);
187343
+ const result = {
187344
+ status: "completed",
187345
+ totalSessions: dedupedSessions.length,
187346
+ alreadyMappedSessions: 0,
187347
+ unmappedSessions: 0,
187348
+ backfilledSessions: 0,
187349
+ skippedDeadDirectories: 0,
187350
+ skippedEmptyDirectories: 0,
187351
+ durationMs: 0
187352
+ };
187353
+ const leaseStatus = acquireBackfillLease(db, harness, now());
187354
+ if (leaseStatus !== "completed") {
187355
+ result.status = leaseStatus;
187356
+ result.durationMs = performance.now() - startedAt;
187357
+ return result;
187358
+ }
187359
+ const mappedSessionIds = getMappedSessionIds(db, harness, dedupedSessions);
187360
+ result.alreadyMappedSessions = mappedSessionIds.size;
187361
+ const existenceCache = new Map;
187362
+ const identityCache2 = new Map;
187363
+ let newDirectoryResolutions = 0;
187364
+ for (const session of dedupedSessions) {
187365
+ if (mappedSessionIds.has(session.sessionId)) {
187366
+ continue;
187367
+ }
187368
+ result.unmappedSessions += 1;
187369
+ if (!session.directory) {
187370
+ result.skippedEmptyDirectories += 1;
187371
+ continue;
187372
+ }
187373
+ const stillExists = existenceCache.get(session.directory) ?? existsSync9(session.directory);
187374
+ existenceCache.set(session.directory, stillExists);
187375
+ if (!stillExists) {
187376
+ result.skippedDeadDirectories += 1;
187377
+ continue;
187378
+ }
187379
+ let identity = identityCache2.get(session.directory);
187380
+ if (!identity) {
187381
+ identity = resolveIdentity(session.directory);
187382
+ identityCache2.set(session.directory, identity);
187383
+ newDirectoryResolutions += 1;
187384
+ if (newDirectoryResolutions % YIELD_EVERY_NEW_DIRECTORIES === 0) {
187385
+ renewBackfillLease(db, harness, now());
187386
+ await yieldFn();
187387
+ renewBackfillLease(db, harness, now());
187388
+ }
187389
+ }
187390
+ recordSessionProjectIdentity(db, session.sessionId, identity);
187391
+ result.backfilledSessions += 1;
187392
+ }
187393
+ markBackfillCompleted(db, harness, now());
187394
+ result.durationMs = performance.now() - startedAt;
187395
+ log(`[session-projects] backfilled ${result.backfilledSessions} of ${result.unmappedSessions} unmapped sessions (skipped ${result.skippedDeadDirectories} dead dirs) in ${Math.round(result.durationMs)}ms`);
187396
+ return result;
187397
+ }
187398
+
186661
187399
  // src/features/magic-context/sidekick/agent.ts
186662
187400
  init_language_directive();
186663
187401
 
@@ -186821,7 +187559,7 @@ await init_storage_db();
186821
187559
  init_logger();
186822
187560
  init_project_identity();
186823
187561
  import { createHash as createHash5 } from "node:crypto";
186824
- import { realpathSync } from "node:fs";
187562
+ import { realpathSync as realpathSync2 } from "node:fs";
186825
187563
  import path5 from "node:path";
186826
187564
 
186827
187565
  // src/features/magic-context/memory/relocate-memory.ts
@@ -186855,7 +187593,10 @@ function countFailures(db) {
186855
187593
  }
186856
187594
  function classifyBackfillError(error51) {
186857
187595
  if (error51 instanceof ProjectIdentityError) {
186858
- return { errorClass: error51.errorClass, errorMessage: error51.message };
187596
+ return {
187597
+ errorClass: error51.errorClass === "dubious_ownership" ? "unknown" : error51.errorClass,
187598
+ errorMessage: error51.message
187599
+ };
186859
187600
  }
186860
187601
  if (error51 instanceof Error) {
186861
187602
  return { errorClass: "unknown", errorMessage: error51.message };
@@ -186875,7 +187616,7 @@ function resolveBackfillIdentity(rawProjectPath) {
186875
187616
  function computeLegacyRustDirIdentity(rawProjectPath) {
186876
187617
  let canonical;
186877
187618
  try {
186878
- canonical = realpathSync(rawProjectPath);
187619
+ canonical = realpathSync2(rawProjectPath);
186879
187620
  } catch {
186880
187621
  canonical = path5.isAbsolute(rawProjectPath) ? rawProjectPath : path5.join(process.cwd(), rawProjectPath);
186881
187622
  }
@@ -187048,45 +187789,45 @@ async function runDeferredV22Backfill(db, options = {}) {
187048
187789
 
187049
187790
  // src/hooks/auto-update-checker/index.ts
187050
187791
  init_logger();
187051
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
187052
- import { dirname as dirname7, join as join9 } from "node:path";
187792
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
187793
+ import { dirname as dirname7, join as join10 } from "node:path";
187053
187794
 
187054
187795
  // src/hooks/auto-update-checker/cache.ts
187055
187796
  init_logger();
187056
187797
  var import_comment_json2 = __toESM(require_src2(), 1);
187057
187798
  import { spawn } from "node:child_process";
187058
- import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
187059
- import { basename as basename2, dirname as dirname6, join as join8 } from "node:path";
187799
+ import { existsSync as existsSync14, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
187800
+ import { basename as basename2, dirname as dirname6, join as join9 } from "node:path";
187060
187801
 
187061
187802
  // src/hooks/auto-update-checker/checker.ts
187062
187803
  init_logger();
187063
187804
  var import_comment_json = __toESM(require_src2(), 1);
187064
- import { existsSync as existsSync9, readFileSync as readFileSync7, renameSync as renameSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "node:fs";
187805
+ import { existsSync as existsSync13, readFileSync as readFileSync7, renameSync as renameSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "node:fs";
187065
187806
  import { homedir as homedir5 } from "node:os";
187066
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join7, resolve as resolve2 } from "node:path";
187807
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join8, resolve as resolve2 } from "node:path";
187067
187808
  import { fileURLToPath } from "node:url";
187068
187809
 
187069
187810
  // src/hooks/auto-update-checker/constants.ts
187070
187811
  import { homedir as homedir4, platform } from "node:os";
187071
- import { join as join6 } from "node:path";
187812
+ import { join as join7 } from "node:path";
187072
187813
  var PACKAGE_NAME = "@cortexkit/opencode-magic-context";
187073
187814
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
187074
187815
  var NPM_FETCH_TIMEOUT = 1e4;
187075
187816
  function getOpenCodeCacheRoot() {
187076
187817
  if (platform() === "win32") {
187077
- return join6(process.env.LOCALAPPDATA ?? homedir4(), "opencode");
187818
+ return join7(process.env.LOCALAPPDATA ?? homedir4(), "opencode");
187078
187819
  }
187079
- return join6(homedir4(), ".cache", "opencode");
187820
+ return join7(homedir4(), ".cache", "opencode");
187080
187821
  }
187081
187822
  function getOpenCodeConfigRoot() {
187082
187823
  if (platform() === "win32") {
187083
- return join6(process.env.APPDATA ?? join6(homedir4(), "AppData", "Roaming"), "opencode");
187824
+ return join7(process.env.APPDATA ?? join7(homedir4(), "AppData", "Roaming"), "opencode");
187084
187825
  }
187085
- return join6(process.env.XDG_CONFIG_HOME ?? join6(homedir4(), ".config"), "opencode");
187826
+ return join7(process.env.XDG_CONFIG_HOME ?? join7(homedir4(), ".config"), "opencode");
187086
187827
  }
187087
- var CACHE_DIR = join6(getOpenCodeCacheRoot(), "packages");
187088
- var USER_OPENCODE_CONFIG = join6(getOpenCodeConfigRoot(), "opencode.json");
187089
- var USER_OPENCODE_CONFIG_JSONC = join6(getOpenCodeConfigRoot(), "opencode.jsonc");
187828
+ var CACHE_DIR = join7(getOpenCodeCacheRoot(), "packages");
187829
+ var USER_OPENCODE_CONFIG = join7(getOpenCodeConfigRoot(), "opencode.json");
187830
+ var USER_OPENCODE_CONFIG_JSONC = join7(getOpenCodeConfigRoot(), "opencode.jsonc");
187090
187831
  // src/hooks/auto-update-checker/types.ts
187091
187832
  init_zod();
187092
187833
  var NpmPackageEnvelopeSchema = exports_external.object({
@@ -187147,8 +187888,8 @@ function extractChannel(version2) {
187147
187888
  }
187148
187889
  function getConfigPaths(directory) {
187149
187890
  return [
187150
- join7(directory, ".opencode", "opencode.json"),
187151
- join7(directory, ".opencode", "opencode.jsonc"),
187891
+ join8(directory, ".opencode", "opencode.json"),
187892
+ join8(directory, ".opencode", "opencode.jsonc"),
187152
187893
  USER_OPENCODE_CONFIG,
187153
187894
  USER_OPENCODE_CONFIG_JSONC
187154
187895
  ];
@@ -187170,8 +187911,8 @@ function findPackageJsonUp(startPath) {
187170
187911
  const stat = statSync4(startPath);
187171
187912
  let dir = stat.isDirectory() ? startPath : dirname5(startPath);
187172
187913
  for (let i = 0;i < 10; i++) {
187173
- const pkgPath = join7(dir, "package.json");
187174
- if (existsSync9(pkgPath)) {
187914
+ const pkgPath = join8(dir, "package.json");
187915
+ if (existsSync13(pkgPath)) {
187175
187916
  try {
187176
187917
  const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync7(pkgPath, "utf-8")));
187177
187918
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
@@ -187189,7 +187930,7 @@ function findPackageJsonUp(startPath) {
187189
187930
  function getLocalDevPath(directory) {
187190
187931
  for (const configPath of getConfigPaths(directory)) {
187191
187932
  try {
187192
- if (!existsSync9(configPath))
187933
+ if (!existsSync13(configPath))
187193
187934
  continue;
187194
187935
  const rawConfig = parseJsonConfig(readFileSync7(configPath, "utf-8"));
187195
187936
  const plugins = getPluginEntries(rawConfig);
@@ -187235,7 +187976,7 @@ function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
187235
187976
  function findPluginEntry(directory) {
187236
187977
  for (const configPath of getConfigPaths(directory)) {
187237
187978
  try {
187238
- if (!existsSync9(configPath))
187979
+ if (!existsSync13(configPath))
187239
187980
  continue;
187240
187981
  const rawConfig = parseJsonConfig(readFileSync7(configPath, "utf-8"));
187241
187982
  const plugins = getPluginEntries(rawConfig);
@@ -187260,7 +188001,7 @@ function findPluginEntry(directory) {
187260
188001
  }
187261
188002
  var cachedPackageVersion = null;
187262
188003
  function getSpecCachePackageJsonPath(spec) {
187263
- return join7(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
188004
+ return join8(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
187264
188005
  }
187265
188006
  function getCachedVersion(spec) {
187266
188007
  if (!spec && cachedPackageVersion)
@@ -187269,11 +188010,11 @@ function getCachedVersion(spec) {
187269
188010
  getCurrentRuntimePackageJsonPath(),
187270
188011
  spec ? getSpecCachePackageJsonPath(spec) : null,
187271
188012
  getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
187272
- join7(homedir5(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
188013
+ join8(homedir5(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
187273
188014
  ].filter(isString);
187274
188015
  for (const packageJsonPath of candidates) {
187275
188016
  try {
187276
- if (!existsSync9(packageJsonPath))
188017
+ if (!existsSync13(packageJsonPath))
187277
188018
  continue;
187278
188019
  const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync7(packageJsonPath, "utf-8")));
187279
188020
  if (pkg.success && pkg.data.version) {
@@ -187327,8 +188068,8 @@ function stripPackageNameFromPath(pathValue, packageName) {
187327
188068
  return current;
187328
188069
  }
187329
188070
  function removeFromPackageLock(installDir, packageName) {
187330
- const lockPath = join8(installDir, "package-lock.json");
187331
- if (!existsSync10(lockPath))
188071
+ const lockPath = join9(installDir, "package-lock.json");
188072
+ if (!existsSync14(lockPath))
187332
188073
  return false;
187333
188074
  try {
187334
188075
  const lock = import_comment_json2.parse(readFileSync8(lockPath, "utf-8"));
@@ -187354,7 +188095,7 @@ function removeFromPackageLock(installDir, packageName) {
187354
188095
  }
187355
188096
  }
187356
188097
  function ensureDependencyVersion(packageJsonPath, packageName, version2) {
187357
- if (!existsSync10(packageJsonPath))
188098
+ if (!existsSync14(packageJsonPath))
187358
188099
  return false;
187359
188100
  try {
187360
188101
  const raw = import_comment_json2.parse(readFileSync8(packageJsonPath, "utf-8"));
@@ -187376,8 +188117,8 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
187376
188117
  }
187377
188118
  }
187378
188119
  function removeInstalledPackage(installDir, packageName) {
187379
- const packageDir = join8(installDir, "node_modules", packageName);
187380
- if (!existsSync10(packageDir))
188120
+ const packageDir = join9(installDir, "node_modules", packageName);
188121
+ if (!existsSync14(packageDir))
187381
188122
  return false;
187382
188123
  try {
187383
188124
  rmSync2(packageDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
@@ -187398,8 +188139,8 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
187398
188139
  const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
187399
188140
  if (nodeModulesDir && basename2(nodeModulesDir) === "node_modules") {
187400
188141
  const installDir = dirname6(nodeModulesDir);
187401
- const packageJsonPath = join8(installDir, "package.json");
187402
- if (!existsSync10(packageJsonPath)) {
188142
+ const packageJsonPath = join9(installDir, "package.json");
188143
+ if (!existsSync14(packageJsonPath)) {
187403
188144
  try {
187404
188145
  writeFileSync4(packageJsonPath, `${JSON.stringify({ private: true, dependencies: {} }, null, 2)}
187405
188146
  `);
@@ -187413,8 +188154,8 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
187413
188154
  }
187414
188155
  return null;
187415
188156
  }
187416
- const legacyPackageJsonPath = join8(dirname6(CACHE_DIR), "package.json");
187417
- if (existsSync10(legacyPackageJsonPath)) {
188157
+ const legacyPackageJsonPath = join9(dirname6(CACHE_DIR), "package.json");
188158
+ if (existsSync14(legacyPackageJsonPath)) {
187418
188159
  return { installDir: dirname6(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
187419
188160
  }
187420
188161
  return null;
@@ -187538,8 +188279,8 @@ function claimCheckSlot(storageDir, intervalMs) {
187538
188279
  if (!storageDir)
187539
188280
  return true;
187540
188281
  try {
187541
- const file2 = join9(storageDir, TIMESTAMP_FILENAME);
187542
- if (existsSync11(file2)) {
188282
+ const file2 = join10(storageDir, TIMESTAMP_FILENAME);
188283
+ if (existsSync15(file2)) {
187543
188284
  try {
187544
188285
  const raw = JSON.parse(readFileSync9(file2, "utf-8"));
187545
188286
  const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
@@ -187845,29 +188586,6 @@ function releaseLease(db, holderId, leaseKey = DREAMING_LEASE_KEY) {
187845
188586
  });
187846
188587
  }
187847
188588
 
187848
- // src/features/magic-context/dreamer/open-opencode-db.ts
187849
- init_logger();
187850
- await __promiseAll([
187851
- init_sqlite(),
187852
- init_compaction_marker()
187853
- ]);
187854
- import { existsSync as existsSync13 } from "node:fs";
187855
- function openOpenCodeDb() {
187856
- const dbPath = getOpenCodeDbPath2();
187857
- if (!existsSync13(dbPath)) {
187858
- log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
187859
- return null;
187860
- }
187861
- try {
187862
- const db = new Database(dbPath, { readonly: true });
187863
- db.exec("PRAGMA busy_timeout = 5000");
187864
- return db;
187865
- } catch (error51) {
187866
- log(`[key-files] failed to open OpenCode DB at ${dbPath}: ${getErrorMessage(error51)}`);
187867
- return null;
187868
- }
187869
- }
187870
-
187871
188589
  // src/features/magic-context/dreamer/retrospective-orphan-sweep.ts
187872
188590
  init_logger();
187873
188591
  var RETROSPECTIVE_CHILD_TITLE = "magic-context-dream-retrospective";
@@ -188290,7 +189008,7 @@ init_task_config();
188290
189008
 
188291
189009
  // src/features/magic-context/dreamer/task-executor.ts
188292
189010
  import { createHash as createHash10 } from "node:crypto";
188293
- import { existsSync as existsSync17 } from "node:fs";
189011
+ import { existsSync as existsSync20 } from "node:fs";
188294
189012
 
188295
189013
  // src/agents/dreamer.ts
188296
189014
  var DREAMER_AGENT = "dreamer";
@@ -190377,7 +191095,7 @@ Output exactly JSON: {"met": false}`;
190377
191095
 
190378
191096
  // src/features/magic-context/dreamer/maintain-docs-protected-enforcement.ts
190379
191097
  init_logger();
190380
- import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
191098
+ import { existsSync as existsSync18, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
190381
191099
  import { join as join15 } from "node:path";
190382
191100
 
190383
191101
  // src/features/magic-context/dreamer/protected-regions.ts
@@ -190481,7 +191199,7 @@ function snapshotMaintainDocsFiles(docsDir) {
190481
191199
  for (const name2 of MAINTAIN_DOCS_SNAPSHOT_FILES) {
190482
191200
  const path8 = join15(docsDir, name2);
190483
191201
  try {
190484
- if (existsSync15(path8)) {
191202
+ if (existsSync18(path8)) {
190485
191203
  snapshot.set(name2, readFileSync11(path8, "utf8"));
190486
191204
  }
190487
191205
  } catch {}
@@ -190513,7 +191231,7 @@ init_memory();
190513
191231
  init_subagent_token_capture();
190514
191232
 
190515
191233
  // src/features/magic-context/dreamer/map-memories-prompt.ts
190516
- import { existsSync as existsSync16, statSync as statSync6 } from "node:fs";
191234
+ import { existsSync as existsSync19, statSync as statSync6 } from "node:fs";
190517
191235
  import path8 from "node:path";
190518
191236
  var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
190519
191237
 
@@ -190551,7 +191269,7 @@ function extractMemoryCandidatePaths(content, repoDir) {
190551
191269
  if (!abs.startsWith(`${root}/`))
190552
191270
  continue;
190553
191271
  try {
190554
- if (existsSync16(abs) && statSync6(abs).isFile())
191272
+ if (existsSync19(abs) && statSync6(abs).isFile())
190555
191273
  found.add(rel);
190556
191274
  } catch {}
190557
191275
  if (found.size >= MAX_SEED_PATHS_PER_MEMORY)
@@ -192380,8 +193098,8 @@ async function runAgenticTask(config2, ctx, helpers) {
192380
193098
  const lastRunAt = getTaskScheduleState(db, projectIdentity, config2.task)?.lastRunAt ?? null;
192381
193099
  const maintainDocsSnapshot = task === "maintain-docs" ? snapshotMaintainDocsFiles(docsDir) : undefined;
192382
193100
  const existingDocs = task === "maintain-docs" ? {
192383
- architecture: existsSync17(`${docsDir}/ARCHITECTURE.md`),
192384
- structure: existsSync17(`${docsDir}/STRUCTURE.md`)
193101
+ architecture: existsSync20(`${docsDir}/ARCHITECTURE.md`),
193102
+ structure: existsSync20(`${docsDir}/STRUCTURE.md`)
192385
193103
  } : undefined;
192386
193104
  const userMemories = task === "curate" ? getActiveUserMemories(db).map((um) => ({ id: um.id, content: um.content })) : undefined;
192387
193105
  let curateMemories;
@@ -194548,6 +195266,17 @@ ${c.content}
194548
195266
  init_send_session_notification();
194549
195267
  var recompConfirmationBySession = new Map;
194550
195268
  var RECOMP_CONFIRMATION_WINDOW_MS = 60000;
195269
+ function isSubagentSession(db, sessionId) {
195270
+ const meta3 = getOrCreateSessionMeta(db, sessionId);
195271
+ if (meta3.isSubagent)
195272
+ return true;
195273
+ try {
195274
+ const row = db.prepare("SELECT is_subagent FROM session_meta WHERE session_id = ?").get(sessionId);
195275
+ return row?.is_subagent === 1 || row?.is_subagent === true;
195276
+ } catch {
195277
+ return false;
195278
+ }
195279
+ }
194551
195280
  var RECOMP_USAGE = [
194552
195281
  "Usage:",
194553
195282
  "- `/ctx-recomp` — full rebuild from message 1 to the protected tail",
@@ -194586,6 +195315,22 @@ ${RECOMP_USAGE}`
194586
195315
  }
194587
195316
  return { kind: "partial", range: { start, end } };
194588
195317
  }
195318
+ function parseWrapupArgs(raw) {
195319
+ const trimmed = raw.trim();
195320
+ if (trimmed === "")
195321
+ return { ok: true, messagesToKeep: 20 };
195322
+ if (!/^\d+$/.test(trimmed)) {
195323
+ return {
195324
+ ok: false,
195325
+ message: "Usage: `/ctx-wrapup [messages_to_keep]` where messages_to_keep is a positive integer."
195326
+ };
195327
+ }
195328
+ const messagesToKeep = Number.parseInt(trimmed, 10);
195329
+ if (!Number.isSafeInteger(messagesToKeep) || messagesToKeep <= 0) {
195330
+ return { ok: false, message: "messages_to_keep must be a positive integer." };
195331
+ }
195332
+ return { ok: true, messagesToKeep };
195333
+ }
194589
195334
  var SENTINEL_PREFIX = "__CONTEXT_MANAGEMENT_";
194590
195335
  var HTTP_SERVER_RESPONSE_TYPE_ID = "~effect/http/HttpServerResponse";
194591
195336
  var HTTP_COOKIES_TYPE_ID = "~effect/http/Cookies";
@@ -194727,6 +195472,7 @@ function createMagicContextCommandHandler(deps) {
194727
195472
  const isStatusCommand = (command) => command === "ctx-status";
194728
195473
  const isFlushCommand = (command) => command === "ctx-flush";
194729
195474
  const isRecompCommand = (command) => command === "ctx-recomp";
195475
+ const isWrapupCommand = (command) => command === "ctx-wrapup";
194730
195476
  const isAugCommand = (command) => command === "ctx-aug";
194731
195477
  const isDreamCommand = (command) => command === "ctx-dream";
194732
195478
  const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
@@ -194736,11 +195482,12 @@ function createMagicContextCommandHandler(deps) {
194736
195482
  const isStatus = isStatusCommand(input.command);
194737
195483
  const isFlush = isFlushCommand(input.command);
194738
195484
  const isRecomp = isRecompCommand(input.command);
195485
+ const isWrapup = isWrapupCommand(input.command);
194739
195486
  const isAug = isAugCommand(input.command);
194740
195487
  const isDream = isDreamCommand(input.command);
194741
195488
  const isSessionUpgrade = isSessionUpgradeCommand(input.command);
194742
195489
  const isEmbed = isEmbedCommand(input.command);
194743
- if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
195490
+ if (!isStatus && !isFlush && !isRecomp && !isWrapup && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
194744
195491
  return;
194745
195492
  }
194746
195493
  const sessionId = input.sessionID;
@@ -194810,6 +195557,26 @@ Embedding status is unavailable.`;
194810
195557
 
194811
195558
  ${statusOutput}` : statusOutput;
194812
195559
  }
195560
+ if (isWrapup) {
195561
+ const parsed = parseWrapupArgs(input.arguments);
195562
+ if (isSubagentSession(deps.db, sessionId)) {
195563
+ result = `## Magic Wrapup — Skipped
195564
+
195565
+ /ctx-wrapup is only available in primary sessions.`;
195566
+ } else if (!parsed.ok) {
195567
+ result = `## Magic Wrapup — Invalid Arguments
195568
+
195569
+ ${parsed.message}`;
195570
+ } else if (!deps.executeWrapup) {
195571
+ result = `## Magic Wrapup
195572
+
195573
+ /ctx-wrapup is unavailable because the historian handler is not configured.`;
195574
+ } else {
195575
+ result = await deps.executeWrapup(sessionId, {
195576
+ messagesToKeep: parsed.messagesToKeep
195577
+ });
195578
+ }
195579
+ }
194813
195580
  if (isRecomp) {
194814
195581
  const parsedArgs = parseRecompArgs(input.arguments);
194815
195582
  if (parsedArgs.kind === "error") {
@@ -195116,7 +195883,8 @@ var canonicalReasons = new Set([
195116
195883
  "first_render",
195117
195884
  "pressure_refold",
195118
195885
  "upgrade_state",
195119
- "cached_m1_missing"
195886
+ "cached_m1_missing",
195887
+ "project_change"
195120
195888
  ]);
195121
195889
  var piReasonAliases = {
195122
195890
  project_memory_change: "project_memory_epoch",
@@ -196047,35 +196815,31 @@ function resolveCtxReduceAvailabilityFromMessages(sessionId, messages) {
196047
196815
  for (const message of messages) {
196048
196816
  if (message.info?.role !== "user")
196049
196817
  continue;
196050
- const verdict = verdictFromToolsMap(message.info.tools);
196051
- if (verdict !== null) {
196052
- availabilityBySession.set(sessionId, verdict);
196053
- return verdict;
196054
- }
196055
- break;
196818
+ const verdict = verdictFromToolsMap(message.info.tools) ?? true;
196819
+ availabilityBySession.set(sessionId, verdict);
196820
+ return verdict;
196056
196821
  }
196057
- availabilityBySession.set(sessionId, true);
196058
196822
  return true;
196059
196823
  }
196060
196824
  function resolveCtxReduceAvailability(sessionId) {
196061
196825
  const cached2 = availabilityBySession.get(sessionId);
196062
196826
  if (cached2 !== undefined)
196063
- return cached2;
196827
+ return { callable: cached2, frozen: true };
196064
196828
  if (!openCodeDbExists())
196065
- return true;
196829
+ return { callable: true, frozen: true };
196066
196830
  try {
196067
196831
  const row = withReadOnlySessionDb((db) => db.prepare(`SELECT json_extract(data, '$.tools') AS tools FROM message
196068
196832
  WHERE session_id = ? AND json_extract(data, '$.role') = 'user'
196069
196833
  ORDER BY time_created ASC LIMIT 1`).get(sessionId));
196070
196834
  if (!row)
196071
- return true;
196835
+ return { callable: true, frozen: false };
196072
196836
  const verdict = row.tools === null ? null : verdictFromToolsMap(JSON.parse(row.tools));
196073
196837
  const resolved = verdict ?? true;
196074
196838
  availabilityBySession.set(sessionId, resolved);
196075
- return resolved;
196839
+ return { callable: resolved, frozen: true };
196076
196840
  } catch (error51) {
196077
196841
  sessionLog(sessionId, "ctx_reduce availability read failed (fail-open):", error51);
196078
- return true;
196842
+ return { callable: true, frozen: false };
196079
196843
  }
196080
196844
  }
196081
196845
 
@@ -197764,6 +198528,7 @@ function containsProbeVerbatim(text, probes) {
197764
198528
  init_memory();
197765
198529
  init_embedding();
197766
198530
  init_storage_memory_fts();
198531
+ init_storage_notes();
197767
198532
  init_storage_primers();
197768
198533
  init_workspaces();
197769
198534
  var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
@@ -198167,6 +198932,147 @@ function searchMessages(args) {
198167
198932
  role: entry.row.role
198168
198933
  }));
198169
198934
  }
198935
+ var NOTE_SEARCHABLE_STATUSES = ["active", "pending", "ready", "dismissed"];
198936
+ function noteSearchText(note) {
198937
+ const reason = note.readyReason?.trim();
198938
+ return reason ? `${note.content}
198939
+ Reason: ${reason}` : note.content;
198940
+ }
198941
+ function tokenizeKeywordNeedle(text) {
198942
+ const matches = text.toLowerCase().match(/[a-z0-9/._:-]+/g) ?? [];
198943
+ const seen = new Set;
198944
+ const tokens = [];
198945
+ for (const match of matches) {
198946
+ if (match.length <= 1 || !/[a-z0-9]/.test(match) || seen.has(match)) {
198947
+ continue;
198948
+ }
198949
+ seen.add(match);
198950
+ tokens.push(match);
198951
+ }
198952
+ return tokens;
198953
+ }
198954
+ function rankNotesForNeedle(notes, needle) {
198955
+ const normalizedNeedle = needle.trim().toLowerCase();
198956
+ if (normalizedNeedle.length === 0) {
198957
+ return [];
198958
+ }
198959
+ const needleTokens = tokenizeKeywordNeedle(normalizedNeedle);
198960
+ const ranked = [];
198961
+ for (const note of notes) {
198962
+ const text = noteSearchText(note);
198963
+ const normalizedText = text.toLowerCase();
198964
+ const noteTokens = new Set(tokenizeKeywordNeedle(normalizedText));
198965
+ const exact = normalizedText.includes(normalizedNeedle);
198966
+ const matchedTokens = needleTokens.filter((token) => noteTokens.has(token)).length;
198967
+ if (!exact && matchedTokens === 0) {
198968
+ continue;
198969
+ }
198970
+ const coverage = needleTokens.length > 0 ? matchedTokens / needleTokens.length : 0;
198971
+ const score = (exact ? 2 : 0) + coverage + (needleTokens.length > 1 && matchedTokens === needleTokens.length ? 0.5 : 0);
198972
+ ranked.push({ note, score, text });
198973
+ }
198974
+ return ranked.sort((left, right) => {
198975
+ if (right.score !== left.score) {
198976
+ return right.score - left.score;
198977
+ }
198978
+ if (right.note.createdAt !== left.note.createdAt) {
198979
+ return right.note.createdAt - left.note.createdAt;
198980
+ }
198981
+ return left.note.id - right.note.id;
198982
+ });
198983
+ }
198984
+ function searchNotes(args) {
198985
+ if (args.limit <= 0) {
198986
+ return [];
198987
+ }
198988
+ const notes = [
198989
+ ...getNotes(args.db, {
198990
+ sessionId: args.sessionId,
198991
+ type: "session",
198992
+ status: NOTE_SEARCHABLE_STATUSES
198993
+ }),
198994
+ ...getNotes(args.db, {
198995
+ projectPath: args.projectPath,
198996
+ type: "smart",
198997
+ status: NOTE_SEARCHABLE_STATUSES
198998
+ })
198999
+ ];
199000
+ if (notes.length === 0) {
199001
+ return [];
199002
+ }
199003
+ const baseList = rankNotesForNeedle(notes, args.query);
199004
+ const probes = args.probes ?? [];
199005
+ if (probes.length === 0) {
199006
+ const ranked2 = baseList.slice(0, args.limit);
199007
+ return ranked2.map((entry, rank) => ({
199008
+ source: "note",
199009
+ content: previewText(entry.text),
199010
+ score: linearDecayScore(rank, ranked2.length),
199011
+ noteId: entry.note.id,
199012
+ status: entry.note.status,
199013
+ createdAt: entry.note.createdAt,
199014
+ anchorOrdinal: entry.note.anchorOrdinal,
199015
+ sourceSessionId: entry.note.sessionId
199016
+ }));
199017
+ }
199018
+ const queryLists = [];
199019
+ if (baseList.length > 0) {
199020
+ queryLists.push({ rows: baseList, weight: 1 });
199021
+ }
199022
+ const probeWeights = new Map;
199023
+ for (const probe of probes) {
199024
+ const rows = rankNotesForNeedle(notes, probe);
199025
+ if (rows.length === 0) {
199026
+ continue;
199027
+ }
199028
+ const weight = probeDiscriminationWeight(rows.length, notes.length);
199029
+ probeWeights.set(probe, weight);
199030
+ queryLists.push({ rows, weight });
199031
+ }
199032
+ const fused = new Map;
199033
+ for (const list of queryLists) {
199034
+ list.rows.forEach((row, rank) => {
199035
+ const rrf = list.weight / (RRF_K + rank);
199036
+ const existing = fused.get(row.note.id);
199037
+ if (existing) {
199038
+ existing.score += rrf;
199039
+ } else {
199040
+ fused.set(row.note.id, { entry: row, score: rrf });
199041
+ }
199042
+ });
199043
+ }
199044
+ for (const match of fused.values()) {
199045
+ let best = 0;
199046
+ for (const probe of probes) {
199047
+ const weight = probeWeights.get(probe) ?? 0;
199048
+ if (weight > best && containsProbeVerbatim(match.entry.text, [probe])) {
199049
+ best = weight;
199050
+ }
199051
+ }
199052
+ if (best > 0) {
199053
+ match.score += best * VERBATIM_RANK_BONUS;
199054
+ }
199055
+ }
199056
+ const ranked = [...fused.values()].sort((left, right) => {
199057
+ if (right.score !== left.score) {
199058
+ return right.score - left.score;
199059
+ }
199060
+ if (right.entry.note.createdAt !== left.entry.note.createdAt) {
199061
+ return right.entry.note.createdAt - left.entry.note.createdAt;
199062
+ }
199063
+ return left.entry.note.id - right.entry.note.id;
199064
+ }).slice(0, args.limit);
199065
+ return ranked.map((entry, rank) => ({
199066
+ source: "note",
199067
+ content: previewText(entry.entry.text),
199068
+ score: linearDecayScore(rank, ranked.length),
199069
+ noteId: entry.entry.note.id,
199070
+ status: entry.entry.note.status,
199071
+ createdAt: entry.entry.note.createdAt,
199072
+ anchorOrdinal: entry.entry.note.anchorOrdinal,
199073
+ sourceSessionId: entry.entry.note.sessionId
199074
+ }));
199075
+ }
198170
199076
  function searchCompartmentChunks(args) {
198171
199077
  if (!args.queryEmbedding || args.limit <= 0 || !args.modelId || args.modelId === "off")
198172
199078
  return [];
@@ -198252,6 +199158,8 @@ function getSourceBoost(result) {
198252
199158
  return GIT_COMMIT_SOURCE_BOOST;
198253
199159
  case "primer":
198254
199160
  return PRIMER_SOURCE_BOOST;
199161
+ case "note":
199162
+ return 1;
198255
199163
  }
198256
199164
  }
198257
199165
  function compareUnifiedResults(left, right) {
@@ -198275,6 +199183,9 @@ function compareUnifiedResults(left, right) {
198275
199183
  if (left.source === "primer" && right.source === "primer") {
198276
199184
  return right.support - left.support || left.primerId - right.primerId;
198277
199185
  }
199186
+ if (left.source === "note" && right.source === "note") {
199187
+ return right.createdAt - left.createdAt || left.noteId - right.noteId;
199188
+ }
198278
199189
  return 0;
198279
199190
  }
198280
199191
  function toGitCommitResult(hit) {
@@ -198342,11 +199253,11 @@ function searchPrimers(args) {
198342
199253
  }
198343
199254
  function resolveSources(sources) {
198344
199255
  if (sources === undefined) {
198345
- return new Set(["memory", "message", "git_commit", "primer"]);
199256
+ return new Set(["memory", "message", "git_commit", "primer", "note"]);
198346
199257
  }
198347
199258
  const set2 = new Set;
198348
199259
  for (const source of sources) {
198349
- if (source === "memory" || source === "message" || source === "git_commit" || source === "primer") {
199260
+ if (source === "memory" || source === "message" || source === "git_commit" || source === "primer" || source === "note") {
198350
199261
  set2.add(source);
198351
199262
  }
198352
199263
  }
@@ -198369,6 +199280,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
198369
199280
  const runMessages = activeSources.has("message");
198370
199281
  const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
198371
199282
  const runPrimers = activeSources.has("primer") && memoryFeatureEnabled;
199283
+ const runNotes = activeSources.has("note");
198372
199284
  const runCompartmentChunks = runMessages && memoryFeatureEnabled && embeddingEnabled;
198373
199285
  const needsEmbedding = (runMemory || runGitCommits || runCompartmentChunks || runPrimers) && embeddingEnabled && isEmbeddingRuntimeEnabled();
198374
199286
  const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options.signal).catch((error51) => {
@@ -198404,7 +199316,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
198404
199316
  compartments: compartmentResults,
198405
199317
  limit: tierLimit
198406
199318
  });
198407
- const [memoryResults, gitCommitResults, primerResults] = await Promise.all([
199319
+ const [memoryResults, gitCommitResults, primerResults, noteResults] = await Promise.all([
198408
199320
  runMemory ? searchMemories({
198409
199321
  db,
198410
199322
  projectPath,
@@ -198431,9 +199343,23 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
198431
199343
  limit: tierLimit,
198432
199344
  queryEmbedding,
198433
199345
  queryModelId: embeddingModelId && embeddingModelId !== "off" ? embeddingModelId : null
199346
+ })) : Promise.resolve([]),
199347
+ runNotes ? Promise.resolve(searchNotes({
199348
+ db,
199349
+ sessionId,
199350
+ projectPath,
199351
+ query: trimmedQuery,
199352
+ limit: tierLimit,
199353
+ probes: messageProbes
198434
199354
  })) : Promise.resolve([])
198435
199355
  ]);
198436
- const results = [...memoryResults, ...primerResults, ...messageLikeResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
199356
+ const results = [
199357
+ ...memoryResults,
199358
+ ...primerResults,
199359
+ ...messageLikeResults,
199360
+ ...gitCommitResults,
199361
+ ...noteResults
199362
+ ].sort(compareUnifiedResults).slice(0, limit);
198437
199363
  const countRetrievals = options.countRetrievals ?? true;
198438
199364
  if (countRetrievals) {
198439
199365
  const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
@@ -198506,6 +199432,10 @@ function renderFragment(result, charCap) {
198506
199432
  const compressed = cavemanCompress(result.content, "ultra");
198507
199433
  return truncate(compressed, charCap);
198508
199434
  }
199435
+ case "note": {
199436
+ const compressed = cavemanCompress(result.content, "ultra");
199437
+ return truncate(compressed, charCap);
199438
+ }
198509
199439
  }
198510
199440
  }
198511
199441
  function buildAutoSearchHint(results, options = {}) {
@@ -199372,6 +200302,13 @@ var degradedCacheCountBySession = new BoundedSessionMap(100);
199372
200302
  function resetDegradedCacheCount(sessionId) {
199373
200303
  degradedCacheCountBySession.delete(sessionId);
199374
200304
  }
200305
+ function pendingMarkerCoveredByConsumedBoundary(pending, injection) {
200306
+ if (!injection)
200307
+ return false;
200308
+ if (pending.endMessageId === injection.compartmentEndMessageId)
200309
+ return true;
200310
+ return pending.ordinal <= injection.compartmentEndMessage;
200311
+ }
199375
200312
  function clearPendingCompactionMarkerAfterSuccessfulDrain(args) {
199376
200313
  if (clearPendingCompactionMarkerStateIf(args.db, args.sessionId, args.pending)) {
199377
200314
  return "cleared";
@@ -199770,24 +200707,29 @@ async function runPostTransformPhase(args) {
199770
200707
  if (historyWasConsumedThisPass && args.deferredHistoryWasPendingAtPassStart) {
199771
200708
  const pending = getPendingCompactionMarkerState(args.db, args.sessionId);
199772
200709
  if (pending) {
199773
- const outcome = applyDeferredCompactionMarker(args.db, args.sessionId, pending, args.sessionDirectory);
199774
- switch (outcome.kind) {
199775
- case "applied":
199776
- case "already-current":
199777
- case "stale-skip":
199778
- if (clearPendingCompactionMarkerAfterSuccessfulDrain({
199779
- db: args.db,
199780
- sessionId: args.sessionId,
199781
- pending,
199782
- deferredHistoryRefreshSessions: args.deferredHistoryRefreshSessions
199783
- }) === "cas-lost-newer-pending") {
200710
+ if (!pendingMarkerCoveredByConsumedBoundary(pending, args.pendingCompartmentInjection)) {
200711
+ suppressV12HistoryDrain = true;
200712
+ sessionLog(args.sessionId, `compaction-marker drain: pending ordinal ${pending.ordinal} is newer than consumed boundary ${args.pendingCompartmentInjection?.compartmentEndMessage ?? "<none>"}; preserving deferred history refresh signal`);
200713
+ } else {
200714
+ const outcome = applyDeferredCompactionMarker(args.db, args.sessionId, pending, args.sessionDirectory);
200715
+ switch (outcome.kind) {
200716
+ case "applied":
200717
+ case "already-current":
200718
+ case "stale-skip":
200719
+ if (clearPendingCompactionMarkerAfterSuccessfulDrain({
200720
+ db: args.db,
200721
+ sessionId: args.sessionId,
200722
+ pending,
200723
+ deferredHistoryRefreshSessions: args.deferredHistoryRefreshSessions
200724
+ }) === "cas-lost-newer-pending") {
200725
+ suppressV12HistoryDrain = true;
200726
+ }
200727
+ break;
200728
+ case "retryable-failure":
200729
+ sessionLog(args.sessionId, "compaction-marker drain: retryable failure; preserving deferred history refresh signal", outcome.error);
199784
200730
  suppressV12HistoryDrain = true;
199785
- }
199786
- break;
199787
- case "retryable-failure":
199788
- sessionLog(args.sessionId, "compaction-marker drain: retryable failure; preserving deferred history refresh signal", outcome.error);
199789
- suppressV12HistoryDrain = true;
199790
- break;
200731
+ break;
200732
+ }
199791
200733
  }
199792
200734
  }
199793
200735
  }
@@ -199892,6 +200834,16 @@ function getMessageTokensCache(sessionId) {
199892
200834
  }
199893
200835
  return cache;
199894
200836
  }
200837
+ function maybeSendProjectIdentityWarning(deps, sessionId, directory, notificationParams) {
200838
+ if (!deps.client)
200839
+ return;
200840
+ const warning = takeDubiousOwnershipProjectIdentityWarning(directory);
200841
+ if (!warning)
200842
+ return;
200843
+ sendIgnoredMessage(deps.client, sessionId, warning, notificationParams).catch((error51) => {
200844
+ sessionLog(sessionId, `project identity warning delivery failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
200845
+ });
200846
+ }
199895
200847
  function clearMessageTokensCache(sessionId, messageId) {
199896
200848
  if (messageId === undefined) {
199897
200849
  messageTokensBySession.delete(sessionId);
@@ -199966,7 +200918,7 @@ function createTransform(deps) {
199966
200918
  }
199967
200919
  const reducedMode = sessionMeta.isSubagent;
199968
200920
  const fullFeatureMode = !reducedMode;
199969
- const ctxReduceEnabledEffective = deps.ctxReduceEnabled !== false && resolveCtxReduceAvailabilityFromMessages(sessionId, messages);
200921
+ const ctxReduceCallable = resolveCtxReduceAvailabilityFromMessages(sessionId, messages);
199970
200922
  let sessionDirectory = deps.directory ?? "";
199971
200923
  let sessionDirectoryResolvedFromHost = false;
199972
200924
  const cachedDirectory = deps.sessionDirectoryBySession?.get(sessionId);
@@ -200249,8 +201201,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
200249
201201
  }
200250
201202
  }
200251
201203
  logTransformTiming(sessionId, "emergencyRecoveryBlock", tFirstPass);
200252
- const projectIdentity = deps.memoryConfig?.enabled ? resolveProjectIdentity(compartmentDirectory || process.cwd()) : undefined;
201204
+ const memoryProjectDirectory = compartmentDirectory || process.cwd();
201205
+ const projectIdentity = deps.memoryConfig?.enabled ? resolveProjectIdentity(memoryProjectDirectory) : undefined;
201206
+ if (deps.memoryConfig?.enabled) {
201207
+ maybeSendProjectIdentityWarning(deps, sessionId, memoryProjectDirectory, notificationParams);
201208
+ }
200253
201209
  const sessionProjectIdentity = projectIdentity ?? (sessionDirectory ? resolveProjectIdentity(sessionDirectory) : deps.projectPath);
201210
+ if (sessionDirectory) {
201211
+ maybeSendProjectIdentityWarning(deps, sessionId, sessionDirectory, notificationParams);
201212
+ }
200254
201213
  if (sessionProjectIdentity && sessionDirectoryResolvedFromHost && recordedSessionProjectIdentity.get(sessionId) !== sessionProjectIdentity) {
200255
201214
  recordSessionProjectIdentity(db, sessionId, sessionProjectIdentity);
200256
201215
  recordedSessionProjectIdentity.set(sessionId, sessionProjectIdentity);
@@ -200308,7 +201267,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
200308
201267
  const tInitFromDb = performance.now();
200309
201268
  deps.tagger.initFromDb(sessionId, db, taggerFloor);
200310
201269
  logTransformTiming(sessionId, "tag.initFromDb", tInitFromDb);
200311
- const skipPrefixInjection = !ctxReduceEnabledEffective;
201270
+ const skipPrefixInjection = !ctxReduceCallable;
200312
201271
  const result = tagMessages(sessionId, messages, deps.tagger, db, {
200313
201272
  skipPrefixInjection
200314
201273
  });
@@ -200365,7 +201324,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
200365
201324
  }
200366
201325
  logTransformTiming(sessionId, "replayReasoningClearing", tReplay);
200367
201326
  }
200368
- if (!deps.ctxReduceEnabled && !reducedMode && deps.cavemanTextCompression?.enabled) {
201327
+ if (!reducedMode && deps.cavemanTextCompression?.enabled) {
200369
201328
  const tCavemanReplay = performance.now();
200370
201329
  const replayedCaveman = replayCavemanCompression(sessionId, db, targets, targetsSliceTags);
200371
201330
  if (replayedCaveman > 0) {
@@ -200495,7 +201454,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
200495
201454
  projectPath: sessionProjectIdentity,
200496
201455
  sessionDirectory,
200497
201456
  autoSearch: deps.autoSearch,
200498
- cavemanTextCompression: deps.ctxReduceEnabled === false && !reducedMode ? deps.cavemanTextCompression : undefined,
201457
+ cavemanTextCompression: !reducedMode ? deps.cavemanTextCompression : undefined,
200499
201458
  smartDrops: deps.smartDrops === true,
200500
201459
  resolvedProviderID,
200501
201460
  historyRefreshSessions: deps.historyRefreshSessions,
@@ -200651,7 +201610,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
200651
201610
  sessionLog(sessionId, "conversation_tokens UPDATE failed:", error51);
200652
201611
  }
200653
201612
  }
200654
- if (ctxReduceEnabledEffective && deps.channel1StateBySession) {
201613
+ if (ctxReduceCallable && deps.channel1StateBySession) {
200655
201614
  try {
200656
201615
  const resolvedExecuteThresholdPct = resolveExecuteThreshold(deps.executeThresholdPercentage ?? 65, deps.getModelKey?.(sessionId), 65, {
200657
201616
  tokensConfig: deps.executeThresholdTokens,
@@ -201136,6 +202095,343 @@ function createTextCompleteHandler() {
201136
202095
  };
201137
202096
  }
201138
202097
 
202098
+ // src/hooks/magic-context/wrapup-orchestrator.ts
202099
+ init_compartment_lease();
202100
+ init_compartment_storage();
202101
+ init_logger();
202102
+ init_rpc_notifications();
202103
+ init_send_session_notification();
202104
+ await __promiseAll([
202105
+ init_storage(),
202106
+ init_compartment_runner(),
202107
+ init_compartment_runner_incremental(),
202108
+ init_protected_tail_boundary(),
202109
+ init_recomp_orchestrator()
202110
+ ]);
202111
+ import * as crypto3 from "node:crypto";
202112
+ var WAIT_FOR_LEASE_MS = 1000;
202113
+ function sleep2(ms) {
202114
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
202115
+ }
202116
+ function plural(value, word) {
202117
+ return `${value} ${word}${value === 1 ? "" : "s"}`;
202118
+ }
202119
+ function formatAlreadyRunningMessage(state) {
202120
+ if (!state)
202121
+ return "/ctx-wrapup is already running for this session.";
202122
+ const chunk = state.expectedChunks > 0 ? ` chunk ${state.chunkIndex}/${state.expectedChunks}` : "";
202123
+ const through = state.lastCompartmentEnd > 0 ? ` through message ${state.lastCompartmentEnd}` : "";
202124
+ return `/ctx-wrapup is already running for this session${chunk}${through}. Wait for it to finish, then run /ctx-wrapup again if more history remains.`;
202125
+ }
202126
+ function appendFlushHint(ctx, sessionId, message) {
202127
+ if (ctx.hasPendingNaturalBust?.(sessionId))
202128
+ return message;
202129
+ return `${message} If you want it applied on the very next message, run /ctx-flush first.`;
202130
+ }
202131
+ function buildPlan(ctx, sessionId, messagesToKeep, anchorRawMessageCount) {
202132
+ return resolveWrapupProtectedTailBoundary({
202133
+ db: ctx.db,
202134
+ sessionId,
202135
+ mode: "manual-wrapup",
202136
+ contextLimit: ctx.contextLimit,
202137
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
202138
+ usage: null,
202139
+ usageSource: "manual-none",
202140
+ providerShapeVersion: "opencode-v1",
202141
+ cacheNamespace: `opencode:${sessionId}`,
202142
+ messagesToKeep,
202143
+ anchorRawMessageCount
202144
+ });
202145
+ }
202146
+ function emitWrapupProgress(ctx, sessionId, progress) {
202147
+ const current = ctx.liveSessionState.recompProgressBySession.get(sessionId);
202148
+ ctx.liveSessionState.recompProgressBySession.set(sessionId, {
202149
+ sessionId,
202150
+ kind: "wrapup",
202151
+ phase: "recomp",
202152
+ processedMessages: progress.processedMessages ?? current?.processedMessages ?? 0,
202153
+ totalMessages: progress.totalMessages ?? current?.totalMessages ?? 0,
202154
+ passCount: progress.passCount ?? current?.passCount ?? 0,
202155
+ compartmentsCreated: progress.compartmentsCreated ?? current?.compartmentsCreated ?? 0,
202156
+ startedAt: current?.startedAt ?? Date.now(),
202157
+ updatedAt: Date.now(),
202158
+ note: progress.note
202159
+ });
202160
+ }
202161
+ async function waitForExistingIncrementalRun(sessionId) {
202162
+ const active = getActiveCompartmentRun(sessionId);
202163
+ if (!active)
202164
+ return "ok";
202165
+ if (active.kind === "recomp" || active.kind === "wrapup")
202166
+ return "busy";
202167
+ try {
202168
+ await active.promise;
202169
+ } catch {}
202170
+ return "ok";
202171
+ }
202172
+ async function acquireCompartmentLeaseForWrapup(ctx, sessionId, renewWrapupMarker) {
202173
+ const holderId = crypto3.randomUUID();
202174
+ for (;; ) {
202175
+ const active = getActiveCompartmentRun(sessionId);
202176
+ if (active?.kind === "recomp" || active?.kind === "wrapup")
202177
+ return null;
202178
+ if (active) {
202179
+ emitWrapupProgress(ctx, sessionId, { note: "Waiting for the active historian run…" });
202180
+ try {
202181
+ await active.promise;
202182
+ } catch {}
202183
+ continue;
202184
+ }
202185
+ const lease = acquireCompartmentLease(ctx.db, sessionId, holderId);
202186
+ if (lease)
202187
+ return holderId;
202188
+ emitWrapupProgress(ctx, sessionId, { note: "Waiting for the compartment-state lease…" });
202189
+ if (!renewWrapupMarker({}))
202190
+ return null;
202191
+ await sleep2(WAIT_FOR_LEASE_MS);
202192
+ }
202193
+ }
202194
+ async function runOneWrapupIteration(args) {
202195
+ const { ctx, sessionId, plan, messagesToKeep, anchorRawMessageCount } = args;
202196
+ const leaseHolderId = await acquireCompartmentLeaseForWrapup(ctx, sessionId, args.renewWrapupMarker);
202197
+ if (!leaseHolderId)
202198
+ return false;
202199
+ const renewal = setInterval(() => {
202200
+ try {
202201
+ if (!renewCompartmentLease(ctx.db, sessionId, leaseHolderId)) {
202202
+ sessionLog(sessionId, "wrapup: compartment lease renewal failed");
202203
+ }
202204
+ } catch (err) {
202205
+ sessionLog(sessionId, `wrapup: compartment lease renewal threw; continuing (${err instanceof Error ? err.message : String(err)})`);
202206
+ }
202207
+ }, COMPARTMENT_LEASE_RENEWAL_MS);
202208
+ const runCompartmentAgentForWrapup = ctx.runCompartmentAgentForWrapup ?? runCompartmentAgent;
202209
+ const runnerPromise = runCompartmentAgentForWrapup({
202210
+ client: ctx.client,
202211
+ db: ctx.db,
202212
+ sessionId,
202213
+ historianChunkTokens: ctx.historianChunkTokens,
202214
+ historianTimeoutMs: ctx.historianTimeoutMs,
202215
+ boundarySnapshot: plan.snapshot,
202216
+ currentContextLimit: ctx.contextLimit,
202217
+ directory: ctx.directory,
202218
+ fallbackModels: ctx.fallbackModels,
202219
+ fallbackModelId: ctx.fallbackModelId,
202220
+ language: ctx.language,
202221
+ historianTwoPass: ctx.historianTwoPass,
202222
+ memoryEnabled: ctx.memoryEnabled,
202223
+ autoPromote: ctx.autoPromote,
202224
+ ensureProjectRegistered: ctx.ensureProjectRegistered,
202225
+ getNotificationParams: () => ctx.getNotificationParams(sessionId),
202226
+ preserveInjectionCacheUntilConsumed: true,
202227
+ compartmentLeaseHolderId: leaseHolderId,
202228
+ forceDrainQuota: true,
202229
+ forceKeepLastCompartment: true,
202230
+ refreshBoundarySnapshot: () => buildPlan(ctx, sessionId, messagesToKeep, anchorRawMessageCount).snapshot,
202231
+ onCompartmentStatePublished: (sid) => {
202232
+ markActiveCompartmentRunPublished(sid);
202233
+ ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
202234
+ ctx.liveSessionState.deferredMaterializationSessions.add(sid);
202235
+ },
202236
+ onDeferredMarkerPending: (sid) => {
202237
+ ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
202238
+ }
202239
+ });
202240
+ registerActiveCompartmentRun(sessionId, runnerPromise, "wrapup");
202241
+ try {
202242
+ await runnerPromise;
202243
+ return true;
202244
+ } finally {
202245
+ clearInterval(renewal);
202246
+ releaseCompartmentLease(ctx.db, sessionId, leaseHolderId);
202247
+ }
202248
+ }
202249
+ async function runManagedWrapup(ctx, sessionId, options) {
202250
+ const messagesToKeep = Math.max(1, Math.floor(options.messagesToKeep));
202251
+ setRecompStarting(ctx.liveSessionState, sessionId, "Estimating wrapup…", "wrapup");
202252
+ const existingWrapup = getWrapupInProgressState(ctx.db, sessionId);
202253
+ if (existingWrapup) {
202254
+ const message2 = formatAlreadyRunningMessage(existingWrapup);
202255
+ setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message2);
202256
+ return `## Magic Wrapup — Skipped
202257
+
202258
+ ${message2}`;
202259
+ }
202260
+ const initialPlan = buildPlan(ctx, sessionId, messagesToKeep);
202261
+ if (initialPlan.rawMessagesAboveLastCompartment <= messagesToKeep || !hasRunnableCompartmentWindow(initialPlan.snapshot)) {
202262
+ const message2 = `Nothing to wrap up — only ${initialPlan.rawMessagesAboveLastCompartment} messages above the last compartment.`;
202263
+ setRecompTerminal(ctx.liveSessionState, sessionId, "done", message2);
202264
+ return message2;
202265
+ }
202266
+ const expectedChunks = Math.max(1, Math.ceil(initialPlan.snapshot.trueRawEligibleTokens / Math.max(1, ctx.historianChunkTokens)));
202267
+ const wrapupHolderId = crypto3.randomUUID();
202268
+ const acquired = acquireWrapupInProgress(ctx.db, sessionId, {
202269
+ holderId: wrapupHolderId,
202270
+ messagesToKeep,
202271
+ anchorRawMessageCount: initialPlan.anchorRawMessageCount,
202272
+ targetEligibleEndOrdinal: initialPlan.targetEligibleEndOrdinal,
202273
+ lastCompartmentEnd: getLastCompartmentEndMessage(ctx.db, sessionId),
202274
+ chunkIndex: 0,
202275
+ expectedChunks
202276
+ });
202277
+ if (!acquired.ok) {
202278
+ const message2 = formatAlreadyRunningMessage(acquired.state);
202279
+ setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message2);
202280
+ return `## Magic Wrapup — Skipped
202281
+
202282
+ ${message2}`;
202283
+ }
202284
+ const startLastEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
202285
+ const startCompartmentCount = getCompartments(ctx.db, sessionId).length;
202286
+ let chunkIndex = 0;
202287
+ let lastEnd = startLastEnd;
202288
+ let stoppedForFailure = false;
202289
+ let stoppedReason = "";
202290
+ let ownershipLost = false;
202291
+ const ownershipLostReason = "another process took over this session's wrapup.";
202292
+ const markOwnershipLost = () => {
202293
+ if (ownershipLost)
202294
+ return;
202295
+ ownershipLost = true;
202296
+ sessionLog(sessionId, "wrapup: durable marker ownership lost; aborting loop");
202297
+ };
202298
+ const renewWrapupMarker = (updates) => {
202299
+ const updated = updateWrapupInProgress(ctx.db, sessionId, wrapupHolderId, updates);
202300
+ if (!updated) {
202301
+ markOwnershipLost();
202302
+ return false;
202303
+ }
202304
+ return true;
202305
+ };
202306
+ const markerRenewal = setInterval(() => {
202307
+ try {
202308
+ renewWrapupMarker({
202309
+ lastCompartmentEnd: getLastCompartmentEndMessage(ctx.db, sessionId),
202310
+ chunkIndex
202311
+ });
202312
+ } catch (err) {
202313
+ sessionLog(sessionId, `wrapup: marker renewal threw; continuing (${err instanceof Error ? err.message : String(err)})`);
202314
+ }
202315
+ }, 60000);
202316
+ markerRenewal.unref?.();
202317
+ try {
202318
+ const activeAtStart = await waitForExistingIncrementalRun(sessionId);
202319
+ if (activeAtStart === "busy") {
202320
+ const message2 = "Another Magic Context rebuild is already running for this session.";
202321
+ setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message2);
202322
+ return `## Magic Wrapup — Skipped
202323
+
202324
+ ${message2}`;
202325
+ }
202326
+ try {
202327
+ sendIgnoredMessage(ctx.client, sessionId, `Magic Wrapup started — compacting about ${plural(expectedChunks, "chunk")} of history. This can take a few minutes; the result posts here when done.`, ctx.getNotificationParams(sessionId));
202328
+ } catch {}
202329
+ try {
202330
+ pushNotification("action", { action: "wrapup-progress-kick" }, sessionId);
202331
+ } catch {}
202332
+ if (ownershipLost) {
202333
+ stoppedForFailure = true;
202334
+ stoppedReason = ownershipLostReason;
202335
+ } else {
202336
+ emitWrapupProgress(ctx, sessionId, {
202337
+ processedMessages: Math.max(0, lastEnd),
202338
+ totalMessages: Math.max(0, initialPlan.targetEligibleEndOrdinal - 1),
202339
+ passCount: 0,
202340
+ compartmentsCreated: 0,
202341
+ note: `Eligible ${plural(initialPlan.snapshot.trueRawEligibleTokens, "token")} across about ${plural(expectedChunks, "chunk")}.`
202342
+ });
202343
+ for (;; ) {
202344
+ if (ownershipLost) {
202345
+ stoppedForFailure = true;
202346
+ stoppedReason = ownershipLostReason;
202347
+ break;
202348
+ }
202349
+ if (!renewWrapupMarker({
202350
+ lastCompartmentEnd: getLastCompartmentEndMessage(ctx.db, sessionId),
202351
+ chunkIndex,
202352
+ expectedChunks
202353
+ })) {
202354
+ stoppedForFailure = true;
202355
+ stoppedReason = ownershipLostReason;
202356
+ break;
202357
+ }
202358
+ const plan = buildPlan(ctx, sessionId, messagesToKeep, initialPlan.anchorRawMessageCount);
202359
+ lastEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
202360
+ if (lastEnd + 1 >= plan.targetEligibleEndOrdinal)
202361
+ break;
202362
+ chunkIndex += 1;
202363
+ emitWrapupProgress(ctx, sessionId, {
202364
+ processedMessages: Math.max(0, lastEnd),
202365
+ totalMessages: Math.max(0, plan.targetEligibleEndOrdinal - 1),
202366
+ passCount: chunkIndex - 1,
202367
+ compartmentsCreated: Math.max(0, getCompartments(ctx.db, sessionId).length - startCompartmentCount),
202368
+ note: `Chunk ${chunkIndex}/${expectedChunks}: messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1}…`
202369
+ });
202370
+ if (!renewWrapupMarker({
202371
+ lastCompartmentEnd: lastEnd,
202372
+ chunkIndex,
202373
+ expectedChunks,
202374
+ targetEligibleEndOrdinal: plan.targetEligibleEndOrdinal
202375
+ })) {
202376
+ stoppedForFailure = true;
202377
+ stoppedReason = ownershipLostReason;
202378
+ break;
202379
+ }
202380
+ const beforeEnd = lastEnd;
202381
+ const beforeFailures = getHistorianFailureState(ctx.db, sessionId).failureCount;
202382
+ const ran = await runOneWrapupIteration({
202383
+ ctx,
202384
+ sessionId,
202385
+ plan,
202386
+ messagesToKeep,
202387
+ anchorRawMessageCount: initialPlan.anchorRawMessageCount,
202388
+ renewWrapupMarker
202389
+ });
202390
+ if (!ran) {
202391
+ stoppedForFailure = true;
202392
+ stoppedReason = ownershipLost ? ownershipLostReason : "Another Magic Context rebuild started while wrapup was waiting.";
202393
+ break;
202394
+ }
202395
+ const afterEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
202396
+ const afterFailures = getHistorianFailureState(ctx.db, sessionId).failureCount;
202397
+ if (afterEnd <= beforeEnd) {
202398
+ stoppedForFailure = true;
202399
+ stoppedReason = afterFailures > beforeFailures ? "The historian failed on the current chunk." : "The historian made no forward progress on the current chunk.";
202400
+ break;
202401
+ }
202402
+ lastEnd = afterEnd;
202403
+ emitWrapupProgress(ctx, sessionId, {
202404
+ processedMessages: Math.max(0, lastEnd),
202405
+ totalMessages: Math.max(0, plan.targetEligibleEndOrdinal - 1),
202406
+ passCount: chunkIndex,
202407
+ compartmentsCreated: Math.max(0, getCompartments(ctx.db, sessionId).length - startCompartmentCount),
202408
+ note: `Wrapped through message ${lastEnd}.`
202409
+ });
202410
+ }
202411
+ }
202412
+ } finally {
202413
+ clearInterval(markerRenewal);
202414
+ releaseWrapupInProgress(ctx.db, sessionId, wrapupHolderId);
202415
+ }
202416
+ const finalEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
202417
+ const compartmentsCreated = Math.max(0, getCompartments(ctx.db, sessionId).length - startCompartmentCount);
202418
+ const messagesWrapped = Math.max(0, finalEnd - Math.max(0, startLastEnd));
202419
+ if (stoppedForFailure) {
202420
+ const message2 = `Wrapped up through message ${Math.max(0, finalEnd)} (${plural(messagesWrapped, "message")} into ${plural(compartmentsCreated, "compartment")}). ${stoppedReason} Run /ctx-wrapup again to continue.`;
202421
+ setRecompTerminal(ctx.liveSessionState, sessionId, "failed", message2);
202422
+ return `## Magic Wrapup — Partial
202423
+
202424
+ ${message2}`;
202425
+ }
202426
+ try {
202427
+ clearEmergencyRecovery(ctx.db, sessionId);
202428
+ } catch {}
202429
+ const base = `Wrapped up ${messagesWrapped} messages into ${compartmentsCreated} compartments. The compacted history is queued and materializes on your next message.`;
202430
+ const message = appendFlushHint(ctx, sessionId, base);
202431
+ setRecompTerminal(ctx.liveSessionState, sessionId, "done", message);
202432
+ return message;
202433
+ }
202434
+
201139
202435
  // src/hooks/magic-context/hook.ts
201140
202436
  await init_compaction_marker_manager();
201141
202437
 
@@ -201474,7 +202770,7 @@ Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-5
201474
202770
  Older tool calls may show \`[dropped §N§]\` sentinels; that is normal context management, not a pattern to copy. ALWAYS make fresh real tool calls when you need data again; never fabricate or inline tool output.`;
201475
202771
  var CAVEMAN_COMPRESSION_WARNING = `
201476
202772
  **BEWARE**: History compression is on; older user AND assistant text — including your own earlier responses — has been deterministically rewritten in a terse caveman style (dropped articles, missing auxiliaries, \`//\` instead of connectives like \`because\`). This is automatic context compression that runs after the fact, not your actual prior wording or the user's. **DO NOT mimic this style in new turns.** Write fresh responses in normal prose. If you notice your output drifting into caveman cadence, that drift is in-context-learning bleeding from the compressed history — consciously revert to full sentences.`;
201477
- function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language, memoryEnabled = true) {
202773
+ function buildMagicContextSection(_agent, protectedTags, ctxReduceCallable = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language, memoryEnabled = true) {
201478
202774
  if (subagentMode) {
201479
202775
  return `## Magic Context
201480
202776
 
@@ -201485,12 +202781,12 @@ When \`surface_condition\` is provided with \`write\`, the note becomes a projec
201485
202781
  The dreamer evaluates smart note conditions during nightly runs and surfaces them when conditions are met.
201486
202782
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
201487
202783
  const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
201488
- const cavemanWarning = cavemanTextCompressionEnabled && !ctxReduceEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
202784
+ const cavemanWarning = cavemanTextCompressionEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
201489
202785
  const languageDirective = buildPrimaryLanguageDirective(language);
201490
202786
  const languageGuidance = languageDirective ? `
201491
202787
 
201492
202788
  ${languageDirective}` : "";
201493
- if (!ctxReduceEnabled) {
202789
+ if (!ctxReduceCallable) {
201494
202790
  return `## Magic Context
201495
202791
 
201496
202792
  ${LONG_TERM_PARTNER_FRAME}
@@ -201503,7 +202799,7 @@ ${BASE_INTRO_NO_REDUCE(memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${c
201503
202799
  ${LONG_TERM_PARTNER_FRAME}
201504
202800
  ${PARTNER_FRAME_CLOSER_REDUCE}
201505
202801
 
201506
- ${BASE_INTRO(protectedTags, memoryEnabled)}${smartNoteGuidance}${temporalGuidance}
202802
+ ${BASE_INTRO(protectedTags, memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}
201507
202803
  ${GENERIC_SECTION}
201508
202804
 
201509
202805
  Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.${languageGuidance}`;
@@ -201556,17 +202852,18 @@ function createSystemPromptHashHandler(deps) {
201556
202852
  } catch (error51) {
201557
202853
  sessionLog(sessionId, "system-prompt-hash session meta load failed:", error51);
201558
202854
  }
201559
- const isSubagentSession = sessionMetaEarly?.isSubagent === true;
201560
- const ctxReduceCallable = resolveCtxReduceAvailability(sessionId);
201561
- const subagentReduceMode = isSubagentSession && deps.ctxReduceEnabled !== false && ctxReduceCallable;
201562
- const effectiveCtxReduceEnabled = isSubagentSession ? false : deps.ctxReduceEnabled !== false && ctxReduceCallable;
201563
- const skipGuidanceForDisabledSubagent = isSubagentSession && (deps.ctxReduceEnabled === false || !ctxReduceCallable);
202855
+ const isSubagentSession2 = sessionMetaEarly?.isSubagent === true;
202856
+ const availability = resolveCtxReduceAvailability(sessionId);
202857
+ const ctxReduceCallable = availability.callable;
202858
+ const subagentReduceMode = isSubagentSession2 && ctxReduceCallable;
202859
+ const effectiveCtxReduceEnabled = isSubagentSession2 ? false : ctxReduceCallable;
202860
+ const skipGuidanceForDisabledSubagent = isSubagentSession2 && !ctxReduceCallable;
201564
202861
  const fullPrompt = output.system.join(`
201565
202862
  `);
201566
202863
  if (fullPrompt.length > 0 && !fullPrompt.includes(MAGIC_CONTEXT_MARKER) && !skipGuidanceForDisabledSubagent) {
201567
202864
  const guidance = buildMagicContextSection(null, deps.protectedTags, effectiveCtxReduceEnabled, deps.dreamerEnabled, deps.experimentalTemporalAwareness, deps.experimentalCavemanTextCompression, subagentReduceMode, deps.language, deps.memoryEnabled !== false);
201568
202865
  output.system.push(guidance);
201569
- sessionLog(sessionId, `injected generic guidance into system prompt (ctxReduce=${effectiveCtxReduceEnabled}, subagent=${isSubagentSession}, subagentReduceMode=${subagentReduceMode})`);
202866
+ sessionLog(sessionId, `injected generic guidance into system prompt (ctxReduce=${effectiveCtxReduceEnabled}, subagent=${isSubagentSession2}, subagentReduceMode=${subagentReduceMode})`);
201570
202867
  }
201571
202868
  const isCacheBusting = deps.systemPromptRefreshSessions.has(sessionId);
201572
202869
  const DATE_PATTERN = /Today's date: .+/;
@@ -201593,6 +202890,8 @@ function createSystemPromptHashHandler(deps) {
201593
202890
  `);
201594
202891
  if (systemContent.length === 0)
201595
202892
  return;
202893
+ if (!availability.frozen)
202894
+ return;
201596
202895
  const currentHash = createHash13("md5").update(systemContent).digest("hex");
201597
202896
  if (!sessionMetaEarly) {
201598
202897
  return;
@@ -201781,7 +203080,7 @@ function createMagicContextHook(deps) {
201781
203080
  notifyMagicContextDisabled(deps.client, reason);
201782
203081
  return null;
201783
203082
  }
201784
- const projectPath = resolveProjectIdentity(deps.directory);
203083
+ const projectPath = resolveProjectIdentityOrFallback(deps.directory);
201785
203084
  try {
201786
203085
  checkCompactionMarkerConsistency(db);
201787
203086
  } catch (error51) {
@@ -201792,20 +203091,21 @@ function createMagicContextHook(deps) {
201792
203091
  const historianFallbackModels = resolveFallbackChain(deps.config.historian?.fallback_models);
201793
203092
  const historyRefreshSessions = deps.liveSessionState?.historyRefreshSessions ?? new Set;
201794
203093
  const deferredHistoryRefreshSessions = deps.liveSessionState?.deferredHistoryRefreshSessions ?? new Set;
203094
+ const systemPromptRefreshSessions = deps.liveSessionState?.systemPromptRefreshSessions ?? new Set;
203095
+ const pendingMaterializationSessions = deps.liveSessionState?.pendingMaterializationSessions ?? new Set;
203096
+ const deferredMaterializationSessions = deps.liveSessionState?.deferredMaterializationSessions ?? new Set;
201795
203097
  try {
201796
203098
  const sessionsWithPending = getSessionsWithPendingMarker(db);
201797
203099
  if (sessionsWithPending.length > 0) {
201798
203100
  for (const sid of sessionsWithPending) {
201799
203101
  deferredHistoryRefreshSessions.add(sid);
203102
+ deferredMaterializationSessions.add(sid);
201800
203103
  }
201801
203104
  log(`[magic-context] rehydrated ${sessionsWithPending.length} session(s) with pending compaction-marker drain at hook init`);
201802
203105
  }
201803
203106
  } catch (error51) {
201804
203107
  log("[magic-context] hook init: pending-marker rehydration failed:", error51);
201805
203108
  }
201806
- const systemPromptRefreshSessions = deps.liveSessionState?.systemPromptRefreshSessions ?? new Set;
201807
- const pendingMaterializationSessions = deps.liveSessionState?.pendingMaterializationSessions ?? new Set;
201808
- const deferredMaterializationSessions = deps.liveSessionState?.deferredMaterializationSessions ?? new Set;
201809
203109
  const lastHeuristicsTurnId = new Map;
201810
203110
  const commitSeenLastPass = new Map;
201811
203111
  const variantBySession = deps.liveSessionState?.variantBySession ?? new Map;
@@ -201826,7 +203126,15 @@ function createMagicContextHook(deps) {
201826
203126
  }
201827
203127
  return;
201828
203128
  };
201829
- const ctxReduceEnabled = deps.config.ctx_reduce_enabled !== false;
203129
+ const maybeSendProjectIdentitySessionWarning = (sessionId, directory) => {
203130
+ const warning = takeDubiousOwnershipProjectIdentityWarning(directory);
203131
+ if (!warning)
203132
+ return;
203133
+ const notificationParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession, deps.config.toast_duration_ms);
203134
+ sendIgnoredMessage(deps.client, sessionId, warning, notificationParams).catch((error51) => {
203135
+ log(`[magic-context] failed to send project identity warning for ${directory}: ${getErrorMessage(error51)}`);
203136
+ });
203137
+ };
201830
203138
  const dreamerRunnable = isDreamerRunnable(deps.config);
201831
203139
  const dreamerConfig = dreamerRunnable ? deps.config.dreamer : undefined;
201832
203140
  const historianRunnable = isHistorianRunnable(deps.config);
@@ -201863,6 +203171,23 @@ function createMagicContextHook(deps) {
201863
203171
  ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
201864
203172
  getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession, deps.config.toast_duration_ms)
201865
203173
  });
203174
+ const buildManagedWrapupCtx = (sessionId) => ({
203175
+ ...buildManagedRecompCtx(sessionId),
203176
+ contextLimit: (() => {
203177
+ const model = resolveLiveModel(sessionId);
203178
+ return model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
203179
+ })(),
203180
+ executeThresholdPercentage: (() => {
203181
+ const model = resolveLiveModel(sessionId);
203182
+ const contextLimit = model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
203183
+ return resolveExecuteThresholdDetail(deps.config.execute_threshold_percentage ?? 65, model ? `${model.providerID}/${model.modelID}` : undefined, 65, {
203184
+ tokensConfig: deps.config.execute_threshold_tokens,
203185
+ contextLimit,
203186
+ sessionId
203187
+ }).percentage;
203188
+ })(),
203189
+ hasPendingNaturalBust: (sid) => historyRefreshSessions.has(sid) || systemPromptRefreshSessions.has(sid) || pendingMaterializationSessions.has(sid)
203190
+ });
201866
203191
  const executeEmbedHistory = async (sessionId, options) => {
201867
203192
  if (deps.config.memory?.enabled === false) {
201868
203193
  return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
@@ -201873,7 +203198,8 @@ function createMagicContextHook(deps) {
201873
203198
  return "Embedding is already running for this session.";
201874
203199
  }
201875
203200
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
201876
- const sessionProjectIdentity = resolveProjectIdentity(directory);
203201
+ const sessionProjectIdentity = resolveProjectIdentityOrFallback(directory);
203202
+ maybeSendProjectIdentitySessionWarning(sessionId, directory);
201877
203203
  embedPauseBySession.delete(sessionId);
201878
203204
  const prior = embedRunStateBySession.get(sessionId);
201879
203205
  if (prior)
@@ -201938,13 +203264,15 @@ function createMagicContextHook(deps) {
201938
203264
  if (ctrl)
201939
203265
  ctrl.abort();
201940
203266
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
201941
- const sessionProjectIdentity = resolveProjectIdentity(directory);
203267
+ const sessionProjectIdentity = resolveProjectIdentityOrFallback(directory);
203268
+ maybeSendProjectIdentitySessionWarning(sessionId, directory);
201942
203269
  const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
201943
203270
  return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
201944
203271
  };
201945
203272
  const getEmbedStatusText = (sessionId) => {
201946
203273
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
201947
- const sessionProjectIdentity = resolveProjectIdentity(directory);
203274
+ const sessionProjectIdentity = resolveProjectIdentityOrFallback(directory);
203275
+ maybeSendProjectIdentitySessionWarning(sessionId, directory);
201948
203276
  const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
201949
203277
  const progress = recompProgressBySession.get(sessionId);
201950
203278
  const drainUi = getEmbedDrainUiStatus(sessionId, progress);
@@ -201967,7 +203295,8 @@ function createMagicContextHook(deps) {
201967
203295
  try {
201968
203296
  await new Promise((resolve4) => setTimeout(resolve4, 0));
201969
203297
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
201970
- const sessionProjectIdentity = resolveProjectIdentity(directory);
203298
+ const sessionProjectIdentity = resolveProjectIdentityOrFallback(directory);
203299
+ maybeSendProjectIdentitySessionWarning(sessionId, directory);
201971
203300
  const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
201972
203301
  if (!coverage.enabled)
201973
203302
  return;
@@ -202001,7 +203330,6 @@ function createMagicContextHook(deps) {
202001
203330
  db,
202002
203331
  channel1StateBySession,
202003
203332
  protectedTags: deps.config.protected_tags,
202004
- ctxReduceEnabled,
202005
203333
  smartDrops: deps.config.smart_drops === true,
202006
203334
  clearReasoningAge: deps.config.clear_reasoning_age ?? 50,
202007
203335
  commitClusterTrigger: deps.config.commit_cluster_trigger,
@@ -202050,7 +203378,7 @@ function createMagicContextHook(deps) {
202050
203378
  directory: deps.directory,
202051
203379
  ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory
202052
203380
  } : undefined,
202053
- cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
203381
+ cavemanTextCompression: deps.config.caveman_text_compression?.enabled === true ? {
202054
203382
  enabled: true,
202055
203383
  minChars: deps.config.caveman_text_compression.min_chars ?? 500
202056
203384
  } : undefined,
@@ -202138,6 +203466,7 @@ function createMagicContextHook(deps) {
202138
203466
  systemPromptRefreshSessions.add(sessionId);
202139
203467
  pendingMaterializationSessions.add(sessionId);
202140
203468
  },
203469
+ executeWrapup: historianRunnable ? async (sessionId, options) => runManagedWrapup(buildManagedWrapupCtx(sessionId), sessionId, options) : undefined,
202141
203470
  executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
202142
203471
  runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
202143
203472
  executeEmbedHistory,
@@ -202181,7 +203510,6 @@ function createMagicContextHook(deps) {
202181
203510
  const systemPromptHash = createSystemPromptHashHandler({
202182
203511
  db,
202183
203512
  protectedTags: deps.config.protected_tags,
202184
- ctxReduceEnabled,
202185
203513
  dreamerEnabled: dreamerRunnable,
202186
203514
  memoryEnabled: deps.config.memory?.enabled !== false,
202187
203515
  language: deps.config.language,
@@ -202196,7 +203524,7 @@ function createMagicContextHook(deps) {
202196
203524
  internalChildSessions,
202197
203525
  experimentalUserMemories: userMemoryCollectionEnabled(deps.config.dreamer),
202198
203526
  experimentalTemporalAwareness: deps.config.temporal_awareness === true,
202199
- experimentalCavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true
203527
+ experimentalCavemanTextCompression: deps.config.caveman_text_compression?.enabled === true
202200
203528
  });
202201
203529
  const systemPromptHashHandler = systemPromptHash.handler;
202202
203530
  const eventHook = createEventHook({
@@ -202215,8 +203543,7 @@ function createMagicContextHook(deps) {
202215
203543
  lastHeuristicsTurnId,
202216
203544
  commitSeenLastPass,
202217
203545
  client: deps.client,
202218
- protectedTags: deps.config.protected_tags,
202219
- ctxReduceEnabled
203546
+ protectedTags: deps.config.protected_tags
202220
203547
  });
202221
203548
  return {
202222
203549
  "experimental.chat.messages.transform": transform2,
@@ -202231,7 +203558,6 @@ function createMagicContextHook(deps) {
202231
203558
  systemPromptRefreshSessions,
202232
203559
  pendingMaterializationSessions,
202233
203560
  lastHeuristicsTurnId,
202234
- ctxReduceEnabled,
202235
203561
  upgradeReminder: historianRunnable ? (sessionId) => maybeSendUpgradeReminder({
202236
203562
  client: deps.client,
202237
203563
  db,
@@ -202263,7 +203589,6 @@ function createMagicContextHook(deps) {
202263
203589
  function buildMagicContextHookConfig(pluginConfig) {
202264
203590
  return {
202265
203591
  protected_tags: pluginConfig.protected_tags ?? DEFAULT_PROTECTED_TAGS,
202266
- ctx_reduce_enabled: pluginConfig.ctx_reduce_enabled,
202267
203592
  cache_ttl: pluginConfig.cache_ttl,
202268
203593
  clear_reasoning_age: pluginConfig.clear_reasoning_age,
202269
203594
  toast_duration_ms: pluginConfig.toast_duration_ms,
@@ -202784,7 +204109,6 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
202784
204109
  contextLimit: 0,
202785
204110
  systemPromptTokens: 0,
202786
204111
  compartmentCount: 0,
202787
- factCount: 0,
202788
204112
  memoryCount: 0,
202789
204113
  memoryBlockCount: 0,
202790
204114
  pendingOpsCount: 0,
@@ -202823,7 +204147,6 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
202823
204147
  const memoryBlockCount = meta3 ? Number(meta3.memory_block_count ?? 0) : 0;
202824
204148
  const compartmentRow = db.prepare("SELECT COUNT(*) as count FROM compartments WHERE session_id = ?").get(sessionId);
202825
204149
  const compartmentCount = compartmentRow?.count ?? 0;
202826
- const factCount = 0;
202827
204150
  let memoryCount = 0;
202828
204151
  if (projectIdentity) {
202829
204152
  const memRow = db.prepare("SELECT COUNT(*) as count FROM memories WHERE project_path = ? AND status = 'active'").get(projectIdentity);
@@ -202930,7 +204253,6 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
202930
204253
  contextLimit,
202931
204254
  systemPromptTokens: calibrated.systemTokens,
202932
204255
  compartmentCount,
202933
- factCount,
202934
204256
  memoryCount,
202935
204257
  memoryBlockCount,
202936
204258
  pendingOpsCount,
@@ -203943,8 +205265,8 @@ function createCtxMemoryTool(deps) {
203943
205265
  }
203944
205266
  });
203945
205267
  const idList = targets.map((t) => t.memoryId).join(", ");
203946
- const plural = targets.length > 1 ? "memories" : "memory";
203947
- return args.reason?.trim() ? `Archived ${plural} [ID: ${idList}] (${args.reason.trim()}).` : `Archived ${plural} [ID: ${idList}].`;
205268
+ const plural2 = targets.length > 1 ? "memories" : "memory";
205269
+ return args.reason?.trim() ? `Archived ${plural2} [ID: ${idList}] (${args.reason.trim()}).` : `Archived ${plural2} [ID: ${idList}].`;
203948
205270
  }
203949
205271
  return "Error: Unknown action.";
203950
205272
  }
@@ -204349,10 +205671,12 @@ Sources (omit for a broad search across all):
204349
205671
  - memory: curated cross-session project knowledge — rules, constraints, conventions.
204350
205672
  - message: the raw conversation behind your compacted history. Hits include message ordinals — expand the surrounding exchange with ctx_expand(start=N-10, end=N+5).
204351
205673
  - git_commit: this repository's commit history.
205674
+ - note: parked decisions, follow-ups, and dismissed notes with their recorded text.
204352
205675
 
204353
205676
  Picking sources:
204354
205677
  - "when did this change / was this working before" → ["git_commit", "message"]
204355
205678
  - "did we discuss this earlier" → ["message"]
205679
+ - "did we decide something about this / leave a follow-up" → ["note"]
204356
205680
  - "what's our convention / rule for X" → ["memory"]`;
204357
205681
  var DEFAULT_CTX_SEARCH_LIMIT = 10;
204358
205682
  // src/tools/ctx-search/tools.ts
@@ -204364,7 +205688,8 @@ var VALID_SOURCES = new Set([
204364
205688
  "memory",
204365
205689
  "message",
204366
205690
  "git_commit",
204367
- "primer"
205691
+ "primer",
205692
+ "note"
204368
205693
  ]);
204369
205694
  function normalizeLimit3(limit) {
204370
205695
  if (typeof limit !== "number" || !Number.isFinite(limit)) {
@@ -204407,7 +205732,8 @@ function formatAge2(committedAtMs) {
204407
205732
  const years = Math.floor(days / 365);
204408
205733
  return years === 1 ? "1y ago" : `${years}y ago`;
204409
205734
  }
204410
- function formatResult(result, index) {
205735
+ var NOTE_EXPAND_HINT = "Use ctx_expand(start=N-10, end=N) around any note @msg anchor above to read the surrounding conversation context.";
205736
+ function formatResult(result, index, currentSessionId) {
204411
205737
  if (result.source === "memory") {
204412
205738
  const source = result.sourceName ? ` source=${result.sourceName}` : "";
204413
205739
  return [
@@ -204428,6 +205754,14 @@ function formatResult(result, index) {
204428
205754
  `[${index}] [primer] score=${result.score.toFixed(2)} id=${result.primerId} support=${result.support} match=${result.matchType}`,
204429
205755
  result.content
204430
205756
  ].join(`
205757
+ `);
205758
+ }
205759
+ if (result.source === "note") {
205760
+ const anchor = result.anchorOrdinal !== null && result.sourceSessionId === currentSessionId ? ` @msg ${result.anchorOrdinal}` : "";
205761
+ return [
205762
+ `[${index}] [note] score=${result.score.toFixed(2)} id=#${result.noteId} status=${result.status} ${formatAge2(result.createdAt)}${anchor}`,
205763
+ result.content
205764
+ ].join(`
204431
205765
  `);
204432
205766
  }
204433
205767
  if (result.source === "compartment") {
@@ -204445,14 +205779,17 @@ function formatResult(result, index) {
204445
205779
  ].join(`
204446
205780
  `);
204447
205781
  }
204448
- function formatSearchResults(query, results) {
205782
+ function formatSearchResults(query, results, currentSessionId) {
204449
205783
  if (results.length === 0) {
204450
- return `No results found for "${query}" across memories, primers, git commits, or message history.`;
205784
+ return `No results found for "${query}" across notes, memories, primers, git commits, or message history.`;
204451
205785
  }
204452
- const bodyParts = results.map((result, index) => formatResult(result, index + 1));
205786
+ const bodyParts = results.map((result, index) => formatResult(result, index + 1, currentSessionId));
204453
205787
  if (results.some((result) => result.source === "message" || result.source === "compartment")) {
204454
205788
  bodyParts.push("Use ctx_expand(start, end) with the range from any message result above to read the full conversation context.");
204455
205789
  }
205790
+ if (results.some((result) => result.source === "note" && result.anchorOrdinal !== null && result.sourceSessionId === currentSessionId)) {
205791
+ bodyParts.push(NOTE_EXPAND_HINT);
205792
+ }
204456
205793
  const body = bodyParts.join(`
204457
205794
 
204458
205795
  `);
@@ -204466,7 +205803,7 @@ function createCtxSearchTool(deps) {
204466
205803
  args: {
204467
205804
  query: tool5.schema.string().describe("Search query. Matches against memory content, Primers, git commit messages, and raw user/assistant message text."),
204468
205805
  limit: tool5.schema.number().optional().describe("Maximum results to return (default: 10)"),
204469
- sources: tool5.schema.array(tool5.schema.enum(["memory", "message", "git_commit", "primer"])).optional().describe('Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources.')
205806
+ sources: tool5.schema.array(tool5.schema.enum(["memory", "message", "git_commit", "primer", "note"])).optional().describe('Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["note"] for parked decisions or follow-ups, ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources.')
204470
205807
  },
204471
205808
  async execute(args, toolContext) {
204472
205809
  const query = args.query?.trim();
@@ -204498,7 +205835,7 @@ function createCtxSearchTool(deps) {
204498
205835
  visibleMemoryIds,
204499
205836
  explicitSearch: true
204500
205837
  });
204501
- return formatSearchResults(query, results);
205838
+ return formatSearchResults(query, results, toolContext.sessionID);
204502
205839
  }
204503
205840
  });
204504
205841
  }
@@ -204555,14 +205892,13 @@ function createToolRegistry(args) {
204555
205892
  return {};
204556
205893
  }
204557
205894
  ensureProjectRegisteredFromOpenCodeDirectory(ctx.directory, db);
204558
- const resolveProjectPath = (directory) => resolveProjectIdentity(directory);
204559
- const ctxReduceEnabled = pluginConfig.ctx_reduce_enabled !== false;
205895
+ const resolveProjectPath = (directory) => resolveProjectIdentityOrFallback(directory);
204560
205896
  const memoryEnabled = pluginConfig.memory?.enabled !== false;
204561
205897
  const allTools = {
204562
- ...ctxReduceEnabled ? createCtxReduceTools({
205898
+ ...createCtxReduceTools({
204563
205899
  db,
204564
205900
  protectedTags: pluginConfig.protected_tags ?? DEFAULT_PROTECTED_TAGS
204565
- }) : {},
205901
+ }),
204566
205902
  ...createCtxExpandTools({ db }),
204567
205903
  ...createCtxNoteTools({
204568
205904
  db,
@@ -204973,6 +206309,31 @@ var server = async (ctx) => {
204973
206309
  log(`[v22-backfill] failed to start background runner: ${err}`);
204974
206310
  }
204975
206311
  }
206312
+ if (pluginConfig.enabled && hooks.magicContext) {
206313
+ setTimeout(() => {
206314
+ try {
206315
+ const db = openDatabase();
206316
+ if (!db || !isDatabasePersisted(db))
206317
+ return;
206318
+ const ocDb = openOpenCodeDb();
206319
+ if (!ocDb)
206320
+ return;
206321
+ try {
206322
+ const sessions = ocDb.prepare("SELECT id, COALESCE(directory, '') AS directory FROM session ORDER BY id ASC").all();
206323
+ runSessionProjectBackfill(db, sessions.map((session) => ({
206324
+ sessionId: session.id,
206325
+ directory: session.directory
206326
+ }))).catch((err) => {
206327
+ log(`[session-projects] background runner failed: ${err}`);
206328
+ });
206329
+ } finally {
206330
+ closeQuietly(ocDb);
206331
+ }
206332
+ } catch (err) {
206333
+ log(`[session-projects] failed to start background runner: ${err}`);
206334
+ }
206335
+ }, 0);
206336
+ }
204976
206337
  const storageDir = getMagicContextStorageDir();
204977
206338
  let rpcServer = null;
204978
206339
  let stopDreamTimerRegistration;
@@ -204980,7 +206341,7 @@ var server = async (ctx) => {
204980
206341
  const dreamerRunnable = isDreamerRunnable(pluginConfig);
204981
206342
  const timerRegistration = {
204982
206343
  directory: ctx.directory,
204983
- projectIdentity: resolveProjectIdentity(ctx.directory),
206344
+ projectIdentity: resolveProjectIdentityOrFallback(ctx.directory),
204984
206345
  client: ctx.client,
204985
206346
  dreamerConfig: dreamerRunnable ? pluginConfig.dreamer : undefined,
204986
206347
  language: pluginConfig.language,