@dsh-enhanced/assistant-dreaming 0.0.0-stage → 0.1.70
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/LICENSE +21 -0
- package/README.md +281 -2
- package/cordis.patch.yml +3 -0
- package/lib/config.d.ts +87 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +145 -0
- package/lib/config.js.map +1 -0
- package/lib/dream-agent.d.ts +58 -0
- package/lib/dream-agent.d.ts.map +1 -0
- package/lib/dream-agent.js +188 -0
- package/lib/dream-agent.js.map +1 -0
- package/lib/index.d.ts +20 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +15 -0
- package/lib/index.js.map +1 -0
- package/lib/notify.d.ts +64 -0
- package/lib/notify.d.ts.map +1 -0
- package/lib/notify.js +52 -0
- package/lib/notify.js.map +1 -0
- package/lib/projection.d.ts +32 -0
- package/lib/projection.d.ts.map +1 -0
- package/lib/projection.js +89 -0
- package/lib/projection.js.map +1 -0
- package/lib/runtime.d.ts +43 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +500 -0
- package/lib/runtime.js.map +1 -0
- package/lib/store.d.ts +113 -0
- package/lib/store.d.ts.map +1 -0
- package/lib/store.js +367 -0
- package/lib/store.js.map +1 -0
- package/lib/trigger.d.ts +134 -0
- package/lib/trigger.d.ts.map +1 -0
- package/lib/trigger.js +155 -0
- package/lib/trigger.js.map +1 -0
- package/lib/version.d.ts +2 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +2 -0
- package/lib/version.js.map +1 -0
- package/package.json +95 -3
package/lib/store.js
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstatSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, isAbsolute } from 'node:path';
|
|
4
|
+
import { types as utilTypes } from 'node:util';
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
/**
|
|
7
|
+
* Private idea ledger. Fail-closed, lane-scoped and side-effect free: it only
|
|
8
|
+
* records dreaming runs, submitted ideas and per-day counters. Nothing in this
|
|
9
|
+
* module adopts, notifies or promotes anything.
|
|
10
|
+
*/
|
|
11
|
+
const SCHEMA_VERSION = 2;
|
|
12
|
+
export function assertLedgerPath(path) {
|
|
13
|
+
if (!isAbsolute(path) || dirname(path) === path)
|
|
14
|
+
throw new Error('dream store: databasePath must be an absolute file path');
|
|
15
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
16
|
+
const parent = lstatSync(dirname(path));
|
|
17
|
+
// Fail closed on shared or world-writable ancestors, mirroring DSH state
|
|
18
|
+
// privacy: the ledger holds owner-derived freeform text.
|
|
19
|
+
if ((parent.mode & 0o022) !== 0 || parent.uid !== process.getuid?.()) {
|
|
20
|
+
throw new Error('dream store: database directory must be private to the service owner');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function assertMutable(closed) {
|
|
24
|
+
if (closed)
|
|
25
|
+
throw new Error('dream store: database is closed');
|
|
26
|
+
}
|
|
27
|
+
/** New idea data must be exact own data: no accessors, proxies or cycles. */
|
|
28
|
+
export function ownData(input) {
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
let nodes = 0;
|
|
31
|
+
const visit = (value, depth) => {
|
|
32
|
+
if (++nodes > 16_384 || depth > 32)
|
|
33
|
+
throw new Error('dream data exceeds bounds');
|
|
34
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'number' && Number.isSafeInteger(value))
|
|
35
|
+
return;
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
if (!value.isWellFormed() || Buffer.byteLength(value) > 65_536)
|
|
38
|
+
throw new Error('dream text exceeds bounds');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (typeof value !== 'object' || utilTypes.isProxy(value) || seen.has(value)
|
|
42
|
+
|| ![Object.prototype, null, Array.prototype].includes(Object.getPrototypeOf(value))) {
|
|
43
|
+
throw new Error('invalid dream data');
|
|
44
|
+
}
|
|
45
|
+
seen.add(value);
|
|
46
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
47
|
+
if (Array.isArray(value))
|
|
48
|
+
delete descriptors.length;
|
|
49
|
+
if (Reflect.ownKeys(descriptors).some(key => typeof key !== 'string')
|
|
50
|
+
|| Object.values(descriptors).some(field => !field.enumerable || !('value' in field))) {
|
|
51
|
+
throw new Error('invalid dream own data');
|
|
52
|
+
}
|
|
53
|
+
for (const field of Object.values(descriptors))
|
|
54
|
+
visit(field.value, depth + 1);
|
|
55
|
+
seen.delete(value);
|
|
56
|
+
};
|
|
57
|
+
visit(input, 0);
|
|
58
|
+
return JSON.parse(JSON.stringify(input));
|
|
59
|
+
}
|
|
60
|
+
export class DreamStore {
|
|
61
|
+
db;
|
|
62
|
+
lane;
|
|
63
|
+
closed = false;
|
|
64
|
+
constructor(databasePath, lane) {
|
|
65
|
+
if (!lane || lane.length > 128 || !/^[\w:-]+$/u.test(lane))
|
|
66
|
+
throw new Error('dream store: invalid lane');
|
|
67
|
+
assertLedgerPath(databasePath);
|
|
68
|
+
this.lane = lane;
|
|
69
|
+
this.db = new DatabaseSync(databasePath);
|
|
70
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
71
|
+
this.db.exec('PRAGMA foreign_keys = ON');
|
|
72
|
+
this.db.exec(`PRAGMA busy_timeout = 5000`);
|
|
73
|
+
this.migrate();
|
|
74
|
+
}
|
|
75
|
+
migrate() {
|
|
76
|
+
const version = Number(this.db.prepare('PRAGMA user_version').get().user_version);
|
|
77
|
+
if (version > SCHEMA_VERSION)
|
|
78
|
+
throw new Error('dream store: database schema is newer than this build');
|
|
79
|
+
if (version === SCHEMA_VERSION)
|
|
80
|
+
return;
|
|
81
|
+
if (version === 0) {
|
|
82
|
+
this.db.exec(`
|
|
83
|
+
CREATE TABLE IF NOT EXISTS dream_runs (
|
|
84
|
+
id TEXT PRIMARY KEY,
|
|
85
|
+
lane TEXT NOT NULL,
|
|
86
|
+
occurrence INTEGER NOT NULL,
|
|
87
|
+
day_key TEXT NOT NULL,
|
|
88
|
+
started_at INTEGER NOT NULL,
|
|
89
|
+
ended_at INTEGER,
|
|
90
|
+
outcome TEXT NOT NULL CHECK (outcome IN ('running','completed','interrupted')),
|
|
91
|
+
draw_digest TEXT NOT NULL,
|
|
92
|
+
idea_count INTEGER NOT NULL DEFAULT 0,
|
|
93
|
+
UNIQUE (lane, occurrence)
|
|
94
|
+
);
|
|
95
|
+
CREATE TABLE IF NOT EXISTS dream_ideas (
|
|
96
|
+
id TEXT PRIMARY KEY,
|
|
97
|
+
lane TEXT NOT NULL,
|
|
98
|
+
run_id TEXT NOT NULL REFERENCES dream_runs(id),
|
|
99
|
+
created_at INTEGER NOT NULL,
|
|
100
|
+
day_key TEXT NOT NULL,
|
|
101
|
+
state TEXT NOT NULL CHECK (state IN ('recorded','withdrawn','owner-acknowledged','validated','rejected')),
|
|
102
|
+
summary TEXT NOT NULL,
|
|
103
|
+
detail TEXT NOT NULL,
|
|
104
|
+
evidence_refs TEXT NOT NULL,
|
|
105
|
+
summary_digest TEXT NOT NULL,
|
|
106
|
+
UNIQUE (lane, summary_digest, day_key)
|
|
107
|
+
);
|
|
108
|
+
CREATE TABLE IF NOT EXISTS dream_idea_events (
|
|
109
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
+
idea_id TEXT NOT NULL REFERENCES dream_ideas(id),
|
|
111
|
+
state TEXT NOT NULL,
|
|
112
|
+
actor TEXT NOT NULL CHECK (actor IN ('owner','host')),
|
|
113
|
+
at INTEGER NOT NULL,
|
|
114
|
+
note TEXT
|
|
115
|
+
);
|
|
116
|
+
CREATE TABLE IF NOT EXISTS dream_counters (
|
|
117
|
+
lane TEXT NOT NULL,
|
|
118
|
+
day_key TEXT NOT NULL,
|
|
119
|
+
dreams INTEGER NOT NULL DEFAULT 0,
|
|
120
|
+
notifications INTEGER NOT NULL DEFAULT 0,
|
|
121
|
+
last_notification_at INTEGER,
|
|
122
|
+
PRIMARY KEY (lane, day_key)
|
|
123
|
+
);
|
|
124
|
+
`);
|
|
125
|
+
this.db.exec(`PRAGMA user_version = 1`);
|
|
126
|
+
}
|
|
127
|
+
// Schema 2: per-subject lottery tickets plus an audit of every recall hit.
|
|
128
|
+
if (version < 2) {
|
|
129
|
+
this.db.exec(`
|
|
130
|
+
CREATE TABLE IF NOT EXISTS dream_tickets (
|
|
131
|
+
subject_kind TEXT NOT NULL CHECK(subject_kind IN ('task-projection','goal','skill','recorded-idea')),
|
|
132
|
+
subject_key TEXT NOT NULL CHECK(length(subject_key) > 0 AND length(subject_key) <= 500),
|
|
133
|
+
ticket_value INTEGER NOT NULL CHECK(ticket_value >= 0 AND ticket_value < 1000000),
|
|
134
|
+
ticket_digest TEXT NOT NULL CHECK(length(ticket_digest) = 64 AND ticket_digest NOT GLOB '*[^a-f0-9]*'),
|
|
135
|
+
created_at INTEGER NOT NULL CHECK(created_at > 0),
|
|
136
|
+
PRIMARY KEY (subject_kind, subject_key)
|
|
137
|
+
) STRICT, WITHOUT ROWID;
|
|
138
|
+
CREATE TABLE IF NOT EXISTS dream_recalls (
|
|
139
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
lane TEXT NOT NULL,
|
|
141
|
+
occurrence INTEGER NOT NULL,
|
|
142
|
+
draw_digest TEXT NOT NULL CHECK(length(draw_digest) = 64 AND draw_digest NOT GLOB '*[^a-f0-9]*'),
|
|
143
|
+
draw_value INTEGER NOT NULL CHECK(draw_value >= 0 AND draw_value < 1000000),
|
|
144
|
+
subject_kind TEXT NOT NULL,
|
|
145
|
+
subject_key TEXT NOT NULL,
|
|
146
|
+
ticket_value INTEGER NOT NULL,
|
|
147
|
+
distance INTEGER NOT NULL CHECK(distance >= 0),
|
|
148
|
+
at INTEGER NOT NULL CHECK(at > 0),
|
|
149
|
+
UNIQUE (lane, occurrence, subject_kind, subject_key)
|
|
150
|
+
) STRICT;
|
|
151
|
+
`);
|
|
152
|
+
this.db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
get isClosed() {
|
|
156
|
+
return this.closed;
|
|
157
|
+
}
|
|
158
|
+
close() {
|
|
159
|
+
if (!this.closed) {
|
|
160
|
+
this.db.close();
|
|
161
|
+
this.closed = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
beginDream(id, occurrence, dayKey, startedAt, drawDigest) {
|
|
165
|
+
assertMutable(this.closed);
|
|
166
|
+
ownData({ id, occurrence, dayKey, startedAt, drawDigest });
|
|
167
|
+
this.db.prepare(`INSERT INTO dream_runs (id, lane, occurrence, day_key, started_at, outcome, draw_digest)
|
|
168
|
+
VALUES (?, ?, ?, ?, ?, 'running', ?)`).run(id, this.lane, occurrence, dayKey, startedAt, drawDigest);
|
|
169
|
+
this.db.prepare(`INSERT INTO dream_counters (lane, day_key, dreams) VALUES (?, ?, 1)
|
|
170
|
+
ON CONFLICT(lane, day_key) DO UPDATE SET dreams = dreams + 1`).run(this.lane, dayKey);
|
|
171
|
+
return this.run(id);
|
|
172
|
+
}
|
|
173
|
+
completeDream(id, endedAt, ideaCount) {
|
|
174
|
+
assertMutable(this.closed);
|
|
175
|
+
const result = this.db.prepare(`UPDATE dream_runs SET outcome='completed', ended_at=?, idea_count=?
|
|
176
|
+
WHERE id=? AND lane=? AND outcome='running'`).run(endedAt, ideaCount, id, this.lane);
|
|
177
|
+
if (result.changes !== 1)
|
|
178
|
+
throw new Error('dream store: running dream not found');
|
|
179
|
+
}
|
|
180
|
+
markInterrupted(id, endedAt) {
|
|
181
|
+
assertMutable(this.closed);
|
|
182
|
+
this.db.prepare(`UPDATE dream_runs SET outcome='interrupted', ended_at=? WHERE id=? AND lane=? AND outcome='running'`)
|
|
183
|
+
.run(endedAt, id, this.lane);
|
|
184
|
+
}
|
|
185
|
+
run(id) {
|
|
186
|
+
const row = this.db.prepare('SELECT * FROM dream_runs WHERE id=? AND lane=?').get(id, this.lane);
|
|
187
|
+
if (row === undefined)
|
|
188
|
+
return undefined;
|
|
189
|
+
return {
|
|
190
|
+
id: row.id, lane: row.lane, occurrence: row.occurrence,
|
|
191
|
+
dayKey: row.day_key, startedAt: row.started_at,
|
|
192
|
+
endedAt: row.ended_at, outcome: row.outcome,
|
|
193
|
+
drawDigest: row.draw_digest, ideaCount: row.idea_count,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
countDreams(dayKey) {
|
|
197
|
+
const row = this.db.prepare('SELECT dreams FROM dream_counters WHERE lane=? AND day_key=?').get(this.lane, dayKey);
|
|
198
|
+
return row?.dreams ?? 0;
|
|
199
|
+
}
|
|
200
|
+
/** L1 notification accounting over the existing per-day counters row. */
|
|
201
|
+
countNotifications(dayKey) {
|
|
202
|
+
const row = this.db.prepare('SELECT notifications FROM dream_counters WHERE lane=? AND day_key=?')
|
|
203
|
+
.get(this.lane, dayKey);
|
|
204
|
+
return row?.notifications ?? 0;
|
|
205
|
+
}
|
|
206
|
+
incrementNotifications(dayKey, at) {
|
|
207
|
+
assertMutable(this.closed);
|
|
208
|
+
this.db.prepare(`INSERT INTO dream_counters (lane, day_key, dreams, notifications, last_notification_at)
|
|
209
|
+
VALUES (?, ?, 0, 1, ?)
|
|
210
|
+
ON CONFLICT(lane, day_key) DO UPDATE SET notifications = notifications + 1, last_notification_at = ?`)
|
|
211
|
+
.run(this.lane, dayKey, at, at);
|
|
212
|
+
return this.countNotifications(dayKey);
|
|
213
|
+
}
|
|
214
|
+
/** Timestamp of the most recent notification, or null when none was sent. */
|
|
215
|
+
lastNotificationAt() {
|
|
216
|
+
const row = this.db.prepare('SELECT MAX(last_notification_at) AS at FROM dream_counters WHERE lane=?')
|
|
217
|
+
.get(this.lane);
|
|
218
|
+
return row?.at ?? null;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Register one subject's lottery ticket. The value is derived by the caller
|
|
222
|
+
* from the plugin seed, so the store never invents randomness; re-registering
|
|
223
|
+
* the same subject keeps its original ticket.
|
|
224
|
+
*/
|
|
225
|
+
upsertTicket(ticket) {
|
|
226
|
+
assertMutable(this.closed);
|
|
227
|
+
if (!['task-projection', 'goal', 'skill', 'recorded-idea'].includes(ticket.subjectKind)
|
|
228
|
+
|| typeof ticket.subjectKey !== 'string' || !ticket.subjectKey || ticket.subjectKey.length > 500
|
|
229
|
+
|| !Number.isSafeInteger(ticket.value) || ticket.value < 0 || ticket.value >= 1_000_000
|
|
230
|
+
|| !/^[a-f0-9]{64}$/u.test(ticket.digest) || !Number.isSafeInteger(ticket.now) || ticket.now < 1) {
|
|
231
|
+
throw new Error('dream store: invalid lottery ticket');
|
|
232
|
+
}
|
|
233
|
+
this.db.prepare(`INSERT INTO dream_tickets (subject_kind, subject_key, ticket_value, ticket_digest, created_at)
|
|
234
|
+
VALUES (?, ?, ?, ?, ?) ON CONFLICT(subject_kind, subject_key) DO NOTHING`)
|
|
235
|
+
.run(ticket.subjectKind, ticket.subjectKey, ticket.value, ticket.digest, ticket.now);
|
|
236
|
+
}
|
|
237
|
+
/** All unsolved lottery tickets for this lane, oldest first. */
|
|
238
|
+
listTickets() {
|
|
239
|
+
return this.db.prepare(`SELECT subject_kind, subject_key, ticket_value, ticket_digest FROM dream_tickets
|
|
240
|
+
WHERE subject_kind IN ('task-projection','goal','skill','recorded-idea') ORDER BY created_at, subject_key`)
|
|
241
|
+
.all()
|
|
242
|
+
.map(row => ({ subjectKind: row.subject_kind, subjectKey: row.subject_key, value: row.ticket_value, digest: row.ticket_digest }));
|
|
243
|
+
}
|
|
244
|
+
/** Record one recall hit; the unique key makes a replayed slot a no-op. */
|
|
245
|
+
recordRecall(input) {
|
|
246
|
+
assertMutable(this.closed);
|
|
247
|
+
const result = this.db.prepare(`INSERT OR IGNORE INTO dream_recalls
|
|
248
|
+
(lane, occurrence, draw_digest, draw_value, subject_kind, subject_key, ticket_value, distance, at)
|
|
249
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
250
|
+
.run(this.lane, input.occurrence, input.drawDigest, input.drawValue, input.subjectKind, input.subjectKey, input.ticketValue, input.distance, input.at);
|
|
251
|
+
return Number(result.changes) === 1;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Drop tickets held by recorded ideas that left the `recorded` state
|
|
255
|
+
* (validated/rejected/withdrawn): a resolved idea must stop being recalled.
|
|
256
|
+
*/
|
|
257
|
+
pruneResolvedIdeaTickets() {
|
|
258
|
+
assertMutable(this.closed);
|
|
259
|
+
return Number(this.db.prepare(`DELETE FROM dream_tickets
|
|
260
|
+
WHERE subject_kind='recorded-idea' AND NOT EXISTS (
|
|
261
|
+
SELECT 1 FROM dream_ideas WHERE dream_ideas.lane=? AND dream_ideas.state='recorded'
|
|
262
|
+
AND dream_ideas.id=dream_tickets.subject_key)`).run(this.lane).changes);
|
|
263
|
+
}
|
|
264
|
+
lastRecall() {
|
|
265
|
+
const row = this.db.prepare(`SELECT occurrence, subject_kind, subject_key, at FROM dream_recalls
|
|
266
|
+
WHERE lane=? ORDER BY seq DESC LIMIT 1`).get(this.lane);
|
|
267
|
+
return row === undefined ? undefined
|
|
268
|
+
: { occurrence: row.occurrence, subjectKind: row.subject_kind, subjectKey: row.subject_key, at: row.at };
|
|
269
|
+
}
|
|
270
|
+
recordIdea(idea) {
|
|
271
|
+
assertMutable(this.closed);
|
|
272
|
+
const refs = ownData(idea.evidenceRefs);
|
|
273
|
+
if (!Array.isArray(refs) || refs.length === 0 || refs.some(ref => typeof ref !== 'string' || !/^[a-f0-9]{64}$/u.test(ref))) {
|
|
274
|
+
throw new Error('dream store: evidence refs must be hex digests');
|
|
275
|
+
}
|
|
276
|
+
const value = ownData(idea);
|
|
277
|
+
if (Buffer.byteLength(value.summary) > 512 || Buffer.byteLength(value.detail) > 4_096) {
|
|
278
|
+
throw new Error('dream store: idea text exceeds bounds');
|
|
279
|
+
}
|
|
280
|
+
const summaryDigest = createHashSafe(`${value.summary}\0${value.dayKey}`);
|
|
281
|
+
this.db.prepare(`INSERT INTO dream_ideas (id, lane, run_id, created_at, day_key, state, summary, detail, evidence_refs, summary_digest)
|
|
282
|
+
VALUES (?, ?, ?, ?, ?, 'recorded', ?, ?, ?, ?)`)
|
|
283
|
+
.run(value.id, this.lane, value.runId, value.createdAt, value.dayKey, value.summary, value.detail, JSON.stringify(refs), summaryDigest);
|
|
284
|
+
this.db.prepare('UPDATE dream_runs SET idea_count = idea_count + 1 WHERE id=? AND lane=?').run(value.runId, this.lane);
|
|
285
|
+
return this.idea(value.id);
|
|
286
|
+
}
|
|
287
|
+
idea(id) {
|
|
288
|
+
const row = this.db.prepare('SELECT * FROM dream_ideas WHERE id=? AND lane=?').get(id, this.lane);
|
|
289
|
+
if (row === undefined)
|
|
290
|
+
return undefined;
|
|
291
|
+
return {
|
|
292
|
+
id: row.id, lane: row.lane, runId: row.run_id,
|
|
293
|
+
createdAt: row.created_at, dayKey: row.day_key,
|
|
294
|
+
state: row.state, summary: row.summary, detail: row.detail,
|
|
295
|
+
evidenceRefs: JSON.parse(row.evidence_refs),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
transitionIdea(id, state, actor, at, note) {
|
|
299
|
+
assertMutable(this.closed);
|
|
300
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
301
|
+
try {
|
|
302
|
+
const current = this.idea(id);
|
|
303
|
+
if (current === undefined)
|
|
304
|
+
throw new Error('dream store: idea not found');
|
|
305
|
+
const allowed = {
|
|
306
|
+
recorded: ['withdrawn', 'owner-acknowledged', 'validated', 'rejected'],
|
|
307
|
+
withdrawn: [],
|
|
308
|
+
'owner-acknowledged': ['validated', 'rejected'],
|
|
309
|
+
validated: [],
|
|
310
|
+
rejected: [],
|
|
311
|
+
};
|
|
312
|
+
if (!allowed[current.state].includes(state))
|
|
313
|
+
throw new Error(`dream store: transition ${current.state} -> ${state} is not allowed`);
|
|
314
|
+
this.db.prepare('UPDATE dream_ideas SET state=? WHERE id=? AND lane=?').run(state, id, this.lane);
|
|
315
|
+
this.db.prepare('INSERT INTO dream_idea_events (idea_id, state, actor, at, note) VALUES (?, ?, ?, ?, ?)')
|
|
316
|
+
.run(id, state, actor, at, note ?? null);
|
|
317
|
+
this.db.exec('COMMIT');
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
this.db.exec('ROLLBACK');
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
listIdeas(limit = 50) {
|
|
325
|
+
const rows = this.db.prepare('SELECT id FROM dream_ideas WHERE lane=? ORDER BY created_at DESC LIMIT ?').all(this.lane, limit);
|
|
326
|
+
return rows.map(row => this.idea(row.id));
|
|
327
|
+
}
|
|
328
|
+
counts() {
|
|
329
|
+
const runs = Number(this.db.prepare('SELECT COUNT(*) AS n FROM dream_runs WHERE lane=?').get(this.lane)['n']);
|
|
330
|
+
const ideas = Number(this.db.prepare('SELECT COUNT(*) AS n FROM dream_ideas WHERE lane=?').get(this.lane).n);
|
|
331
|
+
const recorded = Number(this.db.prepare(`SELECT COUNT(*) AS n FROM dream_ideas WHERE lane=? AND state='recorded'`).get(this.lane).n);
|
|
332
|
+
return { runs, ideas, recorded };
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Hard-delete expired ideas per retention policy; runs keep audit digests
|
|
336
|
+
* only. Events are removed first in the same transaction because
|
|
337
|
+
* `dream_idea_events.idea_id` is a foreign key without ON DELETE CASCADE:
|
|
338
|
+
* a single event row would otherwise abort the whole prune with
|
|
339
|
+
* FOREIGN KEY constraint failed and silently disable retention.
|
|
340
|
+
*/
|
|
341
|
+
prune(now, retentionDays) {
|
|
342
|
+
assertMutable(this.closed);
|
|
343
|
+
const cutoff = now - retentionDays * 86_400_000;
|
|
344
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
345
|
+
try {
|
|
346
|
+
this.db.prepare(`DELETE FROM dream_idea_events WHERE idea_id IN (
|
|
347
|
+
SELECT id FROM dream_ideas WHERE lane=? AND created_at < ?)`).run(this.lane, cutoff);
|
|
348
|
+
const removed = Number(this.db.prepare('DELETE FROM dream_ideas WHERE lane=? AND created_at < ?')
|
|
349
|
+
.run(this.lane, cutoff).changes);
|
|
350
|
+
this.db.exec('COMMIT');
|
|
351
|
+
return removed;
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
this.db.exec('ROLLBACK');
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
interrupt() {
|
|
359
|
+
assertMutable(this.closed);
|
|
360
|
+
this.db.prepare(`UPDATE dream_runs SET outcome='interrupted', ended_at=? WHERE lane=? AND outcome='running'`)
|
|
361
|
+
.run(Date.now(), this.lane);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function createHashSafe(value) {
|
|
365
|
+
return createHash('sha256').update(value, 'utf8').digest('hex');
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=store.js.map
|
package/lib/store.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/lib/trigger.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { DreamingActiveHours } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic dreaming trigger. Randomness is an auditable HMAC draw keyed
|
|
4
|
+
* by an owner seed and a stable occurrence identity, so the same inputs always
|
|
5
|
+
* produce the same decision in tests and after restart. The module never calls
|
|
6
|
+
* Math.random and never schedules anything itself.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MILLION = 1000000;
|
|
9
|
+
declare const MINUTES_PER_DAY = 1440;
|
|
10
|
+
export interface DrawInput {
|
|
11
|
+
seedDigest: string;
|
|
12
|
+
lane: string;
|
|
13
|
+
/** Stable monotonic occurrence number of the scheduler slot. */
|
|
14
|
+
occurrence: number;
|
|
15
|
+
/** Probability of dreaming, in parts per million (0..1_000_000). */
|
|
16
|
+
probabilityPpm: number;
|
|
17
|
+
}
|
|
18
|
+
export interface DrawResult {
|
|
19
|
+
drawDigest: string;
|
|
20
|
+
/** Uniform value in [0, 1_000_000) derived from the HMAC. */
|
|
21
|
+
value: number;
|
|
22
|
+
selected: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns the auditable draw for one slot. Determinism makes the decision
|
|
26
|
+
* replayable; the digest alone is persisted, never the seed.
|
|
27
|
+
*/
|
|
28
|
+
export declare function evaluateDraw(input: DrawInput): DrawResult;
|
|
29
|
+
/**
|
|
30
|
+
* Local-day key in UTC minutes. The scheduler uses this for per-day caps; a
|
|
31
|
+
* timezone-aware activeHours check happens separately.
|
|
32
|
+
*/
|
|
33
|
+
export declare function localDayKey(now: number, timezone: string): string;
|
|
34
|
+
export declare function isWithinActiveHours(now: number, activeHours: DreamingActiveHours | null): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Conservative idle gate based on the latest trusted activity timestamp. The
|
|
37
|
+
* caller supplies activity measured through Host projections (never model
|
|
38
|
+
* prose); a missing marker means "not idle" so the first run fails closed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isIdle(now: number, latestActivityAt: number | null, idleMinMs: number): boolean;
|
|
41
|
+
export type SkipReason = 'outside-active-hours' | 'not-idle' | 'daily-cap' | 'draw-not-selected';
|
|
42
|
+
export interface TriggerInput {
|
|
43
|
+
now: number;
|
|
44
|
+
occurrence: number;
|
|
45
|
+
lane: string;
|
|
46
|
+
seedDigest: string;
|
|
47
|
+
probabilityPpm: number;
|
|
48
|
+
activeHours: DreamingActiveHours | null;
|
|
49
|
+
latestActivityAt: number | null;
|
|
50
|
+
idleMinMs: number;
|
|
51
|
+
dreamsToday: number;
|
|
52
|
+
maxDreamsPerDay: number;
|
|
53
|
+
}
|
|
54
|
+
export type TriggerDecision = {
|
|
55
|
+
fire: true;
|
|
56
|
+
drawDigest: string;
|
|
57
|
+
drawValue: number;
|
|
58
|
+
dayKey: string;
|
|
59
|
+
} | {
|
|
60
|
+
fire: false;
|
|
61
|
+
reason: SkipReason;
|
|
62
|
+
dayKey: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Pure decision combining every gate in fail-closed order: active window,
|
|
66
|
+
* idle window, daily cap, deterministic draw. No side effects.
|
|
67
|
+
*/
|
|
68
|
+
export declare function decideTrigger(input: TriggerInput): TriggerDecision;
|
|
69
|
+
/** Current slot occurrence derived from the base interval since epoch. */
|
|
70
|
+
export declare function slotOccurrence(now: number, baseIntervalMs: number): number;
|
|
71
|
+
/**
|
|
72
|
+
* Per-subject lottery tickets ("无事找事"): every recallable thing — a task,
|
|
73
|
+
* a goal, a skill, or a previously recorded idea — holds one ticket with a
|
|
74
|
+
* stable ticket value. Each wake slot rolls one bounded random number from the
|
|
75
|
+
* HMAC seed; a subject whose ticket value lands inside the rolling window is
|
|
76
|
+
* recalled for focused thought. This is code-driven (the draw and match run
|
|
77
|
+
* without any model call) — the model is invoked only after a hit.
|
|
78
|
+
*
|
|
79
|
+
* Values live in a widened range rather than [0,1) so a configurable window
|
|
80
|
+
* can be made wider without touching the deterministic draw.
|
|
81
|
+
*/
|
|
82
|
+
export declare const TICKET_MODULUS = 1000000;
|
|
83
|
+
/** Default rolling window around the draw value in each direction. */
|
|
84
|
+
export declare const DEFAULT_TICKET_WINDOW = 5000;
|
|
85
|
+
export type LotterySubjectKind = 'task-projection' | 'goal' | 'skill' | 'recorded-idea';
|
|
86
|
+
export interface LotterySubject {
|
|
87
|
+
kind: LotterySubjectKind;
|
|
88
|
+
/** Stable subject key (inbox id, goal id, skill name, or idea id). */
|
|
89
|
+
key: string;
|
|
90
|
+
}
|
|
91
|
+
export interface Ticket {
|
|
92
|
+
subject: LotterySubject;
|
|
93
|
+
/** Stable value in [0, TICKET_MODULUS) derived from the ticket seed. */
|
|
94
|
+
value: number;
|
|
95
|
+
digest: string;
|
|
96
|
+
}
|
|
97
|
+
/** One subject's stable ticket. The seed is only used as HMAC material. */
|
|
98
|
+
export declare function assignTicket(input: {
|
|
99
|
+
seedDigest: string;
|
|
100
|
+
lane: string;
|
|
101
|
+
subject: LotterySubject;
|
|
102
|
+
}): Ticket;
|
|
103
|
+
export interface LotteryDrawInput {
|
|
104
|
+
seedDigest: string;
|
|
105
|
+
lane: string;
|
|
106
|
+
occurrence: number;
|
|
107
|
+
}
|
|
108
|
+
export interface LotteryDraw {
|
|
109
|
+
drawDigest: string;
|
|
110
|
+
/** Rolled value in [0, TICKET_MODULUS). */
|
|
111
|
+
value: number;
|
|
112
|
+
}
|
|
113
|
+
/** The wake's single rolling number, derived deterministically from the seed. */
|
|
114
|
+
export declare function rollLottery(input: LotteryDrawInput): LotteryDraw;
|
|
115
|
+
export interface LotteryMatchInput {
|
|
116
|
+
draw: LotteryDraw;
|
|
117
|
+
tickets: readonly Ticket[];
|
|
118
|
+
/** Half-width of the circular window; default keeps roughly 1% of tickets. */
|
|
119
|
+
window?: number;
|
|
120
|
+
}
|
|
121
|
+
export interface LotteryHit {
|
|
122
|
+
subject: LotterySubject;
|
|
123
|
+
ticketDigest: string;
|
|
124
|
+
ticketValue: number;
|
|
125
|
+
distance: number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns the tickets whose value is within `window` of the draw on the
|
|
129
|
+
* circular modulus. Deterministic and side-effect free; an empty result means
|
|
130
|
+
* nothing is recalled this slot, which is the normal "no thought" outcome.
|
|
131
|
+
*/
|
|
132
|
+
export declare function matchTickets(input: LotteryMatchInput): readonly LotteryHit[];
|
|
133
|
+
export { MINUTES_PER_DAY };
|
|
134
|
+
//# sourceMappingURL=trigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../src/trigger.ts"],"names":[],"mappings":"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"}
|