@opengeni/db 1.0.2 → 1.1.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 (69) hide show
  1. package/dist/artifact-catalog.d.ts +12 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/capability-reconciliation.js +2 -2
  4. package/dist/{chunk-KEDWSSNP.js → chunk-7Z7ALFRE.js} +2 -2
  5. package/dist/{chunk-OQZOGCLG.js → chunk-BLUQG54M.js} +2 -2
  6. package/dist/{chunk-Y77PVZZD.js → chunk-BZV5ZDRY.js} +2 -2
  7. package/dist/{chunk-MACELCXG.js → chunk-CIGHXFPD.js} +3 -3
  8. package/dist/{chunk-7S53WLPI.js → chunk-EKXCF3FH.js} +4 -4
  9. package/dist/{chunk-F3YMVRF4.js → chunk-GLA5YCQS.js} +4 -4
  10. package/dist/{chunk-KH3TYDGY.js → chunk-J4WFOVBV.js} +2 -2
  11. package/dist/{chunk-NLSG7GGA.js → chunk-KJVOBLFC.js} +3 -3
  12. package/dist/{chunk-7POTLB24.js → chunk-M2YGXCCQ.js} +2 -2
  13. package/dist/{chunk-PQGTWFRF.js → chunk-M3SWH4MU.js} +2 -2
  14. package/dist/{chunk-OMHBVHX7.js → chunk-ODKYBA3C.js} +5 -5
  15. package/dist/{chunk-56MYWKQV.js → chunk-SKYRM7R7.js} +2 -2
  16. package/dist/{chunk-N5QCKHYL.js → chunk-T5NHDEZW.js} +62 -19
  17. package/dist/chunk-T5NHDEZW.js.map +1 -0
  18. package/dist/{chunk-LDTQGYD2.js → chunk-VOB6TXSJ.js} +16 -1
  19. package/dist/chunk-VOB6TXSJ.js.map +1 -0
  20. package/dist/{chunk-UEQQ2WWU.js → chunk-Z5HJXH3X.js} +3 -26
  21. package/dist/chunk-Z5HJXH3X.js.map +1 -0
  22. package/dist/connect-attempts.js +3 -3
  23. package/dist/editable-artifact-durable-export.js +2 -2
  24. package/dist/editable-artifacts.js +3 -3
  25. package/dist/index.d.ts +14 -5
  26. package/dist/index.js +85 -30
  27. package/dist/index.js.map +1 -1
  28. package/dist/managed-auth-session-sets.js +3 -3
  29. package/dist/mcp-operations.js +2 -2
  30. package/dist/organization-integration-policy.js +3 -3
  31. package/dist/provision-roles.js +1 -1
  32. package/dist/retained-provider-commands.js +6 -6
  33. package/dist/runtime-posture.d.ts +3 -2
  34. package/dist/schema.d.ts +93 -0
  35. package/dist/schema.js +3 -1
  36. package/dist/session-background-commands.js +4 -4
  37. package/dist/session-command-output.js +5 -5
  38. package/dist/session-event-slices.js +3 -3
  39. package/dist/session-mcp-credential-rotation.js +5 -5
  40. package/dist/session-tenancy.d.ts +6 -1
  41. package/dist/session-tenancy.js +3 -5
  42. package/dist/video-generation.js +3 -3
  43. package/dist/workspace-tool-defaults.js +4 -4
  44. package/drizzle/0610_artifact_catalog_pins.sql +165 -0
  45. package/drizzle/0611_universal_session_tenancy_activation.sql +166 -0
  46. package/drizzle/0612_scheduled_model_unavailable_refusal.sql +21 -0
  47. package/package.json +7 -7
  48. package/src/artifact-catalog.ts +31 -5
  49. package/src/index.ts +94 -19
  50. package/src/provision-roles.ts +16 -0
  51. package/src/runtime-posture-cli.ts +0 -2
  52. package/src/runtime-posture.ts +64 -27
  53. package/src/schema.ts +17 -0
  54. package/src/session-tenancy.ts +9 -31
  55. package/dist/chunk-LDTQGYD2.js.map +0 -1
  56. package/dist/chunk-N5QCKHYL.js.map +0 -1
  57. package/dist/chunk-UEQQ2WWU.js.map +0 -1
  58. /package/dist/{chunk-KEDWSSNP.js.map → chunk-7Z7ALFRE.js.map} +0 -0
  59. /package/dist/{chunk-OQZOGCLG.js.map → chunk-BLUQG54M.js.map} +0 -0
  60. /package/dist/{chunk-Y77PVZZD.js.map → chunk-BZV5ZDRY.js.map} +0 -0
  61. /package/dist/{chunk-MACELCXG.js.map → chunk-CIGHXFPD.js.map} +0 -0
  62. /package/dist/{chunk-7S53WLPI.js.map → chunk-EKXCF3FH.js.map} +0 -0
  63. /package/dist/{chunk-F3YMVRF4.js.map → chunk-GLA5YCQS.js.map} +0 -0
  64. /package/dist/{chunk-KH3TYDGY.js.map → chunk-J4WFOVBV.js.map} +0 -0
  65. /package/dist/{chunk-NLSG7GGA.js.map → chunk-KJVOBLFC.js.map} +0 -0
  66. /package/dist/{chunk-7POTLB24.js.map → chunk-M2YGXCCQ.js.map} +0 -0
  67. /package/dist/{chunk-PQGTWFRF.js.map → chunk-M3SWH4MU.js.map} +0 -0
  68. /package/dist/{chunk-OMHBVHX7.js.map → chunk-ODKYBA3C.js.map} +0 -0
  69. /package/dist/{chunk-56MYWKQV.js.map → chunk-SKYRM7R7.js.map} +0 -0
