@cortexkit/pi-magic-context 0.42.1 → 0.42.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -174,6 +174,7 @@ import {
174
174
  escapeXmlContent,
175
175
  getModuleNoteEvaluationBridge,
176
176
  getContextStoreUuid,
177
+ drainMirrorPages,
177
178
  parseCompartmentOutput,
178
179
  resolveWorkspaceShareCategories,
179
180
  resolveWorkspaceIdentitySet,
@@ -463,7 +464,7 @@ import {
463
464
  registerTodoStateLifecycle,
464
465
  syncCtxMemoryToolEnabled,
465
466
  registerMagicContextTools
466
- } from "./index-pknkkpnp.js";
467
+ } from "./index-6zjn0r2z.js";
467
468
  import {
468
469
  pushNotification2
469
470
  } from "./index-b3eqj1g6.js";
@@ -2414,14 +2415,25 @@ function countActiveMemories(db, projectPath) {
2414
2415
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
2415
2416
  return row?.cnt ?? 0;
2416
2417
  }
2418
+ function countLiveMemories(db, projectPath, options = {}) {
2419
+ const unclassified = options.unclassifiedOnly && hasMemoryClassifiedAtColumn(db);
2420
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
2421
+ FROM memories
2422
+ WHERE project_path = ?
2423
+ AND status IN ('active','permanent')
2424
+ AND (expires_at IS NULL OR expires_at > ?)
2425
+ ${unclassified ? "AND classified_at IS NULL" : ""}`).get(projectPath, Date.now());
2426
+ return row?.cnt ?? 0;
2427
+ }
2417
2428
  function countUnmappedActiveMemories(db, projectPath) {
2418
2429
  const row = db.prepare(`SELECT COUNT(*) AS cnt
2419
2430
  FROM memories m
2420
2431
  WHERE m.project_path = ?
2421
2432
  AND m.status IN ('active','permanent')
2433
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2422
2434
  AND NOT EXISTS (
2423
2435
  SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
2424
- )`).get(projectPath);
2436
+ )`).get(projectPath, Date.now());
2425
2437
  return row?.cnt ?? 0;
2426
2438
  }
2427
2439
  function countCompartmentsSince(db, projectPath, since) {
@@ -2441,7 +2453,8 @@ function countMappedMemories(db, projectPath) {
2441
2453
  JOIN memory_verifications v ON v.memory_id = m.id
2442
2454
  WHERE m.project_path = ?
2443
2455
  AND m.status IN ('active','permanent')
2444
- AND v.file_path <> ''`).get(projectPath);
2456
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2457
+ AND v.file_path <> ''`).get(projectPath, Date.now());
2445
2458
  return row?.cnt ?? 0;
2446
2459
  }
2447
2460
  function countUnverifiedMappedMemories(db, projectPath) {
@@ -2450,8 +2463,9 @@ function countUnverifiedMappedMemories(db, projectPath) {
2450
2463
  JOIN memory_verifications v ON v.memory_id = m.id
2451
2464
  WHERE m.project_path = ?
2452
2465
  AND m.status IN ('active','permanent')
2466
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2453
2467
  AND v.file_path <> ''
2454
- AND v.verified_at = 0`).get(projectPath);
2468
+ AND v.verified_at = 0`).get(projectPath, Date.now());
2455
2469
  return row?.cnt ?? 0;
2456
2470
  }
2457
2471
  function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
@@ -2459,22 +2473,24 @@ function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
2459
2473
  FROM memories m
2460
2474
  WHERE m.project_path = ?
2461
2475
  AND m.status IN ('active','permanent')
2476
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2462
2477
  AND (
2463
2478
  SELECT MAX(v.verified_at)
2464
2479
  FROM memory_verifications v
2465
2480
  WHERE v.memory_id = m.id
2466
2481
  AND v.file_path <> ''
2467
- ) < ?`).get(projectPath, cycleStartAt);
2482
+ ) < ?`).get(projectPath, Date.now(), cycleStartAt);
2468
2483
  return row?.cnt ?? 0;
2469
2484
  }
2470
2485
  function countCueCandidates(db, projectPath) {
2471
2486
  if (!hasMuralCueColumns(db))
2472
- return countActiveMemories(db, projectPath);
2487
+ return countLiveMemories(db, projectPath);
2473
2488
  const row = db.prepare(`SELECT COUNT(*) AS cnt
2474
2489
  FROM memories
2475
2490
  WHERE project_path = ?
2476
2491
  AND status IN ('active','permanent')
2477
- AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
2492
+ AND (expires_at IS NULL OR expires_at > ?)
2493
+ AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath, Date.now());
2478
2494
  return row?.cnt ?? 0;
2479
2495
  }
2480
2496
  function countStalePrimers(db, projectPath) {
@@ -2486,16 +2502,6 @@ function countStalePrimers(db, projectPath) {
2486
2502
  OR last_observed_at > answer_refreshed_at)`).get(projectPath);
2487
2503
  return row?.cnt ?? 0;
2488
2504
  }
