@sixb/sqlite 0.1.19 → 0.1.21

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 (81) hide show
  1. package/dist/action-run-storage.d.ts +4 -0
  2. package/dist/action-run-storage.d.ts.map +1 -1
  3. package/dist/activity-storage.d.ts +20 -0
  4. package/dist/activity-storage.d.ts.map +1 -0
  5. package/dist/agents/agent-storage.d.ts.map +1 -1
  6. package/dist/agents/threads.d.ts +4 -0
  7. package/dist/agents/threads.d.ts.map +1 -1
  8. package/dist/ai-limit-storage.d.ts.map +1 -1
  9. package/dist/ai-usage-storage.d.ts.map +1 -1
  10. package/dist/artifact-storage.d.ts +22 -0
  11. package/dist/artifact-storage.d.ts.map +1 -0
  12. package/dist/auth-storage/auth-storage.d.ts.map +1 -1
  13. package/dist/auth-storage/magic-links.d.ts.map +1 -1
  14. package/dist/auth-storage/oidc-attempts.d.ts.map +1 -1
  15. package/dist/auth-storage/rows.d.ts +0 -3
  16. package/dist/auth-storage/rows.d.ts.map +1 -1
  17. package/dist/auth-storage/sessions.d.ts +2 -11
  18. package/dist/auth-storage/sessions.d.ts.map +1 -1
  19. package/dist/auth-storage/shared.d.ts +0 -2
  20. package/dist/auth-storage/shared.d.ts.map +1 -1
  21. package/dist/execution-storage.d.ts +2 -1
  22. package/dist/execution-storage.d.ts.map +1 -1
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +933 -128
  26. package/dist/index.js.map +32 -30
  27. package/dist/migrations.d.ts +2 -0
  28. package/dist/migrations.d.ts.map +1 -1
  29. package/dist/objects/storage.d.ts.map +1 -1
  30. package/dist/ontology-storage/sources.d.ts +2 -1
  31. package/dist/ontology-storage/sources.d.ts.map +1 -1
  32. package/dist/pipeline-run-storage.d.ts.map +1 -1
  33. package/dist/projection-run-storage.d.ts.map +1 -1
  34. package/dist/rules-storage.d.ts.map +1 -1
  35. package/dist/share-grant-storage.d.ts +2 -1
  36. package/dist/share-grant-storage.d.ts.map +1 -1
  37. package/dist/share-session-storage.d.ts.map +1 -1
  38. package/dist/sync-run-storage.d.ts.map +1 -1
  39. package/dist/timeseries-storage.d.ts.map +1 -1
  40. package/dist/transactions.d.ts +0 -1
  41. package/dist/transactions.d.ts.map +1 -1
  42. package/dist/webhook-run-storage.d.ts.map +1 -1
  43. package/dist/workflow-intervention-storage.d.ts.map +1 -1
  44. package/dist/workflow-run-storage.d.ts +4 -0
  45. package/dist/workflow-run-storage.d.ts.map +1 -1
  46. package/package.json +3 -3
  47. package/src/action-run-storage.ts +27 -5
  48. package/src/activity-storage.ts +588 -0
  49. package/src/agents/agent-storage.ts +0 -5
  50. package/src/agents/threads.ts +27 -0
  51. package/src/ai-limit-storage.ts +0 -2
  52. package/src/ai-usage-storage.ts +0 -4
  53. package/src/artifact-storage.ts +356 -0
  54. package/src/auth-storage/auth-storage.ts +0 -23
  55. package/src/auth-storage/magic-links.ts +1 -6
  56. package/src/auth-storage/oidc-attempts.ts +1 -6
  57. package/src/auth-storage/rows.ts +0 -6
  58. package/src/auth-storage/sessions.ts +21 -42
  59. package/src/auth-storage/shared.ts +3 -20
  60. package/src/execution-storage.ts +56 -0
  61. package/src/index.ts +11 -10
  62. package/src/migrations/061-drop-auth-audience.sql +16 -0
  63. package/src/migrations/062-artifacts.sql +71 -0
  64. package/src/migrations/063-drop-output-diagnostics.sql +19 -0
  65. package/src/migrations/064-activity.sql +61 -0
  66. package/src/migrations/065-activity-timeline.sql +19 -0
  67. package/src/migrations/066-activity-interactions.sql +20 -0
  68. package/src/migrations.ts +36 -0
  69. package/src/objects/storage.ts +0 -5
  70. package/src/ontology-storage/sources.ts +41 -0
  71. package/src/pipeline-run-storage.ts +0 -5
  72. package/src/projection-run-storage.ts +0 -5
  73. package/src/rules-storage.ts +0 -5
  74. package/src/share-grant-storage.ts +25 -2
  75. package/src/share-session-storage.ts +0 -2
  76. package/src/sync-run-storage.ts +0 -5
  77. package/src/timeseries-storage.ts +0 -5
  78. package/src/transactions.ts +8 -8
  79. package/src/webhook-run-storage.ts +0 -3
  80. package/src/workflow-intervention-storage.ts +0 -5
  81. package/src/workflow-run-storage.ts +27 -5
package/dist/index.js CHANGED
@@ -28,6 +28,11 @@ import { assertActionRunExecution } from "@sixb/core/internal/action-run-storage
28
28
  import { normalizeRecordActionRunInput, resolveActionRunEffects } from "@sixb/core/internal/storage";
29
29
  import { ActionRunError } from "@sixb/core/storage";
30
30
 
31
+ // src/transactions.ts
32
+ import { Database as SqliteDatabase } from "bun:sqlite";
33
+ import { mkdirSync as mkdirSync2 } from "fs";
34
+ import { dirname as dirname2 } from "path";
35
+
31
36
  // src/migrations.ts
32
37
  import { Database } from "bun:sqlite";
33
38
  import { createHash } from "crypto";
@@ -5142,6 +5147,230 @@ CREATE INDEX idx_action_runs_project_status_started
5142
5147
  ON action_runs(project_id, status, started_at DESC);