@@ -0,0 +1,166 @@
1
+ -- deployment-mode: rolling
2
+ -- Retire per-organization session-tenancy activation. Every organization is
3
+ -- activated: private ("Only me") sessions, visibility transitions, forks and
4
+ -- personal-resource grants no longer require a version-1
5
+ -- session_tenancy_activations receipt, and the owner/admin Only-me setting
6
+ -- (organization_private_session_settings) now defaults to ENABLED when an
7
+ -- organization has no row. Owners/admins may still disable it; an explicit
8
+ -- disabled row keeps gating new Only-me chats in shared workspaces.
9
+ --
10
+ -- No receipts are written and no data is backfilled. The receipt keeps exactly
11
+ -- one meaning: opengeni_private.session_tenancy_account_activated (0340) still
12
+ -- retires the legacy_user connection and unattributed-writer compatibility
13
+ -- lanes only for organizations that already hold one. Organizations without a
14
+ -- receipt keep those lanes exactly as before this migration, so no stored
15
+ -- authority becomes invisible and no ownership is inferred.
16
+ --
17
+ -- Rolling: this only widens product predicates and flips a missing-row default.
18
+ -- A pre-0611 binary keeps working against the rewritten routines: its own
19
+ -- receipt probe (session_tenancy_product_activated) now answers true, its
20
+ -- startup interlock probe (session_tenancy_any_product_activation) answers
21
+ -- false regardless of the retired environment switch, and every routine keeps
22
+ -- its signature, owner, ACL, SECURITY DEFINER and search_path. No table is
23
+ -- locked or rewritten.
24
+ SET LOCAL lock_timeout = '5s';
25
+ SET LOCAL statement_timeout = '5min';
26
+
27
+ -- Remove the receipt prerequisite from every runtime predicate and default the
28
+ -- Only-me setting to enabled. Each rewrite replaces one exact, reviewed
29
+ -- fragment of the live definition; a changed shape aborts instead of silently
30
+ -- leaving a receipt gate behind. Owner, grants, SECURITY DEFINER and
31
+ -- search_path are preserved by CREATE OR REPLACE of the catalog's own
32
+ -- definition.
33
+ DO $universal_session_tenancy_predicates$
34
+ DECLARE
35
+ data_schema text := pg_catalog.current_schema();
36
+ target regprocedure;
37
+ definition text;
38
+ rewrite record;
39
+ occurrences integer;
40
+ -- Fragments below are routine SOURCE text matched against
41
+ -- pg_get_functiondef, never queries: this block reads only the catalog.
42
+ -- The table names are spliced in so the source is not mistaken for a
43
+ -- migration-time row read of a FORCE-RLS table.
44
+ receipts constant text := 'session_tenancy_activations';
45
+ settings constant text := 'organization_private_session_settings';
46
+ receipt_gate constant text :=
47
+ E' IF NOT EXISTS (\n'
48
+ || E' SELECT 1 FROM ' || receipts || E' activation\n'
49
+ || E' WHERE activation.account_id = p_account_id AND activation.activation_version = 1\n'
50
+ || E' ) THEN\n'
51
+ || E' RAISE EXCEPTION ''session tenancy product is not activated'' USING ERRCODE = ''42501'';\n'
52
+ || E' END IF;\n';
53
+ receipt_gate_removed constant text :=
54
+ E' -- 0611: every organization is session-tenancy activated.\n';
55
+ setting_receipt_gate constant text :=
56
+ E' IF p_enabled AND NOT session_tenancy_product_activated(p_account_id, 1) THEN\n'
57
+ || E' RAISE EXCEPTION ''session tenancy product surface is not available for this organization'' USING ERRCODE = ''55000'';\n'
58
+ || E' END IF;\n';
59
+ BEGIN
60
+ FOR rewrite IN
61
+ SELECT * FROM (VALUES
62
+ (1, pg_catalog.format('%I.session_tenancy_product_activated(uuid,integer)', data_schema),
63
+ E' AND EXISTS (\n'
64
+ || E' SELECT 1 FROM ' || receipts || E' activation\n'
65
+ || E' WHERE activation.account_id = p_account_id\n'
66
+ || E' AND activation.activation_version = p_activation_version\n'
67
+ || E' )',
68
+ E' AND p_account_id IS NOT NULL\n'
69
+ || E' AND p_activation_version IS NOT DISTINCT FROM 1'),
70
+ -- The value-free startup interlock is retired with the deployment switch.
71
+ -- Answering false keeps every earlier binary startable regardless of the
72
+ -- removed OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED value.
73
+ (3, pg_catalog.format('%I.session_tenancy_any_product_activation()', data_schema),
74
+ E' SELECT EXISTS (\n'
75
+ || E' SELECT 1 FROM ' || receipts || E'\n'
76
+ || E' ) INTO activated;',
77
+ E' activated := false;'),
78
+ (4, pg_catalog.format(
79
+ '%I.list_self_user_resource_authorities(uuid,uuid,text,uuid,integer)', data_schema),
80
+ receipt_gate, receipt_gate_removed),
81
+ (5, pg_catalog.format(
82
+ '%I.issue_self_user_resource_grant(uuid,uuid,uuid,text,text,text,uuid,integer,boolean)',
83
+ data_schema),
84
+ receipt_gate, receipt_gate_removed),
85
+ (6, pg_catalog.format('%I.revoke_self_user_resource_grant(uuid,uuid,uuid)', data_schema),
86
+ receipt_gate, receipt_gate_removed),
87
+ (7, pg_catalog.format(
88
+ '%I.accept_turn_personal_resource_attachment(uuid,uuid,uuid,uuid,text,integer,boolean,integer)',
89
+ data_schema),
90
+ receipt_gate, receipt_gate_removed),
91
+ -- Only-me chats default to enabled: only an explicit disabled row gates.
92
+ (8, pg_catalog.format('%I.organization_private_sessions_enabled(uuid)', data_schema),
93
+ E' AND session_tenancy_product_activated(p_account_id, 1)\n'
94
+ || E' AND EXISTS (\n'
95
+ || E' SELECT 1 FROM ' || settings || E' setting\n'
96
+ || E' WHERE setting.account_id = p_account_id AND setting.enabled\n'
97
+ || E' )',
98
+ E' AND NOT EXISTS (\n'
99
+ || E' SELECT 1 FROM ' || settings || E' setting\n'
100
+ || E' WHERE setting.account_id = p_account_id AND NOT setting.enabled\n'
101
+ || E' )'),
102
+ (9, pg_catalog.format(
103
+ '%I.get_organization_private_session_settings(uuid,text)', data_schema),
104
+ E' ''enabled'', coalesce(setting.enabled, false),\n',
105
+ E' ''enabled'', coalesce(setting.enabled, true),\n'),
106
+ (10, pg_catalog.format(
107
+ '%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
108
+ data_schema),
109
+ setting_receipt_gate, ''),
110
+ (11, pg_catalog.format(
111
+ '%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
112
+ data_schema),
113
+ E' did_change := coalesce(setting.enabled, false) IS DISTINCT FROM p_enabled;\n',
114
+ E' did_change := coalesce(setting.enabled, true) IS DISTINCT FROM p_enabled;\n')
115
+ ) AS rewrites(ordinal, signature, old_fragment, new_fragment)
116
+ ORDER BY ordinal
117
+ LOOP
118
+ target := pg_catalog.to_regprocedure(rewrite.signature);
119
+ IF target IS NULL THEN
120
+ RAISE EXCEPTION 'universal session tenancy predicate is missing: %', rewrite.signature
121
+ USING ERRCODE = '55000';
122
+ END IF;
123
+ definition := pg_catalog.pg_get_functiondef(target);
124
+ occurrences := (
125
+ pg_catalog.length(definition)
126
+ - pg_catalog.length(pg_catalog.replace(definition, rewrite.old_fragment, ''))
127
+ ) / pg_catalog.length(rewrite.old_fragment);
128
+ IF occurrences <> 1 THEN
129
+ RAISE EXCEPTION 'universal session tenancy predicate shape changed: % (% matches)',
130
+ rewrite.signature, occurrences USING ERRCODE = '55000';
131
+ END IF;
132
+ EXECUTE pg_catalog.replace(definition, rewrite.old_fragment, rewrite.new_fragment);
133
+ END LOOP;
134
+
135
+ -- Fail closed if any other routine still consults the receipt table. Only
136
+ -- the retained, now-dormant receipt writers (operator, greenfield and
137
+ -- additional-organization activation, plus the operator preference
138
+ -- enabler's lock) and the private legacy-lane retirement predicate may.
139
+ -- That predicate is not a product prerequisite: it closes the legacy_user
140
+ -- connection and unattributed-writer compatibility lanes only for an
141
+ -- organization that already holds a receipt.
142
+ IF EXISTS (
143
+ SELECT 1 FROM pg_catalog.pg_proc routine
144
+ JOIN pg_catalog.pg_namespace namespace ON namespace.oid = routine.pronamespace
145
+ WHERE namespace.nspname IN (data_schema, 'opengeni_private')
146
+ AND routine.prosrc LIKE '%session_tenancy_activations%'
147
+ AND routine.proname NOT IN (
148
+ 'activate_session_tenancy_product',
149
+ 'activate_greenfield_session_tenancy_from_setup',
150
+ 'activate_session_tenancy_from_additional_organization',
151
+ 'enable_organization_private_sessions_from_activation',
152
+ 'session_tenancy_account_activated'
153
+ )
154
+ ) THEN
155
+ RAISE EXCEPTION 'a runtime routine still requires a session tenancy activation receipt'
156
+ USING ERRCODE = '55000';
157
+ END IF;
158
+ END
159
+ $universal_session_tenancy_predicates$;
160
+
161
+ COMMENT ON FUNCTION session_tenancy_product_activated(uuid, integer) IS
162
+ 'Universal since 0611: true for the transaction''s own organization at activation version 1. No per-organization receipt is required.';
163
+ COMMENT ON FUNCTION opengeni_private.session_tenancy_account_activated(uuid) IS
164
+ 'Legacy-lane retirement predicate (0340): true when the transaction''s own organization holds a receipt. Since 0611 it gates no product surface; it only closes the legacy_user connection and unattributed-writer lanes.';
165
+ COMMENT ON FUNCTION session_tenancy_any_product_activation() IS
166
+ 'Retired by 0611 with OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED. Always false so no binary enforces the removed startup interlock.';
@@ -0,0 +1,21 @@
1
+ -- deployment-mode: rolling
2
+ -- A scheduled occurrence whose model is retired or no longer in the catalog is
3
+ -- refused as a visible terminal run (`scheduled_model_unavailable`) instead of
4
+ -- exhausting activity retries and leaving no run. Preserve the immutable 0539
5
+ -- refusal lifecycle (as extended by 0552), adding only this reason. Exact
6
+ -- anchoring refuses an unexpected deployed guard definition. Old binaries never
7
+ -- write the new reason, so this is rolling-safe.
8
+ SET LOCAL lock_timeout = '5s';
9
+ SET LOCAL statement_timeout = '10min';
10
+
11
+ DO $scheduled_model_refusal$
12
+ DECLARE
13
+ definition text:=pg_get_functiondef('opengeni_private.guard_scheduled_admission_refusal()'::regprocedure);
14
+ anchor text:='''monthly_agent_run_limit'', ''allowance_exhausted'')';
15
+ BEGIN
16
+ IF strpos(definition,anchor)=0 OR strpos(substr(definition,strpos(definition,anchor)+length(anchor)),anchor)>0 THEN
17
+ RAISE EXCEPTION '0612 unexpected scheduled refusal guard definition' USING ERRCODE='55000';
18
+ END IF;
19
+ EXECUTE replace(definition,anchor,
20
+ '''monthly_agent_run_limit'', ''allowance_exhausted'', ''scheduled_model_unavailable'')');
21
+ END $scheduled_model_refusal$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "1.0.2",
3
+ "version": "1.1.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": {
@@ -115,12 +115,12 @@
115
115
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
116
116
  },
