@opengeni/db 0.7.3 → 0.10.7

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 (70) hide show
  1. package/dist/{chunk-YFQ7SGE4.js → chunk-BMFDXFPA.js} +23 -1
  2. package/dist/chunk-BMFDXFPA.js.map +1 -0
  3. package/dist/chunk-KW526IJA.js +127 -0
  4. package/dist/chunk-KW526IJA.js.map +1 -0
  5. package/dist/{chunk-B22X3IEZ.js → chunk-P6PKXY5W.js} +604 -32
  6. package/dist/chunk-P6PKXY5W.js.map +1 -0
  7. package/dist/index.d.ts +4 -3
  8. package/dist/index.js +7408 -2276
  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 +1122 -73
  13. package/dist/provision-roles.js +1 -1
  14. package/dist/{schema-BN5mB9xZ.d.ts → schema-CqkzrBRS.d.ts} +6967 -4457
  15. package/dist/schema.d.ts +1 -1
  16. package/dist/schema.js +19 -1
  17. package/drizzle/0053_codex_credential_leases.sql +2 -2
  18. package/drizzle/0057_durable_queue_control.sql +1 -1
  19. package/drizzle/0061_session_workflow_wake_outbox.sql +1 -1
  20. package/drizzle/0062_session_list_snapshot_reaper.sql +1 -1
  21. package/drizzle/0063_session_control_mega_foundation.sql +1 -1
  22. package/drizzle/0064_rotation_strategy_sharded_backfill.sql +15 -0
  23. package/drizzle/0065_codex_subscription_overview.sql +168 -0
  24. package/drizzle/0065_session_attempt_quiescence.sql +26 -0
  25. package/drizzle/0065_session_tool_policy.sql +38 -0
  26. package/drizzle/0066_session_interruption_attempt_lookup.sql +4 -0
  27. package/drizzle/0067_session_event_payload_bounds.sql +209 -0
  28. package/drizzle/0068_workspace_control_event_bounds.sql +134 -0
  29. package/drizzle/0069_session_event_history_backfill.sql +32 -0
  30. package/drizzle/0070_session_event_type_sequence_lookup.sql +4 -0
  31. package/drizzle/0071_session_event_monitoring_tail.sql +10 -0
  32. package/drizzle/0072_sessions_workspace_created_id_idx.sql +4 -0
  33. package/drizzle/0073_sessions_workspace_updated_id_idx.sql +4 -0
  34. package/drizzle/0074_session_activity_revisions.sql +72 -0
  35. package/drizzle/0075_sessions_workspace_activity_revision_idx.sql +4 -0
  36. package/drizzle/0076_session_workflow_wake_acl.sql +13 -0
  37. package/drizzle/0077_session_attempt_latest_lookup.sql +4 -0
  38. package/drizzle/0094_quarantine_credential_bearing_catalog_urls.sql +51 -0
  39. package/drizzle/0095_github_existing_installations.sql +84 -0
  40. package/drizzle/0096_session_turn_initiators.sql +97 -0
  41. package/drizzle/0097_host_export_outbox.sql +1220 -0
  42. package/drizzle/0098_usage_events_workspace_session_idx.sql +4 -0
  43. package/drizzle/0099_session_human_input_attempt_owner_index.sql +6 -0
  44. package/drizzle/0100_session_human_input_requests.sql +89 -0
  45. package/drizzle/0101_session_mcp_connection_refs.sql +30 -0
  46. package/drizzle/0102_session_command_receipt_service_actor.sql +16 -0
  47. package/drizzle/0103_host_export_root_session.sql +166 -0
  48. package/drizzle/0104_host_export_root_session_backfill.sql +27 -0
  49. package/drizzle/0105_session_turn_instructions.sql +9 -0
  50. package/drizzle/0106_session_attempt_mcp_approval_policies.sql +29 -0
  51. package/drizzle/0107_host_export_lineage_contract.sql +381 -0
  52. package/drizzle/0108_fence_invalidated_warming_epochs.sql +76 -0
  53. package/package.json +5 -4
  54. package/src/codex-token-resolver.ts +175 -14
  55. package/src/connection-token-resolver.ts +428 -119
  56. package/src/event-payload-sanitizer.ts +88 -20
  57. package/src/index.ts +7208 -1273
  58. package/src/memory-domain.ts +1 -1
  59. package/src/migrate.ts +86 -23
  60. package/src/persistence-errors.ts +252 -0
  61. package/src/provision-roles.ts +42 -0
  62. package/src/schema.ts +658 -34
  63. package/src/session-control.ts +520 -38
  64. package/src/session-queue-commands.ts +385 -61
  65. package/src/session-tool-call-settlement.ts +58 -7
  66. package/src/turn-initiator.ts +155 -0
  67. package/dist/chunk-7LDU7F5P.js +0 -80
  68. package/dist/chunk-7LDU7F5P.js.map +0 -1
  69. package/dist/chunk-B22X3IEZ.js.map +0 -1
  70. package/dist/chunk-YFQ7SGE4.js.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { boundSessionEventPayload } from "@opengeni/contracts";
