@opengeni/db 0.7.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/{chunk-B22X3IEZ.js → chunk-4LG5NBTC.js} +512 -32
  2. package/dist/chunk-4LG5NBTC.js.map +1 -0
  3. package/dist/{chunk-YFQ7SGE4.js → chunk-BMFDXFPA.js} +23 -1
  4. package/dist/chunk-BMFDXFPA.js.map +1 -0
  5. package/dist/chunk-KW526IJA.js +127 -0
  6. package/dist/chunk-KW526IJA.js.map +1 -0
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +6092 -2080
  9. package/dist/index.js.map +1 -1
  10. package/dist/migrate.d.ts +29 -4
  11. package/dist/migrate.js +3 -1
  12. package/dist/provision-roles.d.ts +732 -47
  13. package/dist/provision-roles.js +1 -1
  14. package/dist/{schema-BN5mB9xZ.d.ts → schema-CdPGTHlD.d.ts} +2118 -119
  15. package/dist/schema.d.ts +1 -1
  16. package/dist/schema.js +17 -1
  17. package/drizzle/0064_rotation_strategy_sharded_backfill.sql +15 -0
  18. package/drizzle/0065_session_attempt_quiescence.sql +26 -0
  19. package/drizzle/0066_session_interruption_attempt_lookup.sql +4 -0
  20. package/drizzle/0067_session_event_payload_bounds.sql +209 -0
  21. package/drizzle/0068_workspace_control_event_bounds.sql +134 -0
  22. package/drizzle/0069_session_event_history_backfill.sql +32 -0
  23. package/drizzle/0070_session_event_type_sequence_lookup.sql +4 -0
  24. package/drizzle/0071_session_event_monitoring_tail.sql +10 -0
  25. package/drizzle/0072_sessions_workspace_created_id_idx.sql +4 -0
  26. package/drizzle/0073_sessions_workspace_updated_id_idx.sql +4 -0
  27. package/drizzle/0074_session_activity_revisions.sql +72 -0
  28. package/drizzle/0075_sessions_workspace_activity_revision_idx.sql +4 -0
  29. package/drizzle/0076_session_workflow_wake_acl.sql +13 -0
  30. package/drizzle/0077_session_attempt_latest_lookup.sql +4 -0
  31. package/drizzle/0094_quarantine_credential_bearing_catalog_urls.sql +51 -0
  32. package/drizzle/0095_github_existing_installations.sql +84 -0
  33. package/drizzle/0096_session_turn_initiators.sql +97 -0
  34. package/drizzle/0097_host_export_outbox.sql +1220 -0
  35. package/drizzle/0098_usage_events_workspace_session_idx.sql +4 -0
  36. package/drizzle/0099_session_human_input_attempt_owner_index.sql +6 -0
  37. package/drizzle/0100_session_human_input_requests.sql +89 -0
  38. package/drizzle/0101_session_mcp_connection_refs.sql +30 -0
  39. package/drizzle/0102_session_command_receipt_service_actor.sql +16 -0
  40. package/drizzle/0103_host_export_root_session.sql +166 -0
  41. package/drizzle/0104_host_export_root_session_backfill.sql +27 -0
  42. package/drizzle/0105_session_turn_instructions.sql +9 -0
  43. package/package.json +4 -4
  44. package/src/connection-token-resolver.ts +287 -1
  45. package/src/event-payload-sanitizer.ts +57 -19
  46. package/src/index.ts +5429 -1131
  47. package/src/memory-domain.ts +1 -1
  48. package/src/migrate.ts +86 -23
  49. package/src/persistence-errors.ts +252 -0
  50. package/src/provision-roles.ts +42 -0
  51. package/src/schema.ts +552 -34
  52. package/src/session-control.ts +518 -38
  53. package/src/session-queue-commands.ts +308 -57
  54. package/src/session-tool-call-settlement.ts +58 -7
  55. package/src/turn-initiator.ts +155 -0
  56. package/dist/chunk-7LDU7F5P.js +0 -80
  57. package/dist/chunk-7LDU7F5P.js.map +0 -1
  58. package/dist/chunk-B22X3IEZ.js.map +0 -1
  59. package/dist/chunk-YFQ7SGE4.js.map +0 -1