117
117
  "dependencies": {
118
- "@opengeni/codemode": "1.0.2",
119
- "@opengeni/codex": "1.0.2",
120
- "@opengeni/config": "1.0.2",
121
- "@opengeni/contracts": "1.0.2",
122
- "@opengeni/network": "1.0.2",
123
- "@opengeni/observability": "1.0.2",
118
+ "@opengeni/codemode": "1.1.0",
119
+ "@opengeni/codex": "1.1.0",
120
+ "@opengeni/config": "1.1.0",
121
+ "@opengeni/contracts": "1.1.0",
122
+ "@opengeni/network": "1.1.0",
123
+ "@opengeni/observability": "1.1.0",
124
124
  "drizzle-orm": "^0.45.2",
125
125
  "postgres": "^3.4.7"
126
126
  },
@@ -2,7 +2,13 @@ import type { ArtifactCatalogKind, ArtifactCatalogListQuery } from "@opengeni/co
2
2
  import { sql, type SQL } from "drizzle-orm";
3
3
  import { withRlsContext, type Database } from "./database";
4
4
 
5
- export type ArtifactCatalogPosition = { key: string; kind: ArtifactCatalogKind; id: string };
5
+ export type ArtifactCatalogPosition = {
6
+ key: string;
7
+ kind: ArtifactCatalogKind;
8
+ id: string;
9
+ /** Legacy cursor frontiers did not carry this bit. */
10
+ pinned?: boolean | undefined;
11
+ };
6
12
  /** Internal candidates are NOT an authorized public projection. */