2
+
1
3
  /**
2
4
  * Last line of defense against a session event crashing a whole turn.
3
5
  *
@@ -8,12 +10,10 @@
8
10
  * such a payload reaches `INSERT INTO session_events`, the driver rejects it
9
11
  * ("Failed query: insert into session_events") and the turn dies.
10
12
  *
11
- * `sanitizeEventPayload` deep-walks any payload value (objects, arrays, nested)
12
- * and, for every string, strips NUL and rewrites invalid/lone UTF-16 surrogates
13
- * to the Unicode replacement char (U+FFFD), so the result is always valid UTF-8
14
- * that jsonb can store. It is cheap and total: only strings are touched, and only
15
- * the two disallowed classes of code unit -- no meaningful text is lost, no
16
- * truncation (truncation is handled elsewhere).
13
+ * `sanitizeEventPayload` deep-walks any payload value (objects, arrays, nested),
14
+ * repairs every string, redacts sensitive fields, then applies the canonical
15
+ * byte-bounded human/audit preview. Conversation truth uses
16
+ * `sanitizeModelPayload` below and remains a separate representation.
17
17
  */
18
18
 
19
19
  const REPLACEMENT = "�";
@@ -88,12 +88,53 @@ export function sanitizeEventString(value: string): string {
88
88
  * combinations are traversed; non-string leaves pass through untouched. Object
89
89
  * keys are sanitized too -- they are jsonb-constrained the same as values.
90
90
  */
91
- export function sanitizeEventPayload<T>(payload: T): T {
91
+ export type SanitizeEventPayloadOptions = {
92
+ /**
93
+ * Separately trusted, server-created retained-output evidence. Never populate
94
+ * this from a producer-controlled payload field.
95
+ */
96
+ fullEvidence?: unknown;
97
+ };
98
+
99
+ export function sanitizeEventPayload<T>(payload: T, options: SanitizeEventPayloadOptions = {}): T {
100
+ // Bound first. The preview walker caps depth/container fan-out and replaces
101
+ // inline media before this sanitizer allocates a deep clone. Reversing this
102
+ // order lets a cyclic, deeply nested, or multi-megabyte tool result exhaust
103
+ // the stack/heap before the durable 64 KiB event boundary can protect it.
104
+ const bounded = boundSessionEventPayload(payload, {
105
+ fullEvidence: options.fullEvidence,
106
+ });
107
+ return sanitizeEventPayloadDeep(
108
+ bounded === payload ? removeProducerTruncationMetadata(bounded) : bounded,
109
+ );
110
+ }
111
+
112
+ /**
113
+ * `truncation` is reserved durable-boundary metadata. An ordinary payload that
114
+ * already fits the envelope otherwise returns by reference, so remove a
115
+ * producer-supplied value before persistence rather than allowing it to forge
116
+ * byte accounting or an available retained-artifact receipt. A payload changed
117
+ * by `boundSessionEventPayload` already carries freshly computed metadata and
118
+ * never reaches this helper.
119
+ */
120
+ function removeProducerTruncationMetadata<T>(payload: T): T {
121
+ if (!isPlainObject(payload)) return payload;
122
+ const descriptor = Object.getOwnPropertyDescriptor(payload, "truncation");
123
+ if (!descriptor?.enumerable) return payload;
124
+ const cleaned = { ...payload };
125
+ delete cleaned.truncation;
126
+ return cleaned as T;
127
+ }
128
+
129
+ function sanitizeEventPayloadDeep<T>(payload: T): T {
92
130
  if (typeof payload === "string") {
93
131
  return sanitizeEventString(payload) as unknown as T;
94
132
  }
95
133
  if (Array.isArray(payload)) {
96
- return payload.map((item) => sanitizeEventPayload(item)) as unknown as T;
134
+ return payload.map((item) => sanitizeEventPayloadDeep(item)) as unknown as T;
135
+ }
136
+ if (payload instanceof Date) {
137
+ return safeDateIso(payload) as unknown as T;
97
138
  }
98
139
  if (payload && typeof payload === "object") {
99
140
  const entries = Object.entries(payload as Record<string, unknown>).map(
@@ -115,21 +156,48 @@ export function sanitizeEventPayload<T>(payload: T): T {
115
156
  * performs only the database-safety repair (NUL removal and UTF-16 repair).
116
157
  */
117
158
  export function sanitizeModelPayload<T>(payload: T): T {
159
+ return sanitizeModelPayloadDeep(payload, new WeakSet<object>(), 0);
160
+ }
161
+
162
+ const MODEL_PAYLOAD_SANITIZE_MAX_DEPTH = 64;
163
+ const MODEL_PAYLOAD_CYCLE_MARKER = "[OpenGeni omitted cyclic model payload]";
164
+ const MODEL_PAYLOAD_DEPTH_MARKER = "[OpenGeni omitted model payload beyond database-safety depth]";
165
+
166
+ function sanitizeModelPayloadDeep<T>(payload: T, seen: WeakSet<object>, depth: number): T {
118
167
  if (typeof payload === "string") {
119
168
  return sanitizeEventString(payload) as unknown as T;
120
169
  }
121
- if (Array.isArray(payload)) {
122
- return payload.map((item) => sanitizeModelPayload(item)) as unknown as T;
170
+ if (!payload || typeof payload !== "object") return payload;
171
+ if (payload instanceof Date) {
172
+ return safeDateIso(payload) as unknown as T;
123
173
  }
124
- if (payload && typeof payload === "object") {
174
+ if (depth >= MODEL_PAYLOAD_SANITIZE_MAX_DEPTH) {
175
+ return MODEL_PAYLOAD_DEPTH_MARKER as unknown as T;
176
+ }
177
+ if (seen.has(payload)) return MODEL_PAYLOAD_CYCLE_MARKER as unknown as T;
178
+ seen.add(payload);
179
+ try {
180
+ if (Array.isArray(payload)) {
181
+ return payload.map((item) => sanitizeModelPayloadDeep(item, seen, depth + 1)) as unknown as T;
182
+ }
125
183
  return Object.fromEntries(
126
184
  Object.entries(payload as Record<string, unknown>).map(([key, value]) => [
127
185
  sanitizeEventString(key),
128
- sanitizeModelPayload(value),
186
+ sanitizeModelPayloadDeep(value, seen, depth + 1),
129
187
  ]),
130
188
  ) as unknown as T;
189
+ } finally {
190
+ seen.delete(payload);
191
+ }
192
+ }
193
+
194
+ function safeDateIso(value: Date): string | null {
195
+ try {
196
+ const epoch = Date.prototype.getTime.call(value);
197
+ return Number.isFinite(epoch) ? Date.prototype.toISOString.call(value) : null;
198
+ } catch {
199
+ return null;
131
200
  }
132
- return payload;
133
201
  }
134
202
 
135
203
  function sanitizeSensitiveEventField(key: string, value: unknown): unknown {
@@ -142,19 +210,19 @@ function sanitizeSensitiveEventField(key: string, value: unknown): unknown {
142
210
  if (SENSITIVE_FIELD_NAMES.has(normalizeFieldName(key))) {
143
211
  return REDACTED;
144
212
  }
145
- return sanitizeEventPayload(value);
213
+ return sanitizeEventPayloadDeep(value);
146
214
  }
147
215
 
148
216
  function sanitizeSessionMcpServerList(value: unknown): unknown {
149
217
  if (!Array.isArray(value)) {
150
- return sanitizeEventPayload(value);
218
+ return sanitizeEventPayloadDeep(value);
151
219
  }
152
220
  return value.map((item) => {
153
221
  if (!isPlainObject(item)) {
154
- return sanitizeEventPayload(item);
222
+ return sanitizeEventPayloadDeep(item);
155
223
  }
156
224
  const { headers, headersEncrypted, ...rest } = item;
157
- const cleaned = sanitizeEventPayload(rest) as Record<string, unknown>;
225
+ const cleaned = sanitizeEventPayloadDeep(rest) as Record<string, unknown>;
158
226
  const headerNames = safeHeaderNames(headers) ?? safeHeaderNames(headersEncrypted);
159
227
  if (headerNames) {
160
228
  cleaned.headerNames = headerNames;
@@ -165,14 +233,14 @@ function sanitizeSessionMcpServerList(value: unknown): unknown {
165
233
 
166
234
  function sanitizeMcpCredentialUpdateList(value: unknown): unknown {
167
235
  if (!Array.isArray(value)) {
168
- return sanitizeEventPayload(value);
236
+ return sanitizeEventPayloadDeep(value);
169
237
  }
170
238
  return value.map((item) => {
171
239
  if (!isPlainObject(item)) {
172
- return sanitizeEventPayload(item);
240
+ return sanitizeEventPayloadDeep(item);
173
241
  }
174
242
  const { headers, headersEncrypted, ...rest } = item;
175
- const cleaned = sanitizeEventPayload(rest) as Record<string, unknown>;
243
+ const cleaned = sanitizeEventPayloadDeep(rest) as Record<string, unknown>;
176
244
  const headerNames = safeHeaderNames(headers) ?? safeHeaderNames(headersEncrypted);
177
245
  if (headerNames) {
178
246
  cleaned.headerNames = headerNames;