2489
- function countUnclassifiedActiveMemories(db, projectPath) {
2490
- if (!hasMemoryClassifiedAtColumn(db))
2491
- return countActiveMemories(db, projectPath);
2492
- const row = db.prepare(`SELECT COUNT(*) AS cnt
2493
- FROM memories
2494
- WHERE project_path = ?
2495
- AND status IN ('active','permanent')
2496
- AND classified_at IS NULL`).get(projectPath);
2497
- return row?.cnt ?? 0;
2498
- }
2499
2505
  function countPendingSmartNotes(db, projectPath) {
2500
2506
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
2501
2507
  return row?.cnt ?? 0;
@@ -2511,7 +2517,7 @@ function countActivePrimers(db, projectPath) {
2511
2517
  function getDreamTaskBacklog(db, projectPath, task, options = {}) {
2512
2518
  switch (task) {
2513
2519
  case "map-memories": {
2514
- const total = countActiveMemories(db, projectPath);
2520
+ const total = countLiveMemories(db, projectPath);
2515
2521
  return { pending: countUnmappedActiveMemories(db, projectPath), total };
2516
2522
  }
2517
2523
  case "verify": {
@@ -2527,16 +2533,19 @@ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
2527
2533
  return { pending, total };
2528
2534
  }
2529
2535
  case "curate": {
2530
- const total = countActiveMemories(db, projectPath);
2536
+ const total = countLiveMemories(db, projectPath);
2531
2537
  return { pending: total, total };
2532
2538
  }
2533
2539
  case "compress-cues": {
2534
- const total = countActiveMemories(db, projectPath);
2540
+ const total = countLiveMemories(db, projectPath);
2535
2541
  return { pending: countCueCandidates(db, projectPath), total };
2536
2542
  }
2537
2543
  case "classify-memories": {
2538
- const total = countActiveMemories(db, projectPath);
2539
- return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
2544
+ const total = countLiveMemories(db, projectPath);
2545
+ return {
2546
+ pending: countLiveMemories(db, projectPath, { unclassifiedOnly: true }),
2547
+ total
2548
+ };
2540
2549
  }
2541
2550
  case "retrospective": {
2542
2551
  const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
@@ -2581,15 +2590,15 @@ function evaluateTaskGate(task, ctx) {
2581
2590
  case "map-memories":
2582
2591
  return countUnmappedActiveMemories(db, project) > 0;
2583
2592
  case "verify":
2584
- return countActiveMemories(db, project) > 0;
2593
+ return countLiveMemories(db, project) > 0;
2585
2594
  case "verify-broad":
2586
- return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countActiveMemories(db, project) > 0;
2595
+ return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countLiveMemories(db, project) > 0;
2587
2596
  case "curate":
2588
2597
  return countActiveMemories(db, project) > 0;
2589
2598
  case "compress-cues":
2590
- return countActiveMemories(db, project) > 0;
2599
+ return countLiveMemories(db, project) > 0;
2591
2600
  case "classify-memories":
2592
- return countActiveMemories(db, project) > 0;
2601
+ return countLiveMemories(db, project) > 0;
2593
2602
  case "retrospective":
2594
2603
  return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
2595
2604
  case "maintain-docs":
@@ -7049,6 +7058,116 @@ Output exactly JSON: {"met": false}`;
7049
7058
  }
7050
7059
  }
7051
7060
 
7061
+ // ../plugin/src/features/magic-context/dreamer/expire-memories.ts
7062
+ var MODULE_ARCHIVE_BATCH_SIZE = 100;
7063
+ var EXPIRED_ARCHIVE_REASON = "expired";
7064
+ function getExpiredActiveMemoryIds(db, projectIdentity, now = Date.now()) {
7065
+ const rows = db.prepare(`SELECT id
7066
+ FROM memories
7067
+ WHERE project_path = ?
7068
+ AND status = 'active'
7069
+ AND expires_at IS NOT NULL
7070
+ AND expires_at <= ?
7071
+ ORDER BY id`).all(projectIdentity, now);
7072
+ return rows.map((row) => row.id);
7073
+ }
7074
+ function assertSuccessfulModuleArchive(response) {
7075
+ const result = response && typeof response === "object" && "result" in response ? response.result : response;
7076
+ if (result && typeof result === "object") {
7077
+ const record = result;
7078
+ if (record.ok === false || record.error) {
7079
+ throw new Error("module rejected expired-memory archive");
7080
+ }
7081
+ }
7082
+ }
7083
+ async function archiveExpiredThroughModule(args) {
7084
+ if (!args.moduleRoute.moduleClient.mirrorPull) {
7085
+ throw new DreamerModuleFailureError("mirror.pull expired archive", new Error("Rust dreamer client omitted the memory mirror route"));
7086
+ }
7087
+ for (let offset = 0;offset < args.expiredContextIds.length; offset += MODULE_ARCHIVE_BATCH_SIZE) {
7088
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7089
+ throw new Error("Dream lease lost before expired-memory archive");
7090
+ }
7091
+ const contextBatch = args.expiredContextIds.slice(offset, offset + MODULE_ARCHIVE_BATCH_SIZE);
7092
+ const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, contextBatch);
7093
+ if (identities.size !== contextBatch.length) {
7094
+ throw new DreamerModuleFailureError("ctx_memory expired archive", new Error("expired memory is missing its module mirror identity"));
7095
+ }
7096
+ const moduleBatch = contextBatch.map((id) => {
7097
+ const identity = identities.get(id);
7098
+ if (!identity)
7099
+ throw new Error(`missing module identity for expired memory ${id}`);
7100
+ return identity.moduleId;
7101
+ });
7102
+ try {
7103
+ const response = await args.moduleRoute.moduleClient.call({
7104
+ sessionId: args.moduleRoute.moduleSessionId,
7105
+ projectRoot: args.moduleRoute.moduleProjectRoot,
7106
+ method: "ctx_memory",
7107
+ body: {
7108
+ name: "ctx_memory",
7109
+ arguments: {
7110
+ action: "archive",
7111
+ memory_project: args.projectIdentity,
7112
+ ids: moduleBatch,
7113
+ reason: EXPIRED_ARCHIVE_REASON,
7114
+ command_id: `${args.moduleRoute.moduleCommandId}:expire:${offset / MODULE_ARCHIVE_BATCH_SIZE}`
7115
+ }
7116
+ }
7117
+ });
7118
+ assertSuccessfulModuleArchive(response);
7119
+ } catch (error) {
7120
+ throw new DreamerModuleFailureError("ctx_memory expired archive", error);
7121
+ }
7122
+ }
7123
+ const mirrorPull = args.moduleRoute.moduleClient.mirrorPull;
7124
+ const drained = await drainMirrorPages({
7125
+ db: args.db,
7126
+ module: {
7127
+ mirrorPull: (request) => mirrorPull({ ...request, projectRoot: args.moduleRoute.moduleProjectRoot })
7128
+ },
7129
+ domain: "memories",
7130
+ limit: 1000
7131
+ });
7132
+ if (!drained.complete) {
7133
+ throw new DreamerModuleFailureError("mirror.pull expired archive", new Error("memory mirror did not reach the module cursor"));
7134
+ }
7135
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7136
+ throw new Error("Dream lease lost during expired-memory archive");
7137
+ }
7138
+ return args.expiredContextIds.length;
7139
+ }
7140
+ async function archiveExpiredMemories(args) {
7141
+ const expiredContextIds = getExpiredActiveMemoryIds(args.db, args.projectIdentity, args.now);
7142
+ if (expiredContextIds.length === 0) {
7143
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7144
+ throw new Error("Dream lease lost before expired-memory probe");
7145
+ }
7146
+ return 0;
7147
+ }
7148
+ if (args.moduleRoute) {
7149
+ return archiveExpiredThroughModule({
7150
+ ...args,
7151
+ expiredContextIds,
7152
+ moduleRoute: args.moduleRoute
7153
+ });
7154
+ }
7155
+ const selectedIds = new Set(expiredContextIds);
7156
+ return runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
7157
+ const stillExpired = getExpiredActiveMemoryIds(args.db, args.projectIdentity, args.now).filter((id) => selectedIds.has(id));
7158
+ for (const id of stillExpired) {
7159
+ archiveMemory(args.db, id, EXPIRED_ARCHIVE_REASON);
7160
+ queueMemoryMutation(args.db, {
7161
+ projectPath: args.projectIdentity,
7162
+ mutationType: "archive",
7163
+ targetMemoryId: id,
7164
+ queuedAt: args.now
7165
+ });
7166
+ }
7167
+ return stillExpired.length;
7168
+ });
7169
+ }
7170
+
7052
7171
  // ../plugin/src/features/magic-context/dreamer/maintain-docs-protected-enforcement.ts
7053
7172
  import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
7054
7173
  import { join as join2 } from "node:path";
@@ -9388,6 +9507,9 @@ function inspectCurateMemoryOperations(messages) {
9388
9507
  }
9389
9508
  return summary;
9390
9509
  }
9510
+ function formatExpiredArchiveProgress(count) {
9511
+ return `curate: archived ${count} expired ${count === 1 ? "memory" : "memories"}`;
9512
+ }
9391
9513
  function formatCurateMemoryOperations(actions) {
9392
9514
  const actionCounts = new Map;
9393
9515
  for (const action of actions)
@@ -9438,7 +9560,7 @@ function createDreamTaskExecutor(deps) {
9438
9560
  };
9439
9561
  const parent = await resolveParentSessionId();
9440
9562
  let moduleRoute;
9441
- if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "classify-memories" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
9563
+ if (config.task === "curate" || config.task === "map-memories" || config.task === "compress-cues" || config.task === "classify-memories" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
9442
9564
  try {
9443
9565
  moduleRoute = await resolveDreamerModuleRoute({
9444
9566
  db,
@@ -9767,7 +9889,9 @@ function createDreamTaskExecutor(deps) {
9767
9889
  parent,
9768
9890
  recordRun,
9769
9891
  computeMemoryDelta,
9770
- reportProgress
9892
+ reportProgress,
9893
+ leaseAcquisition,
9894
+ moduleRoute
9771
9895
  });
9772
9896
  } catch (error) {
9773
9897
  const { transient, brief } = classifyFailure(error);
@@ -10068,17 +10192,6 @@ async function runAgenticTask(config, ctx, helpers) {
10068
10192
  architecture: existsSync5(`${docsDir}/ARCHITECTURE.md`),
10069
10193
  structure: existsSync5(`${docsDir}/STRUCTURE.md`)
10070
10194
  } : undefined;
10071
- let curateMemories;
10072
- if (task === "curate") {
10073
- curateMemories = loadActiveMemoryPromptMemories(db, projectIdentity);
10074
- log(`[dreamer] curate pool: in_scope=${curateMemories.length}`);
10075
- }
10076
- const taskPrompt = buildDreamTaskPrompt(task, {
10077
- projectPath: projectIdentity,
10078
- lastDreamAt: lastRunAt ? String(lastRunAt) : null,
10079
- existingDocs,
10080
- curate: curateMemories ? { memories: curateMemories } : undefined
10081
- });
10082
10195
  const abortController = new AbortController;
10083
10196
  let leaseLost = false;
10084
10197
  const heartbeat = startLeaseHeartbeat(db, holderId, leaseKey, () => {
@@ -10087,7 +10200,37 @@ async function runAgenticTask(config, ctx, helpers) {
10087
10200
  }, ctx.leaseAcquisition);
10088
10201
  let childSessionId = null;
10089
10202
  let promptSettled = false;
10203
+ let expiredArchived = 0;
10090
10204
  try {
10205
+ let curateMemories;
10206
+ if (task === "curate") {
10207
+ expiredArchived = await archiveExpiredMemories({
10208
+ db,
10209
+ projectIdentity,
10210
+ holderId,
10211
+ leaseKey,
10212
+ leaseAcquisition: helpers.leaseAcquisition,
10213
+ moduleRoute: helpers.moduleRoute
10214
+ });
10215
+ if (leaseLost)
10216
+ throw new Error("Dream lease lost during expired-memory archive");
10217
+ curateMemories = loadActiveMemoryPromptMemories(db, projectIdentity);
10218
+ log(`[dreamer] curate pool: in_scope=${curateMemories.length} expired_archived=${expiredArchived}`);
10219
+ if (curateMemories.length === 0 && expiredArchived > 0) {
10220
+ const progress = formatExpiredArchiveProgress(expiredArchived);
10221
+ helpers.recordRun("completed", null, {
10222
+ memoryChanges: helpers.computeMemoryDelta(memoryBefore),
10223
+ progress
10224
+ });
10225
+ return { status: "completed", detail: progress };
10226
+ }
10227
+ }
10228
+ const taskPrompt = buildDreamTaskPrompt(task, {
10229
+ projectPath: projectIdentity,
10230
+ lastDreamAt: lastRunAt ? String(lastRunAt) : null,
10231
+ existingDocs,
10232
+ curate: curateMemories ? { memories: curateMemories } : undefined
10233
+ });
10091
10234
  const createResponse = await createChildSessionWithFence({
10092
10235
  client: deps.client,
10093
10236
  db,
@@ -10179,6 +10322,7 @@ async function runAgenticTask(config, ctx, helpers) {
10179
10322
  }
10180
10323
  const curateOutput = task === "curate" ? run.validated : undefined;
10181
10324
  const progress = [
10325
+ expiredArchived > 0 ? formatExpiredArchiveProgress(expiredArchived) : null,
10182
10326
  curateOutput && curateOutput.memoryOperations.completedActions.length > 0 ? formatCurateMemoryOperations(curateOutput.memoryOperations.completedActions) : null,
10183
10327
  curateRefused > 0 ? `curate: refused ${curateRefused} unsafe mutation(s)` : null
10184
10328
  ].filter((value) => Boolean(value)).join("; ");
@@ -14210,78 +14354,93 @@ function buildReplacementContent(tagId) {
14210
14354
  }
14211
14355
  function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set) {
14212
14356
  let didMutateMessage = false;
14213
- db.transaction(() => {
14214
- const tags = preloadedTags ?? getTagsBySession(db, sessionId);
14215
- const tagStatusById = new Map(tags.map((tag) => [tag.tagNumber, tag.status]));
14216
- const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
14217
- const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
14218
- const opsToApply = [
14219
- ...pendingOps.map((op) => ({ op, synthetic: false })),
14220
- ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
14221
- ];
14222
- const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
14223
- for (const { op: pendingOp, synthetic } of opsToApply) {
14224
- const tagStatus = tagStatusById.get(pendingOp.tagId);
14225
- if (tagStatus === "compacted" || tagStatus === "dropped") {
14226
- if (!synthetic)
14227
- removePendingOp(db, sessionId, pendingOp.tagId);
14228
- continue;
14229
- }
14230
- if (protectedTagIds.has(pendingOp.tagId)) {
14231
- continue;
14232
- }
14233
- const target = targets.get(pendingOp.tagId);
14234
- const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
14235
- if (synthetic) {
14236
- if (!isToolTag || target?.canDrop?.() !== true)
14357
+ let admitted = false;
14358
+ let startedAt = 0;
14359
+ try {
14360
+ db.transaction(() => {
14361
+ admitted = true;
14362
+ startedAt = performance.now();
14363
+ const tags = preloadedTags ?? getTagsBySession(db, sessionId);
14364
+ const tagStatusById = new Map(tags.map((tag) => [tag.tagNumber, tag.status]));
14365
+ const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
14366
+ const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
14367
+ const opsToApply = [
14368
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
14369
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
14370
+ ];
14371
+ const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
14372
+ for (const { op: pendingOp, synthetic } of opsToApply) {
14373
+ const tagStatus = tagStatusById.get(pendingOp.tagId);
14374
+ if (tagStatus === "compacted" || tagStatus === "dropped") {
14375
+ if (!synthetic)
14376
+ removePendingOp(db, sessionId, pendingOp.tagId);
14237
14377
  continue;
14238
- }
14239
- let shouldPersistDrop = false;
14240
- if (isToolTag) {
14241
- if (editMarkerTagIds.has(pendingOp.tagId)) {
14242
- const markResult = target?.editMarker?.() ?? "absent";
14243
- if (markResult === "incomplete" || markResult === "absent") {
14244
- continue;
14245
- }
14246
- didMutateMessage = true;
14247
- updateTagDropMode(db, sessionId, pendingOp.tagId, "edit_marker");
14248
- shouldPersistDrop = true;
14249
- } else if (skeletonWindow.has(pendingOp.tagId)) {
14250
- const truncResult = target?.truncate?.() ?? "absent";
14251
- if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
14378
+ }
14379
+ if (protectedTagIds.has(pendingOp.tagId)) {
14380
+ continue;
14381
+ }
14382
+ const target = targets.get(pendingOp.tagId);
14383
+ const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
14384
+ if (synthetic) {
14385
+ if (!isToolTag || target?.canDrop?.() !== true)
14252
14386
  continue;
14253
- }
14254
- if (truncResult === "truncated") {
14387
+ }
14388
+ let shouldPersistDrop = false;
14389
+ if (isToolTag) {
14390
+ if (editMarkerTagIds.has(pendingOp.tagId)) {
14391
+ const markResult = target?.editMarker?.() ?? "absent";
14392
+ if (markResult === "incomplete" || markResult === "absent") {
14393
+ continue;
14394
+ }
14255
14395
  didMutateMessage = true;
14396
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "edit_marker");
14397
+ shouldPersistDrop = true;
14398
+ } else if (skeletonWindow.has(pendingOp.tagId)) {
14399
+ const truncResult = target?.truncate?.() ?? "absent";
14400
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
14401
+ continue;
14402
+ }
14403
+ if (truncResult === "truncated") {
14404
+ didMutateMessage = true;
14405
+ }
14406
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
14407
+ shouldPersistDrop = true;
14408
+ } else {
14409
+ const dropResult = target?.drop?.() ?? "absent";
14410
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
14411
+ continue;
14412
+ }
14413
+ if (dropResult === "removed") {
14414
+ didMutateMessage = true;
14415
+ }
14416
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
14417
+ shouldPersistDrop = true;
14256
14418
  }
14257
- updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
14258
- shouldPersistDrop = true;
14259
- } else {
14260
- const dropResult = target?.drop?.() ?? "absent";
14261
- if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
14262
- continue;
14263
- }
14264
- if (dropResult === "removed") {
14419
+ } else if (target) {
14420
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
14421
+ if (changed)
14265
14422
  didMutateMessage = true;
14266
- }
14267
- updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
14423
+ shouldPersistDrop = true;
14424
+ } else if (!synthetic) {
14268
14425
  shouldPersistDrop = true;
14269
14426
  }
14270
- } else if (target) {
14271
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
14272
- if (changed)
14273
- didMutateMessage = true;
14274
- shouldPersistDrop = true;
14275
- } else if (!synthetic) {
14276
- shouldPersistDrop = true;
14427
+ if (!shouldPersistDrop)
14428
+ continue;
14429
+ updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
14430
+ if (!synthetic)
14431
+ removePendingOp(db, sessionId, pendingOp.tagId);
14277
14432
  }
14278
- if (!shouldPersistDrop)
14279
- continue;
14280
- updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
14281
- if (!synthetic)
14282
- removePendingOp(db, sessionId, pendingOp.tagId);
14433
+ }).immediate();
14434
+ } catch (error) {
14435
+ if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
14436
+ sessionLog(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
14437
+ return false;
14283
14438
  }
14284
- })();
14439
+ throw error;
14440
+ } finally {
14441
+ if (admitted)
14442
+ logSlowWriteTransaction("apply_pending_operations", startedAt);
14443
+ }
14285
14444
  return didMutateMessage;
14286
14445
  }
14287
14446
  function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
@@ -16456,11 +16615,16 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton) {
16456
16615
  const complete = occurrences.some((occ) => occ.kind === "tool_use") && occurrences.some((occ) => occ.kind === "tool_result");
16457
16616
  if (!complete)
16458
16617
  return "incomplete";
16459
- if (!requiresToolArcSkeleton && occurrences.every((occ) => occ.part.remove && occ.part.canRemove?.() !== false)) {
16460
- let removed = false;
16461
- for (const occ of occurrences)
16462
- removed = occ.part.remove?.() || removed;
16463
- return removed ? "removed" : "absent";
16618
+ if (!requiresToolArcSkeleton) {
16619
+ const authorization = occurrences.map((occ) => occ.part.canRemove?.());
16620
+ if (authorization.includes("defer"))
16621
+ return "incomplete";
16622
+ if (occurrences.every((occ, index) => occ.part.remove && authorization[index] !== false)) {
16623
+ let removed = false;
16624
+ for (const occ of occurrences)
16625
+ removed = occ.part.remove?.() || removed;
16626
+ return removed ? "removed" : "absent";
16627
+ }
16464
16628
  }
16465
16629
  const sentinel = `[dropped §${tagId}§]`;
16466
16630
  let any = false;
@@ -21104,7 +21268,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
21104
21268
  emergencyDroppedTools++;
21105
21269
  }
21106
21270
  }
21107
- })();
21271
+ }).immediate();
21108
21272
  sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
21109
21273
  } else {
21110
21274
  sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
@@ -21133,7 +21297,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
21133
21297
  droppedStaleReduceCalls++;
21134
21298
  }
21135
21299
  }
21136
- })();
21300
+ }).immediate();
21137
21301
  }
21138
21302
  if (routine) {
21139
21303
  db.transaction(() => {
@@ -21170,7 +21334,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
21170
21334
  }
21171
21335
  }
21172
21336
  }
21173
- })();
21337
+ }).immediate();
21174
21338
  }
21175
21339
  const toolFingerprints = routine ? buildPiToolFingerprints(piMessages, resolveStableId) : new Map;
21176
21340
  if (toolFingerprints.size > 0) {
@@ -21208,7 +21372,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
21208
21372
  }
21209
21373
  }
21210
21374
  }
21211
- })();
21375
+ }).immediate();
21212
21376
  }
21213
21377
  if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
21214
21378
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
@@ -23544,6 +23708,23 @@ function applyNativeReasoningReplayPi(args, saved) {
23544
23708
  args.messages[index] = message;
23545
23709
  return nextIds.size;
23546
23710
  }
23711
+ function authorizePiToolRemoval(args) {
23712
+ if (!args.saved)
23713
+ return false;
23714
+ if (args.saved.get(args.callId) === NATIVE_TOOL_REMOVAL_MARKER)
23715
+ return true;
23716
+ if (!args.canApply)
23717
+ return false;
23718
+ try {
23719
+ saveNativeToolInputs(args.db, args.sessionId, new Map([[args.callId, NATIVE_TOOL_REMOVAL_MARKER]]));
23720
+ args.saved.set(args.callId, NATIVE_TOOL_REMOVAL_MARKER);
23721
+ return true;
23722
+ } catch (error) {
23723
+ sessionLog(args.sessionId, `tool arc removal persistence failed; retaining pair: ${String(error)}`);
23724
+ const priorDrop = args.db.prepare("SELECT 1 FROM tags WHERE session_id = ? AND type = 'tool' AND message_id = ? AND status = 'dropped' LIMIT 1").get(args.sessionId, args.callId);
23725
+ return priorDrop ? false : "defer";
23726
+ }
23727
+ }
23547
23728
 
23548
23729
  // src/note-visibility-pi.ts
23549
23730
  var NOTE_TOOL_NAME = "ctx_note";
@@ -23703,13 +23884,38 @@ function parsePersistedLkgSlot(row) {
23703
23884
  const jsonPrefix = record.json_prefix;
23704
23885
  const lastInputMessageId = record.last_input_message_id;
23705
23886
  const capturedAt = record.captured_at;
23706
- const inputIdSeq = parseStringArray(record.input_id_seq);
23887
+ let inputIdsRaw = record.input_id_seq;
23888
+ let piOutputEntryIds;
23889
+ if (typeof inputIdsRaw === "string") {
23890
+ try {
23891
+ const metadata = JSON.parse(inputIdsRaw);
23892
+ if (metadata && !Array.isArray(metadata) && metadata.version === 1 && Array.isArray(metadata.piOutputEntryIds)) {
23893
+ if (!metadata.piOutputEntryIds.every((id) => id === null || typeof id === "string" && id.length > 0))
23894
+ return;
23895
+ piOutputEntryIds = metadata.piOutputEntryIds;
23896
+ inputIdsRaw = JSON.stringify(metadata.inputIds);
23897
+ }
23898
+ } catch {
23899
+ return;
23900
+ }
23901
+ }
23902
+ const inputIdSeq = parseStringArray(inputIdsRaw);
23707
23903
  const inputContentDigests = parseStringArray(record.input_content_digests);
23708
23904
  const modelKey = parseNullableString(record.model_key);
23709
23905
  const providerKey = parseNullableString(record.provider_key);
23710
23906
  if (typeof jsonPrefix !== "string" || typeof lastInputMessageId !== "string" || lastInputMessageId.length === 0 || typeof capturedAt !== "number" || !Number.isFinite(capturedAt) || inputIdSeq === null || inputContentDigests === null || inputContentDigests.length !== inputIdSeq.length || modelKey === undefined || providerKey === undefined) {
23711
23907
  return;
23712
23908
  }
23909
+ if (piOutputEntryIds) {
23910
+ try {
23911
+ const output = JSON.parse(jsonPrefix);
23912
+ const inputs = new Set(inputIdSeq);
23913
+ if (!Array.isArray(output) || output.length !== piOutputEntryIds.length || piOutputEntryIds.some((id) => id !== null && !inputs.has(id)))
23914
+ return;
23915
+ } catch {
23916
+ return;
23917
+ }
23918
+ }
23713
23919
  let inputContentSignatures;
23714
23920
  if (record.input_content_signatures !== null && record.input_content_signatures !== undefined) {
23715
23921
  const parsed = parseStringArray(record.input_content_signatures);
@@ -23728,6 +23934,8 @@ function parsePersistedLkgSlot(row) {
23728
23934
  };
23729
23935
  if (inputContentSignatures)
23730
23936
  slot.inputContentSignatures = inputContentSignatures;
23937
+ if (piOutputEntryIds)
23938
+ slot.piOutputEntryIds = piOutputEntryIds;
23731
23939
  const rowVersion = parseNullableInteger(record.row_version);
23732
23940
  if (rowVersion !== undefined)
23733
23941
  slot.rowVersion = rowVersion;
@@ -23753,7 +23961,11 @@ function saveLkgSlotToDb(db, sessionId, slot) {
23753
23961
  provider_key = excluded.provider_key,
23754
23962
  captured_at = excluded.captured_at,
23755
23963
  row_version = excluded.row_version,
23756
- capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
23964
+ capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.piOutputEntryIds ? {
23965
+ version: 1,
23966
+ inputIds: slot.inputIdSeq,
23967
+ piOutputEntryIds: slot.piOutputEntryIds
23968
+ } : slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
23757
23969
  return true;
23758
23970
  } catch (error) {
23759
23971
  sessionLog(sessionId, "LKG snapshot persistence failed (in-memory slot retained):", error);
@@ -23812,7 +24024,8 @@ function registerLkgPersistence(backend) {
23812
24024
  }
23813
24025
  function slotBytes(slot) {
23814
24026
  const digestBytes = slot.inputContentDigests.reduce((total, digest) => total + 2 * digest.length, 0);
23815
- return 2 * slot.jsonPrefix.length + digestBytes + LKG_METADATA_BYTES;
24027
+ const ownershipBytes = slot.piOutputEntryIds?.reduce((total, id) => total + (id?.length ?? 0) * 2 + 8, 0) ?? 0;
24028
+ return 2 * slot.jsonPrefix.length + digestBytes + ownershipBytes + LKG_METADATA_BYTES;
23816
24029
  }
23817
24030
  var LKG_SNAPSHOT_ARRAY = Symbol("array");
23818
24031
  var LKG_SNAPSHOT_OBJECT = Symbol("object");
@@ -23973,6 +24186,7 @@ function captureSlot(sessionId, slot) {
23973
24186
  const entry = {
23974
24187
  slot: {
23975
24188
  ...slot,
24189
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
23976
24190
  inputIdSeq: [...slot.inputIdSeq],
23977
24191
  inputContentDigests: [...slot.inputContentDigests],
23978
24192
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -24011,6 +24225,7 @@ function installHydratedSlot(sessionId, slot) {
24011
24225
  const entry = {
24012
24226
  slot: {
24013
24227
  ...slot,
24228
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
24014
24229
  inputIdSeq: [...slot.inputIdSeq],
24015
24230
  inputContentDigests: [...slot.inputContentDigests],
24016
24231
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -24043,6 +24258,7 @@ function hydrateSlotFromPersistence(sessionId) {
24043
24258
  function copySlotForRead(slot) {
24044
24259
  return {
24045
24260
  ...slot,
24261
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
24046
24262
  inputIdSeq: [...slot.inputIdSeq],
24047
24263
  inputContentDigests: [...slot.inputContentDigests],
24048
24264
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -24521,16 +24737,13 @@ function snapshotInputs(messages, entryIds) {
24521
24737
  }
24522
24738
  const inputs = [];
24523
24739
  const seen = new Set;
24524
- for (let index = firstStableIndex;index < entryIds.length; index += 1) {
24525
- const id = entryIds[index];
24526
- if (typeof id !== "string" || id.length === 0) {
24527
- return { inputs: [], failure: "lkg_entry_id_gap" };
24528
- }
24529
- if (seen.has(id))
24530
- return { inputs: [], failure: "lkg_duplicate_entry_id" };
24740
+ for (let index = 0;index < entryIds.length; index += 1) {
24531
24741
  const fields = lkgContentFields(messages[index]);
24532
24742
  if (!fields)
24533
24743
  return { inputs: [], failure: "lkg_content_snapshot_failed" };
24744
+ const id = entryIds[index] || `pi-lkg-unmapped:${lkgContentDigestFromFields(fields)}`;
24745
+ if (seen.has(id))
24746
+ return { inputs: [], failure: "lkg_duplicate_entry_id" };
24534
24747
  seen.add(id);
24535
24748
  inputs.push({ id, messageIndex: index, fields });
24536
24749
  }
@@ -24626,6 +24839,29 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24626
24839
  if (snapshot.replayAnchorInputIndex === null || snapshot.pristineTail === null) {
24627
24840
  return { ok: false, reason: "lkg_miss" };
24628
24841
  }
24842
+ const slot = getSlot(snapshot.sessionId);
24843
+ const start = slot?.inputIdSeq.indexOf(snapshot.inputs[0]?.id ?? "") ?? -1;
24844
+ if (slot && start > 0) {
24845
+ const ownership = slot.piOutputEntryIds;
24846
+ if (!ownership)
24847
+ return { ok: false, reason: "lkg_output_mapping_unavailable" };
24848
+ if (slot.modelKey !== snapshot.modelKey || slot.providerKey !== snapshot.providerKey)
24849
+ return { ok: false, reason: "lkg_model_mismatch" };
24850
+ const surviving = snapshot.inputs.slice(0, snapshot.replayAnchorInputIndex + 1);
24851
+ if (surviving.length !== slot.inputIdSeq.length - start || surviving.some((input, index) => input.id !== slot.inputIdSeq[start + index]))
24852
+ return { ok: false, reason: "lkg_invalidated_reshape" };
24853
+ if (surviving.some((input, index) => lkgContentDigestFromFields(input.fields) !== slot.inputContentDigests[start + index]))
24854
+ return { ok: false, reason: "lkg_content_mismatch" };
24855
+ const removed = new Set(slot.inputIdSeq.slice(0, start));
24856
+ const prefix = JSON.parse(slot.jsonPrefix);
24857
+ return {
24858
+ ok: true,
24859
+ messages: [
24860
+ ...prefix.filter((_, index) => ownership[index] === null || !removed.has(ownership[index] ?? "")),
24861
+ ...snapshot.pristineTail
24862
+ ]
24863
+ };
24864
+ }
24629
24865
  const entry = {
24630
24866
  pristineTail: snapshot.pristineTail,
24631
24867
  entryInputIds: snapshot.inputs.map((input) => input.id),
@@ -24662,6 +24898,22 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24662
24898
  return;
24663
24899
  }
24664
24900
  const state = stateFor(snapshot.sessionId);
24901
+ const idsByDigest = new Map;
24902
+ if (!args.outputEntryIds)
24903
+ for (const input of snapshot.inputs) {
24904
+ const digest = lkgContentDigestFromFields(input.fields);
24905
+ idsByDigest.set(digest, idsByDigest.has(digest) ? undefined : input.id);
24906
+ }
24907
+ const inferredIds = args.outputEntryIds ?? args.outputMessages.map((message) => {
24908
+ const fields = lkgContentFields(message);
24909
+ if (!fields)
24910
+ return;
24911
+ const digest = lkgContentDigestFromFields(fields);
24912
+ return idsByDigest.get(digest);
24913
+ });
24914
+ const outputIds = args.outputEntryIds ?? inferredIds;
24915
+ const inputIds = new Set(snapshot.inputs.map((input) => input.id));
24916
+ const ownership = outputIds.length === args.outputMessages.length && outputIds.every((id) => id === null || typeof id === "string" && inputIds.has(id)) ? [...outputIds] : undefined;
24665
24917
  state.captureSequence += 1;
24666
24918
  const plan = {
24667
24919
  sessionId: snapshot.sessionId,
@@ -24702,6 +24954,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24702
24954
  reusedPrefix = incremental.reusedPrefix;
24703
24955
  const slot = {
24704
24956
  jsonPrefix: plan.jsonPrefix,
24957
+ piOutputEntryIds: ownership,
24705
24958
  inputIdSeq,
24706
24959
  inputContentDigests: incremental.digests,
24707
24960
  inputContentSignatures,
@@ -24749,6 +25002,24 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24749
25002
  };
24750
25003
  return { beginPass, replay, captureAppliedPass };
24751
25004
  }
25005
+ function resolvePiLkgOutputEntryIds(messages, syntheticLeadingCount, entryId) {
25006
+ const ids = messages.map((message, index) => index < syntheticLeadingCount ? null : isRecord(message) ? entryId(message) : undefined);
25007
+ const syntheticOwners = new Map;
25008
+ for (let index = 0;index < messages.length; index++) {
25009
+ const message = messages[index];
25010
+ const owner = ids[index];
25011
+ if (typeof owner !== "string" || !isRecord(message) || message.role !== "assistant" || !Array.isArray(message.content))
25012
+ continue;
25013
+ for (const part of message.content) {
25014
+ if (isRecord(part) && part.type === "toolCall" && part.syntheticTodoMarker === true && typeof part.id === "string")
25015
+ syntheticOwners.set(part.id, syntheticOwners.has(part.id) ? undefined : owner);
25016
+ }
25017
+ }
25018
+ return ids.map((id, index) => {
25019
+ const message = messages[index];
25020
+ return id === undefined && isRecord(message) && message.role === "toolResult" && message.syntheticTodoMarker === true && typeof message.toolCallId === "string" ? syntheticOwners.get(message.toolCallId) : id;
25021
+ });
25022
+ }
24752
25023
 
24753
25024
  // src/pi-pressure.ts
24754
25025
  function extractAssistantUsage(message) {
@@ -26593,7 +26864,9 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
26593
26864
  canRemove() {
26594
26865
  const message = working[messageIndex];
26595
26866
  const part = message.content[partIndex];
26596
- return part?.type === "toolCall" && canRemoveNativeToolCall(message, part.id) && toolRemovalAuthorization.get(working)?.(part.id) !== false;
26867
+ if (part?.type !== "toolCall" || !canRemoveNativeToolCall(message, part.id))
26868
+ return false;
26869
+ return toolRemovalAuthorization.get(working)?.(part.id) ?? true;
26597
26870
  },
26598
26871
  remove() {
26599
26872
  return markToolRemoval(working, messageIndex, partIndex);
@@ -27627,6 +27900,9 @@ function registerPiContextHandler(pi, baseOptions) {
27627
27900
  modelKey: lkgModelKey,
27628
27901
  providerKey: lkgProviderKey
27629
27902
  });
27903
+ const lkgInputIdByRef = new Map;
27904
+ for (const input of lkgPassSnapshot.inputs)
27905
+ lkgInputIdByRef.set(event.messages[input.messageIndex], input.id);
27630
27906
  const thinkingBindingRecoveryApplied = applyPiThinkingBindingRecovery({
27631
27907
  db: options.db,
27632
27908
  sessionId,
@@ -27935,6 +28211,7 @@ function registerPiContextHandler(pi, baseOptions) {
27935
28211
  projectDirectory,
27936
28212
  sessionMeta,
27937
28213
  messages: event.messages,
28214
+ lkgEntryIds: event.messages.map((message) => lkgInputIdByRef.get(message)),
27938
28215
  smartDrops: options.smartDrops === true,
27939
28216
  protectedTags: options.protectedTags ?? 20,
27940
28217
  protectedTokens: options.protectedTokens,
@@ -28214,6 +28491,7 @@ function registerPiContextHandler(pi, baseOptions) {
28214
28491
  lkgCoordinator.captureAppliedPass({
28215
28492
  snapshot: lkgPassSnapshot,
28216
28493
  outputMessages,
28494
+ outputEntryIds: resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.postCommitEntryIdByRef.get(message) ?? result.lkgEntryIdByRef.get(message)),
28217
28495
  cacheBusting: result.bustedThisPass
28218
28496
  });
28219
28497
  }
@@ -28774,7 +29052,8 @@ async function runCompactionOffPipeline(args) {
28774
29052
  protectedTagNumbers: new Set,
28775
29053
  pendingDropTagNumbers: new Set
28776
29054
  },
28777
- postCommitEntryIdByRef: new Map
29055
+ postCommitEntryIdByRef: new Map,
29056
+ lkgEntryIdByRef: new Map
28778
29057
  };
28779
29058
  }
28780
29059
  async function runPipeline(args) {
@@ -28816,22 +29095,13 @@ async function runPipeline(args) {
28816
29095
  const tTranscriptBuild = performance.now();
28817
29096
  const transcript = createPiTranscript(args.messages, args.sessionId, args.entryIds, {
28818
29097
  preserveReasoningToolArcs: args.reasoningClearing?.preserveReasoningToolArcs,
28819
- authorizeToolRemoval: (callId) => {
28820
- if (!nativeRemovalInputs)
28821
- return false;
28822
- if (nativeRemovalInputs.get(callId) === NATIVE_TOOL_REMOVAL_MARKER)
28823
- return true;
28824
- if (!isCacheBustingPass)
28825
- return false;
28826
- try {
28827
- saveNativeToolInputs(args.db, args.sessionId, new Map([[callId, NATIVE_TOOL_REMOVAL_MARKER]]));
28828
- nativeRemovalInputs.set(callId, NATIVE_TOOL_REMOVAL_MARKER);
28829
- return true;
28830
- } catch (error) {
28831
- sessionLog(args.sessionId, `tool arc removal persistence failed; retaining pair: ${String(error)}`);
28832
- return false;
28833
- }
28834
- }
29098
+ authorizeToolRemoval: (callId) => authorizePiToolRemoval({
29099
+ db: args.db,
29100
+ sessionId: args.sessionId,
29101
+ callId,
29102
+ saved: nativeRemovalInputs,
29103
+ canApply: isCacheBustingPass
29104
+ })
28835
29105
  });
28836
29106
  logTransformTiming(args.sessionId, "transcriptBuild", tTranscriptBuild);
28837
29107
  const workingMessages = transcript.getWorkingMessages();
@@ -29377,6 +29647,7 @@ async function runPipeline(args) {
29377
29647
  const tPostCommitStableIdMaps = performance.now();
29378
29648
  const postCommitStableIdByRef = new Map;
29379
29649
  const postCommitEntryIdByRef = new Map;
29650
+ const lkgEntryIdByRef = new Map;
29380
29651
  for (let i = 0;i < args.messages.length; i++) {
29381
29652
  const m = args.messages[i];
29382
29653
  if (!m || typeof m !== "object")
@@ -29384,6 +29655,9 @@ async function runPipeline(args) {
29384
29655
  const id = resolvePiStableId(m, i, args.entryIds, args.entryIdByRef ?? undefined);
29385
29656
  if (id)
29386
29657
  postCommitStableIdByRef.set(m, id);
29658
+ const lkgId = args.lkgEntryIds?.[i];
29659
+ if (lkgId)
29660
+ lkgEntryIdByRef.set(m, lkgId);
29387
29661
  const realId = args.entryIds?.[i];
29388
29662
  if (typeof realId === "string" && realId.length > 0) {
29389
29663
  postCommitEntryIdByRef.set(m, realId);
@@ -29548,7 +29822,8 @@ async function runPipeline(args) {
29548
29822
  reasoningWatermark: args.sessionMeta.clearedReasoningThroughTag ?? 0,
29549
29823
  activeTags,
29550
29824
  channelBaselineSnapshot,
29551
- postCommitEntryIdByRef
29825
+ postCommitEntryIdByRef,
29826
+ lkgEntryIdByRef
29552
29827
  };
29553
29828
  }
29554
29829
  var AUTO_SEARCH_NO_HINT_REASONS = new Set([
@@ -32364,7 +32639,7 @@ function formatTailHygiene(status) {
32364
32639
  // package.json
32365
32640
  var package_default = {
32366
32641
  name: "@cortexkit/pi-magic-context",
32367
- version: "0.42.1",
32642
+ version: "0.42.2",
32368
32643
  type: "module",
32369
32644
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
32370
32645
  main: "dist/index.js",
@@ -32395,7 +32670,7 @@ var package_default = {
32395
32670
  build: "tsc -p ../retina-local-fs/tsconfig.build.json && rm -f dist/index.js dist/subagent-entry.js dist/historian-calibration-extension.js dist/transformers-web.js dist/transformers-node-wasm.js dist/*-*.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts --outdir dist --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
32396
32671
  "build:e2e-argv": "bun build src/subagent-runner.ts --outfile ../../tests/docker/.generated/subagent-runner-e2e.mjs --target node --format esm --external node:sqlite",
32397
32672
  typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
32398
- test: "bun install --frozen-lockfile && bun test",
32673
+ test: "bun install --frozen-lockfile && bun test --parallel --timeout 30000",
32399
32674
  lint: "biome check src",
32400
32675
  "lint:fix": "biome check --write src",
32401
32676
  format: "biome format --write src",