draken 0.5.1 → 0.7.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 (210) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/MEMORY.md +43 -6
  3. package/PERSONA.md +9 -2
  4. package/README.md +14 -14
  5. package/SETUP.md +23 -3
  6. package/TRAY.md +4 -4
  7. package/VOICE.md +1 -1
  8. package/dist/auth/middleware.d.ts.map +1 -1
  9. package/dist/auth/middleware.js +0 -13
  10. package/dist/auth/middleware.js.map +1 -1
  11. package/dist/cli.js +6 -2
  12. package/dist/cli.js.map +1 -1
  13. package/dist/data-dir.d.ts +2 -0
  14. package/dist/data-dir.d.ts.map +1 -0
  15. package/dist/data-dir.js +12 -0
  16. package/dist/data-dir.js.map +1 -0
  17. package/dist/db/evidence.d.ts +2 -1
  18. package/dist/db/evidence.d.ts.map +1 -1
  19. package/dist/db/evidence.js +6 -5
  20. package/dist/db/evidence.js.map +1 -1
  21. package/dist/db/pointer-migration.js +4 -4
  22. package/dist/db/pointer-migration.js.map +1 -1
  23. package/dist/db/schema.d.ts +0 -1
  24. package/dist/db/schema.d.ts.map +1 -1
  25. package/dist/db/schema.js +31 -7
  26. package/dist/db/schema.js.map +1 -1
  27. package/dist/errors.d.ts +5 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +12 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/memory/context.d.ts +2 -9
  32. package/dist/memory/context.d.ts.map +1 -1
  33. package/dist/memory/context.js +7 -84
  34. package/dist/memory/context.js.map +1 -1
  35. package/dist/memory/embedding-contract.d.ts +5 -0
  36. package/dist/memory/embedding-contract.d.ts.map +1 -0
  37. package/dist/memory/embedding-contract.js +12 -0
  38. package/dist/memory/embedding-contract.js.map +1 -0
  39. package/dist/memory/embedding-pool.d.ts +1 -2
  40. package/dist/memory/embedding-pool.d.ts.map +1 -1
  41. package/dist/memory/embedding-pool.js +9 -9
  42. package/dist/memory/embedding-pool.js.map +1 -1
  43. package/dist/memory/embedding-worker.js +2 -1
  44. package/dist/memory/embedding-worker.js.map +1 -1
  45. package/dist/{voice/history-focus.d.ts → memory/focus.d.ts} +6 -6
  46. package/dist/memory/focus.d.ts.map +1 -0
  47. package/dist/{voice/history-focus.js → memory/focus.js} +6 -6
  48. package/dist/memory/focus.js.map +1 -0
  49. package/dist/memory/index.d.ts.map +1 -1
  50. package/dist/memory/index.js +12 -15
  51. package/dist/memory/index.js.map +1 -1
  52. package/dist/memory/model-assets.d.ts +5 -0
  53. package/dist/memory/model-assets.d.ts.map +1 -0
  54. package/dist/memory/model-assets.js +84 -0
  55. package/dist/memory/model-assets.js.map +1 -0
  56. package/dist/memory/model.d.ts +2 -7
  57. package/dist/memory/model.d.ts.map +1 -1
  58. package/dist/memory/model.js +13 -83
  59. package/dist/memory/model.js.map +1 -1
  60. package/dist/memory/packet.d.ts +9 -0
  61. package/dist/memory/packet.d.ts.map +1 -0
  62. package/dist/memory/packet.js +78 -0
  63. package/dist/memory/packet.js.map +1 -0
  64. package/dist/memory/passages.d.ts +15 -0
  65. package/dist/memory/passages.d.ts.map +1 -0
  66. package/dist/memory/passages.js +22 -0
  67. package/dist/memory/passages.js.map +1 -0
  68. package/dist/memory/readiness.d.ts.map +1 -1
  69. package/dist/memory/readiness.js +2 -6
  70. package/dist/memory/readiness.js.map +1 -1
  71. package/dist/memory/service.d.ts +2 -1
  72. package/dist/memory/service.d.ts.map +1 -1
  73. package/dist/memory/service.js +46 -22
  74. package/dist/memory/service.js.map +1 -1
  75. package/dist/memory/sources.d.ts +0 -1
  76. package/dist/memory/sources.d.ts.map +1 -1
  77. package/dist/memory/sources.js +3 -7
  78. package/dist/memory/sources.js.map +1 -1
  79. package/dist/memory/stamps.d.ts +3 -3
  80. package/dist/memory/stamps.d.ts.map +1 -1
  81. package/dist/memory/stamps.js +3 -3
  82. package/dist/memory/stamps.js.map +1 -1
  83. package/dist/memory/storage.d.ts.map +1 -1
  84. package/dist/memory/storage.js +3 -2
  85. package/dist/memory/storage.js.map +1 -1
  86. package/dist/memory/types.d.ts +2 -1
  87. package/dist/memory/types.d.ts.map +1 -1
  88. package/dist/memory/types.js +2 -1
  89. package/dist/memory/types.js.map +1 -1
  90. package/dist/pi/runtime.js +7 -7
  91. package/dist/pi/runtime.js.map +1 -1
  92. package/dist/pi/setup.d.ts +0 -1
  93. package/dist/pi/setup.d.ts.map +1 -1
  94. package/dist/pi/setup.js +2 -11
  95. package/dist/pi/setup.js.map +1 -1
  96. package/dist/routes/auth.d.ts.map +1 -1
  97. package/dist/routes/auth.js +0 -5
  98. package/dist/routes/auth.js.map +1 -1
  99. package/dist/routes/http.d.ts +6 -0
  100. package/dist/routes/http.d.ts.map +1 -0
  101. package/dist/routes/http.js +22 -0
  102. package/dist/routes/http.js.map +1 -0
  103. package/dist/routes/live-voice.d.ts.map +1 -1
  104. package/dist/routes/live-voice.js +18 -32
  105. package/dist/routes/live-voice.js.map +1 -1
  106. package/dist/routes/memory.d.ts.map +1 -1
  107. package/dist/routes/memory.js +8 -14
  108. package/dist/routes/memory.js.map +1 -1
  109. package/dist/routes/sync.d.ts.map +1 -1
  110. package/dist/routes/sync.js +65 -65
  111. package/dist/routes/sync.js.map +1 -1
  112. package/dist/routes/voice.d.ts.map +1 -1
  113. package/dist/routes/voice.js +7 -7
  114. package/dist/routes/voice.js.map +1 -1
  115. package/dist/server-ownership.d.ts +5 -0
  116. package/dist/server-ownership.d.ts.map +1 -0
  117. package/dist/server-ownership.js +34 -0
  118. package/dist/server-ownership.js.map +1 -0
  119. package/dist/server-state.js +3 -3
  120. package/dist/server-state.js.map +1 -1
  121. package/dist/server.d.ts.map +1 -1
  122. package/dist/server.js +40 -10
  123. package/dist/server.js.map +1 -1
  124. package/dist/sync/activity.d.ts +26 -0
  125. package/dist/sync/activity.d.ts.map +1 -0
  126. package/dist/sync/activity.js +80 -0
  127. package/dist/sync/activity.js.map +1 -0
  128. package/dist/{memory/automatic.d.ts → sync/background.d.ts} +7 -12
  129. package/dist/sync/background.d.ts.map +1 -0
  130. package/dist/sync/background.js +184 -0
  131. package/dist/sync/background.js.map +1 -0
  132. package/dist/sync/collector.d.ts.map +1 -1
  133. package/dist/sync/collector.js +7 -6
  134. package/dist/sync/collector.js.map +1 -1
  135. package/dist/sync/manager.d.ts +1 -4
  136. package/dist/sync/manager.d.ts.map +1 -1
  137. package/dist/sync/manager.js +29 -49
  138. package/dist/sync/manager.js.map +1 -1
  139. package/dist/sync/models.d.ts +3 -0
  140. package/dist/sync/models.d.ts.map +1 -0
  141. package/dist/sync/models.js +12 -0
  142. package/dist/sync/models.js.map +1 -0
  143. package/dist/sync/pi.d.ts +1 -1
  144. package/dist/sync/pi.d.ts.map +1 -1
  145. package/dist/sync/pi.js +32 -19
  146. package/dist/sync/pi.js.map +1 -1
  147. package/dist/{voice → sync}/preparation.d.ts +0 -1
  148. package/dist/sync/preparation.d.ts.map +1 -0
  149. package/dist/{voice → sync}/preparation.js +10 -18
  150. package/dist/sync/preparation.js.map +1 -0
  151. package/dist/sync/retrieval.d.ts +3 -0
  152. package/dist/sync/retrieval.d.ts.map +1 -0
  153. package/dist/sync/retrieval.js +12 -0
  154. package/dist/sync/retrieval.js.map +1 -0
  155. package/dist/sync/source-files.d.ts +2 -4
  156. package/dist/sync/source-files.d.ts.map +1 -1
  157. package/dist/sync/source-files.js +5 -20
  158. package/dist/sync/source-files.js.map +1 -1
  159. package/dist/sync/store.d.ts +5 -15
  160. package/dist/sync/store.d.ts.map +1 -1
  161. package/dist/sync/store.js +41 -90
  162. package/dist/sync/store.js.map +1 -1
  163. package/dist/sync/summary-preparation.d.ts +14 -0
  164. package/dist/sync/summary-preparation.d.ts.map +1 -0
  165. package/dist/sync/summary-preparation.js +205 -0
  166. package/dist/sync/summary-preparation.js.map +1 -0
  167. package/dist/sync/tree.d.ts +17 -1
  168. package/dist/sync/tree.d.ts.map +1 -1
  169. package/dist/sync/tree.js +109 -49
  170. package/dist/sync/tree.js.map +1 -1
  171. package/dist/sync/usage.d.ts +3 -1
  172. package/dist/sync/usage.d.ts.map +1 -1
  173. package/dist/sync/usage.js +27 -9
  174. package/dist/sync/usage.js.map +1 -1
  175. package/dist/tray/launcher.js +2 -2
  176. package/dist/tray/launcher.js.map +1 -1
  177. package/dist/tray/worker.js +4 -4
  178. package/dist/tray/worker.js.map +1 -1
  179. package/dist/types.d.ts +9 -10
  180. package/dist/types.d.ts.map +1 -1
  181. package/dist/voice/history-delivery.js +3 -3
  182. package/dist/voice/history-delivery.js.map +1 -1
  183. package/dist/voice/live-context.js +3 -3
  184. package/dist/voice/live-context.js.map +1 -1
  185. package/dist/voice/live.d.ts +1 -0
  186. package/dist/voice/live.d.ts.map +1 -1
  187. package/dist/voice/live.js +57 -34
  188. package/dist/voice/live.js.map +1 -1
  189. package/package.json +1 -1
  190. package/public/assets/index-5bTQNQYG.js +131 -0
  191. package/public/index.html +1 -1
  192. package/voice/realtime/README.md +1 -1
  193. package/voice/realtime/helper-process.mjs +54 -0
  194. package/voice/realtime/native.mjs +55 -15
  195. package/voice/realtime/probe-server.mjs +24 -4
  196. package/voice/realtime/probe.mjs +49 -11
  197. package/voice/realtime/rpc.mjs +7 -32
  198. package/voice/realtime/setup.mjs +82 -19
  199. package/dist/memory/automatic.d.ts.map +0 -1
  200. package/dist/memory/automatic.js +0 -200
  201. package/dist/memory/automatic.js.map +0 -1
  202. package/dist/sync/automatic.d.ts +0 -7
  203. package/dist/sync/automatic.d.ts.map +0 -1
  204. package/dist/sync/automatic.js +0 -56
  205. package/dist/sync/automatic.js.map +0 -1
  206. package/dist/voice/history-focus.d.ts.map +0 -1
  207. package/dist/voice/history-focus.js.map +0 -1
  208. package/dist/voice/preparation.d.ts.map +0 -1
  209. package/dist/voice/preparation.js.map +0 -1
  210. package/public/assets/index-DrGvDGTG.js +0 -131
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.summaryPreparationEnabled = void 0;
4
+ exports.cancelSummaryPreparation = cancelSummaryPreparation;
5
+ exports.configureAutoSummary = configureAutoSummary;
6
+ exports.summaryPreparationSettings = summaryPreparationSettings;
7
+ exports.warmSummaryCache = warmSummaryCache;
8
+ const schema_1 = require("../db/schema");
9
+ const errors_1 = require("../errors");
10
+ const activity_1 = require("./activity");
11
+ const service_1 = require("../memory/service");
12
+ const store_1 = require("./store");
13
+ const tree_1 = require("./tree");
14
+ const normalize_1 = require("./normalize");
15
+ const BATCH = 64;
16
+ const FAILURE = 'Background summaries could not finish. Check provider settings, invocation limits and original session files, then explicitly enable summaries to retry. Cached work is retained.';
17
+ const setting = (key) => (0, schema_1.getDb)().prepare('SELECT value FROM sync_settings WHERE key=?').get(key)?.value;
18
+ function save(key, value) {
19
+ (0, schema_1.getDb)().prepare('INSERT INTO sync_settings(key,value) VALUES (?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value').run(key, value);
20
+ }
21
+ const summaryPreparationEnabled = () => setting('auto_summary') === 'true';
22
+ exports.summaryPreparationEnabled = summaryPreparationEnabled;
23
+ const indexingEnabled = () => setting('auto_index') === 'true';
24
+ const idle = () => !activity_1.activity.busy && !activity_1.activity.callActive && !(0, service_1.historyProgress)().active && !(0, service_1.historyProgress)().queued;
25
+ let consentRevision = 0;
26
+ let active;
27
+ /** Cancellation is irrevocable for this job even if consent is immediately re-enabled. */
28
+ function cancelSummaryPreparation() {
29
+ const prior = active;
30
+ prior?.controller.abort();
31
+ // Return the owned promise, not a detached rejecting wrapper. The scheduler
32
+ // retains its rejection handler even when a synchronous setter only aborts.
33
+ return prior?.done || Promise.resolve();
34
+ }
35
+ /** Called by the scheduler's lifecycle owner; reads never start the scheduler. */
36
+ function configureAutoSummary(value) {
37
+ if (typeof value !== 'boolean')
38
+ throw new errors_1.HttpError(400, 'enabled must be a boolean');
39
+ if (value && !indexingEnabled())
40
+ throw new errors_1.HttpError(409, 'Enable automatic indexing before enabling paid background summaries.');
41
+ consentRevision++;
42
+ active?.controller.abort();
43
+ (0, schema_1.getDb)().transaction(() => {
44
+ save('auto_summary', String(value));
45
+ if (value)
46
+ save('auto_summary_error', '');
47
+ })();
48
+ }
49
+ function checkpoint(input) {
50
+ // Canonical recipe/input hashes only; no original text, paths or provider output.
51
+ // SQL sequence IDs can be reassigned by a full rescan; that does not turn
52
+ // unchanged original records into another paid batch. Receipt identity still
53
+ // includes full ordering, while this delta counts canonical source changes.
54
+ return { keys: input.items.map(item => [(0, normalize_1.digest)(JSON.stringify([item.agent || 'pi', item.session_id, item.entry_id])),
55
+ (0, normalize_1.digest)(JSON.stringify([(0, normalize_1.sourceKey)(item), item.agent, item.session_id, item.cwd, item.timestamp, item.role]))]),
56
+ recipe: (0, tree_1.summaryPreparationKey)({ ...input, items: [] }) };
57
+ }
58
+ function completed() {
59
+ return (0, schema_1.getDb)().prepare("SELECT id,input_key,source_keys_json,cache_receipt_json,total_entries,completed_at FROM background_runs WHERE status='completed' ORDER BY id DESC LIMIT 1").get();
60
+ }
61
+ function matching(key) {
62
+ return (0, schema_1.getDb)().prepare("SELECT id,input_key,source_keys_json,cache_receipt_json,total_entries,completed_at FROM background_runs WHERE status='completed' AND input_key=? ORDER BY id DESC LIMIT 1").get(key);
63
+ }
64
+ function cacheIntact(value) {
65
+ let pairs;
66
+ try {
67
+ pairs = typeof value === 'string' ? JSON.parse(value) : value;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ if (!Array.isArray(pairs) || !pairs.length || pairs.some(pair => !Array.isArray(pair) || pair.length !== 2 || pair.some(hash => typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash))))
73
+ return false;
74
+ const lookup = (0, schema_1.getDb)().prepare('SELECT output_json FROM summary_cache WHERE key=?');
75
+ return pairs.every(([key, hash]) => {
76
+ const row = lookup.get(key);
77
+ return row !== undefined && (0, normalize_1.digest)(row.output_json) === hash;
78
+ });
79
+ }
80
+ /** Historical progress, not a live readiness claim. Polling must not reopen
81
+ * transcript bodies; the preparing owner and explicit sync validate originals. */
82
+ function summaryPreparationSettings() {
83
+ const enabled = (0, exports.summaryPreparationEnabled)(), error = setting('auto_summary_error') || '';
84
+ const last = (0, schema_1.getDb)().prepare("SELECT total_entries,completed_at FROM background_runs WHERE status='completed' ORDER BY id DESC LIMIT 1").get();
85
+ return { enabled, status: !enabled ? 'off' : error ? 'error' : !indexingEnabled() || !idle() || active?.controller.signal.aborted ? 'paused' : active?.working ? 'running' : 'waiting',
86
+ prepared_entries: last?.total_entries || 0, last_completed_at: last?.completed_at || null, error };
87
+ }
88
+ /** Prepare only reusable summaries. No sync identity, snapshot, briefing or verdict.
89
+ * A successful receipt suppresses repeated work; a failure blocks all automatic
90
+ * paid retries until explicit consent/retry. Cancellation retains its allowance. */
91
+ async function warmSummaryCache(signal, onWork) {
92
+ if (signal.aborted || !(0, exports.summaryPreparationEnabled)() || !indexingEnabled() || !idle() || setting('auto_summary_error'))
93
+ return;
94
+ if (active)
95
+ return active.done;
96
+ const controller = new AbortController();
97
+ const abort = () => controller.abort();
98
+ signal.addEventListener('abort', abort, { once: true });
99
+ const revision = consentRevision;
100
+ const owner = { controller, done: Promise.resolve(), working: false };
101
+ active = owner;
102
+ owner.done = Promise.resolve().then(async () => {
103
+ let run;
104
+ const admitted = () => !controller.signal.aborted && (0, exports.summaryPreparationEnabled)() && indexingEnabled() && idle();
105
+ const check = () => { if (!admitted()) {
106
+ controller.abort();
107
+ throw new errors_1.HttpError(409, 'Background preparation paused');
108
+ } };
109
+ try {
110
+ check();
111
+ const db = (0, schema_1.getDb)();
112
+ // Startup recovery is responsible for interrupted ownership. A running row
113
+ // with no process-local job can also mean failure metadata could not be
114
+ // committed. Never silently replace that run with a fresh allowance.
115
+ const orphan = db.prepare("SELECT id,invocations,max_invocations FROM background_runs WHERE status='running' LIMIT 1").get();
116
+ if (orphan) {
117
+ run = orphan;
118
+ throw new Error('Background ownership was not recovered');
119
+ }
120
+ const input = (0, store_1.previewSummaryInput)();
121
+ (0, store_1.assertEvidenceAvailable)(input);
122
+ const key = (0, tree_1.summaryPreparationKey)(input);
123
+ const receipt = matching(key);
124
+ if (receipt && cacheIntact(receipt.cache_receipt_json))
125
+ return;
126
+ const sources = checkpoint(input), last = completed();
127
+ let prior;
128
+ if (last) {
129
+ prior = JSON.parse(last.source_keys_json);
130
+ if (!Array.isArray(prior.keys) || prior.keys.some(pair => !Array.isArray(pair) || pair.length !== 2 || pair.some(value => typeof value !== 'string')) || typeof prior.recipe !== 'string')
131
+ throw new Error('Invalid receipt metadata');
132
+ }
133
+ const old = new Map(prior?.keys), current = new Map(sources.keys);
134
+ const changed = sources.keys.filter(([id, hash]) => old.get(id) !== hash).length + [...old.keys()].filter(id => !current.has(id)).length;
135
+ // Model/recipe/context changes need a fresh full receipt even when no IDs arrived.
136
+ if (input.items.length < BATCH || (!receipt && changed < BATCH && (!prior || prior.recipe === sources.recipe)))
137
+ return;
138
+ check();
139
+ run = db.prepare(`SELECT id,invocations,max_invocations FROM background_runs WHERE input_key=?
140
+ AND status IN ('cancelled','interrupted') ORDER BY id DESC LIMIT 1`).get(key);
141
+ // Never resurrect an older interrupted allowance after an explicitly retried attempt.
142
+ const latest = db.prepare('SELECT id FROM background_runs WHERE input_key=? ORDER BY id DESC LIMIT 1').get(key);
143
+ if (run && latest?.id !== run.id)
144
+ run = undefined;
145
+ if (run) {
146
+ db.prepare("UPDATE background_runs SET status='running',stage='Planning',processed_entries=0,cache_hits=0,error='',completed_at=NULL WHERE id=?").run(run.id);
147
+ input.models = { ...input.models, max_invocations: run.max_invocations };
148
+ }
149
+ else {
150
+ const result = db.prepare(`INSERT INTO background_runs(summary_model,final_model,max_invocations,total_entries,started_at,input_key,source_keys_json)
151
+ VALUES (?,?,?,?,?,?,?)`).run(input.models.summary, input.models.final, input.models.max_invocations, input.items.length, new Date().toISOString(), key, JSON.stringify(sources));
152
+ run = { id: Number(result.lastInsertRowid), invocations: 0, max_invocations: input.models.max_invocations };
153
+ }
154
+ owner.working = true;
155
+ onWork?.();
156
+ const tree = tree_1.SummaryTree.forBackground(input, run.id, controller.signal);
157
+ // Recheck at the last boundary before *each* provider call, including repairs
158
+ // and reconciliation after an await. Route-level yielding also drains this owner.
159
+ const call = tree.call.bind(tree);
160
+ tree.call = async (payload, system, final = false) => {
161
+ check();
162
+ if (final)
163
+ throw new Error('Background preparation cannot request a verdict');
164
+ return call(payload, system, false);
165
+ };
166
+ await tree.build();
167
+ check();
168
+ // Inputs can change while a provider is in flight. Keep useful cache fragments,
169
+ // but never publish a receipt for sources/models no longer admitted or available.
170
+ const latestInput = (0, store_1.previewSummaryInput)();
171
+ (0, store_1.assertEvidenceAvailable)(latestInput);
172
+ if ((0, tree_1.summaryPreparationKey)(latestInput) !== key) {
173
+ controller.abort();
174
+ throw new Error('Input changed');
175
+ }
176
+ const cacheReceipt = tree.cacheReceipt();
177
+ if (!cacheIntact(cacheReceipt))
178
+ throw new Error('Summary cache changed before completion');
179
+ db.prepare("UPDATE background_runs SET status='completed',stage='Cached',error='',completed_at=?,cache_receipt_json=? WHERE id=?")
180
+ .run(new Date().toISOString(), JSON.stringify(cacheReceipt), run.id);
181
+ }
182
+ catch {
183
+ const db = (0, schema_1.getDb)();
184
+ const counters = run && db.prepare('SELECT invocations,max_invocations FROM background_runs WHERE id=?').get(run.id);
185
+ const cancelled = controller.signal.aborted;
186
+ // Exhausted cancellations are also blocked: do not spin a quota 409 every minute.
187
+ const exhausted = Boolean(counters && counters.invocations >= counters.max_invocations);
188
+ const status = cancelled && !exhausted ? 'cancelled' : 'failed', at = new Date().toISOString();
189
+ db.transaction(() => {
190
+ if (run)
191
+ db.prepare(`UPDATE background_runs SET status=?,error=?,completed_at=?,
192
+ failure_events_json=json_insert(failure_events_json,'$[#]',json(?)) WHERE id=?`)
193
+ .run(status, cancelled && !exhausted ? '' : FAILURE, at, JSON.stringify({ status, at }), run.id);
194
+ if (revision === consentRevision && (!cancelled || exhausted))
195
+ save('auto_summary_error', FAILURE);
196
+ })();
197
+ }
198
+ }).finally(() => {
199
+ signal.removeEventListener('abort', abort);
200
+ if (active === owner)
201
+ active = undefined;
202
+ });
203
+ return owner.done;
204
+ }
205
+ //# sourceMappingURL=summary-preparation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary-preparation.js","sourceRoot":"","sources":["../../src/sync/summary-preparation.ts"],"names":[],"mappings":";;;AAyBA,4DAMC;AAGD,oDASC;AA+BD,gEAKC;AAKD,4CAwFC;AA5KD,yCAAqC;AACrC,sCAAsC;AAEtC,yCAAsC;AACtC,+CAAoD;AACpD,mCAAuE;AACvE,iCAA+E;AAC/E,2CAAgD;AAEhD,MAAM,KAAK,GAAG,EAAE,CAAC;AACjB,MAAM,OAAO,GAAG,mLAAmL,CAAC;AACpM,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAAE,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC,GAAG,CAAC,GAAG,CAAmC,EAAE,KAAK,CAAC;AACnJ,SAAS,IAAI,CAAC,GAAW,EAAE,KAAa;IACtC,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,uGAAuG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC3I,CAAC;AACM,MAAM,yBAAyB,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,MAAM,CAAC;AAArE,QAAA,yBAAyB,6BAA4C;AAClF,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC;AAC/D,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,mBAAQ,CAAC,IAAI,IAAI,CAAC,mBAAQ,CAAC,UAAU,IAAI,CAAC,IAAA,yBAAe,GAAE,CAAC,MAAM,IAAI,CAAC,IAAA,yBAAe,GAAE,CAAC,MAAM,CAAC;AAIpH,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,IAAI,MAA0F,CAAC;AAE/F,0FAA0F;AAC1F,SAAgB,wBAAwB;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,4EAA4E;IAC5E,4EAA4E;IAC5E,OAAO,KAAK,EAAE,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,kFAAkF;AAClF,SAAgB,oBAAoB,CAAC,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,kBAAS,CAAC,GAAG,EAAE,2BAA2B,CAAC,CAAC;IACtF,IAAI,KAAK,IAAI,CAAC,eAAe,EAAE;QAAE,MAAM,IAAI,kBAAS,CAAC,GAAG,EAAE,sEAAsE,CAAC,CAAC;IAClI,eAAe,EAAE,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3B,IAAA,cAAK,GAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QACvB,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpC,IAAI,KAAK;YAAE,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAED,SAAS,UAAU,CAAC,KAAmB;IACrC,kFAAkF;IAClF,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAC5E,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAA,kBAAM,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClH,IAAA,kBAAM,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAA,qBAAS,EAAC,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7G,MAAM,EAAE,IAAA,4BAAqB,EAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAC7D,CAAC;AACD,SAAS,SAAS;IAChB,OAAO,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,2JAA2J,CAAC,CAAC,GAAG,EAAyB,CAAC;AACnN,CAAC;AACD,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,2KAA2K,CAAC,CAAC,GAAG,CAAC,GAAG,CAAwB,CAAC;AACtO,CAAC;AAED,SAAS,WAAW,CAAC,KAAkC;IACrD,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QAAC,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAC9F,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzM,MAAM,MAAM,GAAG,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC;IACpF,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;QACjC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAwC,CAAC;QACnE,OAAO,GAAG,KAAK,SAAS,IAAI,IAAA,kBAAM,EAAC,GAAG,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;kFACkF;AAClF,SAAgB,0BAA0B;IACxC,MAAM,OAAO,GAAG,IAAA,iCAAyB,GAAE,EAAE,KAAK,GAAG,OAAO,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;IACzF,MAAM,IAAI,GAAG,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,0GAA0G,CAAC,CAAC,GAAG,EAAiE,CAAC;IAC9M,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACpL,gBAAgB,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;AACvG,CAAC;AAED;;oFAEoF;AAC7E,KAAK,UAAU,gBAAgB,CAAC,MAAmB,EAAE,MAAmB;IAC7E,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,IAAA,iCAAyB,GAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,oBAAoB,CAAC;QAAE,OAAO;IAC7H,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACvC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,eAAe,CAAC;IACjC,MAAM,KAAK,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtE,MAAM,GAAG,KAAK,CAAC;IACf,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QAC7C,IAAI,GAAoB,CAAC;QACzB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,IAAA,iCAAyB,GAAE,IAAI,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC;QAChH,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAAC,MAAM,IAAI,kBAAS,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;QAAC,CAAC,CAAC,CAAC,CAAC;QAC5H,IAAI,CAAC;YACH,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,GAAG,IAAA,cAAK,GAAE,CAAC;YACnB,2EAA2E;YAC3E,wEAAwE;YACxE,qEAAqE;YACrE,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,2FAA2F,CAAC,CAAC,GAAG,EAAqB,CAAC;YAChJ,IAAI,MAAM,EAAE,CAAC;gBAAC,GAAG,GAAG,MAAM,CAAC;gBAAC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAAC,CAAC;YACxF,MAAM,KAAK,GAAG,IAAA,2BAAmB,GAAE,CAAC;YAAC,IAAA,+BAAuB,EAAC,KAAK,CAAC,CAAC;YACpE,MAAM,GAAG,GAAG,IAAA,4BAAqB,EAAC,KAAK,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;gBAAE,OAAO;YAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,CAAC;YACtD,IAAI,KAA6B,CAAC;YAClC,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAe,CAAC;gBACxD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACzO,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YACzI,mFAAmF;YACnF,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;gBAAE,OAAO;YACvH,KAAK,EAAE,CAAC;YACR,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC;2EACoD,CAAC,CAAC,GAAG,CAAC,GAAG,CAAoB,CAAC;YACnG,sFAAsF;YACtF,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,2EAA2E,CAAC,CAAC,GAAG,CAAC,GAAG,CAA+B,CAAC;YAC9I,IAAI,GAAG,IAAI,MAAM,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE;gBAAE,GAAG,GAAG,SAAS,CAAC;YAClD,IAAI,GAAG,EAAE,CAAC;gBACR,EAAE,CAAC,OAAO,CAAC,qIAAqI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC9J,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,eAAe,EAAE,CAAC;YAC3E,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC;iCACD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACnL,GAAG,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC9G,CAAC;YACD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YAAC,MAAM,EAAE,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,kBAAW,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACzE,8EAA8E;YAC9E,kFAAkF;YAClF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,KAAK,EAAE,EAAE;gBACnD,KAAK,EAAE,CAAC;gBACR,IAAI,KAAK;oBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBAC9E,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YACtC,CAAC,CAAC;YACF,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,EAAE,CAAC;YACR,gFAAgF;YAChF,kFAAkF;YAClF,MAAM,WAAW,GAAG,IAAA,2BAAmB,GAAE,CAAC;YAAC,IAAA,+BAAuB,EAAC,WAAW,CAAC,CAAC;YAChF,IAAI,IAAA,4BAAqB,EAAC,WAAW,CAAC,KAAK,GAAG,EAAE,CAAC;gBAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAAC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YAAC,CAAC;YACzG,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YACzC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC3F,EAAE,CAAC,OAAO,CAAC,sHAAsH,CAAC;iBAC/H,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,EAAE,GAAG,IAAA,cAAK,GAAE,CAAC;YACnB,MAAM,QAAQ,GAAG,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAoB,CAAC;YACxI,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC;YAC5C,kFAAkF;YAClF,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC;YACxF,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC/F,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;gBAClB,IAAI,GAAG;oBAAE,EAAE,CAAC,OAAO,CAAC;yFAC6D,CAAC;yBAC/E,GAAG,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnG,IAAI,QAAQ,KAAK,eAAe,IAAI,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC;oBAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;YACrG,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;IACH,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;QACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,KAAK;YAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC"}
@@ -18,6 +18,13 @@ export declare function repairSummaryCitations(candidate: NodeSummary, value: un
18
18
  characters: number;
19
19
  }): NodeSummary;
