@cortexkit/opencode-magic-context 0.43.2 → 0.44.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 (252) hide show
  1. package/README.md +18 -2
  2. package/dist/config/index.d.ts +13 -0
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/project-security.d.ts +6 -3
  5. package/dist/config/project-security.d.ts.map +1 -1
  6. package/dist/config/schema/magic-context.d.ts +27 -0
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/{conflict-warning-hook-hyk9y29x.js → conflict-warning-hook-48f7np9d.js} +5 -5
  9. package/dist/{derive-budgets-b8ybssh9.js → derive-budgets-8kgevxmx.js} +2 -2
  10. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  11. package/dist/features/magic-context/compartment-embedding.d.ts +3 -2
  12. package/dist/features/magic-context/compartment-embedding.d.ts.map +1 -1
  13. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  15. package/dist/features/magic-context/dreamer/curate-category-rotation.d.ts.map +1 -1
  16. package/dist/features/magic-context/dreamer/curate-memory-safety.d.ts.map +1 -1
  17. package/dist/features/magic-context/dreamer/failed-invocation-evidence.d.ts +13 -0
  18. package/dist/features/magic-context/dreamer/failed-invocation-evidence.d.ts.map +1 -0
  19. package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/lease.d.ts +5 -2
  21. package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  23. package/dist/features/magic-context/dreamer/memory-claim-safety.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/module-apply.d.ts +1 -0
  25. package/dist/features/magic-context/dreamer/module-apply.d.ts.map +1 -1
  26. package/dist/features/magic-context/dreamer/retrospective-learnings.d.ts.map +1 -1
  27. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +1 -1
  28. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
  29. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  30. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +2 -0
  31. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  32. package/dist/features/magic-context/dreamer/verify.d.ts +19 -0
  33. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  34. package/dist/features/magic-context/fail-closed-block.d.ts +7 -0
  35. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  36. package/dist/features/magic-context/index.d.ts +1 -0
  37. package/dist/features/magic-context/index.d.ts.map +1 -1
  38. package/dist/features/magic-context/install-instance-id.d.ts +33 -0
  39. package/dist/features/magic-context/install-instance-id.d.ts.map +1 -0
  40. package/dist/features/magic-context/memory/constants.d.ts.map +1 -1
  41. package/dist/features/magic-context/memory/embedding-backfill.d.ts.map +1 -1
  42. package/dist/features/magic-context/memory/single-store-embedding-drain.d.ts +35 -0
  43. package/dist/features/magic-context/memory/single-store-embedding-drain.d.ts.map +1 -0
  44. package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
  45. package/dist/features/magic-context/message-index.d.ts +23 -0
  46. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  47. package/dist/features/magic-context/migrations.d.ts +7 -0
  48. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  49. package/dist/features/magic-context/overflow-detection.d.ts +31 -8
  50. package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
  51. package/dist/features/magic-context/project-embedding-registry.d.ts +40 -1
  52. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  53. package/dist/features/magic-context/search.d.ts.map +1 -1
  54. package/dist/features/magic-context/session-decision-calibration.d.ts.map +1 -1
  55. package/dist/features/magic-context/storage-db.d.ts +1 -1
  56. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  57. package/dist/features/magic-context/storage-meta-persisted.d.ts +20 -5
  58. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  59. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  60. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  61. package/dist/features/magic-context/storage-notes.d.ts +1 -0
  62. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  63. package/dist/features/magic-context/storage-primers.d.ts.map +1 -1
  64. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  65. package/dist/features/magic-context/storage-unavailable-reason.d.ts +17 -0
  66. package/dist/features/magic-context/storage-unavailable-reason.d.ts.map +1 -0
  67. package/dist/features/magic-context/storage.d.ts +1 -1
  68. package/dist/features/magic-context/storage.d.ts.map +1 -1
  69. package/dist/features/magic-context/store-generation-rebase.d.ts +33 -1
  70. package/dist/features/magic-context/store-generation-rebase.d.ts.map +1 -1
  71. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  72. package/dist/features/magic-context/transform-decision-log.d.ts +1 -1
  73. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  74. package/dist/features/magic-context/types.d.ts +8 -3
  75. package/dist/features/magic-context/types.d.ts.map +1 -1
  76. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/apply-operations.d.ts +105 -9
  78. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  80. package/dist/hooks/magic-context/compartment-parser.d.ts +2 -0
  81. package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
  82. package/dist/hooks/magic-context/compartment-runner-historian.d.ts +1 -0
  83. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  86. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/compartment-runner-types.d.ts +2 -0
  88. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  89. package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
  90. package/dist/hooks/magic-context/context-limit-resolution.d.ts +31 -0
  91. package/dist/hooks/magic-context/context-limit-resolution.d.ts.map +1 -0
  92. package/dist/hooks/magic-context/embed-history-runner.d.ts +0 -1
  93. package/dist/hooks/magic-context/embed-history-runner.d.ts.map +1 -1
  94. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  95. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
  96. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -1
  97. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  98. package/dist/hooks/magic-context/historian-host-runner.d.ts +233 -0
  99. package/dist/hooks/magic-context/historian-host-runner.d.ts.map +1 -0
  100. package/dist/hooks/magic-context/hook.d.ts +1 -3
  101. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  102. package/dist/hooks/magic-context/image-token-estimate.d.ts +9 -0
  103. package/dist/hooks/magic-context/image-token-estimate.d.ts.map +1 -1
  104. package/dist/hooks/magic-context/inject-compartments.d.ts +60 -1
  105. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  106. package/dist/hooks/magic-context/lkg-slot.d.ts +6 -0
  107. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  108. package/dist/hooks/magic-context/module-client.d.ts +21 -0
  109. package/dist/hooks/magic-context/module-client.d.ts.map +1 -0
  110. package/dist/hooks/magic-context/module-state-sync.d.ts +6 -2
  111. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  112. package/dist/hooks/magic-context/module-tool-backends.d.ts +47 -0
  113. package/dist/hooks/magic-context/module-tool-backends.d.ts.map +1 -0
  114. package/dist/hooks/magic-context/module-transport.d.ts +1 -1
  115. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  116. package/dist/hooks/magic-context/persist-filtered-noise.d.ts.map +1 -1
  117. package/dist/hooks/magic-context/read-session-chunk.d.ts +12 -0
  118. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  119. package/dist/hooks/magic-context/rust-mode-transform.d.ts +35 -0
  120. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  121. package/dist/hooks/magic-context/rust-refusal-recovery.d.ts +18 -0
  122. package/dist/hooks/magic-context/rust-refusal-recovery.d.ts.map +1 -1
  123. package/dist/hooks/magic-context/rust-served-m0.d.ts +20 -0
  124. package/dist/hooks/magic-context/rust-served-m0.d.ts.map +1 -0
  125. package/dist/hooks/magic-context/store-ahead-refusal.d.ts +40 -0
  126. package/dist/hooks/magic-context/store-ahead-refusal.d.ts.map +1 -0
  127. package/dist/hooks/magic-context/strip-content.d.ts +5 -5
  128. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  129. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  130. package/dist/hooks/magic-context/tag-messages.d.ts +19 -0
  131. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  132. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  133. package/dist/hooks/magic-context/tool-drop-target.d.ts +14 -0
  134. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  135. package/dist/hooks/magic-context/tool-input-size.d.ts +21 -0
  136. package/dist/hooks/magic-context/tool-input-size.d.ts.map +1 -0
  137. package/dist/hooks/magic-context/tool-sweep-policy.d.ts.map +1 -1
  138. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +124 -11
  139. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  140. package/dist/hooks/magic-context/transform.d.ts +13 -0
  141. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  142. package/dist/{index-0ccmyzbj.js → index-01ft0h3p.js} +1 -1
  143. package/dist/{index-tdrv6nd0.js → index-2zf5fv89.js} +254 -61
  144. package/dist/{index-6j8th2hd.js → index-5f4qsnaq.js} +2 -2
  145. package/dist/{index-jgnag7sa.js → index-c906f3ff.js} +4 -4
  146. package/dist/{index-pa67brzn.js → index-cj5qe2nw.js} +3 -3
  147. package/dist/{index-febg06dy.js → index-dx0xnd4q.js} +1 -1
  148. package/dist/{index-bmzt6xgy.js → index-pye48m79.js} +1 -1
  149. package/dist/{index-g6hpfkqa.js → index-rdey8znw.js} +932 -261
  150. package/dist/{index-wgzyeyf5.js → index-tjq5jztd.js} +22 -6
  151. package/dist/{index-9r1cbh9e.js → index-v6wsttqt.js} +1 -1
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +3323 -1629
  154. package/dist/{message-time-backfill-0ccvyk9n.js → message-time-backfill-rm9wmcem.js} +3 -3
  155. package/dist/plugin/dream-timer.d.ts +2 -0
  156. package/dist/plugin/dream-timer.d.ts.map +1 -1
  157. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  158. package/dist/plugin/messages-transform.d.ts.map +1 -1
  159. package/dist/plugin/native-compaction-guard.d.ts +22 -0
  160. package/dist/plugin/native-compaction-guard.d.ts.map +1 -0
  161. package/dist/plugin/rpc-handlers.d.ts +11 -0
  162. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  163. package/dist/{read-session-chunk-53a7qbrh.js → read-session-chunk-ktz5zt70.js} +5 -3
  164. package/dist/{recomp-orchestrator-adsgqgse.js → recomp-orchestrator-ahekzt0s.js} +8 -8
  165. package/dist/{safe-notification-target-vrkjrbgb.js → safe-notification-target-6w0esgd6.js} +2 -2
  166. package/dist/{send-session-notification-e28r4xx2.js → send-session-notification-w7xbnrsm.js} +4 -4
  167. package/dist/shared/index.d.ts +1 -0
  168. package/dist/shared/index.d.ts.map +1 -1
  169. package/dist/shared/logger.d.ts.map +1 -1
  170. package/dist/shared/model-suggestion-retry.d.ts +13 -1
  171. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  172. package/dist/shared/prompt-async-transport.d.ts +25 -0
  173. package/dist/shared/prompt-async-transport.d.ts.map +1 -0
  174. package/dist/shared/rpc-server.d.ts +1 -0
  175. package/dist/shared/rpc-server.d.ts.map +1 -1
  176. package/dist/shared/rpc-types.d.ts +16 -0
  177. package/dist/shared/rpc-types.d.ts.map +1 -1
  178. package/dist/shared/rpc-utils.d.ts +3 -0
  179. package/dist/shared/rpc-utils.d.ts.map +1 -1
  180. package/dist/shared/sqlite-write-locker-test-support.d.ts +7 -0
  181. package/dist/shared/sqlite-write-locker-test-support.d.ts.map +1 -0
  182. package/dist/shared/status-summary.d.ts +9 -1
  183. package/dist/shared/status-summary.d.ts.map +1 -1
  184. package/dist/shared/tag-transcript.d.ts.map +1 -1
  185. package/dist/shared/user-facing-codes.d.ts +10 -5
  186. package/dist/shared/user-facing-codes.d.ts.map +1 -1
  187. package/dist/shared/window-geometry.d.ts +6 -3
  188. package/dist/shared/window-geometry.d.ts.map +1 -1
  189. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  190. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  191. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  192. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  193. package/dist/v2/fold/boundary.d.ts +61 -0
  194. package/dist/v2/fold/boundary.d.ts.map +1 -0
  195. package/dist/v2/fold/restore.d.ts.map +1 -1
  196. package/dist/v2/hidden-completion.d.ts +16 -3
  197. package/dist/v2/hidden-completion.d.ts.map +1 -1
  198. package/dist/v2/hooks/context.d.ts +45 -14
  199. package/dist/v2/hooks/context.d.ts.map +1 -1
  200. package/dist/v2/hooks/hidden-child.d.ts +6 -0
  201. package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
  202. package/dist/v2/hooks/payload.d.ts.map +1 -1
  203. package/dist/v2/hooks/rpc-live-state.d.ts +1 -1
  204. package/dist/v2/hooks/rpc-live-state.d.ts.map +1 -1
  205. package/dist/v2/hooks/rust-mode.d.ts +47 -0
  206. package/dist/v2/hooks/rust-mode.d.ts.map +1 -0
  207. package/dist/v2/hooks/session-project-backfill.d.ts +15 -0
  208. package/dist/v2/hooks/session-project-backfill.d.ts.map +1 -0
  209. package/dist/v2/hooks/storage-gate.d.ts +42 -0
  210. package/dist/v2/hooks/storage-gate.d.ts.map +1 -0
  211. package/dist/v2/hooks/storage-notice.d.ts +29 -0
  212. package/dist/v2/hooks/storage-notice.d.ts.map +1 -0
  213. package/dist/v2/hooks/store.d.ts +16 -0
  214. package/dist/v2/hooks/store.d.ts.map +1 -1
  215. package/dist/v2/hooks/tools.d.ts +11 -2
  216. package/dist/v2/hooks/tools.d.ts.map +1 -1
  217. package/dist/v2/hooks/types.d.ts +4 -0
  218. package/dist/v2/hooks/types.d.ts.map +1 -1
  219. package/dist/v2/hooks/usage-persist.d.ts +31 -0
  220. package/dist/v2/hooks/usage-persist.d.ts.map +1 -0
  221. package/dist/v2/server.js +12216 -7963
  222. package/dist/v2/store-reader.d.ts +9 -0
  223. package/dist/v2/store-reader.d.ts.map +1 -1
  224. package/dist/wrapup-orchestrator-9srk0kjf.js +18 -0
  225. package/package.json +1 -1
  226. package/src/agents/magic-context-prompt.ts +3 -3
  227. package/src/config/index.ts +40 -0
  228. package/src/config/misplaced-agent-model.test.ts +61 -0
  229. package/src/config/project-security.test.ts +17 -0
  230. package/src/config/project-security.ts +28 -4
  231. package/src/config/schema/magic-context.ts +29 -2
  232. package/src/shared/index.ts +1 -0
  233. package/src/shared/logger.test.ts +63 -2
  234. package/src/shared/logger.ts +23 -5
  235. package/src/shared/model-suggestion-retry.test.ts +115 -4
  236. package/src/shared/model-suggestion-retry.ts +57 -12
  237. package/src/shared/prompt-async-transport.test.ts +217 -0
  238. package/src/shared/prompt-async-transport.ts +242 -0
  239. package/src/shared/prompt-surface-a1-golden.md +8 -8
  240. package/src/shared/rpc-server.ts +13 -1
  241. package/src/shared/rpc-types.ts +17 -0
  242. package/src/shared/rpc-utils.ts +28 -0
  243. package/src/shared/sqlite-transaction-mode-guard.test.ts +198 -0
  244. package/src/shared/sqlite-write-locker-test-support.ts +62 -0
  245. package/src/shared/status-detail-text.test.ts +23 -0
  246. package/src/shared/status-summary.ts +25 -1
  247. package/src/shared/tag-transcript.ts +82 -19
  248. package/src/shared/user-facing-codes.ts +12 -5
  249. package/src/shared/window-geometry.ts +6 -3
  250. package/dist/v2/hooks/provider-admission.d.ts +0 -22
  251. package/dist/v2/hooks/provider-admission.d.ts.map +0 -1
  252. package/dist/wrapup-orchestrator-8xzbpvm8.js +0 -18
