@sixb/sqlite 0.1.18 → 0.1.20

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 (59) hide show
  1. package/dist/action-run-storage.d.ts +4 -9
  2. package/dist/action-run-storage.d.ts.map +1 -1
  3. package/dist/agents/agent-storage.d.ts.map +1 -1
  4. package/dist/ai-limit-storage.d.ts.map +1 -1
  5. package/dist/ai-usage-storage.d.ts.map +1 -1
  6. package/dist/auth-storage/auth-storage.d.ts.map +1 -1
  7. package/dist/auth-storage/magic-links.d.ts.map +1 -1
  8. package/dist/auth-storage/oidc-attempts.d.ts.map +1 -1
  9. package/dist/auth-storage/rows.d.ts +0 -3
  10. package/dist/auth-storage/rows.d.ts.map +1 -1
  11. package/dist/auth-storage/sessions.d.ts +0 -10
  12. package/dist/auth-storage/sessions.d.ts.map +1 -1
  13. package/dist/auth-storage/shared.d.ts +0 -2
  14. package/dist/auth-storage/shared.d.ts.map +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +301 -398
  17. package/dist/index.js.map +27 -27
  18. package/dist/migrations.d.ts +2 -0
  19. package/dist/migrations.d.ts.map +1 -1
  20. package/dist/objects/storage.d.ts.map +1 -1
  21. package/dist/pipeline-run-storage.d.ts.map +1 -1
  22. package/dist/projection-run-storage.d.ts.map +1 -1
  23. package/dist/rules-storage.d.ts.map +1 -1
  24. package/dist/share-grant-storage.d.ts.map +1 -1
  25. package/dist/share-session-storage.d.ts.map +1 -1
  26. package/dist/sync-run-storage.d.ts.map +1 -1
  27. package/dist/timeseries-storage.d.ts.map +1 -1
  28. package/dist/transactions.d.ts +0 -1
  29. package/dist/transactions.d.ts.map +1 -1
  30. package/dist/webhook-run-storage.d.ts.map +1 -1
  31. package/dist/workflow-intervention-storage.d.ts.map +1 -1
  32. package/dist/workflow-run-storage.d.ts.map +1 -1
  33. package/package.json +3 -3
  34. package/src/action-run-storage.ts +104 -447
  35. package/src/agents/agent-storage.ts +0 -5
  36. package/src/ai-limit-storage.ts +0 -2
  37. package/src/ai-usage-storage.ts +0 -4
  38. package/src/auth-storage/auth-storage.ts +0 -23
  39. package/src/auth-storage/magic-links.ts +1 -6
  40. package/src/auth-storage/oidc-attempts.ts +1 -6
  41. package/src/auth-storage/rows.ts +0 -6
  42. package/src/auth-storage/sessions.ts +1 -49
  43. package/src/auth-storage/shared.ts +3 -20
  44. package/src/index.ts +1 -10
  45. package/src/migrations/060-terminal-action-runs.sql +149 -0
  46. package/src/migrations/061-drop-auth-audience.sql +16 -0
  47. package/src/migrations.ts +24 -0
  48. package/src/objects/storage.ts +0 -5
  49. package/src/pipeline-run-storage.ts +0 -5
  50. package/src/projection-run-storage.ts +0 -5
  51. package/src/rules-storage.ts +0 -5
  52. package/src/share-grant-storage.ts +0 -2
  53. package/src/share-session-storage.ts +0 -2
  54. package/src/sync-run-storage.ts +0 -5
  55. package/src/timeseries-storage.ts +0 -5
  56. package/src/transactions.ts +8 -8
  57. package/src/webhook-run-storage.ts +0 -3
  58. package/src/workflow-intervention-storage.ts +0 -5
  59. package/src/workflow-run-storage.ts +0 -5
@@ -5,32 +5,21 @@ import {
5
5
  serializeActionRunFailure,
6
6
  } from "@sixb/core/internal/action-run-storage"
7
7
  import { assertActionRunExecution } from "@sixb/core/internal/action-run-storage-provider"
