@relaycast/engine 8.7.0 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/adapters/node/realtime.d.ts.map +1 -1
  2. package/dist/adapters/node/realtime.js +13 -4
  3. package/dist/adapters/node/realtime.js.map +1 -1
  4. package/dist/bin/serve.js +3 -0
  5. package/dist/bin/serve.js.map +1 -1
  6. package/dist/db/migrations/0053_agent_retention_indexes.sql +8 -0
  7. package/dist/db/migrations/0054_node_status_heartbeat_index.sql +8 -0
  8. package/dist/db/migrations/0055_session_event_idempotency.sql +8 -0
  9. package/dist/db/migrations/0056_session_event_status_completion.sql +38 -0
  10. package/dist/db/schema.d.ts +89 -0
  11. package/dist/db/schema.d.ts.map +1 -1
  12. package/dist/db/schema.js +27 -0
  13. package/dist/db/schema.js.map +1 -1
  14. package/dist/engine/action.d.ts +4 -0
  15. package/dist/engine/action.d.ts.map +1 -1
  16. package/dist/engine/action.js +104 -35
  17. package/dist/engine/action.js.map +1 -1
  18. package/dist/engine/agentRetention.d.ts +39 -0
  19. package/dist/engine/agentRetention.d.ts.map +1 -0
  20. package/dist/engine/agentRetention.js +117 -0
  21. package/dist/engine/agentRetention.js.map +1 -0
  22. package/dist/engine/delivery.d.ts.map +1 -1
  23. package/dist/engine/delivery.js +6 -1
  24. package/dist/engine/delivery.js.map +1 -1
  25. package/dist/engine/node.d.ts +51 -28
  26. package/dist/engine/node.d.ts.map +1 -1
  27. package/dist/engine/node.js +140 -10
  28. package/dist/engine/node.js.map +1 -1
  29. package/dist/engine/retention.d.ts +12 -0
  30. package/dist/engine/retention.d.ts.map +1 -1
  31. package/dist/engine/retention.js +3 -0
  32. package/dist/engine/retention.js.map +1 -1
  33. package/dist/engine/rowidRetention.d.ts +33 -0
  34. package/dist/engine/rowidRetention.d.ts.map +1 -0
  35. package/dist/engine/rowidRetention.js +190 -0
  36. package/dist/engine/rowidRetention.js.map +1 -0
  37. package/dist/engine/sessionEvent.d.ts +54 -1
  38. package/dist/engine/sessionEvent.d.ts.map +1 -1
  39. package/dist/engine/sessionEvent.js +195 -2
  40. package/dist/engine/sessionEvent.js.map +1 -1
  41. package/dist/engine/workspace.d.ts +8 -9
  42. package/dist/engine/workspace.d.ts.map +1 -1
  43. package/dist/engine/workspace.js +11 -18
  44. package/dist/engine/workspace.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/ports/index.d.ts +7 -0
  49. package/dist/ports/index.d.ts.map +1 -1
  50. package/dist/ports/realtime.d.ts +7 -2
  51. package/dist/ports/realtime.d.ts.map +1 -1
  52. package/dist/ports/realtime.js.map +1 -1
  53. package/dist/routes/agent.d.ts.map +1 -1
  54. package/dist/routes/agent.js +113 -10
  55. package/dist/routes/agent.js.map +1 -1
  56. package/dist/routes/deliveryRouting.d.ts +1 -0
  57. package/dist/routes/deliveryRouting.d.ts.map +1 -1
  58. package/dist/routes/deliveryRouting.js +8 -4
  59. package/dist/routes/deliveryRouting.js.map +1 -1
  60. package/dist/routes/node.js +39 -15
  61. package/dist/routes/node.js.map +1 -1
  62. package/dist/routes/workspace.js +4 -4
  63. package/dist/routes/workspace.js.map +1 -1
  64. package/package.json +3 -3
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ import type { EngineDb } from '../ports/database.js';
3
+ import type { PruneOptions, PruneResult } from './retention.js';
4
+ declare const stateSchema: z.ZodObject<{
5
+ version: z.ZodLiteral<1>;
6
+ next: z.ZodNumber;
7
+ tables: z.ZodRecord<z.ZodString, z.ZodObject<{
8
+ cursor: z.ZodOptional<z.ZodNumber>;
9
+ high: z.ZodOptional<z.ZodNumber>;
10
+ }, z.core.$strip>>;
11
+ }, z.core.$strip>;
12
+ export type RowidRetentionState = z.infer<typeof stateSchema>;
13
+ /**
14
+ * Host-owned durable state for the schema-free retention path. Hosts must
15
+ * serialize maintenance calls for a database, and `save` must resolve only
16
+ * after the checkpoint is durable. This path creates no engine table or index.
17
+ */
18
+ export interface RetentionCursorStore {
19
+ load(): Promise<unknown>;
20
+ save(state: RowidRetentionState): Promise<void>;
21
+ }
22
+ /** Worst-case statement count for one cursor-store invocation, below D1's 1000-query ceiling. */
23
+ export declare const ROWID_RETENTION_D1_QUERY_CEILING: number;
24
+ /**
25
+ * Schema-free recovery mode. It pages by SQLite rowid before policy and
26
+ * eligibility filtering, so retained history cannot turn a bounded run into a
27
+ * full scan. A saved high-water mark makes each traversal finite under writes.
28
+ */
29
+ export declare function pruneRowidPages(db: EngineDb, opts: PruneOptions & {
30
+ cursorStore: RetentionCursorStore;
31
+ }): Promise<PruneResult>;
32
+ export {};
33
+ //# sourceMappingURL=rowidRetention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowidRetention.d.ts","sourceRoot":"","sources":["../../src/engine/rowidRetention.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,gBAAgB,CAAC;AAInF,QAAA,MAAM,WAAW;;;;;;;iBAIf,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAgDD,iGAAiG;AACjG,eAAO,MAAM,gCAAgC,QAEP,CAAC;AAqDvC;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,YAAY,GAAG;IAAE,WAAW,EAAE,oBAAoB,CAAA;CAAE,GACzD,OAAO,CAAC,WAAW,CAAC,CAuGtB"}
@@ -0,0 +1,190 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { z } from 'zod';
3
+ import { snowflakeIdLowerBound } from './snowflake.js';
4
+ const safeRowid = z.number().int().min(Number.MIN_SAFE_INTEGER).max(Number.MAX_SAFE_INTEGER);
5
+ const stateSchema = z.object({
6
+ version: z.literal(1),
7
+ next: z.number().int().min(0).max(4),
8
+ tables: z.record(z.string(), z.object({ cursor: safeRowid.optional(), high: safeRowid.optional() })),
9
+ });
10
+ const SETTLED_DELIVERIES = "deliveries.status IN ('acked', 'failed', 'dead_lettered')";
11
+ const DEFAULT_EXPIRED_DELIVERY_GRACE_DAYS = 7;
12
+ const MAX_ACTIVE_EXPIRY_RECOVERY_BATCHES = 4;
13
+ const ACTIVE_EXPIRY_RECOVERY_DELETE_LIMIT = 1_000;
14
+ const ROWID_PAGE_LIMIT = 200;
15
+ const ROWID_MAX_BATCHES_PER_TABLE = 5;
16
+ const ROWID_DELETE_CHUNK_SIZE = 10;
17
+ const tables = [
18
+ { name: 'messages', result: 'messages', columns: 'id, workspace_id',
19
+ setting: 'message_ttl_days', fallback: 'messageTtlDays', snowflake: true,
20
+ guard: 'NOT EXISTS (SELECT 1 FROM messages replies WHERE replies.thread_id = messages.id)' },
21
+ { name: 'deliveries', result: 'deliveries', columns: 'id, workspace_id, created_at, status, expires_at',
22
+ setting: 'delivery_ttl_days', fallback: 'deliveryTtlDays', guard: SETTLED_DELIVERIES },
23
+ { name: 'message_logs', result: 'messageLogs', columns: 'id, workspace_id',
24
+ setting: 'message_log_ttl_days', fallback: 'messageLogTtlDays', snowflake: true, guard: '1' },
25
+ { name: 'workspace_events', result: 'workspaceEvents', columns: 'workspace_id, seq, created_at',
26
+ setting: 'workspace_event_ttl_days', fallback: 'workspaceEventTtlDays',
27
+ guard: 'EXISTS (SELECT 1 FROM workspace_events hw WHERE hw.workspace_id = workspace_events.workspace_id AND hw.seq > workspace_events.seq LIMIT 1)' },
28
+ { name: 'read_receipts', result: 'readReceipts', columns: 'message_id, agent_id',
29
+ guard: 'NOT EXISTS (SELECT 1 FROM messages m WHERE m.id = read_receipts.message_id)' },
30
+ ];
31
+ /** Worst-case statement count for one cursor-store invocation, below D1's 1000-query ceiling. */
32
+ export const ROWID_RETENTION_D1_QUERY_CEILING = tables.length * (1 + ROWID_MAX_BATCHES_PER_TABLE + ROWID_MAX_BATCHES_PER_TABLE * Math.ceil(ROWID_PAGE_LIMIT / ROWID_DELETE_CHUNK_SIZE)) + MAX_ACTIVE_EXPIRY_RECOVERY_BATCHES;
33
+ /** Delete only long-expired active deliveries through the existing bounded index. */
34
+ async function recoverExpiredActiveDeliveries(db, nowMs, graceDays, maxBatches) {
35
+ const cutoffSeconds = Math.floor((nowMs - Math.max(0, Math.floor(graceDays * 86_400_000))) / 1_000);
36
+ const batches = bounded(maxBatches, MAX_ACTIVE_EXPIRY_RECOVERY_BATCHES, MAX_ACTIVE_EXPIRY_RECOVERY_BATCHES);
37
+ let deleted = 0;
38
+ for (let batch = 0; batch < batches; batch++) {
39
+ // This is the pre-existing global active-expiry index from migration 0031;
40
+ // the outer predicate repeats every condition so extension/status races are safe.
41
+ const changed = await db.all(sql `
42
+ DELETE FROM deliveries
43
+ WHERE status IN ('queued', 'delivered')
44
+ AND expires_at IS NOT NULL AND expires_at < ${cutoffSeconds}
45
+ AND id IN (
46
+ SELECT id FROM deliveries INDEXED BY idx_deliveries_active_expiry
47
+ WHERE status IN ('queued', 'delivered')
48
+ AND expires_at IS NOT NULL AND expires_at < ${cutoffSeconds}
49
+ ORDER BY expires_at, id LIMIT ${ACTIVE_EXPIRY_RECOVERY_DELETE_LIMIT}
50
+ )
51
+ RETURNING id
52
+ `);
53
+ deleted += changed.length;
54
+ if (changed.length < ACTIVE_EXPIRY_RECOVERY_DELETE_LIMIT)
55
+ break;
56
+ }
57
+ return deleted;
58
+ }
59
+ /** Clamp an optional maintenance budget to a positive, finite row count. */
60
+ function bounded(value, fallback, maximum) {
61
+ return value !== undefined && Number.isFinite(value)
62
+ ? Math.max(1, Math.min(maximum, Math.floor(value)))
63
+ : fallback;
64
+ }
65
+ /** Evaluate one candidate against its workspace retention policy and clock. */
66
+ function isExpired(row, table, defaults, nowMs) {
67
+ if (!table.setting || !table.fallback)
68
+ return true;
69
+ const settings = row.retention ? JSON.parse(row.retention) : {};
70
+ const days = settings[table.setting] === undefined ? defaults[table.fallback] : settings[table.setting];
71
+ if (days == null || !Number.isFinite(days) || days <= 0)
72
+ return false;
73
+ const cutoff = nowMs - days * 86_400_000;
74
+ if (!Number.isFinite(cutoff))
75
+ return false;
76
+ if (!table.snowflake)
77
+ return row.created_at * 1_000 < cutoff;
78
+ const bound = snowflakeIdLowerBound(cutoff);
79
+ return row.id.length < bound.length || (row.id.length === bound.length && row.id < bound);
80
+ }
81
+ /**
82
+ * Schema-free recovery mode. It pages by SQLite rowid before policy and
83
+ * eligibility filtering, so retained history cannot turn a bounded run into a
84
+ * full scan. A saved high-water mark makes each traversal finite under writes.
85
+ */
86
+ export async function pruneRowidPages(db, opts) {
87
+ const nowMs = (opts.now ?? new Date()).getTime();
88
+ if (!Number.isFinite(nowMs))
89
+ throw new Error('Invalid retention clock');
90
+ const limit = bounded(opts.batchLimit, ROWID_PAGE_LIMIT, ROWID_PAGE_LIMIT);
91
+ const pageBudget = bounded(opts.maxBatches, ROWID_MAX_BATCHES_PER_TABLE, ROWID_MAX_BATCHES_PER_TABLE) * tables.length;
92
+ const deadline = Date.now() + bounded(opts.maxDurationMs, 10_000, 30_000);
93
+ const defaults = {
94
+ messageTtlDays: null,
95
+ deliveryTtlDays: 90,
96
+ messageLogTtlDays: 90,
97
+ workspaceEventTtlDays: 30,
98
+ ...Object.fromEntries(Object.entries(opts.defaults ?? {}).filter(([, value]) => value !== undefined)),
99
+ };
100
+ const saved = await opts.cursorStore.load();
101
+ // Cursor documents are advisory checkpoints owned by the host. A stale,
102
+ // partial, or legacy document must not take scheduled retention offline, and
103
+ // must never be trusted to construct a delete predicate. Restarting from an
104
+ // empty state is safe because every delete below rechecks row identity and
105
+ // current policy before mutating anything.
106
+ const parsed = saved == null ? undefined : stateSchema.safeParse(saved);
107
+ const state = parsed?.success
108
+ ? parsed.data
109
+ : { version: 1, next: 0, tables: {} };
110
+ const save = () => opts.cursorStore.save(structuredClone(state));
111
+ const result = { messages: 0, deliveries: 0, messageLogs: 0, readReceipts: 0, workspaceEvents: 0 };
112
+ const finished = new Set();
113
+ // Active expiry is opt-in because it intentionally skips delivery.failed
114
+ // fanout; normal scheduled expiry remains the default notification path.
115
+ if (opts.activeExpiryRecovery === true) {
116
+ result.deliveries += await recoverExpiredActiveDeliveries(db, nowMs, opts.expiredDeliveryGraceDays ?? DEFAULT_EXPIRED_DELIVERY_GRACE_DAYS, opts.activeExpiryRecoveryMaxBatches ?? MAX_ACTIVE_EXPIRY_RECOVERY_BATCHES);
117
+ }
118
+ for (let step = 0; step < pageBudget && Date.now() < deadline; step++) {
119
+ const index = state.next;
120
+ state.next = (index + 1) % tables.length;
121
+ if (finished.has(index))
122
+ continue;
123
+ const table = tables[index];
124
+ await save(); // fairness is durable before an admitted page
125
+ const position = state.tables[table.name] ??= {};
126
+ if (Date.now() >= deadline)
127
+ break;
128
+ if (position.high === undefined) {
129
+ const [last] = await db.all(sql `
130
+ SELECT rowid AS _rowid FROM ${sql.raw(table.name)} NOT INDEXED ORDER BY rowid DESC LIMIT 1
131
+ `);
132
+ if (!last) {
133
+ delete state.tables[table.name];
134
+ finished.add(index);
135
+ await save();
136
+ continue;
137
+ }
138
+ position.high = safeRowid.parse(last._rowid);
139
+ }
140
+ if (Date.now() >= deadline)
141
+ break;
142
+ const page = await db.all(sql `
143
+ WITH page AS MATERIALIZED (
144
+ SELECT rowid AS _rowid, ${sql.raw(table.columns)} FROM ${sql.raw(table.name)} NOT INDEXED
145
+ WHERE rowid <= ${position.high}
146
+ ${position.cursor === undefined ? sql `` : sql `AND rowid > ${position.cursor}`}
147
+ ORDER BY rowid LIMIT ${limit}
148
+ ) SELECT page.*, ${sql.raw(table.guard.replaceAll(table.name + '.', 'page.'))} AS eligible,
149
+ ${table.setting ? sql `w.retention` : sql `NULL`} AS retention
150
+ FROM page ${table.setting ? sql `LEFT JOIN workspaces w ON w.id = page.workspace_id` : sql ``}
151
+ ORDER BY page._rowid
152
+ `);
153
+ for (const row of page)
154
+ safeRowid.parse(row._rowid);
155
+ const removable = page.filter(row => row.eligible && isExpired(row, table, defaults, nowMs));
156
+ for (let offset = 0; offset < removable.length; offset += ROWID_DELETE_CHUNK_SIZE) {
157
+ if (Date.now() >= deadline) {
158
+ await save();
159
+ return result;
160
+ }
161
+ const chunk = removable.slice(offset, offset + ROWID_DELETE_CHUNK_SIZE);
162
+ const guards = chunk.map(row => {
163
+ const identity = table.result === 'workspaceEvents'
164
+ ? sql `workspace_id = ${row.workspace_id} AND seq = ${row.seq}`
165
+ : table.result === 'readReceipts'
166
+ ? sql `message_id = ${row.message_id} AND agent_id = ${row.agent_id}`
167
+ : sql `id = ${row.id} AND workspace_id = ${row.workspace_id}`;
168
+ const policy = table.setting ? sql `AND (SELECT retention FROM workspaces WHERE id = ${row.workspace_id}) IS ${row.retention}` : sql ``;
169
+ const created = table.setting && !table.snowflake ? sql `AND created_at = ${row.created_at}` : sql ``;
170
+ const status = table.result === 'deliveries' ? sql `AND status = ${row.status}` : sql ``;
171
+ return sql `(rowid = ${row._rowid} AND ${identity} ${policy} ${created} ${status})`;
172
+ });
173
+ const deleted = await db.all(sql `DELETE FROM ${sql.raw(table.name)} NOT INDEXED
174
+ WHERE (${sql.join(guards, sql ` OR `)}) AND ${sql.raw(table.guard)} RETURNING 1`);
175
+ result[table.result] += deleted.length;
176
+ }
177
+ if (page.length < limit || page.at(-1)?._rowid === position.high) {
178
+ delete state.tables[table.name];
179
+ finished.add(index);
180
+ }
181
+ else {
182
+ position.cursor = page.at(-1)._rowid;
183
+ }
184
+ await save();
185
+ if (finished.size === tables.length)
186
+ break;
187
+ }
188
+ return result;
189
+ }
190
+ //# sourceMappingURL=rowidRetention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowidRetention.js","sourceRoot":"","sources":["../../src/engine/rowidRetention.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,71 @@
1
1
  import type { getDb } from '../db/index.js';
