@agent-native/core 0.77.7 → 0.77.10
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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +46 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/task-store.ts +25 -3
- package/corpus/core/src/agent/harness/store.ts +89 -55
- package/corpus/core/src/agent/observational-memory/store.ts +40 -14
- package/corpus/core/src/agent/production-agent.ts +18 -0
- package/corpus/core/src/agent/run-store.ts +116 -59
- package/corpus/core/src/application-state/store.ts +36 -2
- package/corpus/core/src/audit/store.ts +37 -6
- package/corpus/core/src/browser-sessions/store.ts +31 -12
- package/corpus/core/src/chat-threads/store.ts +67 -3
- package/corpus/core/src/checkpoints/store.ts +14 -3
- package/corpus/core/src/cli/create.ts +9 -0
- package/corpus/core/src/collab/storage.ts +20 -2
- package/corpus/core/src/db/ddl-guard.ts +302 -0
- package/corpus/core/src/extensions/store.ts +76 -3
- package/corpus/core/src/integrations/a2a-continuations-store.ts +69 -26
- package/corpus/core/src/integrations/config-store.ts +19 -18
- package/corpus/core/src/integrations/pending-tasks-store.ts +40 -16
- package/corpus/core/src/integrations/remote-commands-store.ts +37 -23
- package/corpus/core/src/integrations/remote-devices-store.ts +64 -20
- package/corpus/core/src/integrations/remote-push-store.ts +75 -35
- package/corpus/core/src/integrations/remote-run-events-store.ts +38 -12
- package/corpus/core/src/integrations/thread-mapping-store.ts +11 -2
- package/corpus/core/src/mcp/connect-store.ts +53 -16
- package/corpus/core/src/mcp/oauth-store.ts +32 -7
- package/corpus/core/src/notifications/store.ts +20 -4
- package/corpus/core/src/oauth-tokens/store.ts +45 -2
- package/corpus/core/src/observability/store.ts +154 -47
- package/corpus/core/src/progress/store.ts +36 -21
- package/corpus/core/src/provider-api/corpus-jobs-store.ts +25 -5
- package/corpus/core/src/provider-api/custom-registry.ts +15 -4
- package/corpus/core/src/provider-api/quota-governor.ts +15 -3
- package/corpus/core/src/provider-api/staged-datasets-store.ts +22 -5
- package/corpus/core/src/resources/store.ts +66 -31
- package/corpus/core/src/secrets/storage.ts +32 -2
- package/corpus/core/src/server/agent-teams-run-queue.ts +22 -10
- package/corpus/core/src/server/auth.ts +22 -4
- package/corpus/core/src/server/better-auth-instance.ts +49 -15
- package/corpus/core/src/server/embed-session.ts +19 -4
- package/corpus/core/src/server/identity-sso-store.ts +40 -11
- package/corpus/core/src/server/recap-image-store.ts +28 -9
- package/corpus/core/src/settings/store.ts +35 -6
- package/corpus/core/src/usage/store.ts +51 -16
- package/corpus/core/src/workspace-connections/store.ts +200 -22
- package/corpus/templates/calendar/changelog/2026-06-25-booking-link-previews-now-show-the-content-directly-on-the-g.md +6 -0
- package/corpus/templates/calendar/server/lib/booking-og-image.ts +0 -2
- package/corpus/templates/forms/changelog/2026-06-25-form-previews-now-show-the-content-directly-on-the-grid-back.md +6 -0
- package/corpus/templates/forms/server/lib/form-og-image.ts +0 -2
- package/corpus/templates/plan/app/global.css +6 -11
- package/corpus/templates/plan/app/pages/PlansPage.tsx +12 -10
- package/corpus/templates/plan/changelog/2026-06-25-plan-access-request-pages-stay-visible-during-background-ref.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-25-plan-documents-keep-their-full-reading-width-below-1200px-an.md +6 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +19 -3
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/agent/harness/store.d.ts.map +1 -1
- package/dist/agent/harness/store.js +63 -34
- package/dist/agent/harness/store.js.map +1 -1
- package/dist/agent/observational-memory/store.d.ts.map +1 -1
- package/dist/agent/observational-memory/store.js +30 -8
- package/dist/agent/observational-memory/store.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +103 -46
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +28 -2
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +18 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/browser-sessions/store.d.ts.map +1 -1
- package/dist/browser-sessions/store.js +22 -7
- package/dist/browser-sessions/store.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +51 -3
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +12 -3
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +9 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +14 -2
- package/dist/collab/storage.js.map +1 -1
- package/dist/db/ddl-guard.d.ts +151 -0
- package/dist/db/ddl-guard.d.ts.map +1 -0
- package/dist/db/ddl-guard.js +265 -0
- package/dist/db/ddl-guard.js.map +1 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +32 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +44 -24
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +17 -11
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +24 -16
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts.map +1 -1
- package/dist/integrations/remote-commands-store.js +29 -21
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.d.ts.map +1 -1
- package/dist/integrations/remote-devices-store.js +33 -18
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.d.ts.map +1 -1
- package/dist/integrations/remote-push-store.js +53 -31
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
- package/dist/integrations/remote-run-events-store.js +26 -10
- package/dist/integrations/remote-run-events-store.js.map +1 -1
- package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
- package/dist/integrations/thread-mapping-store.js +10 -2
- package/dist/integrations/thread-mapping-store.js.map +1 -1
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +34 -16
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +22 -7
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/notifications/store.js +15 -3
- package/dist/notifications/store.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +33 -2
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +72 -29
- package/dist/observability/store.js.map +1 -1
- package/dist/progress/store.d.ts.map +1 -1
- package/dist/progress/store.js +31 -16
- package/dist/progress/store.js.map +1 -1
- package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -1
- package/dist/provider-api/corpus-jobs-store.js +16 -5
- package/dist/provider-api/corpus-jobs-store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +14 -4
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/quota-governor.d.ts.map +1 -1
- package/dist/provider-api/quota-governor.js +12 -3
- package/dist/provider-api/quota-governor.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +16 -5
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +56 -25
- package/dist/resources/store.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +22 -2
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
- package/dist/server/agent-teams-run-queue.js +15 -4
- package/dist/server/agent-teams-run-queue.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +16 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +55 -21
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +15 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/identity-sso-store.d.ts.map +1 -1
- package/dist/server/identity-sso-store.js +30 -11
- package/dist/server/identity-sso-store.js.map +1 -1
- package/dist/server/recap-image-store.d.ts.map +1 -1
- package/dist/server/recap-image-store.js +21 -7
- package/dist/server/recap-image-store.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +30 -6
- package/dist/settings/store.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +36 -11
- package/dist/usage/store.js.map +1 -1
- package/dist/workspace-connections/store.d.ts.map +1 -1
- package/dist/workspace-connections/store.js +57 -7
- package/dist/workspace-connections/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDbExec, isConnectionError, isPostgres, intType, } from "../db/client.js";
|
|
2
|
+
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
2
3
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
3
4
|
import { emitAppStateChange, emitAppStateDelete } from "./emitter.js";
|
|
4
5
|
let _initPromise;
|
|
@@ -13,7 +14,7 @@ async function ensureTable() {
|
|
|
13
14
|
if (!_initPromise) {
|
|
14
15
|
_initPromise = (async () => {
|
|
15
16
|
const client = getDbExec();
|
|
16
|
-
|
|
17
|
+
const createSql = `
|
|
17
18
|
CREATE TABLE IF NOT EXISTS application_state (
|
|
18
19
|
session_id TEXT NOT NULL,
|
|
19
20
|
key TEXT NOT NULL,
|
|
@@ -21,7 +22,32 @@ async function ensureTable() {
|
|
|
21
22
|
updated_at ${intType()} NOT NULL,
|
|
22
23
|
PRIMARY KEY (session_id, key)
|
|
23
24
|
)
|
|
24
|
-
|
|
25
|
+
`;
|
|
26
|
+
if (isPostgres()) {
|
|
27
|
+
// Hot path: the `application_state` table and its poll indexes are
|
|
28
|
+
// virtually always already present in production. Issuing `CREATE
|
|
29
|
+
// TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh
|
|
30
|
+
// background-worker process behind a concurrent connection on the
|
|
31
|
+
// shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE
|
|
32
|
+
// TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*
|
|
33
|
+
// wrappers probe `information_schema`/`pg_indexes` first (plain reads,
|
|
34
|
+
// no lock) and run DDL ONLY for what is actually missing, bounded by a
|
|
35
|
+
// transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves
|
|
36
|
+
// the schema still missing they RE-PROBE and THROW rather than letting
|
|
37
|
+
// init memoize success against absent schema.
|
|
38
|
+
await ensureTableExists("application_state", createSql);
|
|
39
|
+
// Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres
|
|
40
|
+
// the `Date.now()` written by appStatePut() on every turn overflows int4.
|
|
41
|
+
// Widen it in place (no-op once done / on fresh BIGINT databases).
|
|
42
|
+
await widenIntColumnsToBigInt("application_state", ["updated_at"]);
|
|
43
|
+
// Indexes for the two hot poll paths. Probe pg_indexes first (no lock)
|
|
44
|
+
// and skip the SHARE-locking CREATE INDEX when already present.
|
|
45
|
+
await ensureIndexExists("app_state_updated_at_idx", `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`);
|
|
46
|
+
await ensureIndexExists("app_state_key_updated_idx", `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
50
|
+
await client.execute(createSql);
|
|
25
51
|
// Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres
|
|
26
52
|
// the `Date.now()` written by appStatePut() on every turn overflows int4.
|
|
27
53
|
// Widen it in place (no-op once done / on fresh BIGINT databases).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n isConnectionError,\n isPostgres,\n intType,\n} from \"../db/client.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '\\'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, JSON.stringify(value), Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n isConnectionError,\n isPostgres,\n intType,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '\\'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `application_state` table and its poll indexes are\n // virtually always already present in production. Issuing `CREATE\n // TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh\n // background-worker process behind a concurrent connection on the\n // shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE\n // TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*\n // wrappers probe `information_schema`/`pg_indexes` first (plain reads,\n // no lock) and run DDL ONLY for what is actually missing, bounded by a\n // transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves\n // the schema still missing they RE-PROBE and THROW rather than letting\n // init memoize success against absent schema.\n await ensureTableExists(\"application_state\", createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths. Probe pg_indexes first (no lock)\n // and skip the SHARE-locking CREATE INDEX when already present.\n await ensureIndexExists(\n \"app_state_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n );\n await ensureIndexExists(\n \"app_state_key_updated_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, JSON.stringify(value), Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/audit/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/audit/store.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAIpB,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiFvD;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BvE;AAwBD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AA2CD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,EAAE,CAAC,CAsCvB;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAW5B;AAED,6EAA6E;AAC7E,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED,gFAAgF;AAChF,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
package/dist/audit/store.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* `agent_audit_log`; reads are scoped to the caller's identity in SQL (no
|
|
8
8
|
* shares table — audit rows are never individually shared).
|
|
9
9
|
*/
|
|
10
|
-
import { getDbExec, intType,
|
|
10
|
+
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
11
|
+
import { ensureTableExists, ensureIndexExists } from "../db/ddl-guard.js";
|
|
11
12
|
let _initPromise;
|
|
12
13
|
export async function ensureAuditTables() {
|
|
13
14
|
if (!_initPromise) {
|
|
14
15
|
_initPromise = (async () => {
|
|
15
16
|
const client = getDbExec();
|
|
16
|
-
|
|
17
|
+
const createSql = `
|
|
17
18
|
CREATE TABLE IF NOT EXISTS agent_audit_log (
|
|
18
19
|
id TEXT PRIMARY KEY,
|
|
19
20
|
created_at ${intType()} NOT NULL,
|
|
@@ -33,7 +34,21 @@ export async function ensureAuditTables() {
|
|
|
33
34
|
owner_email TEXT,
|
|
34
35
|
visibility TEXT NOT NULL DEFAULT 'private'
|
|
35
36
|
)
|
|
36
|
-
|
|
37
|
+
`;
|
|
38
|
+
if (isPostgres()) {
|
|
39
|
+
// PG-guard: probe information_schema / pg_indexes before issuing DDL to
|
|
40
|
+
// avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes.
|
|
41
|
+
await ensureTableExists("agent_audit_log", createSql);
|
|
42
|
+
await ensureIndexExists("idx_audit_owner", `CREATE INDEX IF NOT EXISTS idx_audit_owner ON agent_audit_log (owner_email, created_at)`);
|
|
43
|
+
await ensureIndexExists("idx_audit_org", `CREATE INDEX IF NOT EXISTS idx_audit_org ON agent_audit_log (org_id, created_at)`);
|
|
44
|
+
await ensureIndexExists("idx_audit_target", `CREATE INDEX IF NOT EXISTS idx_audit_target ON agent_audit_log (target_type, target_id, created_at)`);
|
|
45
|
+
await ensureIndexExists("idx_audit_turn", `CREATE INDEX IF NOT EXISTS idx_audit_turn ON agent_audit_log (turn_id)`);
|
|
46
|
+
await ensureIndexExists("idx_audit_actor", `CREATE INDEX IF NOT EXISTS idx_audit_actor ON agent_audit_log (actor_email, created_at)`);
|
|
47
|
+
await ensureIndexExists("idx_audit_created", `CREATE INDEX IF NOT EXISTS idx_audit_created ON agent_audit_log (created_at)`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
51
|
+
await client.execute(createSql);
|
|
37
52
|
const indexes = [
|
|
38
53
|
`CREATE INDEX IF NOT EXISTS idx_audit_owner ON agent_audit_log (owner_email, created_at)`,
|
|
39
54
|
`CREATE INDEX IF NOT EXISTS idx_audit_org ON agent_audit_log (org_id, created_at)`,
|
package/dist/audit/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/audit/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL persistence for the framework audit log.\n *\n * Follows the same raw-SQL, provider-agnostic pattern as observability/store.ts\n * and usage/store.ts — framework tables use `getDbExec()` + `intType()` rather\n * than Drizzle ORM (which is for template-level schemas). One append-only table\n * `agent_audit_log`; reads are scoped to the caller's identity in SQL (no\n * shares table — audit rows are never individually shared).\n */\nimport { getDbExec, intType, retryOnDdlRace } from \"../db/client.js\";\nimport type {\n AuditEvent,\n AuditQueryFilters,\n AuditVisibility,\n} from \"./types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nexport async function ensureAuditTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE TABLE IF NOT EXISTS agent_audit_log (\n id TEXT PRIMARY KEY,\n created_at ${intType()} NOT NULL,\n action TEXT NOT NULL,\n caller TEXT NOT NULL,\n actor_kind TEXT NOT NULL,\n actor_email TEXT,\n org_id TEXT,\n thread_id TEXT,\n turn_id TEXT,\n target_type TEXT,\n target_id TEXT,\n status TEXT NOT NULL DEFAULT 'success',\n summary TEXT,\n input TEXT,\n error_code TEXT,\n owner_email TEXT,\n visibility TEXT NOT NULL DEFAULT 'private'\n )\n `),\n );\n\n const indexes = [\n `CREATE INDEX IF NOT EXISTS idx_audit_owner ON agent_audit_log (owner_email, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_org ON agent_audit_log (org_id, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_target ON agent_audit_log (target_type, target_id, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_turn ON agent_audit_log (turn_id)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_actor ON agent_audit_log (actor_email, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_created ON agent_audit_log (created_at)`,\n ];\n for (const sql of indexes) {\n try {\n await client.execute(sql);\n } catch {\n // Index creation is best-effort; a racing boot may have created it.\n }\n }\n })().catch((err) => {\n // Allow a later call to retry if the first init failed.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function insertAuditEvent(event: AuditEvent): Promise<void> {\n await ensureAuditTables();\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO agent_audit_log\n (id, created_at, action, caller, actor_kind, actor_email, org_id,\n thread_id, turn_id, target_type, target_id, status, summary, input,\n error_code, owner_email, visibility)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n event.id,\n event.createdAt,\n event.action,\n event.caller,\n event.actorKind,\n event.actorEmail,\n event.orgId,\n event.threadId,\n event.turnId,\n event.targetType,\n event.targetId,\n event.status,\n event.summary,\n event.input,\n event.errorCode,\n event.ownerEmail,\n event.visibility,\n ],\n });\n}\n\nfunction mapRow(row: any): AuditEvent {\n return {\n id: String(row.id),\n createdAt: Number(row.created_at),\n action: String(row.action),\n caller: String(row.caller),\n actorKind: row.actor_kind,\n actorEmail: row.actor_email ?? null,\n orgId: row.org_id ?? null,\n threadId: row.thread_id ?? null,\n turnId: row.turn_id ?? null,\n targetType: row.target_type ?? null,\n targetId: row.target_id ?? null,\n status: row.status,\n summary: row.summary ?? null,\n input: row.input ?? null,\n errorCode: row.error_code ?? null,\n ownerEmail: row.owner_email ?? null,\n visibility: (row.visibility ?? \"private\") as AuditVisibility,\n };\n}\n\nexport interface AuditReadScope {\n userEmail?: string;\n orgId?: string | null;\n}\n\n/**\n * Build the access-scoping WHERE fragment + args. A caller sees audit rows they\n * own, plus org-visible rows in their org. With no identity, nothing matches —\n * the audit log never leaks cross-tenant. Mirrors the core ownership clause of\n * `accessFilter` (minus shares, which audit rows don't have).\n */\nfunction scopeClause(scope: AuditReadScope): { sql: string; args: any[] } {\n const clauses: string[] = [];\n const args: any[] = [];\n if (scope.userEmail) {\n if (scope.orgId) {\n // Constrain the owner's rows to the active org — plus legacy/solo rows\n // that predate org-scoping (org_id IS NULL) — mirroring sharing's\n // `ownerScopeFilter`, so switching orgs doesn't surface another org's\n // trail.\n clauses.push(\"(owner_email = ? AND (org_id = ? OR org_id IS NULL))\");\n args.push(scope.userEmail, scope.orgId);\n } else {\n clauses.push(\"owner_email = ?\");\n args.push(scope.userEmail);\n }\n }\n if (scope.orgId) {\n clauses.push(\"(visibility = 'org' AND org_id = ?)\");\n args.push(scope.orgId);\n }\n if (clauses.length === 0) return { sql: \"1=0\", args };\n return { sql: `(${clauses.join(\" OR \")})`, args };\n}\n\nconst MAX_LIMIT = 500;\nconst DEFAULT_LIMIT = 100;\n\n// Columns returned by the list surface — deliberately EXCLUDES `input` so a\n// timeline query never streams every event's (redacted) request body in bulk.\n// Fetch the full payload one event at a time via `getAuditEventById`.\nconst LIST_COLUMNS =\n \"id, created_at, action, caller, actor_kind, actor_email, org_id, \" +\n \"thread_id, turn_id, target_type, target_id, status, summary, \" +\n \"error_code, owner_email, visibility\";\n\nexport async function queryAuditEvents(\n scope: AuditReadScope,\n filters: AuditQueryFilters = {},\n): Promise<AuditEvent[]> {\n await ensureAuditTables();\n if (!scope.userEmail && !scope.orgId) return [];\n const client = getDbExec();\n\n const scoped = scopeClause(scope);\n const where: string[] = [scoped.sql];\n const args: any[] = [...scoped.args];\n\n const push = (clause: string, value: any) => {\n where.push(clause);\n args.push(value);\n };\n if (filters.targetType) push(\"target_type = ?\", filters.targetType);\n if (filters.targetId) push(\"target_id = ?\", filters.targetId);\n if (filters.actorKind) push(\"actor_kind = ?\", filters.actorKind);\n if (filters.actorEmail) push(\"actor_email = ?\", filters.actorEmail);\n if (filters.status) push(\"status = ?\", filters.status);\n if (filters.threadId) push(\"thread_id = ?\", filters.threadId);\n if (filters.turnId) push(\"turn_id = ?\", filters.turnId);\n if (filters.action) push(\"action = ?\", filters.action);\n if (typeof filters.sinceMs === \"number\") {\n push(\"created_at >= ?\", Math.floor(filters.sinceMs));\n }\n\n const limit = Math.min(\n Math.max(1, Math.floor(filters.limit ?? DEFAULT_LIMIT)),\n MAX_LIMIT,\n );\n\n const result = await client.execute({\n sql: `SELECT ${LIST_COLUMNS} FROM agent_audit_log\n WHERE ${where.join(\" AND \")}\n ORDER BY created_at DESC\n LIMIT ?`,\n args: [...args, limit],\n });\n return (result.rows ?? []).map(mapRow);\n}\n\nexport async function getAuditEventById(\n id: string,\n scope: AuditReadScope,\n): Promise<AuditEvent | null> {\n await ensureAuditTables();\n if (!scope.userEmail && !scope.orgId) return null;\n const client = getDbExec();\n const scoped = scopeClause(scope);\n const result = await client.execute({\n sql: `SELECT * FROM agent_audit_log WHERE id = ? AND ${scoped.sql} LIMIT 1`,\n args: [id, ...scoped.args],\n });\n const row = (result.rows ?? [])[0];\n return row ? mapRow(row) : null;\n}\n\n/** Purge audit rows older than `cutoffMs`. Returns the deleted row count. */\nexport async function deleteOldAuditEvents(cutoffMs: number): Promise<number> {\n await ensureAuditTables();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM agent_audit_log WHERE created_at < ?`,\n args: [Math.floor(cutoffMs)],\n });\n return Number(result.rowsAffected ?? 0);\n}\n\n/** Test-only: reset the cached init promise so a fresh DB re-creates tables. */\nexport function __resetAuditInitForTests(): void {\n _initPromise = undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/audit/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL persistence for the framework audit log.\n *\n * Follows the same raw-SQL, provider-agnostic pattern as observability/store.ts\n * and usage/store.ts — framework tables use `getDbExec()` + `intType()` rather\n * than Drizzle ORM (which is for template-level schemas). One append-only table\n * `agent_audit_log`; reads are scoped to the caller's identity in SQL (no\n * shares table — audit rows are never individually shared).\n */\nimport { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists, ensureIndexExists } from \"../db/ddl-guard.js\";\nimport type {\n AuditEvent,\n AuditQueryFilters,\n AuditVisibility,\n} from \"./types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nexport async function ensureAuditTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS agent_audit_log (\n id TEXT PRIMARY KEY,\n created_at ${intType()} NOT NULL,\n action TEXT NOT NULL,\n caller TEXT NOT NULL,\n actor_kind TEXT NOT NULL,\n actor_email TEXT,\n org_id TEXT,\n thread_id TEXT,\n turn_id TEXT,\n target_type TEXT,\n target_id TEXT,\n status TEXT NOT NULL DEFAULT 'success',\n summary TEXT,\n input TEXT,\n error_code TEXT,\n owner_email TEXT,\n visibility TEXT NOT NULL DEFAULT 'private'\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema / pg_indexes before issuing DDL to\n // avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes.\n await ensureTableExists(\"agent_audit_log\", createSql);\n await ensureIndexExists(\n \"idx_audit_owner\",\n `CREATE INDEX IF NOT EXISTS idx_audit_owner ON agent_audit_log (owner_email, created_at)`,\n );\n await ensureIndexExists(\n \"idx_audit_org\",\n `CREATE INDEX IF NOT EXISTS idx_audit_org ON agent_audit_log (org_id, created_at)`,\n );\n await ensureIndexExists(\n \"idx_audit_target\",\n `CREATE INDEX IF NOT EXISTS idx_audit_target ON agent_audit_log (target_type, target_id, created_at)`,\n );\n await ensureIndexExists(\n \"idx_audit_turn\",\n `CREATE INDEX IF NOT EXISTS idx_audit_turn ON agent_audit_log (turn_id)`,\n );\n await ensureIndexExists(\n \"idx_audit_actor\",\n `CREATE INDEX IF NOT EXISTS idx_audit_actor ON agent_audit_log (actor_email, created_at)`,\n );\n await ensureIndexExists(\n \"idx_audit_created\",\n `CREATE INDEX IF NOT EXISTS idx_audit_created ON agent_audit_log (created_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n const indexes = [\n `CREATE INDEX IF NOT EXISTS idx_audit_owner ON agent_audit_log (owner_email, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_org ON agent_audit_log (org_id, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_target ON agent_audit_log (target_type, target_id, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_turn ON agent_audit_log (turn_id)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_actor ON agent_audit_log (actor_email, created_at)`,\n `CREATE INDEX IF NOT EXISTS idx_audit_created ON agent_audit_log (created_at)`,\n ];\n for (const sql of indexes) {\n try {\n await client.execute(sql);\n } catch {\n // Index creation is best-effort; a racing boot may have created it.\n }\n }\n })().catch((err) => {\n // Allow a later call to retry if the first init failed.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function insertAuditEvent(event: AuditEvent): Promise<void> {\n await ensureAuditTables();\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO agent_audit_log\n (id, created_at, action, caller, actor_kind, actor_email, org_id,\n thread_id, turn_id, target_type, target_id, status, summary, input,\n error_code, owner_email, visibility)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n event.id,\n event.createdAt,\n event.action,\n event.caller,\n event.actorKind,\n event.actorEmail,\n event.orgId,\n event.threadId,\n event.turnId,\n event.targetType,\n event.targetId,\n event.status,\n event.summary,\n event.input,\n event.errorCode,\n event.ownerEmail,\n event.visibility,\n ],\n });\n}\n\nfunction mapRow(row: any): AuditEvent {\n return {\n id: String(row.id),\n createdAt: Number(row.created_at),\n action: String(row.action),\n caller: String(row.caller),\n actorKind: row.actor_kind,\n actorEmail: row.actor_email ?? null,\n orgId: row.org_id ?? null,\n threadId: row.thread_id ?? null,\n turnId: row.turn_id ?? null,\n targetType: row.target_type ?? null,\n targetId: row.target_id ?? null,\n status: row.status,\n summary: row.summary ?? null,\n input: row.input ?? null,\n errorCode: row.error_code ?? null,\n ownerEmail: row.owner_email ?? null,\n visibility: (row.visibility ?? \"private\") as AuditVisibility,\n };\n}\n\nexport interface AuditReadScope {\n userEmail?: string;\n orgId?: string | null;\n}\n\n/**\n * Build the access-scoping WHERE fragment + args. A caller sees audit rows they\n * own, plus org-visible rows in their org. With no identity, nothing matches —\n * the audit log never leaks cross-tenant. Mirrors the core ownership clause of\n * `accessFilter` (minus shares, which audit rows don't have).\n */\nfunction scopeClause(scope: AuditReadScope): { sql: string; args: any[] } {\n const clauses: string[] = [];\n const args: any[] = [];\n if (scope.userEmail) {\n if (scope.orgId) {\n // Constrain the owner's rows to the active org — plus legacy/solo rows\n // that predate org-scoping (org_id IS NULL) — mirroring sharing's\n // `ownerScopeFilter`, so switching orgs doesn't surface another org's\n // trail.\n clauses.push(\"(owner_email = ? AND (org_id = ? OR org_id IS NULL))\");\n args.push(scope.userEmail, scope.orgId);\n } else {\n clauses.push(\"owner_email = ?\");\n args.push(scope.userEmail);\n }\n }\n if (scope.orgId) {\n clauses.push(\"(visibility = 'org' AND org_id = ?)\");\n args.push(scope.orgId);\n }\n if (clauses.length === 0) return { sql: \"1=0\", args };\n return { sql: `(${clauses.join(\" OR \")})`, args };\n}\n\nconst MAX_LIMIT = 500;\nconst DEFAULT_LIMIT = 100;\n\n// Columns returned by the list surface — deliberately EXCLUDES `input` so a\n// timeline query never streams every event's (redacted) request body in bulk.\n// Fetch the full payload one event at a time via `getAuditEventById`.\nconst LIST_COLUMNS =\n \"id, created_at, action, caller, actor_kind, actor_email, org_id, \" +\n \"thread_id, turn_id, target_type, target_id, status, summary, \" +\n \"error_code, owner_email, visibility\";\n\nexport async function queryAuditEvents(\n scope: AuditReadScope,\n filters: AuditQueryFilters = {},\n): Promise<AuditEvent[]> {\n await ensureAuditTables();\n if (!scope.userEmail && !scope.orgId) return [];\n const client = getDbExec();\n\n const scoped = scopeClause(scope);\n const where: string[] = [scoped.sql];\n const args: any[] = [...scoped.args];\n\n const push = (clause: string, value: any) => {\n where.push(clause);\n args.push(value);\n };\n if (filters.targetType) push(\"target_type = ?\", filters.targetType);\n if (filters.targetId) push(\"target_id = ?\", filters.targetId);\n if (filters.actorKind) push(\"actor_kind = ?\", filters.actorKind);\n if (filters.actorEmail) push(\"actor_email = ?\", filters.actorEmail);\n if (filters.status) push(\"status = ?\", filters.status);\n if (filters.threadId) push(\"thread_id = ?\", filters.threadId);\n if (filters.turnId) push(\"turn_id = ?\", filters.turnId);\n if (filters.action) push(\"action = ?\", filters.action);\n if (typeof filters.sinceMs === \"number\") {\n push(\"created_at >= ?\", Math.floor(filters.sinceMs));\n }\n\n const limit = Math.min(\n Math.max(1, Math.floor(filters.limit ?? DEFAULT_LIMIT)),\n MAX_LIMIT,\n );\n\n const result = await client.execute({\n sql: `SELECT ${LIST_COLUMNS} FROM agent_audit_log\n WHERE ${where.join(\" AND \")}\n ORDER BY created_at DESC\n LIMIT ?`,\n args: [...args, limit],\n });\n return (result.rows ?? []).map(mapRow);\n}\n\nexport async function getAuditEventById(\n id: string,\n scope: AuditReadScope,\n): Promise<AuditEvent | null> {\n await ensureAuditTables();\n if (!scope.userEmail && !scope.orgId) return null;\n const client = getDbExec();\n const scoped = scopeClause(scope);\n const result = await client.execute({\n sql: `SELECT * FROM agent_audit_log WHERE id = ? AND ${scoped.sql} LIMIT 1`,\n args: [id, ...scoped.args],\n });\n const row = (result.rows ?? [])[0];\n return row ? mapRow(row) : null;\n}\n\n/** Purge audit rows older than `cutoffMs`. Returns the deleted row count. */\nexport async function deleteOldAuditEvents(cutoffMs: number): Promise<number> {\n await ensureAuditTables();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM agent_audit_log WHERE created_at < ?`,\n args: [Math.floor(cutoffMs)],\n });\n return Number(result.rowsAffected ?? 0);\n}\n\n/** Test-only: reset the cached init promise so a fresh DB re-creates tables. */\nexport function __resetAuditInitForTests(): void {\n _initPromise = undefined;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/browser-sessions/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/browser-sessions/store.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,+BAA+B,EAC/B,gCAAgC,EAGhC,2CAA2C,EAC3C,sCAAsC,EACvC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,8BAA8B,QAAS,CAAC;AACrD,eAAO,MAAM,0CAA0C,QAAS,CAAC;AACjE,eAAO,MAAM,uCAAuC,MAAM,CAAC;AA0S3D,wBAAsB,sBAAsB,CAC1C,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,sCAAsC,GAC5C,OAAO,CAAC,+BAA+B,CAAC,CA2C1C;AAED,wBAAsB,mBAAmB,CACvC,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAqB5C;AAED,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,OAAO,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAYjD;AAED,wBAAsB,wBAAwB,CAC5C,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AA8CD,wBAAsB,2BAA2B,CAC/C,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,2CAA2C,GACjD,OAAO,CAAC,gCAAgC,CAAC,CAqC3C;AAED,wBAAsB,wBAAwB,CAC5C,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAUlD;AAED,wBAAsB,0BAA0B,CAC9C,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC,CA6BlD;AAED,wBAAsB,6BAA6B,CACjD,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,MAAM,EACF;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAClD,OAAO,CAAC,gCAAgC,CAAC,CAoC3C;AAED,wBAAsB,4BAA4B,CAChD,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,OAAO,CAAC,CAyClB;AAED,wBAAsB,kBAAkB,CACtC,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,2CAA2C,EAClD,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,OAAO,CAAC,CAalB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDbExec, intType, isPostgres, retryOnDdlRace, safeJsonParse, } from "../db/client.js";
|
|
2
|
+
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
2
3
|
export const DEFAULT_BROWSER_SESSION_TTL_MS = 45_000;
|
|
3
4
|
export const DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS = 30_000;
|
|
4
5
|
export const DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS = 250;
|
|
@@ -16,7 +17,7 @@ async function ensureTables() {
|
|
|
16
17
|
if (!initPromise) {
|
|
17
18
|
initPromise = (async () => {
|
|
18
19
|
const client = getDbExec();
|
|
19
|
-
|
|
20
|
+
const createSessionsSql = `
|
|
20
21
|
CREATE TABLE IF NOT EXISTS ${SESSION_TABLE} (
|
|
21
22
|
owner_email TEXT NOT NULL,
|
|
22
23
|
session_id TEXT NOT NULL,
|
|
@@ -30,12 +31,8 @@ async function ensureTables() {
|
|
|
30
31
|
expires_at ${intType()} NOT NULL,
|
|
31
32
|
PRIMARY KEY (owner_email, session_id)
|
|
32
33
|
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx
|
|
36
|
-
ON ${SESSION_TABLE} (owner_email, last_seen_at)
|
|
37
|
-
`));
|
|
38
|
-
await retryOnDdlRace(() => client.execute(`
|
|
34
|
+
`;
|
|
35
|
+
const createRequestsSql = `
|
|
39
36
|
CREATE TABLE IF NOT EXISTS ${REQUEST_TABLE} (
|
|
40
37
|
owner_email TEXT NOT NULL,
|
|
41
38
|
session_id TEXT NOT NULL,
|
|
@@ -53,7 +50,25 @@ async function ensureTables() {
|
|
|
53
50
|
error TEXT,
|
|
54
51
|
PRIMARY KEY (owner_email, request_id)
|
|
55
52
|
)
|
|
53
|
+
`;
|
|
54
|
+
if (isPostgres()) {
|
|
55
|
+
// PG-guard: probe information_schema / pg_indexes first (no lock) and
|
|
56
|
+
// only issue DDL when the table/index is actually missing, wrapped in
|
|
57
|
+
// a transaction-scoped lock_timeout so a contended lock fails fast.
|
|
58
|
+
await ensureTableExists(SESSION_TABLE, createSessionsSql);
|
|
59
|
+
await ensureIndexExists("agent_native_browser_sessions_owner_seen_idx", `CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx ON ${SESSION_TABLE} (owner_email, last_seen_at)`);
|
|
60
|
+
await ensureTableExists(REQUEST_TABLE, createRequestsSql);
|
|
61
|
+
await ensureIndexExists("agent_native_browser_session_requests_pending_idx", `CREATE INDEX IF NOT EXISTS agent_native_browser_session_requests_pending_idx ON ${REQUEST_TABLE} (owner_email, session_id, status, created_at)`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// SQLite (local dev): no ACCESS EXCLUSIVE lock problem — keep existing
|
|
65
|
+
// retryOnDdlRace behaviour.
|
|
66
|
+
await retryOnDdlRace(() => client.execute(createSessionsSql));
|
|
67
|
+
await retryOnDdlRace(() => client.execute(`
|
|
68
|
+
CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx
|
|
69
|
+
ON ${SESSION_TABLE} (owner_email, last_seen_at)
|
|
56
70
|
`));
|
|
71
|
+
await retryOnDdlRace(() => client.execute(createRequestsSql));
|
|
57
72
|
await retryOnDdlRace(() => client.execute(`
|
|
58
73
|
CREATE INDEX IF NOT EXISTS agent_native_browser_session_requests_pending_idx
|
|
59
74
|
ON ${REQUEST_TABLE} (owner_email, session_id, status, created_at)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/browser-sessions/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n intType,\n isPostgres,\n retryOnDdlRace,\n safeJsonParse,\n} from \"../db/client.js\";\nimport type {\n AgentNativeBrowserSession,\n AgentNativeBrowserSessionAction,\n AgentNativeBrowserSessionRecord,\n AgentNativeBrowserSessionRequest,\n AgentNativeBrowserSessionRequestStatus,\n AgentNativeBrowserSessionRequestType,\n CreateAgentNativeBrowserSessionRequestInput,\n RegisterAgentNativeBrowserSessionInput,\n} from \"./types.js\";\n\nexport const DEFAULT_BROWSER_SESSION_TTL_MS = 45_000;\nexport const DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS = 30_000;\nexport const DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS = 250;\n\nconst SESSION_TABLE = \"agent_native_browser_sessions\";\nconst REQUEST_TABLE = \"agent_native_browser_session_requests\";\nconst SAFE_ID_RE = /^[A-Za-z0-9._:-]{1,160}$/;\n\nlet initPromise: Promise<void> | undefined;\n\nfunction nowMs(): number {\n return Date.now();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function ensureTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const client = getDbExec();\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE TABLE IF NOT EXISTS ${SESSION_TABLE} (\n owner_email TEXT NOT NULL,\n session_id TEXT NOT NULL,\n label TEXT,\n url TEXT,\n session_json TEXT NOT NULL,\n context_json TEXT,\n actions_json TEXT,\n connected_at ${intType()} NOT NULL,\n last_seen_at ${intType()} NOT NULL,\n expires_at ${intType()} NOT NULL,\n PRIMARY KEY (owner_email, session_id)\n )\n `),\n );\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx\n ON ${SESSION_TABLE} (owner_email, last_seen_at)\n `),\n );\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE TABLE IF NOT EXISTS ${REQUEST_TABLE} (\n owner_email TEXT NOT NULL,\n session_id TEXT NOT NULL,\n request_id TEXT NOT NULL,\n type TEXT NOT NULL,\n name TEXT,\n command TEXT,\n payload_json TEXT,\n status TEXT NOT NULL,\n created_at ${intType()} NOT NULL,\n claimed_at ${intType()},\n completed_at ${intType()},\n expires_at ${intType()} NOT NULL,\n result_json TEXT,\n error TEXT,\n PRIMARY KEY (owner_email, request_id)\n )\n `),\n );\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE INDEX IF NOT EXISTS agent_native_browser_session_requests_pending_idx\n ON ${REQUEST_TABLE} (owner_email, session_id, status, created_at)\n `),\n );\n })().catch((err) => {\n // Don't cache a transient init failure — otherwise every browser-session\n // call re-awaits the same rejected promise until the process restarts.\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nfunction assertOwnerEmail(ownerEmail: string): string {\n const trimmed = ownerEmail.trim();\n if (!trimmed) throw new Error(\"ownerEmail is required\");\n return trimmed;\n}\n\nfunction assertSafeId(value: string, label: string): string {\n const trimmed = value.trim();\n if (!SAFE_ID_RE.test(trimmed)) {\n throw new Error(\n `${label} must be 1-160 characters using letters, numbers, dot, underscore, colon, or dash`,\n );\n }\n return trimmed;\n}\n\nfunction generateId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction json(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n\nfunction parseOptionalObject(\n value: unknown,\n): Record<string, unknown> | undefined {\n if (value == null) return undefined;\n const parsed = safeJsonParse<unknown>(value, undefined);\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n}\n\nfunction parseActions(value: unknown): AgentNativeBrowserSessionAction[] {\n const parsed = safeJsonParse<unknown>(value, []);\n return Array.isArray(parsed)\n ? (parsed.filter(\n (action) =>\n action &&\n typeof action === \"object\" &&\n typeof (action as { name?: unknown }).name === \"string\",\n ) as AgentNativeBrowserSessionAction[])\n : [];\n}\n\nfunction parseSession(\n value: unknown,\n sessionId: string,\n): AgentNativeBrowserSession {\n const parsed = safeJsonParse<unknown>(value, { id: sessionId });\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? ({\n id: sessionId,\n ...(parsed as Record<string, unknown>),\n } as AgentNativeBrowserSession)\n : { id: sessionId };\n}\n\nfunction coerceTime(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim()) {\n const parsed = Date.parse(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return undefined;\n}\n\nfunction normalizeSessionInput(input: RegisterAgentNativeBrowserSessionInput): {\n sessionId: string;\n session: AgentNativeBrowserSession;\n label?: string;\n url?: string;\n context?: Record<string, unknown>;\n actions: AgentNativeBrowserSessionAction[];\n connectedAt: number;\n ttlMs: number;\n} {\n const sessionRecord: Partial<AgentNativeBrowserSession> =\n input.session && typeof input.session === \"object\" ? input.session : {};\n const rawId = input.sessionId || sessionRecord.id;\n if (typeof rawId !== \"string\") {\n throw new Error(\"session.id is required\");\n }\n const sessionId = assertSafeId(rawId, \"session.id\");\n const now = nowMs();\n const connectedAt = coerceTime(sessionRecord.connectedAt) ?? now;\n const label =\n typeof input.label === \"string\" && input.label.trim()\n ? input.label.trim()\n : typeof sessionRecord.label === \"string\" && sessionRecord.label.trim()\n ? sessionRecord.label.trim()\n : undefined;\n const url =\n typeof input.url === \"string\" && input.url.trim()\n ? input.url.trim()\n : typeof sessionRecord.url === \"string\" && sessionRecord.url.trim()\n ? sessionRecord.url.trim()\n : undefined;\n const session: AgentNativeBrowserSession = {\n ...sessionRecord,\n id: sessionId,\n ...(label ? { label } : {}),\n connectedAt: new Date(connectedAt).toISOString(),\n ...(url ? { url } : {}),\n };\n return {\n sessionId,\n session,\n label,\n url,\n context:\n input.context &&\n typeof input.context === \"object\" &&\n !Array.isArray(input.context)\n ? input.context\n : undefined,\n actions: Array.isArray(input.actions) ? input.actions : [],\n connectedAt,\n ttlMs:\n typeof input.ttlMs === \"number\" && input.ttlMs > 0\n ? Math.min(input.ttlMs, 5 * 60 * 1000)\n : DEFAULT_BROWSER_SESSION_TTL_MS,\n };\n}\n\nfunction rowToSession(\n row: Record<string, unknown>,\n now = nowMs(),\n): AgentNativeBrowserSessionRecord {\n const sessionId = String(row.session_id ?? \"\");\n const expiresAt = Number(row.expires_at ?? 0);\n return {\n sessionId,\n session: parseSession(row.session_json, sessionId),\n label: typeof row.label === \"string\" ? row.label : undefined,\n url: typeof row.url === \"string\" ? row.url : undefined,\n context: parseOptionalObject(row.context_json),\n actions: parseActions(row.actions_json),\n connectedAt: Number(row.connected_at ?? 0),\n lastSeenAt: Number(row.last_seen_at ?? 0),\n expiresAt,\n active: expiresAt > now,\n };\n}\n\nfunction rowToRequest(\n row: Record<string, unknown>,\n): AgentNativeBrowserSessionRequest {\n const type = String(\n row.type ?? \"get-context\",\n ) as AgentNativeBrowserSessionRequestType;\n const payload = safeJsonParse<unknown>(row.payload_json, undefined);\n const result = safeJsonParse<unknown>(row.result_json, undefined);\n const request: AgentNativeBrowserSessionRequest = {\n id: String(row.request_id ?? \"\"),\n sessionId: String(row.session_id ?? \"\"),\n type,\n status: String(\n row.status ?? \"pending\",\n ) as AgentNativeBrowserSessionRequestStatus,\n createdAt: Number(row.created_at ?? 0),\n expiresAt: Number(row.expires_at ?? 0),\n ...(typeof row.name === \"string\" && row.name ? { name: row.name } : {}),\n ...(typeof row.command === \"string\" && row.command\n ? { command: row.command }\n : {}),\n ...(row.claimed_at != null ? { claimedAt: Number(row.claimed_at) } : {}),\n ...(row.completed_at != null\n ? { completedAt: Number(row.completed_at) }\n : {}),\n ...(row.error ? { error: String(row.error) } : {}),\n ...(row.result_json != null ? { result } : {}),\n };\n if (type === \"run-action\") request.args = payload;\n else request.payload = payload;\n return request;\n}\n\nasync function expireOldRequests(\n ownerEmail: string,\n sessionId?: string,\n): Promise<void> {\n await ensureTables();\n const client = getDbExec();\n const now = nowMs();\n await client.execute({\n sql: sessionId\n ? `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser-session request expired'\n WHERE owner_email = ? AND session_id = ? AND status IN ('pending', 'claimed') AND expires_at < ?`\n : `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser-session request expired'\n WHERE owner_email = ? AND status IN ('pending', 'claimed') AND expires_at < ?`,\n args: sessionId\n ? [now, ownerEmail, sessionId, now]\n : [now, ownerEmail, now],\n });\n}\n\nexport async function registerBrowserSession(\n ownerEmailInput: string,\n input: RegisterAgentNativeBrowserSessionInput,\n): Promise<AgentNativeBrowserSessionRecord> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const normalized = normalizeSessionInput(input);\n await ensureTables();\n const client = getDbExec();\n const now = nowMs();\n const expiresAt = now + normalized.ttlMs;\n\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${SESSION_TABLE}\n (owner_email, session_id, label, url, session_json, context_json, actions_json, connected_at, last_seen_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (owner_email, session_id) DO UPDATE SET\n label = EXCLUDED.label,\n url = EXCLUDED.url,\n session_json = EXCLUDED.session_json,\n context_json = EXCLUDED.context_json,\n actions_json = EXCLUDED.actions_json,\n last_seen_at = EXCLUDED.last_seen_at,\n expires_at = EXCLUDED.expires_at`\n : `INSERT OR REPLACE INTO ${SESSION_TABLE}\n (owner_email, session_id, label, url, session_json, context_json, actions_json, connected_at, last_seen_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n ownerEmail,\n normalized.sessionId,\n normalized.label ?? null,\n normalized.url ?? null,\n json(normalized.session),\n normalized.context ? json(normalized.context) : null,\n json(normalized.actions),\n normalized.connectedAt,\n now,\n expiresAt,\n ],\n });\n\n const saved = await getBrowserSession(ownerEmail, normalized.sessionId, {\n includeExpired: true,\n });\n if (!saved) throw new Error(\"Failed to register browser session\");\n return saved;\n}\n\nexport async function listBrowserSessions(\n ownerEmailInput: string,\n options: { includeExpired?: boolean; limit?: number } = {},\n): Promise<AgentNativeBrowserSessionRecord[]> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n await ensureTables();\n await expireOldRequests(ownerEmail);\n const client = getDbExec();\n const limit =\n typeof options.limit === \"number\" && options.limit > 0\n ? Math.min(Math.floor(options.limit), 100)\n : 25;\n const now = nowMs();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${SESSION_TABLE}\n WHERE owner_email = ?\n ORDER BY last_seen_at DESC\n LIMIT ?`,\n args: [ownerEmail, limit],\n });\n const sessions = rows.map((row) => rowToSession(row, now));\n return options.includeExpired\n ? sessions\n : sessions.filter((session) => session.active);\n}\n\nexport async function getBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n options: { includeExpired?: boolean } = {},\n): Promise<AgentNativeBrowserSessionRecord | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${SESSION_TABLE} WHERE owner_email = ? AND session_id = ?`,\n args: [ownerEmail, sessionId],\n });\n if (rows.length === 0) return null;\n const session = rowToSession(rows[0]);\n return options.includeExpired || session.active ? session : null;\n}\n\nexport async function disconnectBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n): Promise<boolean> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n const client = getDbExec();\n await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser session disconnected'\n WHERE owner_email = ? AND session_id = ? AND status IN ('pending', 'claimed')`,\n args: [nowMs(), ownerEmail, sessionId],\n });\n const result = await client.execute({\n sql: `DELETE FROM ${SESSION_TABLE} WHERE owner_email = ? AND session_id = ?`,\n args: [ownerEmail, sessionId],\n });\n return result.rowsAffected > 0;\n}\n\nfunction normalizeRequestInput(\n input: CreateAgentNativeBrowserSessionRequestInput,\n): {\n type: AgentNativeBrowserSessionRequestType;\n name?: string;\n command?: string;\n payload?: unknown;\n timeoutMs: number;\n} {\n if (\n input.type !== \"get-context\" &&\n input.type !== \"list-actions\" &&\n input.type !== \"run-action\" &&\n input.type !== \"command\"\n ) {\n throw new Error(\n \"request type must be get-context, list-actions, run-action, or command\",\n );\n }\n const name =\n typeof input.name === \"string\" && input.name.trim()\n ? input.name.trim()\n : undefined;\n if (input.type === \"run-action\" && !name) {\n throw new Error(\"name is required for run-action requests\");\n }\n const command =\n typeof input.command === \"string\" && input.command.trim()\n ? input.command.trim()\n : input.type === \"command\"\n ? \"refreshData\"\n : undefined;\n return {\n type: input.type,\n name,\n command,\n payload: input.type === \"run-action\" ? input.args : input.payload,\n timeoutMs:\n typeof input.timeoutMs === \"number\" && input.timeoutMs > 0\n ? Math.min(input.timeoutMs, 2 * 60 * 1000)\n : DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS,\n };\n}\n\nexport async function createBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n input: CreateAgentNativeBrowserSessionRequestInput,\n): Promise<AgentNativeBrowserSessionRequest> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n const session = await getBrowserSession(ownerEmail, sessionId);\n if (!session) {\n throw new Error(`No active browser session found for \"${sessionId}\"`);\n }\n const normalized = normalizeRequestInput(input);\n await ensureTables();\n const client = getDbExec();\n const createdAt = nowMs();\n const requestId = generateId(\"browser-request\");\n const expiresAt = createdAt + normalized.timeoutMs + 15_000;\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${REQUEST_TABLE}\n (owner_email, session_id, request_id, type, name, command, payload_json, status, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)\n ON CONFLICT (owner_email, request_id) DO NOTHING`\n : `INSERT OR IGNORE INTO ${REQUEST_TABLE}\n (owner_email, session_id, request_id, type, name, command, payload_json, status, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,\n args: [\n ownerEmail,\n sessionId,\n requestId,\n normalized.type,\n normalized.name ?? null,\n normalized.command ?? null,\n json(normalized.payload),\n createdAt,\n expiresAt,\n ],\n });\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request) throw new Error(\"Failed to create browser-session request\");\n return request;\n}\n\nexport async function getBrowserSessionRequest(\n ownerEmailInput: string,\n requestIdInput: string,\n): Promise<AgentNativeBrowserSessionRequest | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n await ensureTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${REQUEST_TABLE} WHERE owner_email = ? AND request_id = ?`,\n args: [ownerEmail, requestId],\n });\n return rows.length ? rowToRequest(rows[0]) : null;\n}\n\nexport async function claimBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n): Promise<AgentNativeBrowserSessionRequest | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n await expireOldRequests(ownerEmail, sessionId);\n const client = getDbExec();\n const now = nowMs();\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${REQUEST_TABLE}\n WHERE owner_email = ? AND session_id = ? AND status = 'pending' AND expires_at >= ?\n ORDER BY created_at ASC\n LIMIT 1`,\n args: [ownerEmail, sessionId, now],\n });\n if (rows.length === 0) return null;\n const candidate = rowToRequest(rows[0]);\n const updated = await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = 'claimed', claimed_at = ?\n WHERE owner_email = ? AND request_id = ? AND status = 'pending'`,\n args: [now, ownerEmail, candidate.id],\n });\n if (updated.rowsAffected > 0) {\n return getBrowserSessionRequest(ownerEmail, candidate.id);\n }\n }\n return null;\n}\n\nexport async function completeBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n requestIdInput: string,\n result:\n | { ok: true; result?: unknown }\n | { ok: false; error?: string; result?: unknown },\n): Promise<AgentNativeBrowserSessionRequest> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n await ensureTables();\n const client = getDbExec();\n const completedAt = nowMs();\n const status: AgentNativeBrowserSessionRequestStatus =\n result.ok === true ? \"completed\" : \"failed\";\n const error =\n result.ok === false\n ? result.error || \"Browser-session request failed\"\n : null;\n const updated = await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = ?, completed_at = ?, result_json = ?, error = ?\n WHERE owner_email = ? AND session_id = ? AND request_id = ? AND status IN ('pending', 'claimed')`,\n args: [\n status,\n completedAt,\n \"result\" in result ? json(result.result) : null,\n error,\n ownerEmail,\n sessionId,\n requestId,\n ],\n });\n if (updated.rowsAffected === 0) {\n const existing = await getBrowserSessionRequest(ownerEmail, requestId);\n if (existing) return existing;\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n }\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request)\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n return request;\n}\n\nexport async function waitForBrowserSessionRequest(\n ownerEmailInput: string,\n requestIdInput: string,\n options: { timeoutMs?: number; pollMs?: number } = {},\n): Promise<unknown> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n const timeoutMs =\n typeof options.timeoutMs === \"number\" && options.timeoutMs > 0\n ? options.timeoutMs\n : DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS;\n const pollMs =\n typeof options.pollMs === \"number\" && options.pollMs > 0\n ? options.pollMs\n : DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS;\n const deadline = nowMs() + timeoutMs;\n\n while (nowMs() <= deadline) {\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request)\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n if (request.status === \"completed\") return request.result;\n if (request.status === \"failed\") {\n throw new Error(request.error || \"Browser-session request failed\");\n }\n if (request.status === \"expired\" || request.expiresAt < nowMs()) {\n await expireOldRequests(ownerEmail, request.sessionId);\n throw new Error(request.error || \"Browser-session request expired\");\n }\n await sleep(Math.min(pollMs, Math.max(1, deadline - nowMs())));\n }\n\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (request) {\n await completeBrowserSessionRequest(\n ownerEmail,\n request.sessionId,\n requestId,\n {\n ok: false,\n error: \"Timed out waiting for browser-session response\",\n },\n );\n }\n throw new Error(\"Timed out waiting for browser-session response\");\n}\n\nexport async function callBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n input: CreateAgentNativeBrowserSessionRequestInput,\n options: { timeoutMs?: number; pollMs?: number } = {},\n): Promise<unknown> {\n const request = await createBrowserSessionRequest(\n ownerEmailInput,\n sessionIdInput,\n {\n ...input,\n timeoutMs: input.timeoutMs ?? options.timeoutMs,\n },\n );\n return waitForBrowserSessionRequest(ownerEmailInput, request.id, {\n timeoutMs: options.timeoutMs ?? input.timeoutMs,\n pollMs: options.pollMs,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/browser-sessions/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n intType,\n isPostgres,\n retryOnDdlRace,\n safeJsonParse,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport type {\n AgentNativeBrowserSession,\n AgentNativeBrowserSessionAction,\n AgentNativeBrowserSessionRecord,\n AgentNativeBrowserSessionRequest,\n AgentNativeBrowserSessionRequestStatus,\n AgentNativeBrowserSessionRequestType,\n CreateAgentNativeBrowserSessionRequestInput,\n RegisterAgentNativeBrowserSessionInput,\n} from \"./types.js\";\n\nexport const DEFAULT_BROWSER_SESSION_TTL_MS = 45_000;\nexport const DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS = 30_000;\nexport const DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS = 250;\n\nconst SESSION_TABLE = \"agent_native_browser_sessions\";\nconst REQUEST_TABLE = \"agent_native_browser_session_requests\";\nconst SAFE_ID_RE = /^[A-Za-z0-9._:-]{1,160}$/;\n\nlet initPromise: Promise<void> | undefined;\n\nfunction nowMs(): number {\n return Date.now();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function ensureTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const client = getDbExec();\n const createSessionsSql = `\n CREATE TABLE IF NOT EXISTS ${SESSION_TABLE} (\n owner_email TEXT NOT NULL,\n session_id TEXT NOT NULL,\n label TEXT,\n url TEXT,\n session_json TEXT NOT NULL,\n context_json TEXT,\n actions_json TEXT,\n connected_at ${intType()} NOT NULL,\n last_seen_at ${intType()} NOT NULL,\n expires_at ${intType()} NOT NULL,\n PRIMARY KEY (owner_email, session_id)\n )\n `;\n const createRequestsSql = `\n CREATE TABLE IF NOT EXISTS ${REQUEST_TABLE} (\n owner_email TEXT NOT NULL,\n session_id TEXT NOT NULL,\n request_id TEXT NOT NULL,\n type TEXT NOT NULL,\n name TEXT,\n command TEXT,\n payload_json TEXT,\n status TEXT NOT NULL,\n created_at ${intType()} NOT NULL,\n claimed_at ${intType()},\n completed_at ${intType()},\n expires_at ${intType()} NOT NULL,\n result_json TEXT,\n error TEXT,\n PRIMARY KEY (owner_email, request_id)\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema / pg_indexes first (no lock) and\n // only issue DDL when the table/index is actually missing, wrapped in\n // a transaction-scoped lock_timeout so a contended lock fails fast.\n await ensureTableExists(SESSION_TABLE, createSessionsSql);\n await ensureIndexExists(\n \"agent_native_browser_sessions_owner_seen_idx\",\n `CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx ON ${SESSION_TABLE} (owner_email, last_seen_at)`,\n );\n await ensureTableExists(REQUEST_TABLE, createRequestsSql);\n await ensureIndexExists(\n \"agent_native_browser_session_requests_pending_idx\",\n `CREATE INDEX IF NOT EXISTS agent_native_browser_session_requests_pending_idx ON ${REQUEST_TABLE} (owner_email, session_id, status, created_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no ACCESS EXCLUSIVE lock problem — keep existing\n // retryOnDdlRace behaviour.\n await retryOnDdlRace(() => client.execute(createSessionsSql));\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE INDEX IF NOT EXISTS agent_native_browser_sessions_owner_seen_idx\n ON ${SESSION_TABLE} (owner_email, last_seen_at)\n `),\n );\n await retryOnDdlRace(() => client.execute(createRequestsSql));\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE INDEX IF NOT EXISTS agent_native_browser_session_requests_pending_idx\n ON ${REQUEST_TABLE} (owner_email, session_id, status, created_at)\n `),\n );\n })().catch((err) => {\n // Don't cache a transient init failure — otherwise every browser-session\n // call re-awaits the same rejected promise until the process restarts.\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nfunction assertOwnerEmail(ownerEmail: string): string {\n const trimmed = ownerEmail.trim();\n if (!trimmed) throw new Error(\"ownerEmail is required\");\n return trimmed;\n}\n\nfunction assertSafeId(value: string, label: string): string {\n const trimmed = value.trim();\n if (!SAFE_ID_RE.test(trimmed)) {\n throw new Error(\n `${label} must be 1-160 characters using letters, numbers, dot, underscore, colon, or dash`,\n );\n }\n return trimmed;\n}\n\nfunction generateId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction json(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n\nfunction parseOptionalObject(\n value: unknown,\n): Record<string, unknown> | undefined {\n if (value == null) return undefined;\n const parsed = safeJsonParse<unknown>(value, undefined);\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n}\n\nfunction parseActions(value: unknown): AgentNativeBrowserSessionAction[] {\n const parsed = safeJsonParse<unknown>(value, []);\n return Array.isArray(parsed)\n ? (parsed.filter(\n (action) =>\n action &&\n typeof action === \"object\" &&\n typeof (action as { name?: unknown }).name === \"string\",\n ) as AgentNativeBrowserSessionAction[])\n : [];\n}\n\nfunction parseSession(\n value: unknown,\n sessionId: string,\n): AgentNativeBrowserSession {\n const parsed = safeJsonParse<unknown>(value, { id: sessionId });\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? ({\n id: sessionId,\n ...(parsed as Record<string, unknown>),\n } as AgentNativeBrowserSession)\n : { id: sessionId };\n}\n\nfunction coerceTime(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim()) {\n const parsed = Date.parse(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return undefined;\n}\n\nfunction normalizeSessionInput(input: RegisterAgentNativeBrowserSessionInput): {\n sessionId: string;\n session: AgentNativeBrowserSession;\n label?: string;\n url?: string;\n context?: Record<string, unknown>;\n actions: AgentNativeBrowserSessionAction[];\n connectedAt: number;\n ttlMs: number;\n} {\n const sessionRecord: Partial<AgentNativeBrowserSession> =\n input.session && typeof input.session === \"object\" ? input.session : {};\n const rawId = input.sessionId || sessionRecord.id;\n if (typeof rawId !== \"string\") {\n throw new Error(\"session.id is required\");\n }\n const sessionId = assertSafeId(rawId, \"session.id\");\n const now = nowMs();\n const connectedAt = coerceTime(sessionRecord.connectedAt) ?? now;\n const label =\n typeof input.label === \"string\" && input.label.trim()\n ? input.label.trim()\n : typeof sessionRecord.label === \"string\" && sessionRecord.label.trim()\n ? sessionRecord.label.trim()\n : undefined;\n const url =\n typeof input.url === \"string\" && input.url.trim()\n ? input.url.trim()\n : typeof sessionRecord.url === \"string\" && sessionRecord.url.trim()\n ? sessionRecord.url.trim()\n : undefined;\n const session: AgentNativeBrowserSession = {\n ...sessionRecord,\n id: sessionId,\n ...(label ? { label } : {}),\n connectedAt: new Date(connectedAt).toISOString(),\n ...(url ? { url } : {}),\n };\n return {\n sessionId,\n session,\n label,\n url,\n context:\n input.context &&\n typeof input.context === \"object\" &&\n !Array.isArray(input.context)\n ? input.context\n : undefined,\n actions: Array.isArray(input.actions) ? input.actions : [],\n connectedAt,\n ttlMs:\n typeof input.ttlMs === \"number\" && input.ttlMs > 0\n ? Math.min(input.ttlMs, 5 * 60 * 1000)\n : DEFAULT_BROWSER_SESSION_TTL_MS,\n };\n}\n\nfunction rowToSession(\n row: Record<string, unknown>,\n now = nowMs(),\n): AgentNativeBrowserSessionRecord {\n const sessionId = String(row.session_id ?? \"\");\n const expiresAt = Number(row.expires_at ?? 0);\n return {\n sessionId,\n session: parseSession(row.session_json, sessionId),\n label: typeof row.label === \"string\" ? row.label : undefined,\n url: typeof row.url === \"string\" ? row.url : undefined,\n context: parseOptionalObject(row.context_json),\n actions: parseActions(row.actions_json),\n connectedAt: Number(row.connected_at ?? 0),\n lastSeenAt: Number(row.last_seen_at ?? 0),\n expiresAt,\n active: expiresAt > now,\n };\n}\n\nfunction rowToRequest(\n row: Record<string, unknown>,\n): AgentNativeBrowserSessionRequest {\n const type = String(\n row.type ?? \"get-context\",\n ) as AgentNativeBrowserSessionRequestType;\n const payload = safeJsonParse<unknown>(row.payload_json, undefined);\n const result = safeJsonParse<unknown>(row.result_json, undefined);\n const request: AgentNativeBrowserSessionRequest = {\n id: String(row.request_id ?? \"\"),\n sessionId: String(row.session_id ?? \"\"),\n type,\n status: String(\n row.status ?? \"pending\",\n ) as AgentNativeBrowserSessionRequestStatus,\n createdAt: Number(row.created_at ?? 0),\n expiresAt: Number(row.expires_at ?? 0),\n ...(typeof row.name === \"string\" && row.name ? { name: row.name } : {}),\n ...(typeof row.command === \"string\" && row.command\n ? { command: row.command }\n : {}),\n ...(row.claimed_at != null ? { claimedAt: Number(row.claimed_at) } : {}),\n ...(row.completed_at != null\n ? { completedAt: Number(row.completed_at) }\n : {}),\n ...(row.error ? { error: String(row.error) } : {}),\n ...(row.result_json != null ? { result } : {}),\n };\n if (type === \"run-action\") request.args = payload;\n else request.payload = payload;\n return request;\n}\n\nasync function expireOldRequests(\n ownerEmail: string,\n sessionId?: string,\n): Promise<void> {\n await ensureTables();\n const client = getDbExec();\n const now = nowMs();\n await client.execute({\n sql: sessionId\n ? `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser-session request expired'\n WHERE owner_email = ? AND session_id = ? AND status IN ('pending', 'claimed') AND expires_at < ?`\n : `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser-session request expired'\n WHERE owner_email = ? AND status IN ('pending', 'claimed') AND expires_at < ?`,\n args: sessionId\n ? [now, ownerEmail, sessionId, now]\n : [now, ownerEmail, now],\n });\n}\n\nexport async function registerBrowserSession(\n ownerEmailInput: string,\n input: RegisterAgentNativeBrowserSessionInput,\n): Promise<AgentNativeBrowserSessionRecord> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const normalized = normalizeSessionInput(input);\n await ensureTables();\n const client = getDbExec();\n const now = nowMs();\n const expiresAt = now + normalized.ttlMs;\n\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${SESSION_TABLE}\n (owner_email, session_id, label, url, session_json, context_json, actions_json, connected_at, last_seen_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (owner_email, session_id) DO UPDATE SET\n label = EXCLUDED.label,\n url = EXCLUDED.url,\n session_json = EXCLUDED.session_json,\n context_json = EXCLUDED.context_json,\n actions_json = EXCLUDED.actions_json,\n last_seen_at = EXCLUDED.last_seen_at,\n expires_at = EXCLUDED.expires_at`\n : `INSERT OR REPLACE INTO ${SESSION_TABLE}\n (owner_email, session_id, label, url, session_json, context_json, actions_json, connected_at, last_seen_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n ownerEmail,\n normalized.sessionId,\n normalized.label ?? null,\n normalized.url ?? null,\n json(normalized.session),\n normalized.context ? json(normalized.context) : null,\n json(normalized.actions),\n normalized.connectedAt,\n now,\n expiresAt,\n ],\n });\n\n const saved = await getBrowserSession(ownerEmail, normalized.sessionId, {\n includeExpired: true,\n });\n if (!saved) throw new Error(\"Failed to register browser session\");\n return saved;\n}\n\nexport async function listBrowserSessions(\n ownerEmailInput: string,\n options: { includeExpired?: boolean; limit?: number } = {},\n): Promise<AgentNativeBrowserSessionRecord[]> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n await ensureTables();\n await expireOldRequests(ownerEmail);\n const client = getDbExec();\n const limit =\n typeof options.limit === \"number\" && options.limit > 0\n ? Math.min(Math.floor(options.limit), 100)\n : 25;\n const now = nowMs();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${SESSION_TABLE}\n WHERE owner_email = ?\n ORDER BY last_seen_at DESC\n LIMIT ?`,\n args: [ownerEmail, limit],\n });\n const sessions = rows.map((row) => rowToSession(row, now));\n return options.includeExpired\n ? sessions\n : sessions.filter((session) => session.active);\n}\n\nexport async function getBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n options: { includeExpired?: boolean } = {},\n): Promise<AgentNativeBrowserSessionRecord | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${SESSION_TABLE} WHERE owner_email = ? AND session_id = ?`,\n args: [ownerEmail, sessionId],\n });\n if (rows.length === 0) return null;\n const session = rowToSession(rows[0]);\n return options.includeExpired || session.active ? session : null;\n}\n\nexport async function disconnectBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n): Promise<boolean> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n const client = getDbExec();\n await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = 'expired', completed_at = ?, error = 'Browser session disconnected'\n WHERE owner_email = ? AND session_id = ? AND status IN ('pending', 'claimed')`,\n args: [nowMs(), ownerEmail, sessionId],\n });\n const result = await client.execute({\n sql: `DELETE FROM ${SESSION_TABLE} WHERE owner_email = ? AND session_id = ?`,\n args: [ownerEmail, sessionId],\n });\n return result.rowsAffected > 0;\n}\n\nfunction normalizeRequestInput(\n input: CreateAgentNativeBrowserSessionRequestInput,\n): {\n type: AgentNativeBrowserSessionRequestType;\n name?: string;\n command?: string;\n payload?: unknown;\n timeoutMs: number;\n} {\n if (\n input.type !== \"get-context\" &&\n input.type !== \"list-actions\" &&\n input.type !== \"run-action\" &&\n input.type !== \"command\"\n ) {\n throw new Error(\n \"request type must be get-context, list-actions, run-action, or command\",\n );\n }\n const name =\n typeof input.name === \"string\" && input.name.trim()\n ? input.name.trim()\n : undefined;\n if (input.type === \"run-action\" && !name) {\n throw new Error(\"name is required for run-action requests\");\n }\n const command =\n typeof input.command === \"string\" && input.command.trim()\n ? input.command.trim()\n : input.type === \"command\"\n ? \"refreshData\"\n : undefined;\n return {\n type: input.type,\n name,\n command,\n payload: input.type === \"run-action\" ? input.args : input.payload,\n timeoutMs:\n typeof input.timeoutMs === \"number\" && input.timeoutMs > 0\n ? Math.min(input.timeoutMs, 2 * 60 * 1000)\n : DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS,\n };\n}\n\nexport async function createBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n input: CreateAgentNativeBrowserSessionRequestInput,\n): Promise<AgentNativeBrowserSessionRequest> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n const session = await getBrowserSession(ownerEmail, sessionId);\n if (!session) {\n throw new Error(`No active browser session found for \"${sessionId}\"`);\n }\n const normalized = normalizeRequestInput(input);\n await ensureTables();\n const client = getDbExec();\n const createdAt = nowMs();\n const requestId = generateId(\"browser-request\");\n const expiresAt = createdAt + normalized.timeoutMs + 15_000;\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${REQUEST_TABLE}\n (owner_email, session_id, request_id, type, name, command, payload_json, status, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)\n ON CONFLICT (owner_email, request_id) DO NOTHING`\n : `INSERT OR IGNORE INTO ${REQUEST_TABLE}\n (owner_email, session_id, request_id, type, name, command, payload_json, status, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,\n args: [\n ownerEmail,\n sessionId,\n requestId,\n normalized.type,\n normalized.name ?? null,\n normalized.command ?? null,\n json(normalized.payload),\n createdAt,\n expiresAt,\n ],\n });\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request) throw new Error(\"Failed to create browser-session request\");\n return request;\n}\n\nexport async function getBrowserSessionRequest(\n ownerEmailInput: string,\n requestIdInput: string,\n): Promise<AgentNativeBrowserSessionRequest | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n await ensureTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${REQUEST_TABLE} WHERE owner_email = ? AND request_id = ?`,\n args: [ownerEmail, requestId],\n });\n return rows.length ? rowToRequest(rows[0]) : null;\n}\n\nexport async function claimBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n): Promise<AgentNativeBrowserSessionRequest | null> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n await ensureTables();\n await expireOldRequests(ownerEmail, sessionId);\n const client = getDbExec();\n const now = nowMs();\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM ${REQUEST_TABLE}\n WHERE owner_email = ? AND session_id = ? AND status = 'pending' AND expires_at >= ?\n ORDER BY created_at ASC\n LIMIT 1`,\n args: [ownerEmail, sessionId, now],\n });\n if (rows.length === 0) return null;\n const candidate = rowToRequest(rows[0]);\n const updated = await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = 'claimed', claimed_at = ?\n WHERE owner_email = ? AND request_id = ? AND status = 'pending'`,\n args: [now, ownerEmail, candidate.id],\n });\n if (updated.rowsAffected > 0) {\n return getBrowserSessionRequest(ownerEmail, candidate.id);\n }\n }\n return null;\n}\n\nexport async function completeBrowserSessionRequest(\n ownerEmailInput: string,\n sessionIdInput: string,\n requestIdInput: string,\n result:\n | { ok: true; result?: unknown }\n | { ok: false; error?: string; result?: unknown },\n): Promise<AgentNativeBrowserSessionRequest> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const sessionId = assertSafeId(sessionIdInput, \"sessionId\");\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n await ensureTables();\n const client = getDbExec();\n const completedAt = nowMs();\n const status: AgentNativeBrowserSessionRequestStatus =\n result.ok === true ? \"completed\" : \"failed\";\n const error =\n result.ok === false\n ? result.error || \"Browser-session request failed\"\n : null;\n const updated = await client.execute({\n sql: `UPDATE ${REQUEST_TABLE}\n SET status = ?, completed_at = ?, result_json = ?, error = ?\n WHERE owner_email = ? AND session_id = ? AND request_id = ? AND status IN ('pending', 'claimed')`,\n args: [\n status,\n completedAt,\n \"result\" in result ? json(result.result) : null,\n error,\n ownerEmail,\n sessionId,\n requestId,\n ],\n });\n if (updated.rowsAffected === 0) {\n const existing = await getBrowserSessionRequest(ownerEmail, requestId);\n if (existing) return existing;\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n }\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request)\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n return request;\n}\n\nexport async function waitForBrowserSessionRequest(\n ownerEmailInput: string,\n requestIdInput: string,\n options: { timeoutMs?: number; pollMs?: number } = {},\n): Promise<unknown> {\n const ownerEmail = assertOwnerEmail(ownerEmailInput);\n const requestId = assertSafeId(requestIdInput, \"requestId\");\n const timeoutMs =\n typeof options.timeoutMs === \"number\" && options.timeoutMs > 0\n ? options.timeoutMs\n : DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS;\n const pollMs =\n typeof options.pollMs === \"number\" && options.pollMs > 0\n ? options.pollMs\n : DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS;\n const deadline = nowMs() + timeoutMs;\n\n while (nowMs() <= deadline) {\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (!request)\n throw new Error(`No browser-session request found for \"${requestId}\"`);\n if (request.status === \"completed\") return request.result;\n if (request.status === \"failed\") {\n throw new Error(request.error || \"Browser-session request failed\");\n }\n if (request.status === \"expired\" || request.expiresAt < nowMs()) {\n await expireOldRequests(ownerEmail, request.sessionId);\n throw new Error(request.error || \"Browser-session request expired\");\n }\n await sleep(Math.min(pollMs, Math.max(1, deadline - nowMs())));\n }\n\n const request = await getBrowserSessionRequest(ownerEmail, requestId);\n if (request) {\n await completeBrowserSessionRequest(\n ownerEmail,\n request.sessionId,\n requestId,\n {\n ok: false,\n error: \"Timed out waiting for browser-session response\",\n },\n );\n }\n throw new Error(\"Timed out waiting for browser-session response\");\n}\n\nexport async function callBrowserSession(\n ownerEmailInput: string,\n sessionIdInput: string,\n input: CreateAgentNativeBrowserSessionRequestInput,\n options: { timeoutMs?: number; pollMs?: number } = {},\n): Promise<unknown> {\n const request = await createBrowserSessionRequest(\n ownerEmailInput,\n sessionIdInput,\n {\n ...input,\n timeoutMs: input.timeoutMs ?? options.timeoutMs,\n },\n );\n return waitForBrowserSessionRequest(ownerEmailInput, request.id, {\n timeoutMs: options.timeoutMs ?? input.timeoutMs,\n pollMs: options.pollMs,\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/chat-threads/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { mergeThreadDataForClientSave, normalizeThreadRepository, normalizeThreadTitle, } from "../agent/thread-data-builder.js";
|
|
3
|
-
import { getDbExec, intType } from "../db/client.js";
|
|
3
|
+
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
4
|
+
import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
|
|
4
5
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
5
6
|
import { emitChatThreadChange } from "./emitter.js";
|
|
6
7
|
let _initPromise;
|
|
@@ -41,7 +42,7 @@ async function ensureTable() {
|
|
|
41
42
|
if (!_initPromise) {
|
|
42
43
|
_initPromise = (async () => {
|
|
43
44
|
const client = getDbExec();
|
|
44
|
-
|
|
45
|
+
const createSql = `
|
|
45
46
|
CREATE TABLE IF NOT EXISTS chat_threads (
|
|
46
47
|
id TEXT PRIMARY KEY,
|
|
47
48
|
owner_email TEXT NOT NULL,
|
|
@@ -57,7 +58,54 @@ async function ensureTable() {
|
|
|
57
58
|
pinned_at ${intType()},
|
|
58
59
|
archived_at ${intType()}
|
|
59
60
|
)
|
|
60
|
-
|
|
61
|
+
`;
|
|
62
|
+
if (isPostgres()) {
|
|
63
|
+
// Hot path: the `chat_threads` table and its indexes are virtually
|
|
64
|
+
// always already present in production. Issuing `CREATE TABLE`/
|
|
65
|
+
// `CREATE INDEX` still takes a lock that, in a fresh background-worker
|
|
66
|
+
// process behind a concurrent connection on the shared Neon DB, can
|
|
67
|
+
// block ~indefinitely (ACCESS EXCLUSIVE for CREATE TABLE; a write-
|
|
68
|
+
// blocking SHARE lock for CREATE INDEX). The ensure* wrappers probe
|
|
69
|
+
// `information_schema`/`pg_indexes` first (plain reads, no lock) and
|
|
70
|
+
// run DDL ONLY for what is actually missing, bounded by a transaction-
|
|
71
|
+
// scoped `lock_timeout`. If a swallowed lock-timeout leaves the schema
|
|
72
|
+
// still missing they RE-PROBE and THROW rather than letting init
|
|
73
|
+
// memoize success against absent schema. `chat_threads` is the
|
|
74
|
+
// unqualified name even though the table lives in `public`.
|
|
75
|
+
await ensureTableExists("chat_threads", createSql);
|
|
76
|
+
// Additive columns — guarded so the hot path (columns already present)
|
|
77
|
+
// skips the ACCESS EXCLUSIVE ALTER entirely.
|
|
78
|
+
for (const [col, type] of [
|
|
79
|
+
["scope_type", "TEXT"],
|
|
80
|
+
["scope_id", "TEXT"],
|
|
81
|
+
["scope_label", "TEXT"],
|
|
82
|
+
["pinned_at", intType()],
|
|
83
|
+
["archived_at", intType()],
|
|
84
|
+
]) {
|
|
85
|
+
await ensureColumnExists("chat_threads", col, `ALTER TABLE chat_threads ADD COLUMN IF NOT EXISTS ${col} ${type}`);
|
|
86
|
+
}
|
|
87
|
+
// Widen millisecond-timestamp columns that older deployments created as
|
|
88
|
+
// 32-bit `INTEGER`; on Postgres the `Date.now()` written on every turn
|
|
89
|
+
// overflows int4. No-op once widened / on fresh BIGINT databases.
|
|
90
|
+
await widenIntColumnsToBigInt("chat_threads", [
|
|
91
|
+
"created_at",
|
|
92
|
+
"updated_at",
|
|
93
|
+
"pinned_at",
|
|
94
|
+
"archived_at",
|
|
95
|
+
]);
|
|
96
|
+
// Indexes for the hot read paths. Both the sidebar list and the
|
|
97
|
+
// scoped/per-resource list filter on owner_email (and optionally
|
|
98
|
+
// scope) and sort by updated_at. Probe pg_indexes first (no lock)
|
|
99
|
+
// and skip the SHARE-locking CREATE INDEX when already present.
|
|
100
|
+
await ensureIndexExists("chat_threads_owner_updated_idx", `CREATE INDEX IF NOT EXISTS chat_threads_owner_updated_idx ON chat_threads (owner_email, updated_at)`);
|
|
101
|
+
await ensureIndexExists("chat_threads_scope_updated_idx", `CREATE INDEX IF NOT EXISTS chat_threads_scope_updated_idx ON chat_threads (scope_type, scope_id, updated_at)`);
|
|
102
|
+
// One-time backfill of message_count for legacy rows written before
|
|
103
|
+
// the column was maintained.
|
|
104
|
+
await backfillLegacyMessageCounts(client);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// SQLite (local dev): no lock problem — keep the original behaviour.
|
|
108
|
+
await client.execute(createSql);
|
|
61
109
|
// Additive migration for existing tables. Both SQLite and Postgres
|
|
62
110
|
// accept `ALTER TABLE ADD COLUMN` and will raise when the column
|
|
63
111
|
// already exists; the try/catch makes the call idempotent across
|