8
- import {
9
- actionRunPhaseRecordsEqual,
10
- canRequeueActionRunAfterEnqueueFailure,
11
- finishActionRunPhase,
12
- } from "@sixb/core/internal/storage"
8
+ import { normalizeRecordActionRunInput, resolveActionRunEffects } from "@sixb/core/internal/storage"
13
9
  import type {
14
10
  ActionRunEffectsRecord,
15
- ActionRunFailure,
16
- ActionRunParams,
17
11
  ActionRunPhase,
18
12
  ActionRunRecord,
13
+ ActionRunStatus,
19
14
  ActionRunStorage,
20
15
  ActionRunWritebackRecord,
21
- EnterActionRunPhaseInput,
22
16
  ExecutionStorage,
23
- FinishActionRunInput,
24
17
  ListActionRunsInput,
25
18
  ListActionRunsResult,
26
- LockActionMaterializationRunInput,
27
- QueueActionRunInput,
28
19
  RecordActionEffectsInput,
29
- RecordActionWritebackInput,
30
- StartActionRunInput,
20
+ RecordActionRunInput,
31
21
  } from "@sixb/core/storage"
32
- import { ActionRunError, isTerminalActionRun } from "@sixb/core/storage"
33
- import { installFreshSqliteSchema } from "./migrations"
22
+ import { ActionRunError } from "@sixb/core/storage"
34
23
  import {
35
24
  closeSqliteStoreConnection,
36
25
  openSqliteStoreConnection,
@@ -56,299 +45,86 @@ export class SqliteActionRunStorage implements ActionRunStorage {
56
45
  this.connection = openSqliteStoreConnection(options)
57
46
  this.db = this.connection.db
58
47
  this.executions = options.executions
59
-
60
- if (this.connection.installFreshSchema) {
61
- installFreshSqliteSchema(this.db)
62
- }
63
48
  }
64
49
 
65
- async lockForMaterialization(input: LockActionMaterializationRunInput): Promise<ActionRunRecord> {
66
- const row = this.db
67
- .query(`SELECT action_id, status FROM action_runs WHERE project_id = ? AND id = ?`)
68
- .get(input.projectId, input.runId) as {
69
- readonly action_id: string
70
- readonly status: string
71
- } | null
72
- if (!row) {
73
- throw new ActionRunError(
74
- `[SixbSqlite] Action run '${input.runId}' not found for project '${input.projectId}'.`
75
- )
76
- }
77
- if (row.action_id !== input.actionId) {
78
- throw new ActionRunError(
79
- `[SixbSqlite] Action run '${input.runId}' does not belong to action '${input.actionId}'.`
80
- )
81
- }
82
- if (row.status !== "running") {
83
- throw new ActionRunError(
84
- `[SixbSqlite] Action run '${input.runId}' cannot materialize from status '${row.status}'.`
85
- )
86
- }
87
- const record = await this.getById({ projectId: input.projectId, id: input.runId })
88
- if (!record) {
89
- throw new ActionRunError(
90
- `[SixbSqlite] Action run '${input.runId}' disappeared while locking materialization.`
91
- )
92
- }
93
- return record
94
- }
95
-
96
- async queue(input: QueueActionRunInput): Promise<ActionRunRecord> {
97
- const queuedAt = input.queuedAt ?? new Date()
50
+ async record(input: RecordActionRunInput): Promise<ActionRunRecord> {
51
+ const record = normalizeRecordActionRunInput(input)
98
52
  await assertActionRunExecution({
99
53
  executions: this.executions,
100
- projectId: input.projectId,
101
- executionId: input.executionId,
102
- runId: input.id,
103
- actionId: input.actionId,
54
+ projectId: record.projectId,
55
+ executionId: record.executionId,
56
+ runId: record.id,
57
+ actionId: record.actionId,
104
58
  })
105
59
 
106
- try {
107
- this.db
108
- .query(
109
- `
110
- INSERT INTO action_runs (
111
- project_id,
112
- id,
113
- execution_id,
114
- action_id,
115
- subject_kind,
116
- object_type_id,
117
- primary_id,
118
- status,
119
- phase,
120
- queued_at,
121
- started_at,
122
- finished_at,
123
- params,
124
- idempotency_key,
125
- writeback_status,
126
- writeback_completed_at,
127
- writeback_result,
128
- writeback_error,
129
- effects_status,
130
- effects_completed_at,
131
- effects_error,
132
- error
133
- ) VALUES (
134
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?,
135
- NULL, NULL, NULL, NULL,
136
- NULL, NULL, NULL,
137
- NULL
138
- )
60
+ const inserted = this.db
61
+ .query(
139
62
  `
140
- )
141
- .run(
142
- input.projectId,
143
- input.id,
144
- input.executionId,
145
- input.actionId,
146
- input.subject.kind,
147
- input.subject.kind === "object" ? input.subject.objectTypeId : null,
148
- input.subject.kind === "object" ? input.subject.primaryId : null,
149
- "queued",
150
- "request",
151
- queuedAt.toISOString(),
152
- JSON.stringify(input.params),
153
- input.idempotencyKey
154
- )
155
- } catch (error) {
156
- if (isUniqueConstraintError(error)) {
157
- return this.requeueAfterEnqueueFailure(input, queuedAt)
158
- }
159
-
160
- throw error
161
- }
63
+ INSERT INTO action_runs (
64
+ project_id,
65
+ id,
66
+ execution_id,
67
+ action_id,
68
+ subject_kind,
69
+ object_type_id,
70
+ primary_id,
71
+ status,
72
+ phase,
73
+ started_at,
74
+ finished_at,
75
+ params,
76
+ idempotency_key,
77
+ writeback_status,
78
+ writeback_completed_at,
79
+ writeback_result,
80
+ writeback_error,
81
+ error
82
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
83
+ ON CONFLICT DO NOTHING
84
+ `
85
+ )
86
+ .run(
87
+ record.projectId,
88
+ record.id,
89
+ record.executionId,
90
+ record.actionId,
91
+ record.subject.kind,
92
+ record.subject.kind === "object" ? record.subject.objectTypeId : null,
93
+ record.subject.kind === "object" ? record.subject.primaryId : null,
94
+ record.status,
95
+ record.phase,
96
+ record.startedAt.toISOString(),
97
+ record.finishedAt.toISOString(),
98
+ JSON.stringify(record.params),
99
+ record.idempotencyKey,
100
+ record.writeback?.status ?? null,
101
+ record.writeback?.completedAt.toISOString() ?? null,
102
+ record.writeback?.status === "succeeded" ? JSON.stringify(record.writeback.result) : null,
103
+ record.writeback?.status === "failed"
104
+ ? serializeActionRunFailure(record.writeback.error, "writeback")
105
+ : null,
106
+ record.status === "failed" ? serializeActionRunFailure(record.error) : null
107
+ )
162
108
 
163
- const record = await this.getById({ projectId: input.projectId, id: input.id })
164
- if (!record) {
109
+ if (inserted.changes === 0) {
110
+ const conflict = this.selectRow(record.projectId, record.id)
165
111
  throw new ActionRunError(
166
- `[SixbSqlite] Failed to load action run '${input.id}' for project '${input.projectId}'.`
112
+ conflict
113
+ ? `[SixbSqlite] Action run '${record.id}' is already recorded for project '${record.projectId}'.`
114
+ : `[SixbSqlite] Execution '${record.executionId}' already belongs to another Action run.`
167
115
  )
168
116
  }
169
-
170
- return record
171
- }
172
-
173
- private async requeueAfterEnqueueFailure(
174
- input: QueueActionRunInput,
175
- queuedAt: Date
176
- ): Promise<ActionRunRecord> {
177
- return this.db.transaction(() => {
178
- const existing = this.db
179
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
180
- .get(input.projectId, input.id) as DatabaseRow | null
181
-
182
- if (
183
- !existing ||
184
- !canRequeueActionRunAfterEnqueueFailure(rowToActionRunRecord(existing), input)
185
- ) {
186
- throw new ActionRunError(
187
- `[SixbSqlite] Action run '${input.id}' already exists for project '${input.projectId}'.`
188
- )
189
- }
190
-
191
- this.db
192
- .query(
193
- `
194
- UPDATE action_runs
195
- SET
196
- status = ?,
197
- phase = ?,
198
- queued_at = ?,
199
- started_at = NULL,
200
- finished_at = NULL,
201
- writeback_status = NULL,
202
- writeback_completed_at = NULL,
203
- writeback_result = NULL,
204
- writeback_error = NULL,
205
- effects_status = NULL,
206
- effects_completed_at = NULL,
207
- effects_error = NULL,
208
- error = NULL
209
- WHERE project_id = ? AND id = ?
210
- `
211
- )
212
- .run("queued", "request", queuedAt.toISOString(), input.projectId, input.id)
213
-
214
- const updated = this.db
215
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
216
- .get(input.projectId, input.id) as DatabaseRow
217
-
218
- return rowToActionRunRecord(updated)
219
- })()
220
- }
221
-
222
- async start(input: StartActionRunInput): Promise<ActionRunRecord> {
223
- return this.db.transaction(() => {
224
- const existing = this.db
225
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
226
- .get(input.projectId, input.id) as DatabaseRow | null
227
-
228
- if (!existing) {
229
- throw new ActionRunError(
230
- `[SixbSqlite] Action run '${input.id}' not found for project '${input.projectId}'.`
231
- )
232
- }
233
-
234
- if (existing.status !== "queued") {
235
- throw new ActionRunError(
236
- `[SixbSqlite] Action run '${input.id}' cannot start from status '${existing.status}'.`
237
- )
238
- }
239
-
240
- this.db
241
- .query(
242
- `
243
- UPDATE action_runs
244
- SET
245
- status = ?,
246
- phase = ?,
247
- started_at = ?,
248
- error = NULL
249
- WHERE project_id = ? AND id = ?
250
- `
251
- )
252
- .run(
253
- "running",
254
- input.phase ?? "validation",
255
- (input.startedAt ?? new Date()).toISOString(),
256
- input.projectId,
257
- input.id
258
- )
259
-
260
- const updated = this.db
261
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
262
- .get(input.projectId, input.id) as DatabaseRow
263
-
264
- return rowToActionRunRecord(updated)
265
- })()
266
- }
267
-
268
- async enterPhase(input: EnterActionRunPhaseInput): Promise<ActionRunRecord> {
269
- return this.db.transaction(() => {
270
- this.requireRunningRun(input.projectId, input.id, "transition phase")
271
-
272
- this.db
273
- .query(
274
- `
275
- UPDATE action_runs
276
- SET phase = ?
277
- WHERE project_id = ? AND id = ?
278
- `
279
- )
280
- .run(input.phase, input.projectId, input.id)
281
-
282
- const updated = this.db
283
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
284
- .get(input.projectId, input.id) as DatabaseRow
285
-
286
- return rowToActionRunRecord(updated)
287
- })()
288
- }
289
-
290
- async recordWriteback(input: RecordActionWritebackInput): Promise<ActionRunRecord> {
291
- return this.db.transaction(() => {
292
- const existing = this.requireRunningRun(input.projectId, input.id, "record writeback")
293
- const nextWriteback = toWritebackRecord(input, new Date(input.completedAt ?? new Date()))
294
- const currentWriteback = toActionRunWritebackRecord(existing)
295
-
296
- if (currentWriteback) {
297
- if (actionRunPhaseRecordsEqual(currentWriteback, nextWriteback)) {
298
- return rowToActionRunRecord(existing)
299
- }
300
-
301
- throw new ActionRunError(
302
- `[SixbSqlite] Action run '${input.id}' already has a different writeback record.`
303
- )
304
- }
305
-
306
- this.db
307
- .query(
308
- `
309
- UPDATE action_runs
310
- SET
311
- phase = ?,
312
- writeback_status = ?,
313
- writeback_completed_at = ?,
314
- writeback_result = ?,
315
- writeback_error = ?
316
- WHERE project_id = ? AND id = ?
317
- `
318
- )
319
- .run(
320
- "writeback",
321
- input.status,
322
- nextWriteback.completedAt.toISOString(),
323
- input.status === "succeeded" ? serializeJsonValue(input.result) : null,
324
- input.status === "failed" ? serializeActionRunFailure(input.error, "writeback") : null,
325
- input.projectId,
326
- input.id
327
- )
328
-
329
- const updated = this.db
330
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
331
- .get(input.projectId, input.id) as DatabaseRow
332
-
333
- return rowToActionRunRecord(updated)
334
- })()
117
+ return rowToActionRunRecord(this.requireRow(record.projectId, record.id))
335
118
  }
336
119
 
337
120
  async recordEffects(input: RecordActionEffectsInput): Promise<ActionRunRecord> {
338
121
  return this.db.transaction(() => {
339
- const existing = this.requireRunningRun(input.projectId, input.id, "record effects")
340
- const nextEffects = toEffectsRecord(input, new Date(input.completedAt ?? new Date()))
341
- const currentEffects = toActionRunEffectsRecord(existing)
342
-
343
- if (currentEffects) {
344
- if (actionRunPhaseRecordsEqual(currentEffects, nextEffects)) {
345
- return rowToActionRunRecord(existing)
346
- }
347
-
348
- throw new ActionRunError(
349
- `[SixbSqlite] Action run '${input.id}' already has a different effects record.`
350
- )
351
- }
122
+ const existing = this.selectRow(input.projectId, input.id)
123
+ const { run, effects } = resolveActionRunEffects(
124
+ existing ? rowToActionRunRecord(existing) : null,
125
+ input
126
+ )
127
+ if (!effects) return run
352
128
 
353
129
  this.db
354
130
  .query(
@@ -364,67 +140,13 @@ export class SqliteActionRunStorage implements ActionRunStorage {
364
140
  )
365
141
  .run(
366
142
  "effects",
367
- input.status,
368
- nextEffects.completedAt.toISOString(),
369
- input.status === "failed" ? serializeActionRunFailure(input.error, "effects") : null,
370
- input.projectId,
371
- input.id
372
- )
373
-
374
- const updated = this.db
375
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
376
- .get(input.projectId, input.id) as DatabaseRow
377
-
378
- return rowToActionRunRecord(updated)
379
- })()
380
- }
381
-
382
- async finish(input: FinishActionRunInput): Promise<ActionRunRecord> {
383
- return this.db.transaction(() => {
384
- const existing = this.db
385
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
386
- .get(input.projectId, input.id) as DatabaseRow | null
387
-
388
- if (!existing) {
389
- throw new ActionRunError(
390
- `[SixbSqlite] Action run '${input.id}' not found for project '${input.projectId}'.`
391
- )
392
- }
393
-
394
- if (isTerminalActionRun({ status: existing.status })) {
395
- throw new ActionRunError(
396
- `[SixbSqlite] Action run '${input.id}' cannot finish from terminal status '${existing.status}'.`
397
- )
398
- }
399
-
400
- const phase = finishActionRunPhase(input, existing.phase)
401
-
402
- this.db
403
- .query(
404
- `
405
- UPDATE action_runs
406
- SET
407
- status = ?,
408
- phase = ?,
409
- finished_at = ?,
410
- error = ?
411
- WHERE project_id = ? AND id = ?
412
- `
413
- )
414
- .run(
415
- input.status,
416
- phase,
417
- (input.finishedAt ?? new Date()).toISOString(),
418
- input.status === "succeeded" ? null : serializeActionRunFailure(input.error),
143
+ effects.status,
144
+ effects.completedAt.toISOString(),
145
+ effects.status === "failed" ? serializeActionRunFailure(effects.error, "effects") : null,
419
146
  input.projectId,
420
147
  input.id
421
148
  )
422
-
423
- const updated = this.db
424
- .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
425
- .get(input.projectId, input.id) as DatabaseRow
426
-
427
- return rowToActionRunRecord(updated)
149
+ return rowToActionRunRecord(this.requireRow(input.projectId, input.id))
428
150
  })()
429
151
  }
430
152
 
@@ -503,12 +225,12 @@ export class SqliteActionRunStorage implements ActionRunStorage {
503
225
  }
504
226
 
505
227
  if (input.startedAfter) {
506
- whereClauses.push("COALESCE(started_at, queued_at) >= ?")
228
+ whereClauses.push("started_at >= ?")
507
229
  args.push(input.startedAfter.toISOString())
508
230
  }
509
231
 
510
232
  if (input.startedBefore) {
511
- whereClauses.push("COALESCE(started_at, queued_at) <= ?")
233
+ whereClauses.push("started_at <= ?")
512
234
  args.push(input.startedBefore.toISOString())
513
235
  }
514
236
 
@@ -524,7 +246,7 @@ export class SqliteActionRunStorage implements ActionRunStorage {
524
246
  let query = `
525
247
  SELECT * FROM action_runs
526
248
  ${where}
527
- ORDER BY COALESCE(started_at, queued_at) ${order}, id ${order}
249
+ ORDER BY started_at ${order}, id ${order}
528
250
  `
529
251
  const queryArgs = [...args]
530
252
 
@@ -550,51 +272,29 @@ export class SqliteActionRunStorage implements ActionRunStorage {
550
272
  closeSqliteStoreConnection(this.connection)
551
273
  }
552
274
 
553
- private requireRunningRun(projectId: string, id: string, operation: string): DatabaseRow {
554
- const existing = this.db
275
+ private selectRow(projectId: string, id: string): DatabaseRow | null {
276
+ return this.db
555
277
  .query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
556
278
  .get(projectId, id) as DatabaseRow | null
279
+ }
557
280
 
558
- if (!existing) {
559
- throw new ActionRunError(
560
- `[SixbSqlite] Action run '${id}' not found for project '${projectId}'.`
561
- )
562
- }
563
-
564
- if (existing.status !== "running") {
281
+ private requireRow(projectId: string, id: string): DatabaseRow {
282
+ const row = this.selectRow(projectId, id)
283
+ if (!row) {
565
284
  throw new ActionRunError(
566
- `[SixbSqlite] Action run '${id}' cannot ${operation} from status '${existing.status}'.`
285
+ `[SixbSqlite] Action run '${id}' disappeared from project '${projectId}' while recording it.`
567
286
  )
568
287
  }
569
-
570
- return existing
288
+ return row
571
289
  }
572
290
  }
573
291
 
574
- function serializeJsonValue(value: JsonValue): string {
575
- return JSON.stringify(value)
576
- }
577
-
578
- function toActionRunFailure(row: DatabaseRow): ActionRunFailure | undefined {
579
- return row.error === null ? undefined : parseActionRunFailure(row.error)
580
- }
581
-
582
- function parsePhaseFailure<TPhase extends Extract<ActionRunPhase, "writeback" | "effects">>(
583
- value: string | null,
584
- expectedPhase: TPhase
585
- ): ActionRunFailure<TPhase> {
586
- return parseActionRunFailure(value, expectedPhase)
587
- }
588
-
589
292
  function toActionRunWritebackRecord(row: DatabaseRow): ActionRunWritebackRecord | undefined {
590
293
  if (!row.writeback_status) {
591
294
  return undefined
592
295
  }
593
296
 
594
- const completedAt = row.writeback_completed_at
595
- ? new Date(row.writeback_completed_at)
596
- : new Date(row.queued_at)
597
-
297
+ const completedAt = new Date(row.writeback_completed_at ?? row.finished_at)
598
298
  if (row.writeback_status === "succeeded") {
599
299
  return {
600
300
  status: "succeeded",
@@ -607,7 +307,7 @@ function toActionRunWritebackRecord(row: DatabaseRow): ActionRunWritebackRecord
607
307
  return {
608
308
  status: "failed",
609
309
  completedAt,
610
- error: parsePhaseFailure(row.writeback_error, "writeback"),
310
+ error: parseActionRunFailure(row.writeback_error, "writeback"),
611
311
  }
612
312
  }
613
313
 
@@ -616,10 +316,7 @@ function toActionRunEffectsRecord(row: DatabaseRow): ActionRunEffectsRecord | un
616
316
  return undefined
617
317
  }
618
318
 
619
- const completedAt = row.effects_completed_at
620
- ? new Date(row.effects_completed_at)
621
- : new Date(row.queued_at)
622
-
319
+ const completedAt = new Date(row.effects_completed_at ?? row.finished_at)
623
320
  if (row.effects_status === "succeeded") {
624
321
  return {
625
322
  status: "succeeded",
@@ -630,65 +327,30 @@ function toActionRunEffectsRecord(row: DatabaseRow): ActionRunEffectsRecord | un
630
327
  return {
631
328
  status: "failed",
632
329
  completedAt,
633
- error: parsePhaseFailure(row.effects_error, "effects"),
634
- }
635
- }
636
-
637
- function toWritebackRecord(
638
- input: RecordActionWritebackInput,
639
- completedAt: Date
640
- ): ActionRunWritebackRecord {
641
- if (input.status === "succeeded") {
642
- return {
643
- status: "succeeded",
644
- completedAt,
645
- result: input.result,
646
- }
647
- }
648
-
649
- return {
650
- status: "failed",
651
- completedAt,
652
- error: input.error,
653
- }
654
- }
655
-
656
- function toEffectsRecord(
657
- input: RecordActionEffectsInput,
658
- completedAt: Date
659
- ): ActionRunEffectsRecord {
660
- if (input.status === "succeeded") {
661
- return {
662
- status: "succeeded",
663
- completedAt,
664
- }
665
- }
666
-
667
- return {
668
- status: "failed",
669
- completedAt,
670
- error: input.error,
330
+ error: parseActionRunFailure(row.effects_error, "effects"),
671
331
  }
672
332
  }
673
333
 
674
334
  function rowToActionRunRecord(row: DatabaseRow): ActionRunRecord {
675
- return {
335
+ const writeback = toActionRunWritebackRecord(row)
336
+ const effects = toActionRunEffectsRecord(row)
337
+ const fields = {
676
338
  id: row.id,
677
339
  projectId: row.project_id,
678
340
  executionId: row.execution_id,
679
341
  actionId: row.action_id,
680
342
  subject: rowToActionSubject(row),
681
- status: row.status,
682
- phase: row.phase ?? undefined,
683
- queuedAt: new Date(row.queued_at),
684
- startedAt: row.started_at ? new Date(row.started_at) : undefined,
685
- finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
686
- params: JSON.parse(row.params) as ActionRunParams,
343
+ phase: row.phase,
344
+ startedAt: new Date(row.started_at),
345
+ finishedAt: new Date(row.finished_at),
346
+ params: JSON.parse(row.params) as ActionRunRecord["params"],
687
347
  idempotencyKey: row.idempotency_key,
688
- writeback: toActionRunWritebackRecord(row),
689
- effects: toActionRunEffectsRecord(row),
690
- error: toActionRunFailure(row),
348
+ ...(writeback === undefined ? {} : { writeback }),
349
+ ...(effects === undefined ? {} : { effects }),
691
350
  }
351
+
352
+ if (row.status === "succeeded") return { ...fields, status: "succeeded" }
353
+ return { ...fields, status: "failed", error: parseActionRunFailure(row.error) }
692
354
  }
693
355
 
694
356
  function rowToActionSubject(row: DatabaseRow): ActionSubject {
@@ -707,10 +369,6 @@ function rowToActionSubject(row: DatabaseRow): ActionSubject {
707
369
  }
708
370
  }
709
371
 
710
- function isUniqueConstraintError(error: unknown): boolean {
711
- return error instanceof Error && error.message.includes("UNIQUE constraint failed")
712
- }
713
-
714
372
  interface DatabaseRow {
715
373
  project_id: string
716
374
  id: string
@@ -719,11 +377,10 @@ interface DatabaseRow {
719
377
  subject_kind: ActionSubject["kind"]
720
378
  object_type_id: string | null
721
379
  primary_id: string | null
722
- status: ActionRunRecord["status"]
723
- phase: ActionRunPhase | null
724
- queued_at: string
725
- started_at: string | null
726
- finished_at: string | null
380
+ status: ActionRunStatus
381
+ phase: ActionRunPhase
382
+ started_at: string
383
+ finished_at: string
727
384
  params: string
728
385
  idempotency_key: string
729
386
  writeback_status: ActionRunWritebackRecord["status"] | null
@@ -1,5 +1,4 @@
1
1
  import type { AgentStorage, ExecutionStorage } from "@sixb/core/storage"
2
- import { installFreshSqliteSchema } from "../migrations"
3
2
  import {
4
3
  closeSqliteStoreConnection,
5
4
  openSqliteStoreConnection,
@@ -35,10 +34,6 @@ export class SqliteAgentStorage implements AgentStorage {
35
34
  constructor(options: SqliteAgentStorageOptions) {
36
35
  this.connection = openSqliteStoreConnection(options)
37
36
 
38
- if (this.connection.installFreshSchema) {
39
- installFreshSqliteSchema(this.connection.db)
40
- }
41
-
42
37
  this.threads = new SqliteAgentThreadStore(this.connection.db)
43
38
  this.runs = new SqliteAgentRunStore(this.connection.db, options.executions)
44
39
  this.messages = new SqliteAgentMessageStore(this.connection.db)