@stratusagent/gateway 0.13.7 → 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.
@@ -0,0 +1,256 @@
1
+ import type { InboundEvent } from '@stratusagent/agents';
2
+ import { type SqliteScheduleStoreOptions } from './schedules.ts';
3
+ /** Accepted ids are remembered this long: longer than GitHub's 3-day manual redelivery. */
4
+ export declare const EVENT_DEDUP_WINDOW_MS: number;
5
+ /** Delivery rows listed at most: the newest this many, within the dedup window. */
6
+ export declare const EVENT_DELIVERY_ROW_CAP = 10000;
7
+ /** A firing's rendered event block, and so the event text a turn sees, is at most this many bytes. */
8
+ export declare const EVENT_BLOCK_MAX_BYTES: number;
9
+ /** What happened to one request that had a good signature. */
10
+ export type EventDeliveryStatus = 'refused' | 'duplicate' | 'unmatched' | 'matched';
11
+ /**
12
+ * Where one trigger's firing for one delivery stands. `pending`, `held`,
13
+ * `rate_limited` and `firing` are open and keep their event block; the rest
14
+ * are finished and keep none.
15
+ */
16
+ export type EventFiringStatus = 'pending' | 'held' | 'rate_limited' | 'firing' | 'fired' | 'failed' | 'superseded' | 'expired';
17
+ export interface EventDeliveryRow {
18
+ seq: number;
19
+ source: string;
20
+ eventId: string;
21
+ type: string;
22
+ subject?: string;
23
+ status: EventDeliveryStatus;
24
+ reason?: string;
25
+ receivedAt: string;
26
+ }
27
+ export interface EventFiringRow {
28
+ id: string;
29
+ deliverySeq: number;
30
+ triggerId: string;
31
+ agentId: string;
32
+ source: string;
33
+ eventId: string;
34
+ coalescingKey: string;
35
+ sessionId: string;
36
+ /** This firing's own idempotency key: `eventDispatchKey` of its trigger, source and event. */
37
+ dispatchKey: string;
38
+ /**
39
+ * The key the batch this firing was claimed in goes out under. Set by the
40
+ * claim and never changed after: a claimed batch is fixed, so a recovered
41
+ * one can't come back under another key and start a second turn.
42
+ */
43
+ batchKey?: string;
44
+ status: EventFiringStatus;
45
+ /** When its batch was first claimed to go out: what the hourly limit counts from. */
46
+ startedAt?: string;
47
+ /** The rendered event block, kept only while the firing is open. */
48
+ block?: string;
49
+ reason?: string;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ }
53
+ /**
54
+ * What one source has been doing, from the rows (`SqliteEventStore.activity`).
55
+ * Requests refused before they could leave a row (bad signature, too large,
56
+ * over the rate) aren't here: only the running listener counts those.
57
+ */
58
+ export interface EventSourceActivity {
59
+ /** The newest delivery row, whatever its status. */
60
+ lastDelivery?: {
61
+ at: string;
62
+ type: string;
63
+ subject?: string;
64
+ status: EventDeliveryStatus;
65
+ };
66
+ /** The newest signed delivery whose body was refused, and why. */
67
+ lastRefused?: {
68
+ at: string;
69
+ reason: string;
70
+ };
71
+ /** Delivery rows received since the cutoff, by status. Of the rows still kept (the newest 10,000). */
72
+ deliveries: Partial<Record<EventDeliveryStatus, number>>;
73
+ /**
74
+ * Firings by status: finished ones that reached it since the cutoff, and
75
+ * every open one however old, since it is waiting now.
76
+ */
77
+ firings: Partial<Record<EventFiringStatus, number>>;
78
+ }
79
+ /** One trigger's match on a delivery, as the listener hands it to `accept`. */
80
+ export interface NewEventFiring {
81
+ triggerId: string;
82
+ agentId: string;
83
+ coalescingKey: string;
84
+ sessionId: string;
85
+ dispatchKey: string;
86
+ block: string;
87
+ }
88
+ export interface AcceptedEventDelivery {
89
+ source: string;
90
+ eventId: string;
91
+ type: string;
92
+ subject?: string;
93
+ receivedAt: string;
94
+ /** Empty when no trigger matched. */
95
+ firings: NewEventFiring[];
96
+ }
97
+ export interface RefusedEventDelivery {
98
+ source: string;
99
+ eventId: string;
100
+ type?: string;
101
+ subject?: string;
102
+ reason: string;
103
+ receivedAt: string;
104
+ }
105
+ /**
106
+ * What sessions, holds and rate limits group by: source + subject when the
107
+ * event has one, so every Codex round on one PR is one conversation, and
108
+ * source + id when it doesn't, so subjectless events never share a bucket.
109
+ * JSON, so no subject can be spelled to collide with an id key.
110
+ */
111
+ export declare const eventCoalescingKey: (sourceId: string, event: Pick<InboundEvent, "attributes">) => string;
112
+ /**
113
+ * A trigger's session for one coalescing key. In the reserved `schedule:`
114
+ * namespace, which the gateway refuses to dispatch into from outside, so the
115
+ * only turns on it are the trigger's own firings.
116
+ */
117
+ export declare const eventSessionId: (triggerId: string, coalescingKey: string) => string;
118
+ /**
119
+ * The gateway idempotency key for one trigger's firing on one event: a
120
+ * repeat never starts a second turn, and a turn whose process died continues
121
+ * once. Hashed when over the gateway's bound, as channel-slack does, because
122
+ * a sender picks the event id's length.
123
+ */
124
+ export declare const eventDispatchKey: (triggerId: string, sourceId: string, eventId: string) => string;
125
+ /**
126
+ * What a firing's turn sees after the trigger's prompt. Rendered when the
127
+ * delivery is matched and stored with the firing, so a firing that goes out
128
+ * hours later needs neither the request body nor a second match. Attributes
129
+ * and data are JSON, so a newline in a sender's subject or comment can't pass
130
+ * for a line of the block. Capped at `EVENT_BLOCK_MAX_BYTES` with a note: the
131
+ * agent fetches the rest with its own tools (`gh` for a PR).
132
+ */
133
+ export declare const renderEventBlock: (sourceId: string, event: InboundEvent) => string;
134
+ /**
135
+ * Deliveries, dedup tombstones and firings, durable in `fleet.db`. Its own
136
+ * connection, like the schedule store's. `stratus events` reads through the
137
+ * running daemon instead of this file, because the requests refused before
138
+ * a row are counted only in the listener.
139
+ *
140
+ * Every method that changes more than one row does it in one transaction,
141
+ * and every one that moves a firing between statuses is all-or-nothing and
142
+ * conditional on the statuses it expects: a delivery and its firings are
143
+ * committed together before the sender hears 2xx, a batch is claimed whole
144
+ * before its dispatch, and a refused transition changes nothing.
145
+ */
146
+ export declare class SqliteEventStore {
147
+ private readonly db;
148
+ constructor(filePath: string, options?: SqliteScheduleStoreOptions);
149
+ private transaction;
150
+ private insertDelivery;
151
+ /**
152
+ * Record a delivery whose signature checked out and whose body parsed:
153
+ * `duplicate` when its (source, id) was accepted within the dedup window,
154
+ * else `matched` with one `pending` firing per match, or `unmatched`. One
155
+ * write, made before the sender hears 2xx. Accepted deliveries leave a
156
+ * tombstone that outlives the row cap, so dedup never depends on it.
157
+ */
158
+ accept(delivery: AcceptedEventDelivery): {
159
+ status: Exclude<EventDeliveryStatus, 'refused'>;
160
+ seq: number;
161
+ firings: EventFiringRow[];
162
+ };
163
+ /**
164
+ * Record a signed delivery the listener refused (a form-encoded body, a
165
+ * malformed event). No tombstone: the sender can fix it and retry with the
166
+ * same id. Unsigned requests never reach here; they leave no row at all.
167
+ */
168
+ refuse(delivery: RefusedEventDelivery): EventDeliveryRow;
169
+ /** Delivery rows, newest first. */
170
+ deliveries(options?: {
171
+ limit?: number;
172
+ source?: string;
173
+ }): EventDeliveryRow[];
174
+ /**
175
+ * Per source with any row: its last delivery, its last refused one, and
176
+ * counts since `sinceIso` (`stratus events`, `/health`). Grouped in the
177
+ * database, so the cost doesn't grow with the 7 days of rows kept.
178
+ */
179
+ activity(sinceIso: string): Record<string, EventSourceActivity>;
180
+ firing(id: string): EventFiringRow | undefined;
181
+ /**
182
+ * Every open firing (`pending`, `held`, `rate_limited`, `firing`), oldest
183
+ * first, with its block and batch key: what the startup sweep recovers and
184
+ * what a hold releases. A `firing` row is a claim whose dispatch may or may
185
+ * not have reached the gateway; its batch goes out again as stored, and the
186
+ * idempotency key decides whether that starts or continues a turn.
187
+ */
188
+ open(): EventFiringRow[];
189
+ /**
190
+ * Run `update` on exactly `ids`, all or nothing: false, with nothing
191
+ * changed, unless `precheck` (inside the same transaction) passes and
192
+ * every row matched the update's own conditions.
193
+ */
194
+ private allOrNothing;
195
+ /**
196
+ * Claim firings to go out as one turn under `batchKey`, in one write
197
+ * before the dispatch. Either every row is free and they become a new
198
+ * batch, or `ids` is exactly an existing batch under exactly its key,
199
+ * which is how recovery resends one. A batch is fixed once claimed: no
200
+ * row joins it later, and it never changes key. False, with nothing
201
+ * changed, for anything else, or if any row is finished.
202
+ *
203
+ * With `triggerId`, also false unless that schedule row still exists, in
204
+ * the same transaction: `stratus schedules cancel` deletes from another
205
+ * process, and a trigger cancelled after the scan read it must not fire,
206
+ * as a timed schedule's conditional slot claim already guarantees.
207
+ */
208
+ claim(ids: readonly string[], batchKey: string, nowIso: string, options?: {
209
+ triggerId?: string;
210
+ }): boolean;
211
+ /**
212
+ * Hold open firings for quiet hours or paused intake (`held`) or over the
213
+ * trigger's hourly limit (`rate_limited`). The block and any batch key
214
+ * stay, so a held batch still goes out only as itself.
215
+ */
216
+ hold(ids: readonly string[], status: 'held' | 'rate_limited', nowIso: string): boolean;
217
+ /**
218
+ * Finish claimed firings after their dispatch, clearing the block in the
219
+ * same write: from here the payload lives only in the session it fired.
220
+ * Only a `firing` row can finish, so nothing is marked fired that wasn't
221
+ * claimed first.
222
+ */
223
+ finish(ids: readonly string[], outcome: {
224
+ status: 'fired';
225
+ } | {
226
+ status: 'failed';
227
+ reason: string;
228
+ }, nowIso: string): boolean;
229
+ /**
230
+ * Retire open firings that will never go out: `superseded` by newer ones
231
+ * for the same key, or `expired` (held over 24 hours, or their trigger was
232
+ * cancelled). Clears their blocks. Returns how many were retired; a
233
+ * finished firing is left as it is.
234
+ */
235
+ retire(ids: readonly string[], status: 'superseded' | 'expired', reason: string, nowIso: string): number;
236
+ /**
237
+ * Turns a trigger has started since `sinceIso`, for its hourly limit,
238
+ * counted from when each batch was first claimed (a two-hour turn doesn't
239
+ * hold the hour for two more): a batch running now, one that finished,
240
+ * and one that failed all count,
241
+ * since each was a turn started unattended. A failing provider would
242
+ * otherwise let every new event start another. A batch is one turn, so
243
+ * this counts batch keys, not rows; `exceptBatchKey` leaves out a batch
244
+ * being re-sent after a restart, which must not count against itself.
245
+ */
246
+ turnsStartedSince(triggerId: string, sinceIso: string, exceptBatchKey?: string): number;
247
+ /**
248
+ * Drop what the dedup window and the row cap no longer cover: tombstones
249
+ * and delivery rows past 7 days, delivery rows past the newest 10,000, and
250
+ * finished firings past 7 days. An open firing is never pruned: it holds
251
+ * the only copy of an event that hasn't gone out.
252
+ */
253
+ prune(nowIso: string): void;
254
+ close(): void;
255
+ }
256
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}