@@ -6,7 +6,7 @@ import {
6
6
  withReadOnlySessionDb,
7
7
  getRawSessionMessageCountFromDb,
8
8
  getMessageTimesFromOpenCodeDb
9
- } from "./index-bmzt6xgy.js";
9
+ } from "./index-pye48m79.js";
10
10
  import {
11
11
  UNKNOWN_FIT_RATIO,
12
12
  resolveDecisionCalibration,
@@ -520,6 +520,23 @@ function estimateImageTokensFromDataUrl(url) {
520
520
  }
521
521
  return IMAGE_FALLBACK_TOKENS;
522
522
  }
523
+ function estimateToolAttachmentImageTokens(state) {
524
+ if (!state || typeof state !== "object")
525
+ return 0;
526
+ const attachments = state.attachments;
527
+ if (!Array.isArray(attachments))
528
+ return 0;
529
+ let tokens = 0;
530
+ for (const attachment of attachments) {
531
+ if (!attachment || typeof attachment !== "object")
532
+ continue;
533
+ const { mime, url } = attachment;
534
+ if (typeof mime !== "string" || !mime.startsWith("image/"))
535
+ continue;
536
+ tokens += typeof url === "string" && url.startsWith("data:") ? estimateImageTokensFromDataUrl(url) : IMAGE_FALLBACK_TOKENS;
537
+ }
538
+ return tokens;
539
+ }
523
540
  function clampImageTokens(n) {
524
541
  if (n < 1)
525
542
  return 1;
@@ -1260,7 +1277,7 @@ function measureTailHygiene(input) {
1260
1277
  if (isDropSentinel(output)) {
1261
1278
  parts.push(excludedSnapshot(`${key}\x00excludedOutput`, output));
1262
1279
  } else {
1263
- const tokens = memoizedTokens("toolOutput", output);
1280
+ const tokens = memoizedTokens("toolOutput", output) + (part.type === "tool" ? estimateToolAttachmentImageTokens(part.state) : 0);
1264
1281
  const measured = snapshot({
1265
1282
  key: `${key}\x00toolOutput`,
1266
1283
  kind: "toolOutput",
@@ -1562,10 +1579,15 @@ var S_URGENT = 0.6;
1562
1579
  var CHANNEL2_SEVERITY_THRESHOLD = 0.75;
1563
1580
  var CHANNEL2_FLOOR_TOKENS = 50000;
1564
1581
  var LEVEL_RANK = { gentle: 1, firm: 2, urgent: 3 };
1582
+ var DROP_SENTINELS = ["[dropped", "[truncated"];
1565
1583
  function channel1RefireTokens(tailTokens) {
1566
1584
  const scaled = Math.round(0.08 * Math.max(0, tailTokens));
1567
1585
  return Math.max(CHANNEL1_REFIRE_FLOOR_TOKENS, scaled);
1568
1586
  }
1587
+ function isDroppedToolOutput(output) {
1588
+ const head = output.trimStart().replace(/^§\d+§\s*/, "").slice(0, 16).toLowerCase();
1589
+ return DROP_SENTINELS.some((sentinel) => head.startsWith(sentinel));
1590
+ }
1569
1591
  function toolOutputTokens(output) {
1570
1592
  return estimateTokens(stripChannel1ReminderSpans(output));
1571
1593
  }
@@ -1809,7 +1831,7 @@ function buildChannel2Reminder(undroppedTokens, reclaimableToolOutputs, hint) {
1809
1831
  const summary = formatReclaimableOutputSummary(reclaimableToolOutputs, undroppedTokens);
1810
1832
  const hintText = formatOldestReclaimableHint(hint);
1811
1833
  return `<system-reminder>
1812
- ` + `Routine housekeeping: ${summary} are reclaimable — make a ctx_reduce pass at a natural stopping point.${hintText}
1834
+ ` + `Your next step: call ctx_reduce on the outputs you've already used (${summary}). Then continue your task.${hintText}
1813
1835
  ` + `</system-reminder>`;
1814
1836
  }
1815
1837
  var CHANNEL1_STICKY_REAL_USER_TURN_GAP = 5;
@@ -1820,19 +1842,19 @@ function buildChannel1Reminder(level, undroppedTokens, reclaimableToolOutputs, h
1820
1842
  return `
1821
1843
 
1822
1844
  <system-reminder>
1823
- Reminder: ${summary} are still reclaimable — ctx_reduce them at a natural stopping point.${hintText}
1845
+ Still unstamped: ${summary}. Stamp the ones you've used with ctx_reduce now.${hintText}
1824
1846
  </system-reminder>`;
1825
1847
  }
1826
1848
  let body;
1827
1849
  switch (level) {
1828
1850
  case "gentle":
1829
- body = `Housekeeping: ${summary} are reclaimable — drop the ones you have already processed with ctx_reduce at a natural stopping point.`;
1851
+ body = `Housekeeping: ${summary} are reclaimable. Stamp each output with ctx_reduce as soon as you've taken what you need from it; don't wait for the task to finish.`;
1830
1852
  break;
1831
1853
  case "firm":
1832
- body = `Housekeeping: ${summary} are reclaimable — make a ctx_reduce pass at a natural stopping point.`;
1854
+ body = `${summary} are reclaimable. Make a ctx_reduce pass now over the outputs you've already used, then continue.`;
1833
1855
  break;
1834
1856
  case "urgent":
1835
- body = `Housekeeping backlog: ${summary} are reclaimable — a ctx_reduce pass is due.`;
1857
+ body = `${summary} are still unstamped. Call ctx_reduce now, before your next tool call, on every output you've already used.`;
1836
1858
  break;
1837
1859
  }
1838
1860
  return `
@@ -2287,7 +2309,7 @@ function toTagEntry(row) {
2287
2309
  messageId: row.message_id,
2288
2310
  type,
2289
2311
  status,
2290
- dropMode: row.drop_mode === "truncated" ? "truncated" : row.drop_mode === "edit_marker" ? "edit_marker" : "full",
2312
+ dropMode: row.drop_mode === "truncated" ? "truncated" : row.drop_mode === "skeleton_real" ? "skeleton_real" : row.drop_mode === "skeleton_stripped" ? "skeleton_stripped" : row.drop_mode === "edit_marker" ? "edit_marker" : row.drop_mode === "edit_marker_stripped" ? "edit_marker_stripped" : "full",
2291
2313
  toolName: row.tool_name ?? null,
2292
2314
  inputByteSize: row.input_byte_size ?? 0,
2293
2315
  byteSize: row.byte_size,
@@ -2369,8 +2391,22 @@ function maxNullableNumber(a, b) {
2369
2391
  return b;
2370
2392
  return null;
2371
2393
  }
2394
+ var foldStatements = new WeakMap;
2395
+ function foldStatement(db, sql) {
2396
+ let statements = foldStatements.get(db);
2397
+ if (!statements) {
2398
+ statements = new Map;
2399
+ foldStatements.set(db, statements);
2400
+ }
2401
+ let statement = statements.get(sql);
2402
+ if (!statement) {
2403
+ statement = db.prepare(sql);
2404
+ statements.set(sql, statement);
2405
+ }
2406
+ return statement;
2407
+ }
2372
2408
  function mergeSizeAndTokenColumnsIntoSurvivor(db, sessionId, survivor, duplicate) {
2373
- db.prepare(`UPDATE tags
2409
+ foldStatement(db, `UPDATE tags
2374
2410
  SET byte_size = ?,
2375
2411
  reasoning_byte_size = ?,
2376
2412
  input_byte_size = ?,
@@ -2390,41 +2426,42 @@ function applyDroppedStatusIfNeeded(db, sessionId, survivor, duplicate) {
2390
2426
  return;
2391
2427
  if (duplicate.status !== "dropped")
2392
2428
  return;
2393
- db.prepare("UPDATE tags SET status = 'dropped' WHERE session_id = ? AND tag_number = ?").run(sessionId, survivor.tagNumber);
2429
+ foldStatement(db, "UPDATE tags SET status = 'dropped' WHERE session_id = ? AND tag_number = ?").run(sessionId, survivor.tagNumber);
2394
2430
  survivor.status = "dropped";
2395
2431
  }
2396
2432
  function retargetPendingOps(db, sessionId, fromTagNumber, toTagNumber) {
2397
- const rows = db.prepare(`SELECT id, operation
2398
- FROM pending_ops
2399
- WHERE session_id = ? AND tag_id = ?
2400
- ORDER BY id ASC`).all(sessionId, fromTagNumber).filter(isPendingOpIdentityRow);
2433
+ const rows = foldStatement(db, `SELECT id, operation
2434
+ FROM pending_ops
2435
+ WHERE session_id = ? AND tag_id = ?
2436
+ ORDER BY id ASC`).all(sessionId, fromTagNumber).filter(isPendingOpIdentityRow);
2401
2437
  for (const row of rows) {
2402
- const existing = db.prepare(`SELECT 1
2403
- FROM pending_ops
2404
- WHERE session_id = ? AND tag_id = ? AND operation = ?
2405
- LIMIT 1`).get(sessionId, toTagNumber, row.operation);
2438
+ const existing = foldStatement(db, `SELECT 1
2439
+ FROM pending_ops
2440
+ WHERE session_id = ? AND tag_id = ? AND operation = ?
2441
+ LIMIT 1`).get(sessionId, toTagNumber, row.operation);
2406
2442
  if (existing) {
2407
- db.prepare("DELETE FROM pending_ops WHERE session_id = ? AND id = ?").run(sessionId, row.id);
2443
+ foldStatement(db, "DELETE FROM pending_ops WHERE session_id = ? AND id = ?").run(sessionId, row.id);
2408
2444
  } else {
2409
- db.prepare("UPDATE pending_ops SET tag_id = ? WHERE session_id = ? AND id = ?").run(toTagNumber, sessionId, row.id);
2445
+ foldStatement(db, "UPDATE pending_ops SET tag_id = ? WHERE session_id = ? AND id = ?").run(toTagNumber, sessionId, row.id);
2410
2446
  }
2411
2447
  }
2412
- db.prepare("DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ?").run(sessionId, fromTagNumber);
2448
+ foldStatement(db, "DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ?").run(sessionId, fromTagNumber);
2413
2449
  }
2414
2450
  function deleteFoldedDuplicateTag(db, sessionId, tagNumber) {
2415
- db.prepare("DELETE FROM source_contents WHERE session_id = ? AND tag_id = ?").run(sessionId, tagNumber);
2416
- db.prepare("DELETE FROM tags WHERE session_id = ? AND tag_number = ?").run(sessionId, tagNumber);
2417
- db.prepare("DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ?").run(sessionId, tagNumber);
2451
+ foldStatement(db, "DELETE FROM source_contents WHERE session_id = ? AND tag_id = ?").run(sessionId, tagNumber);
2452
+ foldStatement(db, "DELETE FROM tags WHERE session_id = ? AND tag_number = ?").run(sessionId, tagNumber);
2453
+ foldStatement(db, "DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ?").run(sessionId, tagNumber);
2418
2454
  }
2419
- function foldDuplicateIntoSurvivor(db, sessionId, survivor, duplicate) {
2455
+ function foldDuplicateIntoSurvivor(db, sessionId, survivor, duplicate, duplicateMayHaveQueuedOps = true) {
2420
2456
  mergeSizeAndTokenColumnsIntoSurvivor(db, sessionId, survivor, duplicate);
2421
2457
  applyDroppedStatusIfNeeded(db, sessionId, survivor, duplicate);
2422
- retargetPendingOps(db, sessionId, duplicate.tagNumber, survivor.tagNumber);
2458
+ if (duplicateMayHaveQueuedOps) {
2459
+ retargetPendingOps(db, sessionId, duplicate.tagNumber, survivor.tagNumber);
2460
+ }
2423
2461
  deleteFoldedDuplicateTag(db, sessionId, duplicate.tagNumber);
2424
2462
  }
2425
- function readPartTagRows(db, sessionId, messageId) {
2426
- const prefix = `${messageId}:p`;
2427
- return db.prepare(`SELECT tag_number AS tagNumber,
2463
+ function readPartTagRowsByMessage(db, sessionId) {
2464
+ const rows = db.prepare(`SELECT tag_number AS tagNumber,
2428
2465
  message_id AS messageId,
2429
2466
  tool_owner_message_id AS toolOwnerMessageId,
2430
2467
  type,
@@ -2438,22 +2475,39 @@ function readPartTagRows(db, sessionId, messageId) {
2438
2475
  FROM tags
2439
2476
  WHERE session_id = ?
2440
2477
  AND type = 'message'
2441
- AND message_id LIKE ? ESCAPE '\\'
2442
- ORDER BY tag_number ASC`).all(sessionId, `${escapeLikePattern(messageId)}:p%`).filter(isPiFallbackFoldTagRow).flatMap((row) => {
2443
- const suffix = row.messageId.startsWith(prefix) ? row.messageId.slice(prefix.length) : "";
2478
+ ORDER BY tag_number ASC`).all(sessionId).filter(isPiFallbackFoldTagRow);
2479
+ const byMessage = new Map;
2480
+ for (const row of rows) {
2481
+ const marker = row.messageId.lastIndexOf(":p");
2482
+ if (marker < 0)
2483
+ continue;
2484
+ const suffix = row.messageId.slice(marker + 2);
2444
2485
  if (!/^\d+$/.test(suffix))
2445
- return [];
2446
- return [{ ...row, partIndex: Number.parseInt(suffix, 10) }];
2447
- }).sort((a, b) => a.partIndex - b.partIndex);
2486
+ continue;
2487
+ const messageId = row.messageId.slice(0, marker);
2488
+ const group = byMessage.get(messageId);
2489
+ const entry = { ...row, partIndex: Number.parseInt(suffix, 10) };
2490
+ if (group)
2491
+ group.push(entry);
2492
+ else
2493
+ byMessage.set(messageId, [entry]);
2494
+ }
2495
+ for (const group of byMessage.values())
2496
+ group.sort((a, b) => a.partIndex - b.partIndex);
2497
+ return byMessage;
2448
2498
  }
2449
- function tagNumbersWithQueuedDrop(db, sessionId, tagNumbers) {
2450
- if (tagNumbers.length === 0)
2451
- return new Set;
2452
- const placeholders = tagNumbers.map(() => "?").join(", ");
2453
- const rows = db.prepare(`SELECT DISTINCT tag_id AS tagNumber
2454
- FROM pending_ops
2455
- WHERE session_id = ? AND operation = 'drop' AND tag_id IN (${placeholders})`).all(sessionId, ...tagNumbers);
2456
- return new Set(rows.flatMap((row) => typeof row.tagNumber === "number" ? [row.tagNumber] : []));
2499
+ function readQueuedTagNumbers(db, sessionId) {
2500
+ const rows = db.prepare("SELECT tag_id AS tagNumber, operation FROM pending_ops WHERE session_id = ? AND tag_id IS NOT NULL").all(sessionId);
2501
+ const any = new Set;
2502
+ const drop = new Set;
2503
+ for (const row of rows) {
2504
+ if (typeof row.tagNumber !== "number")
2505
+ continue;
2506
+ any.add(row.tagNumber);
2507
+ if (row.operation === "drop")
2508
+ drop.add(row.tagNumber);
2509
+ }
2510
+ return { any, drop };
2457
2511
  }
2458
2512
  function foldShrunkPartTags(db, sessionId, messages) {
2459
2513
  const result = {
@@ -2461,17 +2515,25 @@ function foldShrunkPartTags(db, sessionId, messages) {
2461
2515
  rekeyedTagNumbers: [],
2462
2516
  discardedDropTagNumbers: []
2463
2517
  };
2518
+ if (messages.length === 0)
2519
+ return result;
2520
+ const rowsByMessage = readPartTagRowsByMessage(db, sessionId);
2521
+ const queuedTags = readQueuedTagNumbers(db, sessionId);
2522
+ const discardDrop = foldStatement(db, "DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ? AND operation = 'drop'");
2523
+ const rekey = foldStatement(db, "UPDATE tags SET message_id = ? WHERE session_id = ? AND tag_number = ? AND type = 'message'");
2464
2524
  for (const { messageId, partCount } of messages) {
2465
- const rows = readPartTagRows(db, sessionId, messageId);
2525
+ const rows = rowsByMessage.get(messageId) ?? [];
2466
2526
  if (rows.length === 0)
2467
2527
  continue;
2468
2528
  const orphans = rows.filter((row) => row.partIndex >= partCount);
2469
2529
  if (orphans.length === 0)
2470
2530
  continue;
2471
- const queued = tagNumbersWithQueuedDrop(db, sessionId, rows.map((row) => row.tagNumber));
2472
- if (queued.size > 0 && queued.size < rows.length) {
2531
+ const queued = [
2532
+ ...new Set(rows.map((row) => row.tagNumber).filter((tag) => queuedTags.drop.has(tag)))
2533
+ ].sort((a, b) => a - b);
2534
+ if (queued.length > 0 && queued.length < rows.length) {
2473
2535
  for (const tagNumber of queued) {
2474
- db.prepare("DELETE FROM pending_ops WHERE session_id = ? AND tag_id = ? AND operation = 'drop'").run(sessionId, tagNumber);
2536
+ discardDrop.run(sessionId, tagNumber);
2475
2537
  result.discardedDropTagNumbers.push(tagNumber);
2476
2538
  }
2477
2539
  }
@@ -2480,14 +2542,14 @@ function foldShrunkPartTags(db, sessionId, messages) {
2480
2542
  const lowest = orphans[0];
2481
2543
  if (!lowest)
2482
2544
  continue;
2483
- db.prepare("UPDATE tags SET message_id = ? WHERE session_id = ? AND tag_number = ? AND type = 'message'").run(`${messageId}:p0`, sessionId, lowest.tagNumber);
2545
+ rekey.run(`${messageId}:p0`, sessionId, lowest.tagNumber);
2484
2546
  result.rekeyedTagNumbers.push(lowest.tagNumber);
2485
2547
  survivor = { ...lowest, messageId: `${messageId}:p0`, partIndex: 0 };
2486
2548
  }
2487
2549
  for (const orphan of orphans) {
2488
2550
  if (orphan.tagNumber === survivor.tagNumber)
2489
2551
  continue;
2490
- foldDuplicateIntoSurvivor(db, sessionId, survivor, orphan);
2552
+ foldDuplicateIntoSurvivor(db, sessionId, survivor, orphan, queuedTags.any.has(orphan.tagNumber));
2491
2553
  result.foldedTagNumbers.push(orphan.tagNumber);
2492
2554
  }
2493
2555
  }
@@ -2514,7 +2576,7 @@ function markTagsCompactedByMessageIds(db, sessionId, messageIds) {
2514
2576
  changed += update.all(sessionId, messageId, `${escaped}:p%`, `${escaped}:file%`, messageId).length;
2515
2577
  }
2516
2578
  return changed;
2517
- })();
2579
+ }).immediate();
2518
2580
  }
2519
2581
  function deleteTagsByMessageId(db, sessionId, messageId) {
2520
2582
  const deleteTransaction = db.transaction(() => {
@@ -4252,6 +4314,7 @@ function estimateMessageTokens(message) {
4252
4314
  toolCall += serializedTokens(p.state?.input ?? p.input ?? p.args);
4253
4315
  toolCall += serializedTokens(p.state?.output ?? p.state?.content ?? p.output ?? p.result ?? p.content);
4254
4316
  toolCall += serializedTokens(p.state?.error);
4317
+ toolCall += estimateToolAttachmentImageTokens(p.state);
4255
4318
  break;
4256
4319
  case "tool-call":
4257
4320
  toolCall += serializedTokens(p.input ?? p.args);
@@ -4348,6 +4411,28 @@ function hasCountableParts(message) {
4348
4411
  });
4349
4412
  }
4350
4413
 
4414
+ // src/hooks/magic-context/tool-input-size.ts
4415
+ var SKELETON_REAL_INPUT_MAX_BYTES = 1024;
4416
+ var encoder2 = new TextEncoder;
4417
+ function toolInputStringBytes(value) {
4418
+ const seen = new WeakSet;
4419
+ const visit = (candidate) => {
4420
+ if (typeof candidate === "string")
4421
+ return encoder2.encode(candidate).length;
4422
+ if (candidate === null || typeof candidate !== "object")
4423
+ return 0;
4424
+ if (seen.has(candidate))
4425
+ return 0;
4426
+ seen.add(candidate);
4427
+ let total = 0;
4428
+ const values = Array.isArray(candidate) ? candidate : Object.values(candidate);
4429
+ for (const item of values)
4430
+ total += visit(item);
4431
+ return total;
4432
+ };
4433
+ return visit(value);
4434
+ }
4435
+
4351
4436
  // src/hooks/magic-context/tool-drop-target.ts
4352
4437
  var DROP_PREFIX = "[dropped";
4353
4438
  var IGNORE_PART_TYPES = new Set([
@@ -4400,9 +4485,39 @@ function clampCloneInPlace(occurrence, clamp) {
4400
4485
  const clone = clonePart(occurrence.part);
4401
4486
  clamp(clone);
4402
4487
  const parts = occurrence.message.parts;
4403
- const index = parts.indexOf(occurrence.part);
4404
- if (index >= 0)
4488
+ const index = parts.indexOf(servedPart(occurrence));
4489
+ if (index >= 0) {
4405
4490
  parts[index] = clone;
4491
+ occurrence.served = clone;
4492
+ }
4493
+ }
4494
+ function servedPart(occurrence) {
4495
+ return occurrence.served ?? occurrence.part;
4496
+ }
4497
+ function skeletonRealToolPart(part, tagId, stripAttachments = false) {
4498
+ if (!isRecord(part))
4499
+ return;
4500
+ const sentinel = `[dropped §${tagId}§]`;
4501
+ if (part.type === "tool" && isRecord(part.state)) {
4502
+ part.state.output = sentinel;
4503
+ if (stripAttachments)
4504
+ part.state.attachments = [];
4505
+ return;
4506
+ }
4507
+ if (part.type === "tool_result") {
4508
+ part.content = sentinel;
4509
+ }
4510
+ }
4511
+ function readToolPartRawInput(part) {
4512
+ if (!isRecord(part))
4513
+ return;
4514
+ if (part.type === "tool" && isRecord(part.state))
4515
+ return part.state.input;
4516
+ if (part.type === "tool-invocation")
4517
+ return part.args;
4518
+ if (part.type === "tool_use")
4519
+ return part.input;
4520
+ return;
4406
4521
  }
4407
4522
  function truncateToolPart(part, tagId) {
4408
4523
  if (!isRecord(part))
@@ -4426,12 +4541,14 @@ function truncateToolPart(part, tagId) {
4426
4541
  part.input = droppedInputMarker(tagId);
4427
4542
  }
4428
4543
  }
4429
- function editMarkerToolPart(part, tagId) {
4544
+ function editMarkerToolPart(part, tagId, stripAttachments = false) {
4430
4545
  if (!isRecord(part))
4431
4546
  return;
4432
4547
  const sentinel = `[dropped §${tagId}§]`;
4433
4548
  if (part.type === "tool" && isRecord(part.state)) {
4434
4549
  part.state.output = sentinel;
4550
+ if (stripAttachments)
4551
+ part.state.attachments = [];
4435
4552
  if (isRecord(part.state.input))
4436
4553
  applyEditMarkerToInput(part.state.input);
4437
4554
  return;
@@ -4534,7 +4651,7 @@ class ToolMutationBatch {
4534
4651
  this.messages = messages;
4535
4652
  }
4536
4653
  markForRemoval(occurrence) {
4537
- this.partsToRemove.add(occurrence.part);
4654
+ this.partsToRemove.add(servedPart(occurrence));
4538
4655
  this.affectedMessages.add(occurrence.message);
4539
4656
  }
4540
4657
  wouldStrandConversationEnd(occurrences) {
@@ -4546,7 +4663,7 @@ class ToolMutationBatch {
4546
4663
  }
4547
4664
  if (!end || !occurrences.some((occurrence) => occurrence.message === end))
4548
4665
  return false;
4549
- const leaving = new Set(occurrences.map((occurrence) => occurrence.part));
4666
+ const leaving = new Set(occurrences.map(servedPart));
4550
4667
  const survives = (part) => !leaving.has(part) && !this.partsToRemove.has(part);
4551
4668
  return end.info.role === "assistant" ? !end.parts.some((part) => partHasCompletedResult(part) && survives(part)) : !end.parts.some((part) => hasMeaningfulPart(part) && survives(part));
4552
4669
  }
@@ -4597,7 +4714,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId)
4597
4714
  if (!entry.hasResult)
4598
4715
  return "incomplete";
4599
4716
  if (batch.wouldStrandConversationEnd(entry.occurrences))
4600
- return truncate();
4717
+ return skeletonReal();
4601
4718
  for (const occurrence of entry.occurrences) {
4602
4719
  batch.markForRemoval(occurrence);
4603
4720
  }
@@ -4617,14 +4734,50 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId)
4617
4734
  clearThinkingParts(thinkingParts);
4618
4735
  return "truncated";
4619
4736
  };
4620
- const editMarker = () => {
4737
+ const skeletonReal = () => {
4738
+ const entry = index.get(compositeKey);
4739
+ if (!entry || entry.occurrences.length === 0)
4740
+ return "absent";
4741
+ if (!entry.hasResult)
4742
+ return "incomplete";
4743
+ for (const occurrence of entry.occurrences) {
4744
+ clampCloneInPlace(occurrence, (part) => skeletonRealToolPart(part, tagId));
4745
+ }
4746
+ clearThinkingParts(thinkingParts);
4747
+ return "truncated";
4748
+ };
4749
+ const skeletonStripped = () => {
4750
+ const entry = index.get(compositeKey);
4751
+ if (!entry || entry.occurrences.length === 0)
4752
+ return "absent";
4753
+ if (!entry.hasResult)
4754
+ return "incomplete";
4755
+ for (const occurrence of entry.occurrences) {
4756
+ clampCloneInPlace(occurrence, (part) => skeletonRealToolPart(part, tagId, true));
4757
+ }
4758
+ clearThinkingParts(thinkingParts);
4759
+ return "truncated";
4760
+ };
4761
+ const hasAttachments = () => index.get(compositeKey)?.occurrences.some(({ part }) => isRecord(part) && isRecord(part.state) && Array.isArray(part.state.attachments) && part.state.attachments.length > 0) ?? false;
4762
+ const readRawInput = () => {
4763
+ const entry = index.get(compositeKey);
4764
+ if (!entry)
4765
+ return;
4766
+ for (const occurrence of entry.occurrences) {
4767
+ const input = readToolPartRawInput(occurrence.part);
4768
+ if (input !== undefined)
4769
+ return input;
4770
+ }
4771
+ return;
4772
+ };
4773
+ const applyEditMarker = (stripAttachments) => {
4621
4774
  const entry = index.get(compositeKey);
4622
4775
  if (!entry || entry.occurrences.length === 0)
4623
4776
  return "absent";
4624
4777
  if (!entry.hasResult)
4625
4778
  return "incomplete";
4626
4779
  for (const occurrence of entry.occurrences) {
4627
- clampCloneInPlace(occurrence, (part) => editMarkerToolPart(part, tagId));
4780
+ clampCloneInPlace(occurrence, (part) => editMarkerToolPart(part, tagId, stripAttachments));
4628
4781
  }
4629
4782
  clearThinkingParts(thinkingParts);
4630
4783
  return "truncated";
@@ -4640,7 +4793,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId)
4640
4793
  const before = count(parts);
4641
4794
  const retained = skeleton ? structuredClone(parts) : [];
4642
4795
  for (const part of retained)
4643
- truncateToolPart(part, tagId);
4796
+ skeletonRealToolPart(part, tagId);
4644
4797
  const after = count(retained);
4645
4798
  return {
4646
4799
  beforeTools: before.toolCall,
@@ -4671,7 +4824,23 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId)
4671
4824
  },
4672
4825
  drop,
4673
4826
  truncate,
4674
- editMarker,
4827
+ skeletonReal,
4828
+ skeletonStripped,
4829
+ hasAttachments,
4830
+ editMarker: () => applyEditMarker(false),
4831
+ editMarkerStripped: () => applyEditMarker(true),
4832
+ inputStringBytes: () => {
4833
+ const entry = index.get(compositeKey);
4834
+ if (!entry || entry.occurrences.length === 0)
4835
+ return null;
4836
+ return toolInputStringBytes(readRawInput());
4837
+ },
4838
+ wouldStrandConversationEnd: () => {
4839
+ const entry = index.get(compositeKey);
4840
+ if (!entry || entry.occurrences.length === 0)
4841
+ return false;
4842
+ return batch.wouldStrandConversationEnd(entry.occurrences);
4843
+ },
4675
4844
  canDrop: () => {
4676
4845
  const entry = index.get(compositeKey);
4677
4846
  return !!entry && entry.occurrences.length > 0 && entry.hasResult;
@@ -5008,6 +5177,30 @@ function readRawSessionMessageOrdinalById2(sessionId, messageId) {
5008
5177
  return null;
5009
5178
  return withReadOnlySessionDb((db) => readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId));
5010
5179
  }
5180
+ function compareRawSessionMessageOrder2(sessionId, leftId, rightId) {
5181
+ const provider = sessionProviders.get(sessionId);
5182
+ if (provider) {
5183
+ if (!provider.readMessageOrdinalById)
5184
+ return null;
5185
+ const left = provider.readMessageOrdinalById(leftId);
5186
+ const right = provider.readMessageOrdinalById(rightId);
5187
+ return left === null || right === null ? null : left - right;
5188
+ }
5189
+ if (!openCodeDbExists())
5190
+ return null;
5191
+ return withReadOnlySessionDb((db) => {
5192
+ const lookup = db.prepare("SELECT time_created, id FROM message WHERE session_id = ? AND id = ? LIMIT 1");
5193
+ const left = lookup.get(sessionId, leftId);
5194
+ const right = lookup.get(sessionId, rightId);
5195
+ if (typeof left?.time_created !== "number" || typeof right?.time_created !== "number" || typeof left.id !== "string" || typeof right.id !== "string") {
5196
+ return null;
5197
+ }
5198
+ if (left.time_created !== right.time_created) {
5199
+ return left.time_created - right.time_created;
5200
+ }
5201
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
5202
+ });
5203
+ }
5011
5204
  function readRawSessionMessageById2(sessionId, messageId) {
5012
5205
  const provider = sessionProviders.get(sessionId);
5013
5206
  if (provider?.readMessageById) {
@@ -5406,4 +5599,4 @@ function readRawSessionSeedTail2(sessionId, boundaryId, onQuery) {
5406
5599
  });
5407
5600
  }
5408
5601
 
5409
- export { textMentionsRecentCommit, removeSystemReminders, hasMeaningfulUserText2, extractTexts2, extractToolCallSummaries, getTokenizerNativeMemoryStats, preloadTokenizer, estimateTokens, normalizeText, stableStringify, getCurrentToolSetHash, setDatabase, loadToolDefinitionMeasurements, recordToolDefinition, getMeasuredToolDefinitionTokens, CTX_REDUCE_KEEP, newestCtxReduceTagNumbers, stripWellFormedLeadingTagPrefix, stripPersistedAssistantText, byteSize, stripTagPrefix, peelLeadingMcTagNotation, prependTag, isThinkingPart, isRecord, estimateImageTokensFromDataUrl, normalizeTodoStateJson, buildSyntheticTodoPart, computeSyntheticCallId, isSyntheticTodoPart, countRealUserMessages, tailHygieneStructuralSignature, sameTailHygieneStructuralSignature, formatTailHygienePrefixMismatch, refreshTailHygieneBaseline, effectiveTailHygiene, assertTailHygieneContentUnchanged, CHANNEL1_SENTINEL, CHANNEL1_FLOOR_TOKENS, toolOutputTokens, decideChannel1, evaluateChannel2, formatChannel1Evaluation, formatChannel2Evaluation, reclaimableToolOutputCount, buildChannel2Reminder, buildChannel1Reminder, estimateEmergencyDropReclaimTokens, planEmergencyDrop, measureEmergencyTag, getRecentTagOwnerMessageIds, AGE_RECLAIM_MIN_TOKENS, getOldestActiveUnprotectedToolTags, getActiveToolTagsForAgeReclaim, getTriggerTagTokenUpperBound, getActiveTagTokenTotalsByMessage, getAllStatusTagTokenTotalsFlat, tagTokenCountIsNull, backfillTagTokenCounts, insertTag, updateTagStatus, markWhitespaceAssistantTagInert, getInertWhitespaceAssistantTags, updateTagDropMode, updateCavemanDepth, updateTagMessageId, foldShrunkPartTags, markTagsCompactedByMessageIds, deleteTagsByMessageId, getMaxTagNumberBySession, getTagNumberByMessageId, getAssignableTagNumberByMessageId, deriveTagLoadFloor, TAG_SELECT_COLUMNS, getTagsBySession, getActiveTagsBySession, getTailHygieneTags, getDroppedTagsBySession, getTagsByNumbers, getDroppedTagsByNumbers, getMaxDroppedTagNumber, getTagById, getToolTagNumberByOwner, getNullOwnerToolTag, adoptNullOwnerToolTag, getCandidateToolOwners, pickNearestPriorOwner, markHostUnservedRow, retreatPastHostUnservedRows, snapTerminalCompartmentToServedRow, isHostRenderedSystemMessage, isStrictGapHealingMessage, isRawCompactionSummaryInfo, extractInMemoryMessageViews, buildInMemoryTailRawMessages, completedToolArcCrossesBoundary, estimateTrueRawMessageTokens, buildToolArcs, fenceBoundaryForCompletedToolArcs, fenceBoundaryForToolArcs, buildTrueRawTokenIndex, computeRawRangeFingerprint, invalidateTrueRawTokenCache, isTextPart, isToolPartWithOutput, isFilePart, buildFileSourceContent, recordToolParameters, createDroppedInputGuard, assertExecutableToolInput, createDroppedInputToolExecuteBeforeHook, isEditTool, describeFinalWireTail, estimateMessageTokens, estimateFinalWireInputTokens, partHasCompletedResult, extractToolCallObservation, ToolMutationBatch, createToolDropTarget, resolveHostServedBoundaryId2, hasRawMessageProvider2, setRawMessageProvider2, setBoundedRawMessageProvider2, cleanUserText2, withRawSessionMessageCache2, readRawSessionMessages2, readRawSessionMessagePage2, getRawSessionMessageOrdinalCount2, readRawSessionMessageRange2, primeTailRawMessageCache2, getCachedAbsoluteMessageCount2, primeInMemoryTailRawMessageCache2, readRawSessionMessageOrdinalPage2, getRawSessionStoredMessageCount2, readRawSessionMessageIdOrdinalsForRange2, readRawSessionMessagePartsById2, hasRawSessionMessageById2, readRawSessionMessageOrdinalById2, readRawSessionMessageById2, getRawSessionMessageCount2, getRawSessionTagKeysThrough2, getLegacyProtectedTailStartOrdinal2, readSessionChunk2, readRawSessionSeedTail2 };
5602
+ export { textMentionsRecentCommit, removeSystemReminders, hasMeaningfulUserText2, extractTexts2, extractToolCallSummaries, getTokenizerNativeMemoryStats, preloadTokenizer, estimateTokens, normalizeText, stableStringify, getCurrentToolSetHash, setDatabase, loadToolDefinitionMeasurements, recordToolDefinition, getMeasuredToolDefinitionTokens, CTX_REDUCE_KEEP, newestCtxReduceTagNumbers, stripWellFormedLeadingTagPrefix, stripPersistedAssistantText, byteSize, stripTagPrefix, peelLeadingMcTagNotation, prependTag, isThinkingPart, isRecord, estimateImageTokensFromDataUrl, estimateToolAttachmentImageTokens, normalizeTodoStateJson, buildSyntheticTodoPart, computeSyntheticCallId, isSyntheticTodoPart, countRealUserMessages, tailHygieneStructuralSignature, sameTailHygieneStructuralSignature, formatTailHygienePrefixMismatch, refreshTailHygieneBaseline, effectiveTailHygiene, assertTailHygieneContentUnchanged, CHANNEL1_SENTINEL, CHANNEL1_FLOOR_TOKENS, isDroppedToolOutput, toolOutputTokens, decideChannel1, evaluateChannel2, formatChannel1Evaluation, formatChannel2Evaluation, reclaimableToolOutputCount, buildChannel2Reminder, buildChannel1Reminder, estimateEmergencyDropReclaimTokens, planEmergencyDrop, measureEmergencyTag, getRecentTagOwnerMessageIds, AGE_RECLAIM_MIN_TOKENS, getOldestActiveUnprotectedToolTags, getActiveToolTagsForAgeReclaim, getTriggerTagTokenUpperBound, getActiveTagTokenTotalsByMessage, getAllStatusTagTokenTotalsFlat, tagTokenCountIsNull, backfillTagTokenCounts, insertTag, updateTagStatus, markWhitespaceAssistantTagInert, getInertWhitespaceAssistantTags, updateTagDropMode, updateCavemanDepth, updateTagMessageId, foldShrunkPartTags, markTagsCompactedByMessageIds, deleteTagsByMessageId, getMaxTagNumberBySession, getTagNumberByMessageId, getAssignableTagNumberByMessageId, deriveTagLoadFloor, TAG_SELECT_COLUMNS, getTagsBySession, getActiveTagsBySession, getTailHygieneTags, getDroppedTagsBySession, getTagsByNumbers, getDroppedTagsByNumbers, getMaxDroppedTagNumber, getTagById, getToolTagNumberByOwner, getNullOwnerToolTag, adoptNullOwnerToolTag, getCandidateToolOwners, pickNearestPriorOwner, markHostUnservedRow, retreatPastHostUnservedRows, snapTerminalCompartmentToServedRow, isHostRenderedSystemMessage, isStrictGapHealingMessage, isRawCompactionSummaryInfo, extractInMemoryMessageViews, buildInMemoryTailRawMessages, completedToolArcCrossesBoundary, estimateTrueRawMessageTokens, buildToolArcs, fenceBoundaryForCompletedToolArcs, fenceBoundaryForToolArcs, buildTrueRawTokenIndex, computeRawRangeFingerprint, invalidateTrueRawTokenCache, isTextPart, isToolPartWithOutput, isFilePart, buildFileSourceContent, recordToolParameters, createDroppedInputGuard, assertExecutableToolInput, createDroppedInputToolExecuteBeforeHook, isEditTool, describeFinalWireTail, estimateMessageTokens, estimateFinalWireInputTokens, SKELETON_REAL_INPUT_MAX_BYTES, partHasCompletedResult, extractToolCallObservation, ToolMutationBatch, createToolDropTarget, resolveHostServedBoundaryId2, hasRawMessageProvider2, setRawMessageProvider2, setBoundedRawMessageProvider2, cleanUserText2, withRawSessionMessageCache2, readRawSessionMessages2, readRawSessionMessagePage2, getRawSessionMessageOrdinalCount2, readRawSessionMessageRange2, primeTailRawMessageCache2, getCachedAbsoluteMessageCount2, primeInMemoryTailRawMessageCache2, readRawSessionMessageOrdinalPage2, getRawSessionStoredMessageCount2, readRawSessionMessageIdOrdinalsForRange2, readRawSessionMessagePartsById2, hasRawSessionMessageById2, readRawSessionMessageOrdinalById2, compareRawSessionMessageOrder2, readRawSessionMessageById2, getRawSessionMessageCount2, getRawSessionTagKeysThrough2, getLegacyProtectedTailStartOrdinal2, readSessionChunk2, readRawSessionSeedTail2 };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log
3
- } from "./index-wgzyeyf5.js";
3
+ } from "./index-tjq5jztd.js";
4
4
  import {
5
5
  sendStatusNotification2
6
- } from "./index-jgnag7sa.js";
6
+ } from "./index-c906f3ff.js";
7
7
  import {
8
8
  readJsoncFile
9
9
  } from "./index-sd3egckz.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  sessionLog
3
- } from "./index-wgzyeyf5.js";
3
+ } from "./index-tjq5jztd.js";
4
4
  import {
5
5
  isDefaultSessionTitle2
6
- } from "./index-febg06dy.js";
6
+ } from "./index-dx0xnd4q.js";
7
7
  import {
8
8
  shouldHoldIgnoredNotification
9
- } from "./index-bmzt6xgy.js";
9
+ } from "./index-pye48m79.js";
10
10
 
11
11
  // src/shared/error-message.ts
12
12
  function getErrorMessage(error) {
@@ -186,7 +186,7 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
186
186
  holdIgnoredNotification({ client, sessionId, text, params, forcePersist }, "during target checks");
187
187
  return "queued";
188
188
  }
189
- const { waitForSafeNotificationTarget } = await import("./safe-notification-target-vrkjrbgb.js");
189
+ const { waitForSafeNotificationTarget } = await import("./safe-notification-target-6w0esgd6.js");
190
190
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
191
191
  sessionLog(sessionId, "notification skipped (session not titled yet)");
192
192
  return "skipped";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionLog
3
- } from "./index-wgzyeyf5.js";
3
+ } from "./index-tjq5jztd.js";
4
4
  import {
5
5
  COMPARTMENT_LEASE_RENEWAL_MS,
6
6
  acquireCompartmentLease,
@@ -23,13 +23,13 @@ import {
23
23
  registerActiveCompartmentRun,
24
24
  setRecompStarting2,
25
25
  setRecompTerminal2
26
- } from "./index-g6hpfkqa.js";
26
+ } from "./index-rdey8znw.js";
27
27
  import {
28
28
  pushNotification2
29
29
  } from "./index-jdxxq8cz.js";
30
30
  import {
31
31
  sendStatusNotification2
32
- } from "./index-jgnag7sa.js";
32
+ } from "./index-c906f3ff.js";
33
33
 
34
34
  // src/hooks/magic-context/wrapup-orchestrator.ts
35
35
  import * as crypto from "node:crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./index-wgzyeyf5.js";
3
+ } from "./index-tjq5jztd.js";
4
4
 
5
5
  // src/shared/safe-notification-target.ts
6
6
  var DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./index-wgzyeyf5.js";
3
+ } from "./index-tjq5jztd.js";
4
4
 
5
5
  // src/shared/opencode-db-path.ts
6
6
  import { existsSync, readdirSync, statSync } from "node:fs";