@opengeni/db 3.6.0 → 3.7.3-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 (80) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-744DFC65.js → chunk-4NAR7FBA.js} +92 -6
  3. package/dist/chunk-4NAR7FBA.js.map +1 -0
  4. package/dist/{chunk-IW6O6G7R.js → chunk-5NNCGFQY.js} +2 -2
  5. package/dist/{chunk-G3732M3T.js → chunk-DZCYW2WT.js} +2 -2
  6. package/dist/{chunk-WW7AJ5IC.js → chunk-F6PBS6WE.js} +5 -7
  7. package/dist/chunk-F6PBS6WE.js.map +1 -0
  8. package/dist/{chunk-UJ2EW3QI.js → chunk-O56DM5GZ.js} +340 -85
  9. package/dist/chunk-O56DM5GZ.js.map +1 -0
  10. package/dist/{chunk-CDMMSUWJ.js → chunk-S3TDDCS2.js} +3 -3
  11. package/dist/{chunk-Q5YUGQVK.js → chunk-SZAWDXQC.js} +3 -3
  12. package/dist/{chunk-UGDVSQ6U.js → chunk-U4WPPEBH.js} +2 -2
  13. package/dist/{chunk-2NT476HD.js → chunk-US4IS6IV.js} +2 -2
  14. package/dist/codex-token-resolver.d.ts +12 -1
  15. package/dist/company-profile-agent-admin.d.ts +4 -2
  16. package/dist/company-profile-agent-policy.d.ts +20 -0
  17. package/dist/company-profile-schema.d.ts +573 -0
  18. package/dist/editable-artifact-durable-export.js +2 -2
  19. package/dist/editable-artifacts.js +3 -3
  20. package/dist/index.d.ts +382 -18
  21. package/dist/index.js +4513 -2065
  22. package/dist/index.js.map +1 -1
  23. package/dist/managed-auth-session-sets.js +3 -3
  24. package/dist/managed-human-provisioning.d.ts +3 -3
  25. package/dist/memory-domain.d.ts +5 -3
  26. package/dist/provision-roles.js +1 -1
  27. package/dist/runtime-posture.d.ts +5 -5
  28. package/dist/schema.d.ts +725 -4
  29. package/dist/schema.js +13 -1
  30. package/dist/session-background-commands.js +3 -3
  31. package/dist/session-control.d.ts +40 -5
  32. package/dist/session-queue-commands.d.ts +2 -0
  33. package/dist/session-tenancy.js +3 -3
  34. package/dist/user-resource-authority.d.ts +8 -0
  35. package/dist/video-generation.js +3 -3
  36. package/drizzle/0369_local_human_personal_authority.sql +350 -0
  37. package/drizzle/0370_workspace_member_management_scope.sql +98 -0
  38. package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
  39. package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
  40. package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
  41. package/drizzle/0374_session_event_cursors.sql +315 -0
  42. package/drizzle/0375_session_recovery_observability.sql +73 -0
  43. package/drizzle/0376_organization_workspace_inventory.sql +88 -0
  44. package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
  45. package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
  46. package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
  47. package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
  48. package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
  49. package/drizzle/0382_organization_api_key_provenance.sql +91 -0
  50. package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
  51. package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
  52. package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
  53. package/drizzle/0386_local_organization_administration.sql +157 -0
  54. package/package.json +6 -6
  55. package/src/codex-token-resolver.ts +26 -1
  56. package/src/company-brain-governed-writes.ts +30 -1
  57. package/src/company-profile-agent-admin.ts +68 -1
  58. package/src/company-profile-agent-policy.ts +126 -0
  59. package/src/company-profile-schema.ts +115 -1
  60. package/src/database.ts +3 -5
  61. package/src/index.ts +3626 -456
  62. package/src/managed-human-provisioning.ts +3 -3
  63. package/src/memory-domain.ts +9 -4
  64. package/src/organization-membership-lifecycle.ts +0 -1
  65. package/src/provision-roles.ts +83 -4
  66. package/src/runtime-posture.ts +22 -0
  67. package/src/schema.ts +161 -8
  68. package/src/session-control.ts +177 -59
  69. package/src/session-queue-commands.ts +146 -40
  70. package/src/session-tool-call-settlement.ts +34 -7
  71. package/src/user-resource-authority.ts +56 -0
  72. package/dist/chunk-744DFC65.js.map +0 -1
  73. package/dist/chunk-UJ2EW3QI.js.map +0 -1
  74. package/dist/chunk-WW7AJ5IC.js.map +0 -1
  75. /package/dist/{chunk-IW6O6G7R.js.map → chunk-5NNCGFQY.js.map} +0 -0
  76. /package/dist/{chunk-G3732M3T.js.map → chunk-DZCYW2WT.js.map} +0 -0
  77. /package/dist/{chunk-CDMMSUWJ.js.map → chunk-S3TDDCS2.js.map} +0 -0
  78. /package/dist/{chunk-Q5YUGQVK.js.map → chunk-SZAWDXQC.js.map} +0 -0
  79. /package/dist/{chunk-UGDVSQ6U.js.map → chunk-U4WPPEBH.js.map} +0 -0
  80. /package/dist/{chunk-2NT476HD.js.map → chunk-US4IS6IV.js.map} +0 -0