7
13
  export type ArtifactCatalogCandidate = {
8
14
  id: string;
@@ -16,8 +22,22 @@ export type ArtifactCatalogCandidate = {
16
22
  source_session_id: string | null;
17
23
  version_id: string | null;
18
24
  sort_key: string;
25
+ pinned?: boolean;
19
26
  };
20
27
 
28
+ /** API callers must first verify publish permission and domain-specific read authority. */
29
+ export async function updateArtifactPin(
30
+ db: Database,
31
+ scope: { accountId: string; workspaceId: string },
32
+ input: { kind: ArtifactCatalogKind; artifactId: string; pinned: boolean },
33
+ ): Promise<void> {
34
+ await withRlsContext(db, scope, async (tx) => {
35
+ await tx.execute(sql`select opengeni_private.update_artifact_pin(
36
+ ${scope.accountId}::uuid, ${scope.workspaceId}::uuid,
37
+ ${input.kind}::text, ${input.artifactId}::text, ${input.pinned}::boolean)`);
38
+ });
39
+ }
40
+
21
41
  /** Called only after immutable bytes have been retained by explicit publication. */
22
42
  export async function recordSandboxFilePublication(
23
43
  db: Database,
@@ -84,7 +104,7 @@ export async function listArtifactCatalogCandidates(
84
104
  branches.push(sql`
85
105
  SELECT p.file_id::text AS id, p.kind, p.title, 'active'::text AS status,
86
106
  p.published_at AS created_at, p.published_at AS updated_at, p.source_session_id, NULL::text AS version_id, 'sandbox_file'::text AS origin
87
- FROM opengeni_private.list_sandbox_file_publications(${scope.accountId}::uuid, ${scope.workspaceId}::uuid,
107
+ FROM opengeni_private.list_sandbox_file_publications_pinned(${scope.accountId}::uuid, ${scope.workspaceId}::uuid,
88
108
  ${JSON.stringify({ sourceSessionId: source, q: query.q, kinds: publishedKinds, sort: query.sort, snapshotAt: input.snapshotAt, after: input.after, limit: input.limit })}::jsonb) p`);
89
109
  if (!branches.length) return [];
90
110
  const sort =
@@ -95,22 +115,28 @@ export async function listArtifactCatalogCandidates(
95
115
  : sql`to_char(updated_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US')`;
96
116
  const descending = query.sort !== "title";
97
117
  const position = input.after;
98
- const keyset = !position
118
+ const secondaryKeyset = !position
99
119
  ? sql`true`
100
120
  : sql`(
101
121
  sort_key COLLATE "C" ${descending ? sql`<` : sql`>`} ${position.key} COLLATE "C"
102
122
  OR (sort_key = ${position.key} AND (kind COLLATE "C", id COLLATE "C") > (${position.kind}, ${position.id})))`;
123
+ const keyset = !position
124
+ ? sql`true`
125
+ : sql`(pinned < ${position.pinned ?? false}::boolean OR
126
+ (pinned = ${position.pinned ?? false}::boolean AND ${secondaryKeyset}))`;
103
127
  return withRlsContext(
104
128
  db,
105
129
  scope,
106
130
  async (tx) => {
107
131
  const result = await tx.execute(sql`
108
132
  WITH candidates AS (${sql.join(branches, sql` UNION ALL `)}),
109
- ordered AS (SELECT *, ${sort} AS sort_key FROM candidates
133
+ ordered AS (SELECT candidates.*, ${sort} AS sort_key, (pins.artifact_id IS NOT NULL) AS pinned
134
+ FROM candidates LEFT JOIN opengeni_private.list_artifact_pins(${scope.accountId}::uuid, ${scope.workspaceId}::uuid) pins
135
+ ON pins.kind = candidates.kind AND pins.artifact_id = candidates.id
110
136
  WHERE status = ${query.status} AND created_at <= ${input.snapshotAt}::timestamptz
111
137
  ${query.q ? sql`AND strpos(lower(title), lower(${query.q})) > 0` : sql``})
112
138
  SELECT * FROM ordered WHERE ${keyset}
113
- ORDER BY sort_key COLLATE "C" ${descending ? sql`DESC` : sql`ASC`}, kind COLLATE "C", id COLLATE "C"
139
+ ORDER BY pinned DESC, sort_key COLLATE "C" ${descending ? sql`DESC` : sql`ASC`}, kind COLLATE "C", id COLLATE "C"
114
140
  LIMIT ${input.limit}`);
115
141
  return (
116
142
  Array.isArray(result) ? result : (result as { rows: ArtifactCatalogCandidate[] }).rows
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  } from "./inbox-execution-context";
7
7
  import { parentOutboxAuthorityTx } from "./child-outbox-authority";
8
8
  export { SubscriptionAccountChangedError } from "./subscription-account-conflict";
9
+ import type { GoalAdmissionPausedReason } from "@opengeni/contracts";
9
10
  import {
10
11
  claudeSubscriptionAccountRepository,
11
12
  claudeSubscriptionTables,
@@ -439,7 +440,6 @@ import {
439
440
  closePrivateSessionCreateCapability,
440
441
  openPrivateChildSessionCreateCapability,
441
442
  openPrivateSessionCreateCapability,
442
- sessionTenancyProductActivated,
443
443
  } from "./session-tenancy";
444
444
  import {
445
445
  completeCodemodeOperationInTransaction,
@@ -18575,6 +18575,7 @@ export type ScheduledTaskAdmissionRefusalReason =
18575
18575
  | "machine_enrollment_inactive"
18576
18576
  | "variable_set_unavailable"
18577
18577
  | "rig_version_unavailable"
18578
+ | "scheduled_model_unavailable"
18578
18579
  | "insufficient_credits"
18579
18580
  | "allowance_exhausted"
18580
18581
  | "monthly_model_cost_limit"
@@ -37685,11 +37686,6 @@ async function readSessionListForSubject(
37685
37686
  throw new SessionListAccessError();
37686
37687
  }
37687
37688
 
37688
- const tenancyActivated = await sessionTenancyProductActivated(
37689
- tx as unknown as Database,
37690
- workspaceId,
37691
- );
37692
-
37693
37689
  const archiveMode = options.archiveStatus ?? (options.archivedOnly ? "archived" : "active");
37694
37690
  const sortBy = options.sortBy ?? (archiveMode === "archived" ? "archivedAt" : "updatedAt");
37695
37691
  if (options.archivedOnly && archiveMode !== "archived") {
@@ -38139,7 +38135,7 @@ async function readSessionListForSubject(
38139
38135
  ? { archivedAt: exactArchiveTimestamps.get(session.id)! }
38140
38136
  : {}),
38141
38137
  },
38142
- { subjectId: options.subjectId, activated: tenancyActivated },
38138
+ { subjectId: options.subjectId },
38143
38139
  ),
38144
38140
  ...(sortBy === "updatedAt" && exactOrdinaryTimestamps.has(session.id)
38145
38141
  ? { updatedAt: exactOrdinaryTimestamps.get(session.id)! }
@@ -38395,7 +38391,6 @@ export async function getSessionForSubject(
38395
38391
  const mcpServers = await sessionMcpServerMetadataForSessions(scopedDb, workspaceId, [
38396
38392
  sessionId,
38397
38393
  ]);
38398
- const tenancyActivated = await sessionTenancyProductActivated(scopedDb, workspaceId);
38399
38394
  const failureDiagnostics = await sessionFailureDiagnostics(scopedDb, workspaceId, session);
38400
38395
  return projectSessionForRelatedAccess(
38401
38396
  {
@@ -38407,7 +38402,7 @@ export async function getSessionForSubject(
38407
38402
  mapSessionAttention(session, row.pin),
38408
38403
  mapSessionArchive(row.pin),
38409
38404
  undefined,
38410
- { subjectId, activated: tenancyActivated },
38405
+ { subjectId },
38411
38406
  )),
38412
38407
  failureDiagnostics,
38413
38408
  },
@@ -44562,6 +44557,19 @@ export async function applyContextCompaction(
44562
44557
  ),
44563
44558
  );
44564
44559
  const supersededFrom = Math.floor(Number(maxPosition)) + 1;
44560
+ // Installing a successful summary is model progress, including a
44561
+ // maintenance turn that never enters the ordinary response stream.
44562
+ // Clear only provider recovery under the same locked attempt fence.
44563
+ if (providerRecoveryCountFromTurnMetadata(fence.turn!.metadata) > 0) {
44564
+ await tx
44565
+ .update(schema.sessionTurns)
44566
+ .set({
44567
+ metadata: metadataWithoutProviderRecoveryCount(fence.turn!.metadata),
44568
+ version: fence.turn!.version + 1,
44569
+ updatedAt: new Date(),
44570
+ })
44571
+ .where(eq(schema.sessionTurns.id, input.turnId));
44572
+ }
44565
44573
  await tx
44566
44574
  .update(schema.sessionHistoryItems)
44567
44575
  .set({ active: false })
@@ -68341,6 +68349,16 @@ export async function updateSessionTitleWithEvent(
68341
68349
  );
68342
68350
  }
68343
68351
 
68352
+ /** Read-only admission at a locked paused-to-active transition. */
68353
+ export type SessionGoalResumeValidation = (
68354
+ tx: Database,
68355
+ session: Pick<
68356
+ Session,
68357
+ "accountId" | "workspaceId" | "model" | "codexCompactionMode" | "latencyMode"
68358
+ >,
68359
+ causalTurn: { id: string; initiatingHumanSubjectId: string | null } | null,
68360
+ ) => Promise<void>;
68361
+
68344
68362
  /**
68345
68363
  * Status transition helper. Idempotent: requesting the current status returns
68346
68364
  * `changed: false` so callers can skip emitting a duplicate event. `completed`
@@ -68360,6 +68378,8 @@ export async function setSessionGoalStatus(
68360
68378
  reportDeliveries?: SessionGoalReportDelivery[];
68361
68379
  reportArtifactActor?: ReportArtifactActor;
68362
68380
  commandActor?: Extract<SessionCommandActor, { type: "agent_attempt" }>;
68381
+ /** Runs under the canonical control/session/goal locks, before a paused goal changes. */
68382
+ beforeResume?: SessionGoalResumeValidation;
68363
68383
  },
68364
68384
  ): Promise<{
68365
68385
  goal: SessionGoal;
@@ -68409,6 +68429,51 @@ export async function setSessionGoalStatus(
68409
68429
  if (existing.status === "completed") {
68410
68430
  throw new Error("session goal is completed; set a new goal to continue");
68411
68431
  }
68432
+ if (input.status === "active" && input.beforeResume) {
68433
+ const [effectiveSession] = await withEffectiveSessionPolicy(scopedDb, workspaceId, [session]);
68434
+ // An accepted running turn owns the work the goal will continue after it
68435
+ // settles. An idle Resume uses the exact latest-finished causal row.
68436
+ const causalTurnFilter = session.activeTurnId
68437
+ ? or(
68438
+ eq(schema.sessionTurns.id, session.activeTurnId),
68439
+ sql`${schema.sessionTurns.finishedAt} is not null`,
68440
+ )
68441
+ : sql`${schema.sessionTurns.finishedAt} is not null`;
68442
+ const [causalTurn] = await scopedDb
68443
+ .select({
68444
+ id: schema.sessionTurns.id,
68445
+ initiatingHumanSubjectId: schema.sessionTurns.initiatingHumanSubjectId,
68446
+ })
68447
+ .from(schema.sessionTurns)
68448
+ .where(
68449
+ and(
68450
+ eq(schema.sessionTurns.workspaceId, workspaceId),
68451
+ eq(schema.sessionTurns.sessionId, sessionId),
68452
+ causalTurnFilter,
68453
+ ),
68454
+ )
68455
+ .orderBy(
68456
+ ...(session.activeTurnId
68457
+ ? [desc(sql`${schema.sessionTurns.id} = ${session.activeTurnId}`)]
68458
+ : []),
68459
+ desc(schema.sessionTurns.finishedAt),
68460
+ desc(schema.sessionTurns.position),
68461
+ desc(schema.sessionTurns.createdAt),
68462
+ desc(schema.sessionTurns.id),
68463
+ )
68464
+ .limit(1);
68465
+ await input.beforeResume(
68466
+ scopedDb,
68467
+ {
68468
+ accountId: session.accountId,
68469
+ workspaceId,
68470
+ model: effectiveSession!.model,
68471
+ latencyMode: effectiveSession!.latencyMode as Session["latencyMode"],
68472
+ codexCompactionMode: session.codexCompactionMode as Session["codexCompactionMode"],
68473
+ },
68474
+ causalTurn ?? null,
68475
+ );
68476
+ }
68412
68477
  if (input.status === "completed") {
68413
68478
  await verifyGoalReportDeliveries(scopedDb, {
68414
68479
  accountId: session.accountId,
@@ -68502,6 +68567,7 @@ export async function setSessionGoalStatusWithEvent(
68502
68567
  reportDeliveries?: SessionGoalReportDelivery[];
68503
68568
  reportArtifactActor?: ReportArtifactActor;
68504
68569
  commandActor?: Extract<SessionCommandActor, { type: "agent_attempt" }>;
68570
+ beforeResume?: SessionGoalResumeValidation;
68505
68571
  event: SetSessionGoalStatusEvent;
68506
68572
  },
68507
68573
  ): Promise<{
@@ -68535,6 +68601,7 @@ export async function setSessionGoalStatusWithEvent(
68535
68601
  : {}),
68536
68602
  ...(input.reportArtifactActor ? { reportArtifactActor: input.reportArtifactActor } : {}),
68537
68603
  ...(input.commandActor ? { commandActor: input.commandActor } : {}),
68604
+ ...(input.beforeResume ? { beforeResume: input.beforeResume } : {}),
68538
68605
  ...(input.evidence !== undefined ? { evidence: input.evidence } : {}),
68539
68606
  ...(input.rationale !== undefined ? { rationale: input.rationale } : {}),
68540
68607
  ...(input.pausedReason !== undefined ? { pausedReason: input.pausedReason } : {}),
@@ -68633,7 +68700,7 @@ export type GoalContinuationDecision =
68633
68700
  | { decision: "queue" }
68634
68701
  | {
68635
68702
  decision: "paused";
68636
- reason: "max_auto_continuations" | "limits" | "allowance";
68703
+ reason: "max_auto_continuations" | "limits" | GoalAdmissionPausedReason;
68637
68704
  goal: SessionGoal;
68638
68705
  }
68639
68706
  | {
@@ -68712,7 +68779,7 @@ export async function evaluateGoalContinuation(
68712
68779
  // decision (before the counter bump) so a budget pause never consumes
68713
68780
  // continuation budget.
68714
68781
  budgetBlocked?: string | null;
68715
- budgetPausedReason?: "limits" | "allowance" | undefined;
68782
+ budgetPausedReason?: "limits" | GoalAdmissionPausedReason | undefined;
68716
68783
  },
68717
68784
  ): Promise<GoalContinuationDecision> {
68718
68785
  return await withWorkspaceRls(
@@ -68974,7 +69041,7 @@ export async function materializeGoalContinuation(
68974
69041
  workflowId: string;
68975
69042
  defaultMaxAutoContinuations?: number | null;
68976
69043
  budgetBlocked?: string | null;
68977
- budgetPausedReason?: "limits" | "allowance" | undefined;
69044
+ budgetPausedReason?: "limits" | GoalAdmissionPausedReason | undefined;
68978
69045
  /** Trusted worker admission, evaluated under the same session/goal locks
68979
69046
  * as lineage materialization. Never substitutes a mutable latest human. */
68980
69047
  admission?: (
@@ -68982,7 +69049,7 @@ export async function materializeGoalContinuation(
68982
69049
  causalTurn: { id: string; initiatingHumanSubjectId: string | null } | null,
68983
69050
  ) => Promise<{
68984
69051
  budgetBlocked: string | null;
68985
- budgetPausedReason?: "limits" | "allowance";
69052
+ budgetPausedReason?: "limits" | GoalAdmissionPausedReason;
68986
69053
  }>;
68987
69054
  idleBackoff?: GoalIdleBackoffPolicy | null;
68988
69055
  policy: {
@@ -78873,6 +78940,8 @@ function metadataWithoutProviderRecoveryCount(
78873
78940
  ): Record<string, unknown> {
78874
78941
  const next = { ...(metadata ?? {}) };
78875
78942
  delete next.providerRecoveryCount;
78943
+ delete next.providerRecoveryStartedAt;
78944
+ delete next.providerRecoveryReason;
78876
78945
  return next;
78877
78946
  }
78878
78947
 
@@ -81315,7 +81384,15 @@ export async function requestSessionTurnRecovery(
81315
81384
  }
81316
81385
  : {}),
81317
81386
  ...(input.providerRecoveryCount !== undefined
81318
- ? { providerRecoveryCount: input.providerRecoveryCount }
81387
+ ? {
81388
+ providerRecoveryCount: input.providerRecoveryCount,
81389
+ providerRecoveryStartedAt:
81390
+ providerRecoveryCountFromTurnMetadata(turn.metadata) > 0 &&
81391
+ typeof turn.metadata?.providerRecoveryStartedAt === "string"
81392
+ ? turn.metadata.providerRecoveryStartedAt
81393
+ : now.toISOString(),
81394
+ providerRecoveryReason: input.reason,
81395
+ }
81319
81396
  : {}),
81320
81397
  },
81321
81398
  updatedAt: now,
@@ -86759,7 +86836,7 @@ async function mapSessionWithControl(
86759
86836
  },
86760
86837
  archive: Pick<Session, "archived" | "archivedAt" | "archiveVersion"> = mapSessionArchive(null),
86761
86838
  workspaceControl?: WorkspaceControlRow,
86762
- tenancyViewer?: { subjectId: string; activated: boolean },
86839
+ tenancyViewer?: { subjectId: string },
86763
86840
  ): Promise<Session> {
86764
86841
  const controls = await sessionControlProjections(db, row.workspaceId, [row.id], workspaceControl);
86765
86842
  const control = controls.get(row.id);
@@ -86896,7 +86973,7 @@ function mapSession(
86896
86973
  attentionVersion: 0,
86897
86974
  },
86898
86975
  archive: Pick<Session, "archived" | "archivedAt" | "archiveVersion"> = mapSessionArchive(null),
86899
- tenancyViewer?: { subjectId: string; activated: boolean },
86976
+ tenancyViewer?: { subjectId: string },
86900
86977
  ): Session {
86901
86978
  const bundledSkillIds = bundledSkillSelectionFromMetadata(row.metadata);
86902
86979
  return {
@@ -86930,9 +87007,7 @@ function mapSession(
86930
87007
  toolPolicyVersion: Number(row.toolPolicyVersion),
86931
87008
  mcpApprovalPolicies: row.mcpApprovalPolicies,
86932
87009
  metadata: row.metadata,
86933
- ...(tenancyViewer?.activated
86934
- ? { tenancy: mapSessionTenancy(row, tenancyViewer.subjectId) }
86935
- : {}),
87010
+ ...(tenancyViewer ? { tenancy: mapSessionTenancy(row, tenancyViewer.subjectId) } : {}),
86936
87011
  createdBy: initiatorFromStorage(
86937
87012
  row.createdByKind,
86938
87013
  row.createdBySubjectId,
@@ -2163,6 +2163,22 @@ BEGIN
2163
2163
  REVOKE ALL ON FUNCTION opengeni_private.record_sandbox_file_publication(uuid,uuid,uuid,uuid) FROM PUBLIC;
2164
2164
  REVOKE ALL ON FUNCTION opengeni_private.list_sandbox_file_publications(uuid,uuid,jsonb) FROM PUBLIC;
2165
2165
  END IF;
2166
+ IF to_regclass('opengeni_private.artifact_catalog_pins') IS NOT NULL THEN
2167
+ EXECUTE format('REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM %I', ${literal(role)});
2168
+ -- Table revocation does not remove column ACLs. Reconcile every current
2169
+ -- column for both the runtime and PUBLIC, including future additions.
2170
+ EXECUTE format('REVOKE ALL (%s) ON TABLE opengeni_private.artifact_catalog_pins FROM %I',
2171
+ (SELECT string_agg(quote_ident(attname),',' ORDER BY attnum) FROM pg_attribute
2172
+ WHERE attrelid='opengeni_private.artifact_catalog_pins'::regclass AND attnum>0 AND NOT attisdropped),
2173
+ ${literal(role)});
2174
+ REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM PUBLIC;
2175
+ EXECUTE format('REVOKE ALL (%s) ON TABLE opengeni_private.artifact_catalog_pins FROM PUBLIC',
2176
+ (SELECT string_agg(quote_ident(attname),',' ORDER BY attnum) FROM pg_attribute
2177
+ WHERE attrelid='opengeni_private.artifact_catalog_pins'::regclass AND attnum>0 AND NOT attisdropped));
2178
+ REVOKE ALL ON FUNCTION opengeni_private.update_artifact_pin(uuid,uuid,text,text,boolean),
2179
+ opengeni_private.list_artifact_pins(uuid,uuid),
2180
+ opengeni_private.list_sandbox_file_publications_pinned(uuid,uuid,jsonb) FROM PUBLIC;
2181
+ END IF;
2166
2182
  IF to_regclass('opengeni_private.slack_file_upload_operations') IS NOT NULL THEN
2167
2183
  -- Ordinary RLS repositories own the upload CAS, not owner capabilities.
2168
2184
  -- Never allow deletion/truncation to erase its durable completion fence.
@@ -25,8 +25,6 @@ try {
25
25
  rlsStrategy: settings.rlsStrategy,
26
26
  expectedRole: settings.runtimeDatabaseRole,
27
27
  targetSchema: settings.dbSchema.trim() || "public",
28
- organizationTenancyCanonicalActivationEnabled:
29
- settings.organizationTenancyCanonicalActivationEnabled,
30
28
  });
31
29
  // Structural evidence only: never print a connection string, secret, GUC, or
32
30
  // tenant row. The command is intended for release Jobs and audit artifacts.