@opengeni/db 3.2.0-canary.0 → 3.4.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-SIBYBA2D.js → chunk-6DPJA36C.js} +30 -11
  3. package/dist/chunk-6DPJA36C.js.map +1 -0
  4. package/dist/{chunk-KRLPEMLH.js → chunk-C2AFP7SB.js} +184 -6
  5. package/dist/chunk-C2AFP7SB.js.map +1 -0
  6. package/dist/{chunk-AZHW7R5V.js → chunk-H7MXYG7R.js} +12 -8
  7. package/dist/chunk-H7MXYG7R.js.map +1 -0
  8. package/dist/{chunk-2PRS4J5Q.js → chunk-HY3F5Q4E.js} +3 -3
  9. package/dist/{chunk-NAB6FKFN.js → chunk-O6FTELKH.js} +3 -3
  10. package/dist/{chunk-UZNXG44K.js → chunk-PYMY3AJW.js} +324 -8
  11. package/dist/chunk-PYMY3AJW.js.map +1 -0
  12. package/dist/{chunk-6HMM2R5T.js → chunk-TRBOWUG3.js} +98 -4
  13. package/dist/chunk-TRBOWUG3.js.map +1 -0
  14. package/dist/{chunk-E6CLMKEE.js → chunk-V3TRUXFJ.js} +2 -2
  15. package/dist/{chunk-2EH4WRHL.js → chunk-ZWRIPCTR.js} +2 -2
  16. package/dist/editable-artifact-durable-export.js +2 -2
  17. package/dist/editable-artifacts.js +3 -3
  18. package/dist/governed-learning-activation.d.ts +16 -0
  19. package/dist/index.d.ts +108 -7
  20. package/dist/index.js +3605 -1417
  21. package/dist/index.js.map +1 -1
  22. package/dist/insights-model-bundle.d.ts +30 -0
  23. package/dist/insights-usage-bundle.d.ts +32 -0
  24. package/dist/insights.d.ts +10 -2
  25. package/dist/lossless-json.d.ts +1 -1
  26. package/dist/managed-user-setup.d.ts +82 -0
  27. package/dist/memory-domain.d.ts +2 -2
  28. package/dist/migrate.d.ts +3 -1
  29. package/dist/migrate.js +1 -1
  30. package/dist/new-session-drafts.d.ts +4 -4
  31. package/dist/organization-membership-lifecycle.d.ts +60 -1
  32. package/dist/pr-review.d.ts +44 -3
  33. package/dist/provision-roles.js +1 -1
  34. package/dist/runtime-posture.d.ts +12 -7
  35. package/dist/schema.d.ts +839 -101
  36. package/dist/schema.js +9 -1
  37. package/dist/session-background-commands.js +3 -3
  38. package/dist/session-tenancy.d.ts +12 -1
  39. package/dist/session-tenancy.js +3 -3
  40. package/dist/video-generation.js +3 -3
  41. package/drizzle/0348_named_signup_and_user_setup.sql +838 -0
  42. package/drizzle/0349_greenfield_session_tenancy_activation.sql +792 -0
  43. package/drizzle/0350_organization_shared_workspace_administration.sql +889 -0
  44. package/drizzle/0351_organization_user_setup_delivery.sql +978 -0
  45. package/drizzle/0352_session_variable_set_attachments.sql +1661 -0
  46. package/drizzle/0353_automatic_session_title_policy_fence.sql +476 -0
  47. package/drizzle/0354_automatic_session_title_quarantine_index.sql +9 -0
  48. package/drizzle/0355_automatic_session_title_quarantine.sql +92 -0
  49. package/drizzle/0356_set_based_insights_session_visibility.sql +237 -0
  50. package/drizzle/0357_rig_platform_base_only.sql +44 -0
  51. package/drizzle/0358_pr_review_managed_github_app.sql +108 -0
  52. package/drizzle/0359_insights_force_rls_read_capability.sql +936 -0
  53. package/drizzle/0360_organization_identity_confirmation_prompt.sql +103 -0
  54. package/drizzle/0361_remember_knowledge_memory_materialization.sql +249 -0
  55. package/package.json +6 -6
  56. package/src/database.ts +12 -8
  57. package/src/governed-learning-activation.ts +74 -2
  58. package/src/index.ts +879 -92
  59. package/src/insights-model-bundle.ts +681 -0
  60. package/src/insights-usage-bundle.ts +240 -0
  61. package/src/insights.ts +265 -152
  62. package/src/lossless-json.ts +2 -1
  63. package/src/managed-user-setup.ts +316 -0
  64. package/src/memory-domain.ts +3 -3
  65. package/src/migrate.ts +38 -14
  66. package/src/new-session-drafts.ts +22 -15
  67. package/src/organization-membership-lifecycle.ts +253 -1
  68. package/src/pr-review.ts +449 -6
  69. package/src/provision-roles.ts +92 -0
  70. package/src/runtime-posture.ts +247 -4
  71. package/src/schema.ts +210 -4
  72. package/src/session-tenancy.ts +120 -2
  73. package/dist/chunk-6HMM2R5T.js.map +0 -1
  74. package/dist/chunk-AZHW7R5V.js.map +0 -1
  75. package/dist/chunk-KRLPEMLH.js.map +0 -1
  76. package/dist/chunk-SIBYBA2D.js.map +0 -1
  77. package/dist/chunk-UZNXG44K.js.map +0 -1
  78. /package/dist/{chunk-2PRS4J5Q.js.map → chunk-HY3F5Q4E.js.map} +0 -0
  79. /package/dist/{chunk-NAB6FKFN.js.map → chunk-O6FTELKH.js.map} +0 -0
  80. /package/dist/{chunk-E6CLMKEE.js.map → chunk-V3TRUXFJ.js.map} +0 -0
  81. /package/dist/{chunk-2EH4WRHL.js.map → chunk-ZWRIPCTR.js.map} +0 -0