5143
5148
  `;
5144
5149
 
5150
+ // src/migrations/061-drop-auth-audience.sql
5151
+ var _061_drop_auth_audience_default = `-- A project has one browser app, so sessions, magic links and sign-in attempts no longer name the
5152
+ -- one they belong to. Atlas's browser sessions end here: their cookie is no longer read. Native
5153
+ -- sessions carried Atlas's name only because the column needed a value, so they stay.
5154
+ UPDATE auth_sessions
5155
+ SET revoked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
5156
+ WHERE audience = 'atlas'
5157
+ AND refresh_token_hash IS NULL
5158
+ AND revoked_at IS NULL;
5159
+
5160
+ DROP INDEX idx_auth_sessions_user_active;
5161
+ ALTER TABLE auth_sessions DROP COLUMN audience;
5162
+ CREATE INDEX idx_auth_sessions_user_active
5163
+ ON auth_sessions(project_id, user_id, revoked_at, expires_at);
5164
+
5165
+ ALTER TABLE auth_magic_links DROP COLUMN audience;
5166
+ ALTER TABLE auth_oidc_authorization_attempts DROP COLUMN audience;
5167
+ `;
5168
+
5169
+ // src/migrations/062-artifacts.sql
5170
+ var _062_artifacts_default = `-- Artifacts: versioned documents, sheets, decks, and files. An artifact's kind never
5171
+ -- changes. Versions take \`next_version\`, which only grows, so a number is never reused after its
5172
+ -- version is deleted. Timestamps are ISO-8601 UTC text and compare lexicographically.
5173
+ CREATE TABLE artifacts (
5174
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
5175
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
5176
+ kind TEXT NOT NULL CHECK (kind IN ('document', 'sheet', 'slides', 'file')),
5177
+ title TEXT NOT NULL CHECK (length(trim(title)) > 0),
5178
+ owner_principal_type TEXT NOT NULL
5179
+ CHECK (owner_principal_type IN ('user', 'serviceAccount', 'system')),
5180
+ owner_principal_id TEXT NOT NULL CHECK (length(owner_principal_id) > 0),
5181
+ latest_version INTEGER NOT NULL CHECK (latest_version > 0),
5182
+ next_version INTEGER NOT NULL CHECK (next_version > latest_version),
5183
+ created_at TEXT NOT NULL,
5184
+ updated_at TEXT NOT NULL,
5185
+ PRIMARY KEY (project_id, id)
5186
+ );
5187
+
5188
+ CREATE INDEX idx_artifacts_project_updated ON artifacts (project_id, updated_at DESC, id);
5189
+ CREATE INDEX idx_artifacts_owner_updated
5190
+ ON artifacts (project_id, owner_principal_type, owner_principal_id, updated_at DESC, id);
5191
+
5192
+ -- \`refs\`, not \`references\`: that word is reserved in SQL.
5193
+ CREATE TABLE artifact_versions (
5194
+ project_id TEXT NOT NULL,
5195
+ artifact_id TEXT NOT NULL,
5196
+ version INTEGER NOT NULL CHECK (version > 0),
5197
+ title TEXT NOT NULL CHECK (length(trim(title)) > 0),
5198
+ entry TEXT NOT NULL CHECK (length(entry) > 0),
5199
+ files TEXT NOT NULL CHECK (json_type(files) = 'array'),
5200
+ outline TEXT NOT NULL CHECK (json_type(outline) = 'object'),
5201
+ refs TEXT NOT NULL CHECK (json_type(refs) = 'array'),
5202
+ author_principal_type TEXT NOT NULL
5203
+ CHECK (author_principal_type IN ('user', 'serviceAccount', 'system')),
5204
+ author_principal_id TEXT NOT NULL CHECK (length(author_principal_id) > 0),
5205
+ origin_thread_id TEXT,
5206
+ origin_run_id TEXT,
5207
+ created_at TEXT NOT NULL,
5208
+ PRIMARY KEY (project_id, artifact_id, version),
5209
+ FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
5210
+ CHECK ((origin_thread_id IS NULL) = (origin_run_id IS NULL))
5211
+ );
5212
+
5213
+ CREATE INDEX idx_artifact_versions_origin_run
5214
+ ON artifact_versions (project_id, origin_run_id) WHERE origin_run_id IS NOT NULL;
5215
+ CREATE INDEX idx_artifact_versions_origin_thread
5216
+ ON artifact_versions (project_id, origin_thread_id, artifact_id) WHERE origin_thread_id IS NOT NULL;
5217
+
5218
+ -- Short-lived links that a sandboxed frame loads a deck's files through. Only the SHA-256 of each
5219
+ -- link's token is stored. \`targets\` is what the frame may show from other artifacts, decided for
5220
+ -- its reader when the link was issued: a JSON array of { address, artifactId, version }.
5221
+ CREATE TABLE artifact_frame_grants (
5222
+ project_id TEXT NOT NULL,
5223
+ token_hash TEXT NOT NULL CHECK (
5224
+ length(token_hash) = 64
5225
+ AND token_hash = lower(token_hash)
5226
+ AND token_hash NOT GLOB '*[^0-9a-f]*'
5227
+ ),
5228
+ artifact_id TEXT NOT NULL,
5229
+ version INTEGER NOT NULL CHECK (version > 0),
5230
+ targets TEXT NOT NULL CHECK (json_type(targets) = 'array'),
5231
+ created_at TEXT NOT NULL,
5232
+ expires_at TEXT NOT NULL,
5233
+ PRIMARY KEY (project_id, token_hash),
5234
+ FOREIGN KEY (project_id, artifact_id) REFERENCES artifacts (project_id, id) ON DELETE CASCADE,
5235
+ CHECK (expires_at > created_at)
5236
+ );
5237
+
5238
+ CREATE INDEX idx_artifact_frame_grants_expiry ON artifact_frame_grants (project_id, expires_at);
5239
+ -- Deleting an artifact cascades here.
5240
+ CREATE INDEX idx_artifact_frame_grants_artifact ON artifact_frame_grants (project_id, artifact_id);
5241
+ `;
5242
+
5243
+ // src/migrations/063-drop-output-diagnostics.sql
5244
+ var _063_drop_output_diagnostics_default = `-- The Agent delivers files by publishing artifacts, so a run no longer collects an output
5245
+ -- directory or reports on it. Stored \`output_*\` diagnostics go; a run left with none stores none.
5246
+ UPDATE agent_runs
5247
+ SET diagnostics = (
5248
+ SELECT CASE WHEN count(*) = 0 THEN NULL ELSE json_group_array(json(value)) END
5249
+ FROM (
5250
+ SELECT value
5251
+ FROM json_each(agent_runs.diagnostics)
5252
+ WHERE json_extract(value, '$.code') NOT LIKE 'output\\_%' ESCAPE '\\'
5253
+ ORDER BY key
5254
+ )
5255
+ )
5256
+ WHERE diagnostics IS NOT NULL
5257
+ AND json_valid(diagnostics)
5258
+ AND EXISTS (
5259
+ SELECT 1
5260
+ FROM json_each(agent_runs.diagnostics)
5261
+ WHERE json_extract(value, '$.code') LIKE 'output\\_%' ESCAPE '\\'
5262
+ );
5263
+ `;
5264
+
5265
+ // src/migrations/064-activity.sql
5266
+ var _064_activity_default = `-- App activity: one source per verified session, the visits it opened, and their page views.
5267
+ -- Foreground activity moves a visit's \`last_seen_at\` and stores no row, unless it opens a visit.
5268
+ -- A session id names an \`auth_sessions\` row for a user and a \`share_sessions\` row for a share; it
5269
+ -- is not a secret, and no foreign key holds it, so history outlives its sessions. Timestamps are
5270
+ -- ISO-8601 UTC text and compare lexicographically.
5271
+ CREATE TABLE activity_sources (
5272
+ project_id TEXT NOT NULL,
5273
+ actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
5274
+ session_id TEXT NOT NULL CHECK (length(session_id) > 0),
5275
+ visit_id TEXT NOT NULL,
5276
+ last_seen_at TEXT NOT NULL,
5277
+ sequence INTEGER NOT NULL CHECK (sequence >= 0),
5278
+ window_start TEXT NOT NULL,
5279
+ window_count INTEGER NOT NULL CHECK (window_count >= 0),
5280
+ PRIMARY KEY (project_id, actor_type, session_id)
5281
+ );
5282
+
5283
+ CREATE TABLE activity_visits (
5284
+ project_id TEXT NOT NULL,
5285
+ id TEXT NOT NULL,
5286
+ actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'share')),
5287
+ user_id TEXT,
5288
+ grant_id TEXT,
5289
+ session_id TEXT NOT NULL CHECK (length(session_id) > 0),
5290
+ started_at TEXT NOT NULL,
5291
+ last_seen_at TEXT NOT NULL CHECK (last_seen_at >= started_at),
5292
+ page_views INTEGER NOT NULL CHECK (page_views >= 0),
5293
+ attribution TEXT CHECK (attribution IS NULL OR json_type(attribution) = 'object'),
5294
+ PRIMARY KEY (project_id, id),
5295
+ CHECK (
5296
+ (actor_type = 'user' AND user_id IS NOT NULL AND grant_id IS NULL)
5297
+ OR (actor_type = 'share' AND user_id IS NULL AND grant_id IS NOT NULL)
5298
+ )
5299
+ );
5300
+
5301
+ -- A period reads the visits it overlaps. A visit lasts less than a day, so they started in
5302
+ -- \`[from - 1 day, to)\`: both period indexes bound \`started_at\` on both sides. \`last_seen_at\` is
5303
+ -- left out of every index, so the update each batch makes touches none of them. The user index
5304
+ -- also serves deleting one user's history.
5305
+ CREATE INDEX idx_activity_visits_started
5306
+ ON activity_visits (project_id, started_at, id);
5307
+ CREATE INDEX idx_activity_visits_user
5308
+ ON activity_visits (project_id, user_id, started_at, id);
5309
+ -- A session's visits, to join them with the session and the executions it authorized.
5310
+ CREATE INDEX idx_activity_visits_session
5311
+ ON activity_visits (project_id, actor_type, session_id);
5312
+
5313
+ CREATE TABLE activity_page_views (
5314
+ project_id TEXT NOT NULL,
5315
+ visit_id TEXT NOT NULL,
5316
+ sequence INTEGER NOT NULL CHECK (sequence > 0),
5317
+ id TEXT NOT NULL,
5318
+ route TEXT NOT NULL,
5319
+ received_at TEXT NOT NULL,
5320
+ attribution TEXT CHECK (attribution IS NULL OR json_type(attribution) = 'object'),
5321
+ PRIMARY KEY (project_id, visit_id, sequence),
5322
+ FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
5323
+ );
5324
+
5325
+ -- Finds a retried page view by the id its browser chose.
5326
+ CREATE INDEX idx_activity_page_views_id ON activity_page_views (project_id, id);
5327
+ `;
5328
+
5329
+ // src/migrations/065-activity-timeline.sql
5330
+ var _065_activity_timeline_default = `-- A visit's timeline reads what its session started: the roots it authorized in a window, then
5331
+ -- everything they caused. A signed-in session is the credential of its roots, a shared link's
5332
+ -- session their delegation; a child names its parent. Agent executions copy their parent's
5333
+ -- session: the root indexes hold only executions without a parent, so they never see those, and
5334
+ -- end with \`id\` so the root read's order needs no sort.
5335
+ CREATE INDEX idx_executions_session_roots
5336
+ ON executions (project_id, authority_session_id, created_at, id)
5337
+ WHERE authority_session_id IS NOT NULL AND parent_execution_id IS NULL;
5338
+ CREATE INDEX idx_executions_delegation_roots
5339
+ ON executions (project_id, authority_delegation_session_id, created_at, id)
5340
+ WHERE authority_delegation_session_id IS NOT NULL AND parent_execution_id IS NULL;
5341
+ CREATE INDEX idx_executions_parent
5342
+ ON executions (project_id, parent_execution_id)
5343
+ WHERE parent_execution_id IS NOT NULL;
5344
+
5345
+ -- A user's connection history lists all their sessions, revoked and expired ones too, newest
5346
+ -- first.
5347
+ CREATE INDEX idx_auth_sessions_user_created
5348
+ ON auth_sessions (project_id, user_id, created_at, id);
5349
+ `;
5350
+
5351
+ // src/migrations/066-activity-interactions.sql
5352
+ var _066_activity_interactions_default = `-- Interactions an app marks, such as a button: their name and route, never a value. They share
5353
+ -- the visit's sequence with its page views, so a visit reads both in the order they happened, and
5354
+ -- cascade with the visit, which counts them as it counts its page views.
5355
+ ALTER TABLE activity_visits
5356
+ ADD COLUMN interactions INTEGER NOT NULL DEFAULT 0 CHECK (interactions >= 0);
5357
+
5358
+ CREATE TABLE activity_interactions (
5359
+ project_id TEXT NOT NULL,
5360
+ visit_id TEXT NOT NULL,
5361
+ sequence INTEGER NOT NULL CHECK (sequence > 0),
5362
+ id TEXT NOT NULL,
5363
+ name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 100),
5364
+ route TEXT NOT NULL,
5365
+ received_at TEXT NOT NULL,
5366
+ PRIMARY KEY (project_id, visit_id, sequence),
5367
+ FOREIGN KEY (project_id, visit_id) REFERENCES activity_visits (project_id, id) ON DELETE CASCADE
5368
+ );
5369
+
5370
+ -- Finds a retried interaction by the id its browser chose.
5371
+ CREATE INDEX idx_activity_interactions_id ON activity_interactions (project_id, id);
5372
+ `;
5373
+
5145
5374
  // src/migrations.ts
