@opengeni/db 0.28.1 → 0.28.9
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/dist/{chunk-HT5T62CC.js → chunk-P6CUHXQZ.js} +651 -206
- package/dist/chunk-P6CUHXQZ.js.map +1 -0
- package/dist/{chunk-DBS5HPEW.js → chunk-VHBFHMPC.js} +7 -1
- package/dist/chunk-VHBFHMPC.js.map +1 -0
- package/dist/environment-crypto.d.ts +8 -4
- package/dist/index.d.ts +265 -20
- package/dist/index.js +3966 -2185
- package/dist/index.js.map +1 -1
- package/dist/lossless-columns.d.ts +58 -0
- package/dist/lossless-json.d.ts +39 -0
- package/dist/memory-domain.d.ts +3 -6
- package/dist/persistence-errors.d.ts +7 -14
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +1419 -271
- package/dist/schema.js +9 -1
- package/dist/session-realtime-terminal.d.ts +7 -0
- package/dist/transcription-recordings-schema.d.ts +44 -6
- package/dist/transcription-recordings.d.ts +1 -1
- package/drizzle/0065_enrollment_credential_generation.sql +10 -0
- package/drizzle/0140_retained_screenshot_artifacts.sql +192 -0
- package/drizzle/0176_lossless_canonical_json.sql +975 -0
- package/drizzle/0177_session_events_workspace_turn_type_index.sql +5 -0
- package/drizzle/0178_permissioned_secret_reads.sql +14 -0
- package/drizzle/0179_slack_private_shortcut_delivery_gate.sql +85 -0
- package/drizzle/0180_retained_screenshot_lifecycle_fences.sql +273 -0
- package/drizzle/0181_connected_machine_removal.sql +52 -0
- package/drizzle/0182_connected_machine_remove_session_default.sql +77 -0
- package/package.json +4 -4
- package/src/database.ts +7 -1
- package/src/environment-crypto.ts +22 -9
- package/src/index.ts +4253 -1601
- package/src/lossless-columns.ts +35 -0
- package/src/lossless-json.ts +322 -0
- package/src/memory-domain.ts +5 -44
- package/src/persistence-errors.ts +29 -34
- package/src/runtime-posture.ts +6 -0
- package/src/schema.ts +260 -38
- package/src/session-control.ts +125 -76
- package/src/session-queue-commands.ts +325 -234
- package/src/session-realtime-context.ts +18 -5
- package/src/session-realtime-ledger.ts +29 -7
- package/src/session-realtime-mirror.ts +4 -2
- package/src/session-realtime-terminal.ts +89 -21
- package/src/session-realtime.ts +3 -2
- package/src/session-tool-call-settlement.ts +29 -15
- package/src/transcription-recordings-schema.ts +5 -2
- package/src/transcription-recordings.ts +70 -40
- package/dist/chunk-DBS5HPEW.js.map +0 -1
- package/dist/chunk-HT5T62CC.js.map +0 -1
- package/dist/event-payload-sanitizer.d.ts +0 -32
- package/src/event-payload-sanitizer.ts +0 -377
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Canonical OpenGeni JSON is stored in full. Bounded payloads are derived only
|
|
3
|
+
-- for explicit monitoring/public projections; the write boundary must not
|
|
4
|
+
-- replace source bytes merely to satisfy a timeline preview envelope.
|
|
5
|
+
|
|
6
|
+
SET lock_timeout = '5s';
|
|
7
|
+
SET statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
-- Codec/version truth is out-of-band. Existing rows and writes from an older
|
|
10
|
+
-- application remain NULL and are therefore literal data, even when a value is
|
|
11
|
+
-- byte-for-byte identical to a valid active marker. The new application writes
|
|
12
|
+
-- version 1 only in the same statement as its encoded value; there is
|
|
13
|
+
-- deliberately no database or application default during the rolling window.
|
|
14
|
+
ALTER TABLE sessions
|
|
15
|
+
ADD COLUMN IF NOT EXISTS initial_message_codec_version integer;
|
|
16
|
+
ALTER TABLE session_realtime_entries
|
|
17
|
+
ADD COLUMN IF NOT EXISTS text_codec_version integer,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer;
|
|
19
|
+
ALTER TABLE knowledge_memories
|
|
20
|
+
ADD COLUMN IF NOT EXISTS text_codec_version integer;
|
|
21
|
+
ALTER TABLE session_turns
|
|
22
|
+
ADD COLUMN IF NOT EXISTS prompt_codec_version integer;
|
|
23
|
+
ALTER TABLE session_system_updates
|
|
24
|
+
ADD COLUMN IF NOT EXISTS summary_codec_version integer,
|
|
25
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer;
|
|
26
|
+
ALTER TABLE session_system_update_outbox
|
|
27
|
+
ADD COLUMN IF NOT EXISTS summary_codec_version integer,
|
|
28
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer;
|
|
29
|
+
ALTER TABLE session_events
|
|
30
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer;
|
|
31
|
+
ALTER TABLE host_export_outbox
|
|
32
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer;
|
|
33
|
+
ALTER TABLE host_export_consumers
|
|
34
|
+
ADD COLUMN IF NOT EXISTS last_error_codec_version integer;
|
|
35
|
+
ALTER TABLE agent_run_states
|
|
36
|
+
ADD COLUMN IF NOT EXISTS serialized_run_state_codec_version integer,
|
|
37
|
+
ADD COLUMN IF NOT EXISTS pending_approvals_codec_version integer;
|
|
38
|
+
ALTER TABLE session_history_items
|
|
39
|
+
ADD COLUMN IF NOT EXISTS item_codec_version integer;
|
|
40
|
+
ALTER TABLE session_pending_tool_calls
|
|
41
|
+
ADD COLUMN IF NOT EXISTS call_item_codec_version integer,
|
|
42
|
+
ADD COLUMN IF NOT EXISTS result_item_codec_version integer;
|
|
43
|
+
ALTER TABLE sandbox_session_envelopes
|
|
44
|
+
ADD COLUMN IF NOT EXISTS envelope_codec_version integer;
|
|
45
|
+
ALTER TABLE session_recordings
|
|
46
|
+
ADD COLUMN IF NOT EXISTS reason_codec_version integer;
|
|
47
|
+
ALTER TABLE host_export_dead_letters
|
|
48
|
+
ADD COLUMN IF NOT EXISTS envelope_codec_version integer,
|
|
49
|
+
ADD COLUMN IF NOT EXISTS event_payload_codec_version integer;
|
|
50
|
+
ALTER TABLE audit_events
|
|
51
|
+
ADD COLUMN IF NOT EXISTS metadata_codec_version integer;
|
|
52
|
+
ALTER TABLE rig_changes
|
|
53
|
+
ADD COLUMN IF NOT EXISTS payload_codec_version integer,
|
|
54
|
+
ADD COLUMN IF NOT EXISTS verification_codec_version integer;
|
|
55
|
+
ALTER TABLE transcription_recordings
|
|
56
|
+
ADD COLUMN IF NOT EXISTS transcript_text_codec_version integer;
|
|
57
|
+
ALTER TABLE transcription_recording_segments
|
|
58
|
+
ADD COLUMN IF NOT EXISTS transcript_text_codec_version integer;
|
|
59
|
+
|
|
60
|
+
ALTER TABLE sessions
|
|
61
|
+
ADD CONSTRAINT sessions_initial_message_codec_version_chk
|
|
62
|
+
CHECK (initial_message_codec_version IS NULL OR initial_message_codec_version = 1) NOT VALID;
|
|
63
|
+
ALTER TABLE session_realtime_entries
|
|
64
|
+
ADD CONSTRAINT session_realtime_entries_text_codec_version_chk
|
|
65
|
+
CHECK (text_codec_version IS NULL OR text_codec_version = 1) NOT VALID,
|
|
66
|
+
ADD CONSTRAINT session_realtime_entries_payload_codec_version_chk
|
|
67
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID;
|
|
68
|
+
ALTER TABLE knowledge_memories
|
|
69
|
+
ADD CONSTRAINT knowledge_memories_text_codec_version_chk
|
|
70
|
+
CHECK (text_codec_version IS NULL OR text_codec_version = 1) NOT VALID;
|
|
71
|
+
ALTER TABLE session_turns
|
|
72
|
+
ADD CONSTRAINT session_turns_prompt_codec_version_chk
|
|
73
|
+
CHECK (prompt_codec_version IS NULL OR prompt_codec_version = 1) NOT VALID;
|
|
74
|
+
ALTER TABLE session_system_updates
|
|
75
|
+
ADD CONSTRAINT session_system_updates_summary_codec_version_chk
|
|
76
|
+
CHECK (summary_codec_version IS NULL OR summary_codec_version = 1) NOT VALID,
|
|
77
|
+
ADD CONSTRAINT session_system_updates_payload_codec_version_chk
|
|
78
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID;
|
|
79
|
+
ALTER TABLE session_system_update_outbox
|
|
80
|
+
ADD CONSTRAINT session_system_update_outbox_summary_codec_version_chk
|
|
81
|
+
CHECK (summary_codec_version IS NULL OR summary_codec_version = 1) NOT VALID,
|
|
82
|
+
ADD CONSTRAINT session_system_update_outbox_payload_codec_version_chk
|
|
83
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID;
|
|
84
|
+
ALTER TABLE session_events
|
|
85
|
+
ADD CONSTRAINT session_events_payload_codec_version_chk
|
|
86
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID;
|
|
87
|
+
ALTER TABLE host_export_outbox
|
|
88
|
+
ADD CONSTRAINT host_export_outbox_payload_codec_version_chk
|
|
89
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID;
|
|
90
|
+
ALTER TABLE host_export_consumers
|
|
91
|
+
ADD CONSTRAINT host_export_consumers_last_error_codec_version_chk
|
|
92
|
+
CHECK (last_error_codec_version IS NULL OR last_error_codec_version = 1) NOT VALID;
|
|
93
|
+
ALTER TABLE agent_run_states
|
|
94
|
+
ADD CONSTRAINT agent_run_states_serialized_codec_version_chk
|
|
95
|
+
CHECK (serialized_run_state_codec_version IS NULL OR serialized_run_state_codec_version = 1) NOT VALID,
|
|
96
|
+
ADD CONSTRAINT agent_run_states_pending_codec_version_chk
|
|
97
|
+
CHECK (pending_approvals_codec_version IS NULL OR pending_approvals_codec_version = 1) NOT VALID;
|
|
98
|
+
ALTER TABLE session_history_items
|
|
99
|
+
ADD CONSTRAINT session_history_items_item_codec_version_chk
|
|
100
|
+
CHECK (item_codec_version IS NULL OR item_codec_version = 1) NOT VALID;
|
|
101
|
+
ALTER TABLE session_pending_tool_calls
|
|
102
|
+
ADD CONSTRAINT session_pending_tool_calls_call_codec_version_chk
|
|
103
|
+
CHECK (call_item_codec_version IS NULL OR call_item_codec_version = 1) NOT VALID,
|
|
104
|
+
ADD CONSTRAINT session_pending_tool_calls_result_codec_version_chk
|
|
105
|
+
CHECK (result_item_codec_version IS NULL OR result_item_codec_version = 1) NOT VALID;
|
|
106
|
+
ALTER TABLE sandbox_session_envelopes
|
|
107
|
+
ADD CONSTRAINT sandbox_session_envelopes_codec_version_chk
|
|
108
|
+
CHECK (envelope_codec_version IS NULL OR envelope_codec_version = 1) NOT VALID;
|
|
109
|
+
ALTER TABLE session_recordings
|
|
110
|
+
ADD CONSTRAINT session_recordings_reason_codec_version_chk
|
|
111
|
+
CHECK (reason_codec_version IS NULL OR reason_codec_version = 1) NOT VALID;
|
|
112
|
+
ALTER TABLE host_export_dead_letters
|
|
113
|
+
ADD CONSTRAINT host_export_dead_letters_codec_version_chk
|
|
114
|
+
CHECK (envelope_codec_version IS NULL OR envelope_codec_version = 1) NOT VALID,
|
|
115
|
+
ADD CONSTRAINT host_export_dead_letters_event_payload_codec_version_chk
|
|
116
|
+
CHECK (event_payload_codec_version IS NULL OR event_payload_codec_version = 1) NOT VALID;
|
|
117
|
+
ALTER TABLE audit_events
|
|
118
|
+
ADD CONSTRAINT audit_events_metadata_codec_version_chk
|
|
119
|
+
CHECK (metadata_codec_version IS NULL OR metadata_codec_version = 1) NOT VALID;
|
|
120
|
+
ALTER TABLE rig_changes
|
|
121
|
+
ADD CONSTRAINT rig_changes_payload_codec_version_chk
|
|
122
|
+
CHECK (payload_codec_version IS NULL OR payload_codec_version = 1) NOT VALID,
|
|
123
|
+
ADD CONSTRAINT rig_changes_verification_codec_version_chk
|
|
124
|
+
CHECK (verification_codec_version IS NULL OR verification_codec_version = 1) NOT VALID;
|
|
125
|
+
ALTER TABLE transcription_recordings
|
|
126
|
+
ADD CONSTRAINT transcription_recordings_text_codec_version_chk
|
|
127
|
+
CHECK (transcript_text_codec_version IS NULL OR transcript_text_codec_version = 1) NOT VALID;
|
|
128
|
+
ALTER TABLE transcription_recording_segments
|
|
129
|
+
ADD CONSTRAINT transcription_recording_segments_text_codec_version_chk
|
|
130
|
+
CHECK (transcript_text_codec_version IS NULL OR transcript_text_codec_version = 1) NOT VALID;
|
|
131
|
+
|
|
132
|
+
-- During the rolling window, an old application can update a protected value
|
|
133
|
+
-- on a row whose companion is already 1 while omitting the unknown companion
|
|
134
|
+
-- column. Preserve unrelated updates, but reset codec truth to literal NULL
|
|
135
|
+
-- when such an identified old writer changes protected content without also
|
|
136
|
+
-- changing its companion. New createDb connections carry a distinct startup
|
|
137
|
+
-- application_name; supported injected/embedded handles set the transaction-
|
|
138
|
+
-- local writer GUC from the shared RLS context hook.
|
|
139
|
+
CREATE OR REPLACE FUNCTION opengeni_private.fence_legacy_lossless_content_update()
|
|
140
|
+
RETURNS trigger
|
|
141
|
+
LANGUAGE plpgsql
|
|
142
|
+
SET search_path = pg_catalog
|
|
143
|
+
AS $function$
|
|
144
|
+
BEGIN
|
|
145
|
+
IF current_setting('opengeni.lossless_content_writer', true) = '1'
|
|
146
|
+
OR current_setting('application_name', true) = 'opengeni-lossless-v1' THEN
|
|
147
|
+
RETURN NEW;
|
|
148
|
+
END IF;
|
|
149
|
+
|
|
150
|
+
CASE TG_TABLE_NAME
|
|
151
|
+
WHEN 'sessions' THEN
|
|
152
|
+
IF NEW.initial_message IS DISTINCT FROM OLD.initial_message
|
|
153
|
+
AND NEW.initial_message_codec_version IS NOT DISTINCT FROM OLD.initial_message_codec_version THEN
|
|
154
|
+
NEW.initial_message_codec_version := NULL;
|
|
155
|
+
END IF;
|
|
156
|
+
WHEN 'session_realtime_entries' THEN
|
|
157
|
+
IF NEW.text IS DISTINCT FROM OLD.text
|
|
158
|
+
AND NEW.text_codec_version IS NOT DISTINCT FROM OLD.text_codec_version THEN
|
|
159
|
+
NEW.text_codec_version := NULL;
|
|
160
|
+
END IF;
|
|
161
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
162
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
163
|
+
NEW.payload_codec_version := NULL;
|
|
164
|
+
END IF;
|
|
165
|
+
WHEN 'knowledge_memories' THEN
|
|
166
|
+
IF NEW.text IS DISTINCT FROM OLD.text
|
|
167
|
+
AND NEW.text_codec_version IS NOT DISTINCT FROM OLD.text_codec_version THEN
|
|
168
|
+
NEW.text_codec_version := NULL;
|
|
169
|
+
END IF;
|
|
170
|
+
WHEN 'session_turns' THEN
|
|
171
|
+
IF NEW.prompt IS DISTINCT FROM OLD.prompt
|
|
172
|
+
AND NEW.prompt_codec_version IS NOT DISTINCT FROM OLD.prompt_codec_version THEN
|
|
173
|
+
NEW.prompt_codec_version := NULL;
|
|
174
|
+
END IF;
|
|
175
|
+
WHEN 'session_system_updates' THEN
|
|
176
|
+
IF NEW.summary IS DISTINCT FROM OLD.summary
|
|
177
|
+
AND NEW.summary_codec_version IS NOT DISTINCT FROM OLD.summary_codec_version THEN
|
|
178
|
+
NEW.summary_codec_version := NULL;
|
|
179
|
+
END IF;
|
|
180
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
181
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
182
|
+
NEW.payload_codec_version := NULL;
|
|
183
|
+
END IF;
|
|
184
|
+
WHEN 'session_system_update_outbox' THEN
|
|
185
|
+
IF NEW.summary IS DISTINCT FROM OLD.summary
|
|
186
|
+
AND NEW.summary_codec_version IS NOT DISTINCT FROM OLD.summary_codec_version THEN
|
|
187
|
+
NEW.summary_codec_version := NULL;
|
|
188
|
+
END IF;
|
|
189
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
190
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
191
|
+
NEW.payload_codec_version := NULL;
|
|
192
|
+
END IF;
|
|
193
|
+
WHEN 'session_events' THEN
|
|
194
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
195
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
196
|
+
NEW.payload_codec_version := NULL;
|
|
197
|
+
END IF;
|
|
198
|
+
WHEN 'host_export_outbox' THEN
|
|
199
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
200
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
201
|
+
NEW.payload_codec_version := NULL;
|
|
202
|
+
END IF;
|
|
203
|
+
WHEN 'host_export_consumers' THEN
|
|
204
|
+
IF NEW.last_error IS DISTINCT FROM OLD.last_error
|
|
205
|
+
AND NEW.last_error_codec_version IS NOT DISTINCT FROM OLD.last_error_codec_version THEN
|
|
206
|
+
NEW.last_error_codec_version := NULL;
|
|
207
|
+
END IF;
|
|
208
|
+
WHEN 'agent_run_states' THEN
|
|
209
|
+
IF NEW.serialized_run_state IS DISTINCT FROM OLD.serialized_run_state
|
|
210
|
+
AND NEW.serialized_run_state_codec_version IS NOT DISTINCT FROM OLD.serialized_run_state_codec_version THEN
|
|
211
|
+
NEW.serialized_run_state_codec_version := NULL;
|
|
212
|
+
END IF;
|
|
213
|
+
IF NEW.pending_approvals IS DISTINCT FROM OLD.pending_approvals
|
|
214
|
+
AND NEW.pending_approvals_codec_version IS NOT DISTINCT FROM OLD.pending_approvals_codec_version THEN
|
|
215
|
+
NEW.pending_approvals_codec_version := NULL;
|
|
216
|
+
END IF;
|
|
217
|
+
WHEN 'session_history_items' THEN
|
|
218
|
+
IF NEW.item IS DISTINCT FROM OLD.item
|
|
219
|
+
AND NEW.item_codec_version IS NOT DISTINCT FROM OLD.item_codec_version THEN
|
|
220
|
+
NEW.item_codec_version := NULL;
|
|
221
|
+
END IF;
|
|
222
|
+
WHEN 'session_pending_tool_calls' THEN
|
|
223
|
+
IF NEW.call_item IS DISTINCT FROM OLD.call_item
|
|
224
|
+
AND NEW.call_item_codec_version IS NOT DISTINCT FROM OLD.call_item_codec_version THEN
|
|
225
|
+
NEW.call_item_codec_version := NULL;
|
|
226
|
+
END IF;
|
|
227
|
+
IF NEW.result_item IS DISTINCT FROM OLD.result_item
|
|
228
|
+
AND NEW.result_item_codec_version IS NOT DISTINCT FROM OLD.result_item_codec_version THEN
|
|
229
|
+
NEW.result_item_codec_version := NULL;
|
|
230
|
+
END IF;
|
|
231
|
+
WHEN 'sandbox_session_envelopes' THEN
|
|
232
|
+
IF NEW.envelope IS DISTINCT FROM OLD.envelope
|
|
233
|
+
AND NEW.envelope_codec_version IS NOT DISTINCT FROM OLD.envelope_codec_version THEN
|
|
234
|
+
NEW.envelope_codec_version := NULL;
|
|
235
|
+
END IF;
|
|
236
|
+
WHEN 'session_recordings' THEN
|
|
237
|
+
IF NEW.reason IS DISTINCT FROM OLD.reason
|
|
238
|
+
AND NEW.reason_codec_version IS NOT DISTINCT FROM OLD.reason_codec_version THEN
|
|
239
|
+
NEW.reason_codec_version := NULL;
|
|
240
|
+
END IF;
|
|
241
|
+
WHEN 'host_export_dead_letters' THEN
|
|
242
|
+
IF NEW.envelope IS DISTINCT FROM OLD.envelope
|
|
243
|
+
AND NEW.envelope_codec_version IS NOT DISTINCT FROM OLD.envelope_codec_version THEN
|
|
244
|
+
NEW.envelope_codec_version := NULL;
|
|
245
|
+
END IF;
|
|
246
|
+
IF NEW.envelope #> '{event,payload}' IS DISTINCT FROM OLD.envelope #> '{event,payload}'
|
|
247
|
+
AND NEW.event_payload_codec_version IS NOT DISTINCT FROM OLD.event_payload_codec_version THEN
|
|
248
|
+
NEW.event_payload_codec_version := NULL;
|
|
249
|
+
END IF;
|
|
250
|
+
WHEN 'audit_events' THEN
|
|
251
|
+
IF NEW.metadata IS DISTINCT FROM OLD.metadata
|
|
252
|
+
AND NEW.metadata_codec_version IS NOT DISTINCT FROM OLD.metadata_codec_version THEN
|
|
253
|
+
NEW.metadata_codec_version := NULL;
|
|
254
|
+
END IF;
|
|
255
|
+
WHEN 'rig_changes' THEN
|
|
256
|
+
IF NEW.payload IS DISTINCT FROM OLD.payload
|
|
257
|
+
AND NEW.payload_codec_version IS NOT DISTINCT FROM OLD.payload_codec_version THEN
|
|
258
|
+
NEW.payload_codec_version := NULL;
|
|
259
|
+
END IF;
|
|
260
|
+
IF NEW.verification IS DISTINCT FROM OLD.verification
|
|
261
|
+
AND NEW.verification_codec_version IS NOT DISTINCT FROM OLD.verification_codec_version THEN
|
|
262
|
+
NEW.verification_codec_version := NULL;
|
|
263
|
+
END IF;
|
|
264
|
+
WHEN 'transcription_recordings' THEN
|
|
265
|
+
IF NEW.transcript_text IS DISTINCT FROM OLD.transcript_text
|
|
266
|
+
AND NEW.transcript_text_codec_version IS NOT DISTINCT FROM OLD.transcript_text_codec_version THEN
|
|
267
|
+
NEW.transcript_text_codec_version := NULL;
|
|
268
|
+
END IF;
|
|
269
|
+
WHEN 'transcription_recording_segments' THEN
|
|
270
|
+
IF NEW.transcript_text IS DISTINCT FROM OLD.transcript_text
|
|
271
|
+
AND NEW.transcript_text_codec_version IS NOT DISTINCT FROM OLD.transcript_text_codec_version THEN
|
|
272
|
+
NEW.transcript_text_codec_version := NULL;
|
|
273
|
+
END IF;
|
|
274
|
+
ELSE
|
|
275
|
+
RAISE EXCEPTION 'unsupported lossless-content fence table: %', TG_TABLE_NAME;
|
|
276
|
+
END CASE;
|
|
277
|
+
RETURN NEW;
|
|
278
|
+
END
|
|
279
|
+
$function$;
|
|
280
|
+
|
|
281
|
+
DROP TRIGGER IF EXISTS sessions_lossless_legacy_update_fence ON sessions;
|
|
282
|
+
CREATE TRIGGER sessions_lossless_legacy_update_fence
|
|
283
|
+
BEFORE UPDATE OF initial_message ON sessions
|
|
284
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
285
|
+
DROP TRIGGER IF EXISTS session_realtime_entries_lossless_legacy_update_fence ON session_realtime_entries;
|
|
286
|
+
CREATE TRIGGER session_realtime_entries_lossless_legacy_update_fence
|
|
287
|
+
BEFORE UPDATE OF text, payload ON session_realtime_entries
|
|
288
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
289
|
+
DROP TRIGGER IF EXISTS knowledge_memories_lossless_legacy_update_fence ON knowledge_memories;
|
|
290
|
+
CREATE TRIGGER knowledge_memories_lossless_legacy_update_fence
|
|
291
|
+
BEFORE UPDATE OF text ON knowledge_memories
|
|
292
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
293
|
+
DROP TRIGGER IF EXISTS session_turns_lossless_legacy_update_fence ON session_turns;
|
|
294
|
+
CREATE TRIGGER session_turns_lossless_legacy_update_fence
|
|
295
|
+
BEFORE UPDATE OF prompt ON session_turns
|
|
296
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
297
|
+
DROP TRIGGER IF EXISTS session_system_updates_lossless_legacy_update_fence ON session_system_updates;
|
|
298
|
+
CREATE TRIGGER session_system_updates_lossless_legacy_update_fence
|
|
299
|
+
BEFORE UPDATE OF summary, payload ON session_system_updates
|
|
300
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
301
|
+
DROP TRIGGER IF EXISTS session_system_update_outbox_lossless_legacy_update_fence ON session_system_update_outbox;
|
|
302
|
+
CREATE TRIGGER session_system_update_outbox_lossless_legacy_update_fence
|
|
303
|
+
BEFORE UPDATE OF summary, payload ON session_system_update_outbox
|
|
304
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
305
|
+
DROP TRIGGER IF EXISTS session_events_lossless_legacy_update_fence ON session_events;
|
|
306
|
+
CREATE TRIGGER session_events_lossless_legacy_update_fence
|
|
307
|
+
BEFORE UPDATE OF payload ON session_events
|
|
308
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
309
|
+
DROP TRIGGER IF EXISTS host_export_outbox_lossless_legacy_update_fence ON host_export_outbox;
|
|
310
|
+
CREATE TRIGGER host_export_outbox_lossless_legacy_update_fence
|
|
311
|
+
BEFORE UPDATE OF payload ON host_export_outbox
|
|
312
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
313
|
+
DROP TRIGGER IF EXISTS host_export_consumers_lossless_legacy_update_fence ON host_export_consumers;
|
|
314
|
+
CREATE TRIGGER host_export_consumers_lossless_legacy_update_fence
|
|
315
|
+
BEFORE UPDATE OF last_error ON host_export_consumers
|
|
316
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
317
|
+
DROP TRIGGER IF EXISTS agent_run_states_lossless_legacy_update_fence ON agent_run_states;
|
|
318
|
+
CREATE TRIGGER agent_run_states_lossless_legacy_update_fence
|
|
319
|
+
BEFORE UPDATE OF serialized_run_state, pending_approvals ON agent_run_states
|
|
320
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
321
|
+
DROP TRIGGER IF EXISTS session_history_items_lossless_legacy_update_fence ON session_history_items;
|
|
322
|
+
CREATE TRIGGER session_history_items_lossless_legacy_update_fence
|
|
323
|
+
BEFORE UPDATE OF item ON session_history_items
|
|
324
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
325
|
+
DROP TRIGGER IF EXISTS session_pending_tool_calls_lossless_legacy_update_fence ON session_pending_tool_calls;
|
|
326
|
+
CREATE TRIGGER session_pending_tool_calls_lossless_legacy_update_fence
|
|
327
|
+
BEFORE UPDATE OF call_item, result_item ON session_pending_tool_calls
|
|
328
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
329
|
+
DROP TRIGGER IF EXISTS sandbox_session_envelopes_lossless_legacy_update_fence ON sandbox_session_envelopes;
|
|
330
|
+
CREATE TRIGGER sandbox_session_envelopes_lossless_legacy_update_fence
|
|
331
|
+
BEFORE UPDATE OF envelope ON sandbox_session_envelopes
|
|
332
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
333
|
+
DROP TRIGGER IF EXISTS session_recordings_lossless_legacy_update_fence ON session_recordings;
|
|
334
|
+
CREATE TRIGGER session_recordings_lossless_legacy_update_fence
|
|
335
|
+
BEFORE UPDATE OF reason ON session_recordings
|
|
336
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
337
|
+
DROP TRIGGER IF EXISTS host_export_dead_letters_lossless_legacy_update_fence ON host_export_dead_letters;
|
|
338
|
+
CREATE TRIGGER host_export_dead_letters_lossless_legacy_update_fence
|
|
339
|
+
BEFORE UPDATE OF envelope ON host_export_dead_letters
|
|
340
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
341
|
+
DROP TRIGGER IF EXISTS audit_events_lossless_legacy_update_fence ON audit_events;
|
|
342
|
+
CREATE TRIGGER audit_events_lossless_legacy_update_fence
|
|
343
|
+
BEFORE UPDATE OF metadata ON audit_events
|
|
344
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
345
|
+
DROP TRIGGER IF EXISTS rig_changes_lossless_legacy_update_fence ON rig_changes;
|
|
346
|
+
CREATE TRIGGER rig_changes_lossless_legacy_update_fence
|
|
347
|
+
BEFORE UPDATE OF payload, verification ON rig_changes
|
|
348
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
349
|
+
DROP TRIGGER IF EXISTS transcription_recordings_lossless_legacy_update_fence ON transcription_recordings;
|
|
350
|
+
CREATE TRIGGER transcription_recordings_lossless_legacy_update_fence
|
|
351
|
+
BEFORE UPDATE OF transcript_text ON transcription_recordings
|
|
352
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
353
|
+
DROP TRIGGER IF EXISTS transcription_recording_segments_lossless_legacy_update_fence ON transcription_recording_segments;
|
|
354
|
+
CREATE TRIGGER transcription_recording_segments_lossless_legacy_update_fence
|
|
355
|
+
BEFORE UPDATE OF transcript_text ON transcription_recording_segments
|
|
356
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.fence_legacy_lossless_content_update();
|
|
357
|
+
|
|
358
|
+
REVOKE ALL ON FUNCTION opengeni_private.fence_legacy_lossless_content_update() FROM PUBLIC;
|
|
359
|
+
|
|
360
|
+
-- The claim function has an explicit return shape, so carry the out-of-band
|
|
361
|
+
-- versions with the claimed canonical fields.
|
|
362
|
+
DROP FUNCTION opengeni_private.claim_session_system_update_outbox(integer);
|
|
363
|
+
CREATE FUNCTION opengeni_private.claim_session_system_update_outbox(p_limit integer)
|
|
364
|
+
RETURNS TABLE (
|
|
365
|
+
id uuid, account_id uuid, workspace_id uuid, source_session_id uuid,
|
|
366
|
+
target_session_id uuid, dedupe_key text, kind text, classification text,
|
|
367
|
+
source_id text, summary text, summary_codec_version integer,
|
|
368
|
+
payload jsonb, payload_codec_version integer, lineage jsonb,
|
|
369
|
+
personal_connection_delegations jsonb
|
|
370
|
+
)
|
|
371
|
+
LANGUAGE plpgsql
|
|
372
|
+
SECURITY DEFINER
|
|
373
|
+
AS $function$
|
|
374
|
+
BEGIN
|
|
375
|
+
RETURN QUERY
|
|
376
|
+
WITH claimed AS (
|
|
377
|
+
SELECT o.id FROM session_system_update_outbox o
|
|
378
|
+
WHERE o.status = 'pending'
|
|
379
|
+
ORDER BY o.created_at, o.id
|
|
380
|
+
FOR UPDATE SKIP LOCKED
|
|
381
|
+
LIMIT greatest(1, least(coalesce(p_limit, 100), 100))
|
|
382
|
+
)
|
|
383
|
+
UPDATE session_system_update_outbox o
|
|
384
|
+
SET attempts = o.attempts + 1, updated_at = now()
|
|
385
|
+
FROM claimed c WHERE o.id = c.id
|
|
386
|
+
RETURNING o.id, o.account_id, o.workspace_id, o.source_session_id,
|
|
387
|
+
o.target_session_id, o.dedupe_key, o.kind, o.classification,
|
|
388
|
+
o.source_id, o.summary, o.summary_codec_version,
|
|
389
|
+
o.payload, o.payload_codec_version, o.lineage,
|
|
390
|
+
o.personal_connection_delegations;
|
|
391
|
+
END
|
|
392
|
+
$function$;
|
|
393
|
+
REVOKE ALL ON FUNCTION opengeni_private.claim_session_system_update_outbox(integer) FROM PUBLIC;
|
|
394
|
+
DO $block$
|
|
395
|
+
BEGIN
|
|
396
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
397
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.claim_session_system_update_outbox(integer)
|
|
398
|
+
TO opengeni_app;
|
|
399
|
+
END IF;
|
|
400
|
+
END
|
|
401
|
+
$block$;
|
|
402
|
+
|
|
403
|
+
-- Keep the published claim_host_export_batch signature and ACL stable. The
|
|
404
|
+
-- deferred producer writes the session-event storage payload and its out-of-
|
|
405
|
+
-- band truth atomically; the application reads immutable sidecars under the
|
|
406
|
+
-- exact current lease in the same transaction as the existing claim call.
|
|
407
|
+
DO $migration$
|
|
408
|
+
DECLARE target_schema text := current_schema();
|
|
409
|
+
BEGIN
|
|
410
|
+
EXECUTE format($create$
|
|
411
|
+
CREATE OR REPLACE FUNCTION opengeni_private.enqueue_host_session_event_export()
|
|
412
|
+
RETURNS trigger
|
|
413
|
+
LANGUAGE plpgsql
|
|
414
|
+
SECURITY DEFINER
|
|
415
|
+
SET search_path = pg_catalog
|
|
416
|
+
AS $function$
|
|
417
|
+
DECLARE
|
|
418
|
+
v_enabled boolean;
|
|
419
|
+
v_initiator jsonb;
|
|
420
|
+
v_context jsonb := '{}'::jsonb;
|
|
421
|
+
v_origin text;
|
|
422
|
+
v_payload_bytes integer;
|
|
423
|
+
BEGIN
|
|
424
|
+
IF NEW.type IN (
|
|
425
|
+
'agent.message.delta', 'agent.reasoning.delta',
|
|
426
|
+
'sandbox.command.output.delta', 'terminal.pty.output.delta'
|
|
427
|
+
) THEN
|
|
428
|
+
RETURN NEW;
|
|
429
|
+
END IF;
|
|
430
|
+
|
|
431
|
+
SELECT c.session_events_enabled INTO v_enabled
|
|
432
|
+
FROM %1$I.host_export_config c WHERE c.id = 1
|
|
433
|
+
FOR SHARE;
|
|
434
|
+
IF coalesce(v_enabled, false) = false THEN
|
|
435
|
+
RETURN NEW;
|
|
436
|
+
END IF;
|
|
437
|
+
|
|
438
|
+
IF NEW.turn_id IS NOT NULL THEN
|
|
439
|
+
SELECT
|
|
440
|
+
CASE WHEN octet_length(t.initiator_subject_id) <= 1024 THEN
|
|
441
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
442
|
+
'kind', t.initiator_kind,
|
|
443
|
+
'subjectId', t.initiator_subject_id,
|
|
444
|
+
'label', CASE
|
|
445
|
+
WHEN jsonb_typeof(t.initiator_context -> 'label') = 'string'
|
|
446
|
+
THEN left(t.initiator_context ->> 'label', 256)
|
|
447
|
+
ELSE NULL
|
|
448
|
+
END
|
|
449
|
+
))
|
|
450
|
+
ELSE NULL END,
|
|
451
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
452
|
+
'label', CASE
|
|
453
|
+
WHEN jsonb_typeof(t.initiator_context -> 'label') = 'string'
|
|
454
|
+
THEN left(t.initiator_context ->> 'label', 256)
|
|
455
|
+
ELSE NULL
|
|
456
|
+
END,
|
|
457
|
+
'backfill', CASE
|
|
458
|
+
WHEN jsonb_typeof(t.initiator_context -> 'backfill') = 'boolean'
|
|
459
|
+
THEN t.initiator_context -> 'backfill'
|
|
460
|
+
ELSE NULL
|
|
461
|
+
END,
|
|
462
|
+
'attributionOmitted', CASE
|
|
463
|
+
WHEN octet_length(t.initiator_subject_id) > 1024 THEN 'subject_id_too_large'
|
|
464
|
+
ELSE NULL
|
|
465
|
+
END
|
|
466
|
+
)),
|
|
467
|
+
t.source
|
|
468
|
+
INTO v_initiator, v_context, v_origin
|
|
469
|
+
FROM %1$I.session_turns t
|
|
470
|
+
WHERE t.workspace_id = NEW.workspace_id AND t.id = NEW.turn_id;
|
|
471
|
+
ELSIF NEW.type = 'session.created' THEN
|
|
472
|
+
SELECT
|
|
473
|
+
CASE WHEN octet_length(s.created_by_subject_id) <= 1024 THEN
|
|
474
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
475
|
+
'kind', s.created_by_kind,
|
|
476
|
+
'subjectId', s.created_by_subject_id,
|
|
477
|
+
'label', CASE
|
|
478
|
+
WHEN jsonb_typeof(s.created_by_context -> 'label') = 'string'
|
|
479
|
+
THEN left(s.created_by_context ->> 'label', 256)
|
|
480
|
+
ELSE NULL
|
|
481
|
+
END
|
|
482
|
+
))
|
|
483
|
+
ELSE NULL END,
|
|
484
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
485
|
+
'label', CASE
|
|
486
|
+
WHEN jsonb_typeof(s.created_by_context -> 'label') = 'string'
|
|
487
|
+
THEN left(s.created_by_context ->> 'label', 256)
|
|
488
|
+
ELSE NULL
|
|
489
|
+
END,
|
|
490
|
+
'backfill', CASE
|
|
491
|
+
WHEN jsonb_typeof(s.created_by_context -> 'backfill') = 'boolean'
|
|
492
|
+
THEN s.created_by_context -> 'backfill'
|
|
493
|
+
ELSE NULL
|
|
494
|
+
END,
|
|
495
|
+
'attributionOmitted', CASE
|
|
496
|
+
WHEN octet_length(s.created_by_subject_id) > 1024 THEN 'subject_id_too_large'
|
|
497
|
+
ELSE NULL
|
|
498
|
+
END
|
|
499
|
+
)),
|
|
500
|
+
NULL
|
|
501
|
+
INTO v_initiator, v_context, v_origin
|
|
502
|
+
FROM %1$I.sessions s
|
|
503
|
+
WHERE s.workspace_id = NEW.workspace_id AND s.id = NEW.session_id;
|
|
504
|
+
END IF;
|
|
505
|
+
|
|
506
|
+
v_payload_bytes := octet_length(NEW.payload::text)
|
|
507
|
+
+ octet_length(NEW.type)
|
|
508
|
+
+ coalesce(octet_length(NEW.client_event_id), 0)
|
|
509
|
+
+ coalesce(octet_length(NEW.turn_association), 0)
|
|
510
|
+
+ coalesce(octet_length(NEW.duplicate_reason), 0)
|
|
511
|
+
+ octet_length(coalesce(v_initiator, 'null'::jsonb)::text)
|
|
512
|
+
+ octet_length(v_context::text)
|
|
513
|
+
+ 768;
|
|
514
|
+
INSERT INTO %1$I.host_export_outbox (
|
|
515
|
+
export_kind, source_id, account_id, workspace_id, session_id,
|
|
516
|
+
turn_id, turn_generation, turn_attempt_id, session_sequence,
|
|
517
|
+
client_event_id, turn_association, duplicate_of_event_id, duplicate_reason,
|
|
518
|
+
event_type, idempotency_key, initiator, initiator_context, origin,
|
|
519
|
+
payload, payload_codec_version, envelope_bytes, occurred_at,
|
|
520
|
+
source_recorded_at, enqueued_at
|
|
521
|
+
) VALUES (
|
|
522
|
+
'session_event', NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id,
|
|
523
|
+
NEW.turn_id, NEW.turn_generation, NEW.turn_attempt_id, NEW.sequence,
|
|
524
|
+
NEW.client_event_id, NEW.turn_association, NEW.duplicate_of_event_id,
|
|
525
|
+
NEW.duplicate_reason,
|
|
526
|
+
NEW.type, 'session_event:' || NEW.id::text, v_initiator,
|
|
527
|
+
coalesce(v_context, '{}'::jsonb), v_origin, NEW.payload,
|
|
528
|
+
NEW.payload_codec_version, greatest(1, v_payload_bytes), NEW.occurred_at,
|
|
529
|
+
NEW.created_at, clock_timestamp()
|
|
530
|
+
)
|
|
531
|
+
ON CONFLICT (export_kind, source_id) DO NOTHING;
|
|
532
|
+
RETURN NEW;
|
|
533
|
+
END $function$;
|
|
534
|
+
$create$, target_schema);
|
|
535
|
+
|
|
536
|
+
EXECUTE format($create$
|
|
537
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.ack_host_export_batch(
|
|
538
|
+
p_export_kind text, p_consumer_id text, p_lease_token uuid
|
|
539
|
+
) RETURNS bigint
|
|
540
|
+
LANGUAGE plpgsql
|
|
541
|
+
SECURITY DEFINER
|
|
542
|
+
SET search_path = pg_catalog
|
|
543
|
+
AS $function$
|
|
544
|
+
DECLARE v_checkpoint bigint;
|
|
545
|
+
BEGIN
|
|
546
|
+
UPDATE %1$I.host_export_consumers c
|
|
547
|
+
SET checkpoint = c.lease_through,
|
|
548
|
+
lease_token = NULL, lease_holder_id = NULL, lease_expires_at = NULL,
|
|
549
|
+
lease_from = NULL, lease_through = NULL,
|
|
550
|
+
consecutive_failures = 0, next_attempt_at = now(),
|
|
551
|
+
last_error = NULL, last_error_codec_version = NULL,
|
|
552
|
+
last_error_at = NULL, updated_at = now()
|
|
553
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id
|
|
554
|
+
AND c.lease_token = p_lease_token AND c.lease_through IS NOT NULL
|
|
555
|
+
RETURNING c.checkpoint INTO v_checkpoint;
|
|
556
|
+
IF v_checkpoint IS NULL THEN
|
|
557
|
+
RAISE EXCEPTION 'host export lease is stale' USING ERRCODE = '40001';
|
|
558
|
+
END IF;
|
|
559
|
+
RETURN v_checkpoint;
|
|
560
|
+
END $function$;
|
|
561
|
+
$create$, target_schema);
|
|
562
|
+
|
|
563
|
+
-- Keep the published five-argument function as the rolling old-writer
|
|
564
|
+
-- protocol. Its input is literal PostgreSQL text, so its companion remains
|
|
565
|
+
-- NULL even when the content happens to match the active codec marker.
|
|
566
|
+
EXECUTE format($create$
|
|
567
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.fail_host_export_batch(
|
|
568
|
+
p_export_kind text, p_consumer_id text, p_lease_token uuid,
|
|
569
|
+
p_error text, p_max_failures integer
|
|
570
|
+
) RETURNS integer
|
|
571
|
+
LANGUAGE plpgsql
|
|
572
|
+
SECURITY DEFINER
|
|
573
|
+
SET search_path = pg_catalog
|
|
574
|
+
AS $function$
|
|
575
|
+
DECLARE v_failures integer;
|
|
576
|
+
BEGIN
|
|
577
|
+
UPDATE %1$I.host_export_consumers c
|
|
578
|
+
SET consecutive_failures = c.consecutive_failures + 1,
|
|
579
|
+
lease_token = NULL, lease_holder_id = NULL, lease_expires_at = NULL,
|
|
580
|
+
lease_from = NULL, lease_through = NULL,
|
|
581
|
+
next_attempt_at = now() + make_interval(
|
|
582
|
+
secs => least(300, greatest(1, power(2, least(c.consecutive_failures, 8))::integer))
|
|
583
|
+
),
|
|
584
|
+
last_error = coalesce(p_error, 'host sink failed'),
|
|
585
|
+
last_error_codec_version = NULL,
|
|
586
|
+
last_error_at = now(),
|
|
587
|
+
blocked_at = CASE
|
|
588
|
+
WHEN c.consecutive_failures + 1 >= greatest(1, least(coalesce(p_max_failures, 20), 1000))
|
|
589
|
+
THEN now() ELSE NULL END,
|
|
590
|
+
updated_at = now()
|
|
591
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id
|
|
592
|
+
AND c.lease_token = p_lease_token
|
|
593
|
+
RETURNING c.consecutive_failures INTO v_failures;
|
|
594
|
+
IF v_failures IS NULL THEN
|
|
595
|
+
RAISE EXCEPTION 'host export lease is stale' USING ERRCODE = '40001';
|
|
596
|
+
END IF;
|
|
597
|
+
RETURN v_failures;
|
|
598
|
+
END $function$;
|
|
599
|
+
$create$, target_schema);
|
|
600
|
+
|
|
601
|
+
-- New writers encode the entire error in application code and stamp the
|
|
602
|
+
-- companion in this same SQL statement. PostgreSQL never implements or
|
|
603
|
+
-- infers the JavaScript lossless-text codec.
|
|
604
|
+
EXECUTE format($create$
|
|
605
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.fail_host_export_batch(
|
|
606
|
+
p_export_kind text, p_consumer_id text, p_lease_token uuid,
|
|
607
|
+
p_error text, p_max_failures integer, p_error_codec_version integer
|
|
608
|
+
) RETURNS integer
|
|
609
|
+
LANGUAGE plpgsql
|
|
610
|
+
SECURITY DEFINER
|
|
611
|
+
SET search_path = pg_catalog
|
|
612
|
+
AS $function$
|
|
613
|
+
DECLARE v_failures integer;
|
|
614
|
+
BEGIN
|
|
615
|
+
IF p_error_codec_version IS DISTINCT FROM 1 THEN
|
|
616
|
+
RAISE EXCEPTION 'host export error codec version must be 1'
|
|
617
|
+
USING ERRCODE = '22023';
|
|
618
|
+
END IF;
|
|
619
|
+
UPDATE %1$I.host_export_consumers c
|
|
620
|
+
SET consecutive_failures = c.consecutive_failures + 1,
|
|
621
|
+
lease_token = NULL, lease_holder_id = NULL, lease_expires_at = NULL,
|
|
622
|
+
lease_from = NULL, lease_through = NULL,
|
|
623
|
+
next_attempt_at = now() + make_interval(
|
|
624
|
+
secs => least(300, greatest(1, power(2, least(c.consecutive_failures, 8))::integer))
|
|
625
|
+
),
|
|
626
|
+
last_error = coalesce(p_error, 'host sink failed'),
|
|
627
|
+
last_error_codec_version = p_error_codec_version,
|
|
628
|
+
last_error_at = now(),
|
|
629
|
+
blocked_at = CASE
|
|
630
|
+
WHEN c.consecutive_failures + 1 >= greatest(1, least(coalesce(p_max_failures, 20), 1000))
|
|
631
|
+
THEN now() ELSE NULL END,
|
|
632
|
+
updated_at = now()
|
|
633
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id
|
|
634
|
+
AND c.lease_token = p_lease_token
|
|
635
|
+
RETURNING c.consecutive_failures INTO v_failures;
|
|
636
|
+
IF v_failures IS NULL THEN
|
|
637
|
+
RAISE EXCEPTION 'host export lease is stale' USING ERRCODE = '40001';
|
|
638
|
+
END IF;
|
|
639
|
+
RETURN v_failures;
|
|
640
|
+
END $function$;
|
|
641
|
+
$create$, target_schema);
|
|
642
|
+
|
|
643
|
+
EXECUTE format($create$
|
|
644
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.resume_host_export_consumer(
|
|
645
|
+
p_export_kind text, p_consumer_id text
|
|
646
|
+
) RETURNS void
|
|
647
|
+
LANGUAGE plpgsql
|
|
648
|
+
SECURITY DEFINER
|
|
649
|
+
SET search_path = pg_catalog
|
|
650
|
+
AS $function$
|
|
651
|
+
BEGIN
|
|
652
|
+
UPDATE %1$I.host_export_consumers c
|
|
653
|
+
SET blocked_at = NULL, consecutive_failures = 0, next_attempt_at = now(),
|
|
654
|
+
lease_token = NULL, lease_holder_id = NULL, lease_expires_at = NULL,
|
|
655
|
+
lease_from = NULL, lease_through = NULL,
|
|
656
|
+
last_error = NULL, last_error_codec_version = NULL,
|
|
657
|
+
last_error_at = NULL, updated_at = now()
|
|
658
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id;
|
|
659
|
+
IF NOT FOUND THEN
|
|
660
|
+
RAISE EXCEPTION 'host export consumer not found' USING ERRCODE = 'P0002';
|
|
661
|
+
END IF;
|
|
662
|
+
END $function$;
|
|
663
|
+
$create$, target_schema);
|
|
664
|
+
|
|
665
|
+
EXECUTE format($create$
|
|
666
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.host_export_claim_sidecars(
|
|
667
|
+
p_export_kind text,
|
|
668
|
+
p_consumer_id text,
|
|
669
|
+
p_lease_token uuid
|
|
670
|
+
) RETURNS TABLE (
|
|
671
|
+
export_cursor bigint,
|
|
672
|
+
root_session_id uuid,
|
|
673
|
+
payload_codec_version integer
|
|
674
|
+
)
|
|
675
|
+
LANGUAGE plpgsql
|
|
676
|
+
SECURITY DEFINER
|
|
677
|
+
SET search_path = pg_catalog
|
|
678
|
+
AS $function$
|
|
679
|
+
DECLARE
|
|
680
|
+
v_consumer %1$I.host_export_consumers%%ROWTYPE;
|
|
681
|
+
BEGIN
|
|
682
|
+
SELECT * INTO v_consumer
|
|
683
|
+
FROM %1$I.host_export_consumers c
|
|
684
|
+
WHERE c.export_kind = p_export_kind
|
|
685
|
+
AND c.consumer_id = p_consumer_id
|
|
686
|
+
FOR UPDATE;
|
|
687
|
+
IF NOT FOUND
|
|
688
|
+
OR v_consumer.lease_token IS DISTINCT FROM p_lease_token
|
|
689
|
+
OR v_consumer.lease_from IS NULL
|
|
690
|
+
OR v_consumer.lease_through IS NULL
|
|
691
|
+
OR v_consumer.lease_expires_at IS NULL
|
|
692
|
+
OR v_consumer.lease_expires_at <= now() THEN
|
|
693
|
+
RAISE EXCEPTION 'host export lease is not current'
|
|
694
|
+
USING ERRCODE = '55000';
|
|
695
|
+
END IF;
|
|
696
|
+
|
|
697
|
+
RETURN QUERY
|
|
698
|
+
SELECT o.export_cursor, o.root_session_id, o.payload_codec_version
|
|
699
|
+
FROM %1$I.host_export_outbox o
|
|
700
|
+
WHERE o.export_kind = p_export_kind
|
|
701
|
+
AND o.export_cursor > v_consumer.lease_from
|
|
702
|
+
AND o.export_cursor <= v_consumer.lease_through
|
|
703
|
+
ORDER BY o.export_cursor;
|
|
704
|
+
END $function$;
|
|
705
|
+
$create$, target_schema);
|
|
706
|
+
|
|
707
|
+
-- Preserve the published host_export_consumer_status return signature. The
|
|
708
|
+
-- application joins this one-column companion helper in the same statement
|
|
709
|
+
-- and decodes the returned last_error only when the sidecar is 1.
|
|
710
|
+
EXECUTE format($create$
|
|
711
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.host_export_consumer_status_sidecar(
|
|
712
|
+
p_export_kind text,
|
|
713
|
+
p_consumer_id text
|
|
714
|
+
) RETURNS TABLE (
|
|
715
|
+
last_error_codec_version integer
|
|
716
|
+
)
|
|
717
|
+
LANGUAGE sql
|
|
718
|
+
SECURITY DEFINER
|
|
719
|
+
SET search_path = pg_catalog
|
|
720
|
+
AS $function$
|
|
721
|
+
SELECT c.last_error_codec_version
|
|
722
|
+
FROM %1$I.host_export_consumers c
|
|
723
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id;
|
|
724
|
+
$function$;
|
|
725
|
+
$create$, target_schema);
|
|
726
|
+
|
|
727
|
+
EXECUTE format($create$
|
|
728
|
+
CREATE OR REPLACE FUNCTION opengeni_host_export.dead_letter_host_export_head(
|
|
729
|
+
p_export_kind text, p_consumer_id text, p_lease_token uuid,
|
|
730
|
+
p_export_cursor bigint, p_reason text
|
|
731
|
+
) RETURNS bigint
|
|
732
|
+
LANGUAGE plpgsql
|
|
733
|
+
SECURITY DEFINER
|
|
734
|
+
SET search_path = pg_catalog
|
|
735
|
+
AS $function$
|
|
736
|
+
DECLARE
|
|
737
|
+
v_consumer %1$I.host_export_consumers%%ROWTYPE;
|
|
738
|
+
v_row %1$I.host_export_outbox%%ROWTYPE;
|
|
739
|
+
v_envelope jsonb;
|
|
740
|
+
BEGIN
|
|
741
|
+
IF p_reason IS NULL OR length(p_reason) NOT BETWEEN 1 AND 500 THEN
|
|
742
|
+
RAISE EXCEPTION 'dead-letter reason must contain 1 to 500 characters'
|
|
743
|
+
USING ERRCODE = '22023';
|
|
744
|
+
END IF;
|
|
745
|
+
SELECT * INTO v_consumer
|
|
746
|
+
FROM %1$I.host_export_consumers c
|
|
747
|
+
WHERE c.export_kind = p_export_kind AND c.consumer_id = p_consumer_id
|
|
748
|
+
FOR UPDATE;
|
|
749
|
+
IF NOT FOUND OR v_consumer.lease_token IS DISTINCT FROM p_lease_token
|
|
750
|
+
OR v_consumer.lease_through IS NULL THEN
|
|
751
|
+
RAISE EXCEPTION 'host export lease is stale' USING ERRCODE = '40001';
|
|
752
|
+
END IF;
|
|
753
|
+
IF p_export_cursor IS NULL OR p_export_cursor <> v_consumer.checkpoint + 1
|
|
754
|
+
OR p_export_cursor > v_consumer.lease_through THEN
|
|
755
|
+
RAISE EXCEPTION 'only the leased head event may be dead-lettered'
|
|
756
|
+
USING ERRCODE = '22023';
|
|
757
|
+
END IF;
|
|
758
|
+
|
|
759
|
+
SELECT * INTO STRICT v_row FROM %1$I.host_export_outbox o
|
|
760
|
+
WHERE o.export_kind = p_export_kind AND o.export_cursor = p_export_cursor;
|
|
761
|
+
IF p_export_kind = 'session_event' THEN
|
|
762
|
+
v_envelope := jsonb_build_object(
|
|
763
|
+
'schemaRevision', '2026-07-host-export-v1',
|
|
764
|
+
'cursor', v_row.export_cursor::text,
|
|
765
|
+
'idempotencyKey', v_row.idempotency_key,
|
|
766
|
+
'accountId', v_row.account_id,
|
|
767
|
+
'workspaceId', v_row.workspace_id,
|
|
768
|
+
'initiator', v_row.initiator,
|
|
769
|
+
'initiatorContext', v_row.initiator_context,
|
|
770
|
+
'origin', v_row.origin,
|
|
771
|
+
'event', jsonb_strip_nulls(jsonb_build_object(
|
|
772
|
+
'id', v_row.source_id,
|
|
773
|
+
'workspaceId', v_row.workspace_id,
|
|
774
|
+
'sessionId', v_row.session_id,
|
|
775
|
+
'sequence', v_row.session_sequence,
|
|
776
|
+
'type', v_row.event_type,
|
|
777
|
+
'payload', v_row.payload,
|
|
778
|
+
'occurredAt', v_row.occurred_at,
|
|
779
|
+
'clientEventId', v_row.client_event_id,
|
|
780
|
+
'turnId', v_row.turn_id,
|
|
781
|
+
'turnGeneration', v_row.turn_generation,
|
|
782
|
+
'turnAttemptId', v_row.turn_attempt_id,
|
|
783
|
+
'turnAssociation', v_row.turn_association,
|
|
784
|
+
'duplicateOfEventId', v_row.duplicate_of_event_id,
|
|
785
|
+
'duplicateReason', v_row.duplicate_reason
|
|
786
|
+
))
|
|
787
|
+
);
|
|
788
|
+
ELSE
|
|
789
|
+
v_envelope := jsonb_build_object(
|
|
790
|
+
'schemaRevision', '2026-07-host-export-v1',
|
|
791
|
+
'cursor', v_row.export_cursor::text,
|
|
792
|
+
'accountId', v_row.account_id,
|
|
793
|
+
'workspaceId', v_row.workspace_id,
|
|
794
|
+
'sessionId', v_row.session_id,
|
|
795
|
+
'turnId', v_row.turn_id,
|
|
796
|
+
'turnAttemptId', v_row.turn_attempt_id,
|
|
797
|
+
'initiator', v_row.initiator,
|
|
798
|
+
'initiatorContext', v_row.initiator_context,
|
|
799
|
+
'origin', v_row.origin,
|
|
800
|
+
'usage', v_row.payload
|
|
801
|
+
);
|
|
802
|
+
END IF;
|
|
803
|
+
|
|
804
|
+
INSERT INTO %1$I.host_export_dead_letters (
|
|
805
|
+
consumer_id, export_kind, export_cursor, source_id, reason, envelope,
|
|
806
|
+
envelope_codec_version, event_payload_codec_version
|
|
807
|
+
) VALUES (
|
|
808
|
+
p_consumer_id, p_export_kind, p_export_cursor, v_row.source_id,
|
|
809
|
+
p_reason, v_envelope, NULL,
|
|
810
|
+
CASE WHEN p_export_kind = 'session_event' THEN v_row.payload_codec_version ELSE NULL END
|
|
811
|
+
) ON CONFLICT (export_kind, consumer_id, export_cursor) DO NOTHING;
|
|
812
|
+
|
|
813
|
+
UPDATE %1$I.host_export_consumers c
|
|
814
|
+
SET (
|
|
815
|
+
checkpoint, lease_token, lease_holder_id, lease_expires_at,
|
|
816
|
+
lease_from, lease_through, consecutive_failures, next_attempt_at,
|
|
817
|
+
last_error, last_error_codec_version, last_error_at, blocked_at, updated_at
|
|
818
|
+
) = (
|
|
819
|
+
p_export_cursor, NULL::uuid, NULL::text, NULL::timestamptz,
|
|
820
|
+
NULL::bigint, NULL::bigint, 0, now(),
|
|
821
|
+
'dead-lettered: ' || p_reason, NULL::integer, now(), NULL::timestamptz, now()
|
|
822
|
+
)
|
|
823
|
+
WHERE c.id = v_consumer.id;
|
|
824
|
+
RETURN p_export_cursor;
|
|
825
|
+
END $function$;
|
|
826
|
+
$create$, target_schema);
|
|
827
|
+
END $migration$;
|
|
828
|
+
|
|
829
|
+
REVOKE ALL ON FUNCTION
|
|
830
|
+
opengeni_private.enqueue_host_session_event_export() FROM PUBLIC;
|
|
831
|
+
REVOKE ALL ON FUNCTION
|
|
832
|
+
opengeni_host_export.fail_host_export_batch(text, text, uuid, text, integer) FROM PUBLIC;
|
|
833
|
+
REVOKE ALL ON FUNCTION
|
|
834
|
+
opengeni_host_export.fail_host_export_batch(text, text, uuid, text, integer, integer) FROM PUBLIC;
|
|
835
|
+
REVOKE ALL ON FUNCTION
|
|
836
|
+
opengeni_host_export.host_export_claim_sidecars(text, text, uuid) FROM PUBLIC;
|
|
837
|
+
REVOKE ALL ON FUNCTION
|
|
838
|
+
opengeni_host_export.host_export_consumer_status_sidecar(text, text) FROM PUBLIC;
|
|
839
|
+
REVOKE ALL ON FUNCTION
|
|
840
|
+
opengeni_host_export.dead_letter_host_export_head(text, text, uuid, bigint, text) FROM PUBLIC;
|
|
841
|
+
|
|
842
|
+
-- Preserve existing host-configured exporter grants for the new sidecar
|
|
843
|
+
-- helper without guessing a role name. The published claim function remains
|
|
844
|
+
-- the ACL authority and is otherwise unchanged.
|
|
845
|
+
DO $migration$
|
|
846
|
+
DECLARE v_role name;
|
|
847
|
+
BEGIN
|
|
848
|
+
FOR v_role IN
|
|
849
|
+
SELECT grantee.rolname
|
|
850
|
+
FROM pg_catalog.pg_proc proc
|
|
851
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
852
|
+
coalesce(proc.proacl, pg_catalog.acldefault('f', proc.proowner))
|
|
853
|
+
) privilege
|
|
854
|
+
JOIN pg_catalog.pg_roles grantee ON grantee.oid = privilege.grantee
|
|
855
|
+
WHERE proc.oid =
|
|
856
|
+
'opengeni_host_export.claim_host_export_batch(text, text, uuid, text, integer, integer, integer)'::regprocedure
|
|
857
|
+
AND privilege.grantee <> proc.proowner
|
|
858
|
+
AND privilege.privilege_type = 'EXECUTE'
|
|
859
|
+
LOOP
|
|
860
|
+
EXECUTE format(
|
|
861
|
+
'GRANT EXECUTE ON FUNCTION opengeni_host_export.host_export_claim_sidecars(text, text, uuid) TO %I',
|
|
862
|
+
v_role
|
|
863
|
+
);
|
|
864
|
+
END LOOP;
|
|
865
|
+
END $migration$;
|
|
866
|
+
|
|
867
|
+
-- Preserve the old failure-settlement ACL on the new codec-aware overload.
|
|
868
|
+
DO $migration$
|
|
869
|
+
DECLARE v_role name;
|
|
870
|
+
BEGIN
|
|
871
|
+
FOR v_role IN
|
|
872
|
+
SELECT grantee.rolname
|
|
873
|
+
FROM pg_catalog.pg_proc proc
|
|
874
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
875
|
+
coalesce(proc.proacl, pg_catalog.acldefault('f', proc.proowner))
|
|
876
|
+
) privilege
|
|
877
|
+
JOIN pg_catalog.pg_roles grantee ON grantee.oid = privilege.grantee
|
|
878
|
+
WHERE proc.oid =
|
|
879
|
+
'opengeni_host_export.fail_host_export_batch(text, text, uuid, text, integer)'::regprocedure
|
|
880
|
+
AND privilege.grantee <> proc.proowner
|
|
881
|
+
AND privilege.privilege_type = 'EXECUTE'
|
|
882
|
+
LOOP
|
|
883
|
+
EXECUTE format(
|
|
884
|
+
'GRANT EXECUTE ON FUNCTION opengeni_host_export.fail_host_export_batch(text, text, uuid, text, integer, integer) TO %I',
|
|
885
|
+
v_role
|
|
886
|
+
);
|
|
887
|
+
END LOOP;
|
|
888
|
+
END $migration$;
|
|
889
|
+
|
|
890
|
+
-- Preserve the published consumer-status ACL on its out-of-band companion.
|
|
891
|
+
DO $migration$
|
|
892
|
+
DECLARE v_role name;
|
|
893
|
+
BEGIN
|
|
894
|
+
FOR v_role IN
|
|
895
|
+
SELECT grantee.rolname
|
|
896
|
+
FROM pg_catalog.pg_proc proc
|
|
897
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
898
|
+
coalesce(proc.proacl, pg_catalog.acldefault('f', proc.proowner))
|
|
899
|
+
) privilege
|
|
900
|
+
JOIN pg_catalog.pg_roles grantee ON grantee.oid = privilege.grantee
|
|
901
|
+
WHERE proc.oid =
|
|
902
|
+
'opengeni_host_export.host_export_consumer_status(text, text)'::regprocedure
|
|
903
|
+
AND privilege.grantee <> proc.proowner
|
|
904
|
+
AND privilege.privilege_type = 'EXECUTE'
|
|
905
|
+
LOOP
|
|
906
|
+
EXECUTE format(
|
|
907
|
+
'GRANT EXECUTE ON FUNCTION opengeni_host_export.host_export_consumer_status_sidecar(text, text) TO %I',
|
|
908
|
+
v_role
|
|
909
|
+
);
|
|
910
|
+
END LOOP;
|
|
911
|
+
END $migration$;
|
|
912
|
+
|
|
913
|
+
DROP TRIGGER IF EXISTS session_events_bound_payload_before_insert ON session_events;
|
|
914
|
+
DROP FUNCTION IF EXISTS opengeni_private.bound_session_event_payload();
|
|
915
|
+
|
|
916
|
+
-- Retain a bounded, explicit monitoring projection without mutating the
|
|
917
|
+
-- canonical row. Unlike the retired write guard, this read helper accurately
|
|
918
|
+
-- reports that authorized callers can request the full retained payload.
|
|
919
|
+
CREATE OR REPLACE FUNCTION opengeni_private.project_session_event_payload(source_payload jsonb)
|
|
920
|
+
RETURNS jsonb
|
|
921
|
+
LANGUAGE sql
|
|
922
|
+
IMMUTABLE
|
|
923
|
+
SET search_path = pg_catalog
|
|
924
|
+
AS $function$
|
|
925
|
+
SELECT CASE
|
|
926
|
+
WHEN octet_length(source_payload::text) <= 65536 THEN source_payload
|
|
927
|
+
ELSE jsonb_build_object(
|
|
928
|
+
'_monitoring', jsonb_build_object(
|
|
929
|
+
'payloadMode', 'summary',
|
|
930
|
+
'payloadTruncated', true,
|
|
931
|
+
'originalBytes', octet_length(source_payload::text),
|
|
932
|
+
'fullPayload', 'request payloadMode=full explicitly'
|
|
933
|
+
),
|
|
934
|
+
'preview', left(source_payload::text, 2048)
|
|
935
|
+
)
|
|
936
|
+
END
|
|
937
|
+
$function$;
|
|
938
|
+
|
|
939
|
+
ALTER TABLE session_events
|
|
940
|
+
DROP CONSTRAINT IF EXISTS session_events_payload_bytes_check;
|
|
941
|
+
|
|
942
|
+
ALTER TABLE session_realtime_entries
|
|
943
|
+
DROP CONSTRAINT IF EXISTS session_realtime_entries_text_check,
|
|
944
|
+
DROP CONSTRAINT IF EXISTS session_realtime_entries_payload_check;
|
|
945
|
+
|
|
946
|
+
COMMENT ON COLUMN session_events.payload IS
|
|
947
|
+
'Canonical lossless event payload. Explicit read/transport projections apply their own byte bounds.';
|
|
948
|
+
COMMENT ON COLUMN session_history_items.item IS
|
|
949
|
+
'Canonical model/history item. PostgreSQL-unsafe string code units use a reversible per-string encoding that leaves structural JSON keys queryable.';
|
|
950
|
+
COMMENT ON COLUMN rig_changes.payload IS
|
|
951
|
+
'Canonical lossless rig-change payload; public summaries are separate projections.';
|
|
952
|
+
COMMENT ON COLUMN rig_changes.verification IS
|
|
953
|
+
'Canonical lossless rig-verification result; public summaries are separate projections.';
|
|
954
|
+
COMMENT ON COLUMN session_pending_tool_calls.call_item IS
|
|
955
|
+
'Canonical pending SDK call item; structural JSON keys remain SQL-queryable.';
|
|
956
|
+
COMMENT ON COLUMN session_pending_tool_calls.result_item IS
|
|
957
|
+
'Canonical pending SDK result item; structural JSON keys remain SQL-queryable.';
|
|
958
|
+
COMMENT ON COLUMN session_system_updates.payload IS
|
|
959
|
+
'Canonical machine-input payload; public monitoring projections are separate.';
|
|
960
|
+
COMMENT ON COLUMN host_export_outbox.payload IS
|
|
961
|
+
'Original storage payload. Session-event rows carry exact codec truth in payload_codec_version; usage rows remain ordinary JSON with NULL codec version.';
|
|
962
|
+
COMMENT ON COLUMN host_export_outbox.payload_codec_version IS
|
|
963
|
+
'Out-of-band codec truth for the complete outbox payload. NULL is literal legacy or ordinary usage JSON; 1 means the whole payload used the application lossless codec.';
|
|
964
|
+
COMMENT ON COLUMN host_export_consumers.last_error IS
|
|
965
|
+
'Exact internal host-sink failure storage. Public logs use a separate fixed structural projection.';
|
|
966
|
+
COMMENT ON COLUMN host_export_consumers.last_error_codec_version IS
|
|
967
|
+
'Out-of-band codec truth for the complete last_error value. NULL is literal old-writer data; 1 means the application encoded the whole value.';
|
|
968
|
+
COMMENT ON COLUMN host_export_dead_letters.envelope IS
|
|
969
|
+
'Original mixed dead-letter envelope. SQL-built envelopes remain literal with NULL envelope_codec_version; nested session-event payload codec truth is separate.';
|
|
970
|
+
COMMENT ON COLUMN host_export_dead_letters.event_payload_codec_version IS
|
|
971
|
+
'Out-of-band codec truth only for envelope.event.payload. It never implies that the surrounding mixed envelope is encoded.';
|
|
972
|
+
COMMENT ON COLUMN knowledge_memories.text IS
|
|
973
|
+
'Canonical exact memory text; PostgreSQL-unsafe code units use the application text codec.';
|
|
974
|
+
COMMENT ON COLUMN audit_events.metadata IS
|
|
975
|
+
'Canonical audit metadata; value-bearing secret audit fields remain prohibited by their domain contract.';
|