@retaw106/mmc-oc 2.2.1 → 2.3.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 (211) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/config/deprecated-config.d.ts +4 -0
  3. package/dist/config/deprecated-config.d.ts.map +1 -0
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/project-security.d.ts +2 -2
  6. package/dist/config/schema/magic-context.d.ts +3 -3
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/config-init.js +2 -2
  9. package/dist/{context-mrvm92n8.js → context-wt7g4f8k.js} +1641 -1543
  10. package/dist/{derive-budgets-rjjz5f7y.js → derive-budgets-s1p37t2g.js} +2 -2
  11. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  12. package/dist/features/magic-context/historian-failure-reason.d.ts +7 -0
  13. package/dist/features/magic-context/historian-failure-reason.d.ts.map +1 -0
  14. package/dist/features/magic-context/message-index-async.d.ts +21 -3
  15. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  16. package/dist/features/magic-context/message-index.d.ts +7 -1
  17. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  18. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  19. package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
  20. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
  21. package/dist/features/magic-context/search.d.ts +29 -0
  22. package/dist/features/magic-context/search.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-clone.d.ts +2 -0
  24. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-compartment-history-version.d.ts +4 -0
  26. package/dist/features/magic-context/storage-compartment-history-version.d.ts.map +1 -0
  27. package/dist/features/magic-context/storage-db.d.ts +1 -1
  28. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  30. package/dist/features/magic-context/storage-meta-persisted.d.ts +23 -5
  31. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-meta-session.d.ts +7 -1
  33. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-session-tables.d.ts +5 -1
  35. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  36. package/dist/features/magic-context/storage-tag-identity-revision.d.ts +50 -0
  37. package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
  38. package/dist/features/magic-context/storage-tags.d.ts +155 -5
  39. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  40. package/dist/features/magic-context/tagger.d.ts +2 -0
  41. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  42. package/dist/features/magic-context/transform-decision-log.d.ts +3 -0
  43. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/active-anthropic-turn.d.ts +2 -0
  45. package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/apply-operations.d.ts +7 -0
  47. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/auto-search-runner.d.ts +2 -2
  49. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/history-boundary-repair.d.ts +11 -2
  52. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/lkg-measured-request.d.ts +3 -1
  55. package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/lkg-persist.d.ts +10 -13
  57. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts +62 -0
  59. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts.map +1 -0
  60. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts +38 -0
  61. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts.map +1 -0
  62. package/dist/hooks/magic-context/lkg-replay.d.ts +16 -9
  63. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  64. package/dist/hooks/magic-context/lkg-slot.d.ts +141 -2
  65. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/opaque-thinking.d.ts +12 -0
  67. package/dist/hooks/magic-context/opaque-thinking.d.ts.map +1 -0
  68. package/dist/hooks/magic-context/pass-outcome.d.ts +1 -0
  69. package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/pending-materialization.d.ts +8 -0
  71. package/dist/hooks/magic-context/pending-materialization.d.ts.map +1 -0
  72. package/dist/hooks/magic-context/postprocess-read-cache.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
  74. package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
  75. package/dist/hooks/magic-context/read-session-chunk.d.ts +22 -1
  76. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/read-session-db.d.ts +3 -0
  78. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
  80. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/read-session-raw.d.ts +123 -1
  82. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/sentinel.d.ts +41 -1
  86. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts +8 -0
  88. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts.map +1 -0
  89. package/dist/hooks/magic-context/strip-content.d.ts +38 -24
  90. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/system-date-refresh.d.ts +29 -0
  92. package/dist/hooks/magic-context/system-date-refresh.d.ts.map +1 -0
  93. package/dist/hooks/magic-context/system-prompt-dates.d.ts +6 -0
  94. package/dist/hooks/magic-context/system-prompt-dates.d.ts.map +1 -0
  95. package/dist/hooks/magic-context/system-prompt-delivery.d.ts +47 -0
  96. package/dist/hooks/magic-context/system-prompt-delivery.d.ts.map +1 -0
  97. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  98. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/tag-messages.d.ts +19 -0
  100. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  101. package/dist/hooks/magic-context/tag-owner-warmup.d.ts +14 -0
  102. package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
  103. package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
  104. package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
  105. package/dist/hooks/magic-context/tool-drop-target.d.ts +2 -0
  106. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  107. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +7 -0
  108. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/transform.d.ts +21 -4
  110. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  111. package/dist/index-18yrysyj.js +1477 -0
  112. package/dist/{index-g2z2k5m4.js → index-3gj1ak4c.js} +2 -2
  113. package/dist/{index-5cep2r9n.js → index-5ty9rnbx.js} +60 -258
  114. package/dist/{index-amycb7b1.js → index-a5mw0xp3.js} +3 -3
  115. package/dist/{index-1j34a56j.js → index-czba8tw3.js} +266 -32
  116. package/dist/{index-qbkzdkk0.js → index-k59af64t.js} +6 -2
  117. package/dist/{index-53g38ggt.js → index-kmbme1v1.js} +1 -1
  118. package/dist/{index-5b7x80ec.js → index-kw21z5pe.js} +6055 -3973
  119. package/dist/{index-33gprt9g.js → index-yjv8dpht.js} +1 -1
  120. package/dist/{index-btp5tdc8.js → index-z2w1eq3y.js} +840 -456
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +114 -2
  123. package/dist/{message-time-backfill-phrwp2fz.js → message-time-backfill-sykevdax.js} +3 -3
  124. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  125. package/dist/{read-session-chunk-2xm4sc4y.js → read-session-chunk-863d6c06.js} +6 -4
  126. package/dist/recomp-orchestrator-wrjy3qym.js +21 -0
  127. package/dist/{safe-notification-target-ph3xd1ve.js → safe-notification-target-1jkf0m82.js} +2 -2
  128. package/dist/{send-session-notification-rnc4nrsr.js → send-session-notification-cz6ev088.js} +5 -5
  129. package/dist/shared/auto-search-lifecycle.d.ts +14 -0
  130. package/dist/shared/auto-search-lifecycle.d.ts.map +1 -1
  131. package/dist/shared/logger.d.ts.map +1 -1
  132. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  133. package/dist/shared/models-dev-temperature.d.ts +18 -0
  134. package/dist/shared/models-dev-temperature.d.ts.map +1 -0
  135. package/dist/shared/processed-image-eligibility.d.ts +22 -0
  136. package/dist/shared/processed-image-eligibility.d.ts.map +1 -0
  137. package/dist/shared/project-directory-key.d.ts +16 -0
  138. package/dist/shared/project-directory-key.d.ts.map +1 -1
  139. package/dist/shared/recovery-reasoning-prefix.d.ts +9 -0
  140. package/dist/shared/recovery-reasoning-prefix.d.ts.map +1 -0
  141. package/dist/shared/sqlite.d.ts +33 -1
  142. package/dist/shared/sqlite.d.ts.map +1 -1
  143. package/dist/shared/stable-json.d.ts.map +1 -1
  144. package/dist/shared/storage-file-mutex.d.ts +9 -0
  145. package/dist/shared/storage-file-mutex.d.ts.map +1 -0
  146. package/dist/shared/storage-permissions.d.ts +6 -0
  147. package/dist/shared/storage-permissions.d.ts.map +1 -1
  148. package/dist/shared/subagent-temperature.d.ts +46 -0
  149. package/dist/shared/subagent-temperature.d.ts.map +1 -0
  150. package/dist/shared/tag-owner-id.d.ts +1 -0
  151. package/dist/shared/tag-owner-id.d.ts.map +1 -1
  152. package/dist/shared/tag-transcript.d.ts +9 -0
  153. package/dist/shared/tag-transcript.d.ts.map +1 -1
  154. package/dist/shared/window-geometry.d.ts +9 -66
  155. package/dist/shared/window-geometry.d.ts.map +1 -1
  156. package/dist/standalone-plugin-marker.d.ts +8 -0
  157. package/dist/standalone-plugin-marker.d.ts.map +1 -0
  158. package/dist/tools/ctx-expand/search-archive.d.ts +4 -0
  159. package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -0
  160. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  161. package/dist/tools/ctx-reduce/constants.d.ts +1 -2
  162. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  163. package/dist/tools/ctx-reduce/tools.d.ts +1 -1
  164. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  165. package/dist/v2/hooks/context.d.ts +1 -1
  166. package/dist/v2/hooks/context.d.ts.map +1 -1
  167. package/dist/v2/hooks/copied-tag-notation.d.ts +19 -0
  168. package/dist/v2/hooks/copied-tag-notation.d.ts.map +1 -0
  169. package/dist/v2/hooks/generate.d.ts +9 -2
  170. package/dist/v2/hooks/generate.d.ts.map +1 -1
  171. package/dist/v2/hooks/payload.d.ts.map +1 -1
  172. package/dist/v2/hooks/system-prompt-response.d.ts +5 -0
  173. package/dist/v2/hooks/system-prompt-response.d.ts.map +1 -0
  174. package/dist/v2/server.d.ts.map +1 -1
  175. package/dist/v2/server.js +34550 -30855
  176. package/dist/web/main.js +373 -19
  177. package/dist/{wrapup-orchestrator-yknks3ad.js → wrapup-orchestrator-bmybc5p5.js} +10 -10
  178. package/package.json +1 -1
  179. package/src/config/deprecated-config.ts +18 -0
  180. package/src/config/index.test.ts +13 -37
  181. package/src/config/index.ts +2 -2
  182. package/src/config/project-security.ts +3 -3
  183. package/src/config/schema/magic-context.ts +10 -6
  184. package/src/shared/auto-search-lifecycle.ts +43 -0
  185. package/src/shared/logger.ts +10 -2
  186. package/src/shared/models-dev-cache.ts +1 -7
  187. package/src/shared/models-dev-temperature.ts +107 -0
  188. package/src/shared/processed-image-eligibility.test.ts +68 -0
  189. package/src/shared/processed-image-eligibility.ts +41 -0
  190. package/src/shared/processed-image-position-review.json +20 -0
  191. package/src/shared/processed-image-position-review.test.ts +19 -0
  192. package/src/shared/project-directory-key.ts +20 -0
  193. package/src/shared/recovery-reasoning-prefix.test.ts +34 -0
  194. package/src/shared/recovery-reasoning-prefix.ts +35 -0
  195. package/src/shared/sqlite-acquisition-routing.test.ts +4 -3
  196. package/src/shared/sqlite-foreground-wait.test.ts +68 -0
  197. package/src/shared/sqlite-pass-scope.test.ts +4 -4
  198. package/src/shared/sqlite-writer-diagnostics.test.ts +52 -12
  199. package/src/shared/sqlite.ts +380 -39
  200. package/src/shared/stable-json.ts +37 -7
  201. package/src/shared/storage-file-mutex.test.ts +50 -0
  202. package/src/shared/storage-file-mutex.ts +130 -0
  203. package/src/shared/storage-permissions.ts +46 -0
  204. package/src/shared/subagent-temperature.ts +110 -0
  205. package/src/shared/tag-owner-id.ts +13 -0
  206. package/src/shared/tag-transcript.ts +118 -0
  207. package/src/shared/window-geometry.test.ts +102 -236
  208. package/src/shared/window-geometry.ts +23 -413
  209. package/dist/index-ramz5kkm.js +0 -514
  210. package/dist/recomp-orchestrator-frmwj9hz.js +0 -21
  211. package/src/shared/__fixtures__/window-overlay.v1.json +0 -337