5146
5375
  var MIGRATIONS_TABLE_SQL = `
5147
5376
  CREATE TABLE IF NOT EXISTS sixb_migrations (
@@ -5249,7 +5478,13 @@ var sqliteStorageMigrations = defineMigrations({
5249
5478
  sqliteSql("057-reranking-model-kind", _057_reranking_model_kind_default),
5250
5479
  sqliteSql("058-file-download-grants", _058_file_download_grants_default),
5251
5480
  sqliteSql("059-drained-source-versions", _059_drained_source_versions_default),
5252
- sqliteSql("060-terminal-action-runs", _060_terminal_action_runs_default)
5481
+ sqliteSql("060-terminal-action-runs", _060_terminal_action_runs_default),
5482
+ sqliteSql("061-drop-auth-audience", _061_drop_auth_audience_default),
5483
+ sqliteSql("062-artifacts", _062_artifacts_default),
5484
+ sqliteSql("063-drop-output-diagnostics", _063_drop_output_diagnostics_default),
5485
+ sqliteSql("064-activity", _064_activity_default),
5486
+ sqliteSql("065-activity-timeline", _065_activity_timeline_default),
5487
+ sqliteSql("066-activity-interactions", _066_activity_interactions_default)
5253
5488
  ]
5254
5489
  });
5255
5490
  function sqliteStoragePath(basePath) {
@@ -5263,6 +5498,20 @@ function sqliteSql(id, sqlText) {
5263
5498
  function sqliteStep(id, up, options = {}) {
5264
5499
  return step(id, up, options);
5265
5500
  }
5501
+ var freshSchema;
5502
+ function freshSqliteDatabase() {
5503
+ if (!freshSchema) {
5504
+ const template = new Database(":memory:");
5505
+ try {
5506
+ template.run("PRAGMA foreign_keys = ON");
5507
+ installFreshSqliteSchema(template);
5508
+ freshSchema = template.serialize();
5509
+ } finally {
5510
+ template.close();
5511
+ }
5512
+ }
5513
+ return Database.deserialize(freshSchema);
5514
+ }
5266
5515
  function installFreshSqliteSchema(db) {
5267
5516
  for (const migration of sqliteStorageMigrations.steps) {
5268
5517
  const installed = migration.up(db);
@@ -5480,28 +5729,20 @@ function rollbackQuietly(db) {
5480
5729
  }
5481
5730
 
5482
5731
  // src/transactions.ts
5483
- import { Database as SqliteDatabase } from "bun:sqlite";
5484
- import { mkdirSync as mkdirSync2 } from "fs";
5485
- import { dirname as dirname2 } from "path";
5486
5732
  function openSqliteStoreConnection(options = {}) {
5487
5733
  if (options.connection) {
5488
5734
  return {
5489
5735
  db: options.connection.db,
5490
- ownsConnection: false,
5491
- installFreshSchema: false
5736
+ ownsConnection: false
5492
5737
  };
5493
5738
  }
5494
5739
  const path = options.path ?? ":memory:";
5495
5740
  if (path !== ":memory:" && !options.readonly)
5496
5741
  mkdirSync2(dirname2(path), { recursive: true });
5497
- const db = new SqliteDatabase(path, options.readonly ? { readonly: true } : undefined);
5742
+ const db = path === ":memory:" && !options.readonly ? freshSqliteDatabase() : new SqliteDatabase(path, options.readonly ? { readonly: true } : undefined);
5498
5743
  db.run("PRAGMA foreign_keys = ON");
5499
5744
  db.run("PRAGMA busy_timeout = 5000");
5500
- return {
5501
- db,
5502
- ownsConnection: true,
5503
- installFreshSchema: path === ":memory:" && !options.readonly
5504
- };
5745
+ return { db, ownsConnection: true };
5505
5746
  }
5506
5747
  function closeSqliteStoreConnection(connection) {
5507
5748
  if (connection.ownsConnection) {
@@ -5582,9 +5823,6 @@ class SqliteActionRunStorage {
5582
5823
  this.connection = openSqliteStoreConnection(options);
5583
5824
  this.db = this.connection.db;
5584
5825
  this.executions = options.executions;
5585
- if (this.connection.installFreshSchema) {
5586
- installFreshSqliteSchema(this.db);
5587
- }
5588
5826
  }
5589
5827
  async record(input) {
5590
5828
  const record = normalizeRecordActionRunInput(input);
@@ -5646,6 +5884,23 @@ class SqliteActionRunStorage {
5646
5884
  const row = this.db.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
5647
5885
  return row ? rowToActionRunRecord(row) : null;
5648
5886
  }
5887
+ async getByExecutionIds(params) {
5888
+ const executionIds = [...new Set(params.executionIds)];
5889
+ if (executionIds.length === 0) {
5890
+ return [];
5891
+ }
5892
+ const rows = this.db.query(`
5893
+ SELECT *
5894
+ FROM action_runs
5895
+ WHERE project_id = ?
5896
+ AND execution_id IN (SELECT value FROM json_each(?))
5897
+ `).all(params.projectId, JSON.stringify(executionIds));
5898
+ const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
5899
+ return executionIds.flatMap((executionId) => {
5900
+ const row = byExecution.get(executionId);
5901
+ return row ? [rowToActionRunRecord(row)] : [];
5902
+ });
5903
+ }
5649
5904
  async list(input) {
5650
5905
  if (input.statuses && input.statuses.length === 0 || input.actionIds?.length === 0) {
5651
5906
  return {
@@ -5817,6 +6072,342 @@ function rowToActionSubject(row) {
5817
6072
  };
5818
6073
  }
5819
6074
 
6075
+ // src/activity-storage.ts
6076
+ import {
6077
+ ACTIVITY_MAX_VISIT_MS,
6078
+ activityActor,
6079
+ activityCalendarDays,
6080
+ parseActivityAttribution,
6081
+ planActivityAppend,
6082
+ serializeActivityAttribution,
6083
+ validateActivityAppend
6084
+ } from "@sixb/core/internal/storage";
6085
+ var VISIT_COLUMNS = `
6086
+ v.id, v.actor_type, v.user_id, v.grant_id, v.started_at, v.last_seen_at, v.page_views,
6087
+ v.interactions, v.attribution
6088
+ `;
6089
+ var NO_CURSOR = { at: "9999-12-31T23:59:59.999Z", id: "" };
6090
+
6091
+ class SqliteActivityStorage {
6092
+ db;
6093
+ constructor(connection) {
6094
+ this.db = connection.db;
6095
+ }
6096
+ async append(input) {
6097
+ validateActivityAppend(input);
6098
+ return runImmediateTransaction(this.db, () => {
6099
+ const plan = planActivityAppend(input, this.source(input), this.storedEvents(input));
6100
+ if (plan.status !== "append") {
6101
+ return { status: plan.status === "duplicate" ? "accepted" : "throttled", accepted: 0 };
6102
+ }
6103
+ const { state } = plan;
6104
+ const lastSeenAt = iso(state.lastSeenAt);
6105
+ if (plan.newVisit) {
6106
+ this.run(`
6107
+ INSERT INTO activity_visits (
6108
+ project_id, id, actor_type, user_id, grant_id, session_id,
6109
+ started_at, last_seen_at, page_views, interactions, attribution
6110
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6111
+ `, input.projectId, state.visitId, input.actor.type, input.actor.type === "user" ? input.actor.userId : null, input.actor.type === "share" ? input.actor.grantId : null, input.sessionId, lastSeenAt, lastSeenAt, plan.pageViews.length, plan.interactions.length, plan.newVisit.attribution === null ? null : serializeActivityAttribution(plan.newVisit.attribution));
6112
+ } else {
6113
+ this.run(`
6114
+ UPDATE activity_visits
6115
+ SET
6116
+ last_seen_at = ?,
6117
+ page_views = page_views + ?,
6118
+ interactions = interactions + ?
6119
+ WHERE project_id = ? AND id = ?
6120
+ `, lastSeenAt, plan.pageViews.length, plan.interactions.length, input.projectId, state.visitId);
6121
+ }
6122
+ for (const pageView of plan.pageViews) {
6123
+ this.run(`
6124
+ INSERT INTO activity_page_views (
6125
+ project_id, visit_id, sequence, id, route, received_at, attribution
6126
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
6127
+ `, input.projectId, pageView.visitId, pageView.sequence, pageView.id, pageView.route, pageView.receivedAt.toISOString(), serializeActivityAttribution(pageView.attribution));
6128
+ }
6129
+ for (const interaction of plan.interactions) {
6130
+ this.run(`
6131
+ INSERT INTO activity_interactions (
6132
+ project_id, visit_id, sequence, id, name, route, received_at
6133
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
6134
+ `, input.projectId, interaction.visitId, interaction.sequence, interaction.id, interaction.name, interaction.route, interaction.receivedAt.toISOString());
6135
+ }
6136
+ this.run(`
6137
+ INSERT INTO activity_sources (
6138
+ project_id, actor_type, session_id, visit_id, last_seen_at, sequence,
6139
+ window_start, window_count
6140
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
6141
+ ON CONFLICT (project_id, actor_type, session_id) DO UPDATE SET
6142
+ visit_id = excluded.visit_id,
6143
+ last_seen_at = excluded.last_seen_at,
6144
+ sequence = excluded.sequence,
6145
+ window_start = excluded.window_start,
6146
+ window_count = excluded.window_count
6147
+ `, input.projectId, input.actor.type, input.sessionId, state.visitId, lastSeenAt, state.sequence, iso(state.windowStart), state.windowCount);
6148
+ return { status: "accepted", accepted: plan.accepted };
6149
+ });
6150
+ }
6151
+ async list(input) {
6152
+ const period = overlapping(input);
6153
+ const before = cursorOf(input.before);
6154
+ const rows = this.all(`
6155
+ SELECT ${VISIT_COLUMNS}
6156
+ FROM activity_visits v
6157
+ WHERE ${period.where}
6158
+ AND (v.last_seen_at < ? OR (v.last_seen_at = ? AND v.id < ?))
6159
+ ORDER BY v.last_seen_at DESC, v.id DESC
6160
+ LIMIT ?
6161
+ `, ...period.params, before.at, before.at, before.id, input.limit + 1);
6162
+ return {
6163
+ visits: rows.slice(0, input.limit).map(visitFromRow),
6164
+ hasMore: rows.length > input.limit
6165
+ };
6166
+ }
6167
+ async users(input) {
6168
+ const period = overlapping(input);
6169
+ const before = cursorOf(input.before);
6170
+ const rows = this.all(`
6171
+ SELECT
6172
+ v.user_id,
6173
+ MAX(v.last_seen_at) AS last_seen_at,
6174
+ COUNT(DISTINCT v.id) AS visits,
6175
+ COUNT(p.id) AS page_views
6176
+ FROM activity_visits v
6177
+ LEFT JOIN activity_page_views p
6178
+ ON p.project_id = v.project_id
6179
+ AND p.visit_id = v.id
6180
+ AND p.received_at >= ?
6181
+ AND p.received_at < ?
6182
+ WHERE ${period.where} AND v.actor_type = 'user'
6183
+ GROUP BY v.user_id
6184
+ HAVING MAX(v.last_seen_at) < ? OR (MAX(v.last_seen_at) = ? AND v.user_id < ?)
6185
+ ORDER BY last_seen_at DESC, v.user_id DESC
6186
+ LIMIT ?
6187
+ `, input.from.toISOString(), input.to.toISOString(), ...period.params, before.at, before.at, before.id, input.limit + 1);
6188
+ return {
6189
+ users: rows.slice(0, input.limit).map((row) => ({
6190
+ userId: row.user_id,
6191
+ lastSeenAt: new Date(row.last_seen_at),
6192
+ visits: Number(row.visits),
6193
+ pageViews: Number(row.page_views)
6194
+ })),
6195
+ hasMore: rows.length > input.limit
6196
+ };
6197
+ }
6198
+ async summarize(input) {
6199
+ const period = overlapping(input);
6200
+ const [row] = this.all(`
6201
+ SELECT
6202
+ COUNT(DISTINCT v.user_id) AS users,
6203
+ COUNT(DISTINCT v.id) AS visits,
6204
+ COUNT(DISTINCT CASE WHEN v.actor_type = 'share' THEN v.id END) AS shared_visits,
6205
+ COUNT(p.id) AS page_views
6206
+ FROM activity_visits v
6207
+ LEFT JOIN activity_page_views p
6208
+ ON p.project_id = v.project_id
6209
+ AND p.visit_id = v.id
6210
+ AND p.received_at >= ?
6211
+ AND p.received_at < ?
6212
+ WHERE ${period.where}
6213
+ `, input.from.toISOString(), input.to.toISOString(), ...period.params);
6214
+ return {
6215
+ users: Number(row?.users ?? 0),
6216
+ visits: Number(row?.visits ?? 0),
6217
+ sharedVisits: Number(row?.shared_visits ?? 0),
6218
+ pageViews: Number(row?.page_views ?? 0)
6219
+ };
6220
+ }
6221
+ async calendar(input) {
6222
+ const period = overlapping(input);
6223
+ const visits = this.all(`
6224
+ SELECT v.started_at, v.last_seen_at
6225
+ FROM activity_visits v
6226
+ WHERE ${period.where}
6227
+ `, ...period.params);
6228
+ const days = this.all(`
6229
+ SELECT substr(p.received_at, 1, 10) AS day, COUNT(*) AS page_views
6230
+ FROM activity_visits v
6231
+ JOIN activity_page_views p ON p.project_id = v.project_id AND p.visit_id = v.id
6232
+ WHERE ${period.where}
6233
+ AND p.received_at >= ?
6234
+ AND p.received_at < ?
6235
+ GROUP BY day
6236
+ `, ...period.params, input.from.toISOString(), input.to.toISOString());
6237
+ return activityCalendarDays(input, visits.map((visit) => ({
6238
+ startedAt: new Date(visit.started_at),
6239
+ lastSeenAt: new Date(visit.last_seen_at)
6240
+ })), new Map(days.map((row) => [row.day, Number(row.page_views)])));
6241
+ }
6242
+ async read(input) {
6243
+ const [visit] = this.all(`
6244
+ SELECT ${VISIT_COLUMNS}, v.session_id
6245
+ FROM activity_visits v
6246
+ WHERE v.project_id = ? AND v.id = ?
6247
+ `, input.projectId, input.id);
6248
+ if (!visit) {
6249
+ return null;
6250
+ }
6251
+ const rows = this.all(`
6252
+ SELECT id, visit_id, sequence, route, NULL AS name, received_at
6253
+ FROM activity_page_views
6254
+ WHERE project_id = ? AND visit_id = ?
6255
+ UNION ALL
6256
+ SELECT id, visit_id, sequence, route, name, received_at
6257
+ FROM activity_interactions
6258
+ WHERE project_id = ? AND visit_id = ?
6259
+ ORDER BY sequence
6260
+ LIMIT ?
6261
+ `, input.projectId, input.id, input.projectId, input.id, input.limit + 1);
6262
+ const pageViews = [];
6263
+ const interactions = [];
6264
+ for (const row of rows.slice(0, input.limit)) {
6265
+ if (row.name === null) {
6266
+ pageViews.push(pageViewFromRow(row));
6267
+ } else {
6268
+ interactions.push({ ...pageViewFromRow(row), name: row.name });
6269
+ }
6270
+ }
6271
+ return {
6272
+ visit: visitFromRow(visit),
6273
+ sessionId: visit.session_id,
6274
+ pageViews,
6275
+ interactions,
6276
+ hasMore: rows.length > input.limit
6277
+ };
6278
+ }
6279
+ async sessionVisits(input) {
6280
+ if (input.sessionIds.length === 0) {
6281
+ return [];
6282
+ }
6283
+ const rows = this.all(`
6284
+ SELECT *
6285
+ FROM (
6286
+ SELECT
6287
+ ${VISIT_COLUMNS},
6288
+ v.session_id,
6289
+ ROW_NUMBER() OVER (
6290
+ PARTITION BY v.session_id ORDER BY v.started_at DESC, v.id DESC
6291
+ ) AS position,
6292
+ COUNT(*) OVER (PARTITION BY v.session_id) AS total
6293
+ FROM activity_visits v
6294
+ WHERE v.project_id = ?
6295
+ AND v.actor_type = 'user'
6296
+ AND v.session_id IN (SELECT value FROM json_each(?))
6297
+ AND v.user_id = ?
6298
+ )
6299
+ WHERE position <= ?
6300
+ ORDER BY session_id, position
6301
+ `, input.projectId, JSON.stringify(input.sessionIds), input.userId, input.limit);
6302
+ const bySession = new Map;
6303
+ for (const row of rows) {
6304
+ const session = bySession.get(row.session_id) ?? { visits: Number(row.total), latest: [] };
6305
+ session.latest.push(visitFromRow(row));
6306
+ bySession.set(row.session_id, session);
6307
+ }
6308
+ return [...bySession].map(([sessionId, session]) => ({ sessionId, ...session }));
6309
+ }
6310
+ source(input) {
6311
+ const [row] = this.all(`
6312
+ SELECT
6313
+ s.visit_id, v.started_at AS visit_started_at, s.last_seen_at, s.sequence,
6314
+ s.window_start, s.window_count
6315
+ FROM activity_sources s
6316
+ LEFT JOIN activity_visits v ON v.project_id = s.project_id AND v.id = s.visit_id
6317
+ WHERE s.project_id = ? AND s.actor_type = ? AND s.session_id = ?
6318
+ `, input.projectId, input.actor.type, input.sessionId);
6319
+ if (!row) {
6320
+ return;
6321
+ }
6322
+ return {
6323
+ visitId: row.visit_id,
6324
+ visitStartedAt: row.visit_started_at === null ? null : Date.parse(row.visit_started_at),
6325
+ lastSeenAt: Date.parse(row.last_seen_at),
6326
+ sequence: Number(row.sequence),
6327
+ windowStart: Date.parse(row.window_start),
6328
+ windowCount: Number(row.window_count)
6329
+ };
6330
+ }
6331
+ storedEvents(input) {
6332
+ const ids = JSON.stringify(input.events.map((event) => event.id));
6333
+ const rows = this.all(`
6334
+ SELECT p.id, p.route, NULL AS name, p.attribution
6335
+ FROM activity_page_views p
6336
+ JOIN activity_visits v ON v.project_id = p.project_id AND v.id = p.visit_id
6337
+ WHERE p.project_id = ?
6338
+ AND p.id IN (SELECT value FROM json_each(?))
6339
+ AND v.actor_type = ?
6340
+ AND v.session_id = ?
6341
+ UNION ALL
6342
+ SELECT i.id, i.route, i.name, NULL AS attribution
6343
+ FROM activity_interactions i
6344
+ JOIN activity_visits v ON v.project_id = i.project_id AND v.id = i.visit_id
6345
+ WHERE i.project_id = ?
6346
+ AND i.id IN (SELECT value FROM json_each(?))
6347
+ AND v.actor_type = ?
6348
+ AND v.session_id = ?
6349
+ `, input.projectId, ids, input.actor.type, input.sessionId, input.projectId, ids, input.actor.type, input.sessionId);
6350
+ return rows.map((row) => {
6351
+ if (row.name !== null) {
6352
+ return { id: row.id, route: row.route, name: row.name };
6353
+ }
6354
+ const attribution = parseActivityAttribution(row.attribution);
6355
+ return { id: row.id, route: row.route, ...attribution ? { attribution } : {} };
6356
+ });
6357
+ }
6358
+ run(sql, ...params) {
6359
+ this.db.query(sql).run(...params);
6360
+ }
6361
+ all(sql, ...params) {
6362
+ return this.db.query(sql).all(...params);
6363
+ }
6364
+ }
6365
+ function overlapping(input) {
6366
+ const conditions = [
6367
+ "v.project_id = ?",
6368
+ "v.started_at >= ?",
6369
+ "v.started_at < ?",
6370
+ "v.last_seen_at >= ?"
6371
+ ];
6372
+ const params = [
6373
+ input.projectId,
6374
+ new Date(input.from.getTime() - ACTIVITY_MAX_VISIT_MS).toISOString(),
6375
+ input.to.toISOString(),
6376
+ input.from.toISOString()
6377
+ ];
6378
+ if (input.userId !== undefined) {
6379
+ conditions.push("v.user_id = ?");
6380
+ params.push(input.userId);
6381
+ }
6382
+ return { where: conditions.join(" AND "), params };
6383
+ }
6384
+ function cursorOf(cursor) {
6385
+ return cursor ? { at: cursor.at.toISOString(), id: cursor.id } : NO_CURSOR;
6386
+ }
6387
+ function visitFromRow(row) {
6388
+ return {
6389
+ id: row.id,
6390
+ actor: activityActor(row.actor_type, row.user_id, row.grant_id),
6391
+ attribution: parseActivityAttribution(row.attribution),
6392
+ startedAt: new Date(row.started_at),
6393
+ lastSeenAt: new Date(row.last_seen_at),
6394
+ pageViews: Number(row.page_views),
6395
+ interactions: Number(row.interactions)
6396
+ };
6397
+ }
6398
+ function pageViewFromRow(row) {
6399
+ return {
6400
+ id: row.id,
6401
+ visitId: row.visit_id,
6402
+ sequence: Number(row.sequence),
6403
+ route: row.route,
6404
+ receivedAt: new Date(row.received_at)
6405
+ };
6406
+ }
6407
+ function iso(epochMs) {
6408
+ return new Date(epochMs).toISOString();
6409
+ }
6410
+
5820
6411
  // src/agents/checkpoints.ts
5821
6412
  import {
5822
6413
  agentContextCheckpointMatchesCreateInput,
@@ -6599,6 +7190,23 @@ class SqliteAgentThreadStore {
6599
7190
  const row = this.db.query("SELECT * FROM agent_threads WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
6600
7191
  return row ? rowToThreadRecord(row) : null;
6601
7192
  }
7193
+ async getByIds(params) {
7194
+ const ids = [...new Set(params.ids)];
7195
+ if (ids.length === 0) {
7196
+ return [];
7197
+ }
7198
+ const rows = this.db.query(`
7199
+ SELECT *
7200
+ FROM agent_threads
7201
+ WHERE project_id = ?
7202
+ AND id IN (SELECT value FROM json_each(?))
7203
+ `).all(params.projectId, JSON.stringify(ids));
7204
+ const byId = new Map(rows.map((row) => [row.id, row]));
7205
+ return ids.flatMap((id) => {
7206
+ const row = byId.get(id);
7207
+ return row ? [rowToThreadRecord(row)] : [];
7208
+ });
7209
+ }
6602
7210
  async list(input) {
6603
7211
  if (input.statuses !== undefined && input.statuses.length === 0) {
6604
7212
  return { threads: [], hasMore: false, total: 0 };
@@ -6643,9 +7251,6 @@ class SqliteAgentStorage {
6643
7251
  connection;
6644
7252
  constructor(options) {
6645
7253
  this.connection = openSqliteStoreConnection(options);
6646
- if (this.connection.installFreshSchema) {
6647
- installFreshSqliteSchema(this.connection.db);
6648
- }
6649
7254
  this.threads = new SqliteAgentThreadStore(this.connection.db);
6650
7255
  this.runs = new SqliteAgentRunStore(this.connection.db, options.executions);
6651
7256
  this.messages = new SqliteAgentMessageStore(this.connection.db);
@@ -6777,9 +7382,6 @@ class SqliteAiUsageStorage {
6777
7382
  connection;
6778
7383
  constructor(options = {}) {
6779
7384
  this.connection = openSqliteStoreConnection(options);
6780
- if (this.connection.installFreshSchema) {
6781
- installFreshSqliteSchema(this.connection.db);
6782
- }
6783
7385
  }
6784
7386
  async recordModelCall(input) {
6785
7387
  const record = normalizeAiModelCallRecord(input);
@@ -7511,8 +8113,6 @@ class SqliteAiLimitStorage {
7511
8113
  connection;
7512
8114
  constructor(options = {}) {
7513
8115
  this.connection = openSqliteStoreConnection(options);
7514
- if (this.connection.installFreshSchema)
7515
- installFreshSqliteSchema(this.connection.db);
7516
8116
  }
7517
8117
  async createPolicy(input) {
7518
8118
  const policy = normalizeCreateAiLimitPolicy(input);
@@ -8045,6 +8645,185 @@ function isConstraintError(error) {
8045
8645
  return error instanceof Error && /constraint/i.test(error.message);
8046
8646
  }
8047
8647
 
8648
+ // src/artifact-storage.ts
8649
+ import {
8650
+ artifactStorageErrors,
8651
+ parseArtifactFrameGrantRow,
8652
+ parseArtifactRow,
8653
+ parseArtifactVersionRow
8654
+ } from "@sixb/core/internal/storage";
8655
+ var ARTIFACT_COLUMNS = `
8656
+ project_id AS projectId, id, kind, title,
8657
+ owner_principal_type AS ownerPrincipalType, owner_principal_id AS ownerPrincipalId,
8658
+ latest_version AS latestVersion, created_at AS createdAt, updated_at AS updatedAt
8659
+ `;
8660
+ var VERSION_COLUMNS = `
8661
+ project_id AS projectId, artifact_id AS artifactId, version, title, entry, files, outline, refs,
8662
+ author_principal_type AS authorPrincipalType, author_principal_id AS authorPrincipalId,
8663
+ origin_thread_id AS originThreadId, origin_run_id AS originRunId, created_at AS createdAt
8664
+ `;
8665
+ var FRAME_GRANT_COLUMNS = `
8666
+ project_id AS projectId, token_hash AS tokenHash, artifact_id AS artifactId, version, targets,
8667
+ created_at AS createdAt, expires_at AS expiresAt
8668
+ `;
8669
+
8670
+ class SqliteArtifactStorage {
8671
+ db;
8672
+ constructor(connection) {
8673
+ this.db = connection.db;
8674
+ }
8675
+ async create(input) {
8676
+ return runImmediateTransaction(this.db, () => {
8677
+ const createdAt = input.version.createdAt.toISOString();
8678
+ const artifact = this.db.query(`INSERT INTO artifacts (
8679
+ project_id, id, kind, title, owner_principal_type, owner_principal_id,
8680
+ latest_version, next_version, created_at, updated_at
8681
+ ) VALUES (?, ?, ?, ?, ?, ?, 1, 2, ?, ?)
8682
+ ON CONFLICT (project_id, id) DO NOTHING
8683
+ RETURNING ${ARTIFACT_COLUMNS}`).get(input.projectId, input.id, input.kind, input.version.title, input.owner.type, input.owner.id, createdAt, createdAt);
8684
+ if (!artifact)
8685
+ throw artifactStorageErrors.exists(input.id);
8686
+ return {
8687
+ artifact: parseArtifactRow(artifact),
8688
+ version: this.insertVersion(input.projectId, input.id, 1, input.version)
8689
+ };
8690
+ });
8691
+ }
8692
+ async publish(input) {
8693
+ return runImmediateTransaction(this.db, () => {
8694
+ const current = this.db.query("SELECT kind, next_version AS nextVersion FROM artifacts WHERE project_id = ? AND id = ?").get(input.projectId, input.artifactId);
8695
+ if (!current)
8696
+ throw artifactStorageErrors.notFound(input.artifactId);
8697
+ if (current.kind !== input.kind) {
8698
+ throw artifactStorageErrors.kindMismatch(input.artifactId, current.kind, input.kind);
8699
+ }
8700
+ const version = this.insertVersion(input.projectId, input.artifactId, current.nextVersion, input.version);
8701
+ const artifact = this.db.query(`UPDATE artifacts
8702
+ SET title = ?, latest_version = ?, next_version = ?, updated_at = ?
8703
+ WHERE project_id = ? AND id = ?
8704
+ RETURNING ${ARTIFACT_COLUMNS}`).get(input.version.title, version.version, version.version + 1, input.version.createdAt.toISOString(), input.projectId, input.artifactId);
8705
+ return { artifact: parseArtifactRow(artifact), version };
8706
+ });
8707
+ }
8708
+ async get(input) {
8709
+ const row = this.db.query(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts WHERE project_id = ? AND id = ?`).get(input.projectId, input.id);
8710
+ return row ? parseArtifactRow(row) : null;
8711
+ }
8712
+ async list(input) {
8713
+ const where = ["a.project_id = ?"];
8714
+ const params = [input.projectId];
8715
+ if (input.owner) {
8716
+ where.push("a.owner_principal_type = ? AND a.owner_principal_id = ?");
8717
+ params.push(input.owner.type, input.owner.id);
8718
+ }
8719
+ if (input.originThreadId !== undefined) {
8720
+ where.push(`EXISTS (
8721
+ SELECT 1 FROM artifact_versions v
8722
+ WHERE v.project_id = a.project_id AND v.artifact_id = a.id AND v.origin_thread_id = ?
8723
+ )`);
8724
+ params.push(input.originThreadId);
8725
+ }
8726
+ if (input.kind !== undefined) {
8727
+ where.push("a.kind = ?");
8728
+ params.push(input.kind);
8729
+ }
8730
+ const clause = where.join(" AND ");
8731
+ const { total } = this.db.query(`SELECT COUNT(*) AS total FROM artifacts a WHERE ${clause}`).get(...params);
8732
+ const offset = input.offset ?? 0;
8733
+ const rows = this.db.query(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts a WHERE ${clause}
8734
+ ORDER BY a.updated_at DESC, a.id ASC LIMIT ? OFFSET ?`).all(...params, input.limit ?? -1, offset);
8735
+ return {
8736
+ artifacts: rows.map(parseArtifactRow),
8737
+ hasMore: offset + rows.length < total,
8738
+ total
8739
+ };
8740
+ }
8741
+ async getVersion(input) {
8742
+ const row = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions
8743
+ WHERE project_id = ? AND artifact_id = ? AND version = ?`).get(input.projectId, input.artifactId, input.version);
8744
+ return row ? parseArtifactVersionRow(row) : null;
8745
+ }
8746
+ async listVersions(input) {
8747
+ const { total } = this.db.query("SELECT COUNT(*) AS total FROM artifact_versions WHERE project_id = ? AND artifact_id = ?").get(input.projectId, input.artifactId);
8748
+ const offset = input.offset ?? 0;
8749
+ const rows = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions
8750
+ WHERE project_id = ? AND artifact_id = ?
8751
+ ORDER BY version DESC LIMIT ? OFFSET ?`).all(input.projectId, input.artifactId, input.limit ?? -1, offset);
8752
+ return {
8753
+ versions: rows.map(parseArtifactVersionRow),
8754
+ hasMore: offset + rows.length < total,
8755
+ total
8756
+ };
8757
+ }
8758
+ async listReferencingVersions(input) {
8759
+ const prefix = `artifact:${input.artifactId}`;
8760
+ const rows = this.db.query(`SELECT ${VERSION_COLUMNS} FROM artifact_versions v
8761
+ WHERE (project_id, artifact_id, version) IN (
8762
+ SELECT project_id, id, latest_version FROM artifacts WHERE project_id = ?
8763
+ ) AND EXISTS (
8764
+ SELECT 1 FROM json_each(v.refs) r
8765
+ WHERE substr(json_extract(r.value, '$.address'), 1, ?) = ?
8766
+ AND substr(json_extract(r.value, '$.address'), ? + 1, 1) IN ('', '@', '/', '#')
8767
+ )
8768
+ ORDER BY artifact_id`).all(input.projectId, prefix.length, prefix, prefix.length);
8769
+ return rows.map(parseArtifactVersionRow);
8770
+ }
8771
+ async createFrameGrant(record) {
8772
+ runImmediateTransaction(this.db, () => {
8773
+ const exists = this.db.query("SELECT 1 FROM artifacts WHERE project_id = ? AND id = ?").get(record.projectId, record.artifactId);
8774
+ if (!exists)
8775
+ throw artifactStorageErrors.notFound(record.artifactId);
8776
+ this.db.query(`INSERT INTO artifact_frame_grants
8777
+ (project_id, token_hash, artifact_id, version, targets, created_at, expires_at)
8778
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.projectId, record.tokenHash, record.artifactId, record.version, JSON.stringify(record.targets), record.createdAt.toISOString(), record.expiresAt.toISOString());
8779
+ });
8780
+ }
8781
+ async findFrameGrant(input) {
8782
+ const row = this.db.query(`SELECT ${FRAME_GRANT_COLUMNS} FROM artifact_frame_grants
8783
+ WHERE project_id = ? AND token_hash = ?`).get(input.projectId, input.tokenHash);
8784
+ return row ? parseArtifactFrameGrantRow(row) : null;
8785
+ }
8786
+ async deleteExpiredFrameGrants(input) {
8787
+ return this.db.query(`DELETE FROM artifact_frame_grants WHERE rowid IN (
8788
+ SELECT rowid FROM artifact_frame_grants
8789
+ WHERE project_id = ? AND expires_at < ?
8790
+ ORDER BY expires_at LIMIT ?
8791
+ )`).run(input.projectId, input.expiredBefore.toISOString(), input.limit).changes;
8792
+ }
8793
+ async delete(input) {
8794
+ return this.db.query("DELETE FROM artifacts WHERE project_id = ? AND id = ?").run(input.projectId, input.id).changes > 0;
8795
+ }
8796
+ async deleteVersionsByOrigin(input) {
8797
+ return runImmediateTransaction(this.db, () => {
8798
+ const affected = this.db.query(`SELECT DISTINCT artifact_id AS artifactId FROM artifact_versions
8799
+ WHERE project_id = ? AND origin_run_id = ?`).all(input.projectId, input.runId);
8800
+ const deleted = this.db.query("DELETE FROM artifact_versions WHERE project_id = ? AND origin_run_id = ?").run(input.projectId, input.runId).changes;
8801
+ for (const { artifactId } of affected) {
8802
+ this.repointLatest(input.projectId, artifactId);
8803
+ }
8804
+ return deleted;
8805
+ });
8806
+ }
8807
+ repointLatest(projectId, artifactId) {
8808
+ const latest = this.db.query(`SELECT version, title, created_at AS createdAt FROM artifact_versions
8809
+ WHERE project_id = ? AND artifact_id = ? ORDER BY version DESC LIMIT 1`).get(projectId, artifactId);
8810
+ if (!latest) {
8811
+ this.db.query("DELETE FROM artifacts WHERE project_id = ? AND id = ?").run(projectId, artifactId);
8812
+ return;
8813
+ }
8814
+ this.db.query(`UPDATE artifacts SET title = ?, latest_version = ?, updated_at = ?
8815
+ WHERE project_id = ? AND id = ?`).run(latest.title, latest.version, latest.createdAt, projectId, artifactId);
8816
+ }
8817
+ insertVersion(projectId, artifactId, version, input) {
8818
+ const row = this.db.query(`INSERT INTO artifact_versions (
8819
+ project_id, artifact_id, version, title, entry, files, outline, refs,
8820
+ author_principal_type, author_principal_id, origin_thread_id, origin_run_id, created_at
8821
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8822
+ RETURNING ${VERSION_COLUMNS}`).get(projectId, artifactId, version, input.title, input.entry, JSON.stringify(input.files), JSON.stringify(input.outline), JSON.stringify(input.references), input.author.type, input.author.id, input.origin?.threadId ?? null, input.origin?.runId ?? null, input.createdAt.toISOString());
8823
+ return parseArtifactVersionRow(row);
8824
+ }
8825
+ }
8826
+
8048
8827
  // src/auth-storage/auth-storage.ts
