@sixb/sqlite 0.1.20 → 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 (40) 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/threads.d.ts +4 -0
  6. package/dist/agents/threads.d.ts.map +1 -1
  7. package/dist/artifact-storage.d.ts +22 -0
  8. package/dist/artifact-storage.d.ts.map +1 -0
  9. package/dist/auth-storage/sessions.d.ts +2 -1
  10. package/dist/auth-storage/sessions.d.ts.map +1 -1
  11. package/dist/execution-storage.d.ts +2 -1
  12. package/dist/execution-storage.d.ts.map +1 -1
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +880 -14
  16. package/dist/index.js.map +14 -12
  17. package/dist/migrations.d.ts.map +1 -1
  18. package/dist/ontology-storage/sources.d.ts +2 -1
  19. package/dist/ontology-storage/sources.d.ts.map +1 -1
  20. package/dist/share-grant-storage.d.ts +2 -1
  21. package/dist/share-grant-storage.d.ts.map +1 -1
  22. package/dist/workflow-run-storage.d.ts +4 -0
  23. package/dist/workflow-run-storage.d.ts.map +1 -1
  24. package/package.json +3 -3
  25. package/src/action-run-storage.ts +27 -0
  26. package/src/activity-storage.ts +588 -0
  27. package/src/agents/threads.ts +27 -0
  28. package/src/artifact-storage.ts +356 -0
  29. package/src/auth-storage/sessions.ts +27 -0
  30. package/src/execution-storage.ts +56 -0
  31. package/src/index.ts +10 -0
  32. package/src/migrations/062-artifacts.sql +71 -0
  33. package/src/migrations/063-drop-output-diagnostics.sql +19 -0
  34. package/src/migrations/064-activity.sql +61 -0
  35. package/src/migrations/065-activity-timeline.sql +19 -0
  36. package/src/migrations/066-activity-interactions.sql +20 -0
  37. package/src/migrations.ts +14 -0
  38. package/src/ontology-storage/sources.ts +41 -0
  39. package/src/share-grant-storage.ts +25 -0
  40. package/src/workflow-run-storage.ts +27 -0
package/dist/index.js CHANGED
@@ -5166,6 +5166,211 @@ ALTER TABLE auth_magic_links DROP COLUMN audience;
5166
5166
  ALTER TABLE auth_oidc_authorization_attempts DROP COLUMN audience;
5167
5167
  `;
5168
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
+
5169
5374
  // src/migrations.ts
5170
5375
  var MIGRATIONS_TABLE_SQL = `
5171
5376
  CREATE TABLE IF NOT EXISTS sixb_migrations (
@@ -5274,7 +5479,12 @@ var sqliteStorageMigrations = defineMigrations({
5274
5479
  sqliteSql("058-file-download-grants", _058_file_download_grants_default),
5275
5480
  sqliteSql("059-drained-source-versions", _059_drained_source_versions_default),
5276
5481
  sqliteSql("060-terminal-action-runs", _060_terminal_action_runs_default),
5277
- sqliteSql("061-drop-auth-audience", _061_drop_auth_audience_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)
5278
5488
  ]
5279
5489
  });