@@ -0,0 +1,103 @@
1
+ -- deployment-mode: rolling
2
+ -- The durable company-profile storage contract retains its historical list
3
+ -- fields for revision compatibility, but new product surfaces use only the
4
+ -- small always-on organization identity and mission. Existing proposal rows
5
+ -- keep their immutable human-input payload; only proposals prepared after this
6
+ -- migration receive the narrowed confirmation copy.
7
+
8
+ CREATE OR REPLACE FUNCTION company_profile_agent_confirmation_summary(p_content_json text)
9
+ RETURNS text
10
+ LANGUAGE plpgsql IMMUTABLE
11
+ AS $body$
12
+ DECLARE
13
+ profile jsonb := p_content_json::jsonb;
14
+ summary text;
15
+ list_name text;
16
+ list_text text;
17
+ BEGIN
18
+ summary := 'Identity: ' || coalesce(nullif(btrim(profile->>'identity'), ''), '(none)')
19
+ || E'\nMission: ' || coalesce(nullif(btrim(profile->>'mission'), ''), '(none)');
20
+ -- Rolling compatibility: old API instances may still submit these fields.
21
+ -- Never hide nonempty content from the human confirmation. New instances
22
+ -- submit empty arrays, so the narrowed identity UI remains concise.
23
+ FOREACH list_name IN ARRAY ARRAY['products', 'customers', 'goals', 'constraints'] LOOP
24
+ IF jsonb_array_length(coalesce(profile->list_name, '[]'::jsonb)) > 0 THEN
25
+ SELECT string_agg('- ' || (item->>'key') || ': ' || (item->>'content'), E'\n'
26
+ ORDER BY entries.ordinality)
27
+ INTO list_text
28
+ FROM jsonb_array_elements(profile->list_name)
29
+ WITH ORDINALITY AS entries(item, ordinality);
30
+ summary := summary || E'\nLegacy ' || initcap(list_name)
31
+ || ' (retained compatibility context):' || E'\n' || list_text;
32
+ END IF;
33
+ END LOOP;
34
+ -- HumanInputQuestion.helpText is capped at 2048 UTF-16 code units; the
35
+ -- revision/hash prefix uses under 100. Bound the rendered bytes (a UTF-16
36
+ -- unit never needs more bytes than it needs units) without splitting a
37
+ -- character.
38
+ IF octet_length(summary) > 1800 THEN
39
+ WHILE octet_length(summary) > 1797 LOOP
40
+ summary := left(
41
+ summary,
42
+ length(summary) - greatest(1, (octet_length(summary) - 1797) / 4)
43
+ );
44
+ END LOOP;
45
+ summary := summary || '...';
46
+ END IF;
47
+ RETURN summary;
48
+ END
49
+ $body$;
50
+
51
+ CREATE OR REPLACE FUNCTION company_profile_agent_confirmation_prompt(
52
+ p_revision_id uuid,
53
+ p_revision bigint,
54
+ p_content_hash text,
55
+ p_content_json text
56
+ ) RETURNS jsonb
57
+ LANGUAGE sql IMMUTABLE
58
+ AS $body$
59
+ SELECT jsonb_build_object(
60
+ 'questions', jsonb_build_array(jsonb_build_object(
61
+ 'id', 'company-profile:' || p_revision_id::text,
62
+ 'kind', 'single_select',
63
+ 'prompt', CASE
64
+ WHEN jsonb_array_length(coalesce((p_content_json::jsonb)->'products', '[]'::jsonb))
65
+ + jsonb_array_length(coalesce((p_content_json::jsonb)->'customers', '[]'::jsonb))
66
+ + jsonb_array_length(coalesce((p_content_json::jsonb)->'goals', '[]'::jsonb))
67
+ + jsonb_array_length(coalesce((p_content_json::jsonb)->'constraints', '[]'::jsonb)) > 0
68
+ THEN 'Activate this organization identity and retained legacy details?'
69
+ ELSE 'Activate this organization identity and mission?'
70
+ END,
71
+ 'label', 'Organization identity',
72
+ 'helpText', 'Revision ' || p_revision::text || '; SHA-256 ' || p_content_hash
73
+ || E'.\n\n' || company_profile_agent_confirmation_summary(p_content_json),
74
+ 'options', jsonb_build_array(
75
+ jsonb_build_object('id', 'activate', 'label', 'Activate'),
76
+ jsonb_build_object('id', 'skip', 'label', 'Do not activate')
77
+ ),
78
+ 'required', true,
79
+ 'allowOther', true
80
+ )),
81
+ 'allowSkip', false
82
+ )
83
+ $body$;
84
+
85
+ -- CREATE OR REPLACE resets per-function configuration. Restore the pinned
86
+ -- search path installed by migration 0324 so these helpers retain the same
87
+ -- schema-agnostic hardening after the rolling replacement.
88
+ DO $organization_identity_confirmation_hardening$
89
+ DECLARE
90
+ target_schema text := current_schema();
91
+ BEGIN
92
+ EXECUTE format(
93
+ 'ALTER FUNCTION %I.company_profile_agent_confirmation_summary(text) '
94
+ || 'SET search_path = pg_catalog, %I, pg_temp',
95
+ target_schema, target_schema
96
+ );
97
+ EXECUTE format(
98
+ 'ALTER FUNCTION %I.company_profile_agent_confirmation_prompt(uuid,bigint,text,text) '
99
+ || 'SET search_path = pg_catalog, %I, pg_temp',
100
+ target_schema, target_schema
101
+ );
102
+ END
103
+ $organization_identity_confirmation_hardening$;
@@ -0,0 +1,249 @@
1
+ -- deployment-mode: rolling
2
+ -- A human-confirmed `remember` Knowledge claim materializes into one exact
3
+ -- Memory record. The confirmation receipt is the idempotency identity: general
4
+ -- Memory deduplication must never substitute a normalized lookalike, and replay
5
+ -- must keep returning the same Memory id after later archival.
6
+
7
+ SET lock_timeout = '5s';
8
+ SET statement_timeout = '60s';
9
+
10
+ CREATE TABLE remember_knowledge_memory_materializations (
11
+ confirmation_receipt_id uuid PRIMARY KEY
12
+ REFERENCES remember_knowledge_confirmation_receipts(id) ON DELETE CASCADE,
13
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
14
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
15
+ session_id uuid NOT NULL,
16
+ memory_id uuid NOT NULL,
17
+ task_note_text_hash text NOT NULL,
18
+ created_at timestamptz NOT NULL DEFAULT transaction_timestamp(),
19
+ CONSTRAINT remember_knowledge_memory_materializations_workspace_account_fk
20
+ FOREIGN KEY (workspace_id, account_id)
21
+ REFERENCES workspaces(id, account_id) ON DELETE CASCADE,
22
+ CONSTRAINT remember_knowledge_memory_materializations_session_fk
23
+ FOREIGN KEY (workspace_id, session_id)
24
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE,
25
+ CONSTRAINT remember_knowledge_memory_materializations_memory_uq
26
+ UNIQUE (workspace_id, memory_id),
27
+ CONSTRAINT remember_knowledge_memory_materializations_hash_chk
28
+ CHECK (task_note_text_hash ~ '^[0-9a-f]{64}$')
29
+ );
30
+
31
+ CREATE INDEX remember_knowledge_memory_materializations_workspace_time_idx
32
+ ON remember_knowledge_memory_materializations (workspace_id, created_at DESC, memory_id);
33
+
34
+ ALTER TABLE remember_knowledge_memory_materializations ENABLE ROW LEVEL SECURITY;
35
+ ALTER TABLE remember_knowledge_memory_materializations FORCE ROW LEVEL SECURITY;
36
+ CREATE POLICY remember_knowledge_memory_materializations_tenant
37
+ ON remember_knowledge_memory_materializations
38
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
39
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
40
+ CREATE POLICY session_visibility_isolation
41
+ ON remember_knowledge_memory_materializations AS RESTRICTIVE
42
+ USING (session_reference_visible(account_id, workspace_id, session_id))
43
+ WITH CHECK (session_reference_visible(account_id, workspace_id, session_id));
44
+
45
+ CREATE OR REPLACE FUNCTION reject_remember_knowledge_memory_materialization_mutation()
46
+ RETURNS trigger LANGUAGE plpgsql AS $body$
47
+ BEGIN
48
+ IF TG_OP = 'DELETE' AND pg_catalog.pg_trigger_depth() > 1 AND (
49
+ NOT EXISTS (SELECT 1 FROM workspaces WHERE id = OLD.workspace_id)
50
+ OR NOT EXISTS (
51
+ SELECT 1 FROM sessions WHERE workspace_id = OLD.workspace_id AND id = OLD.session_id
52
+ )
53
+ OR NOT EXISTS (
54
+ SELECT 1 FROM remember_knowledge_confirmation_receipts
55
+ WHERE id = OLD.confirmation_receipt_id
56
+ )
57
+ ) THEN
58
+ RETURN OLD;
59
+ END IF;
60
+ RAISE EXCEPTION 'remember Knowledge Memory materializations are immutable'
61
+ USING ERRCODE = '55000';
62
+ END
63
+ $body$;
64
+
65
+ CREATE TRIGGER remember_knowledge_memory_materializations_immutable
66
+ BEFORE UPDATE OR DELETE ON remember_knowledge_memory_materializations
67
+ FOR EACH ROW EXECUTE FUNCTION reject_remember_knowledge_memory_materialization_mutation();
68
+
69
+ CREATE OR REPLACE FUNCTION materialize_remember_knowledge_memory(
70
+ p_account_id uuid,
71
+ p_workspace_id uuid,
72
+ p_confirmation_receipt_id uuid
73
+ ) RETURNS SETOF remember_knowledge_memory_materializations
74
+ LANGUAGE plpgsql SECURITY DEFINER AS $body$
75
+ DECLARE
76
+ caller_subject_id text := nullif(current_setting('opengeni.subject_id', true), '');
77
+ receipt_row remember_knowledge_confirmation_receipts%ROWTYPE;
78
+ result_row remember_knowledge_memory_materializations%ROWTYPE;
79
+ source_text text;
80
+ memory_id_value uuid;
81
+ normalized_text text;
82
+ normalized_text_hash text;
83
+ visible_count integer;
84
+ BEGIN
85
+ IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_confirmation_receipt_id IS NULL
86
+ OR caller_subject_id IS NULL OR length(btrim(caller_subject_id)) NOT BETWEEN 1 AND 1024
87
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
88
+ OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
89
+ THEN
90
+ RAISE EXCEPTION 'remember Knowledge Memory materialization requires exact tenant authority'
91
+ USING ERRCODE = '42501';
92
+ END IF;
93
+
94
+ -- Serialize every first materialization/replay on the immutable confirmation
95
+ -- identity. The Memory row may later become archived; the mapping remains the
96
+ -- stable answer and is deliberately not foreign-keyed to mutable Memory state.
97
+ SELECT * INTO receipt_row
98
+ FROM remember_knowledge_confirmation_receipts receipt
99
+ WHERE receipt.id = p_confirmation_receipt_id
100
+ AND receipt.account_id = p_account_id
101
+ AND receipt.workspace_id = p_workspace_id
102
+ AND receipt.initiating_human_subject_id = caller_subject_id
103
+ FOR UPDATE;
104
+ IF NOT FOUND THEN
105
+ RAISE EXCEPTION 'remember Knowledge confirmation is unavailable'
106
+ USING ERRCODE = '42501';
107
+ END IF;
108
+
109
+ SELECT * INTO result_row
110
+ FROM remember_knowledge_memory_materializations materialization
111
+ WHERE materialization.confirmation_receipt_id = p_confirmation_receipt_id;
112
+ IF FOUND THEN
113
+ IF result_row.account_id <> p_account_id
114
+ OR result_row.workspace_id <> p_workspace_id
115
+ OR result_row.session_id <> receipt_row.session_id
116
+ OR result_row.task_note_text_hash <> receipt_row.task_note_text_hash
117
+ THEN
118
+ RAISE EXCEPTION 'remember Knowledge Memory materialization conflicts with immutable receipt'
119
+ USING ERRCODE = '23505';
120
+ END IF;
121
+ RETURN NEXT result_row;
122
+ RETURN;
123
+ END IF;
124
+
125
+ SELECT fact.object_value #>> '{}'
126
+ INTO source_text
127
+ FROM knowledge_claims claim
128
+ JOIN knowledge_facts fact
129
+ ON fact.account_id = claim.account_id
130
+ AND fact.scope_key = claim.scope_key
131
+ AND fact.id = claim.fact_id
132
+ JOIN knowledge_claim_evidence evidence
133
+ ON evidence.account_id = claim.account_id
134
+ AND evidence.scope_key = claim.scope_key
135
+ AND evidence.claim_id = claim.id
136
+ JOIN knowledge_claim_reviews review
137
+ ON review.account_id = claim.account_id
138
+ AND review.scope_key = claim.scope_key
139
+ AND review.claim_id = claim.id
140
+ WHERE claim.account_id = p_account_id
141
+ AND claim.scope_kind = 'workspace'
142
+ AND claim.scope_workspace_id = p_workspace_id
143
+ AND claim.scope_subject_id IS NULL
144
+ AND claim.id = receipt_row.claim_id
145
+ AND claim.extraction_method = 'task-note-promotion-v1'
146
+ AND claim.initiating_human_subject_id = caller_subject_id
147
+ AND fact.object_kind = 'text'
148
+ AND evidence.id = receipt_row.evidence_id
149
+ AND evidence.task_note_id = receipt_row.task_note_id
150
+ AND evidence.task_note_version = 1
151
+ AND evidence.content_hash = receipt_row.task_note_text_hash
152
+ AND review.id = receipt_row.approval_review_id
153
+ AND review.review_revision = receipt_row.approval_review_revision
154
+ AND review.state = 'approved'
155
+ LIMIT 1;
156
+
157
+ normalized_text := lower(btrim(regexp_replace(source_text, '\s+', ' ', 'g')));
158
+ IF source_text IS NULL OR normalized_text = '' OR length(source_text) > 4000
159
+ OR encode(sha256(convert_to(source_text, 'UTF8')), 'hex')
160
+ <> receipt_row.task_note_text_hash
161
+ THEN
162
+ RAISE EXCEPTION 'remember Knowledge confirmation no longer resolves to its exact approved text'
163
+ USING ERRCODE = '23514';
164
+ END IF;
165
+ normalized_text_hash := encode(sha256(convert_to(normalized_text, 'UTF8')), 'hex');
166
+
167
+ SELECT count(*)::integer INTO visible_count
168
+ FROM knowledge_memories memory
169
+ WHERE memory.account_id = p_account_id
170
+ AND memory.workspace_id = p_workspace_id
171
+ AND memory.status IN ('active', 'approved');
172
+ IF visible_count >= 2000 THEN
173
+ RAISE EXCEPTION 'Workspace visible Memory is full (2000 visible records)'
174
+ USING ERRCODE = '54000';
175
+ END IF;
176
+
177
+ memory_id_value := gen_random_uuid();
178
+ PERFORM set_config('opengeni.memory_actor_kind', 'subject', true);
179
+ PERFORM set_config('opengeni.memory_actor_id', caller_subject_id, true);
180
+ PERFORM set_config('opengeni.memory_session_id', receipt_row.session_id::text, true);
181
+
182
+ INSERT INTO knowledge_memories (
183
+ id, account_id, workspace_id, status, kind, scope, text, text_codec_version,
184
+ source_refs, confidence, metadata, created_by_session_id, pinned,
185
+ scope_type, scope_subject_id, scope_role_key, scope_session_id,
186
+ namespace_key, labels, text_hash
187
+ ) VALUES (
188
+ memory_id_value, p_account_id, p_workspace_id, 'active', 'semantic', 'workspace',
189
+ source_text, NULL,
190
+ jsonb_build_array(jsonb_build_object(
191
+ 'kind', 'session_event', 'id', receipt_row.session_id, 'metadata', '{}'::jsonb
192
+ )),
193
+ 100,
194
+ jsonb_build_object(
195
+ 'origin', 'human',
196
+ 'source', 'remember_confirmation',
197
+ 'confirmationReceiptId', receipt_row.id,
198
+ 'claimId', receipt_row.claim_id,
199
+ 'evidenceId', receipt_row.evidence_id,
200
+ 'taskNoteId', receipt_row.task_note_id
201
+ ),
202
+ receipt_row.session_id, false,
203
+ 'workspace', NULL, NULL, NULL,
204
+ 'remember/' || receipt_row.id::text, '{}'::text[], normalized_text_hash
205
+ );
206
+
207
+ INSERT INTO remember_knowledge_memory_materializations (
208
+ confirmation_receipt_id, account_id, workspace_id, session_id,
209
+ memory_id, task_note_text_hash
210
+ ) VALUES (
211
+ receipt_row.id, p_account_id, p_workspace_id, receipt_row.session_id,
212
+ memory_id_value, receipt_row.task_note_text_hash
213
+ ) RETURNING * INTO result_row;
214
+
215
+ RETURN NEXT result_row;
216
+ END
217
+ $body$;
218
+
219
+ REVOKE ALL ON TABLE remember_knowledge_memory_materializations FROM PUBLIC;
220
+ REVOKE ALL ON FUNCTION materialize_remember_knowledge_memory(uuid, uuid, uuid) FROM PUBLIC;
221
+
222
+ DO $remember_knowledge_memory_materialization_hardening$
223
+ DECLARE
224
+ target_schema text := current_schema();
225
+ runtime_role text := nullif(current_setting('opengeni.runtime_role', true), '');
226
+ BEGIN
227
+ EXECUTE format(
228
+ 'ALTER FUNCTION %I.materialize_remember_knowledge_memory(uuid,uuid,uuid) '
229
+ || 'SET search_path = pg_catalog, %I, pg_temp',
230
+ target_schema, target_schema
231
+ );
232
+ IF runtime_role IS NULL AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
233
+ runtime_role := 'opengeni_app';
234
+ END IF;
235
+ IF runtime_role IS NOT NULL THEN
236
+ EXECUTE format(
237
+ 'REVOKE ALL ON TABLE %I.remember_knowledge_memory_materializations FROM %I',
238
+ target_schema, runtime_role
239
+ );
240
+ EXECUTE format(
241
+ 'GRANT EXECUTE ON FUNCTION %I.materialize_remember_knowledge_memory(uuid,uuid,uuid) TO %I',
242
+ target_schema, runtime_role
243
+ );
244
+ END IF;
245
+ END
246
+ $remember_knowledge_memory_materialization_hardening$;
247
+
248
+ RESET statement_timeout;
249
+ RESET lock_timeout;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "3.2.0-canary.0",
3
+ "version": "3.4.0-canary.0",
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": {
@@ -74,11 +74,11 @@
74
74
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
75
75
  },