8049
8828
  import { AuthStorageError as AuthStorageError11 } from "@sixb/core/storage";
8050
8829
 
@@ -8106,7 +8885,6 @@ function rowToSessionRecord(row) {
8106
8885
  projectId: row.project_id,
8107
8886
  userId: row.user_id,
8108
8887
  strategyId: row.strategy_id,
8109
- audience: row.audience,
8110
8888
  tokenHash: row.token_hash,
8111
8889
  createdAt: new Date(row.created_at),
8112
8890
  expiresAt: new Date(row.expires_at),
@@ -8179,7 +8957,6 @@ function rowToMagicLinkRecord(row) {
8179
8957
  id: row.id,
8180
8958
  projectId: row.project_id,
8181
8959
  strategyId: row.strategy_id,
8182
- audience: row.audience,
8183
8960
  email: row.email,
8184
8961
  tokenHash: row.token_hash,
8185
8962
  returnTo: row.return_to ?? undefined,
@@ -8194,7 +8971,6 @@ function rowToOidcAuthorizationAttemptRecord(row) {
8194
8971
  id: row.id,
8195
8972
  projectId: row.project_id,
8196
8973
  strategyId: row.strategy_id,
8197
- audience: row.audience,
8198
8974
  stateHash: row.state_hash,
8199
8975
  nonceHash: row.nonce_hash,
8200
8976
  codeVerifier: row.code_verifier,
@@ -8412,7 +9188,6 @@ function consumeOidcAttempt(db, params) {
8412
9188
  }
8413
9189
  function validateCompleteSessionInput(db, projectId, session) {
8414
9190
  const sessionId = assertNonEmpty(session.id, "Session id");
8415
- assertNonEmpty(session.audience, "Session audience");
8416
9191
  assertNonEmpty(session.tokenHash, "Session token hash");
8417
9192
  assertSessionIdAvailable(db, projectId, sessionId);
8418
9193
  }
@@ -8426,7 +9201,6 @@ function createSession(db, input) {
8426
9201
  const projectId = assertNonEmpty(input.projectId, "Project id");
8427
9202
  const userId = assertNonEmpty(input.userId, "User id");
8428
9203
  const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
8429
- const audience = assertNonEmpty(input.audience, "Session audience");
8430
9204
  const tokenHash = assertNonEmpty(input.tokenHash, "Session token hash");
8431
9205
  assertSessionIdAvailable(db, projectId, id);
8432
9206
  db.query(`
@@ -8435,7 +9209,6 @@ function createSession(db, input) {
8435
9209
  id,
8436
9210
  user_id,
8437
9211
  strategy_id,
8438
- audience,
8439
9212
  token_hash,
8440
9213
  created_at,
8441
9214
  expires_at,
@@ -8445,8 +9218,8 @@ function createSession(db, input) {
8445
9218
  client_name,
8446
9219
  access_expires_at,
8447
9220
  refresh_token_hash
8448
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8449
- `).run(projectId, id, userId, strategyId, audience, tokenHash, toIso(input.createdAt), toIso(input.expiresAt), input.absoluteExpiresAt ? toIso(input.absoluteExpiresAt) : null, input.userAgent ?? null, input.ipAddress ?? null, input.bearer ? assertNonEmpty(input.bearer.clientName, "Session client name") : null, input.bearer ? toIso(input.bearer.accessExpiresAt) : null, input.bearer ? assertNonEmpty(input.bearer.refreshTokenHash, "Refresh token hash") : null);
9221
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9222
+ `).run(projectId, id, userId, strategyId, tokenHash, toIso(input.createdAt), toIso(input.expiresAt), input.absoluteExpiresAt ? toIso(input.absoluteExpiresAt) : null, input.userAgent ?? null, input.ipAddress ?? null, input.bearer ? assertNonEmpty(input.bearer.clientName, "Session client name") : null, input.bearer ? toIso(input.bearer.accessExpiresAt) : null, input.bearer ? assertNonEmpty(input.bearer.refreshTokenHash, "Refresh token hash") : null);
8450
9223
  const session = getSessionById(db, { projectId, id });
8451
9224
  if (!session) {
8452
9225
  throw new AuthStorageError("missing_session", `[Sixb] Failed to load session '${id}' for project '${projectId}'.`);
@@ -8455,30 +9228,25 @@ function createSession(db, input) {
8455
9228
  }
8456
9229
  function revokeActiveSessionsForUser(db, params) {
8457
9230
  const revokedAt = toIso(params.revokedAt);
8458
- const audienceCondition = params.audience === undefined ? "" : "AND audience = ?";
8459
- const selectArgs = params.audience === undefined ? [params.projectId, params.userId, revokedAt, revokedAt] : [params.projectId, params.userId, params.audience, revokedAt, revokedAt];
8460
- const updateArgs = params.audience === undefined ? [revokedAt, params.projectId, params.userId, revokedAt, revokedAt] : [revokedAt, params.projectId, params.userId, params.audience, revokedAt, revokedAt];
8461
9231
  const rows = db.query(`
8462
9232
  SELECT *
8463
9233
  FROM auth_sessions
8464
9234
  WHERE project_id = ?
8465
9235
  AND user_id = ?
8466
- ${audienceCondition}
8467
9236
  AND revoked_at IS NULL
8468
9237
  AND expires_at > ?
8469
9238
  AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
8470
9239
  ORDER BY created_at ASC, id ASC
8471
- `).all(...selectArgs);
9240
+ `).all(params.projectId, params.userId, revokedAt, revokedAt);
8472
9241
  db.query(`
8473
9242
  UPDATE auth_sessions
8474
9243
  SET revoked_at = ?
8475
9244
  WHERE project_id = ?
8476
9245
  AND user_id = ?
8477
- ${audienceCondition}
8478
9246
  AND revoked_at IS NULL
8479
9247
  AND expires_at > ?
8480
9248
  AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
8481
- `).run(...updateArgs);
9249
+ `).run(revokedAt, params.projectId, params.userId, revokedAt, revokedAt);
8482
9250
  return rows.map((row) => rowToSessionRecord({
8483
9251
  ...row,
8484
9252
  revoked_at: revokedAt
@@ -9056,7 +9824,6 @@ class SqliteAuthInvitationStore {
9056
9824
  }
9057
9825
 
9058
9826
  // src/auth-storage/magic-links.ts
9059
- import { resolveAuthSessionAudience } from "@sixb/core";
9060
9827
  import { AuthStorageError as AuthStorageError5 } from "@sixb/core/storage";
9061
9828
  class SqliteAuthMagicLinkStore {
9062
9829
  db;
@@ -9068,7 +9835,6 @@ class SqliteAuthMagicLinkStore {
9068
9835
  const id = assertNonEmpty(input.id, "Magic link id");
9069
9836
  const projectId = assertNonEmpty(input.projectId, "Project id");
9070
9837
  const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
9071
- const audience = resolveAuthSessionAudience(input.audience);
9072
9838
  const email = normalizeEmail(input.email);
9073
9839
  const tokenHash = assertNonEmpty(input.tokenHash, "Magic link token hash");
9074
9840
  if (getMagicLinkRowById(this.db, { projectId, id })) {
@@ -9085,14 +9851,13 @@ class SqliteAuthMagicLinkStore {
9085
9851
  project_id,
9086
9852
  id,
9087
9853
  strategy_id,
9088
- audience,
9089
9854
  email,
9090
9855
  token_hash,
9091
9856
  return_to,
9092
9857
  created_at,
9093
9858
  expires_at
9094
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
9095
- `).run(projectId, id, strategyId, audience, email, tokenHash, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
9859
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
9860
+ `).run(projectId, id, strategyId, email, tokenHash, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
9096
9861
  } catch (error) {
9097
9862
  mapUniqueConstraintError(error, "duplicate_magic_link", `[Sixb] Magic link '${id}' already exists for project '${projectId}'.`);
9098
9863
  }
@@ -9100,7 +9865,6 @@ class SqliteAuthMagicLinkStore {
9100
9865
  id,
9101
9866
  projectId,
9102
9867
  strategyId,
9103
- audience,
9104
9868
  email,
9105
9869
  tokenHash,
9106
9870
  returnTo: input.returnTo,
@@ -9135,7 +9899,6 @@ class SqliteAuthMagicLinkStore {
9135
9899
  }
9136
9900
 
9137
9901
  // src/auth-storage/oidc-attempts.ts
9138
- import { resolveAuthSessionAudience as resolveAuthSessionAudience2 } from "@sixb/core";
9139
9902
  import { AuthStorageError as AuthStorageError6 } from "@sixb/core/storage";
9140
9903
  class SqliteAuthOidcAuthorizationAttemptStore {
9141
9904
  db;
@@ -9147,7 +9910,6 @@ class SqliteAuthOidcAuthorizationAttemptStore {
9147
9910
  const id = assertNonEmpty(input.id, "OIDC authorization attempt id");
9148
9911
  const projectId = assertNonEmpty(input.projectId, "Project id");
9149
9912
  const strategyId = assertNonEmpty(input.strategyId, "Strategy id");
9150
- const audience = resolveAuthSessionAudience2(input.audience);
9151
9913
  const stateHash = assertNonEmpty(input.stateHash, "OIDC state hash");
9152
9914
  const nonceHash = assertNonEmpty(input.nonceHash, "OIDC nonce hash");
9153
9915
  const codeVerifier = assertNonEmpty(input.codeVerifier, "OIDC code verifier");
@@ -9160,15 +9922,14 @@ class SqliteAuthOidcAuthorizationAttemptStore {
9160
9922
  project_id,
9161
9923
  id,
9162
9924
  strategy_id,
9163
- audience,
9164
9925
  state_hash,
9165
9926
  nonce_hash,
9166
9927
  code_verifier,
9167
9928
  return_to,
9168
9929
  created_at,
9169
9930
  expires_at
9170
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9171
- `).run(projectId, id, strategyId, audience, stateHash, nonceHash, codeVerifier, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
9931
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
9932
+ `).run(projectId, id, strategyId, stateHash, nonceHash, codeVerifier, input.returnTo ?? null, toIso(input.createdAt), toIso(input.expiresAt));
9172
9933
  } catch (error) {
9173
9934
  mapUniqueConstraintError(error, "duplicate_oidc_attempt", `[Sixb] OIDC authorization attempt '${id}' already exists for project '${projectId}'.`);
9174
9935
  }
@@ -9176,7 +9937,6 @@ class SqliteAuthOidcAuthorizationAttemptStore {
9176
9937
  id,
9177
9938
  projectId,
9178
9939
  strategyId,
9179
- audience,
9180
9940
  stateHash,
9181
9941
  nonceHash,
9182
9942
  codeVerifier,
@@ -9395,21 +10155,6 @@ class SqliteAuthSessionStore {
9395
10155
  async getById(params) {
9396
10156
  return getSessionById(this.db, params);
9397
10157
  }
9398
- async getActiveByUserId(params) {
9399
- const row = this.db.query(`
9400
- SELECT *
9401
- FROM auth_sessions
9402
- WHERE project_id = ?
9403
- AND user_id = ?
9404
- AND audience = ?
9405
- AND revoked_at IS NULL
9406
- AND expires_at > ?
9407
- AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
9408
- ORDER BY created_at DESC, id DESC
9409
- LIMIT 1
9410
- `).get(params.projectId, params.userId, params.audience, toIso(params.now), toIso(params.now));
9411
- return row ? rowToSessionRecord(row) : null;
9412
- }
9413
10158
  async listActiveByUserId(params) {
9414
10159
  const rows = this.db.query(`
9415
10160
  SELECT *
@@ -9423,9 +10168,22 @@ class SqliteAuthSessionStore {
9423
10168
  `).all(params.projectId, params.userId, toIso(params.now), toIso(params.now));
9424
10169
  return rows.map(rowToSessionRecord);
9425
10170
  }
10171
+ async listByUserId(params) {
10172
+ const rows = this.db.query(`
10173
+ SELECT *
10174
+ FROM auth_sessions
10175
+ WHERE project_id = ? AND user_id = ? AND created_at <= ?
10176
+ ORDER BY created_at DESC, id DESC
10177
+ LIMIT ? OFFSET ?
10178
+ `).all(params.projectId, params.userId, params.before ? toIso(params.before.createdAt) : "9999-12-31T23:59:59.999Z", params.limit + 1, params.before?.skip ?? 0);
10179
+ return {
10180
+ sessions: rows.slice(0, params.limit).map(rowToSessionRecord),
10181
+ hasMore: rows.length > params.limit
10182
+ };
10183
+ }
9426
10184
  async findValidByTokenHash(params) {
9427
10185
  const row = getSessionRowById(this.db, params);
9428
- if (!row || row.refresh_token_hash !== null || row.audience !== params.audience || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
10186
+ if (!row || row.refresh_token_hash !== null || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
9429
10187
  return null;
9430
10188
  }
9431
10189
  return rowToSessionRecord(row);
@@ -9480,13 +10238,12 @@ class SqliteAuthSessionStore {
9480
10238
  last_seen_at = ?
9481
10239
  WHERE project_id = ?
9482
10240
  AND id = ?
9483
- AND audience = ?
9484
10241
  AND token_hash = ?
9485
10242
  AND refresh_token_hash IS NULL
9486
10243
  AND revoked_at IS NULL
9487
10244
  AND expires_at > ?
9488
10245
  AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
9489
- `).run(expiresAt, expiresAt, now, params.projectId, params.id, params.audience, params.tokenHash, now, now);
10246
+ `).run(expiresAt, expiresAt, now, params.projectId, params.id, params.tokenHash, now, now);
9490
10247
  return result.changes === 0 ? null : getSessionById(this.db, params);
9491
10248
  });
9492
10249
  }
@@ -9679,9 +10436,6 @@ class SqliteAuthStorage {
9679
10436
  constructor(options = {}) {
9680
10437
  this.connection = openSqliteStoreConnection(options);
9681
10438
  this.db = this.connection.db;
9682
- if (this.connection.installFreshSchema) {
9683
- installFreshSqliteSchema(this.db);
9684
- }
9685
10439
  this.users = new SqliteAuthUserStore(this.db);
9686
10440
  this.identities = new SqliteAuthUserIdentityStore(this.db);
9687
10441
  this.serviceAccounts = new SqliteAuthServiceAccountStore(this.db);
@@ -9760,7 +10514,6 @@ class SqliteAuthStorage {
9760
10514
  };
9761
10515
  }
9762
10516
  validateCompleteSessionInput(this.db, projectId, input.session);
9763
- assertSignInSessionAudience(projectId, input.session.audience, magicLink.audience);
9764
10517
  let newUserId;
9765
10518
  if (shouldCreateUser) {
9766
10519
  newUserId = assertNonEmpty(input.newUserId, "User id");
@@ -9857,7 +10610,6 @@ class SqliteAuthStorage {
9857
10610
  };
9858
10611
  }
9859
10612
  validateCompleteSessionInput(this.db, projectId, input.session);
9860
- assertSignInSessionAudience(projectId, input.session.audience, attempt.audience);
9861
10613
  let newUserId;
9862
10614
  if (shouldCreateUser) {
9863
10615
  newUserId = assertNonEmpty(input.newUserId, "User id");
@@ -10076,7 +10828,6 @@ class SqliteAuthStorage {
10076
10828
  projectId: input.projectId,
10077
10829
  userId: input.userId,
10078
10830
  strategyId: input.strategyId,
10079
- audience: input.session.audience,
10080
10831
  tokenHash: input.session.tokenHash,
10081
10832
  createdAt: input.session.createdAt,
10082
10833
  expiresAt: input.session.expiresAt,
@@ -10121,12 +10872,6 @@ class SqliteAuthStorage {
10121
10872
  });
10122
10873
  }
10123
10874
  }
10124
- function assertSignInSessionAudience(projectId, sessionAudience, storedAudience) {
10125
- if (sessionAudience === storedAudience) {
10126
- return;
10127
- }
10128
- throw new AuthStorageError11("invalid_input", `[Sixb] Sign-in session audience '${sessionAudience}' does not match stored auth audience '${storedAudience}' for project '${projectId}'.`);
10129
- }
10130
10875
  function assertCompletableDeviceAuthorization(authorization, input) {
10131
10876
  if (authorization.deviceCodeHash !== input.deviceCodeHash) {
10132
10877
  throw new AuthStorageError11("invalid_device_authorization", "[Sixb] Invalid device authorization.");
@@ -10194,7 +10939,7 @@ class SqliteConnectorConnectionPersistence {
10194
10939
  callback_binding_hash, reauthorization_id, reauthorization_revision,
10195
10940
  reauthorization_connection_ids, created_at, expires_at
10196
10941
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10197
- `).run(record.projectId, record.connectorId, record.id, record.slot, record.initiatedByExecutionId, record.stateHash, record.codeVerifier === undefined ? null : json(record.codeVerifier), record.redirectUri, record.connectionRunId ?? null, record.returnTo ?? null, record.callbackBindingHash ?? null, record.reauthorizationId ?? null, record.reauthorizationRevision ?? null, record.reauthorizationConnectionIds ? json(record.reauthorizationConnectionIds) : null, iso(record.createdAt), iso(record.expiresAt));
10942
+ `).run(record.projectId, record.connectorId, record.id, record.slot, record.initiatedByExecutionId, record.stateHash, record.codeVerifier === undefined ? null : json(record.codeVerifier), record.redirectUri, record.connectionRunId ?? null, record.returnTo ?? null, record.callbackBindingHash ?? null, record.reauthorizationId ?? null, record.reauthorizationRevision ?? null, record.reauthorizationConnectionIds ? json(record.reauthorizationConnectionIds) : null, iso2(record.createdAt), iso2(record.expiresAt));
10198
10943
  return true;
10199
10944
  } catch (error) {
10200
10945
  if (isUniqueConstraintError(error))
@@ -10285,7 +11030,7 @@ class SqliteConnectorConnectionPersistence {
10285
11030
  project_id, connector_id, id, authorization_id, slot, account,
10286
11031
  account_id, status, disconnected_at, created_at, updated_at
10287
11032
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10288
- `).run(record.projectId, record.connectorId, record.id, record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso(record.createdAt), iso(record.updatedAt));
11033
+ `).run(record.projectId, record.connectorId, record.id, record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso2(record.createdAt), iso2(record.updatedAt));
10289
11034
  return true;
10290
11035
  } catch (error) {
10291
11036
  if (isUniqueConstraintError(error))
@@ -10337,7 +11082,7 @@ class SqliteConnectorConnectionPersistence {
10337
11082
  SET authorization_id = ?, slot = ?, account = ?, account_id = ?,
10338
11083
  status = ?, disconnected_at = ?, updated_at = ?
10339
11084
  WHERE project_id = ? AND connector_id = ? AND id = ?
10340
- `).run(record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso(record.updatedAt), record.projectId, record.connectorId, record.id);
11085
+ `).run(record.authorizationId, record.slot, json(record.account), record.account.id, record.status, optionalIso(record.disconnectedAt), iso2(record.updatedAt), record.projectId, record.connectorId, record.id);
10341
11086
  assertChanged(result.changes, "connector connection", record.id);
10342
11087
  }
10343
11088
  insertRun(record) {
@@ -10349,7 +11094,7 @@ class SqliteConnectorConnectionPersistence {
10349
11094
  expires_at, authorization_id, cleanup_authorization_id, connections, error,
10350
11095
  created_at, updated_at, finished_at
10351
11096
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10352
- `).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -2), iso(record.createdAt), ...values.slice(-2));
11097
+ `).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -2), iso2(record.createdAt), ...values.slice(-2));
10353
11098
  }
10354
11099
  insertAuthorizationRow(record) {
10355
11100
  const values = authorizationValues(record);
@@ -10361,7 +11106,7 @@ class SqliteConnectorConnectionPersistence {
10361
11106
  mutation_expected_connection_ids, staged_credentials, staged_credential_expires_at,
10362
11107
  staged_scopes, created_at, updated_at
10363
11108
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10364
- `).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -1), iso(record.createdAt), ...values.slice(-1));
11109
+ `).run(record.projectId, record.connectorId, record.id, ...values.slice(0, -1), iso2(record.createdAt), ...values.slice(-1));
10365
11110
  }
10366
11111
  getById(table, id) {
10367
11112
  if (this.scope.connectorId) {
@@ -10398,13 +11143,13 @@ function authorizationAttemptFromRow(row) {
10398
11143
  function runValues(record) {
10399
11144
  const waitingFor = record.status === "waiting" ? record.waitingFor : null;
10400
11145
  const processingId = record.status === "running" ? record.processingId : null;
10401
- const callbackStartedAt = record.status === "running" ? iso(record.callbackStartedAt) : null;
10402
- const expiresAt = record.status === "waiting" || record.status === "running" ? iso(record.expiresAt) : null;
11146
+ const callbackStartedAt = record.status === "running" ? iso2(record.callbackStartedAt) : null;
11147
+ const expiresAt = record.status === "waiting" || record.status === "running" ? iso2(record.expiresAt) : null;
10403
11148
  const authorizationId = "authorizationId" in record ? record.authorizationId ?? null : null;
10404
11149
  const cleanupAuthorizationId = record.status === "succeeded" ? record.cleanupAuthorizationId ?? null : null;
10405
11150
  const connections = record.status === "succeeded" ? json(record.connections) : null;
10406
11151
  const error = record.status === "failed" ? json(record.error) : null;
10407
- const finishedAt = record.status === "succeeded" || record.status === "failed" || record.status === "cancelled" || record.status === "expired" ? iso(record.finishedAt) : null;
11152
+ const finishedAt = record.status === "succeeded" || record.status === "failed" || record.status === "cancelled" || record.status === "expired" ? iso2(record.finishedAt) : null;
10408
11153
  return [
10409
11154
  record.kind,
10410
11155
  record.slot,
@@ -10418,7 +11163,7 @@ function runValues(record) {
10418
11163
  cleanupAuthorizationId,
10419
11164
  connections,
10420
11165
  error,
10421
- iso(record.updatedAt),
11166
+ iso2(record.updatedAt),
10422
11167
  finishedAt
10423
11168
  ];
10424
11169
  }
@@ -10521,7 +11266,7 @@ function authorizationValues(record) {
10521
11266
  staged ? json(staged.credentials) : null,
10522
11267
  optionalIso(staged?.credentialExpiresAt),
10523
11268
  staged ? json(staged.scopes) : null,
10524
- iso(record.updatedAt)
11269
+ iso2(record.updatedAt)
10525
11270
  ];
10526
11271
  }
10527
11272
  function authorizationFromRow(row) {
@@ -10596,7 +11341,7 @@ function json(value) {
10596
11341
  function parseJson(value) {
10597
11342
  return JSON.parse(value);
10598
11343
  }
10599
- function iso(value) {
11344
+ function iso2(value) {
10600
11345
  return value.toISOString();
10601
11346
  }
10602
11347
  function optionalIso(value) {
@@ -10618,6 +11363,7 @@ function assertChanged(changes, label, id) {
10618
11363
 
10619
11364
  // src/execution-storage.ts
10620
11365
  import {
11366
+ compareExecutionCreation,
10621
11367
  executionRecordFromStorageRow,
10622
11368
  executionRecordToStorageRow,
10623
11369
  prepareExecutionRecord
@@ -10684,6 +11430,39 @@ class SqliteExecutionStorage {
10684
11430
  `).get(params.projectId, params.id);
