@opengeni/db 3.6.0 → 3.7.4-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 (92) hide show
  1. package/dist/automations.d.ts +4 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/{chunk-UGDVSQ6U.js → chunk-3ZMN4EXC.js} +2 -2
  4. package/dist/{chunk-744DFC65.js → chunk-6FIVOJ3V.js} +96 -6
  5. package/dist/chunk-6FIVOJ3V.js.map +1 -0
  6. package/dist/{chunk-UJ2EW3QI.js → chunk-DXJ2WY6W.js} +427 -85
  7. package/dist/chunk-DXJ2WY6W.js.map +1 -0
  8. package/dist/{chunk-CDMMSUWJ.js → chunk-JP6IYGYI.js} +3 -3
  9. package/dist/{chunk-Q5YUGQVK.js → chunk-JS3AWDKZ.js} +3 -3
  10. package/dist/{chunk-G3732M3T.js → chunk-LHKB62GC.js} +2 -2
  11. package/dist/{chunk-2NT476HD.js → chunk-QL4AHBYD.js} +2 -2
  12. package/dist/{chunk-IW6O6G7R.js → chunk-VDXHLVDE.js} +2 -2
  13. package/dist/{chunk-WW7AJ5IC.js → chunk-ZTSJPFDH.js} +5 -7
  14. package/dist/chunk-ZTSJPFDH.js.map +1 -0
  15. package/dist/codex-token-resolver.d.ts +12 -1
  16. package/dist/company-profile-agent-admin.d.ts +4 -2
  17. package/dist/company-profile-agent-policy.d.ts +20 -0
  18. package/dist/company-profile-schema.d.ts +573 -0
  19. package/dist/editable-artifact-durable-export.js +2 -2
  20. package/dist/editable-artifacts.js +3 -3
  21. package/dist/index.d.ts +487 -20
  22. package/dist/index.js +9489 -6041
  23. package/dist/index.js.map +1 -1
  24. package/dist/managed-auth-session-sets.js +3 -3
  25. package/dist/managed-human-provisioning.d.ts +3 -3
  26. package/dist/memory-domain.d.ts +5 -3
  27. package/dist/model-catalog.d.ts +153 -0
  28. package/dist/pr-review.d.ts +9 -0
  29. package/dist/provision-roles.js +1 -1
  30. package/dist/runtime-posture.d.ts +7 -7
  31. package/dist/schema.d.ts +1069 -6
  32. package/dist/schema.js +17 -1
  33. package/dist/session-background-commands.js +3 -3
  34. package/dist/session-control.d.ts +75 -5
  35. package/dist/session-queue-commands.d.ts +19 -0
  36. package/dist/session-tenancy.js +3 -3
  37. package/dist/user-resource-authority.d.ts +8 -0
  38. package/dist/video-generation.js +3 -3
  39. package/dist/xai-subscription.d.ts +8 -0
  40. package/drizzle/0369_local_human_personal_authority.sql +350 -0
  41. package/drizzle/0370_workspace_member_management_scope.sql +98 -0
  42. package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
  43. package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
  44. package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
  45. package/drizzle/0374_session_event_cursors.sql +315 -0
  46. package/drizzle/0375_session_recovery_observability.sql +73 -0
  47. package/drizzle/0376_organization_workspace_inventory.sql +88 -0
  48. package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
  49. package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
  50. package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
  51. package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
  52. package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
  53. package/drizzle/0382_organization_api_key_provenance.sql +91 -0
  54. package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
  55. package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
  56. package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
  57. package/drizzle/0386_local_organization_administration.sql +157 -0
  58. package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
  59. package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
  60. package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
  61. package/package.json +6 -6
  62. package/src/automations.ts +9 -1
  63. package/src/codex-token-resolver.ts +26 -1
  64. package/src/company-brain-governed-writes.ts +30 -1
  65. package/src/company-profile-agent-admin.ts +68 -1
  66. package/src/company-profile-agent-policy.ts +126 -0
  67. package/src/company-profile-schema.ts +115 -1
  68. package/src/database.ts +3 -5
  69. package/src/index.ts +4473 -570
  70. package/src/insights.ts +21 -7
  71. package/src/managed-human-provisioning.ts +3 -3
  72. package/src/memory-domain.ts +9 -4
  73. package/src/model-catalog.ts +682 -0
  74. package/src/organization-membership-lifecycle.ts +0 -1
  75. package/src/pr-review.ts +19 -0
  76. package/src/provision-roles.ts +83 -4
  77. package/src/runtime-posture.ts +26 -0
  78. package/src/schema.ts +259 -10
  79. package/src/session-control.ts +347 -59
  80. package/src/session-queue-commands.ts +303 -122
  81. package/src/session-tool-call-settlement.ts +34 -7
  82. package/src/user-resource-authority.ts +56 -0
  83. package/src/xai-subscription.ts +66 -14
  84. package/dist/chunk-744DFC65.js.map +0 -1
  85. package/dist/chunk-UJ2EW3QI.js.map +0 -1
  86. package/dist/chunk-WW7AJ5IC.js.map +0 -1
  87. /package/dist/{chunk-UGDVSQ6U.js.map → chunk-3ZMN4EXC.js.map} +0 -0
  88. /package/dist/{chunk-CDMMSUWJ.js.map → chunk-JP6IYGYI.js.map} +0 -0
  89. /package/dist/{chunk-Q5YUGQVK.js.map → chunk-JS3AWDKZ.js.map} +0 -0
  90. /package/dist/{chunk-G3732M3T.js.map → chunk-LHKB62GC.js.map} +0 -0
  91. /package/dist/{chunk-2NT476HD.js.map → chunk-QL4AHBYD.js.map} +0 -0
  92. /package/dist/{chunk-IW6O6G7R.js.map → chunk-VDXHLVDE.js.map} +0 -0
