@stratusagent/gateway 0.13.6 → 0.14.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/dist/events.js ADDED
@@ -0,0 +1,467 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { MAX_IDEMPOTENCY_KEY_LENGTH } from '@stratusagent/core';
3
+ import { openFleetDatabase, SCHEDULE_SESSION_ID_PREFIX } from "./schedules.js";
4
+ // Event triggers' durable half (card 590, agent-plans
5
+ // roadmap/590-event-triggers.md): deliveries, dedup tombstones and firings,
6
+ // in fleet.db next to the schedules a trigger is. The listener decides what a
7
+ // request is and which triggers it matches; the dispatch side decides when a
8
+ // firing goes out. This file owns only what must survive a crash between
9
+ // those steps, and the keys that make a firing run at most once.
10
+ /** Accepted ids are remembered this long: longer than GitHub's 3-day manual redelivery. */
11
+ export const EVENT_DEDUP_WINDOW_MS = 7 * 24 * 60 * 60 * 1_000;
12
+ /** Delivery rows listed at most: the newest this many, within the dedup window. */
13
+ export const EVENT_DELIVERY_ROW_CAP = 10_000;
14
+ /** A firing's rendered event block, and so the event text a turn sees, is at most this many bytes. */
15
+ export const EVENT_BLOCK_MAX_BYTES = 16 * 1_024;
16
+ const OPEN_STATUSES = ['pending', 'held', 'rate_limited', 'firing'];
17
+ const OPEN_SQL = OPEN_STATUSES.map((status) => `'${status}'`).join(', ');
18
+ const sha256 = (text) => createHash('sha256').update(text).digest('hex');
19
+ /**
20
+ * What sessions, holds and rate limits group by: source + subject when the
21
+ * event has one, so every Codex round on one PR is one conversation, and
22
+ * source + id when it doesn't, so subjectless events never share a bucket.
23
+ * JSON, so no subject can be spelled to collide with an id key.
24
+ */
25
+ export const eventCoalescingKey = (sourceId, event) => {
26
+ const subject = event.attributes.subject;
27
+ return subject !== undefined && subject.length > 0
28
+ ? JSON.stringify([sourceId, 'subject', subject])
29
+ : JSON.stringify([sourceId, 'id', event.attributes.id ?? '']);
30
+ };
31
+ /**
32
+ * A trigger's session for one coalescing key. In the reserved `schedule:`
33
+ * namespace, which the gateway refuses to dispatch into from outside, so the
34
+ * only turns on it are the trigger's own firings.
35
+ */
36
+ export const eventSessionId = (triggerId, coalescingKey) => `${SCHEDULE_SESSION_ID_PREFIX}${triggerId}:event-${sha256(coalescingKey).slice(0, 32)}`;
37
+ /**
38
+ * The gateway idempotency key for one trigger's firing on one event: a
39
+ * repeat never starts a second turn, and a turn whose process died continues
40
+ * once. Hashed when over the gateway's bound, as channel-slack does, because
41
+ * a sender picks the event id's length.
42
+ */
43
+ export const eventDispatchKey = (triggerId, sourceId, eventId) => {
44
+ const key = `event:${triggerId}:${sourceId}:${eventId}`;
45
+ return key.length <= MAX_IDEMPOTENCY_KEY_LENGTH ? key : `event-sha256:${sha256(key)}`;
46
+ };
47
+ const ATTRIBUTE_ORDER = ['id', 'source', 'type', 'subject', 'time'];
48
+ /** The longest prefix of `text` that fits in `bytes` of UTF-8, never splitting a character. */
49
+ const utf8Prefix = (text, bytes) => {
50
+ const { read } = new TextEncoder().encodeInto(text, new Uint8Array(Math.max(0, bytes)));
51
+ return text.slice(0, read);
52
+ };
53
+ /**
54
+ * What a firing's turn sees after the trigger's prompt. Rendered when the
55
+ * delivery is matched and stored with the firing, so a firing that goes out
56
+ * hours later needs neither the request body nor a second match. Attributes
57
+ * and data are JSON, so a newline in a sender's subject or comment can't pass
58
+ * for a line of the block. Capped at `EVENT_BLOCK_MAX_BYTES` with a note: the
59
+ * agent fetches the rest with its own tools (`gh` for a PR).
60
+ */
61
+ export const renderEventBlock = (sourceId, event) => {
62
+ const attributes = {};
63
+ for (const name of ATTRIBUTE_ORDER) {
64
+ const value = event.attributes[name];
65
+ if (value !== undefined) {
66
+ attributes[name] = value;
67
+ }
68
+ }
69
+ for (const name of Object.keys(event.attributes).sort()) {
70
+ if (name !== 'specversion' && !ATTRIBUTE_ORDER.includes(name)) {
71
+ attributes[name] = event.attributes[name];
72
+ }
73
+ }
74
+ const full = [
75
+ `Event from source "${sourceId}":`,
76
+ JSON.stringify(attributes),
77
+ 'data:',
78
+ ...(event.data !== undefined ? [JSON.stringify(event.data)] : []),
79
+ ].join('\n');
80
+ const size = Buffer.byteLength(full);
81
+ if (size <= EVENT_BLOCK_MAX_BYTES) {
82
+ return full;
83
+ }
84
+ const note = `\n[cut at ${Math.round(EVENT_BLOCK_MAX_BYTES / 1_024)} KB of ${Math.round(size / 1_024)} KB; fetch the rest with your own tools]`;
85
+ return utf8Prefix(full, EVENT_BLOCK_MAX_BYTES - Buffer.byteLength(note)) + note;
86
+ };
87
+ const deliveryFrom = (row) => ({
88
+ seq: row.seq,
89
+ source: row.source,
90
+ eventId: row.event_id,
91
+ type: row.type,
92
+ ...(row.subject !== '' ? { subject: row.subject } : {}),
93
+ status: row.status,
94
+ ...(row.reason !== '' ? { reason: row.reason } : {}),
95
+ receivedAt: row.received_at,
96
+ });
97
+ const firingFrom = (row) => ({
98
+ id: row.id,
99
+ deliverySeq: row.delivery_seq,
100
+ triggerId: row.trigger_id,
101
+ agentId: row.agent_id,
102
+ source: row.source,
103
+ eventId: row.event_id,
104
+ coalescingKey: row.coalescing_key,
105
+ sessionId: row.session_id,
106
+ dispatchKey: row.dispatch_key,
107
+ ...(row.batch_key !== '' ? { batchKey: row.batch_key } : {}),
108
+ status: row.status,
109
+ ...(row.started_at !== '' ? { startedAt: row.started_at } : {}),
110
+ ...(row.block !== null ? { block: row.block } : {}),
111
+ ...(row.reason !== '' ? { reason: row.reason } : {}),
112
+ createdAt: row.created_at,
113
+ updatedAt: row.updated_at,
114
+ });
115
+ const minus = (iso, ms) => new Date(Date.parse(iso) - ms).toISOString();
116
+ /**
117
+ * Deliveries, dedup tombstones and firings, durable in `fleet.db`. Its own
118
+ * connection, like the schedule store's. `stratus events` reads through the
119
+ * running daemon instead of this file, because the requests refused before
120
+ * a row are counted only in the listener.
121
+ *
122
+ * Every method that changes more than one row does it in one transaction,
123
+ * and every one that moves a firing between statuses is all-or-nothing and
124
+ * conditional on the statuses it expects: a delivery and its firings are
125
+ * committed together before the sender hears 2xx, a batch is claimed whole
126
+ * before its dispatch, and a refused transition changes nothing.
127
+ */
128
+ export class SqliteEventStore {
129
+ db;
130
+ constructor(filePath, options = {}) {
131
+ this.db = openFleetDatabase(filePath, options, `
132
+ CREATE TABLE IF NOT EXISTS event_deliveries (
133
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
134
+ source TEXT NOT NULL,
135
+ event_id TEXT NOT NULL,
136
+ type TEXT NOT NULL,
137
+ subject TEXT NOT NULL,
138
+ status TEXT NOT NULL,
139
+ reason TEXT NOT NULL,
140
+ received_at TEXT NOT NULL
141
+ );
142
+ CREATE INDEX IF NOT EXISTS event_deliveries_received ON event_deliveries (received_at);
143
+ CREATE TABLE IF NOT EXISTS event_seen (
144
+ source TEXT NOT NULL,
145
+ id_hash TEXT NOT NULL,
146
+ seen_at TEXT NOT NULL,
147
+ PRIMARY KEY (source, id_hash)
148
+ ) WITHOUT ROWID;
149
+ CREATE TABLE IF NOT EXISTS event_firings (
150
+ id TEXT PRIMARY KEY,
151
+ delivery_seq INTEGER NOT NULL,
152
+ trigger_id TEXT NOT NULL,
153
+ agent_id TEXT NOT NULL,
154
+ source TEXT NOT NULL,
155
+ event_id TEXT NOT NULL,
156
+ coalescing_key TEXT NOT NULL,
157
+ session_id TEXT NOT NULL,
158
+ dispatch_key TEXT NOT NULL,
159
+ batch_key TEXT NOT NULL,
160
+ status TEXT NOT NULL,
161
+ started_at TEXT NOT NULL DEFAULT '',
162
+ block TEXT,
163
+ reason TEXT NOT NULL,
164
+ created_at TEXT NOT NULL,
165
+ updated_at TEXT NOT NULL
166
+ );
167
+ CREATE INDEX IF NOT EXISTS event_firings_status ON event_firings (status, created_at);
168
+ CREATE INDEX IF NOT EXISTS event_firings_trigger ON event_firings (trigger_id, status, updated_at);
169
+ CREATE INDEX IF NOT EXISTS event_firings_batch ON event_firings (batch_key);
170
+ `);
171
+ // A table made before `started_at` existed (an unreleased build of
172
+ // this feature) gains it; rows claimed then count from their update.
173
+ const columns = this.db.prepare('PRAGMA table_info(event_firings)').all();
174
+ if (!columns.some((column) => column.name === 'started_at')) {
175
+ this.db.exec("ALTER TABLE event_firings ADD COLUMN started_at TEXT NOT NULL DEFAULT ''");
176
+ }
177
+ }
178
+ transaction(work) {
179
+ // IMMEDIATE takes the write lock up front, so the dedup check and the
180
+ // insert it guards can't interleave with another writer's.
181
+ this.db.exec('BEGIN IMMEDIATE');
182
+ try {
183
+ const result = work();
184
+ this.db.exec('COMMIT');
185
+ return result;
186
+ }
187
+ catch (error) {
188
+ this.db.exec('ROLLBACK');
189
+ throw error;
190
+ }
191
+ }
192
+ insertDelivery(delivery, status, reason = '') {
193
+ const result = this.db
194
+ .prepare('INSERT INTO event_deliveries (source, event_id, type, subject, status, reason, received_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
195
+ .run(delivery.source, delivery.eventId, delivery.type ?? '', delivery.subject ?? '', status, reason, delivery.receivedAt);
196
+ return Number(result.lastInsertRowid);
197
+ }
198
+ /**
199
+ * Record a delivery whose signature checked out and whose body parsed:
200
+ * `duplicate` when its (source, id) was accepted within the dedup window,
201
+ * else `matched` with one `pending` firing per match, or `unmatched`. One
202
+ * write, made before the sender hears 2xx. Accepted deliveries leave a
203
+ * tombstone that outlives the row cap, so dedup never depends on it.
204
+ */
205
+ accept(delivery) {
206
+ return this.transaction(() => {
207
+ // Insert, or revive a tombstone older than the window; zero changes
208
+ // means a live tombstone, so the id was seen.
209
+ const fresh = this.db
210
+ .prepare(`
211
+ INSERT INTO event_seen (source, id_hash, seen_at) VALUES (?, ?, ?)
212
+ ON CONFLICT (source, id_hash) DO UPDATE SET seen_at = excluded.seen_at WHERE seen_at < ?
213
+ `)
214
+ .run(delivery.source, sha256(delivery.eventId), delivery.receivedAt, minus(delivery.receivedAt, EVENT_DEDUP_WINDOW_MS))
215
+ .changes > 0;
216
+ if (!fresh) {
217
+ return { status: 'duplicate', seq: this.insertDelivery(delivery, 'duplicate'), firings: [] };
218
+ }
219
+ const status = delivery.firings.length > 0 ? 'matched' : 'unmatched';
220
+ const seq = this.insertDelivery(delivery, status);
221
+ const insert = this.db.prepare(`
222
+ INSERT INTO event_firings (id, delivery_seq, trigger_id, agent_id, source, event_id, coalescing_key,
223
+ session_id, dispatch_key, batch_key, status, block, reason, created_at, updated_at)
224
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, '', 'pending', ?, '', ?, ?)
225
+ `);
226
+ const firings = delivery.firings.map((firing) => {
227
+ const id = randomUUID();
228
+ insert.run(id, seq, firing.triggerId, firing.agentId, delivery.source, delivery.eventId, firing.coalescingKey, firing.sessionId, firing.dispatchKey, firing.block, delivery.receivedAt, delivery.receivedAt);
229
+ return {
230
+ id,
231
+ deliverySeq: seq,
232
+ triggerId: firing.triggerId,
233
+ agentId: firing.agentId,
234
+ source: delivery.source,
235
+ eventId: delivery.eventId,
236
+ coalescingKey: firing.coalescingKey,
237
+ sessionId: firing.sessionId,
238
+ dispatchKey: firing.dispatchKey,
239
+ status: 'pending',
240
+ block: firing.block,
241
+ createdAt: delivery.receivedAt,
242
+ updatedAt: delivery.receivedAt,
243
+ };
244
+ });
245
+ return { status, seq, firings };
246
+ });
247
+ }
248
+ /**
249
+ * Record a signed delivery the listener refused (a form-encoded body, a
250
+ * malformed event). No tombstone: the sender can fix it and retry with the
251
+ * same id. Unsigned requests never reach here; they leave no row at all.
252
+ */
253
+ refuse(delivery) {
254
+ const seq = this.insertDelivery(delivery, 'refused', delivery.reason);
255
+ return deliveryFrom(this.db.prepare('SELECT * FROM event_deliveries WHERE seq = ?').get(seq));
256
+ }
257
+ /** Delivery rows, newest first. */
258
+ deliveries(options = {}) {
259
+ const limit = options.limit ?? 100;
260
+ const rows = (options.source !== undefined
261
+ ? this.db.prepare('SELECT * FROM event_deliveries WHERE source = ? ORDER BY seq DESC LIMIT ?').all(options.source, limit)
262
+ : this.db.prepare('SELECT * FROM event_deliveries ORDER BY seq DESC LIMIT ?').all(limit));
263
+ return rows.map(deliveryFrom);
264
+ }
265
+ /**
266
+ * Per source with any row: its last delivery, its last refused one, and
267
+ * counts since `sinceIso` (`stratus events`, `/health`). Grouped in the
268
+ * database, so the cost doesn't grow with the 7 days of rows kept.
269
+ */
270
+ activity(sinceIso) {
271
+ const result = {};
272
+ const entry = (source) => (result[source] ??= { deliveries: {}, firings: {} });
273
+ const latest = this.db.prepare(`
274
+ SELECT d.* FROM event_deliveries d
275
+ JOIN (SELECT source, MAX(seq) AS seq FROM event_deliveries GROUP BY source) newest ON newest.seq = d.seq
276
+ `).all();
277
+ for (const row of latest) {
278
+ entry(row.source).lastDelivery = {
279
+ at: row.received_at,
280
+ type: row.type,
281
+ ...(row.subject !== '' ? { subject: row.subject } : {}),
282
+ status: row.status,
283
+ };
284
+ }
285
+ const refused = this.db.prepare(`
286
+ SELECT d.* FROM event_deliveries d
287
+ JOIN (SELECT source, MAX(seq) AS seq FROM event_deliveries WHERE status = 'refused' GROUP BY source) newest ON newest.seq = d.seq
288
+ `).all();
289
+ for (const row of refused) {
290
+ entry(row.source).lastRefused = { at: row.received_at, reason: row.reason };
291
+ }
292
+ const deliveries = this.db.prepare(`
293
+ SELECT source, status, COUNT(*) AS n FROM event_deliveries WHERE received_at >= ? GROUP BY source, status
294
+ `).all(sinceIso);
295
+ for (const row of deliveries) {
296
+ entry(row.source).deliveries[row.status] = Number(row.n);
297
+ }
298
+ const firings = this.db.prepare(`
299
+ SELECT source, status, COUNT(*) AS n FROM event_firings
300
+ WHERE status IN (${OPEN_SQL}) OR updated_at >= ? GROUP BY source, status
301
+ `).all(sinceIso);
302
+ for (const row of firings) {
303
+ entry(row.source).firings[row.status] = Number(row.n);
304
+ }
305
+ return result;
306
+ }
307
+ firing(id) {
308
+ const row = this.db.prepare('SELECT * FROM event_firings WHERE id = ?').get(id);
309
+ return row ? firingFrom(row) : undefined;
310
+ }
311
+ /**
312
+ * Every open firing (`pending`, `held`, `rate_limited`, `firing`), oldest
313
+ * first, with its block and batch key: what the startup sweep recovers and
314
+ * what a hold releases. A `firing` row is a claim whose dispatch may or may
315
+ * not have reached the gateway; its batch goes out again as stored, and the
316
+ * idempotency key decides whether that starts or continues a turn.
317
+ */
318
+ open() {
319
+ return this.db
320
+ .prepare(`SELECT * FROM event_firings WHERE status IN (${OPEN_SQL}) ORDER BY created_at ASC, delivery_seq ASC, rowid ASC`)
321
+ .all().map(firingFrom);
322
+ }
323
+ /**
324
+ * Run `update` on exactly `ids`, all or nothing: false, with nothing
325
+ * changed, unless `precheck` (inside the same transaction) passes and
326
+ * every row matched the update's own conditions.
327
+ */
328
+ allOrNothing(ids, update, precheck = () => true) {
329
+ if (ids.length === 0 || new Set(ids).size !== ids.length) {
330
+ return false;
331
+ }
332
+ try {
333
+ return this.transaction(() => {
334
+ if (!precheck()) {
335
+ throw new RefusedTransition();
336
+ }
337
+ for (const id of ids) {
338
+ if (update(id) === 0) {
339
+ throw new RefusedTransition();
340
+ }
341
+ }
342
+ return true;
343
+ });
344
+ }
345
+ catch (error) {
346
+ if (error instanceof RefusedTransition) {
347
+ return false;
348
+ }
349
+ throw error;
350
+ }
351
+ }
352
+ /**
353
+ * Claim firings to go out as one turn under `batchKey`, in one write
354
+ * before the dispatch. Either every row is free and they become a new
355
+ * batch, or `ids` is exactly an existing batch under exactly its key,
356
+ * which is how recovery resends one. A batch is fixed once claimed: no
357
+ * row joins it later, and it never changes key. False, with nothing
358
+ * changed, for anything else, or if any row is finished.
359
+ *
360
+ * With `triggerId`, also false unless that schedule row still exists, in
361
+ * the same transaction: `stratus schedules cancel` deletes from another
362
+ * process, and a trigger cancelled after the scan read it must not fire,
363
+ * as a timed schedule's conditional slot claim already guarantees.
364
+ */
365
+ claim(ids, batchKey, nowIso, options = {}) {
366
+ const members = this.db.prepare('SELECT id FROM event_firings WHERE batch_key = ?');
367
+ const update = this.db.prepare(`
368
+ UPDATE event_firings
369
+ SET status = 'firing', batch_key = ?, updated_at = ?,
370
+ started_at = CASE WHEN started_at = '' THEN ? ELSE started_at END
371
+ WHERE id = ? AND status IN (${OPEN_SQL}) AND (batch_key = '' OR batch_key = ?)
372
+ `);
373
+ const wanted = new Set(ids);
374
+ return this.allOrNothing(ids, (id) => Number(update.run(batchKey, nowIso, nowIso, id, batchKey).changes), () => {
375
+ if (options.triggerId !== undefined
376
+ && this.db.prepare('SELECT 1 FROM schedules WHERE id = ?').get(options.triggerId) === undefined) {
377
+ return false;
378
+ }
379
+ const existing = members.all(batchKey).map((row) => row.id);
380
+ return existing.length === 0 || (existing.length === wanted.size && existing.every((id) => wanted.has(id)));
381
+ });
382
+ }
383
+ /**
384
+ * Hold open firings for quiet hours or paused intake (`held`) or over the
385
+ * trigger's hourly limit (`rate_limited`). The block and any batch key
386
+ * stay, so a held batch still goes out only as itself.
387
+ */
388
+ hold(ids, status, nowIso) {
389
+ const update = this.db.prepare(`UPDATE event_firings SET status = ?, updated_at = ? WHERE id = ? AND status IN (${OPEN_SQL})`);
390
+ return this.allOrNothing(ids, (id) => Number(update.run(status, nowIso, id).changes));
391
+ }
392
+ /**
393
+ * Finish claimed firings after their dispatch, clearing the block in the
394
+ * same write: from here the payload lives only in the session it fired.
395
+ * Only a `firing` row can finish, so nothing is marked fired that wasn't
396
+ * claimed first.
397
+ */
398
+ finish(ids, outcome, nowIso) {
399
+ const reason = outcome.status === 'failed' ? outcome.reason : '';
400
+ const update = this.db.prepare(`
401
+ UPDATE event_firings SET status = ?, reason = ?, block = NULL, updated_at = ?
402
+ WHERE id = ? AND status = 'firing'
403
+ `);
404
+ return this.allOrNothing(ids, (id) => Number(update.run(outcome.status, reason, nowIso, id).changes));
405
+ }
406
+ /**
407
+ * Retire open firings that will never go out: `superseded` by newer ones
408
+ * for the same key, or `expired` (held over 24 hours, or their trigger was
409
+ * cancelled). Clears their blocks. Returns how many were retired; a
410
+ * finished firing is left as it is.
411
+ */
412
+ retire(ids, status, reason, nowIso) {
413
+ const update = this.db.prepare(`
414
+ UPDATE event_firings SET status = ?, reason = ?, block = NULL, updated_at = ?
415
+ WHERE id = ? AND status IN (${OPEN_SQL})
416
+ `);
417
+ return this.transaction(() => ids.reduce((count, id) => count + Number(update.run(status, reason, nowIso, id).changes), 0));
418
+ }
419
+ /**
420
+ * Turns a trigger has started since `sinceIso`, for its hourly limit,
421
+ * counted from when each batch was first claimed (a two-hour turn doesn't
422
+ * hold the hour for two more): a batch running now, one that finished,
423
+ * and one that failed all count,
424
+ * since each was a turn started unattended. A failing provider would
425
+ * otherwise let every new event start another. A batch is one turn, so
426
+ * this counts batch keys, not rows; `exceptBatchKey` leaves out a batch
427
+ * being re-sent after a restart, which must not count against itself.
428
+ */
429
+ turnsStartedSince(triggerId, sinceIso, exceptBatchKey = '') {
430
+ const row = this.db
431
+ .prepare(`
432
+ SELECT COUNT(DISTINCT batch_key) AS turns FROM event_firings
433
+ WHERE trigger_id = ? AND status IN ('firing', 'fired', 'failed')
434
+ AND (CASE WHEN started_at = '' THEN updated_at ELSE started_at END) >= ? AND batch_key != ?
435
+ `)
436
+ .get(triggerId, sinceIso, exceptBatchKey);
437
+ return row.turns;
438
+ }
439
+ /**
440
+ * Drop what the dedup window and the row cap no longer cover: tombstones
441
+ * and delivery rows past 7 days, delivery rows past the newest 10,000, and
442
+ * finished firings past 7 days. An open firing is never pruned: it holds
443
+ * the only copy of an event that hasn't gone out.
444
+ */
445
+ prune(nowIso) {
446
+ const cutoff = minus(nowIso, EVENT_DEDUP_WINDOW_MS);
447
+ this.transaction(() => {
448
+ this.db.prepare('DELETE FROM event_seen WHERE seen_at < ?').run(cutoff);
449
+ this.db.prepare('DELETE FROM event_deliveries WHERE received_at < ?').run(cutoff);
450
+ this.db
451
+ .prepare('DELETE FROM event_deliveries WHERE seq <= (SELECT seq FROM event_deliveries ORDER BY seq DESC LIMIT 1 OFFSET ?)')
452
+ .run(EVENT_DELIVERY_ROW_CAP);
453
+ this.db.prepare(`DELETE FROM event_firings WHERE status NOT IN (${OPEN_SQL}) AND updated_at < ?`).run(cutoff);
454
+ });
455
+ }
456
+ close() {
457
+ this.db.close();
458
+ }
459
+ }
460
+ /** Unwinds an all-or-nothing transition's transaction; never escapes the store. */
461
+ class RefusedTransition extends Error {
462
+ constructor() {
463
+ super('refused transition');
464
+ this.name = 'RefusedTransition';
465
+ }
466
+ }
467
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import { EventBus, type AgentDefinition, type CredentialScope, type AlwaysMeans, type ApprovalDelegationOrigin, type ApprovalScheduleDestination, type ApprovalAnswer, type ApprovalOutcome, type ApprovalPolicy, type ImageAttachment, type ObserveEntry, type JsonObject, type Session, type ToolRisk, type TrustLevel } from '@stratusagent/core';
2
- import { type ConversationReader, type ParsedSoul, type ScheduleRecord } from '@stratusagent/agents';
2
+ import { type ConversationReader, type ConversationCreator, type ParsedSoul, type ScheduleRecord } from '@stratusagent/agents';
3
+ import { type EventIntakeRequest, type EventIntakeResponse, type EventSourceConfig, type EventsSummary } from './event-intake.ts';
4
+ import { type EventDeliveryRow } from './events.ts';
3
5
  import { type SchedulerLimits } from './schedules.ts';
4
6
  import { ShardedSessionStore } from './sessions.ts';
5
7
  export { claimHome, homeLockPath, HomeClaimedError, type HomeClaim, } from './lock.ts';
6
8
  export { SqliteSessionStore, ShardedSessionStore, FleetSessionIndex, SessionIdTakenError, tightenSqliteFile, type SqliteSessionStoreOptions, type ShardedSessionStoreOptions, type SessionIndexRow, type SessionReconcileReport, } from './sessions.ts';
7
9
  export { SqliteScheduleStore, createSchedulerRuntime, isScheduleSessionId, SCHEDULE_SESSION_ID_PREFIX, type ScheduleStoreLike, type SchedulerLimits, type SchedulerRuntime, type SchedulerRuntimeOptions, } from './schedules.ts';
10
+ export { SqliteEventStore, EVENT_BLOCK_MAX_BYTES, EVENT_DEDUP_WINDOW_MS, EVENT_DELIVERY_ROW_CAP, eventCoalescingKey, eventDispatchKey, eventSessionId, renderEventBlock, type AcceptedEventDelivery, type EventDeliveryRow, type EventDeliveryStatus, type EventFiringRow, type EventFiringStatus, type EventSourceActivity, type NewEventFiring, type RefusedEventDelivery, } from './events.ts';
11
+ export { createEventIntake, mapGithubDelivery, parseStandardWebhookEvent, verifyGithubSignature, verifyStandardWebhookSignature, DEFAULT_EVENT_MAX_BODY_BYTES, EVENT_SOURCE_REQUESTS_PER_MINUTE, type EventIntake, type EventIntakeCounters, type EventIntakeOptions, type EventIntakeRequest, type EventIntakeResponse, type EventSourceConfig, type EventSourceCounters, type EventSourceSummary, type EventsSummary, type EventTrigger, } from './event-intake.ts';
8
12
  import { type OptionalModuleHost } from '@stratusagent/plugins';
9
13
  import { type PluginsConfig, type RuntimeSelection, type StateEnvironment } from '@stratusagent/state';
10
14
  /**
@@ -20,8 +24,9 @@ export { applySoulPins, type SoulPinContext } from '@stratusagent/state';
20
24
  /**
21
25
  * A channel adapter as the gateway sees it: started after the roster
22
26
  * loads, stopped before the store drains. Structurally identical to
23
- * @stratusagent/channels' ChannelAdapter — kept structural here so the
24
- * gateway does not depend on the channels package.
27
+ * `ChannelAdapter` in @stratusagent/core, declared here because it
28
+ * predates the channel contract moving into core, when the gateway could
29
+ * not import it without depending on the channels package.
25
30
  */
26
31
  export interface GatewayChannelAdapter {
27
32
  name: string;
@@ -50,9 +55,8 @@ export interface GatewayChannelAdapter {
50
55
  */
51
56
  servesWhileHeld?: boolean;
52
57
  /**
53
- * The addressable outbound seam, mirroring
54
- * `@stratusagent/channels`' `ChannelAdapter.resolveOutbound` structurally
55
- * for the same reason the rest of this interface does. Optional: an
58
+ * The addressable outbound seam, mirroring `ChannelAdapter.resolveOutbound`
59
+ * structurally for the same reason the rest of this interface does. Optional: an
56
60
  * adapter with no concept of a destination simply lacks it, and the
57
61
  * gateway treats absence as "this channel cannot be spoken through".
58
62
  * Implementations MUST reject a destination they could not deliver to,
@@ -71,7 +75,7 @@ export interface GatewayChannelAdapter {
71
75
  }>;
72
76
  /**
73
77
  * The read side of an addressable conversation, mirroring
74
- * `@stratusagent/channels`' `ChannelAdapter.readConversation`. Optional:
78
+ * `ChannelAdapter.readConversation`. Optional:
75
79
  * an adapter without it cannot be read through `message.read`.
76
80
  * Implementations MUST reject a conversation their app may not read,
77
81
  * with a sentence for the agent.
@@ -83,14 +87,25 @@ export interface GatewayChannelAdapter {
83
87
  after?: string;
84
88
  before?: string;
85
89
  limit: number;
90
+ sessionId?: string;
86
91
  }): ReturnType<ConversationReader>;
87
- /** See `@stratusagent/channels`' `ChannelAdapter.accountFor`. */
92
+ /**
93
+ * Creates a conversation with the agent's own app, mirroring
94
+ * `ChannelAdapter.createConversation`. Optional: an adapter without it
95
+ * cannot serve `channel.create`. Implementations MUST reject what they
96
+ * can tell is invalid before creating anything, and MUST resolve once
97
+ * the conversation exists, reporting what did not happen after it.
98
+ */
99
+ createConversation?(request: Omit<Parameters<ConversationCreator>[0], 'channel'>): ReturnType<ConversationCreator>;
100
+ /** See `ChannelAdapter.accountFor`. */
88
101
  accountFor?(agentId: string): string | undefined;
89
- /** See `@stratusagent/channels`' `ChannelAdapter.conversationKindFor`. */
102
+ /** See `ChannelAdapter.conversationKindFor`. */
90
103
  conversationKindFor?(agentId: string, conversation: string): Promise<'channel' | 'direct' | undefined>;
104
+ /** See `ChannelAdapter.sessionConversation`. */
105
+ sessionConversation?(agentId: string, sessionId: string): string | undefined;
91
106
  /**
92
107
  * Shows a person a form for a credential an agent asked for, mirroring
93
- * `@stratusagent/channels`' `ChannelAdapter.requestCredential`. Optional:
108
+ * `ChannelAdapter.requestCredential`. Optional:
94
109
  * a channel without it cannot take credentials, and a request made in
95
110
  * its conversations is refused instead of announced. Implementations
96
111
  * MUST reject when the form reached nobody who can answer it, with a
@@ -104,10 +119,13 @@ export interface GatewayChannelAdapter {
104
119
  scope: CredentialScope;
105
120
  reason?: string;
106
121
  replace?: boolean;
122
+ env?: string;
107
123
  metadata: JsonObject;
108
124
  }): Promise<void>;
109
- /** See `@stratusagent/channels`' `ChannelAdapter.credentialReplaceForms`. */
125
+ /** See `ChannelAdapter.credentialReplaceForms`. */
110
126
  credentialReplaceForms?: boolean;
127
+ /** See `ChannelAdapter.credentialEnvForms`. */
128
+ credentialEnvForms?: boolean;
111
129
  /**
112
130
  * Issues a one-time link to a form for a credential request, for the
113
131
  * agent to pass on: what a request falls back to when its conversation
@@ -127,6 +145,8 @@ export interface GatewayChannelAdapter {
127
145
  name: string;
128
146
  scope: CredentialScope;
129
147
  reason?: string;
148
+ /** The variable the agent's shell will read the key from; the page must say so. */
149
+ env?: string;
130
150
  }): Promise<{
131
151
  url: string;
132
152
  expiresAt: string;
@@ -136,9 +156,8 @@ export interface GatewayChannelAdapter {
136
156
  }
137
157
  /**
138
158
  * Where a session came from, as the gateway reports it. Structural for the
139
- * same reason as the adapter above — @stratusagent/channels declares the
140
- * matching shape for adapters to consume, and neither package imports the
141
- * other to agree on it.
159
+ * same reason as the adapter above: core's channel contract declares the
160
+ * matching shape (`SessionRouting`) for adapters to consume.
142
161
  */
143
162
  export interface SessionRouting {
144
163
  /** Whose app must do the talking. */
@@ -148,12 +167,12 @@ export interface SessionRouting {
148
167
  /**
149
168
  * When this agent last spoke, ISO-8601 — its newest reply's timestamp,
150
169
  * absent when it has never produced one. Not the session's modification
151
- * time, which a mid-turn save moves; see `@stratusagent/channels`.
170
+ * time, which a mid-turn save moves; see `SessionRouting` in @stratusagent/core.
152
171
  */
153
172
  lastSpokeAt?: string;
154
- /** The latest turn's text (`latestTurnReply`), when it produced any — see `@stratusagent/channels`. */
173
+ /** The latest turn's text (`latestTurnReply`), when it produced any — see `SessionRouting` in @stratusagent/core. */
155
174
  reply?: string;
156
- /** Whether the turn the session is on is one nobody asked for (`isUnaddressedTurn`) — see `@stratusagent/channels`. */
175
+ /** Whether the turn the session is on is one nobody asked for (`isUnaddressedTurn`) — see `SessionRouting` in @stratusagent/core. */
157
176
  unaddressed?: boolean;
158
177
  }
159
178
  /**
@@ -510,6 +529,15 @@ export interface GatewayOptions {
510
529
  * event, or response resets it. 0 disables. Default 120s.
511
530
  */
512
531
  idleTimeoutMs?: number;
532
+ /**
533
+ * The events listener's sources and body limit (card 590), from the
534
+ * trusted config's `events` block. With no sources the event store is
535
+ * never opened, and every delivery is to an unknown source.
536
+ */
537
+ events?: {
538
+ sources?: Record<string, EventSourceConfig>;
539
+ maxBodyBytes?: number;
540
+ };
513
541
  /**
514
542
  * Where the per-agent session stores (`agents/<id>/sessions.db`) and the
515
543
  * fleet database (`fleet.db`, holding the schedules and the session
@@ -837,6 +865,24 @@ export interface Gateway {
837
865
  * such schedule exists.
838
866
  */
839
867
  cancelSchedule(scheduleId: string): boolean;
868
+ /**
869
+ * One request to the events port (card 590): verified, deduplicated,
870
+ * matched against event triggers and recorded, all before the answer.
871
+ * The listener in control-api calls this with the raw body.
872
+ */
873
+ receiveEvent(request: EventIntakeRequest): Promise<EventIntakeResponse>;
874
+ /**
875
+ * Per configured event source: the last delivery, the last refusal and
876
+ * why, and the last 24 hours by status, with the requests refused before
877
+ * they could leave a row (`stratus events`, `/health`). No sources when
878
+ * none is configured.
879
+ */
880
+ eventSummary(): EventsSummary;
881
+ /** Delivery rows, newest first; a row never holds the body. Empty when no source is configured. */
882
+ eventDeliveries(options?: {
883
+ source?: string;
884
+ limit?: number;
885
+ }): EventDeliveryRow[];
840
886
  /**
841
887
  * Every registered tool, with the risk it will actually be judged at and
842
888
  * the package it came from.