10685
11431
  return row ? sqliteExecutionRecord(row) : null;
10686
11432
  }
11433
+ async listBySession(input) {
11434
+ const sessionColumn = input.session.type === "user" ? "authority_session_id" : "authority_delegation_session_id";
11435
+ const rows = this.db.query(`
11436
+ WITH RECURSIVE tree AS (
11437
+ SELECT roots.*, 0 AS depth
11438
+ FROM (
11439
+ SELECT *
11440
+ FROM executions
11441
+ WHERE project_id = ?
11442
+ AND ${sessionColumn} = ?
11443
+ AND parent_execution_id IS NULL
11444
+ AND created_at >= ?
11445
+ AND created_at < ?
11446
+ ORDER BY created_at, id
11447
+ LIMIT ?
11448
+ ) roots
11449
+ UNION ALL
11450
+ SELECT child.*, tree.depth + 1
11451
+ FROM tree
11452
+ CROSS JOIN executions child
11453
+ ON child.project_id = tree.project_id
11454
+ AND child.parent_execution_id = tree.id
11455
+ ORDER BY depth, created_at, id
11456
+ LIMIT ?
11457
+ )
11458
+ SELECT * FROM tree
11459
+ `).all(input.projectId, input.session.id, input.from.toISOString(), input.to.toISOString(), input.limit + 1, input.limit + 1);
11460
+ const kept = rows.slice(0, input.limit).map(sqliteExecutionRecord);
11461
+ return {
11462
+ executions: kept.sort(compareExecutionCreation),
11463
+ truncated: rows.length > input.limit
11464
+ };
11465
+ }
10687
11466
  }