@@ -0,0 +1,241 @@
1
+ -- deployment-mode: maintenance
2
+ -- Migration 0389: deployment-owned model catalog authority, provider-scoped
3
+ -- workspace Vercel AI Gateway/OpenRouter slugs, and indexed pre-catalog prompt
4
+ -- receipt replay.
5
+ -- This changes the exact runtime-posture table/grant contract. Stop every API,
6
+ -- control worker, and turn worker before applying it, and never restart a
7
+ -- pre-0389 image after commit.
8
+
9
+ SET LOCAL lock_timeout = '5s';
10
+ SET LOCAL statement_timeout = '5min';
11
+
12
+ DO $model_catalog_runtime_drain_before$
13
+ DECLARE
14
+ configured_roles_text text := nullif(
15
+ current_setting('opengeni.migration_application_roles', true), ''
16
+ );
17
+ configured_roles jsonb;
18
+ BEGIN
19
+ IF configured_roles_text IS NULL THEN
20
+ RAISE EXCEPTION
21
+ '0389 model catalog activation requires an explicit application database role list'
22
+ USING ERRCODE = '55000';
23
+ END IF;
24
+ BEGIN
25
+ configured_roles := configured_roles_text::jsonb;
26
+ EXCEPTION WHEN OTHERS THEN
27
+ RAISE EXCEPTION
28
+ '0389 model catalog activation received a malformed application database role list'
29
+ USING ERRCODE = '55000';
30
+ END;
31
+ IF jsonb_typeof(configured_roles) <> 'array'
32
+ OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
33
+ OR EXISTS (
34
+ SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
35
+ WHERE jsonb_typeof(value) <> 'string'
36
+ OR btrim(value #>> '{}') = ''
37
+ OR octet_length(value #>> '{}') > 63
38
+ )
39
+ OR (
40
+ SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
41
+ ) <> (
42
+ SELECT count(DISTINCT value)
43
+ FROM jsonb_array_elements_text(configured_roles) AS roles(value)
44
+ )
45
+ THEN
46
+ RAISE EXCEPTION
47
+ '0389 model catalog activation received an invalid application database role list'
48
+ USING ERRCODE = '55000';
49
+ END IF;
50
+ IF EXISTS (
51
+ SELECT 1 FROM pg_stat_activity activity
52
+ JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
53
+ ON roles.role_name = activity.usename
54
+ WHERE activity.datname = current_database()
55
+ AND activity.pid <> pg_backend_pid()
56
+ )
57
+ THEN
58
+ RAISE EXCEPTION
59
+ '0389 model catalog activation requires all configured OpenGeni application database sessions to be stopped'
60
+ USING ERRCODE = '55000';
61
+ END IF;
62
+ END
63
+ $model_catalog_runtime_drain_before$;
64
+
65
+ -- Exactly one deployment-global, secret-free catalog document. Ordinary API
66
+ -- and worker roles may only read it; the operator upsert command uses a
67
+ -- migration/admin credential. No seed or boot-time rewrite is intentional.
68
+ CREATE TABLE deployment_model_catalog (
69
+ singleton boolean PRIMARY KEY DEFAULT true,
70
+ document jsonb NOT NULL,
71
+ version bigint NOT NULL DEFAULT 1,
72
+ updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
73
+ CONSTRAINT deployment_model_catalog_singleton_chk CHECK (singleton),
74
+ CONSTRAINT deployment_model_catalog_document_chk CHECK (jsonb_typeof(document) = 'object'),
75
+ CONSTRAINT deployment_model_catalog_version_chk CHECK (version > 0)
76
+ );
77
+
78
+ COMMENT ON TABLE deployment_model_catalog IS
79
+ 'Deployment-global, secret-free model catalog singleton. Runtime is SELECT-only; operators update it explicitly.';
80
+
81
+ CREATE TABLE workspace_gateway_custom_models (
82
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
83
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
84
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
85
+ provider_kind text NOT NULL,
86
+ upstream_model_id text NOT NULL,
87
+ label text,
88
+ version integer NOT NULL DEFAULT 1,
89
+ create_operation_id uuid NOT NULL,
90
+ create_request_hash text NOT NULL,
91
+ delete_operation_id uuid,
92
+ delete_request_hash text,
93
+ created_by_subject_id text NOT NULL,
94
+ retired_at timestamptz,
95
+ created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
96
+ updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
97
+ CONSTRAINT workspace_gateway_custom_models_workspace_account_fk
98
+ FOREIGN KEY (workspace_id, account_id)
99
+ REFERENCES workspaces(id, account_id) ON DELETE CASCADE,
100
+ CONSTRAINT workspace_gateway_custom_models_provider_kind_chk CHECK (
101
+ provider_kind IN ('vercel_gateway', 'openrouter')
102
+ ),
103
+ CONSTRAINT workspace_gateway_custom_models_upstream_chk CHECK (
104
+ octet_length(upstream_model_id) BETWEEN 1 AND 238
105
+ AND upstream_model_id ~ '^[!-~]+$'
106
+ AND upstream_model_id !~ '[|]'
107
+ ),
108
+ CONSTRAINT workspace_gateway_custom_models_label_chk CHECK (
109
+ label IS NULL OR (
110
+ octet_length(label) BETWEEN 1 AND 128
111
+ AND label !~ '[\r\n|]'
112
+ )
113
+ ),
114
+ CONSTRAINT workspace_gateway_custom_models_actor_chk CHECK (
115
+ octet_length(created_by_subject_id) BETWEEN 1 AND 1024
116
+ ),
117
+ CONSTRAINT workspace_gateway_custom_models_version_chk CHECK (version > 0),
118
+ CONSTRAINT workspace_gateway_custom_models_create_hash_chk CHECK (
119
+ create_request_hash ~ '^[a-f0-9]{64}$'
120
+ ),
121
+ CONSTRAINT workspace_gateway_custom_models_delete_receipt_chk CHECK (
122
+ (delete_operation_id IS NULL AND delete_request_hash IS NULL)
123
+ OR (
124
+ delete_operation_id IS NOT NULL
125
+ AND delete_request_hash ~ '^[a-f0-9]{64}$'
126
+ AND retired_at IS NOT NULL
127
+ )
128
+ )
129
+ );
130
+
131
+ CREATE UNIQUE INDEX workspace_gateway_custom_models_workspace_upstream_uq
132
+ ON workspace_gateway_custom_models (workspace_id, provider_kind, upstream_model_id)
133
+ WHERE retired_at IS NULL;
134
+
135
+ CREATE UNIQUE INDEX workspace_gateway_custom_models_create_operation_uq
136
+ ON workspace_gateway_custom_models (workspace_id, provider_kind, create_operation_id);
137
+
138
+ CREATE UNIQUE INDEX workspace_gateway_custom_models_delete_operation_uq
139
+ ON workspace_gateway_custom_models (workspace_id, provider_kind, delete_operation_id)
140
+ WHERE delete_operation_id IS NOT NULL;
141
+
142
+ -- New Send/Steer receipts persist one parsed boundary-request fingerprint and
143
+ -- replay it before mutable model-catalog resolution. Legacy receipt identity
144
+ -- remains unchanged; this partial index keeps the actor/key compatibility
145
+ -- probe bounded even when the prior action or target session differs.
146
+ CREATE INDEX session_command_receipts_prompt_actor_operation_idx
147
+ ON session_command_receipts (
148
+ workspace_id,
149
+ actor_type,
150
+ actor_subject_id,
151
+ actor_attempt_id,
152
+ operation_key
153
+ )
154
+ WHERE action IN ('prompt.send', 'prompt.steer');
155
+
156
+ ALTER TABLE workspace_gateway_custom_models ENABLE ROW LEVEL SECURITY;
157
+ ALTER TABLE workspace_gateway_custom_models FORCE ROW LEVEL SECURITY;
158
+ CREATE POLICY workspace_isolation ON workspace_gateway_custom_models
159
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
160
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
161
+
162
+ -- Migration 0040 registers owner default privileges for the historical
163
+ -- opengeni_app role. Rotated-role deployments must not inherit that stale
164
+ -- grant on these new tables, and host defaults may name other obsolete
165
+ -- application roles. Strip every explicit non-owner table grantee. The
166
+ -- migration_application_roles list above is drain detection only; the
167
+ -- post-migration role provisioner grants only the deployment's current target
168
+ -- runtime role.
169
+ DO $model_catalog_table_acl_reset$
170
+ DECLARE
171
+ data_schema text := pg_catalog.current_schema();
172
+ relation_name text;
173
+ role_name text;
174
+ BEGIN
175
+ FOR relation_name IN
176
+ SELECT relation.value
177
+ FROM pg_catalog.unnest(
178
+ ARRAY[
179
+ 'deployment_model_catalog',
180
+ 'workspace_gateway_custom_models'
181
+ ]::text[]
182
+ ) AS relation(value)
183
+ LOOP
184
+ EXECUTE pg_catalog.format(
185
+ 'REVOKE ALL ON TABLE %I.%I FROM PUBLIC',
186
+ data_schema,
187
+ relation_name
188
+ );
189
+ FOR role_name IN
190
+ SELECT grantee_role.rolname
191
+ FROM pg_catalog.pg_class relation
192
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
193
+ coalesce(
194
+ relation.relacl,
195
+ pg_catalog.acldefault('r', relation.relowner)
196
+ )
197
+ ) privilege
198
+ INNER JOIN pg_catalog.pg_roles grantee_role
199
+ ON grantee_role.oid = privilege.grantee
200
+ WHERE relation.oid = pg_catalog.to_regclass(
201
+ pg_catalog.format('%I.%I', data_schema, relation_name)
202
+ )
203
+ AND privilege.grantee <> 0
204
+ AND privilege.grantee <> relation.relowner
205
+ GROUP BY grantee_role.rolname
206
+ ORDER BY grantee_role.rolname COLLATE "C"
207
+ LOOP
208
+ EXECUTE pg_catalog.format(
209
+ 'REVOKE ALL ON TABLE %I.%I FROM %I',
210
+ data_schema,
211
+ relation_name,
212
+ role_name
213
+ );
214
+ END LOOP;
215
+ END LOOP;
216
+ END
217
+ $model_catalog_table_acl_reset$;
218
+
219
+ COMMENT ON TABLE workspace_gateway_custom_models IS
220
+ 'Workspace-owned Vercel AI Gateway and OpenRouter upstream slugs. Retired rows remain execution evidence; capabilities and billing are never stored here.';
221
+
222
+ DO $model_catalog_runtime_drain_after$
223
+ DECLARE
224
+ configured_roles jsonb := current_setting(
225
+ 'opengeni.migration_application_roles', false
226
+ )::jsonb;
227
+ BEGIN
228
+ IF EXISTS (
229
+ SELECT 1 FROM pg_stat_activity activity
230
+ JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
231
+ ON roles.role_name = activity.usename
232
+ WHERE activity.datname = current_database()
233
+ AND activity.pid <> pg_backend_pid()
234
+ )
235
+ THEN
236
+ RAISE EXCEPTION
237
+ '0389 model catalog activation observed a configured OpenGeni application database session after schema installation'
238
+ USING ERRCODE = '55000';
239
+ END IF;
240
+ END
241
+ $model_catalog_runtime_drain_after$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "3.6.0",
3
+ "version": "3.7.4-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": {
@@ -78,11 +78,11 @@
78
78
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
79
79
  },
80
80
  "dependencies": {
81
- "@opengeni/codemode": "^0.4.17",
82
- "@opengeni/codex": "^0.2.19",
83
- "@opengeni/config": "^0.22.0",
84
- "@opengeni/contracts": "^2.7.0",
85
- "@opengeni/network": "^0.2.3",
81
+ "@opengeni/codemode": "^0.4.22-canary.1",
82
+ "@opengeni/codex": "^0.2.20-canary.1",
83
+ "@opengeni/config": "^0.22.5-canary.1",
84
+ "@opengeni/contracts": "^2.9.2-canary.1",
85
+ "@opengeni/network": "^0.2.3-canary.6",
86
86
  "drizzle-orm": "^0.45.2",
87
87
  "postgres": "^3.4.7"
88
88
  },
@@ -223,6 +223,8 @@ export async function createAutomationTrigger(
223
223
  createdBySubjectId: string;
224
224
  request: CreateAutomationTriggerRequest;
225
225
  adapterId: string;
226
+ /** Trusted database-only admission seam. Throwing rolls creation back. */
227
+ beforeCreateCommit?: (tx: Database) => Promise<void>;
226
228
  },
227
229
  ): Promise<AutomationTrigger> {
228
230
  return await withWorkspaceRls(
@@ -289,6 +291,7 @@ export async function createAutomationTrigger(
289
291
  throw new Error("Automation Pack trigger must use its frozen manifest template");
290
292
  }
291
293
  }
294
+ await input.beforeCreateCommit?.(tx);
292
295
  const [head] = await tx
293
296
  .insert(schema.automationTriggers)
294
297
  .values({
@@ -438,6 +441,8 @@ export async function updateAutomationTrigger(
438
441
  triggerId: string;
439
442
  subjectId: string;
440
443
  request: UpdateAutomationTriggerRequest;
444
+ /** Trusted database-only admission seam. Throwing rolls the revision back. */
445
+ beforeUpdateCommit?: (tx: Database) => Promise<void>;
441
446
  },
442
447
  ): Promise<AutomationTrigger | null> {
443
448
  return await withWorkspaceRls(
@@ -478,6 +483,7 @@ export async function updateAutomationTrigger(
478
483
  "Pack-owned automation triggers must be managed through their Pack setup API",
479
484
  );
480
485
  }
486
+ await input.beforeUpdateCommit?.(tx);
481
487
  const revisionNumber = existing.head.currentRevision + 1;
482
488
  const [head] = await tx
483
489
  .update(schema.automationTriggers)
@@ -696,7 +702,9 @@ export async function claimAutomationRun(
696
702
  .for("update")
697
703
  .limit(1);
698
704
  if (!row) return null;
699
- if (row.status === "dispatched" || row.status === "skipped") return mapRunExecution(row);
705
+ if (row.status === "dispatched" || row.status === "skipped" || row.status === "failed") {
706
+ return mapRunExecution(row);
707
+ }
700
708
  const [updated] = await tx
701
709
  .update(schema.automationRuns)
702
710
  .set({
@@ -24,6 +24,7 @@ import {
24
24
  CODEX_REFRESH_FALLBACK_MS,
25
25
  CODEX_REFRESH_WINDOW_MS,
26
26
  CodexReloginRequired,
27
+ codexUsageConfirmsQuotaAvailable,
27
28
  type CodexTokenSnapshot,
28
29
  type CodexUsagePayload,
29
30
  type CodexFetch,
@@ -43,6 +44,8 @@ export type CodexCredentialTokens = {
43
44
  idToken: string;
44
45
  };
45
46
 
47
+ export type CodexCredentialCooldownKind = "quota" | "rate_limit";
48
+
46
49
  export type CodexCredentialForRun = {
47
50
  id: string;
48
51
  version: number;
@@ -56,6 +59,9 @@ export type CodexCredentialForRun = {
56
59
  lastRefreshAt: Date | null;
57
60
  status: string;
58
61
  lastError: string | null;
62
+ exhaustedUntil: Date | null;
63
+ exhaustedKind: CodexCredentialCooldownKind | null;
64
+ exhaustedRevision: number;
59
65
  };
60
66
 
61
67
  export type CodexAccountUsageSnapshot = {
@@ -66,6 +72,12 @@ export type CodexAccountUsageSnapshot = {
66
72
  checkedAt?: Date;
67
73
  resetCreditAvailableCount?: number | null;
68
74
  resetCreditsCheckedAt?: Date | null;
75
+ /**
76
+ * Clear only a quota cooldown with this exact revision. Set solely after a
77
+ * live provider response proves allowance is open; a concurrent refusal
78
+ * advances the revision and makes this observation stale.
79
+ */
80
+ clearQuotaCooldownRevision?: number;
69
81
  };
70
82
 
71
83
  type CodexCredentialRefreshInput = {
@@ -375,7 +387,8 @@ function errorUsagePayload(reason?: "needs_relogin"): CodexUsagePayload {
375
387
  * account's expired JWT from 401-ing the usage read.
376
388
  * 2. fetch GET /wham/usage with that bearer.
377
389
  * 3. normalize (§3) into the P2/P3 contract.
378
- * 4. on any windows present, write the five usage-cache columns (the TTL clock).
390
+ * 4. on any windows present, write the usage cache and conditionally reconcile
391
+ * the exact older typed quota cooldown observed before provider I/O.
379
392
  *
380
393
  * A refresh that stamps needs_relogin returns { status:"error", reason } and never
381
394
  * hits the provider; a transient refresh error returns a plain error payload.
@@ -396,6 +409,13 @@ export async function fetchCodexUsageForAccount(
396
409
  return errorUsagePayload(error instanceof CodexReloginRequired ? "needs_relogin" : undefined);
397
410
  }
398
411
 
412
+ // Snapshot cooldown authority immediately before provider I/O. The usage
413
+ // write may clear only this exact revision; any concurrent refusal advances
414
+ // it and wins. A metadata-read failure must not sink the usage response.
415
+ const observedCredential = await deps
416
+ .loadCredential(db, settings, workspaceId, credentialId)
417
+ .catch(() => null);
418
+
399
419
  let normalized: CodexUsagePayload;
400
420
  try {
401
421
  const usage = await fetchCodexUsage(
@@ -438,6 +458,11 @@ export async function fetchCodexUsageForAccount(
438
458
  checkedAt,
439
459
  }
440
460
  : {}),
461
+ ...(observedCredential?.exhaustedUntil &&
462
+ observedCredential.exhaustedKind === "quota" &&
463
+ codexUsageConfirmsQuotaAvailable(normalized)
464
+ ? { clearQuotaCooldownRevision: observedCredential.exhaustedRevision }
465
+ : {}),
441
466
  ...(normalized.rateLimitResetCredits
442
467
  ? {
443
468
  resetCreditAvailableCount: normalized.rateLimitResetCredits.availableCount,
@@ -15,7 +15,11 @@ import {
15
15
  import { and, eq, inArray, sql } from "drizzle-orm";
16
16
  import type { Database } from "./database";
17
17
  import { rawRows, setSubjectRlsContext, withRlsContext } from "./database";
18
- import { sanitizePreferenceDescriptorText } from "./preference-registry";
18
+ import {
19
+ PreferenceRegistryStableKeyConflictError,
20
+ sanitizePreferenceDescriptorText,
21
+ } from "./preference-registry";
22
+ import { nestedPostgresSqlState, safeDatabaseErrorFacts } from "./persistence-errors";
19
23
  import {
20
24
  appendKnowledgeClaim,
21
25
  appendKnowledgeClaimReview,
@@ -847,6 +851,26 @@ function isInstructionPolicyProposal(
847
851
  );
848
852
  }
849
853
 
854
+ function isPreferenceProposal(
855
+ request: CompanyBrainGovernedWriteRequestType,
856
+ ): request is PreferenceProposalRequest {
857
+ return request.kind === "propose_preference" || request.kind === "promote_task_note_preference";
858
+ }
859
+
860
+ const PREFERENCE_PROPOSAL_STABLE_KEY_CONFLICT_DIAGNOSTIC =
861
+ "workspace preference key is bound to another proposal";
862
+
863
+ function isPreferenceProposalStableKeyConflict(error: unknown): boolean {
864
+ if (nestedPostgresSqlState(error) !== "23505") return false;
865
+ const constraint = safeDatabaseErrorFacts(error).constraint ?? "";
866
+ if (constraint.startsWith("preference_registry_preferences_")) return true;
867
+ for (let current = error, depth = 0; current instanceof Error && depth < 8; depth += 1) {
868
+ if (current.message.includes(PREFERENCE_PROPOSAL_STABLE_KEY_CONFLICT_DIAGNOSTIC)) return true;
869
+ current = current.cause;
870
+ }
871
+ return false;
872
+ }
873
+
850
874
  async function materializeWaysOfWorkingProposal(
851
875
  db: Database,
852
876
  input: {
@@ -1008,6 +1032,11 @@ export async function writeCompanyBrainGovernedProposal(
1008
1032
  ) {
1009
1033
  throw error;
1010
1034
  }
1035
+ if (isPreferenceProposal(request) && isPreferenceProposalStableKeyConflict(error)) {
1036
+ throw new PreferenceRegistryStableKeyConflictError(
1037
+ "A preference with this stable key already exists for the workspace",
1038
+ );
1039
+ }
1011
1040
  if (error instanceof ScopedKnowledgeAuthorityError) {
1012
1041
  throw new CompanyBrainGovernedWriteAuthorityError(error.message);
1013
1042
  }
@@ -28,6 +28,7 @@ export class CompanyProfileAgentAdminError extends Error {
28
28
  constructor(
29
29
  readonly code:
30
30
  | "authority_unavailable"
31
+ | "policy_disabled"
31
32
  | "confirmation_unavailable"
32
33
  | "profile_conflict"
33
34
  | "operation_reused"
@@ -62,6 +63,13 @@ function translate(error: unknown, stage: "propose" | "confirm"): never {
62
63
  { cause: error },
63
64
  );
64
65
  }
66
+ if (state === "P1852") {
67
+ throw new CompanyProfileAgentAdminError(
68
+ "policy_disabled",
69
+ "Agent-authored company-profile changes are disabled by organization policy.",
70
+ { cause: error },
71
+ );
72
+ }
65
73
  if (state === "40001") {
66
74
  throw new CompanyProfileAgentAdminError(
67
75
  "profile_conflict",
@@ -86,6 +94,18 @@ function translate(error: unknown, stage: "propose" | "confirm"): never {
86
94
  throw error;
87
95
  }
88
96
 
97
+ /** Deterministic UUID-shaped id for the activation stage of one proposal operation. */
98
+ export function derivedCompanyProfileAutomaticActivationOperationId(operationId: string): string {
99
+ const bytes = createHash("sha256")
100
+ .update(`company-profile-agent-admin:v1:${operationId}:automatic-activation`, "utf8")
101
+ .digest()
102
+ .subarray(0, 16);
103
+ bytes[6] = (bytes[6]! & 0x0f) | 0x50;
104
+ bytes[8] = (bytes[8]! & 0x3f) | 0x80;
105
+ const hex = bytes.toString("hex");
106
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
107
+ }
108
+
89
109
  async function withAgentAttemptContext<T>(
90
110
  db: Database,
91
111
  attempt: CompanyProfileAgentAttemptType,
@@ -119,10 +139,13 @@ export async function proposeCompanyProfileForAgent(
119
139
  receipt_id: string;
120
140
  revision_id: string;
121
141
  human_input: unknown;
142
+ policy_mode: "suggest" | "automatic";
143
+ automatic_activation_receipt_id: string | null;
144
+ activation_event_id: string | null;
122
145
  replayed: boolean;
123
146
  }>(
124
147
  scoped,
125
- sql`select * from propose_company_profile_for_attempt(
148
+ sql`select * from propose_company_profile_for_attempt_v2(
126
149
  ${attempt.accountId}::uuid,
127
150
  ${attempt.workspaceId}::uuid,
128
151
  ${attempt.sessionId}::uuid,
@@ -130,6 +153,7 @@ export async function proposeCompanyProfileForAgent(
130
153
  ${attempt.attemptId}::uuid,
131
154
  ${attempt.executionGeneration},
132
155
  ${request.operationId}::uuid,
156
+ ${derivedCompanyProfileAutomaticActivationOperationId(request.operationId)}::uuid,
133
157
  ${contentJson},
134
158
  ${contentHash},
135
159
  ${request.reason}
@@ -138,6 +162,48 @@ export async function proposeCompanyProfileForAgent(
138
162
  if (!row) throw new Error("Company-profile proposal returned no receipt");
139
163
  return row;
140
164
  });
165
+ if (
166
+ result.policy_mode === "automatic" &&
167
+ result.automatic_activation_receipt_id &&
168
+ result.activation_event_id
169
+ ) {
170
+ const [inventory, event] = await Promise.all([
171
+ listCompanyProfile(db, {
172
+ accountId: attempt.accountId,
173
+ workspaceId: attempt.workspaceId,
174
+ limit: 1,
175
+ }),
176
+ getCompanyProfileActivationEvent(db, {
177
+ accountId: attempt.accountId,
178
+ workspaceId: attempt.workspaceId,
179
+ eventId: result.activation_event_id,
180
+ }),
181
+ ]);
182
+ if (!event?.newRevision) {
183
+ throw new Error("Company-profile automatic activation event is unavailable");
184
+ }
185
+ const revision = await getCompanyProfileRevision(db, {
186
+ accountId: attempt.accountId,
187
+ workspaceId: attempt.workspaceId,
188
+ revisionId: event.newRevision.id,
189
+ });
190
+ return CompanyProfileAgentProposalReceipt.parse({
191
+ status: "activated",
192
+ operationId: request.operationId,
193
+ proposalReceiptId: result.receipt_id,
194
+ automaticActivationReceiptId: result.automatic_activation_receipt_id,
195
+ policyMode: "automatic",
196
+ mutation: {
197
+ revision,
198
+ head: inventory.current?.revisionId === revision.id ? inventory.current : null,
199
+ event,
200
+ },
201
+ replayed: result.replayed,
202
+ });
203
+ }
204
+ if (result.policy_mode !== "suggest") {
205
+ throw new Error("Company-profile proposal returned an incomplete automatic activation");
206
+ }
141
207
  const revision = await getCompanyProfileRevision(db, {
142
208
  accountId: attempt.accountId,
143
209
  workspaceId: attempt.workspaceId,
@@ -148,6 +214,7 @@ export async function proposeCompanyProfileForAgent(
148
214
  operationId: request.operationId,
149
215
  proposalReceiptId: result.receipt_id,
150
216
  revision,
217
+ policyMode: "suggest",
151
218
  humanInput: CompanyProfileAgentHumanInputPrompt.parse(result.human_input),
152
219
  confirmWith: "company_profile_confirm",
153
220
  replayed: result.replayed,
@@ -0,0 +1,126 @@
1
+ import {
2
+ CompanyProfileAgentPolicy,
3
+ CompanyProfileAgentPolicyMode,
4
+ type CompanyProfileAgentPolicy as CompanyProfileAgentPolicyType,
5
+ type CompanyProfileAgentPolicyMode as CompanyProfileAgentPolicyModeType,
6
+ } from "@opengeni/contracts";
7
+ import { sql } from "drizzle-orm";
8
+ import { rawRows, withRlsContext, withSessionRlsActorContext, type Database } from "./database";
9
+ import { nestedPostgresSqlState } from "./persistence-errors";
10
+
11
+ export class CompanyProfileAgentPolicyError extends Error {
12
+ readonly name = "CompanyProfileAgentPolicyError";
13
+
14
+ constructor(
15
+ readonly code: "authority_unavailable" | "policy_conflict" | "operation_reused" | "invalid",
16
+ message: string,
17
+ options?: ErrorOptions,
18
+ ) {
19
+ super(message, options);
20
+ }
21
+ }
22
+
23
+ function translate(error: unknown): never {
24
+ const state = nestedPostgresSqlState(error);
25
+ if (state === "42501") {
26
+ throw new CompanyProfileAgentPolicyError(
27
+ "authority_unavailable",
28
+ "An active organization owner is required to manage company-profile agent autonomy.",
29
+ { cause: error },
30
+ );
31
+ }
32
+ if (state === "40001") {
33
+ throw new CompanyProfileAgentPolicyError(
34
+ "policy_conflict",
35
+ "The company-profile agent policy changed in another request. Reload and try again.",
36
+ { cause: error },
37
+ );
38
+ }
39
+ if (state === "23505" || state === "P1851") {
40
+ throw new CompanyProfileAgentPolicyError(
41
+ "operation_reused",
42
+ "The company-profile agent policy operation id was already used for different input.",
43
+ { cause: error },
44
+ );
45
+ }
46
+ if (state === "22023" || state === "23514" || state === "55000") {
47
+ throw new CompanyProfileAgentPolicyError(
48
+ "invalid",
49
+ "The company-profile agent policy request is invalid.",
50
+ { cause: error },
51
+ );
52
+ }
53
+ throw error;
54
+ }
55
+
56
+ async function withHumanPolicyContext<T>(
57
+ db: Database,
58
+ input: { accountId: string; workspaceId: string; actorSubjectId: string },
59
+ fn: (scoped: Database) => Promise<T>,
60
+ ): Promise<T> {
61
+ return await withSessionRlsActorContext({ subjectId: input.actorSubjectId }, async () =>
62
+ withRlsContext(
63
+ db,
64
+ { accountId: input.accountId, workspaceId: input.workspaceId },
65
+ async (scoped) => {
66
+ await scoped.execute(
67
+ sql`select set_config('opengeni.principal_kind', 'human_session', true)`,
68
+ );
69
+ return await fn(scoped);
70
+ },
71
+ ),
72
+ );
73
+ }
74
+
75
+ export async function getCompanyProfileAgentPolicy(
76
+ db: Database,
77
+ input: { accountId: string; workspaceId: string; actorSubjectId: string },
78
+ ): Promise<CompanyProfileAgentPolicyType> {
79
+ try {
80
+ return await withHumanPolicyContext(db, input, async (scoped) => {
81
+ const [row] = await rawRows<{ result: unknown }>(
82
+ scoped,
83
+ sql`select get_company_profile_agent_policy(
84
+ ${input.accountId}::uuid,
85
+ ${input.workspaceId}::uuid,
86
+ ${input.actorSubjectId}
87
+ ) as result`,
88
+ );
89
+ return CompanyProfileAgentPolicy.parse(row?.result);
90
+ });
91
+ } catch (error) {
92
+ translate(error);
93
+ }
94
+ }
95
+
96
+ export async function updateCompanyProfileAgentPolicy(
97
+ db: Database,
98
+ input: {
99
+ accountId: string;
100
+ workspaceId: string;
101
+ actorSubjectId: string;
102
+ mode: CompanyProfileAgentPolicyModeType;
103
+ expectedVersion: number;
104
+ operationId: string;
105
+ },
106
+ ): Promise<CompanyProfileAgentPolicyType> {
107
+ const mode = CompanyProfileAgentPolicyMode.parse(input.mode);
108
+ try {
109
+ return await withHumanPolicyContext(db, input, async (scoped) => {
110
+ const [row] = await rawRows<{ result: unknown }>(
111
+ scoped,
112
+ sql`select update_company_profile_agent_policy(
113
+ ${input.accountId}::uuid,
114
+ ${input.workspaceId}::uuid,
115
+ ${input.actorSubjectId},
116
+ ${mode},
117
+ ${input.expectedVersion}::bigint,
118
+ ${input.operationId}::uuid
119
+ ) as result`,
120
+ );
121
+ return CompanyProfileAgentPolicy.parse(row?.result);
122
+ });
123
+ } catch (error) {
124
+ translate(error);
125
+ }
126
+ }