2
+ import { sessionEvents } from '../db/schema.js';
2
3
  type Db = ReturnType<typeof getDb>;
3
4
  export type SessionEventType = 'status.changed' | 'status.idle' | 'status.active' | 'status.blocked' | 'status.waiting' | 'status.offline' | 'tool.called' | 'tool.completed' | 'tool.failed' | 'tool.output' | 'message.received' | 'message.sent' | 'delivery.accepted' | 'delivery.delivered' | 'delivery.deferred' | 'delivery.failed' | 'action.invoked' | 'action.completed' | 'action.failed' | 'action.denied' | 'transcript.chunk' | 'file.changed' | 'command.started' | 'command.completed' | 'command.failed' | 'terminal.output' | 'terminal.screen' | 'usage.updated' | 'session.started' | 'session.released' | 'session.resumed' | 'session.forked' | 'log' | 'error';
4
5
  export declare function isValidEventType(type: string): type is SessionEventType;
6
+ /** `status.*` events are the only ones with a side-effecting agent-row mutation to track. */
7
+ export declare function isStatusEventType(type: string): boolean;
8
+ export interface StatusEventEffectResult {
9
+ /** Whether this request won the durable completion-marker claim. */
10
+ claimed: boolean;
11
+ /** Whether the current agent row was actually changed by this event. */
12
+ mutated: boolean;
13
+ }
5
14
  export declare function recordSessionEvent(db: Db, workspaceId: string, agentId: string, data: {
6
15
  type: SessionEventType;
7
16
  payload: Record<string, unknown>;
8
17
  }): Promise<{
18
+ event: ReturnType<typeof toPublicEvent>;
19
+ replayed: boolean;
20
+ pendingStatusApplication: boolean;
21
+ }>;
22
+ /**
23
+ * Record an event with a durable caller-provided identity.
24
+ *
25
+ * The key is hashed before persistence and scoped by workspace + agent in the
26
+ * unique index. The request digest is retained beside the event so reusing a
27
+ * key with a different event cannot accidentally replay the first event.
28
+ */
29
+ export declare function recordSessionEventWithIdempotency(db: Db, workspaceId: string, agentId: string, data: {
30
+ type: SessionEventType;
31
+ payload: Record<string, unknown>;
32
+ }, idempotencyKey: string): Promise<{
33
+ event: ReturnType<typeof toPublicEvent>;
34
+ replayed: boolean;
35
+ pendingStatusApplication: boolean;
36
+ }>;
37
+ /**
38
+ * Apply a `status.*` event's agent-row mutation and claim its completion
39
+ * durably, as one atomic unit.
40
+ *
41
+ * This is the fix for the crash window between "the event/idempotency claim
42
+ * committed" and "the agent's status row is updated": if either statement
43
+ * here failed independently, a retry could see `replayed: true` and return
44
+ * 201 while the agent row stayed stale forever. Batching both writes through
45
+ * `runAtomicWrites` means a failure here rolls back *both* the status change
46
+ * and the completion marker, so `pendingStatusApplication` stays true and the
47
+ * next replay retries the whole mutation — it can never observe "claimed but
48
+ * never applied" as a terminal state.
49
+ *
50
+ * The completion update is the single-winner claim for side effects: a replay
51
+ * that loses a concurrent claim gets `claimed: false` and must not fan out or
52
+ * enqueue another webhook. The agent update also carries a durable ordering
53
+ * fence: an older pending event may be terminalized, but it cannot overwrite
54
+ * a newer status event that was recorded while the older event was pending.
55
+ * A released agent's row is intentionally not updated (matching
56
+ * `updateAgentById`), but the event is still marked applied: there is no
57
+ * agent row left to reconcile, and retrying forever would just repeat the
58
+ * same no-op.
59
+ */
60
+ export declare function applyStatusEventEffect(db: Db, workspaceId: string, agentId: string, eventId: string, status: string): Promise<StatusEventEffectResult>;
61
+ declare function toPublicEvent(event: typeof sessionEvents.$inferSelect, agentId: string): {
9
62
  id: string;
10
63
  agent_id: string;
11
64
  type: string;
12
65
  payload: unknown;
13
66
  sequence: number;
14
67
  created_at: string;
15
- }>;
68
+ };
16
69
  export declare function listSessionEvents(db: Db, workspaceId: string, agentId: string, options?: {
17
70
  type?: string;
18
71
  limit?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"sessionEvent.d.ts","sourceRoot":"","sources":["../../src/engine/sessionEvent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAI5C,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AAEnC,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,cAAc,GACd,mBAAmB,GACnB,oBAAoB,GACpB,mBAAmB,GACnB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,kBAAkB,GAClB,iBAAiB,GACjB,gBAAgB,GAChB,KAAK,GACL,OAAO,CAAC;AAkBZ,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,gBAAgB,CAEvE;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;;;;;;;GA0BF;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IACR,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;KA0BF;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,GAC1B,MAAM,GAAG,IAAI,CAUf"}
1
+ {"version":3,"file":"sessionEvent.d.ts","sourceRoot":"","sources":["../../src/engine/sessionEvent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAU,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOxD,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AAEnC,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,cAAc,GACd,mBAAmB,GACnB,oBAAoB,GACpB,mBAAmB,GACnB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,kBAAkB,GAClB,iBAAiB,GACjB,gBAAgB,GAChB,KAAK,GACL,OAAO,CAAC;AAkBZ,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,gBAAgB,CAEvE;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,wBAAwB,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB5G;AAED;;;;;;GAMG;AACH,wBAAsB,iCAAiC,CACrD,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,EACD,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,wBAAwB,EAAE,OAAO,CAAA;CAAE,CAAC,CAmE5G;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,uBAAuB,CAAC,CA2FlC;AAaD,iBAAS,aAAa,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM;;;;;;;EAS/E;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,EAAE,EACN,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IACR,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;KA0BF;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,GAC1B,MAAM,GAAG,IAAI,CAUf"}
@@ -1,6 +1,10 @@
1
- import { eq, and, desc, sql } from 'drizzle-orm';
2
- import { sessionEvents } from '../db/schema.js';
1
+ import { eq, and, ne, desc, isNull, sql } from 'drizzle-orm';
2
+ import { agents, sessionEvents } from '../db/schema.js';
3
3
  import { generateId } from './snowflake.js';
4
+ import { sha256Hex } from '../lib/crypto.js';
5
+ import { codedError } from '../lib/httpError.js';
6
+ import { runAtomicWrites } from '../ports/database.js';
7
+ import { RELEASED_AGENT_STATUS } from './agent.js';
4
8
  const VALID_EVENT_TYPES = new Set([
5
9
  'status.changed', 'status.idle', 'status.active', 'status.blocked',
6
10
  'status.waiting', 'status.offline',
@@ -19,6 +23,10 @@ const VALID_EVENT_TYPES = new Set([
19
23
  export function isValidEventType(type) {
20
24
  return VALID_EVENT_TYPES.has(type);
21
25
  }
26
+ /** `status.*` events are the only ones with a side-effecting agent-row mutation to track. */
27
+ export function isStatusEventType(type) {
28
+ return type.startsWith('status.');
29
+ }
22
30
  export async function recordSessionEvent(db, workspaceId, agentId, data) {
23
31
  const id = `evt_${generateId()}`;
24
32
  // Sequence is assigned atomically via a scalar subquery — read and write in
@@ -34,6 +42,191 @@ export async function recordSessionEvent(db, workspaceId, agentId, data) {
34
42
  sequence: sql `(SELECT COALESCE(MAX(sequence), 0) + 1 FROM session_events WHERE agent_id = ${agentId})`,
35
43
  })
36
44
  .returning();
45
+ return {
46
+ event: toPublicEvent(event, agentId),
47
+ replayed: false,
48
+ // Unkeyed events have no durable claim to replay against — the caller
49
+ // always applies the status mutation immediately after this call, so
50
+ // there is nothing to recover across a retry.
51
+ pendingStatusApplication: isStatusEventType(data.type),
52
+ };
53
+ }
54
+ /**
55
+ * Record an event with a durable caller-provided identity.
56
+ *
57
+ * The key is hashed before persistence and scoped by workspace + agent in the
58
+ * unique index. The request digest is retained beside the event so reusing a
59
+ * key with a different event cannot accidentally replay the first event.
60
+ */
61
+ export async function recordSessionEventWithIdempotency(db, workspaceId, agentId, data, idempotencyKey) {
62
+ const [idempotencyKeyHash, requestDigest] = await Promise.all([
63
+ sha256Hex(`session-event-key-v1\0${idempotencyKey}`),
64
+ sha256Hex(`session-event-payload-v1\0${canonicalJson({ type: data.type, payload: data.payload })}`),
65
+ ]);
66
+ const id = `evt_${generateId()}`;
67
+ // The insert and the unique identity claim are one durable operation. A
68
+ // conflict is followed by a scoped lookup so concurrent retries return the
69
+ // winner's exact event rather than allocating a second sequence number.
70
+ const [created] = await db
71
+ .insert(sessionEvents)
72
+ .values({
73
+ id,
74
+ workspaceId,
75
+ agentId,
76
+ type: data.type,
77
+ payload: data.payload,
78
+ idempotencyKeyHash,
79
+ requestDigest,
80
+ sequence: sql `(SELECT COALESCE(MAX(sequence), 0) + 1 FROM session_events WHERE agent_id = ${agentId})`,
81
+ })
82
+ .onConflictDoNothing()
83
+ .returning();
84
+ if (created) {
85
+ return {
86
+ event: toPublicEvent(created, agentId),
87
+ replayed: false,
88
+ pendingStatusApplication: isStatusEventType(data.type),
89
+ };
90
+ }
91
+ const [existing] = await db
92
+ .select()
93
+ .from(sessionEvents)
94
+ .where(and(eq(sessionEvents.workspaceId, workspaceId), eq(sessionEvents.agentId, agentId), eq(sessionEvents.idempotencyKeyHash, idempotencyKeyHash)));
95
+ if (!existing) {
96
+ throw codedError('The event idempotency claim could not be read after a storage conflict; retry with the same Idempotency-Key', 'idempotency_unavailable', 503);
97
+ }
98
+ if (existing.requestDigest !== requestDigest) {
99
+ throw codedError('Idempotency-Key was reused with a different request payload', 'idempotency_key_reused', 409);
100
+ }
101
+ return {
102
+ event: toPublicEvent(existing, agentId),
103
+ replayed: true,
104
+ // `status_applied_at` is set atomically with the agent-row status write
105
+ // (see `applyStatusEventEffect`). NULL here means either the mutation
106
+ // never ran, or it ran but the process crashed before marking it durable
107
+ // — both cases are indistinguishable from "not yet applied" and safe to
108
+ // retry, because the write that sets this column is the same atomic unit
109
+ // as the status mutation itself. A replay that is still pending finishes
110
+ // the interrupted work instead of returning 201 with a stale agent row.
111
+ pendingStatusApplication: isStatusEventType(existing.type) && existing.statusAppliedAt == null,
112
+ };
113
+ }
114
+ /**
115
+ * Apply a `status.*` event's agent-row mutation and claim its completion
116
+ * durably, as one atomic unit.
117
+ *
118
+ * This is the fix for the crash window between "the event/idempotency claim
119
+ * committed" and "the agent's status row is updated": if either statement
120
+ * here failed independently, a retry could see `replayed: true` and return
121
+ * 201 while the agent row stayed stale forever. Batching both writes through
122
+ * `runAtomicWrites` means a failure here rolls back *both* the status change
123
+ * and the completion marker, so `pendingStatusApplication` stays true and the
124
+ * next replay retries the whole mutation — it can never observe "claimed but
125
+ * never applied" as a terminal state.
126
+ *
127
+ * The completion update is the single-winner claim for side effects: a replay
128
+ * that loses a concurrent claim gets `claimed: false` and must not fan out or
129
+ * enqueue another webhook. The agent update also carries a durable ordering
130
+ * fence: an older pending event may be terminalized, but it cannot overwrite
131
+ * a newer status event that was recorded while the older event was pending.
132
+ * A released agent's row is intentionally not updated (matching
133
+ * `updateAgentById`), but the event is still marked applied: there is no
134
+ * agent row left to reconcile, and retrying forever would just repeat the
135
+ * same no-op.
136
+ */
137
+ export async function applyStatusEventEffect(db, workspaceId, agentId, eventId, status) {
138
+ // The agent mutation runs first in the atomic unit. This ordering is
139
+ // important for D1 batches: the completion marker must still be NULL when
140
+ // the conditional status update is evaluated. The following marker update
141
+ // then claims the event; Node transactions and D1 batches serialize this
142
+ // pair, so an identical concurrent retry cannot mutate after the winner has
143
+ // claimed it.
144
+ const [mutationResult, claimResult] = await runAtomicWrites(db, (tx) => [
145
+ tx.update(agents)
146
+ .set({ status })
147
+ .where(and(eq(agents.workspaceId, workspaceId), eq(agents.id, agentId), ne(agents.status, RELEASED_AGENT_STATUS),
148
+ // Do not let an interrupted older event roll a newer status back.
149
+ // The sequence is allocated per agent and is durable across retries;
150
+ // an event that is newer than this one wins the status row. A newer
151
+ // event that is itself pending will be responsible for applying its
152
+ // own status when it replays.
153
+ sql `EXISTS (
154
+ SELECT 1
155
+ FROM session_events AS current_event
156
+ WHERE current_event.id = ${eventId}
157
+ AND current_event.workspace_id = ${workspaceId}
158
+ AND current_event.agent_id = ${agentId}
159
+ AND current_event.status_applied_at IS NULL
160
+ )`, sql `NOT EXISTS (
161
+ SELECT 1
162
+ FROM session_events AS newer_event
163
+ WHERE newer_event.workspace_id = ${workspaceId}
164
+ AND newer_event.agent_id = ${agentId}
165
+ AND newer_event.type LIKE 'status.%'
166
+ AND newer_event.sequence > (
167
+ SELECT current_event.sequence
168
+ FROM session_events AS current_event
169
+ WHERE current_event.id = ${eventId}
170
+ AND current_event.workspace_id = ${workspaceId}
171
+ AND current_event.agent_id = ${agentId}
172
+ )
173
+ )`,
174
+ // Migration 0056 cannot know whether the pre-marker route reached
175
+ // this agent write: it inserted the event and updated the agent in
176
+ // separate operations. Its write-time witness is therefore the only
177
+ // safe evidence available. Reapply a legacy event only when the agent
178
+ // row demonstrably predates the event; an equal or newer witness may
179
+ // be the original status write, a heartbeat, or another status writer.
180
+ // In those ambiguous cases claim the event but never clobber the row,
181
+ // regardless of whether its current status matches the event.
182
+ sql `(
183
+ NOT EXISTS (
184
+ SELECT 1
185
+ FROM session_events AS legacy_event
186
+ WHERE legacy_event.id = ${eventId}
187
+ AND legacy_event.workspace_id = ${workspaceId}
188
+ AND legacy_event.agent_id = ${agentId}
189
+ AND legacy_event.status_legacy_pending = 1
190
+ )
191
+ OR (
192
+ ${agents.status} <> ${status}
193
+ AND
194
+ ${agents.statusUpdatedAt} IS NOT NULL
195
+ AND ${agents.statusUpdatedAt} < (
196
+ SELECT legacy_event.created_at
197
+ FROM session_events AS legacy_event
198
+ WHERE legacy_event.id = ${eventId}
199
+ AND legacy_event.workspace_id = ${workspaceId}
200
+ AND legacy_event.agent_id = ${agentId}
201
+ )
202
+ )
203
+ )`))
204
+ .returning({ id: agents.id }),
205
+ tx.update(sessionEvents)
206
+ .set({ statusAppliedAt: sql `(unixepoch())` })
207
+ .where(and(eq(sessionEvents.id, eventId), eq(sessionEvents.workspaceId, workspaceId), eq(sessionEvents.agentId, agentId), isNull(sessionEvents.statusAppliedAt)))
208
+ .returning({ id: sessionEvents.id }),
209
+ ], { requireAtomic: true });
210
+ const claimed = Array.isArray(claimResult) && claimResult.length > 0;
211
+ const mutated = Array.isArray(mutationResult) && mutationResult.length > 0;
212
+ // Only the caller that both changed the current row and claimed the marker
213
+ // may emit external status side effects. The claim may intentionally win
214
+ // with `mutated === false` when a newer event superseded this one or the
215
+ // agent was released between route validation and this atomic write.
216
+ return { claimed, mutated: claimed && mutated };
217
+ }
218
+ function canonicalJson(value) {
219
+ if (Array.isArray(value))
220
+ return `[${value.map(canonicalJson).join(',')}]`;
221
+ if (value && typeof value === 'object') {
222
+ return `{${Object.keys(value)
223
+ .sort()
224
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
225
+ .join(',')}}`;
226
+ }
227
+ return JSON.stringify(value);
228
+ }
229
+ function toPublicEvent(event, agentId) {
37
230
  return {
38
231
  id: event.id,
39
232
  agent_id: agentId,
@@ -1 +1 @@
1
- {"version":3,"file":"sessionEvent.js","sourceRoot":"","sources":["../../src/engine/sessionEvent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessionEvent.js","sourceRoot":"","sources":["../../src/engine/sessionEvent.ts"],"names":[],"mappings":"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"}
@@ -8,14 +8,14 @@ type CreateWorkspaceOptions = string | {
8
8
  /** Deployment-configured secret used to derive the anonymous bootstrap child key. */
9
9
  bootstrapSecret?: string;
10
10
  /**
11
- * The bootstrap secret as presented by the caller. An anonymous,
12
- * idempotency-keyed create must prove it, matching `bootstrapSecret`,
13
- * before any binding lookup or credential is returned — otherwise the
14
- * `Idempotency-Key` alone (a caller-chosen, non-secret value) would let
15
- * any network peer who guesses or observes it retrieve a workspace's
16
- * deterministic API key. See createWorkspace's bootstrap-proof check.
11
+ * The bootstrap secret as presented by the caller when a self-host opts
12
+ * into shared-secret proof enforcement. Hosted deployments keep the
13
+ * server derivation secret private and use the high-entropy
14
+ * Idempotency-Key as the narrowly scoped recovery capability.
17
15
  */
18
16
  bootstrapSecretProof?: string;
17
+ /** Enable the optional self-host bootstrap-secret proof requirement. */
18
+ bootstrapProofRequired?: boolean;
19
19
  /**
20
20
  * Crash-safe workspace-create replay key (relaycast#371/#379).
21
21
  *
@@ -25,9 +25,8 @@ type CreateWorkspaceOptions = string | {
25
25
  * be unique per logical operation (e.g. a job id).
26
26
  *
27
27
  * Anonymous bootstrap (no owner key): the key is scoped to the
28
- * deployment, not to a caller, and — together with
29
- * `X-Workspace-Bootstrap-Secret` — is what recovers or replays the
30
- * binding. Generate it with a CSPRNG, never a derived or guessable
28
+ * deployment, not to a caller, and is the reveal-once recovery
29
+ * capability for the binding. Generate it with a CSPRNG, never a derived or guessable
31
30
  * value such as a job id, timestamp, or counter. A v4 UUID, 16 random
32
31
  * bytes hex-encoded, or at least 24 random bytes base64url-encoded meet
33
32
  * the 32-character structural minimum. `createWorkspace` cannot verify
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/engine/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/engine/workspace.ts"],"names":[],"mappings":"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"}