76
76
  "dependencies": {
77
- "@opengeni/codemode": "^0.4.13-canary.0",
78
- "@opengeni/codex": "^0.2.19-canary.0",
79
- "@opengeni/config": "^0.19.1-canary.0",
80
- "@opengeni/contracts": "^2.3.0-canary.0",
81
- "@opengeni/network": "^0.2.3-canary.0",
77
+ "@opengeni/codemode": "^0.4.15-canary.0",
78
+ "@opengeni/codex": "^0.2.19-canary.1",
79
+ "@opengeni/config": "^0.20.1-canary.0",
80
+ "@opengeni/contracts": "^2.5.0-canary.0",
81
+ "@opengeni/network": "^0.2.3-canary.1",
82
82
  "drizzle-orm": "^0.45.2",
83
83
  "postgres": "^3.4.7"
84
84
  },
package/src/database.ts CHANGED
@@ -317,11 +317,14 @@ export function createDb(databaseUrl: string, options: CreateDbOptions = {}): Db
317
317
  prepare: false,
318
318
  idle_timeout: 30,
319
319
  max_lifetime: 1800,
320
- // `connection` carries per-session Postgres STARTUP parameters. `application_name`
321
- // (always) aids server-side diagnostics; `search_path` (embedded only) is the
322
- // supported, query-param-free way to scope a connection to a dedicated schema —
323
- // postgres-js IGNORES a URL `?search_path=`. Unset searchPath → omit it so the
324
- // server default (`public`) is unchanged for standalone.
320
+ // `connection` carries per-session Postgres STARTUP parameters. The exact
321
+ // `application_name` is also the PgBouncer-compatible current-image receipt
322
+ // for migration 0352's restrictive sessions policy; arbitrary custom startup
323
+ // parameters are not portable through transaction poolers. `search_path`
324
+ // (embedded only) is the supported, query-param-free way to scope a connection
325
+ // to a dedicated schema — postgres-js IGNORES a URL `?search_path=`. Unset
326
+ // searchPath → omit it so the server default (`public`) is unchanged for
327
+ // standalone.
325
328
  connection: {
326
329
  application_name: LOSSLESS_CONTENT_WRITER_APPLICATION_NAME,
327
330
  ...(options.searchPath ? { search_path: options.searchPath } : {}),
@@ -377,12 +380,13 @@ export async function setRlsContext(db: Database, context: RlsContext): Promise<
377
380
  );