@@ -0,0 +1,91 @@
1
+ -- deployment-mode: rolling
2
+ -- Organization-wide workspace authority must come from an explicitly issued
3
+ -- organization key, never from the historical workspace_id IS NULL shape.
4
+
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '5min';
7
+
8
+ ALTER TABLE api_keys
9
+ ADD COLUMN credential_kind text;
10
+
11
+ ALTER TABLE api_keys NO FORCE ROW LEVEL SECURITY;
12
+
13
+ UPDATE api_keys
14
+ SET
15
+ credential_kind = CASE
16
+ WHEN workspace_id IS NOT NULL THEN 'workspace'
17
+ ELSE 'legacy_account'
18
+ END,
19
+ revoked_at = CASE
20
+ WHEN workspace_id IS NULL THEN COALESCE(revoked_at, CURRENT_TIMESTAMP)
21
+ ELSE revoked_at
22
+ END,
23
+ updated_at = CASE
24
+ WHEN workspace_id IS NULL AND revoked_at IS NULL THEN CURRENT_TIMESTAMP
25
+ ELSE updated_at
26
+ END;
27
+
28
+ ALTER TABLE api_keys FORCE ROW LEVEL SECURITY;
29
+
30
+ CREATE FUNCTION opengeni_private.normalize_api_key_credential_kind()
31
+ RETURNS trigger
32
+ LANGUAGE plpgsql
33
+ SECURITY INVOKER
34
+ SET search_path = pg_catalog, public
35
+ AS $function$
36
+ BEGIN
37
+ IF NEW.workspace_id IS NOT NULL THEN
38
+ NEW.credential_kind := 'workspace';
39
+ ELSIF NEW.credential_kind IS NULL THEN
40
+ -- The previous application version omitted this column. Its organization-key
41
+ -- route is the only null-workspace insert path with this exact fixed grant.
42
+ IF NEW.permissions = '["account:read","workspace:create","workspace:read","workspace:admin","api_keys:manage"]'::jsonb THEN
43
+ NEW.credential_kind := 'organization';
44
+ ELSE
45
+ NEW.credential_kind := 'legacy_account';
46
+ END IF;
47
+ END IF;
48
+
49
+ -- Old application versions authorize every active null-workspace row by
50
+ -- shape alone. Keep ambiguous legacy rows unusable through that old lookup
51
+ -- path for the entire rolling window; only the exact previous organization
52
+ -- writer above may create an active null-workspace credential.
53
+ IF NEW.credential_kind = 'legacy_account' AND NEW.revoked_at IS NULL THEN
54
+ NEW.revoked_at := CURRENT_TIMESTAMP;
55
+ NEW.updated_at := CURRENT_TIMESTAMP;
56
+ END IF;
57
+
58
+ RETURN NEW;
59
+ END;
60
+ $function$;
61
+
62
+ REVOKE ALL ON FUNCTION opengeni_private.normalize_api_key_credential_kind() FROM PUBLIC;
63
+
64
+ CREATE TRIGGER api_keys_00_normalize_credential_kind
65
+ BEFORE INSERT OR UPDATE OF workspace_id, credential_kind, permissions, revoked_at
66
+ ON api_keys
67
+ FOR EACH ROW
68
+ EXECUTE FUNCTION opengeni_private.normalize_api_key_credential_kind();
69
+
70
+ ALTER TABLE api_keys
71
+ ALTER COLUMN credential_kind SET NOT NULL;
72
+
73
+ ALTER TABLE api_keys
74
+ ADD CONSTRAINT api_keys_credential_kind_check
75
+ CHECK (
76
+ (workspace_id IS NOT NULL AND credential_kind = 'workspace')
77
+ OR
78
+ (workspace_id IS NULL AND credential_kind = 'organization')
79
+ OR
80
+ (workspace_id IS NULL AND credential_kind = 'legacy_account' AND revoked_at IS NOT NULL)
81
+ ) NOT VALID;
82
+
83
+ ALTER TABLE api_keys
84
+ VALIDATE CONSTRAINT api_keys_credential_kind_check;
85
+
86
+ CREATE INDEX api_keys_organization_account_idx
87
+ ON api_keys (account_id, created_at DESC)
88
+ WHERE credential_kind = 'organization';
89
+
90
+ COMMENT ON COLUMN api_keys.credential_kind IS
91
+ 'Persisted API-key provenance. Only organization keys receive organization-wide shared-workspace authority; ambiguous legacy account keys are revoked.';
@@ -0,0 +1,51 @@
1
+ -- deployment-mode: rolling
2
+ --
3
+ -- Give Codex cooldowns typed provenance plus an independent revision. A fresh
4
+ -- provider usage read may then clear only the exact quota cooldown it observed
5
+ -- before the request; generic backpressure and a concurrently newer refusal
6
+ -- remain authoritative. Legacy writers do not know these columns, so the
7
+ -- trigger advances the revision and removes typed auto-clear authority whenever
8
+ -- they change exhausted_until alone during a rolling deployment.
9
+
10
+ ALTER TABLE "codex_subscription_credentials"
11
+ ADD COLUMN "exhausted_kind" text,
12
+ ADD COLUMN "exhausted_revision" bigint NOT NULL DEFAULT 0,
13
+ ADD CONSTRAINT "codex_credentials_exhausted_kind_chk" CHECK (
14
+ ("exhausted_until" IS NULL AND "exhausted_kind" IS NULL)
15
+ OR (
16
+ "exhausted_until" IS NOT NULL
17
+ AND ("exhausted_kind" IS NULL OR "exhausted_kind" IN ('quota', 'rate_limit'))
18
+ )
19
+ );
20
+
21
+ CREATE OR REPLACE FUNCTION opengeni_private.codex_cooldown_revision_guard()
22
+ RETURNS trigger
23
+ LANGUAGE plpgsql
24
+ SET search_path = pg_catalog
25
+ AS $function$
26
+ BEGIN
27
+ IF NEW.exhausted_until IS DISTINCT FROM OLD.exhausted_until THEN
28
+ IF NEW.exhausted_revision = OLD.exhausted_revision THEN
29
+ -- Compatibility path for a pre-0382 writer: retain the cooldown but do
30
+ -- not let a new usage reader mistake its unknown cause for quota.
31
+ NEW.exhausted_kind := NULL;
32
+ NEW.exhausted_revision := OLD.exhausted_revision + 1;
33
+ ELSIF NEW.exhausted_revision <> OLD.exhausted_revision + 1 THEN
34
+ RAISE EXCEPTION 'Codex cooldown revision must advance exactly once';
35
+ END IF;
36
+ ELSIF NEW.exhausted_kind IS DISTINCT FROM OLD.exhausted_kind
37
+ OR NEW.exhausted_revision IS DISTINCT FROM OLD.exhausted_revision
38
+ THEN
39
+ IF NEW.exhausted_revision <> OLD.exhausted_revision + 1 THEN
40
+ RAISE EXCEPTION 'Codex cooldown metadata revision must advance exactly once';
41
+ END IF;
42
+ END IF;
43
+ RETURN NEW;
44
+ END
45
+ $function$;
46
+
47
+ CREATE TRIGGER codex_cooldown_revision_guard
48
+ BEFORE UPDATE OF exhausted_until, exhausted_kind, exhausted_revision
49
+ ON "codex_subscription_credentials"
50
+ FOR EACH ROW
51
+ EXECUTE FUNCTION opengeni_private.codex_cooldown_revision_guard();
@@ -0,0 +1,6 @@
1
+ -- deployment-mode: rolling
2
+ -- Trigger helpers are owner-internal implementation details. PostgreSQL grants
3
+ -- PUBLIC execute on new functions by default, so remove the inherited privilege
4
+ -- that exposed this guard to unrelated runtime roles.
5
+
6
+ REVOKE ALL ON FUNCTION opengeni_private.codex_cooldown_revision_guard() FROM PUBLIC;
@@ -0,0 +1,25 @@
1
+ -- deployment-mode: rolling
2
+ -- Native Connected Machine workspace roots made tilde paths invalid. Repair
3
+ -- only legacy sessions whose active machine has reported an exact root; leave
4
+ -- ambiguous/offline legacy rows untouched rather than guessing a user home.
5
+
6
+ -- Migrations run as the NOSUPERUSER/NOBYPASSRLS table owner. Temporarily let
7
+ -- that owner see the joined rows; application roles remain policy-bound.
8
+ ALTER TABLE "sessions" NO FORCE ROW LEVEL SECURITY;
9
+ ALTER TABLE "sandboxes" NO FORCE ROW LEVEL SECURITY;
10
+ ALTER TABLE "enrollments" NO FORCE ROW LEVEL SECURITY;
11
+
12
+ UPDATE "sessions" AS session
13
+ SET "working_dir" =
14
+ rtrim(enrollment."workspace_root", '/') || '/' || substring(session."working_dir" FROM 3)
15
+ FROM "sandboxes" AS sandbox
16
+ JOIN "enrollments" AS enrollment ON enrollment."id" = sandbox."enrollment_id"
17
+ WHERE session."active_sandbox_id" = sandbox."id"
18
+ AND session."working_dir" LIKE '~/%'
19
+ AND enrollment."status" = 'active'
20
+ AND enrollment."workspace_root" IS NOT NULL
21
+ AND enrollment."workspace_root" <> '';
22
+
23
+ ALTER TABLE "enrollments" FORCE ROW LEVEL SECURITY;
24
+ ALTER TABLE "sandboxes" FORCE ROW LEVEL SECURITY;
25
+ ALTER TABLE "sessions" FORCE ROW LEVEL SECURITY;
@@ -0,0 +1,157 @@
1
+ -- deployment-mode: maintenance
2
+ --
3
+ -- The canonical single-user local bootstrap already grants `dev` account-owner
4
+ -- authority in the application, but its durable organization membership was
5
+ -- left at the default member role. Converge that exact built-in identity and
6
+ -- admit it to organization Codex administration without broadening configured,
7
+ -- delegated, API-key, service, or agent principals.
8
+
9
+ SET LOCAL lock_timeout = '5s';
10
+ SET LOCAL statement_timeout = '10min';
11
+
12
+ CREATE OR REPLACE FUNCTION opengeni_private.assign_managed_self_organization_owner()
13
+ RETURNS trigger
14
+ LANGUAGE plpgsql
15
+ SECURITY DEFINER
16
+ SET search_path FROM CURRENT
17
+ AS $body$
18
+ BEGIN
19
+ IF EXISTS (
20
+ SELECT 1 FROM managed_accounts account
21
+ WHERE account.id = NEW.account_id
22
+ AND (
23
+ (
24
+ account.external_source = 'better-auth:user'
25
+ AND NEW.subject_id = 'user:' || account.external_id
26
+ ) OR (
27
+ account.external_source = 'opengeni:local'
28
+ AND account.external_id = 'default'
29
+ AND NEW.subject_id = 'dev'
30
+ )
31
+ )
32
+ ) THEN
33
+ NEW.role := 'owner';
34
+ END IF;
35
+ RETURN NEW;
36
+ END
37
+ $body$;
38
+
39
+ DO $pin_local_owner_trigger$
40
+ DECLARE data_schema text := current_schema();
41
+ BEGIN
42
+ EXECUTE format(
43
+ 'ALTER FUNCTION opengeni_private.assign_managed_self_organization_owner() SET search_path = pg_catalog, %I, pg_temp',
44
+ data_schema
45
+ );
46
+ END
47
+ $pin_local_owner_trigger$;
48
+
49
+ ALTER TABLE organization_memberships NO FORCE ROW LEVEL SECURITY;
50
+ UPDATE organization_memberships membership
51
+ SET role = 'owner',
52
+ authorization_revision = membership.authorization_revision + 1,
53
+ updated_at = clock_timestamp()
54
+ FROM managed_accounts account
55
+ WHERE account.id = membership.account_id
56
+ AND account.external_source = 'opengeni:local'
57
+ AND account.external_id = 'default'
58
+ AND membership.subject_id = 'dev'
59
+ AND membership.status = 'active'
60
+ AND membership.role <> 'owner';
61
+ ALTER TABLE organization_memberships FORCE ROW LEVEL SECURITY;
62
+
63
+ DO $codex_scope_visibility_schema$
64
+ DECLARE data_schema text := current_schema();
65
+ BEGIN
66
+ EXECUTE format($ddl$
67
+ CREATE OR REPLACE FUNCTION opengeni_private.codex_organization_scope_visible(
68
+ p_account_id uuid
69
+ ) RETURNS boolean
70
+ LANGUAGE plpgsql
71
+ SECURITY DEFINER
72
+ SET search_path = pg_catalog
73
+ AS $function$
74
+ DECLARE
75
+ workspace_value uuid := opengeni_private.current_workspace_id();
76
+ subject_value text := opengeni_private.current_subject_id();
77
+ visible boolean := false;
78
+ previous_lifecycle text := pg_catalog.current_setting(
79
+ 'opengeni.organization_tenancy_lifecycle', true
80
+ );
81
+ BEGIN
82
+ IF p_account_id IS NULL
83
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
84
+ THEN
85
+ RETURN false;
86
+ END IF;
87
+ IF workspace_value IS NOT NULL THEN
88
+ PERFORM pg_catalog.set_config(
89
+ 'opengeni.organization_tenancy_lifecycle',
90
+ 'organization_membership_lifecycle', true
91
+ );
92
+ SELECT EXISTS (
93
+ SELECT 1 FROM %1$I.workspaces workspace
94
+ WHERE workspace.account_id = p_account_id AND workspace.id = workspace_value
95
+ ) AND NOT EXISTS (
96
+ SELECT 1 FROM %1$I.organization_memberships membership
97
+ WHERE membership.account_id = p_account_id
98
+ AND membership.personal_workspace_id = workspace_value
99
+ ) INTO visible;
100
+ PERFORM pg_catalog.set_config(
101
+ 'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
102
+ );
103
+ RETURN coalesce(visible, false);
104
+ END IF;
105
+ IF subject_value IS NULL OR NOT (
106
+ subject_value LIKE 'user:%%'
107
+ OR (
108
+ subject_value = 'dev'
109
+ AND EXISTS (
110
+ SELECT 1 FROM %1$I.managed_accounts account
111
+ WHERE account.id = p_account_id
112
+ AND account.external_source = 'opengeni:local'
113
+ AND account.external_id = 'default'
114
+ )
115
+ )
116
+ ) THEN
117
+ RETURN false;
118
+ END IF;
119
+ PERFORM pg_catalog.set_config(
120
+ 'opengeni.organization_tenancy_lifecycle',
121
+ 'organization_membership_lifecycle', true
122
+ );
123
+ SELECT EXISTS (
124
+ SELECT 1 FROM %1$I.organization_memberships membership
125
+ WHERE membership.account_id = p_account_id
126
+ AND membership.subject_id = subject_value
127
+ AND membership.status = 'active'
128
+ AND membership.role IN ('owner', 'admin')
129
+ ) INTO visible;
130
+ PERFORM pg_catalog.set_config(
131
+ 'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
132
+ );
133
+ RETURN coalesce(visible, false);
134
+ EXCEPTION WHEN OTHERS THEN
135
+ PERFORM pg_catalog.set_config(
136
+ 'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
137
+ );
138
+ RAISE;
139
+ END
140
+ $function$
141
+ $ddl$, data_schema);
142
+ END
143
+ $codex_scope_visibility_schema$;
144
+
145
+ REVOKE ALL ON FUNCTION opengeni_private.assign_managed_self_organization_owner() FROM PUBLIC;
146
+ REVOKE ALL ON FUNCTION opengeni_private.codex_organization_scope_visible(uuid) FROM PUBLIC;
147
+
148
+ DO $grants$
149
+ BEGIN
150
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
151
+ GRANT EXECUTE ON FUNCTION opengeni_private.assign_managed_self_organization_owner()
152
+ TO opengeni_app;
153
+ GRANT EXECUTE ON FUNCTION opengeni_private.codex_organization_scope_visible(uuid)
154
+ TO opengeni_app;
155
+ END IF;
156
+ END
157
+ $grants$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "3.6.0",
3
+ "version": "3.7.3-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.0",
82
+ "@opengeni/codex": "^0.2.20-canary.0",
83
+ "@opengeni/config": "^0.22.5-canary.0",
84
+ "@opengeni/contracts": "^2.9.2-canary.0",
85
+ "@opengeni/network": "^0.2.3-canary.5",
86
86
  "drizzle-orm": "^0.45.2",
87
87
  "postgres": "^3.4.7"
88
88
  },
@@ -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,