5280
5490
  function sqliteStoragePath(basePath) {
@@ -5674,6 +5884,23 @@ class SqliteActionRunStorage {
5674
5884
  const row = this.db.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
5675
5885
  return row ? rowToActionRunRecord(row) : null;
5676
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
+ }
5677
5904
  async list(input) {
5678
5905
  if (input.statuses && input.statuses.length === 0 || input.actionIds?.length === 0) {
5679
5906
  return {
@@ -5845,6 +6072,342 @@ function rowToActionSubject(row) {
5845
6072
  };
5846
6073
  }
5847
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
+
5848
6411
  // src/agents/checkpoints.ts
5849
6412
  import {
5850
6413
  agentContextCheckpointMatchesCreateInput,
@@ -6627,6 +7190,23 @@ class SqliteAgentThreadStore {
6627
7190
  const row = this.db.query("SELECT * FROM agent_threads WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
6628
7191
  return row ? rowToThreadRecord(row) : null;
6629
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
+ }
6630
7210
  async list(input) {
6631
7211
  if (input.statuses !== undefined && input.statuses.length === 0) {
6632
7212
  return { threads: [], hasMore: false, total: 0 };
@@ -8065,6 +8645,185 @@ function isConstraintError(error) {
8065
8645
  return error instanceof Error && /constraint/i.test(error.message);
8066
8646
  }
8067
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
+
8068
8827
  // src/auth-storage/auth-storage.ts
8069
8828
  import { AuthStorageError as AuthStorageError11 } from "@sixb/core/storage";
8070
8829
 
@@ -9409,6 +10168,19 @@ class SqliteAuthSessionStore {
9409
10168
  `).all(params.projectId, params.userId, toIso(params.now), toIso(params.now));
9410
10169
  return rows.map(rowToSessionRecord);
9411
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
+ }
9412
10184
  async findValidByTokenHash(params) {
9413
10185
  const row = getSessionRowById(this.db, params);
9414
10186
  if (!row || row.refresh_token_hash !== null || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
@@ -10167,7 +10939,7 @@ class SqliteConnectorConnectionPersistence {
10167
10939
  callback_binding_hash, reauthorization_id, reauthorization_revision,
10168
10940
  reauthorization_connection_ids, created_at, expires_at
10169
10941
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10170
- `).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));
10171
10943
  return true;
10172
10944
  } catch (error) {
10173
10945
  if (isUniqueConstraintError(error))
@@ -10258,7 +11030,7 @@ class SqliteConnectorConnectionPersistence {
10258
11030
  project_id, connector_id, id, authorization_id, slot, account,
10259
11031
  account_id, status, disconnected_at, created_at, updated_at
10260
11032
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10261
- `).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));
10262
11034
  return true;
10263
11035
  } catch (error) {
10264
11036
  if (isUniqueConstraintError(error))
@@ -10310,7 +11082,7 @@ class SqliteConnectorConnectionPersistence {
10310
11082
  SET authorization_id = ?, slot = ?, account = ?, account_id = ?,
10311
11083
  status = ?, disconnected_at = ?, updated_at = ?
10312
11084
  WHERE project_id = ? AND connector_id = ? AND id = ?
10313
- `).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);
10314
11086
  assertChanged(result.changes, "connector connection", record.id);
10315
11087
  }
10316
11088
  insertRun(record) {
@@ -10322,7 +11094,7 @@ class SqliteConnectorConnectionPersistence {
10322
11094
  expires_at, authorization_id, cleanup_authorization_id, connections, error,
10323
11095
  created_at, updated_at, finished_at
10324
11096
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10325
- `).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));
10326
11098
  }
10327
11099
  insertAuthorizationRow(record) {
10328
11100
  const values = authorizationValues(record);
@@ -10334,7 +11106,7 @@ class SqliteConnectorConnectionPersistence {
10334
11106
  mutation_expected_connection_ids, staged_credentials, staged_credential_expires_at,
10335
11107
  staged_scopes, created_at, updated_at
10336
11108
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10337
- `).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));
10338
11110
  }
10339
11111
  getById(table, id) {
10340
11112
  if (this.scope.connectorId) {
@@ -10371,13 +11143,13 @@ function authorizationAttemptFromRow(row) {
10371
11143
  function runValues(record) {
10372
11144
  const waitingFor = record.status === "waiting" ? record.waitingFor : null;
10373
11145
  const processingId = record.status === "running" ? record.processingId : null;
10374
- const callbackStartedAt = record.status === "running" ? iso(record.callbackStartedAt) : null;
10375
- 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;
10376
11148
  const authorizationId = "authorizationId" in record ? record.authorizationId ?? null : null;
10377
11149
  const cleanupAuthorizationId = record.status === "succeeded" ? record.cleanupAuthorizationId ?? null : null;
10378
11150
  const connections = record.status === "succeeded" ? json(record.connections) : null;
10379
11151
  const error = record.status === "failed" ? json(record.error) : null;
10380
- 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;
10381
11153
  return [
10382
11154
  record.kind,
10383
11155
  record.slot,
@@ -10391,7 +11163,7 @@ function runValues(record) {
10391
11163
  cleanupAuthorizationId,
10392
11164
  connections,
10393
11165
  error,
10394
- iso(record.updatedAt),
11166
+ iso2(record.updatedAt),
10395
11167
  finishedAt
10396
11168
  ];
10397
11169
  }
@@ -10494,7 +11266,7 @@ function authorizationValues(record) {
10494
11266
  staged ? json(staged.credentials) : null,
10495
11267
  optionalIso(staged?.credentialExpiresAt),
10496
11268
  staged ? json(staged.scopes) : null,
10497
- iso(record.updatedAt)
11269
+ iso2(record.updatedAt)
10498
11270
  ];
10499
11271
  }
10500
11272
  function authorizationFromRow(row) {
@@ -10569,7 +11341,7 @@ function json(value) {
10569
11341
  function parseJson(value) {
10570
11342
  return JSON.parse(value);
10571
11343
  }
10572
- function iso(value) {
11344
+ function iso2(value) {
10573
11345
  return value.toISOString();
10574
11346
  }
10575
11347
  function optionalIso(value) {
@@ -10591,6 +11363,7 @@ function assertChanged(changes, label, id) {
10591
11363
 
10592
11364
  // src/execution-storage.ts
10593
11365
  import {
11366
+ compareExecutionCreation,
10594
11367
  executionRecordFromStorageRow,
10595
11368
  executionRecordToStorageRow,
10596
11369
  prepareExecutionRecord
@@ -10657,6 +11430,39 @@ class SqliteExecutionStorage {
10657
11430
  `).get(params.projectId, params.id);
10658
11431
  return row ? sqliteExecutionRecord(row) : null;
10659
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
+ }
10660
11466
  }
10661
11467
  function sqliteExecutionRecord(row) {
10662
11468
  return executionRecordFromStorageRow(toStorageRow(row));
@@ -15792,6 +16598,26 @@ class SqliteOntologySourceStorage {
15792
16598
  return row ? sourceRecord(row) : null;
15793
16599
  });
15794
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
+ }
15795
16621
  async adopt(input) {
15796
16622
  return this.runRootOperation(() => {
15797
16623
  assertProjectAndSource(input);
@@ -17356,6 +18182,23 @@ class SqliteShareGrantStorage {
17356
18182
  const normalized = normalizeGetShareGrantByIdInput(input);
17357
18183
  return this.getRow(normalized.projectId, normalized.id);
17358
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
+ }
17359
18202
  async list(input) {
17360
18203
  const normalized = normalizeListShareGrantsInput(input);
17361
18204
  const where = ["project_id = ?"];
@@ -18442,6 +19285,23 @@ class SqliteWorkflowRunStorage {
18442
19285
  const row = this.db.query("SELECT * FROM workflow_runs WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
18443
19286
  return row ? rowToWorkflowRunRecord(row) : null;
18444
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
+ }
18445
19305
  async list(input) {
18446
19306
  if (hasEmptyStatuses(input) || input.workflowIds?.length === 0) {
18447
19307
  return {
@@ -18907,6 +19767,7 @@ class SqliteStorage {
18907
19767
  auth;
18908
19768
  executions;
18909
19769
  agents;
19770
+ artifacts;
18910
19771
  aiUsage;
18911
19772
  aiCosts;
18912
19773
  aiLimits;
@@ -18924,6 +19785,7 @@ class SqliteStorage {
18924
19785
  connectorConnections;
18925
19786
  fileUploadSessions;
18926
19787
  fileDownloadGrants;
19788
+ activity;
18927
19789
  migrators;
18928
19790
  connection;
18929
19791
  readConnection;
@@ -18956,6 +19818,7 @@ class SqliteStorage {
18956
19818
  this.auth = createAuthOperationScope(stores.auth, scope);
18957
19819
  this.executions = createOperationScopedFacade(stores.executions, scope);
18958
19820
  this.agents = createAgentOperationScope(stores.agents, scope);
19821
+ this.artifacts = createOperationScopedFacade(stores.artifacts, scope);
18959
19822
  this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope);
18960
19823
  this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope);
18961
19824
  this.aiLimits = createOperationScopedFacade(stores.aiLimits, scope);
@@ -18973,6 +19836,7 @@ class SqliteStorage {
18973
19836
  this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope);
18974
19837
  this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope);
18975
19838
  this.fileDownloadGrants = createOperationScopedFacade(stores.fileDownloadGrants, scope);
19839
+ this.activity = createOperationScopedFacade(stores.activity, scope);
18976
19840
  this.migrators = options.path ? createSqliteStorageMigrators(options.path) : [];
18977
19841
  registerSqliteStorageTestingAdapter(this, {
18978
19842
  advanceConnectorConnectionTime: (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs),
@@ -19085,6 +19949,7 @@ function createSqliteStores(connection, options) {
19085
19949
  auth,
19086
19950
  executions,
19087
19951
  agents: new SqliteAgentStorage({ connection, executions }),
19952
+ artifacts: new SqliteArtifactStorage(connection),
19088
19953
  aiUsage: new SqliteAiUsageStorage({ connection }),
19089
19954
  aiCosts: new SqliteAiCostStorage(connection),
19090
19955
  aiLimits: new SqliteAiLimitStorage({ connection }),
@@ -19101,7 +19966,8 @@ function createSqliteStores(connection, options) {
19101
19966
  shareSessions,
19102
19967
  connectorConnections: new SqliteConnectorConnectionStorage(connection),
19103
19968
  fileUploadSessions: new SqliteFileUploadSessionStorage(connection),
19104
- fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection)
19969
+ fileDownloadGrants: new SqliteFileDownloadGrantStorage(connection),
19970
+ activity: new SqliteActivityStorage(connection)
19105
19971
  };
19106
19972
  }
19107
19973
  export {
@@ -19109,4 +19975,4 @@ export {
19109
19975
  migrateSqliteStorage
19110
19976
  };
19111
19977
 
19112
- //# debugId=7AEE546F2916C86164756E2164756E21
19978
+ //# debugId=B9F8223963B8B3F664756E2164756E21