10688
11467
  function sqliteExecutionRecord(row) {
10689
11468
  return executionRecordFromStorageRow(toStorageRow(row));
@@ -12785,9 +13564,6 @@ class SqliteObjectStorage {
12785
13564
  this.connection = openSqliteStoreConnection(options);
12786
13565
  this.db = this.connection.db;
12787
13566
  this.reader = new SqliteObjectReader(this.db, DEFAULT_OBJECT_QUERY_SOURCE);
12788
- if (this.connection.installFreshSchema) {
12789
- installFreshSqliteSchema(this.db);
12790
- }
12791
13567
  }
12792
13568
  queryCapabilities() {
12793
13569
  return SQLITE_OBJECT_QUERY_CAPABILITIES;
@@ -15822,6 +16598,26 @@ class SqliteOntologySourceStorage {
15822
16598
  return row ? sourceRecord(row) : null;
15823
16599
  });
15824
16600
  }
16601
+ async listActive(input) {
16602
+ return this.runRootOperation(() => {
16603
+ assertNonblank(input.projectId, "Source project id");
16604
+ const rows = this.db.query(`
16605
+ SELECT source_id, dataset_id, dataset_version_id, dataset_version_created_at,
16606
+ projection_revision
16607
+ FROM ontology_sources
16608
+ WHERE project_id = ? AND status = 'active'
16609
+ `).all(input.projectId);
16610
+ return rows.map((row) => ({
16611
+ source: { projectionId: row.source_id },
16612
+ datasetVersion: {
16613
+ datasetId: row.dataset_id,
16614
+ versionId: row.dataset_version_id,
16615
+ createdAt: row.dataset_version_created_at
16616
+ },
16617
+ projectionRevision: row.projection_revision
16618
+ }));
16619
+ });
16620
+ }
15825
16621
  async adopt(input) {
15826
16622
  return this.runRootOperation(() => {
15827
16623
  assertProjectAndSource(input);
@@ -16330,9 +17126,6 @@ class SqlitePipelineRunStorage {
16330
17126
  this.connection = openSqliteStoreConnection(options);
16331
17127
  this.db = this.connection.db;
16332
17128
  this.executions = options.executions;
16333
- if (this.connection.installFreshSchema) {
16334
- installFreshSqliteSchema(this.db);
16335
- }
16336
17129
  }
16337
17130
  async queue(input) {
16338
17131
  const queuedAt = input.queuedAt ?? new Date;
@@ -16678,9 +17471,6 @@ class SqliteProjectionRunStorage {
16678
17471
  this.connection = openSqliteStoreConnection(options);
16679
17472
  this.db = this.connection.db;
16680
17473
  this.executions = options.executions;
16681
- if (this.connection.installFreshSchema) {
16682
- installFreshSqliteSchema(this.db);
16683
- }
16684
17474
  }
16685
17475
  async queue(input) {
16686
17476
  assertProjectionRunQueueInput(input);
@@ -17124,9 +17914,6 @@ class SqliteRulesStorage {
17124
17914
  constructor(options = {}) {
17125
17915
  this.connection = openSqliteStoreConnection(options);
17126
17916
  this.db = this.connection.db;
17127
- if (this.connection.installFreshSchema) {
17128
- installFreshSqliteSchema(this.db);
17129
- }
17130
17917
  }
17131
17918
  async getActive(params) {
17132
17919
  const row = this.db.query(`
@@ -17372,8 +18159,6 @@ class SqliteShareGrantStorage {
17372
18159
  constructor(options = {}) {
17373
18160
  this.connection = openSqliteStoreConnection(options);
17374
18161
  this.db = this.connection.db;
17375
- if (this.connection.installFreshSchema)
17376
- installFreshSqliteSchema(this.db);
17377
18162
  }
17378
18163
  async create(input) {
17379
18164
  const record = normalizeShareGrantCreate(input);
@@ -17397,6 +18182,23 @@ class SqliteShareGrantStorage {
17397
18182
  const normalized = normalizeGetShareGrantByIdInput(input);
17398
18183
  return this.getRow(normalized.projectId, normalized.id);
17399
18184
  }
18185
+ async getByIds(input) {
18186
+ const ids = [...new Set(input.ids)];
18187
+ if (ids.length === 0) {
18188
+ return [];
18189
+ }
18190
+ const rows = this.db.query(`
18191
+ SELECT *
18192
+ FROM share_grants
18193
+ WHERE project_id = ?
18194
+ AND id IN (SELECT value FROM json_each(?))
18195
+ `).all(input.projectId, JSON.stringify(ids));
18196
+ const byId = new Map(rows.map((row) => [row.id, row]));
18197
+ return ids.flatMap((id) => {
18198
+ const row = byId.get(id);
18199
+ return row ? [rowToRecord(row)] : [];
18200
+ });
18201
+ }
17400
18202
  async list(input) {
17401
18203
  const normalized = normalizeListShareGrantsInput(input);
17402
18204
  const where = ["project_id = ?"];
@@ -17518,8 +18320,6 @@ class SqliteShareSessionStorage {
17518
18320
  constructor(options = {}) {
17519
18321
  this.connection = openSqliteStoreConnection(options);
17520
18322
  this.db = this.connection.db;
17521
- if (this.connection.installFreshSchema)
17522
- installFreshSqliteSchema(this.db);
17523
18323
  }
17524
18324
  async create(input) {
17525
18325
  const record = normalizeShareSessionCreate(input);
@@ -17622,9 +18422,6 @@ class SqliteSyncRunStorage {
17622
18422
  this.connection = openSqliteStoreConnection(options);
17623
18423
  this.db = this.connection.db;
17624
18424
  this.executions = options.executions;
17625
- if (this.connection.installFreshSchema) {
17626
- installFreshSqliteSchema(this.db);
17627
- }
17628
18425
  }
17629
18426
  async queue(input) {
17630
18427
  const queuedAt = input.queuedAt ?? new Date;
@@ -17834,9 +18631,6 @@ class SqliteTimeseriesStorage {
17834
18631
  constructor(options = {}) {
17835
18632
  this.connection = openSqliteStoreConnection(options);
17836
18633
  this.db = this.connection.db;
17837
- if (this.connection.installFreshSchema) {
17838
- installFreshSqliteSchema(this.db);
17839
- }
17840
18634
  }
17841
18635
  async getHistory(params) {
17842
18636
  let query = `
@@ -17963,8 +18757,6 @@ class SqliteWebhookRunStorage {
17963
18757
  this.connection = openSqliteStoreConnection(options);
17964
18758
  this.db = this.connection.db;
17965
18759
  this.executions = options.executions;
17966
- if (this.connection.installFreshSchema)
17967
- installFreshSqliteSchema(this.db);
17968
18760
  }
17969
18761
  async start(input) {
17970
18762
  const startedAt = input.startedAt ?? new Date;
@@ -18110,9 +18902,6 @@ class SqliteWorkflowInterventionStorage {
18110
18902
  constructor(options = {}) {
18111
18903
  this.connection = openSqliteStoreConnection(options);
18112
18904
  this.db = this.connection.db;
18113
- if (this.connection.installFreshSchema) {
18114
- installFreshSqliteSchema(this.db);
18115
- }
18116
18905
  }
18117
18906
  async create(input) {
18118
18907
  assertNonNegativeInteger(input.nodeIndex, "nodeIndex");
@@ -18347,9 +19136,6 @@ class SqliteWorkflowRunStorage {
18347
19136
  this.connection = openSqliteStoreConnection(options);
18348
19137
  this.db = this.connection.db;
18349
19138
  this.executions = options.executions;
18350
- if (this.connection.installFreshSchema) {
18351
- installFreshSqliteSchema(this.db);
18352
- }
18353
19139
  this.nodes = new SqliteWorkflowNodeRunStorage(this.db);
18354
19140
  this.agentNodes = new SqliteWorkflowAgentNodeRunStorage(this.db, this.executions);
18355
19141
  }
@@ -18499,6 +19285,23 @@ class SqliteWorkflowRunStorage {
18499
19285
  const row = this.db.query("SELECT * FROM workflow_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
18500
19286
  return row ? rowToWorkflowRunRecord(row) : null;
18501
19287
  }
19288
+ async getByExecutionIds(params) {
19289
+ const executionIds = [...new Set(params.executionIds)];
19290
+ if (executionIds.length === 0) {
19291
+ return [];
19292
+ }
19293
+ const rows = this.db.query(`
19294
+ SELECT *
19295
+ FROM workflow_runs
19296
+ WHERE project_id = ?
19297
+ AND execution_id IN (SELECT value FROM json_each(?))
19298
+ `).all(params.projectId, JSON.stringify(executionIds));
19299
+ const byExecution = new Map(rows.map((row) => [row.execution_id, row]));
19300
+ return executionIds.flatMap((executionId) => {
19301
+ const row = byExecution.get(executionId);
19302
+ return row ? [rowToWorkflowRunRecord(row)] : [];
19303
+ });
19304
+ }
18502
19305
  async list(input) {
18503
19306
  if (hasEmptyStatuses(input) || input.workflowIds?.length === 0) {
18504
19307
  return {
@@ -18964,6 +19767,7 @@ class SqliteStorage {
18964
19767
  auth;
18965
19768
  executions;
18966
19769
  agents;
19770
+ artifacts;
18967
19771
  aiUsage;
18968
19772
  aiCosts;
18969
19773
  aiLimits;
@@ -18981,6 +19785,7 @@ class SqliteStorage {
18981
19785
  connectorConnections;
18982
19786
  fileUploadSessions;
18983
19787
  fileDownloadGrants;
19788
+ activity;
18984
19789
  migrators;
18985
19790
  connection;
18986
19791
  readConnection;
@@ -18995,9 +19800,6 @@ class SqliteStorage {
18995
19800
  } else {
18996
19801
  this.readConnection = this.connection;
18997
19802
  }
18998
- if (this.connection.installFreshSchema) {
18999
- installFreshSqliteSchema(this.connection.db);
19000
- }
19001
19803
  const stores = createSqliteStores(this.connection, {
19002
19804
  runOntologyOperation: async (run) => run(),
19003
19805
  transactionContext: null
@@ -19016,6 +19818,7 @@ class SqliteStorage {
19016
19818
  this.auth = createAuthOperationScope(stores.auth, scope);
19017
19819
  this.executions = createOperationScopedFacade(stores.executions, scope);
19018
19820
  this.agents = createAgentOperationScope(stores.agents, scope);
19821
+ this.artifacts = createOperationScopedFacade(stores.artifacts, scope);
19019
19822
  this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope);
19020
19823
  this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope);
19021
19824
  this.aiLimits = createOperationScopedFacade(stores.aiLimits, scope);
@@ -19033,6 +19836,7 @@ class SqliteStorage {
19033
19836
  this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope);
19034
19837
  this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope);
19035
19838
  this.fileDownloadGrants = createOperationScopedFacade(stores.fileDownloadGrants, scope);
19839
+ this.activity = createOperationScopedFacade(stores.activity, scope);
19036
19840
  this.migrators = options.path ? createSqliteStorageMigrators(options.path) : [];
19037
19841
  registerSqliteStorageTestingAdapter(this, {
19038
19842
  advanceConnectorConnectionTime: (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs),
@@ -19090,8 +19894,7 @@ class SqliteStorage {
19090
19894
  return {
19091
19895
  ...createSqliteStores({
19092
19896
  db: this.connection.db,
19093
- ownsConnection: false,
19094
- installFreshSchema: false
19897
+ ownsConnection: false
19095
19898
  }, {
19096
19899
  runOntologyOperation: async (run) => run(),
19097
19900
  transactionContext
@@ -19146,6 +19949,7 @@ function createSqliteStores(connection, options) {
19146
19949
  auth,
19147
19950
  executions,
19148
19951
  agents: new SqliteAgentStorage({ connection, executions }),
19952
+ artifacts: new SqliteArtifactStorage(connection),
19149
19953
  aiUsage: new SqliteAiUsageStorage({ connection }),
19150
19954
  aiCosts: new SqliteAiCostStorage(connection),
19151
19955
  aiLimits: new SqliteAiLimitStorage({ connection }),
@@ -19162,7 +19966,8 @@ function createSqliteStores(connection, options) {
19162
19966
  shareSessions,
19163
19967
  connectorConnections: new SqliteConnectorConnectionStorage(connection),
19164
19968
  fileUploadSessions: new SqliteFileUploadSessionStorage(connection),
19165
- fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection)
19969
+ fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection),
19970
+ activity: new SqliteActivityStorage(connection)
19166
19971
  };
19167
19972
  }
19168
19973
  export {
@@ -19170,4 +19975,4 @@ export {
19170
19975
  migrateSqliteStorage
19171
19976
  };
19172
19977
 
19173
- //# debugId=682E7F57F0A7280864756E2164756E21
19978
+ //# debugId=B9F8223963B8B3F664756E2164756E21