378
381
  // Transaction-local writer identity covers supported injected/embedded
379
382
  // database handles whose connection-level application_name is host-owned.
380
- // Old OpenGeni binaries do not set this GUC, so migration-installed update
381
- // fences can distinguish their partial writes without inspecting content.
383
+ // Standalone createDb connections also carry version receipts in their exact
384
+ // application_name, while old OpenGeni binaries set neither current receipt.
382
385
  await db.execute(sql`select set_config('opengeni.lossless_content_writer', '1', true)`);
383
386
  await db.execute(sql`select
384
387
  set_config('opengeni.sandbox_recovery_protocol_v2', '1', true),
385
- set_config('opengeni.pending_tool_event_output_v1', '1', true)`);
388
+ set_config('opengeni.pending_tool_event_output_v1', '1', true),
389
+ set_config('opengeni.session_variable_set_attachments_v1', '1', true)`);
386
390
  const sessionActor = sessionRlsActorContext.getStore();
387
391
  if (sessionActor) {
388
392
  await setSubjectRlsContext(db, sessionActor.subjectId);
@@ -174,8 +174,17 @@ function undoFromRow(row: UndoRow): UndoReceipt {
174
174
  /** Subject-filtered activation and compensation history for Workspace State. */
175
175
  export async function listGovernedLearningActivationHistory(
176
176
  db: Database,
177
- input: { workspaceId: string; subjectId: string; principalKind: string; limit: number },
178
- ): Promise<{ activations: ActivationReceipt[]; undos: UndoReceipt[]; truncated: boolean }> {
177
+ input: {
178
+ workspaceId: string;
179
+ subjectId: string;
180
+ principalKind: string;
181
+ limit: number;
182
+ },
183
+ ): Promise<{
184
+ activations: ActivationReceipt[];
185
+ undos: UndoReceipt[];
186
+ truncated: boolean;
187
+ }> {
179
188
  if (input.principalKind !== "human_session") {
180
189
  throw new GovernedLearningActivationAuthorityError(
181
190
  "Governed-learning history requires an exact authenticated human actor",
@@ -562,6 +571,69 @@ export async function confirmRememberKnowledgeClaim(
562
571
  }
563
572
  }
564
573
 
574
+ export type RememberKnowledgeMemoryMaterialization = {
575
+ confirmationReceiptId: string;
576
+ accountId: string;
577
+ workspaceId: string;
578
+ sessionId: string;
579
+ memoryId: string;
580
+ taskNoteTextHash: string;
581
+ createdAt: string;
582
+ };
583
+
584
+ /**
585
+ * Materialize the exact approved Task-note text behind one completed remember
586
+ * confirmation. The database capability owns the insert and its immutable
587
+ * confirmation-to-Memory mapping, so generic deduplication cannot substitute a
588
+ * normalized lookalike and replay keeps the same Memory id after archival.
589
+ */
590
+ export async function materializeConfirmedRememberKnowledgeMemory(
591
+ db: Database,
592
+ receipt: RememberKnowledgeConfirmationReceiptType,
593
+ ): Promise<RememberKnowledgeMemoryMaterialization> {
594
+ const confirmed = RememberKnowledgeConfirmationReceipt.parse(receipt);
595
+ try {
596
+ return await withWorkspaceSubjectRls(
597
+ db,
598
+ confirmed.workspaceId,
599
+ confirmed.initiatingHumanSubjectId,
600
+ async (scoped) => {
601
+ const rows = await rawRows<{
602
+ confirmation_receipt_id: string;
603
+ account_id: string;
604
+ workspace_id: string;
605
+ session_id: string;
606
+ memory_id: string;
607
+ task_note_text_hash: string;
608
+ created_at: Date | string;
609
+ }>(
610
+ scoped,
611
+ sql`SELECT * FROM materialize_remember_knowledge_memory(
612
+ ${confirmed.accountId}::uuid,
613
+ ${confirmed.workspaceId}::uuid,
614
+ ${confirmed.id}::uuid
615
+ )`,
616
+ );
617
+ const row = rows[0];
618
+ if (rows.length !== 1 || !row) {
619
+ throw new Error("remember Knowledge Memory materialization returned no unique receipt");
620
+ }
621
+ return {
622
+ confirmationReceiptId: row.confirmation_receipt_id,
623
+ accountId: row.account_id,
624
+ workspaceId: row.workspace_id,
625
+ sessionId: row.session_id,
626
+ memoryId: row.memory_id,
627
+ taskNoteTextHash: row.task_note_text_hash,
628
+ createdAt: iso(row.created_at),
629
+ };
630
+ },
631
+ );
632
+ } catch (error) {
633
+ translate(error);
634
+ }
635
+ }
636
+
565
637
  /**
566
638
  * Read the initiating human bound to one workspace-scoped Knowledge claim under
567
639
  * workspace RLS. Used by `remember_confirm` for the Knowledge lane; exposes no