20
20
  export declare function validateGroups(value: unknown, allowed: Set<string>): TopicGroups;
21
+ /** Evidence and models only: background preparation does not own a sync. */
22
+ export type SummaryInput = Pick<SyncDetail, 'items' | 'context' | 'models'>;
23
+ declare const BACKGROUND: unique symbol;
24
+ /** A receipt identity, not a persisted copy of the evidence. Keep this recipe
25
+ * version in step with planning/validation/transport changes, even when old
26
+ * validated leaf cache keys intentionally remain compatible. */
27
+ export declare function summaryPreparationKey(input: SummaryInput): string;
21
28
  export declare class SummaryTree {
22
29
  private sync;
23
30
  private runId;
@@ -26,7 +33,15 @@ export declare class SummaryTree {
26
33
  private readonly sourceMap;
27
34
  private readonly primary;
28
35
  private readonly accountAggregation;
29
- constructor(sync: SyncDetail, runId: number, signal: AbortSignal);
36
+ private readonly runTable;
37
+ private readonly background;
38
+ private readonly cacheHashes;
39
+ static forBackground(input: SummaryInput, runId: number, signal: AbortSignal): SummaryTree;
40
+ constructor(sync: SummaryInput & {
41
+ id?: number;
42
+ }, runId: number, signal: AbortSignal, mode?: typeof BACKGROUND);
43
+ /** Hashes of the validated outputs consumed by this background build. */
44
+ cacheReceipt(): [string, string][];
30
45
  private stage;
31
46
  call(payload: unknown, system: string, final?: boolean): Promise<string>;
32
47
  private register;
@@ -66,4 +81,5 @@ export declare class SummaryTree {
66
81
  topic: string;
67
82
  }[];
68
83
  }
84
+ export {};
69
85
  //# sourceMappingURL=tree.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/sync/tree.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAgDrG,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA4BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;;;;CAAgB,GAAG,WAAW,CAoBzG;AAED,iGAAiG;AACjG,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;;;;CAAgB,GAAG,WAAW,CAiBxI;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,WAAW,CAehF;AAoBD,qBAAa,WAAW;IAKV,OAAO,CAAC,IAAI;IAAc,OAAO,CAAC,KAAK;IAAU,OAAO,CAAC,MAAM;IAJ3E,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;gBACzB,IAAI,EAAE,UAAU,EAAU,KAAK,EAAE,MAAM,EAAU,MAAM,EAAE,WAAW;IAQxF,OAAO,CAAC,KAAK;IAKP,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAqC5E,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,IAAI;YAYE,IAAI;IAqElB,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,KAAK;YAYC,UAAU;YAeV,MAAM;IAQpB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,UAAU;IAuClB,OAAO,CAAC,YAAY;YAKN,cAAc;YAed,YAAY;IAiCpB,KAAK,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAmErC,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;CACpC"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/sync/tree.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAiDrG,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA4BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;;;;CAAgB,GAAG,WAAW,CAmBzG;AAED,iGAAiG;AACjG,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;;;;CAAgB,GAAG,WAAW,CAiBxI;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,WAAW,CAehF;AAoBD,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;AAC5E,QAAA,MAAM,UAAU,eAA2C,CAAC;AAE5D;;gEAEgE;AAChE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAajE;AAED,qBAAa,WAAW;IAaV,OAAO,CAAC,IAAI;IAAkC,OAAO,CAAC,KAAK;IAAU,OAAO,CAAC,MAAM;IAZ/F,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW;gBAItE,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,EAAU,KAAK,EAAE,MAAM,EAAU,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,OAAO,UAAU;IAWtI,yEAAyE;IACzE,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;IAElC,OAAO,CAAC,KAAK;IAOP,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAsC5E,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;YAYE,IAAI;IA4ElB,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,KAAK;YAYC,UAAU;YAeV,MAAM;IAQpB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,UAAU;IAoDlB,OAAO,CAAC,YAAY;YAKN,cAAc;YAed,YAAY;IA+BpB,KAAK,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAmErC,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;CACpC"}
package/dist/sync/tree.js CHANGED
@@ -4,9 +4,11 @@ exports.SummaryTree = void 0;
4
4
  exports.validateSummary = validateSummary;
5
5
  exports.repairSummaryCitations = repairSummaryCitations;
6
6
  exports.validateGroups = validateGroups;
7
+ exports.summaryPreparationKey = summaryPreparationKey;
7
8
  const schema_1 = require("../db/schema");
8
9
  const normalize_1 = require("./normalize");
9
10
  const store_1 = require("./store");
11
+ const errors_1 = require("../errors");
10
12
  const pi_1 = require("./pi");
11
13
  const references_1 = require("./references");
12
14
  const summary_budget_1 = require("./summary-budget");
@@ -87,7 +89,6 @@ function validateSummary(value, allowed, limits = LEGACY_OUTPUT) {
87
89
  const result = { observations, limitations };
88
90
  if (JSON.stringify(result).length > limits.characters)
89
91
  throw new Error('Summary is too large');
90
- // Check ALL schema fields and source membership before considering a citation-only repair.
91
92
  if (observations.some(item => item.evidence.length > MAX_EVIDENCE))
92
93
  throw new CitationLimitError(result);
93
94
  return result;
@@ -148,7 +149,7 @@ function pack(items, cost, maxChars, maxItems) {
148
149
  for (const item of items) {
149
150
  const chars = cost(item);
150
151
  if (chars > maxChars)
151
- throw new store_1.HttpError(400, 'One summary input exceeds the safety limit; it was not silently dropped.');
152
+ throw new errors_1.HttpError(400, 'One summary input exceeds the safety limit; it was not silently dropped.');
152
153
  if (group.length && (size + chars > maxChars || group.length >= maxItems)) {
153
154
  groups.push(group);
154
155
  group = [];
@@ -164,6 +165,24 @@ function pack(items, cost, maxChars, maxItems) {
164
165
  function sourceData(item, background = false) {
165
166
  return { ...(item.agent && item.agent !== 'pi' ? { agent: item.agent } : {}), id: (0, normalize_1.sourceKey)(item), workspace: item.cwd, timestamp: item.timestamp, sequence: item.sequence, role: item.role, background, text: (0, normalize_1.cleanText)(item.text, Number.MAX_SAFE_INTEGER) };
166
167
  }
168
+ const BACKGROUND = Symbol('background summary preparation');
169
+ /** A receipt identity, not a persisted copy of the evidence. Keep this recipe
170
+ * version in step with planning/validation/transport changes, even when old
171
+ * validated leaf cache keys intentionally remain compatible. */
172
+ function summaryPreparationKey(input) {
173
+ const sources = (items, background) => items.map(item => ({
174
+ session: item.session_id, label: item.session_name, ...sourceData(item, background),
175
+ }));
176
+ return (0, normalize_1.digest)(JSON.stringify({
177
+ recipe: 'summary-preparation-v1-stable-prefix',
178
+ prompts: { PROMPT_VERSION, legacySummarySystem, summarySystem, batchSummarySystem, citationRepairSystem, groupingSystem },
179
+ budgets: { CHUNK_CHARS, MERGE_CHARS, OUTPUT_CHARS, BATCH_OUTPUT, LEGACY_OUTPUT, MAX_EVIDENCE, LUNA_BATCH: summary_budget_1.LUNA_BATCH },
180
+ // Versions of the shared normalization, short-label transport and token estimator.
181
+ dependencies: ['normalization-v2', 'references-v2', 'o200k_base-gpt-tokenizer-4.1.0', 'prompt-headroom-1.2-8192'],
182
+ models: { summary: input.models.summary, final: input.models.final, max_invocations: input.models.max_invocations },
183
+ items: sources(input.items, false), context: sources(input.context, true),
184
+ }));
185
+ }
167
186
  class SummaryTree {
168
187
  sync;
169
188
  runId;
@@ -172,51 +191,68 @@ class SummaryTree {
172
191
  sourceMap;
173
192
  primary;
174
193
  accountAggregation;
175
- constructor(sync, runId, signal) {
194
+ runTable;
195
+ background;
196
+ cacheHashes = new Map();
197
+ static forBackground(input, runId, signal) {
198
+ return new SummaryTree(input, runId, signal, BACKGROUND);
199
+ }
200
+ constructor(sync, runId, signal, mode) {
176
201
  this.sync = sync;
177
202
  this.runId = runId;
178
203
  this.signal = signal;
204
+ this.background = mode === BACKGROUND;
205
+ this.runTable = this.background ? 'background_runs' : 'sync_runs';
206
+ if (!this.background && !Number.isSafeInteger(sync.id))
207
+ throw new Error('Foreground summary preparation requires a sync ID');
179
208
  (0, store_1.assertEvidenceAvailable)(sync);
180
- this.accountAggregation = this.db.prepare('SELECT question FROM sync_runs WHERE id=?').get(runId)?.question === null;
209
+ this.accountAggregation = this.background || this.db.prepare('SELECT question FROM sync_runs WHERE id=?').get(runId)?.question === null;
181
210
  this.sourceMap = new Map([...sync.context, ...sync.items].map(item => [(0, normalize_1.sourceKey)(item), item]));
182
211
  this.primary = new Set(sync.items.map(normalize_1.sourceKey));
183
212
  if (this.primary.size !== sync.items.length)
184
213
  throw new Error('Duplicate canonical source IDs in snapshot');
185
214
  }
186
- stage(stage) {
215
+ /** Hashes of the validated outputs consumed by this background build. */
216
+ cacheReceipt() { return [...this.cacheHashes]; }
217
+ stage(stage, detail) {
187
218
  if (this.signal.aborted)
188
- throw new store_1.HttpError(409, 'Generation cancelled');
189
- this.db.prepare('UPDATE sync_runs SET stage = ? WHERE id = ?').run(stage, this.runId);
219
+ throw new errors_1.HttpError(409, 'Generation cancelled');
220
+ // Background accounting must never retain session titles, paths or model prose.
221
+ const label = !this.background && detail ? `${stage}: ${detail}` : stage;
222
+ this.db.prepare(`UPDATE ${this.runTable} SET stage = ? WHERE id = ?`).run(label, this.runId);
190
223
  }
191
224
  async call(payload, system, final = false) {
225
+ if (this.background && final)
226
+ throw new errors_1.HttpError(409, 'Background preparation cannot call the final provider');
192
227
  if (this.signal.aborted)
193
- throw new store_1.HttpError(409, 'Generation cancelled');
228
+ throw new errors_1.HttpError(409, 'Generation cancelled');
194
229
  const model = final ? this.sync.models.final : this.sync.models.summary;
195
230
  const large = !final && (0, summary_budget_1.isBatchSummaryModel)(model);
196
231
  const prompt = JSON.stringify(payload);
197
232
  const estimatedTokens = (0, summary_budget_1.promptTokens)(prompt, system);
198
233
  if (large && estimatedTokens > summary_budget_1.LUNA_BATCH.requestTokens)
199
- throw new store_1.HttpError(400, 'Summary request exceeds its token safety budget; cached work is intact.');
234
+ throw new errors_1.HttpError(400, 'Summary request exceeds its token safety budget; cached work is intact.');
200
235
  if (final && (0, summary_budget_1.isBatchSummaryModel)(this.sync.models.summary) && estimatedTokens > summary_budget_1.LUNA_BATCH.finalTokens)
201
- throw new store_1.HttpError(400, 'The final input exceeds its compact token budget; no larger or more expensive request was substituted. Cached summaries are intact.');
202
- // Reserve one invocation for the final verdict. Retries count against the same ceiling.
203
- const limit = this.sync.models.max_invocations - (final ? 0 : 1);
236
+ throw new errors_1.HttpError(400, 'The final input exceeds its compact token budget; no larger or more expensive request was substituted. Cached summaries are intact.');
237
+ const limit = this.sync.models.max_invocations - (final || this.background ? 0 : 1);
204
238
  const started = Date.now(), request = payload;
205
239
  const pending = { started_at: new Date(started).toISOString(), task: request.task || 'unknown', stage: request.stage || null,
206
240
  repair: Boolean(request.repair), model, elapsed_ms: 0, input_characters: prompt.length, input_tokens_estimate: estimatedTokens,
207
241
  output_characters: null, output_tokens_estimate: null, status: 'running' };
208
242
  // Reserve budget and the timestamped metadata slot atomically. An interrupted
209
243
  // request remains explicitly unpriced rather than disappearing from usage.
210
- const claimed = this.db.prepare(`UPDATE sync_runs SET invocations=invocations+1,
244
+ const claimed = this.db.prepare(`UPDATE ${this.runTable} SET invocations=invocations+1,
211
245
  metrics_json=json_insert(metrics_json,'$[#]',json(?)) WHERE id=? AND invocations<?
212
246
  RETURNING json_array_length(metrics_json)-1 AS metric_index`).get(JSON.stringify(pending), this.runId, limit);
213
247
  if (!claimed)
214
- throw new store_1.HttpError(409, 'Invocation budget reached. Completed summaries are cached. Raise the limit and retry; nothing was marked covered.');
248
+ throw new errors_1.HttpError(409, 'Invocation budget reached. Completed summaries are cached. Raise the limit and retry; nothing was marked covered.');
215
249
  let response;
216
250
  let accounting;
217
251
  try {
218
252
  response = await (0, pi_1.invokePi)(prompt, { model, system, signal: this.signal, thinking: final ? 'medium' : 'off', largeSummary: large,
219
253
  onAccounting: this.accountAggregation ? value => { accounting = value; } : undefined });
254
+ if (this.signal.aborted)
255
+ throw new errors_1.HttpError(409, 'Generation cancelled');
220
256
  return response;
221
257
  }
222
258
  finally {
@@ -224,12 +260,14 @@ class SummaryTree {
224
260
  elapsed_ms: Date.now() - started, input_characters: prompt.length,
225
261
  input_tokens_estimate: estimatedTokens, output_characters: response?.length ?? null,
226
262
  output_tokens_estimate: response === undefined ? null : (0, summary_budget_1.summaryTokens)(response),
227
- status: response !== undefined ? 'completed' : this.signal.aborted ? 'cancelled' : 'failed' };
263
+ status: this.signal.aborted ? 'cancelled' : response !== undefined ? 'completed' : 'failed' };
228
264
  // Metadata only: no prompts, rejected prose, source text, paths or credentials.
229
- this.db.prepare('UPDATE sync_runs SET metrics_json=json_set(metrics_json,?,json(?)) WHERE id=?').run(`$[${claimed.metric_index}]`, JSON.stringify(metric), this.runId);
265
+ this.db.prepare(`UPDATE ${this.runTable} SET metrics_json=json_set(metrics_json,?,json(?)) WHERE id=?`).run(`$[${claimed.metric_index}]`, JSON.stringify(metric), this.runId);
230
266
  }
231
267
  }
232
268
  register(spec) {
269
+ if (this.background)
270
+ return;
233
271
  this.db.prepare(`INSERT OR IGNORE INTO sync_tree_nodes(sync_id, key, kind, label, children_json, source_count)
234
272
  VALUES (?, ?, ?, ?, ?, ?)`).run(this.sync.id, spec.key, spec.kind, spec.label, JSON.stringify(spec.children), spec.sources.length);
235
273
  }
@@ -248,13 +286,22 @@ class SummaryTree {
248
286
  async node(spec, validate, derived) {
249
287
  this.register(spec);
250
288
  const limits = spec.batch ? BATCH_OUTPUT : LEGACY_OUTPUT;
251
- const setStatus = (status, error = '') => this.db.prepare('UPDATE sync_tree_nodes SET status = ?, error = ? WHERE sync_id = ? AND key = ?').run(status, error, this.sync.id, spec.key);
289
+ const setStatus = (status, error = '') => {
290
+ if (!this.background)
291
+ this.db.prepare('UPDATE sync_tree_nodes SET status = ?, error = ? WHERE sync_id = ? AND key = ?').run(status, error, this.sync.id, spec.key);
292
+ };
252
293
  const finish = (cached) => {
294
+ if (this.background) {
295
+ const row = this.db.prepare('SELECT output_json FROM summary_cache WHERE key=?').get(spec.key);
296
+ if (!row)
297
+ throw new Error('A validated summary cache entry disappeared');
298
+ this.cacheHashes.set(spec.key, (0, normalize_1.digest)(row.output_json));
299
+ }
253
300
  setStatus(cached ? 'cached' : 'done');
254
- this.db.prepare('UPDATE sync_runs SET cache_hits = cache_hits + ?, processed_entries = processed_entries + ? WHERE id = ?')
301
+ this.db.prepare(`UPDATE ${this.runTable} SET cache_hits = cache_hits + ?, processed_entries = processed_entries + ? WHERE id = ?`)
255
302
  .run(Number(cached), spec.kind === 'chunk' ? spec.sources.length : 0, this.runId);
256
303
  };
257
- this.stage(`${spec.kind}: ${spec.label}`);
304
+ this.stage(spec.kind, spec.label);
258
305
  for (const cacheKey of [spec.key, spec.legacyKey].filter((key) => Boolean(key))) {
259
306
  const cache = this.db.prepare('SELECT output_json FROM summary_cache WHERE key = ?').get(cacheKey);
260
307
  if (!cache)
@@ -287,7 +334,7 @@ class SummaryTree {
287
334
  let parsed;
288
335
  for (let attempt = 0; attempt < 2; attempt++) {
289
336
  if (attempt)
290
- this.stage(`Repairing ${spec.kind}: ${spec.label} (attempt 2 of 2)`);
337
+ this.stage(`Repairing ${spec.kind}`, `${spec.label} (attempt 2 of 2)`);
291
338
  const citationRepair = attempt > 0 && citationCandidate !== undefined;
292
339
  const response = await this.call({ task: citationRepair ? 'repair_summary_citations' : spec.kind === 'topics' ? 'group_topics' : 'summarize', stage: spec.kind,
293
340
  input: wire.input,
@@ -307,11 +354,11 @@ class SummaryTree {
307
354
  }
308
355
  }
309
356
  if (parsed === undefined)
310
- throw new store_1.HttpError(502, `Summary validation failed: ${validationError}. Valid chunks are cached for retry.`);
357
+ throw new errors_1.HttpError(502, `Summary validation failed: ${validationError}. Valid chunks are cached for retry.`);
311
358
  output = parsed;
312
359
  }
313
360
  if (this.signal.aborted)
314
- throw new store_1.HttpError(409, 'Generation cancelled');
361
+ throw new errors_1.HttpError(409, 'Generation cancelled');
315
362
  this.db.prepare(`INSERT OR REPLACE INTO summary_cache(key, kind, model, prompt_version, output_json, created_at)
316
363
  VALUES (?, ?, ?, ?, ?, ?)`).run(spec.key, spec.kind, this.sync.models.summary, spec.batch ? summary_budget_1.LUNA_BATCH.version : PROMPT_VERSION, JSON.stringify(output), new Date().toISOString());
317
364
  finish(false);
@@ -376,7 +423,7 @@ class SummaryTree {
376
423
  next.push({ key: spec.key, kind, label: spec.label, sources, summary });
377
424
  }
378
425
  if (nodes.length > 1 && next.length >= nodes.length)
379
- throw new store_1.HttpError(400, 'Summaries did not fit a merge. No input was discarded.');
426
+ throw new errors_1.HttpError(400, 'Summaries did not fit a merge. No input was discarded.');
380
427
  return next;
381
428
  }
382
429
  async reduce(nodes, kind, label) {
@@ -405,13 +452,13 @@ class SummaryTree {
405
452
  let current = [], tokens = 0, bytes = 0;
406
453
  for (const item of items) {
407
454
  if (this.signal.aborted)
408
- throw new store_1.HttpError(409, 'Generation cancelled');
455
+ throw new errors_1.HttpError(409, 'Generation cancelled');
409
456
  const json = JSON.stringify(body(item));
410
457
  // Per-item overhead covers delimiters and the separate allowed-ID list.
411
458
  // The complete wire request is checked again before invoking the model.
412
459
  const count = (0, summary_budget_1.summaryTokens)(json) + 32, size = Buffer.byteLength(json) + 64;
413
460
  if (count > summary_budget_1.LUNA_BATCH.inputTokens || size > summary_budget_1.LUNA_BATCH.inputBytes)
414
- throw new store_1.HttpError(400, 'One summary input exceeds the token/byte budget; no evidence was dropped.');
461
+ throw new errors_1.HttpError(400, 'One summary input exceeds the token/byte budget; no evidence was dropped.');
415
462
  if (current.length && (tokens + count > summary_budget_1.LUNA_BATCH.inputTokens || bytes + size > summary_budget_1.LUNA_BATCH.inputBytes || current.length >= summary_budget_1.LUNA_BATCH.entries)) {
416
463
  batches.push(current);
417
464
  current = [];
@@ -439,18 +486,33 @@ class SummaryTree {
439
486
  const first = positions.get((0, normalize_1.sourceKey)(batch[0]));
440
487
  const background = first ? items.slice(Math.max(0, first - 2), first).map(item => sourceData(item, true))
441
488
  : this.sync.context.filter(item => item.session_id === session).slice(-6).map(item => sourceData(item, true));
442
- // No sync ID, E-reference or display ordinal in the cache key.
443
489
  const input = { session, workspace: batch[0].cwd, evidence: batch.map(item => sourceData(item)), background };
444
490
  const allowed = new Set([...input.evidence, ...background].map(item => item.id));
445
- const wire = (0, references_1.prepareModelInput)('chunk', input, allowed);
446
- const prompt = JSON.stringify({ task: 'summarize', stage: 'chunk', allowed_source_ids: [...wire.allowed], input: wire.input });
447
- if ((0, summary_budget_1.promptTokens)(prompt, batchSummarySystem) > summary_budget_1.LUNA_BATCH.plannedRequestTokens ||
448
- Buffer.byteLength(prompt) + Buffer.byteLength(batchSummarySystem) > summary_budget_1.LUNA_BATCH.inputBytes) {
449
- if (batch.length === 1)
450
- throw new store_1.HttpError(400, 'One entry and its background exceed the summary budget; no evidence was dropped.');
451
- const middle = Math.ceil(batch.length / 2);
452
- append(batch.slice(0, middle));
453
- append(batch.slice(middle));
491
+ const fits = (length) => {
492
+ const prefix = { ...input, evidence: input.evidence.slice(0, length) };
493
+ const refs = new Set([...prefix.evidence, ...background].map(item => item.id));
494
+ const wire = (0, references_1.prepareModelInput)('chunk', prefix, refs);
495
+ const prompt = JSON.stringify({ task: 'summarize', stage: 'chunk', allowed_source_ids: [...wire.allowed], input: wire.input });
496
+ return (0, summary_budget_1.promptTokens)(prompt, batchSummarySystem) <= summary_budget_1.LUNA_BATCH.plannedRequestTokens &&
497
+ Buffer.byteLength(prompt) + Buffer.byteLength(batchSummarySystem) <= summary_budget_1.LUNA_BATCH.inputBytes;
498
+ };
499
+ if (!fits(batch.length)) {
500
+ if (!fits(1))
501
+ throw new errors_1.HttpError(400, 'One entry and its background exceed the summary budget; no evidence was dropped.');
502
+ // Never bisect by the current tail length: its midpoint moves when
503
+ // history is appended, invalidating completed large leaves. Fill a
504
+ // deterministic prefix instead, then plan the remainder with its
505
+ // own adjacent evidence. Existing unsplit keys remain unchanged.
506
+ let low = 1, high = batch.length;
507
+ while (low + 1 < high) {
508
+ const middle = Math.floor((low + high) / 2);
509
+ if (fits(middle))
510
+ low = middle;
511
+ else
512
+ high = middle;
513
+ }
514
+ append(batch.slice(0, low));
515
+ append(batch.slice(low));
454
516
  return;
455
517
  }
456
518
  result.push(this.spec('chunk', `${batch[0].session_name || batch[0].cwd} · batch ${first + 1}–${first + batch.length}`, input, allowed, batch.map(normalize_1.sourceKey), [], true));
@@ -498,24 +560,22 @@ class SummaryTree {
498
560
  const missing = specs.filter(spec => !this.cached(spec)).length;
499
561
  // A lower bound: reconciliation may add calls, or reuse a cached parent.
500
562
  // Do not reject an all-cached tree that only needs its reserved final slot.
501
- const minimum = missing + 1;
563
+ const minimum = missing + (this.background ? 0 : 1);
502
564
  if (minimum > this.sync.models.max_invocations)
503
- throw new store_1.HttpError(409, `At least ${minimum} invocations are needed for uncached batches and the verdict, before any uncached reconciliation. Raise the invocation limit; no model calls have been made.`);
565
+ throw new errors_1.HttpError(409, `At least ${minimum} invocations are needed for uncached batches${this.background ? '' : ' and the verdict'}, before any uncached reconciliation. Raise the invocation limit; no model calls have been made.`);
504
566
  let nodes = [];
505
567
  for (const spec of specs)
506
568
  nodes.push({ key: spec.key, kind: 'chunk', label: spec.label, sources: spec.sources,
507
569
  summary: await this.node(spec, (value, refs) => validateSummary(value, refs, spec.batch ? BATCH_OUTPUT : LEGACY_OUTPUT)) });
508
- const processed = this.db.prepare('SELECT processed_entries FROM sync_runs WHERE id=?').get(this.runId).processed_entries;
570
+ const processed = this.db.prepare(`SELECT processed_entries FROM ${this.runTable} WHERE id=?`).get(this.runId).processed_entries;
509
571
  if (processed !== this.sync.items.length)
510
572
  throw new Error('Incomplete source coverage: refusing to generate a final verdict');
511
- // One global Luna reconciliation instead of session merges → routing →
512
- // topic merges. Add a level only when the combined input exceeds a budget.
513
573
  for (let depth = 0; nodes.length > 1; depth++) {
514
574
  if (depth > 20)
515
575
  throw new Error('Summary tree depth exceeded');
516
576
  const groups = this.tokenBatches(nodes, node => this.mergePayload([node], true));
517
577
  if (groups.length >= nodes.length)
518
- throw new store_1.HttpError(400, 'Summaries cannot be safely reconciled within the input budget. Cached results are intact.');
578
+ throw new errors_1.HttpError(400, 'Summaries cannot be safely reconciled within the input budget. Cached results are intact.');
519
579
  const next = [];
520
580
  for (const [index, group] of groups.entries())
521
581
  next.push(group.length === 1 ? group[0] : await this.reconcileBatch(group, `Global reconciliation ${depth + 1} · part ${index + 1}`));
@@ -527,8 +587,8 @@ class SummaryTree {
527
587
  if (nodes.length && !this.compactRoots(nodes))
528
588
  nodes = [await this.reconcileBatch(nodes, 'Compact final input', true)];
529
589
  if (!this.compactRoots(nodes))
530
- throw new store_1.HttpError(400, 'The reconciled summary cannot fit the compact final budget. Cached results remain inspectable; no evidence was marked reviewed.');
531
- this.stage('Final verdict');
590
+ throw new errors_1.HttpError(400, 'The reconciled summary cannot fit the compact final budget. Cached results remain inspectable; no evidence was marked reviewed.');
591
+ this.stage(this.background ? 'Summaries prepared' : 'Final verdict');
532
592
  return nodes;
533
593
  }
534
594
  async build() {
@@ -536,9 +596,10 @@ class SummaryTree {
536
596
  return this.buildBatched();
537
597
  // Unknown/operator-selected models retain their established conservative limits.
538
598
  const specs = this.chunkSpecs();
539
- const missing = [...specs.values()].flat().filter(spec => !this.db.prepare('SELECT 1 FROM summary_cache WHERE key = ? OR key = ?').get(spec.key, spec.legacyKey || spec.key)).length;
540
- if (missing + 1 > this.sync.models.max_invocations)
541
- throw new store_1.HttpError(409, `At least ${missing + 1} invocations are needed just for uncached chunks and the verdict, before merges. Raise the invocation limit; no model calls have been made.`);
599
+ const missing = [...specs.values()].flat().filter(spec => !this.cached(spec)).length;
600
+ const minimum = missing + (this.background ? 0 : 1);
601
+ if (minimum > this.sync.models.max_invocations)
602
+ throw new errors_1.HttpError(409, `At least ${minimum} invocations are needed just for uncached chunks${this.background ? '' : ' and the verdict'}, before merges. Raise the invocation limit; no model calls have been made.`);
542
603
  const sessionRoots = [];
543
604
  // Deliberately serial for predictable quotas/cancellation. Cached nodes are instant.
544
605
  for (const [session, chunks] of specs) {
@@ -547,7 +608,7 @@ class SummaryTree {
547
608
  nodes.push({ key: spec.key, kind: 'chunk', label: spec.label, sources: spec.sources, summary: await this.node(spec, validateSummary) });
548
609
  sessionRoots.push(await this.reduce(nodes, 'session', `Session ${session.slice(0, 8)}`));
549
610
  }
550
- const processed = this.db.prepare('SELECT processed_entries FROM sync_runs WHERE id = ?').get(this.runId).processed_entries;
611
+ const processed = this.db.prepare(`SELECT processed_entries FROM ${this.runTable} WHERE id = ?`).get(this.runId).processed_entries;
551
612
  if (processed !== this.sync.items.length)
552
613
  throw new Error('Incomplete source coverage: refusing to generate a final verdict');
553
614
  const descriptors = sessionRoots.flatMap(node => node.summary.observations.map((observation, index) => ({
@@ -571,7 +632,6 @@ class SummaryTree {
571
632
  const topics = [];
572
633
  for (const group of topicGroups.values()) {
573
634
  const members = group.members.map(id => descriptorMap.get(id));
574
- // Input observations have been partitioned exactly once by validateGroups.
575
635
  const pieces = members.map(item => ({ ...item.node, key: item.id,
576
636
  summary: { observations: [item.observation], limitations: item.node.summary.limitations } }));
577
637
  const chunks = pack(pieces, item => JSON.stringify(this.mergePayload([item])).length, MERGE_CHARS, 12);
@@ -594,7 +654,7 @@ class SummaryTree {
594
654
  throw new Error('Cross-topic tree depth exceeded');
595
655
  roots = await this.mergeLevel(roots, 'overview', `Cross-topic reconciliation ${depth + 1}`);
596
656
  }
597
- this.stage('Final verdict');
657
+ this.stage(this.background ? 'Summaries prepared' : 'Final verdict');
598
658
  return roots;
599
659
  }
600
660
  // Final verification records supply roles/session/sequence once per distinct