@@ -0,0 +1,1477 @@
1
+ import {
2
+ Database,
3
+ getSqliteDatabasePath
4
+ } from "./index-czba8tw3.js";
5
+ import {
6
+ harnessOwnsOpenCodeStore,
7
+ log
8
+ } from "./index-k59af64t.js";
9
+
10
+ // src/hooks/magic-context/read-session-raw.ts
11
+ import { isMainThread } from "node:worker_threads";
12
+ function isStrictGapHealingMessage(message) {
13
+ if (message.storeType === "synthetic")
14
+ return true;
15
+ let sawTool = false;
16
+ for (const part of message.parts) {
17
+ if (!part || typeof part !== "object" || Array.isArray(part))
18
+ return false;
19
+ const record = part;
20
+ const type = typeof record.type === "string" ? record.type : "";
21
+ if (type === "tool" || type === "tool_use" || type === "tool_result") {
22
+ sawTool = true;
23
+ continue;
24
+ }
25
+ const text = typeof record.text === "string" ? record.text : typeof record.content === "string" ? record.content : "";
26
+ if (text.trim().length > 0 || type.length > 0)
27
+ return false;
28
+ }
29
+ return sawTool;
30
+ }
31
+ var RAW_MESSAGE_PARTS_BY_ID_SQL = "SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001) ORDER BY time_created ASC, id ASC";
32
+ function isRawMessageRow(row) {
33
+ if (row === null || typeof row !== "object")
34
+ return false;
35
+ const candidate = row;
36
+ return typeof candidate.id === "string" && typeof candidate.data === "string";
37
+ }
38
+ function isRawPartRow(row) {
39
+ if (row === null || typeof row !== "object")
40
+ return false;
41
+ const candidate = row;
42
+ return typeof candidate.message_id === "string" && typeof candidate.data === "string";
43
+ }
44
+ function parseJsonRecord(value) {
45
+ try {
46
+ const parsed = JSON.parse(value);
47
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
48
+ return null;
49
+ }
50
+ return parsed;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function isRawCompactionSummaryInfo(info) {
56
+ if (info === null || typeof info !== "object" || Array.isArray(info))
57
+ return false;
58
+ const candidate = info;
59
+ return candidate.summary === true && candidate.finish === "stop";
60
+ }
61
+ var RAW_METADATA_KEEP_CHARS = 16384;
62
+ var RAW_METADATA_KEPT_KEYS = [
63
+ "description",
64
+ "title",
65
+ "userAnswer",
66
+ "answers",
67
+ "answer",
68
+ "selectedOptions",
69
+ "customInput",
70
+ "results"
71
+ ];
72
+ var RAW_PART_DATA_SQL = `CASE
73
+ WHEN json_valid(data) = 1
74
+ AND json_type(data, '$.state.metadata') = 'object'
75
+ AND COALESCE(json_extract(data, '$.state.status'), '') <> 'error'
76
+ AND length(json_extract(data, '$.state.metadata')) > ${RAW_METADATA_KEEP_CHARS}
77
+ THEN json_set(data, '$.state.metadata', json_patch('{}', json_object(${RAW_METADATA_KEPT_KEYS.map((key) => `'${key}', json_extract(data, '$.state.metadata.${key}')`).join(", ")})))
78
+ ELSE data
79
+ END`;
80
+ function parseJsonUnknown(value) {
81
+ try {
82
+ return JSON.parse(value);
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+ function attachRawPartVersion(value, timeUpdated) {
88
+ if (value === null || typeof value !== "object" || Array.isArray(value))
89
+ return value;
90
+ if (typeof timeUpdated !== "number")
91
+ return value;
92
+ try {
93
+ Object.defineProperty(value, "__magicContextPartUpdatedAt", {
94
+ value: timeUpdated,
95
+ enumerable: false,
96
+ configurable: true
97
+ });
98
+ } catch {}
99
+ return value;
100
+ }
101
+ function readRawSessionMessagesFromDb(db, sessionId) {
102
+ const messageRows = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
103
+ const partsByMessageId = new Map;
104
+ const partMessageBatchSize = 128;
105
+ for (let offset = 0;offset < messageRows.length; offset += partMessageBatchSize) {
106
+ const messageIds = messageRows.slice(offset, offset + partMessageBatchSize).map((row) => row.id);
107
+ if (messageIds.length === 0)
108
+ continue;
109
+ const placeholders = messageIds.map(() => "?").join(", ");
110
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
111
+ FROM part
112
+ WHERE +session_id = ?
113
+ AND likelihood(message_id IN (${placeholders}), 0.000001)
114
+ ORDER BY message_id ASC, time_created ASC, id ASC`).all(sessionId, ...messageIds).filter(isRawPartRow);
115
+ for (const part of partRows) {
116
+ const list = partsByMessageId.get(part.message_id) ?? [];
117
+ list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
118
+ partsByMessageId.set(part.message_id, list);
119
+ }
120
+ }
121
+ const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
122
+ return filtered.flatMap((row, index) => {
123
+ const info = parseJsonRecord(row.data);
124
+ if (!info)
125
+ return [];
126
+ const role = typeof info.role === "string" ? info.role : "unknown";
127
+ return {
128
+ ordinal: index + 1,
129
+ id: row.id,
130
+ role,
131
+ parts: partsByMessageId.get(row.id) ?? [],
132
+ createdAt: row.time_created ?? null,
133
+ version: row.time_updated ?? null
134
+ };
135
+ });
136
+ }
137
+ function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark = Number.MAX_SAFE_INTEGER, after) {
138
+ const messageRows = readRawMessagePageRows(db, sessionId, afterOrdinal, limit, finalWatermark, after);
139
+ if (messageRows.length === 0)
140
+ return [];
141
+ const placeholders = messageRows.map(() => "?").join(", ");
142
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
143
+ FROM part
144
+ WHERE +session_id = ?
145
+ AND likelihood(message_id IN (${placeholders}), 0.000001)
146
+ ORDER BY message_id ASC, time_created ASC, id ASC`).all(sessionId, ...messageRows.map((row) => row.id)).filter(isRawPartRow);
147
+ return assembleRawMessagePage(messageRows, partRows);
148
+ }
149
+ function readRawMessagePageRows(db, sessionId, afterOrdinal, limit, finalWatermark, after) {
150
+ try {
151
+ return readRawMessagePageRowsBounded(db, sessionId, afterOrdinal, limit, finalWatermark, after);
152
+ } catch (error) {
153
+ if (error !== ORDINALS_COLD)
154
+ throw error;
155
+ if (warmRawSessionOrdinalsForDb(db, sessionId)) {
156
+ try {
157
+ return readRawMessagePageRowsBounded(db, sessionId, afterOrdinal, limit, finalWatermark, after);
158
+ } catch (retry) {
159
+ if (retry !== ORDINALS_COLD)
160
+ throw retry;
161
+ }
162
+ }
163
+ return readRawMessagePageRowsOffset(db, sessionId, afterOrdinal, limit, finalWatermark, after);
164
+ }
165
+ }
166
+ var ORDINALS_COLD = Symbol("mmcCanonicalOrdinalsCold");
167
+ function readRawMessagePageRowsBounded(db, sessionId, afterOrdinal, limit, finalWatermark, after) {
168
+ const remaining = Math.max(0, Math.floor(finalWatermark) - Math.floor(afterOrdinal));
169
+ const pageSize = Math.min(Math.max(1, Math.floor(limit)), remaining);
170
+ if (pageSize === 0)
171
+ return [];
172
+ return withReadSnapshot(db, () => {
173
+ const ordinal = Math.max(0, Math.floor(afterOrdinal));
174
+ const points = currentOrdinalCheckpoints(db, sessionId)?.scan.checkpoints ?? [];
175
+ let low = 0;
176
+ let high = points.length;
177
+ while (low < high) {
178
+ const middle = Math.floor((low + high) / 2);
179
+ if (points[middle].ordinal <= ordinal)
180
+ low = middle + 1;
181
+ else
182
+ high = middle;
183
+ }
184
+ const floor = points[low - 1];
185
+ const useFloor = floor && (!after || compareOrdinalKeys(floor, after) > 0);
186
+ const anchor = useFloor ? floor : after;
187
+ const skip = anchor === after && after ? 0 : ordinal - (floor?.ordinal ?? 0);
188
+ if (skip > ORDINAL_LOOKUP_LIMIT)
189
+ throw ORDINALS_COLD;
190
+ const rawLimit = skip + pageSize + ORDINAL_LOOKUP_LIMIT;
191
+ const rows = db.prepare(`SELECT id, data, time_created, time_updated, ${EXCLUDED_ROW_SQL} AS excluded
192
+ FROM message WHERE id IN (
193
+ SELECT id FROM message WHERE session_id = ?
194
+ ${anchor ? "AND (time_created, id) > (?, ?)" : ""}
195
+ ORDER BY time_created, id LIMIT ?)
196
+ ORDER BY time_created, id`).all(sessionId, ...anchor ? [anchor.timeCreated, anchor.id] : [], rawLimit);
197
+ rawMessagePageRowsVisited += rows.length;
198
+ const eligible = rows.filter((row) => row.excluded !== 1);
199
+ if (eligible.length <= skip && rows.length === rawLimit)
200
+ throw ORDINALS_COLD;
201
+ return eligible.slice(skip, skip + pageSize).filter(isRawMessageRow).map((row, index) => ({
202
+ ...row,
203
+ ordinal: ordinal + index + 1
204
+ }));
205
+ });
206
+ }
207
+ var rawMessagePageRowsVisited = 0;
208
+ function readRawMessagePageRowsOffset(db, sessionId, afterOrdinal, limit, finalWatermark, after) {
209
+ const remaining = Math.max(0, Math.floor(finalWatermark) - Math.floor(afterOrdinal));
210
+ const pageSize = Math.min(Math.max(1, Math.floor(limit)), remaining);
211
+ if (pageSize === 0)
212
+ return [];
213
+ const parameters = [sessionId];
214
+ if (after)
215
+ parameters.push(after.timeCreated, after.timeCreated, after.timeCreated, after.id);
216
+ parameters.push(pageSize);
217
+ if (!after)
218
+ parameters.push(Math.max(0, Math.floor(afterOrdinal)));
219
+ return db.prepare(`SELECT id, data, time_created, time_updated
220
+ FROM message
221
+ WHERE session_id = ?
222
+ ${after ? "AND time_created >= ? AND (time_created > ? OR (time_created = ? AND id > ?))" : ""}
223
+ AND NOT (
224
+ CASE WHEN json_valid(data) = 1
225
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
226
+ ELSE 0 END = 1
227
+ AND CASE WHEN json_valid(data) = 1
228
+ THEN COALESCE(json_extract(data, '$.finish'), '')
229
+ ELSE '' END = 'stop'
230
+ )
231
+ ORDER BY time_created ASC, id ASC
232
+ LIMIT ? ${after ? "" : "OFFSET ?"}`).all(...parameters).filter(isRawMessageRow).map((row, index) => ({
233
+ ...row,
234
+ ordinal: Math.floor(afterOrdinal) + index + 1
235
+ }));
236
+ }
237
+ function assembleRawMessagePage(messageRows, partRows) {
238
+ const partsByMessageId = new Map;
239
+ for (const part of partRows) {
240
+ const list = partsByMessageId.get(part.message_id) ?? [];
241
+ list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
242
+ partsByMessageId.set(part.message_id, list);
243
+ }
244
+ return messageRows.map((row) => {
245
+ const info = parseJsonRecord(row.data);
246
+ return {
247
+ ordinal: row.ordinal,
248
+ id: row.id,
249
+ role: typeof info?.role === "string" ? info.role : "unknown",
250
+ parts: partsByMessageId.get(row.id) ?? [],
251
+ createdAt: row.time_created ?? null,
252
+ version: row.time_updated ?? null
253
+ };
254
+ });
255
+ }
256
+ var RAW_SUMMARY_TEXT_MAX_CHARS = 8192;
257
+ var RAW_SUMMARY_ARG_MAX_CHARS = 512;
258
+ var RAW_SUMMARY_TOOL_INPUT_KEYS = [
259
+ "description",
260
+ "filePath",
261
+ "path",
262
+ "pattern",
263
+ "query",
264
+ "symbol",
265
+ "module",
266
+ "action"
267
+ ];
268
+ function summaryStringField(jsonPath) {
269
+ return `CASE WHEN json_type(data, '${jsonPath}') = 'text' THEN substr(json_extract(data, '${jsonPath}'), 1, ${RAW_SUMMARY_ARG_MAX_CHARS}) END`;
270
+ }
271
+ var RAW_SUMMARY_PART_DATA_SQL = `CASE
272
+ WHEN json_extract(data, '$.type') = 'text'
273
+ THEN json_set(data, '$.text', substr(json_extract(data, '$.text'), 1, ${RAW_SUMMARY_TEXT_MAX_CHARS}))
274
+ ELSE json_object(
275
+ 'type', 'tool',
276
+ 'tool', ${summaryStringField("$.tool")},
277
+ 'callID', ${summaryStringField("$.callID")},
278
+ 'state', json_object(
279
+ 'status', ${summaryStringField("$.state.status")},
280
+ 'input', json_object(${RAW_SUMMARY_TOOL_INPUT_KEYS.map((key) => `'${key}', ${summaryStringField(`$.state.input.${key}`)}`).join(", ")}),
281
+ 'metadata', json_object('description', ${summaryStringField("$.state.metadata.description")})
282
+ )
283
+ )
284
+ END`;
285
+ function readRawSessionMessageSummaryPageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark = Number.MAX_SAFE_INTEGER, after) {
286
+ const messageRows = readRawMessagePageRows(db, sessionId, afterOrdinal, limit, finalWatermark, after);
287
+ if (messageRows.length === 0)
288
+ return [];
289
+ const placeholders = messageRows.map(() => "?").join(", ");
290
+ const partRows = db.prepare(`SELECT message_id, ${RAW_SUMMARY_PART_DATA_SQL} AS data, time_updated
291
+ FROM part
292
+ WHERE +session_id = ?
293
+ AND likelihood(message_id IN (${placeholders}), 0.000001)
294
+ AND json_valid(data) = 1
295
+ AND json_extract(data, '$.type') IN ('text', 'tool')
296
+ ORDER BY message_id ASC, time_created ASC, id ASC`).all(sessionId, ...messageRows.map((row) => row.id)).filter(isRawPartRow);
297
+ return assembleRawMessagePage(messageRows, partRows);
298
+ }
299
+ function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
300
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, null, null);
301
+ if (indexed !== null)
302
+ return indexed;
303
+ const row = db.prepare(`SELECT COUNT(*) AS count
304
+ FROM message
305
+ WHERE session_id = ?
306
+ AND NOT (
307
+ CASE WHEN json_valid(data) = 1
308
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
309
+ ELSE 0 END = 1
310
+ AND CASE WHEN json_valid(data) = 1
311
+ THEN COALESCE(json_extract(data, '$.finish'), '')
312
+ ELSE '' END = 'stop'
313
+ )`).get(sessionId);
314
+ return typeof row?.count === "number" ? row.count : 0;
315
+ }
316
+ function readRawSessionMessageIdOrdinalsForRangeFromDb(db, sessionId, fromOrdinal, toOrdinal) {
317
+ const from = Math.max(1, Math.floor(fromOrdinal));
318
+ const to = Math.floor(toOrdinal);
319
+ if (to < from)
320
+ return new Map;
321
+ const rows = db.prepare(`
322
+ SELECT id, data FROM message WHERE session_id = ?
323
+ AND CASE WHEN json_valid(data) THEN NOT (
324
+ COALESCE(json_type(data, '$.summary'), '') = 'true'
325
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
326
+ ) ELSE 1 END
327
+ ORDER BY time_created, id LIMIT ? OFFSET ?
328
+ `).all(sessionId, to - from + 1, from - 1).filter(isRawMessageRow);
329
+ const result = new Map;
330
+ for (const [index, row] of rows.entries()) {
331
+ if (parseJsonRecord(row.data))
332
+ result.set(row.id, from + index);
333
+ }
334
+ return result;
335
+ }
336
+ function isAnchorRow(row) {
337
+ return row !== null && typeof row === "object" && typeof row.time_created === "number" && typeof row.id === "string";
338
+ }
339
+ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
340
+ const anchorRow = db.prepare("SELECT time_created, id, data FROM message WHERE id = ? AND session_id = ?").get(anchorMessageId, sessionId);
341
+ if (!isAnchorRow(anchorRow))
342
+ return null;
343
+ const anchorInfo = parseJsonRecord(anchorRow.data ?? "");
344
+ if (anchorInfo?.summary === true && anchorInfo?.finish === "stop")
345
+ return null;
346
+ const messageRows = db.prepare(`SELECT id, data, time_created, time_updated FROM message
347
+ WHERE session_id = ?
348
+ AND (time_created > ? OR (time_created = ? AND id >= ?))
349
+ ORDER BY time_created ASC, id ASC`).all(sessionId, anchorRow.time_created, anchorRow.time_created, anchorRow.id).filter(isRawMessageRow);
350
+ const filtered = messageRows.filter((row) => {
351
+ const info = parseJsonRecord(row.data);
352
+ return !(info?.summary === true && info?.finish === "stop");
353
+ });
354
+ const ids = filtered.map((row) => row.id);
355
+ const partsByMessageId = new Map;
356
+ if (ids.length > 0) {
357
+ const CHUNK = 800;
358
+ for (let i = 0;i < ids.length; i += CHUNK) {
359
+ const slice = ids.slice(i, i + CHUNK);
360
+ const placeholders = slice.map(() => "?").join(",");
361
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id IN (${placeholders}), 0.000001) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
362
+ for (const part of partRows) {
363
+ const list = partsByMessageId.get(part.message_id) ?? [];
364
+ list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
365
+ partsByMessageId.set(part.message_id, list);
366
+ }
367
+ }
368
+ }
369
+ const messages = [];
370
+ let ord = baseOrdinal;
371
+ for (const row of filtered) {
372
+ const info = parseJsonRecord(row.data);
373
+ if (!info) {
374
+ ord += 1;
375
+ continue;
376
+ }
377
+ messages.push({
378
+ ordinal: ord,
379
+ id: row.id,
380
+ role: typeof info.role === "string" ? info.role : "unknown",
381
+ parts: partsByMessageId.get(row.id) ?? [],
382
+ createdAt: row.time_created ?? null,
383
+ version: row.time_updated ?? null
384
+ });
385
+ ord += 1;
386
+ }
387
+ return { messages, absoluteMessageCount: Math.max(0, ord - 1) };
388
+ }
389
+ function extractInMemoryMessageViews(messages) {
390
+ return messages.map((m) => {
391
+ const info = m.info ?? {};
392
+ return {
393
+ id: typeof info.id === "string" ? info.id : "",
394
+ role: typeof info.role === "string" ? info.role : "unknown",
395
+ parts: Array.isArray(m.parts) ? m.parts : [],
396
+ summary: info.summary === true ? true : undefined,
397
+ finish: typeof info.finish === "string" ? info.finish : undefined
398
+ };
399
+ });
400
+ }
401
+ function buildInMemoryTailRawMessages(args) {
402
+ const { messages, lastCompartmentEnd, anchorMessageId } = args;
403
+ const filtered = messages.filter((m) => !(m.summary === true && m.finish === "stop"));
404
+ if (filtered.length === 0)
405
+ return null;
406
+ let startIndex = 0;
407
+ let baseOrdinal;
408
+ let anchorFound = false;
409
+ if (anchorMessageId) {
410
+ const anchorIndex = filtered.findIndex((m) => m.id === anchorMessageId);
411
+ if (anchorIndex >= 0) {
412
+ anchorFound = true;
413
+ startIndex = anchorIndex;
414
+ baseOrdinal = lastCompartmentEnd;
415
+ } else {
416
+ baseOrdinal = Math.max(1, lastCompartmentEnd + 1);
417
+ }
418
+ } else {
419
+ baseOrdinal = Math.max(1, lastCompartmentEnd + 1);
420
+ }
421
+ const out = [];
422
+ let ord = baseOrdinal;
423
+ for (let i = startIndex;i < filtered.length; i += 1) {
424
+ const m = filtered[i];
425
+ if (!m.id || typeof m.id !== "string") {
426
+ ord += 1;
427
+ continue;
428
+ }
429
+ out.push({
430
+ ordinal: ord,
431
+ id: m.id,
432
+ role: typeof m.role === "string" ? m.role : "unknown",
433
+ parts: m.parts ?? [],
434
+ version: null
435
+ });
436
+ ord += 1;
437
+ }
438
+ return { messages: out, absoluteMessageCount: Math.max(0, ord - 1), anchorFound };
439
+ }
440
+ var ORDINAL_SUMMARY_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
441
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
442
+ ELSE 0 END = 1)`;
443
+ var ORDINAL_STOP_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
444
+ THEN COALESCE(json_extract(data, '$.finish'), '')
445
+ ELSE '' END = 'stop')`;
446
+ function ordinalRangeSql(lowerBound) {
447
+ return `SELECT COUNT(*) AS visited,
448
+ COALESCE(SUM(CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}
449
+ THEN 0 ELSE 1 END), 0) AS eligible
450
+ FROM message
451
+ WHERE session_id = ?
452
+ ${lowerBound ? "AND (time_created, id) > (?, ?)" : ""}
453
+ AND (time_created, id) <= (?, ?)`;
454
+ }
455
+ var ORDINAL_RANGE_FROM_START_SQL = ordinalRangeSql(false);
456
+ var ORDINAL_RANGE_BETWEEN_SQL = ordinalRangeSql(true);
457
+ function readStoreStamp(db) {
458
+ try {
459
+ const row = db.prepare(`SELECT data_version AS dataVersion, total_changes() AS totalChanges,
460
+ (SELECT schema_version FROM pragma_schema_version) AS schemaVersion
461
+ FROM pragma_data_version`).get();
462
+ if (typeof row?.dataVersion !== "number" || typeof row.totalChanges !== "number" || typeof row.schemaVersion !== "number")
463
+ return null;
464
+ return {
465
+ dataVersion: row.dataVersion,
466
+ totalChanges: row.totalChanges,
467
+ schemaVersion: row.schemaVersion
468
+ };
469
+ } catch {
470
+ return null;
471
+ }
472
+ }
473
+ function sameStoreStamp(left, right) {
474
+ return left !== null && right !== null && left.dataVersion === right.dataVersion && left.totalChanges === right.totalChanges && left.schemaVersion === right.schemaVersion;
475
+ }
476
+ var ordinalWatermarks = new WeakMap;
477
+ var ordinalWatermarkEpochs = new Map;
478
+ var ordinalRowsVisited = 0;
479
+ var STABLE_STORE_ATTEMPTS = 3;
480
+ function compareOrdinalKeys(left, right) {
481
+ if (left.timeCreated !== right.timeCreated)
482
+ return left.timeCreated < right.timeCreated ? -1 : 1;
483
+ if (left.id === right.id)
484
+ return 0;
485
+ return Buffer.compare(Buffer.from(left.id, "utf8"), Buffer.from(right.id, "utf8"));
486
+ }
487
+ function countEligibleOrdinals(db, sessionId, after, through) {
488
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, after, through);
489
+ if (indexed !== null)
490
+ return indexed;
491
+ const row = after ? db.prepare(ORDINAL_RANGE_BETWEEN_SQL).get(sessionId, after.timeCreated, after.id, through.timeCreated, through.id) : db.prepare(ORDINAL_RANGE_FROM_START_SQL).get(sessionId, through.timeCreated, through.id);
492
+ const visited = typeof row?.visited === "number" ? row.visited : 0;
493
+ ordinalRowsVisited += visited;
494
+ ordinalJsonRowsRead += visited;
495
+ return typeof row?.eligible === "number" ? row.eligible : 0;
496
+ }
497
+ var EXCLUDED_ROW_SQL = `${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}`;
498
+ var SUMMARY_ANCHOR_SPAN = 256;
499
+ var SUMMARY_ANCHOR_LIMIT = 64;
500
+ var summaryCandidateSets = new WeakMap;
501
+ var summaryCandidateEpochs = new Map;
502
+ var writtenSummaryIds = new Map;
503
+ var indexedCountUnsupported = new WeakSet;
504
+ var ordinalJsonRowsRead = 0;
505
+ var summaryFullScans = 0;
506
+ var ANCHOR_ROWS_SQL = `SELECT rowid AS rid, id, 1 AS kind FROM message
507
+ WHERE rowid > (SELECT MAX(rowid) FROM message) - ${SUMMARY_ANCHOR_SPAN}`;
508
+ var SUMMARY_FULL_SCAN_SQL = `${ANCHOR_ROWS_SQL}
509
+ UNION ALL
510
+ SELECT rowid, id, 2 FROM message WHERE session_id = ? AND ${ORDINAL_SUMMARY_FLAG_SQL}
511
+ UNION ALL
512
+ SELECT COUNT(*), NULL, 4 FROM message WHERE session_id = ?`;
513
+ var SUMMARY_SCAN_AFTER_SQL = `${ANCHOR_ROWS_SQL}
514
+ UNION ALL
515
+ SELECT rowid, id, CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} THEN 2 ELSE 3 END
516
+ FROM message WHERE rowid > ? AND +session_id = ?`;
517
+ var ANCHOR_CHECK_SQL = "SELECT rowid AS rid, id FROM message WHERE rowid IN (SELECT value FROM json_each(?))";
518
+ function survivingAnchor(db, set) {
519
+ if (set.anchors.length === 0)
520
+ return null;
521
+ const current = new Map(db.prepare(ANCHOR_CHECK_SQL).all(JSON.stringify(set.anchors.map((anchor) => anchor.rowid))).map((row) => [row.rid, row.id]));
522
+ return set.anchors.find((anchor) => current.get(anchor.rowid) === anchor.id);
523
+ }
524
+ var ordinalCheckpoints = new WeakMap;
525
+ var ordinalSummarySeeds = new WeakMap;
526
+ var ORDINAL_LOOKUP_LIMIT = 512;
527
+ function currentOrdinalCheckpoints(db, sessionId) {
528
+ const cached = ordinalCheckpoints.get(db)?.get(sessionId);
529
+ if (!cached)
530
+ return;
531
+ return withReadSnapshot(db, () => {
532
+ if (!extendOrdinalCheckpoints(db, sessionId, cached.scan, ORDINAL_LOOKUP_LIMIT))
533
+ return;
534
+ cached.epoch = getRawSessionSummaryEpoch(sessionId);
535
+ return cached;
536
+ });
537
+ }
538
+ function extendOrdinalCheckpoints(db, sessionId, scan, limit) {
539
+ const points = scan.checkpoints ?? [];
540
+ const exists = (point) => Boolean(db.prepare("SELECT 1 FROM message WHERE id=? AND session_id=? AND time_created=?").get(point.id, sessionId, point.timeCreated));
541
+ if (points.length && !exists(points[points.length - 1])) {
542
+ let low = 0;
543
+ let high = points.length;
544
+ while (low < high) {
545
+ const middle = Math.floor((low + high) / 2);
546
+ if (exists(points[middle]))
547
+ low = middle + 1;
548
+ else
549
+ high = middle;
550
+ }
551
+ points.length = low;
552
+ scan.total = points.at(-1)?.ordinal ?? 0;
553
+ scan.sessionRows = points.at(-1)?.storedCount ?? 0;
554
+ }
555
+ const tail = points.at(-1);
556
+ const rows = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message
557
+ WHERE session_id=?${tail ? " AND (time_created,id) > (?,?)" : ""}
558
+ ORDER BY time_created,id${limit === undefined ? "" : ` LIMIT ${limit + 1}`}`).all(sessionId, ...tail ? [tail.timeCreated, tail.id] : []);
559
+ ordinalJsonRowsRead += rows.length;
560
+ if (limit !== undefined && rows.length > limit)
561
+ return false;
562
+ let total = scan.total ?? 0;
563
+ if (rows.length && tail && (tail.storedCount ?? 0) % 256 !== 0)
564
+ points.pop();
565
+ for (let index = 0;index < rows.length; index++) {
566
+ const row = rows[index];
567
+ if (row.excluded !== 1)
568
+ total++;
569
+ const storedCount = scan.sessionRows + index + 1;
570
+ if (storedCount % 256 === 0 || index === rows.length - 1)
571
+ points.push({ id: row.id, timeCreated: row.timeCreated, ordinal: total, storedCount });
572
+ }
573
+ scan.checkpoints = points;
574
+ scan.sessionRows += rows.length;
575
+ scan.total = total;
576
+ return true;
577
+ }
578
+ function warmRawSessionOrdinalsForDb(db, sessionId) {
579
+ if (isRawSessionSummaryWarm(db, sessionId))
580
+ return true;
581
+ try {
582
+ const epoch = getRawSessionSummaryEpoch(sessionId);
583
+ const scan = scanRawSessionSummaryRows(db, sessionId);
584
+ installRawSessionSummaryScan(db, sessionId, scan, epoch, true);
585
+ } catch {
586
+ return false;
587
+ }
588
+ return isRawSessionSummaryWarm(db, sessionId);
589
+ }
590
+ function boundedOrdinalPrefix(db, sessionId, through) {
591
+ try {
592
+ return boundedOrdinalPrefixBounded(db, sessionId, through);
593
+ } catch (error) {
594
+ if (error !== ORDINALS_COLD)
595
+ throw error;
596
+ if (warmRawSessionOrdinalsForDb(db, sessionId)) {
597
+ try {
598
+ return boundedOrdinalPrefixBounded(db, sessionId, through);
599
+ } catch (retry) {
600
+ if (retry !== ORDINALS_COLD)
601
+ throw retry;
602
+ }
603
+ }
604
+ const bindings = [sessionId];
605
+ if (through)
606
+ bindings.push(through.timeCreated, through.id);
607
+ const rows = db.prepare(`SELECT ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE id IN (
608
+ SELECT id FROM message WHERE session_id = ?${through ? " AND (time_created, id) <= (?, ?)" : ""}
609
+ ORDER BY time_created, id)`).all(...bindings);
610
+ ordinalRowsVisited += rows.length;
611
+ ordinalJsonRowsRead += rows.length;
612
+ return rows.reduce((sum, row) => sum + (row.excluded === 1 ? 0 : 1), 0);
613
+ }
614
+ }
615
+ function boundedOrdinalPrefixBounded(db, sessionId, through) {
616
+ const cached = currentOrdinalCheckpoints(db, sessionId);
617
+ if (cached && through === null)
618
+ return cached.scan.total ?? 0;
619
+ let floor;
620
+ if (cached && through) {
621
+ const points = cached.scan.checkpoints ?? [];
622
+ let low = 0;
623
+ let high = points.length;
624
+ while (low < high) {
625
+ const middle = Math.floor((low + high) / 2);
626
+ if (compareOrdinalKeys(points[middle], through) <= 0)
627
+ low = middle + 1;
628
+ else
629
+ high = middle;
630
+ }
631
+ floor = points[low - 1];
632
+ if (floor && compareOrdinalKeys(floor, through) === 0)
633
+ return floor.ordinal;
634
+ }
635
+ const bindings = [sessionId];
636
+ if (floor)
637
+ bindings.push(floor.timeCreated, floor.id);
638
+ if (through)
639
+ bindings.push(through.timeCreated, through.id);
640
+ const rows = db.prepare(`SELECT ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE id IN (
641
+ SELECT id FROM message WHERE session_id = ?${floor ? " AND (time_created, id) > (?, ?)" : ""}${through ? " AND (time_created, id) <= (?, ?)" : ""}
642
+ ORDER BY time_created, id LIMIT ${ORDINAL_LOOKUP_LIMIT + 1})`).all(...bindings);
643
+ ordinalRowsVisited += rows.length;
644
+ ordinalJsonRowsRead += rows.length;
645
+ if (rows.length > ORDINAL_LOOKUP_LIMIT)
646
+ throw ORDINALS_COLD;
647
+ return (floor?.ordinal ?? 0) + rows.reduce((sum, row) => sum + (row.excluded === 1 ? 0 : 1), 0);
648
+ }
649
+ function scanRawSessionSummaryRows(db, sessionId, previous) {
650
+ return withReadSnapshot(db, () => {
651
+ if (previous?.checkpoints) {
652
+ extendOrdinalCheckpoints(db, sessionId, previous);
653
+ return previous;
654
+ }
655
+ if (previous) {
656
+ const sessions = summaryCandidateSets.get(db) ?? new Map;
657
+ sessions.set(sessionId, {
658
+ epoch: getRawSessionSummaryEpoch(sessionId),
659
+ anchors: previous.anchors,
660
+ ids: new Set(previous.ids)
661
+ });
662
+ summaryCandidateSets.set(db, sessions);
663
+ }
664
+ const incremental = previous && isRawSessionSummaryWarm(db, sessionId) ? summaryCandidatesOf(db, sessionId) : undefined;
665
+ const rows = incremental ? [] : db.prepare(SUMMARY_FULL_SCAN_SQL).all(sessionId, sessionId);
666
+ const anchors = [];
667
+ const ids = [];
668
+ let sessionRows = 0;
669
+ for (const row of rows) {
670
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
671
+ anchors.push({ rowid: row.rid, id: row.id });
672
+ else if (row.kind === 2 && typeof row.id === "string")
673
+ ids.push(row.id);
674
+ else if (row.kind === 4 && typeof row.rid === "number")
675
+ sessionRows = row.rid;
676
+ }
677
+ if (incremental) {
678
+ ids.push(...incremental.ids);
679
+ anchors.push(...summaryCandidateSets.get(db)?.get(sessionId)?.anchors ?? []);
680
+ sessionRows = countStoredRawSessionRowsIndexed(db, sessionId);
681
+ }
682
+ anchors.sort((left, right) => right.rowid - left.rowid);
683
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
684
+ const keys = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE session_id = ?
685
+ ORDER BY time_created, id`).all(sessionId);
686
+ const checkpoints = [];
687
+ let total = 0;
688
+ for (let index = 0;index < keys.length; index++) {
689
+ const key = keys[index];
690
+ if (key.excluded !== 1)
691
+ total++;
692
+ if (index % 256 === 255 || index === keys.length - 1)
693
+ checkpoints.push({
694
+ id: key.id,
695
+ timeCreated: key.timeCreated,
696
+ ordinal: total,
697
+ storedCount: index + 1
698
+ });
699
+ }
700
+ return { anchors, ids, sessionRows, checkpoints, total };
701
+ });
702
+ }
703
+ function getRawSessionSummaryEpoch(sessionId) {
704
+ return summaryCandidateEpochs.get(sessionId) ?? 0;
705
+ }
706
+ function installRawSessionSummaryScan(db, sessionId, scan, epoch, workerPrepared = false) {
707
+ if (epoch !== getRawSessionSummaryEpoch(sessionId))
708
+ return false;
709
+ const seeds = ordinalSummarySeeds.get(db) ?? new Map;
710
+ seeds.set(sessionId, { epoch, scan });
711
+ ordinalSummarySeeds.set(db, seeds);
712
+ if (scan.checkpoints && workerPrepared) {
713
+ const counts = ordinalCheckpoints.get(db) ?? new Map;
714
+ counts.set(sessionId, { epoch, scan });
715
+ ordinalCheckpoints.set(db, counts);
716
+ } else if (getSqliteDatabasePath(db) && isMainThread)
717
+ return false;
718
+ if (isRawSessionSummaryWarm(db, sessionId))
719
+ return true;
720
+ let sessions = summaryCandidateSets.get(db);
721
+ if (!sessions) {
722
+ sessions = new Map;
723
+ summaryCandidateSets.set(db, sessions);
724
+ }
725
+ sessions.set(sessionId, { epoch, anchors: [...scan.anchors], ids: new Set(scan.ids) });
726
+ return isRawSessionSummaryWarm(db, sessionId);
727
+ }
728
+ function isRawSessionSummaryWarm(db, sessionId) {
729
+ if (isMainThread && getSqliteDatabasePath(db)) {
730
+ return Boolean(currentOrdinalCheckpoints(db, sessionId));
731
+ }
732
+ const set = summaryCandidateSets.get(db)?.get(sessionId);
733
+ if (!set || set.epoch !== getRawSessionSummaryEpoch(sessionId))
734
+ return false;
735
+ try {
736
+ return survivingAnchor(db, set) !== undefined;
737
+ } catch {
738
+ return false;
739
+ }
740
+ }
741
+ function countStoredRawSessionRowsIndexed(db, sessionId) {
742
+ const row = db.prepare("SELECT COUNT(*) AS count FROM message WHERE session_id = ?").get(sessionId);
743
+ return typeof row?.count === "number" ? row.count : 0;
744
+ }
745
+ function summaryCandidatesOf(db, sessionId) {
746
+ let sessions = summaryCandidateSets.get(db);
747
+ if (!sessions) {
748
+ sessions = new Map;
749
+ summaryCandidateSets.set(db, sessions);
750
+ }
751
+ const epoch = getRawSessionSummaryEpoch(sessionId);
752
+ const cached = sessions.get(sessionId);
753
+ const floor = cached && cached.epoch === epoch ? survivingAnchor(db, cached) : undefined;
754
+ let ids;
755
+ let anchors;
756
+ if (floor === undefined) {
757
+ const scan = scanRawSessionSummaryRows(db, sessionId);
758
+ summaryFullScans += 1;
759
+ ordinalJsonRowsRead += scan.sessionRows;
760
+ ids = new Set(scan.ids);
761
+ anchors = scan.anchors;
762
+ } else {
763
+ const rows = db.prepare(SUMMARY_SCAN_AFTER_SQL).all(floor?.rowid ?? 0, sessionId);
764
+ ids = cached?.ids ?? new Set;
765
+ anchors = [];
766
+ for (const row of rows) {
767
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
768
+ anchors.push({ rowid: row.rid, id: row.id });
769
+ else if (row.kind === 2 && typeof row.id === "string")
770
+ ids.add(row.id);
771
+ if (row.kind === 2 || row.kind === 3)
772
+ ordinalJsonRowsRead += 1;
773
+ }
774
+ anchors.sort((left, right) => right.rowid - left.rowid);
775
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
776
+ }
777
+ for (const id of writtenSummaryIds.get(sessionId) ?? [])
778
+ ids.add(id);
779
+ sessions.set(sessionId, { epoch, anchors, ids });
780
+ const top = anchors[0];
781
+ return { ids: [...ids], floorRowid: top?.rowid ?? 0, floorId: top?.id ?? null };
782
+ }
783
+ function canonicalCountSql(lower, upper) {
784
+ const range = `${lower ? " AND (time_created, id) > (?, ?)" : ""}${upper ? " AND (time_created, id) <= (?, ?)" : ""}`;
785
+ return `SELECT
786
+ (SELECT COUNT(*) FROM message WHERE session_id = ?${range}) AS stored,
787
+ (SELECT COUNT(*) FROM message
788
+ WHERE id IN (SELECT value FROM json_each(?)) AND +session_id = ?${range}
789
+ AND ${EXCLUDED_ROW_SQL}) AS knownExcluded,
790
+ (SELECT COUNT(*) FROM message
791
+ WHERE rowid > ? AND +session_id = ? AND id NOT IN (SELECT value FROM json_each(?))${range}
792
+ AND ${EXCLUDED_ROW_SQL}) AS newExcluded,
793
+ (SELECT id FROM message WHERE rowid = ?) AS floorId`;
794
+ }
795
+ var CANONICAL_COUNT_SQL = new Map;
796
+ function canonicalCountStatementText(lower, upper) {
797
+ const key = `${lower}:${upper}`;
798
+ let sql = CANONICAL_COUNT_SQL.get(key);
799
+ if (!sql) {
800
+ sql = canonicalCountSql(lower, upper);
801
+ CANONICAL_COUNT_SQL.set(key, sql);
802
+ }
803
+ return sql;
804
+ }
805
+ function countCanonicalOrdinalsIndexed(db, sessionId, after, through) {
806
+ if (isMainThread && getSqliteDatabasePath(db)) {
807
+ return withReadSnapshot(db, () => boundedOrdinalPrefix(db, sessionId, through) - (after ? boundedOrdinalPrefix(db, sessionId, after) : 0));
808
+ }
809
+ if (indexedCountUnsupported.has(db))
810
+ return null;
811
+ const range = [];
812
+ if (after)
813
+ range.push(after.timeCreated, after.id);
814
+ if (through)
815
+ range.push(through.timeCreated, through.id);
816
+ try {
817
+ return withReadSnapshot(db, () => {
818
+ for (let attempt = 0;attempt < STABLE_STORE_ATTEMPTS; attempt += 1) {
819
+ const view = summaryCandidatesOf(db, sessionId);
820
+ const ids = JSON.stringify(view.ids);
821
+ const row = db.prepare(canonicalCountStatementText(after !== null, through !== null)).get(sessionId, ...range, ids, sessionId, ...range, view.floorRowid, sessionId, ids, ...range, view.floorRowid);
822
+ if (view.floorId !== null && row?.floorId !== view.floorId)
823
+ continue;
824
+ const stored = typeof row?.stored === "number" ? row.stored : 0;
825
+ const excluded = (typeof row?.knownExcluded === "number" ? row.knownExcluded : 0) + (typeof row?.newExcluded === "number" ? row.newExcluded : 0);
826
+ ordinalRowsVisited += stored;
827
+ ordinalJsonRowsRead += view.ids.length;
828
+ return stored - excluded;
829
+ }
830
+ return null;
831
+ });
832
+ } catch (error) {
833
+ if (/no such (column|table|function)|json_each/i.test(String(error))) {
834
+ indexedCountUnsupported.add(db);
835
+ return null;
836
+ }
837
+ throw error;
838
+ }
839
+ }
840
+ function connectionHasOpenTransaction(db) {
841
+ const state = db;
842
+ return state.inTransaction === true || state.isTransaction === true;
843
+ }
844
+ function withReadSnapshot(db, read) {
845
+ if (connectionHasOpenTransaction(db))
846
+ return read();
847
+ db.exec("BEGIN");
848
+ try {
849
+ return read();
850
+ } finally {
851
+ if (connectionHasOpenTransaction(db))
852
+ db.exec("ROLLBACK");
853
+ }
854
+ }
855
+ function reusableWatermark(db, sessionId, stamp) {
856
+ const watermark = ordinalWatermarks.get(db)?.get(sessionId);
857
+ if (!watermark || watermark.epoch !== (ordinalWatermarkEpochs.get(sessionId) ?? 0) || !sameStoreStamp(watermark.stamp, stamp))
858
+ return null;
859
+ return watermark;
860
+ }
861
+ function rememberOrdinalWatermark(db, sessionId, key, ordinal, stamp) {
862
+ let sessions = ordinalWatermarks.get(db);
863
+ if (!sessions) {
864
+ sessions = new Map;
865
+ ordinalWatermarks.set(db, sessions);
866
+ }
867
+ const current = sessions.get(sessionId);
868
+ if (current && sameStoreStamp(current.stamp, stamp) && compareOrdinalKeys(current, key) >= 0)
869
+ return;
870
+ sessions.set(sessionId, {
871
+ timeCreated: key.timeCreated,
872
+ id: key.id,
873
+ ordinal,
874
+ stamp,
875
+ epoch: ordinalWatermarkEpochs.get(sessionId) ?? 0
876
+ });
877
+ }
878
+ function canonicalOrdinalOf(db, sessionId, readTarget) {
879
+ for (let attempt = 1;; attempt += 1) {
880
+ const before = readStoreStamp(db);
881
+ const target = readTarget();
882
+ if (!target)
883
+ return null;
884
+ const watermark = attempt < STABLE_STORE_ATTEMPTS ? reusableWatermark(db, sessionId, before) : null;
885
+ if (!watermark) {
886
+ const ordinal = countEligibleOrdinals(db, sessionId, null, target);
887
+ if (before && sameStoreStamp(before, readStoreStamp(db)))
888
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
889
+ return { target, ordinal };
890
+ }
891
+ const order = compareOrdinalKeys(target, watermark);
892
+ const ordinal = order === 0 ? watermark.ordinal : order < 0 ? watermark.ordinal - countEligibleOrdinals(db, sessionId, target, watermark) : watermark.ordinal + countEligibleOrdinals(db, sessionId, watermark, target);
893
+ if (sameStoreStamp(before, readStoreStamp(db))) {
894
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
895
+ return { target, ordinal };
896
+ }
897
+ }
898
+ }
899
+ function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
900
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
901
+ const row = db.prepare(`SELECT id, time_created, ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL} AS excluded
902
+ FROM message WHERE session_id = ? AND id = ?`).get(sessionId, messageId);
903
+ if (typeof row?.id !== "string" || typeof row.time_created !== "number")
904
+ return null;
905
+ return { id: row.id, timeCreated: row.time_created, excluded: row.excluded === 1 };
906
+ });
907
+ if (!counted || counted.target.excluded)
908
+ return null;
909
+ return counted.ordinal > 0 ? counted.ordinal : null;
910
+ }
911
+ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
912
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
913
+ const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
914
+ if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number")
915
+ return null;
916
+ const parsed = parseJsonRecord(row.data);
917
+ if (!parsed || isRawCompactionSummaryInfo(parsed))
918
+ return null;
919
+ return { ...row, timeCreated: row.time_created, info: parsed };
920
+ });
921
+ if (!counted)
922
+ return null;
923
+ const { target: row, ordinal } = counted;
924
+ const info = row.info;
925
+ if (ordinal <= 0) {
926
+ return null;
927
+ }
928
+ const partRows = db.prepare(RAW_MESSAGE_PARTS_BY_ID_SQL).all(sessionId, messageId).filter(isRawPartRow);
929
+ const role = typeof info.role === "string" ? info.role : "unknown";
930
+ return {
931
+ ordinal,
932
+ id: row.id,
933
+ role,
934
+ parts: partRows.map((part) => attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated)),
935
+ createdAt: row.time_created,
936
+ version: row.time_updated ?? null
937
+ };
938
+ }
939
+ function hasRawSessionMessageByIdFromDb(db, sessionId, messageId) {
940
+ const row = db.prepare("SELECT id, data, time_created FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
941
+ if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number")
942
+ return false;
943
+ const info = parseJsonRecord(row.data);
944
+ if (!info || isRawCompactionSummaryInfo(info))
945
+ return false;
946
+ if (info.summary === 1 && info.finish === "stop")
947
+ return readRawSessionMessageByIdFromDb(db, sessionId, messageId) !== null;
948
+ return true;
949
+ }
950
+ var boundaryEndpointCaches = new WeakMap;
951
+
952
+ // src/shared/opencode-db-path.ts
953
+ import { existsSync, readdirSync, statSync } from "node:fs";
954
+ import { homedir } from "node:os";
955
+ import { isAbsolute, join, resolve } from "node:path";
956
+ var cachedResolution = null;
957
+ var lastReadFailure = null;
958
+ var claimedDiagnostics = new Set;
959
+ function openCodeDataDir(env = process.env, dataHome) {
960
+ return join(dataHome ?? env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "opencode");
961
+ }
962
+ function environmentKey(dataDir, hostGeneration, channel, env) {
963
+ return [
964
+ hostGeneration,
965
+ dataDir,
966
+ env.OPENCODE_DB ?? "",
967
+ env.OPENCODE_DISABLE_CHANNEL_DB ?? "",
968
+ channel ?? env.OPENCODE_CHANNEL ?? ""
969
+ ].join("\x00");
970
+ }
971
+ function channelPath(dataDir, channel) {
972
+ return ["latest", "beta", "prod"].includes(channel) ? join(dataDir, "opencode.db") : join(dataDir, `opencode-${channel}.db`);
973
+ }
974
+ function discoveredCandidateNames(dataDir) {
975
+ const names = ["opencode.db", "opencode-local.db", "opencode-dev.db"];
976
+ try {
977
+ const discovered = readdirSync(dataDir, { withFileTypes: true }).filter((entry) => /^opencode-.+\.db$/.test(entry.name) && !names.includes(entry.name)).map((entry) => entry.name).sort();
978
+ names.push(...discovered);
979
+ } catch {}
980
+ return names;
981
+ }
982
+ function discoverOpenCodeDb(dataDir) {
983
+ const candidates = discoveredCandidateNames(dataDir).map((name, order) => {
984
+ const path = join(dataDir, name);
985
+ try {
986
+ const metadata = statSync(path);
987
+ return metadata.isFile() ? { path, order, mtimeMs: metadata.mtimeMs } : null;
988
+ } catch {
989
+ return null;
990
+ }
991
+ });
992
+ const existing = candidates.filter((candidate) => candidate !== null).sort((left, right) => right.mtimeMs - left.mtimeMs || left.order - right.order)[0];
993
+ if (!existing) {
994
+ return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
995
+ }
996
+ const name = existing.path.slice(dataDir.length + 1);
997
+ const channel = name === "opencode.db" ? null : name.slice("opencode-".length, -".db".length) || null;
998
+ return { path: existing.path, source: "discovered", channel };
999
+ }
1000
+ function resolveV1Fresh(dataDir, env = process.env) {
1001
+ const explicit = env.OPENCODE_DB;
1002
+ if (explicit !== undefined && explicit.length > 0) {
1003
+ if (explicit === ":memory:") {
1004
+ return { path: explicit, source: "OPENCODE_DB", channel: null };
1005
+ }
1006
+ return {
1007
+ path: isAbsolute(explicit) ? explicit : join(dataDir, explicit),
1008
+ source: "OPENCODE_DB",
1009
+ channel: null
1010
+ };
1011
+ }
1012
+ const disableChannelDb = env.OPENCODE_DISABLE_CHANNEL_DB;
1013
+ if (disableChannelDb === "1" || disableChannelDb === "true") {
1014
+ return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
1015
+ }
1016
+ const channel = env.OPENCODE_CHANNEL;
1017
+ if (channel !== undefined && channel.length > 0) {
1018
+ return { path: channelPath(dataDir, channel), source: "channel", channel };
1019
+ }
1020
+ return discoverOpenCodeDb(dataDir);
1021
+ }
1022
+ function sourceOpenCodeDatabaseFilename(hostGeneration, channel, env = process.env) {
1023
+ if (hostGeneration === "v1") {
1024
+ const explicit = env.OPENCODE_DB;
1025
+ if (explicit !== undefined && explicit.length > 0)
1026
+ return explicit;
1027
+ if (env.OPENCODE_DISABLE_CHANNEL_DB === "1" || env.OPENCODE_DISABLE_CHANNEL_DB === "true") {
1028
+ return "opencode.db";
1029
+ }
1030
+ return ["latest", "beta", "prod"].includes(channel) ? "opencode.db" : `opencode-${channel}.db`;
1031
+ }
1032
+ return env.OPENCODE_DB ?? (["latest", "dev", "beta", "next", "prod"].includes(channel) || env.OPENCODE_DISABLE_CHANNEL_DB === "1" || env.OPENCODE_DISABLE_CHANNEL_DB === "true" ? "opencode.db" : `opencode-${channel.replace(/[^a-zA-Z0-9._-]/g, "")}.db`);
1033
+ }
1034
+ function resolveV2Fresh(dataDir, channel, env) {
1035
+ const filename = sourceOpenCodeDatabaseFilename("v2", channel, env);
1036
+ const explicit = env.OPENCODE_DB !== undefined;
1037
+ return {
1038
+ path: filename === ":memory:" ? filename : resolve(dataDir, filename),
1039
+ source: explicit ? "OPENCODE_DB" : env.OPENCODE_CHANNEL ? "channel" : "default",
1040
+ channel: explicit ? null : channel
1041
+ };
1042
+ }
1043
+ function resolveOpenCodeDbPath(hostGeneration = "v1", options = {}) {
1044
+ const env = options.env ?? process.env;
1045
+ const dataDir = openCodeDataDir(env, options.dataHome);
1046
+ const channel = options.channel ?? env.OPENCODE_CHANNEL;
1047
+ const key = environmentKey(dataDir, hostGeneration, channel, env);
1048
+ if (cachedResolution?.key === key && (!cachedResolution.existed || existsSync(cachedResolution.resolution.path))) {
1049
+ if (cachedResolution.existed)
1050
+ return cachedResolution.resolution;
1051
+ }
1052
+ const resolution = hostGeneration === "v2" ? resolveV2Fresh(dataDir, channel ?? "latest", env) : resolveV1Fresh(dataDir, env);
1053
+ cachedResolution = {
1054
+ key,
1055
+ resolution,
1056
+ existed: resolution.path !== ":memory:" && existsSync(resolution.path)
1057
+ };
1058
+ return resolution;
1059
+ }
1060
+ function schemaTableNames(db, schema = "main") {
1061
+ const rows = db.prepare(`SELECT name FROM ${schema}.sqlite_master WHERE type = 'table' AND name IN ('message', 'part', 'session', 'project', 'session_message', 'session_v2')`).all();
1062
+ return new Set(rows.flatMap((row) => typeof row.name === "string" ? [row.name] : []));
1063
+ }
1064
+ function hasV1MessageTables(db, schema = "main") {
1065
+ const tables = schemaTableNames(db, schema);
1066
+ return tables.has("message") && tables.has("part");
1067
+ }
1068
+ function detectOpenCodeStoreGeneration(db, schema = "main") {
1069
+ const tables = schemaTableNames(db, schema);
1070
+ const hasV1Messages = tables.has("message") && tables.has("part");
1071
+ if (hasV1Messages)
1072
+ return "v1";
1073
+ if (tables.has("session_message"))
1074
+ return "v2";
1075
+ if (tables.has("session") || tables.has("project"))
1076
+ return "v1";
1077
+ return "unknown";
1078
+ }
1079
+ function hasMigratedV2Schema(tables) {
1080
+ return tables.has("session_message") && tables.has("session_v2");
1081
+ }
1082
+ function isOpenCodeV2Store(db, schema = "main") {
1083
+ return detectOpenCodeStoreGeneration(db, schema) === "v2" || hasMigratedV2Schema(schemaTableNames(db, schema));
1084
+ }
1085
+ function assertOpenCodeStoreGeneration(db, expected, path, schema = "main") {
1086
+ const actual = detectOpenCodeStoreGeneration(db, schema);
1087
+ if (actual === expected)
1088
+ return;
1089
+ if (expected === "v2" && isOpenCodeV2Store(db, schema))
1090
+ return;
1091
+ if (actual === "unknown")
1092
+ return;
1093
+ throw new Error(`OpenCode store generation mismatch at ${path}: expected ${expected}, found ${actual}; refusing generation-specific database access`);
1094
+ }
1095
+ function openCodeDbPathExists(resolution = resolveOpenCodeDbPath()) {
1096
+ return resolution.path !== ":memory:" && existsSync(resolution.path);
1097
+ }
1098
+ function recordOpenCodeDbReadFailure(resolution, error) {
1099
+ const message = error instanceof Error ? error.message : String(error);
1100
+ lastReadFailure = { ...resolution, message };
1101
+ return lastReadFailure;
1102
+ }
1103
+ function clearOpenCodeDbReadFailure(path) {
1104
+ if (path === undefined || lastReadFailure?.path === path)
1105
+ lastReadFailure = null;
1106
+ }
1107
+ function claimOpenCodeDbDiagnosticOnce(surface, resolution) {
1108
+ const key = `${surface}\x00${resolution.path}\x00${resolution.source}`;
1109
+ if (claimedDiagnostics.has(key))
1110
+ return false;
1111
+ claimedDiagnostics.add(key);
1112
+ return true;
1113
+ }
1114
+
1115
+ // src/shared/sqlite-helpers.ts
1116
+ function closeQuietly(db) {
1117
+ if (!db)
1118
+ return;
1119
+ try {
1120
+ db.close();
1121
+ } catch {}
1122
+ }
1123
+ function isKnownAutocommit(db) {
1124
+ const state = db;
1125
+ try {
1126
+ return state.inTransaction === false || state.isTransaction === false;
1127
+ } catch {
1128
+ return false;
1129
+ }
1130
+ }
1131
+
1132
+ // src/hooks/magic-context/read-session-db.ts
1133
+ function openCodeDbExists() {
1134
+ return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
1135
+ }
1136
+ var cachedReadOnlyDb = null;
1137
+ function closeCachedReadOnlyDb() {
1138
+ if (!cachedReadOnlyDb) {
1139
+ return;
1140
+ }
1141
+ try {
1142
+ closeQuietly(cachedReadOnlyDb.db);
1143
+ } catch (error) {
1144
+ log("[magic-context] failed to close cached OpenCode read-only DB:", error);
1145
+ } finally {
1146
+ cachedReadOnlyDb = null;
1147
+ }
1148
+ }
1149
+ function getReadOnlySessionDb() {
1150
+ if (!harnessOwnsOpenCodeStore()) {
1151
+ throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
1152
+ }
1153
+ const resolution = resolveOpenCodeDbPath();
1154
+ const dbPath = resolution.path;
1155
+ if (!openCodeDbPathExists(resolution)) {
1156
+ throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
1157
+ }
1158
+ if (cachedReadOnlyDb?.path === dbPath) {
1159
+ return cachedReadOnlyDb.db;
1160
+ }
1161
+ closeCachedReadOnlyDb();
1162
+ const db = new Database(dbPath, { readonly: true });
1163
+ try {
1164
+ assertOpenCodeStoreGeneration(db, "v1", dbPath);
1165
+ } catch (error) {
1166
+ closeQuietly(db);
1167
+ throw error;
1168
+ }
1169
+ cachedReadOnlyDb = { path: dbPath, db };
1170
+ clearOpenCodeDbReadFailure();
1171
+ return db;
1172
+ }
1173
+ function withReadOnlySessionDb(fn) {
1174
+ return fn(getReadOnlySessionDb());
1175
+ }
1176
+ function getRawSessionMessageCountFromDb(db, sessionId) {
1177
+ const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
1178
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
1179
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
1180
+ return typeof row?.count === "number" ? row.count : 0;
1181
+ }
1182
+ var trackedSessions = new Map;
1183
+ var pendingParts = new Map;
1184
+ var probeLogObserverForTests;
1185
+ function asRecord(value) {
1186
+ return value !== null && typeof value === "object" ? value : null;
1187
+ }
1188
+ function truthyStoredFlag(value) {
1189
+ return value === true || value === 1 || value === "true";
1190
+ }
1191
+ function isMachineGeneratedPart(value) {
1192
+ const part = asRecord(value);
1193
+ if (!part)
1194
+ return false;
1195
+ const metadata = asRecord(part.metadata);
1196
+ const marker = asRecord(metadata?.marker);
1197
+ return truthyStoredFlag(part.synthetic) || truthyStoredFlag(part.ignored) || marker !== null && marker.kind !== null && marker.kind !== undefined;
1198
+ }
1199
+ function messageTimeCreated(message, fallback) {
1200
+ const time = asRecord(message.info.time);
1201
+ return typeof time?.created === "number" ? time.created : fallback;
1202
+ }
1203
+ function latestMessageByRole(messages, role, realUserOnly = false) {
1204
+ let latest = null;
1205
+ for (const [index, message] of messages.entries()) {
1206
+ if (message.info.role !== role)
1207
+ continue;
1208
+ if (realUserOnly && message.parts.length > 0 && message.parts.every(isMachineGeneratedPart)) {
1209
+ continue;
1210
+ }
1211
+ const timeCreated = messageTimeCreated(message, index);
1212
+ if (latest === null || timeCreated > latest.timeCreated || timeCreated === latest.timeCreated && index > latest.index) {
1213
+ latest = { message, timeCreated, index };
1214
+ }
1215
+ }
1216
+ return latest;
1217
+ }
1218
+ function assistantAwaitingToolsFromMessages(messages) {
1219
+ const latestAssistant = latestMessageByRole(messages, "assistant");
1220
+ if (!latestAssistant)
1221
+ return false;
1222
+ const assistantTime = asRecord(latestAssistant.message.info.time);
1223
+ if (typeof assistantTime?.created !== "number")
1224
+ return false;
1225
+ const latestRealUser = latestMessageByRole(messages, "user", true);
1226
+ if (latestRealUser && latestRealUser.timeCreated > latestAssistant.timeCreated)
1227
+ return false;
1228
+ if (latestAssistant.message.info.finish === "tool-calls")
1229
+ return true;
1230
+ return latestAssistant.message.parts.some((value) => {
1231
+ const part = asRecord(value);
1232
+ return part?.type === "tool" && part.providerExecuted !== true;
1233
+ });
1234
+ }
1235
+ function shouldHoldIgnoredNotificationFromMessages(messages) {
1236
+ if (assistantAwaitingToolsFromMessages(messages))
1237
+ return true;
1238
+ const latestAssistant = latestMessageByRole(messages, "assistant");
1239
+ if (latestAssistant) {
1240
+ const finish = latestAssistant.message.info.finish;
1241
+ if (typeof finish !== "string" || finish.length === 0)
1242
+ return true;
1243
+ if (finish === "tool-calls" || finish === "unknown")
1244
+ return true;
1245
+ }
1246
+ const latestRealUser = latestMessageByRole(messages, "user", true);
1247
+ return latestRealUser !== null && latestRealUser.timeCreated > (latestAssistant?.timeCreated ?? -1);
1248
+ }
1249
+ function trackedMessages(session) {
1250
+ let assistant;
1251
+ let user;
1252
+ for (const message of session.messages.values()) {
1253
+ if (message.role === "assistant" && (!assistant || message.timeCreated >= assistant.timeCreated))
1254
+ assistant = message;
1255
+ if (message.role === "user" && (!user || message.timeCreated >= user.timeCreated) && (message.parts.size === 0 || ![...message.parts.values()].every(isMachineGeneratedPart)))
1256
+ user = message;
1257
+ }
1258
+ return [user, assistant].filter((message) => !!message).map((message) => ({
1259
+ info: {
1260
+ id: message.id,
1261
+ role: message.role,
1262
+ time: { created: message.timeCreated },
1263
+ ...message.finish === undefined ? {} : { finish: message.finish }
1264
+ },
1265
+ parts: [...message.parts.values()]
1266
+ }));
1267
+ }
1268
+ function logProbeFailureOnce(resolution, error) {
1269
+ const failure = recordOpenCodeDbReadFailure(resolution, error);
1270
+ if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
1271
+ return;
1272
+ const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
1273
+ probeLogObserverForTests?.(message);
1274
+ log(message);
1275
+ }
1276
+ function resolvedDbIsAvailable() {
1277
+ const resolution = resolveOpenCodeDbPath();
1278
+ if (!harnessOwnsOpenCodeStore())
1279
+ return { resolution, available: false };
1280
+ const available = openCodeDbPathExists(resolution);
1281
+ if (available)
1282
+ clearOpenCodeDbReadFailure(resolution.path);
1283
+ else
1284
+ logProbeFailureOnce(resolution, "opencode_db_missing");
1285
+ return { resolution, available };
1286
+ }
1287
+ function shouldHoldIgnoredNotificationFromOpenCodeDb(db, sessionId) {
1288
+ if (assistantAwaitingToolsFromOpenCodeDb(db, sessionId))
1289
+ return true;
1290
+ if (hasUnfinishedAssistant(db, sessionId))
1291
+ return true;
1292
+ if (hasUnansweredRealUser(db, sessionId))
1293
+ return true;
1294
+ return false;
1295
+ }
1296
+ function shouldHoldIgnoredNotification(sessionId) {
1297
+ if (process.env.MMC_NOTICE_GATE === "bypass")
1298
+ return false;
1299
+ if (process.env.MMC_NOTICE_GATE === "hold")
1300
+ return true;
1301
+ const availability = resolvedDbIsAvailable();
1302
+ const tracked = trackedSessions.get(sessionId);
1303
+ if (tracked)
1304
+ return shouldHoldIgnoredNotificationFromMessages(trackedMessages(tracked));
1305
+ if (!availability.available)
1306
+ return false;
1307
+ try {
1308
+ return withReadOnlySessionDb((db) => shouldHoldIgnoredNotificationFromOpenCodeDb(db, sessionId));
1309
+ } catch (error) {
1310
+ logProbeFailureOnce(availability.resolution, error);
1311
+ return false;
1312
+ }
1313
+ }
1314
+ function assistantAwaitingToolsFromOpenCodeDb(db, sessionId) {
1315
+ const latestAssistant = latestAssistantRow(db, sessionId);
1316
+ if (typeof latestAssistant?.id !== "string")
1317
+ return false;
1318
+ if (hasNewerRealUserMessage(db, sessionId, latestAssistant.timeCreated))
1319
+ return false;
1320
+ if (latestAssistant.finish === "tool-calls")
1321
+ return true;
1322
+ const partRows = db.prepare("SELECT data FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001)").all(sessionId, latestAssistant.id);
1323
+ return partRows.some((row) => {
1324
+ if (typeof row.data !== "string" || row.data.length === 0)
1325
+ return false;
1326
+ try {
1327
+ const part = JSON.parse(row.data);
1328
+ return part.type === "tool" && part.providerExecuted !== true;
1329
+ } catch {
1330
+ return false;
1331
+ }
1332
+ });
1333
+ }
1334
+ function latestRealUserTurnIdFromOpenCodeDb(sessionId) {
1335
+ try {
1336
+ return withReadOnlySessionDb((db) => {
1337
+ const row = db.prepare(`SELECT m.id FROM message m
1338
+ WHERE m.session_id = ? AND json_extract(m.data, '$.role') = 'user'
1339
+ AND COALESCE(json_type(m.data, '$.synthetic'), 'null') != 'true'
1340
+ AND m.id NOT LIKE 'synth-user-%'
1341
+ AND NOT (EXISTS (SELECT 1 FROM part p WHERE p.message_id = m.id)
1342
+ AND NOT EXISTS (SELECT 1 FROM part p WHERE p.message_id = m.id
1343
+ AND COALESCE(json_type(p.data, '$.synthetic'), 'null') != 'true'
1344
+ AND COALESCE(json_type(p.data, '$.ignored'), 'null') != 'true'
1345
+ AND COALESCE(json_extract(p.data, '$.type'), '') NOT IN
1346
+ ('tool_result', 'toolResult', 'tool-result')))
1347
+ ORDER BY m.time_created DESC, m.id DESC LIMIT 1`).get(sessionId);
1348
+ return typeof row?.id === "string" ? row.id : undefined;
1349
+ }) ?? undefined;
1350
+ } catch {
1351
+ return;
1352
+ }
1353
+ }
1354
+ function hasNewerRealUserMessage(db, sessionId, latestAssistantTimeCreated) {
1355
+ if (typeof latestAssistantTimeCreated !== "number")
1356
+ return false;
1357
+ const row = db.prepare(`SELECT 1 as one
1358
+ FROM message m
1359
+ WHERE m.session_id = ?
1360
+ AND m.time_created > ?
1361
+ AND json_extract(m.data, '$.role') = 'user'
1362
+ AND NOT (
1363
+ EXISTS (SELECT 1 FROM part p WHERE p.message_id = m.id)
1364
+ AND NOT EXISTS (
1365
+ SELECT 1 FROM part p
1366
+ WHERE p.message_id = m.id
1367
+ AND COALESCE(json_extract(p.data, '$.synthetic'), 0) NOT IN (1, 'true')
1368
+ AND json_extract(p.data, '$.metadata.marker.kind') IS NULL
1369
+ AND COALESCE(json_extract(p.data, '$.ignored'), 0) NOT IN (1, 'true')
1370
+ )
1371
+ )
1372
+ LIMIT 1`).get(sessionId, latestAssistantTimeCreated);
1373
+ return row?.one === 1;
1374
+ }
1375
+ function latestAssistantRow(db, sessionId) {
1376
+ return db.prepare(`SELECT id,
1377
+ json_extract(data, '$.finish') as finish,
1378
+ time_created as timeCreated
1379
+ FROM message
1380
+ WHERE session_id = ?
1381
+ AND json_extract(data, '$.role') = 'assistant'
1382
+ ORDER BY time_created DESC
1383
+ LIMIT 1`).get(sessionId);
1384
+ }
1385
+ function hasUnfinishedAssistant(db, sessionId) {
1386
+ const latestAssistant = latestAssistantRow(db, sessionId);
1387
+ if (typeof latestAssistant?.id !== "string")
1388
+ return false;
1389
+ const finish = latestAssistant.finish;
1390
+ if (typeof finish !== "string" || finish.length === 0)
1391
+ return true;
1392
+ return finish === "tool-calls" || finish === "unknown";
1393
+ }
1394
+ function hasUnansweredRealUser(db, sessionId) {
1395
+ const latestAssistant = latestAssistantRow(db, sessionId);
1396
+ const latestAssistantTime = typeof latestAssistant?.timeCreated === "number" ? latestAssistant.timeCreated : -1;
1397
+ return hasNewerRealUserMessage(db, sessionId, latestAssistantTime);
1398
+ }
1399
+ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
1400
+ const result = new Map;
1401
+ if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
1402
+ return result;
1403
+ try {
1404
+ withReadOnlySessionDb((db) => {
1405
+ const placeholders = messageIds.map(() => "?").join(",");
1406
+ const rows = db.prepare(`SELECT id, time_created FROM message WHERE +session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
1407
+ for (const row of rows) {
1408
+ if (typeof row.id === "string" && typeof row.time_created === "number") {
1409
+ result.set(row.id, row.time_created);
1410
+ }
1411
+ }
1412
+ });
1413
+ } catch (error) {
1414
+ logProbeFailureOnce(resolveOpenCodeDbPath(), error);
1415
+ }
1416
+ return result;
1417
+ }
1418
+ function findLastAssistantModelFromOpenCodeDb(sessionId) {
1419
+ if (!harnessOwnsOpenCodeStore())
1420
+ return null;
1421
+ const resolution = resolveOpenCodeDbPath();
1422
+ try {
1423
+ if (!openCodeDbPathExists(resolution)) {
1424
+ throw new Error(`OpenCode session database is unavailable at ${resolution.path}`);
1425
+ }
1426
+ const db = new Database(resolution.path, { readonly: true });
1427
+ try {
1428
+ const queryV2 = () => db.prepare(`SELECT json_extract(data, '$.model.providerID') as providerID,
1429
+ json_extract(data, '$.model.id') as modelID,
1430
+ json_extract(data, '$.agent') as agent
1431
+ FROM session_message
1432
+ WHERE session_id = ?
1433
+ AND type = 'assistant'
1434
+ AND json_extract(data, '$.model.providerID') IS NOT NULL
1435
+ AND json_extract(data, '$.model.id') IS NOT NULL
1436
+ ORDER BY seq DESC
1437
+ LIMIT 1`).get(sessionId);
1438
+ const queryV1 = () => db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
1439
+ json_extract(data, '$.modelID') as modelID,
1440
+ json_extract(data, '$.agent') as agent
1441
+ FROM message
1442
+ WHERE session_id = ?
1443
+ AND json_extract(data, '$.role') = 'assistant'
1444
+ AND json_extract(data, '$.providerID') IS NOT NULL
1445
+ AND json_extract(data, '$.modelID') IS NOT NULL
1446
+ ORDER BY time_created DESC
1447
+ LIMIT 1`).get(sessionId);
1448
+ let row = null;
1449
+ try {
1450
+ assertOpenCodeStoreGeneration(db, "v2", resolution.path);
1451
+ row = queryV2();
1452
+ } catch {}
1453
+ if (!row) {
1454
+ try {
1455
+ assertOpenCodeStoreGeneration(db, "v1", resolution.path);
1456
+ row = queryV1();
1457
+ } catch {}
1458
+ }
1459
+ if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
1460
+ return null;
1461
+ }
1462
+ const agent = typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined;
1463
+ return {
1464
+ providerID: row.providerID,
1465
+ modelID: row.modelID,
1466
+ ...agent ? { agent } : {}
1467
+ };
1468
+ } finally {
1469
+ closeQuietly(db);
1470
+ }
1471
+ } catch (error) {
1472
+ logProbeFailureOnce(resolution, error);
1473
+ return null;
1474
+ }
1475
+ }
1476
+
1477
+ export { closeQuietly, isKnownAutocommit, resolveOpenCodeDbPath, hasV1MessageTables, assertOpenCodeStoreGeneration, openCodeDbPathExists, claimOpenCodeDbDiagnosticOnce, isStrictGapHealingMessage, readRawSessionMessagesFromDb, readRawSessionMessagePageFromDb, readRawSessionMessageSummaryPageFromDb, countRawSessionMessageOrdinalsFromDb, readRawSessionMessageIdOrdinalsForRangeFromDb, readRawSessionTailFromDb, extractInMemoryMessageViews, buildInMemoryTailRawMessages, warmRawSessionOrdinalsForDb, readRawSessionMessageOrdinalByIdFromDb, readRawSessionMessageByIdFromDb, hasRawSessionMessageByIdFromDb, openCodeDbExists, withReadOnlySessionDb, getRawSessionMessageCountFromDb, shouldHoldIgnoredNotification, latestRealUserTurnIdFromOpenCodeDb, getMessageTimesFromOpenCodeDb, findLastAssistantModelFromOpenCodeDb };