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.
- package/CHANGELOG.md +40 -0
- package/MEMORY.md +43 -6
- package/PERSONA.md +9 -2
- package/README.md +14 -14
- package/SETUP.md +23 -3
- package/TRAY.md +4 -4
- package/VOICE.md +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +0 -13
- package/dist/auth/middleware.js.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/data-dir.d.ts +2 -0
- package/dist/data-dir.d.ts.map +1 -0
- package/dist/data-dir.js +12 -0
- package/dist/data-dir.js.map +1 -0
- package/dist/db/evidence.d.ts +2 -1
- package/dist/db/evidence.d.ts.map +1 -1
- package/dist/db/evidence.js +6 -5
- package/dist/db/evidence.js.map +1 -1
- package/dist/db/pointer-migration.js +4 -4
- package/dist/db/pointer-migration.js.map +1 -1
- package/dist/db/schema.d.ts +0 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +31 -7
- package/dist/db/schema.js.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/errors.js.map +1 -0
- package/dist/memory/context.d.ts +2 -9
- package/dist/memory/context.d.ts.map +1 -1
- package/dist/memory/context.js +7 -84
- package/dist/memory/context.js.map +1 -1
- package/dist/memory/embedding-contract.d.ts +5 -0
- package/dist/memory/embedding-contract.d.ts.map +1 -0
- package/dist/memory/embedding-contract.js +12 -0
- package/dist/memory/embedding-contract.js.map +1 -0
- package/dist/memory/embedding-pool.d.ts +1 -2
- package/dist/memory/embedding-pool.d.ts.map +1 -1
- package/dist/memory/embedding-pool.js +9 -9
- package/dist/memory/embedding-pool.js.map +1 -1
- package/dist/memory/embedding-worker.js +2 -1
- package/dist/memory/embedding-worker.js.map +1 -1
- package/dist/{voice/history-focus.d.ts → memory/focus.d.ts} +6 -6
- package/dist/memory/focus.d.ts.map +1 -0
- package/dist/{voice/history-focus.js → memory/focus.js} +6 -6
- package/dist/memory/focus.js.map +1 -0
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +12 -15
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/model-assets.d.ts +5 -0
- package/dist/memory/model-assets.d.ts.map +1 -0
- package/dist/memory/model-assets.js +84 -0
- package/dist/memory/model-assets.js.map +1 -0
- package/dist/memory/model.d.ts +2 -7
- package/dist/memory/model.d.ts.map +1 -1
- package/dist/memory/model.js +13 -83
- package/dist/memory/model.js.map +1 -1
- package/dist/memory/packet.d.ts +9 -0
- package/dist/memory/packet.d.ts.map +1 -0
- package/dist/memory/packet.js +78 -0
- package/dist/memory/packet.js.map +1 -0
- package/dist/memory/passages.d.ts +15 -0
- package/dist/memory/passages.d.ts.map +1 -0
- package/dist/memory/passages.js +22 -0
- package/dist/memory/passages.js.map +1 -0
- package/dist/memory/readiness.d.ts.map +1 -1
- package/dist/memory/readiness.js +2 -6
- package/dist/memory/readiness.js.map +1 -1
- package/dist/memory/service.d.ts +2 -1
- package/dist/memory/service.d.ts.map +1 -1
- package/dist/memory/service.js +46 -22
- package/dist/memory/service.js.map +1 -1
- package/dist/memory/sources.d.ts +0 -1
- package/dist/memory/sources.d.ts.map +1 -1
- package/dist/memory/sources.js +3 -7
- package/dist/memory/sources.js.map +1 -1
- package/dist/memory/stamps.d.ts +3 -3
- package/dist/memory/stamps.d.ts.map +1 -1
- package/dist/memory/stamps.js +3 -3
- package/dist/memory/stamps.js.map +1 -1
- package/dist/memory/storage.d.ts.map +1 -1
- package/dist/memory/storage.js +3 -2
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/types.d.ts +2 -1
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +2 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/pi/runtime.js +7 -7
- package/dist/pi/runtime.js.map +1 -1
- package/dist/pi/setup.d.ts +0 -1
- package/dist/pi/setup.d.ts.map +1 -1
- package/dist/pi/setup.js +2 -11
- package/dist/pi/setup.js.map +1 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/auth.js +0 -5
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/http.d.ts +6 -0
- package/dist/routes/http.d.ts.map +1 -0
- package/dist/routes/http.js +22 -0
- package/dist/routes/http.js.map +1 -0
- package/dist/routes/live-voice.d.ts.map +1 -1
- package/dist/routes/live-voice.js +18 -32
- package/dist/routes/live-voice.js.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +8 -14
- package/dist/routes/memory.js.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/dist/routes/sync.js +65 -65
- package/dist/routes/sync.js.map +1 -1
- package/dist/routes/voice.d.ts.map +1 -1
- package/dist/routes/voice.js +7 -7
- package/dist/routes/voice.js.map +1 -1
- package/dist/server-ownership.d.ts +5 -0
- package/dist/server-ownership.d.ts.map +1 -0
- package/dist/server-ownership.js +34 -0
- package/dist/server-ownership.js.map +1 -0
- package/dist/server-state.js +3 -3
- package/dist/server-state.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +40 -10
- package/dist/server.js.map +1 -1
- package/dist/sync/activity.d.ts +26 -0
- package/dist/sync/activity.d.ts.map +1 -0
- package/dist/sync/activity.js +80 -0
- package/dist/sync/activity.js.map +1 -0
- package/dist/{memory/automatic.d.ts → sync/background.d.ts} +7 -12
- package/dist/sync/background.d.ts.map +1 -0
- package/dist/sync/background.js +184 -0
- package/dist/sync/background.js.map +1 -0
- package/dist/sync/collector.d.ts.map +1 -1
- package/dist/sync/collector.js +7 -6
- package/dist/sync/collector.js.map +1 -1
- package/dist/sync/manager.d.ts +1 -4
- package/dist/sync/manager.d.ts.map +1 -1
- package/dist/sync/manager.js +29 -49
- package/dist/sync/manager.js.map +1 -1
- package/dist/sync/models.d.ts +3 -0
- package/dist/sync/models.d.ts.map +1 -0
- package/dist/sync/models.js +12 -0
- package/dist/sync/models.js.map +1 -0
- package/dist/sync/pi.d.ts +1 -1
- package/dist/sync/pi.d.ts.map +1 -1
- package/dist/sync/pi.js +32 -19
- package/dist/sync/pi.js.map +1 -1
- package/dist/{voice → sync}/preparation.d.ts +0 -1
- package/dist/sync/preparation.d.ts.map +1 -0
- package/dist/{voice → sync}/preparation.js +10 -18
- package/dist/sync/preparation.js.map +1 -0
- package/dist/sync/retrieval.d.ts +3 -0
- package/dist/sync/retrieval.d.ts.map +1 -0
- package/dist/sync/retrieval.js +12 -0
- package/dist/sync/retrieval.js.map +1 -0
- package/dist/sync/source-files.d.ts +2 -4
- package/dist/sync/source-files.d.ts.map +1 -1
- package/dist/sync/source-files.js +5 -20
- package/dist/sync/source-files.js.map +1 -1
- package/dist/sync/store.d.ts +5 -15
- package/dist/sync/store.d.ts.map +1 -1
- package/dist/sync/store.js +41 -90
- package/dist/sync/store.js.map +1 -1
- package/dist/sync/summary-preparation.d.ts +14 -0
- package/dist/sync/summary-preparation.d.ts.map +1 -0
- package/dist/sync/summary-preparation.js +205 -0
- package/dist/sync/summary-preparation.js.map +1 -0
- package/dist/sync/tree.d.ts +17 -1
- package/dist/sync/tree.d.ts.map +1 -1
- package/dist/sync/tree.js +109 -49
- package/dist/sync/tree.js.map +1 -1
- package/dist/sync/usage.d.ts +3 -1
- package/dist/sync/usage.d.ts.map +1 -1
- package/dist/sync/usage.js +27 -9
- package/dist/sync/usage.js.map +1 -1
- package/dist/tray/launcher.js +2 -2
- package/dist/tray/launcher.js.map +1 -1
- package/dist/tray/worker.js +4 -4
- package/dist/tray/worker.js.map +1 -1
- package/dist/types.d.ts +9 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/voice/history-delivery.js +3 -3
- package/dist/voice/history-delivery.js.map +1 -1
- package/dist/voice/live-context.js +3 -3
- package/dist/voice/live-context.js.map +1 -1
- package/dist/voice/live.d.ts +1 -0
- package/dist/voice/live.d.ts.map +1 -1
- package/dist/voice/live.js +57 -34
- package/dist/voice/live.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-5bTQNQYG.js +131 -0
- package/public/index.html +1 -1
- package/voice/realtime/README.md +1 -1
- package/voice/realtime/helper-process.mjs +54 -0
- package/voice/realtime/native.mjs +55 -15
- package/voice/realtime/probe-server.mjs +24 -4
- package/voice/realtime/probe.mjs +49 -11
- package/voice/realtime/rpc.mjs +7 -32
- package/voice/realtime/setup.mjs +82 -19
- package/dist/memory/automatic.d.ts.map +0 -1
- package/dist/memory/automatic.js +0 -200
- package/dist/memory/automatic.js.map +0 -1
- package/dist/sync/automatic.d.ts +0 -7
- package/dist/sync/automatic.d.ts.map +0 -1
- package/dist/sync/automatic.js +0 -56
- package/dist/sync/automatic.js.map +0 -1
- package/dist/voice/history-focus.d.ts.map +0 -1
- package/dist/voice/history-focus.js.map +0 -1
- package/dist/voice/preparation.d.ts.map +0 -1
- package/dist/voice/preparation.js.map +0 -1
- 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":"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"}
|
package/dist/sync/tree.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/sync/tree.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
189
|
-
|
|
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
|
|
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
|
|
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
|
|
202
|
-
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
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 = '') =>
|
|
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(
|
|
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(
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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.
|
|
540
|
-
|
|
541
|
-
|
|
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(
|
|
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
|