@@ -0,0 +1,4 @@
1
+ -- deployment-mode: rolling
2
+ -- opengeni:concurrent-index lock-timeout=5s
3
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS "usage_events_workspace_session_idx"
4
+ ON "usage_events" ("workspace_id", "session_id", "occurred_at");
@@ -0,0 +1,6 @@
1
+ -- deployment-mode: rolling
2
+ -- opengeni:concurrent-index lock-timeout=5s
3
+ CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "session_turn_attempts_human_input_owner_uq"
4
+ ON "session_turn_attempts" (
5
+ "account_id", "workspace_id", "session_id", "turn_id", "id"
6
+ );
@@ -0,0 +1,89 @@
1
+ -- deployment-mode: rolling
2
+ -- Durable, host-neutral structured human input. The OpenAI Agents SDK approval
3
+ -- interruption is an internal freeze primitive only; this table owns the
4
+ -- distinct request/response protocol and its idempotent settlement state. Its
5
+ -- strong attempt-owner index is installed online by migration 0099.
6
+
7
+ SET LOCAL lock_timeout = '5s';
8
+ SET LOCAL statement_timeout = '10min';
9
+
10
+ CREATE TABLE IF NOT EXISTS "session_human_input_requests" (
11
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
12
+ "account_id" uuid NOT NULL,
13
+ "workspace_id" uuid NOT NULL,
14
+ "session_id" uuid NOT NULL,
15
+ "turn_id" uuid NOT NULL,
16
+ "turn_generation" integer NOT NULL,
17
+ "creation_attempt_id" uuid NOT NULL,
18
+ "tool_call_id" text NOT NULL,
19
+ "status" text NOT NULL DEFAULT 'pending',
20
+ "questions" jsonb NOT NULL,
21
+ "allow_skip" boolean NOT NULL DEFAULT false,
22
+ "response" jsonb,
23
+ "responded_by" text,
24
+ "responded_at" timestamptz,
25
+ "expires_at" timestamptz,
26
+ "created_at" timestamptz NOT NULL DEFAULT now(),
27
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
28
+ CONSTRAINT "session_human_input_requests_creation_attempt_fk"
29
+ FOREIGN KEY (
30
+ "account_id", "workspace_id", "session_id", "turn_id", "creation_attempt_id"
31
+ )
32
+ REFERENCES "session_turn_attempts" (
33
+ "account_id", "workspace_id", "session_id", "turn_id", "id"
34
+ )
35
+ ON DELETE CASCADE,
36
+ CONSTRAINT "session_human_input_requests_status_check"
37
+ CHECK ("status" IN ('pending','answered','skipped','expired','cancelled')),
38
+ CONSTRAINT "session_human_input_requests_generation_check"
39
+ CHECK ("turn_generation" > 0),
40
+ CONSTRAINT "session_human_input_requests_tool_call_bytes_check"
41
+ CHECK (octet_length("tool_call_id") BETWEEN 1 AND 1024),
42
+ CONSTRAINT "session_human_input_requests_questions_bytes_check"
43
+ CHECK (octet_length("questions"::text) <= 49152),
44
+ CONSTRAINT "session_human_input_requests_response_bytes_check"
45
+ CHECK ("response" IS NULL OR octet_length("response"::text) <= 49152),
46
+ CONSTRAINT "session_human_input_requests_actor_bytes_check"
47
+ CHECK ("responded_by" IS NULL OR octet_length("responded_by") <= 1024)
48
+ );
49
+
50
+ CREATE UNIQUE INDEX IF NOT EXISTS "session_human_input_requests_tool_call_uq"
51
+ ON "session_human_input_requests" (
52
+ "workspace_id", "session_id", "turn_id", "tool_call_id"
53
+ );
54
+ CREATE INDEX IF NOT EXISTS "session_human_input_requests_pending_session_idx"
55
+ ON "session_human_input_requests" ("workspace_id", "session_id", "created_at", "id")
56
+ WHERE "status" = 'pending';
57
+ CREATE INDEX IF NOT EXISTS "session_human_input_requests_pending_expiry_idx"
58
+ ON "session_human_input_requests" ("expires_at", "id")
59
+ WHERE "status" = 'pending' AND "expires_at" IS NOT NULL;
60
+
61
+ ALTER TABLE "session_human_input_requests" ENABLE ROW LEVEL SECURITY;
62
+ ALTER TABLE "session_human_input_requests" FORCE ROW LEVEL SECURITY;
63
+
64
+ DO $$
65
+ BEGIN
66
+ IF EXISTS (
67
+ SELECT 1 FROM pg_policies
68
+ WHERE schemaname = current_schema()
69
+ AND tablename = 'session_human_input_requests'
70
+ AND policyname = 'workspace_isolation'
71
+ ) THEN
72
+ DROP POLICY workspace_isolation ON "session_human_input_requests";
73
+ END IF;
74
+ END $$;
75
+
76
+ CREATE POLICY workspace_isolation ON "session_human_input_requests"
77
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
78
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
79
+
80
+ DO $grants$
81
+ DECLARE target_schema text := current_schema();
82
+ BEGIN
83
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
84
+ EXECUTE format(
85
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.session_human_input_requests TO opengeni_app',
86
+ target_schema
87
+ );
88
+ END IF;
89
+ END $grants$;
@@ -0,0 +1,30 @@
1
+ -- deployment-mode: rolling
2
+ -- A session-scoped MCP server may point at a host-owned connection. The value
3
+ -- is non-secret and remains inert unless the standalone broker or an embedding
4
+ -- host credential port resolves it at request time.
5
+
6
+ ALTER TABLE "session_mcp_servers"
7
+ ADD COLUMN IF NOT EXISTS "connection_ref" jsonb;
8
+
9
+ DO $$
10
+ BEGIN
11
+ IF NOT EXISTS (
12
+ SELECT 1 FROM pg_constraint
13
+ WHERE conname = 'session_mcp_servers_connection_ref_object_chk'
14
+ AND conrelid = 'session_mcp_servers'::regclass
15
+ ) THEN
16
+ ALTER TABLE "session_mcp_servers"
17
+ ADD CONSTRAINT "session_mcp_servers_connection_ref_object_chk"
18
+ CHECK (
19
+ "connection_ref" IS NULL
20
+ OR (
21
+ jsonb_typeof("connection_ref") = 'object'
22
+ AND COALESCE(jsonb_typeof("connection_ref"->'providerDomain') = 'string', false)
23
+ AND COALESCE(length("connection_ref"->>'providerDomain') > 0, false)
24
+ )
25
+ ) NOT VALID;
26
+ END IF;
27
+ END $$;
28
+
29
+ ALTER TABLE "session_mcp_servers"
30
+ VALIDATE CONSTRAINT "session_mcp_servers_connection_ref_object_chk";
@@ -0,0 +1,16 @@
1
+ -- deployment-mode: rolling
2
+ SET LOCAL lock_timeout = '5s';
3
+
4
+ ALTER TABLE "session_command_receipts"
5
+ DROP CONSTRAINT "session_command_receipts_actor_check",
6
+ ADD CONSTRAINT "session_command_receipts_actor_check"
7
+ CHECK (
8
+ ("actor_type" = 'agent_attempt' AND "actor_attempt_id" IS NOT NULL
9
+ AND "actor_subject_id" IS NULL)
10
+ OR ("actor_type" IN ('human', 'operator', 'service')
11
+ AND "actor_subject_id" IS NOT NULL
12
+ AND "actor_attempt_id" IS NULL)
13
+ ) NOT VALID;
14
+
15
+ ALTER TABLE "session_command_receipts"
16
+ VALIDATE CONSTRAINT "session_command_receipts_actor_check";
@@ -0,0 +1,166 @@
1
+ -- deployment-mode: rolling
2
+ -- Preserve immutable session lineage in durable host exports. The outbox owns
3
+ -- the captured root so a later session deletion cannot change an unacknowledged
4
+ -- export. Legacy rows whose source session is already gone remain explicitly
5
+ -- unresolved (NULL) instead of guessing.
6
+
7
+ ALTER TABLE "host_export_outbox"
8
+ ADD COLUMN IF NOT EXISTS "root_session_id" uuid;
9
+
10
+ DO $migration$
11
+ DECLARE target_schema text := current_schema();
12
+ BEGIN
13
+ EXECUTE format($create$
14
+ CREATE OR REPLACE FUNCTION opengeni_private.host_export_session_root(
15
+ p_workspace_id uuid,
16
+ p_session_id uuid
17
+ ) RETURNS uuid
18
+ LANGUAGE plpgsql
19
+ SECURITY DEFINER
20
+ SET search_path = pg_catalog
21
+ AS $function$
22
+ DECLARE
23
+ v_root_id uuid;
24
+ v_parent_id uuid;
25
+ v_depth integer;
26
+ v_cycle boolean;
27
+ BEGIN
28
+ IF p_session_id IS NULL THEN
29
+ RETURN NULL;
30
+ END IF;
31
+ WITH RECURSIVE lineage(id, parent_session_id, depth, path, cycle) AS (
32
+ SELECT s.id, s.parent_session_id, 0, ARRAY[s.id], false
33
+ FROM %1$I.sessions s
34
+ WHERE s.workspace_id = p_workspace_id AND s.id = p_session_id
35
+ UNION ALL
36
+ SELECT parent.id, parent.parent_session_id, lineage.depth + 1,
37
+ lineage.path || parent.id, parent.id = ANY(lineage.path)
38
+ FROM %1$I.sessions parent
39
+ JOIN lineage ON lineage.parent_session_id = parent.id
40
+ WHERE parent.workspace_id = p_workspace_id
41
+ AND NOT lineage.cycle
42
+ AND lineage.depth < 64
43
+ )
44
+ SELECT id, parent_session_id, depth, cycle
45
+ INTO v_root_id, v_parent_id, v_depth, v_cycle
46
+ FROM lineage
47
+ ORDER BY depth DESC
48
+ LIMIT 1;
49
+
50
+ IF v_root_id IS NULL THEN
51
+ RETURN NULL;
52
+ END IF;
53
+ IF v_cycle OR v_parent_id IS NOT NULL OR v_depth >= 64 THEN
54
+ RAISE EXCEPTION 'session lineage for %% has no valid workspace root', p_session_id
55
+ USING ERRCODE = '23514';
56
+ END IF;
57
+ RETURN v_root_id;
58
+ END $function$;
59
+ $create$, target_schema);
60
+
61
+ EXECUTE format($create$
62
+ CREATE OR REPLACE FUNCTION opengeni_private.capture_host_export_root_session()
63
+ RETURNS trigger
64
+ LANGUAGE plpgsql
65
+ SECURITY DEFINER
66
+ SET search_path = pg_catalog
67
+ AS $function$
68
+ BEGIN
69
+ NEW.root_session_id := opengeni_private.host_export_session_root(
70
+ NEW.workspace_id,
71
+ NEW.session_id
72
+ );
73
+ IF NEW.session_id IS NOT NULL AND NEW.root_session_id IS NULL THEN
74
+ RAISE EXCEPTION 'host export session %% does not exist in workspace %%',
75
+ NEW.session_id, NEW.workspace_id
76
+ USING ERRCODE = '23503';
77
+ END IF;
78
+ RETURN NEW;
79
+ END $function$;
80
+ $create$, target_schema);
81
+ END $migration$;
82
+
83
+ DROP TRIGGER IF EXISTS host_export_outbox_capture_root_session
84
+ ON "host_export_outbox";
85
+ CREATE TRIGGER host_export_outbox_capture_root_session
86
+ BEFORE INSERT ON "host_export_outbox"
87
+ FOR EACH ROW EXECUTE FUNCTION
88
+ opengeni_private.capture_host_export_root_session();
89
+
90
+ DO $migration$
91
+ DECLARE target_schema text := current_schema();
92
+ BEGIN
93
+ EXECUTE format($create$
94
+ CREATE OR REPLACE FUNCTION opengeni_host_export.host_export_cursor_roots(
95
+ p_export_kind text,
96
+ p_consumer_id text,
97
+ p_lease_token uuid
98
+ ) RETURNS TABLE (export_cursor bigint, root_session_id uuid)
99
+ LANGUAGE plpgsql
100
+ SECURITY DEFINER
101
+ SET search_path = pg_catalog
102
+ AS $function$
103
+ DECLARE
104
+ v_consumer %1$I.host_export_consumers%%ROWTYPE;
105
+ BEGIN
106
+ SELECT * INTO v_consumer
107
+ FROM %1$I.host_export_consumers c
108
+ WHERE c.export_kind = p_export_kind
109
+ AND c.consumer_id = p_consumer_id
110
+ FOR UPDATE;
111
+ IF NOT FOUND
112
+ OR v_consumer.lease_token IS DISTINCT FROM p_lease_token
113
+ OR v_consumer.lease_from IS NULL
114
+ OR v_consumer.lease_through IS NULL
115
+ OR v_consumer.lease_expires_at IS NULL
116
+ OR v_consumer.lease_expires_at <= now() THEN
117
+ RAISE EXCEPTION 'host export lease is not current'
118
+ USING ERRCODE = '55000';
119
+ END IF;
120
+
121
+ RETURN QUERY
122
+ SELECT o.export_cursor, o.root_session_id
123
+ FROM %1$I.host_export_outbox o
124
+ WHERE o.export_kind = p_export_kind
125
+ AND o.export_cursor > v_consumer.lease_from
126
+ AND o.export_cursor <= v_consumer.lease_through
127
+ ORDER BY o.export_cursor;
128
+ END $function$;
129
+ $create$, target_schema);
130
+ END $migration$;
131
+
132
+ REVOKE ALL ON FUNCTION
133
+ opengeni_private.host_export_session_root(uuid, uuid) FROM PUBLIC;
134
+ REVOKE ALL ON FUNCTION
135
+ opengeni_private.capture_host_export_root_session() FROM PUBLIC;
136
+ REVOKE ALL ON FUNCTION
137
+ opengeni_host_export.host_export_cursor_roots(text, text, uuid) FROM PUBLIC;
138
+
139
+ -- Preserve already-provisioned exporter identities during migration-only
140
+ -- upgrades. Exporter role names are host-configurable, so copy the explicit
141
+ -- EXECUTE grantees from the canonical claim function instead of assuming a
142
+ -- role name. The function owner and PUBLIC are deliberately excluded.
143
+ DO $migration$
144
+ DECLARE v_role name;
145
+ BEGIN
146
+ FOR v_role IN
147
+ SELECT grantee.rolname
148
+ FROM pg_catalog.pg_proc proc
149
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
150
+ coalesce(
151
+ proc.proacl,
152
+ pg_catalog.acldefault('f', proc.proowner)
153
+ )
154
+ ) privilege
155
+ JOIN pg_catalog.pg_roles grantee ON grantee.oid = privilege.grantee
156
+ WHERE proc.oid =
157
+ 'opengeni_host_export.claim_host_export_batch(text, text, uuid, text, integer, integer, integer)'::regprocedure
158
+ AND privilege.grantee <> proc.proowner
159
+ AND privilege.privilege_type = 'EXECUTE'
160
+ LOOP
161
+ EXECUTE format(
162
+ 'GRANT EXECUTE ON FUNCTION opengeni_host_export.host_export_cursor_roots(text, text, uuid) TO %I',
163
+ v_role
164
+ );
165
+ END LOOP;
166
+ END $migration$;
@@ -0,0 +1,27 @@
1
+ -- deployment-mode: maintenance
2
+ -- Backfill immutable root lineage for the pre-0103 durable outbox population.
3
+ -- Migration 0103 already captures every new row, so this unbounded rewrite is
4
+ -- isolated to an explicitly reviewed maintenance step instead of hiding in the
5
+ -- rolling expand migration. Deleted source sessions remain unresolved (NULL).
6
+
7
+ WITH distinct_sessions AS MATERIALIZED (
8
+ SELECT DISTINCT "workspace_id", "session_id"
9
+ FROM "host_export_outbox"
10
+ WHERE "session_id" IS NOT NULL
11
+ AND "root_session_id" IS NULL
12
+ ), resolved_roots AS MATERIALIZED (
13
+ SELECT
14
+ "workspace_id",
15
+ "session_id",
16
+ opengeni_private.host_export_session_root(
17
+ "workspace_id",
18
+ "session_id"
19
+ ) AS "root_session_id"
20
+ FROM distinct_sessions
21
+ )
22
+ UPDATE "host_export_outbox" o
23
+ SET "root_session_id" = roots."root_session_id"
24
+ FROM resolved_roots roots
25
+ WHERE o."workspace_id" = roots."workspace_id"
26
+ AND o."session_id" = roots."session_id"
27
+ AND o."root_session_id" IS NULL;
@@ -0,0 +1,9 @@
1
+ -- deployment-mode: rolling
2
+ -- Durable, invisible host context for one exact turn. This is intentionally
3
+ -- separate from the user prompt and session-wide persona instructions.
4
+
5
+ ALTER TABLE "session_turns"
6
+ ADD COLUMN IF NOT EXISTS "turn_instructions" text;
7
+
8
+ ALTER TABLE "sessions"
9
+ ADD COLUMN IF NOT EXISTS "initial_turn_instructions" text;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.7.2",
3
+ "version": "0.9.3",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -49,9 +49,9 @@
49
49
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
50
50
  },
51
51
  "dependencies": {
52
- "@opengeni/codex": "^0.2.3",
53
- "@opengeni/config": "^0.5.1",
54
- "@opengeni/contracts": "^0.10.0",
52
+ "@opengeni/codex": "^0.2.5",
53
+ "@opengeni/config": "^0.6.2",
54
+ "@opengeni/contracts": "^0.15.0",
55
55
  "drizzle-orm": "^0.45.2",
56
56
  "postgres": "^3.4.7"
57
57
  },