@opengeni/db 3.4.0-canary.2 → 3.5.2-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.
- package/dist/canonical-human-identities.d.ts +20 -0
- package/dist/canonical-human-identities.js +7 -3
- package/dist/{chunk-C2AFP7SB.js → chunk-6NYKC5DD.js} +6507 -6040
- package/dist/chunk-6NYKC5DD.js.map +1 -0
- package/dist/{chunk-O6FTELKH.js → chunk-FJHEWYV5.js} +3 -3
- package/dist/{chunk-ZWRIPCTR.js → chunk-NRGUX3M5.js} +2 -2
- package/dist/{chunk-TRBOWUG3.js → chunk-ORKVXHL2.js} +2 -2
- package/dist/{chunk-PYMY3AJW.js → chunk-PERPE2UY.js} +172 -7
- package/dist/chunk-PERPE2UY.js.map +1 -0
- package/dist/chunk-TRCYF26N.js +299 -0
- package/dist/chunk-TRCYF26N.js.map +1 -0
- package/dist/{chunk-V3TRUXFJ.js → chunk-U7NKCX5E.js} +72 -19
- package/dist/chunk-U7NKCX5E.js.map +1 -0
- package/dist/{chunk-H7MXYG7R.js → chunk-X6T2P3L3.js} +2 -2
- package/dist/{chunk-HY3F5Q4E.js → chunk-Y44FKTYU.js} +3 -3
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +1698 -995
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-set-schema.d.ts +1399 -0
- package/dist/managed-auth-session-sets.d.ts +145 -0
- package/dist/managed-auth-session-sets.js +50 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/organization-recovery-schema.d.ts +2144 -0
- package/dist/organization-recovery.d.ts +186 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -3
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +37 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +5 -3
- package/dist/session-queue-commands.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/drizzle/0362_managed_auth_session_sets.sql +2347 -0
- package/drizzle/0363_organization_recovery_custody.sql +2418 -0
- package/drizzle/0364_workspace_learning_policy_snapshot_lock_order.sql +221 -0
- package/package.json +10 -6
- package/src/canonical-human-identities.ts +105 -27
- package/src/index.ts +420 -103
- package/src/managed-auth-session-set-schema.ts +176 -0
- package/src/managed-auth-session-sets.ts +430 -0
- package/src/organization-recovery-schema.ts +277 -0
- package/src/organization-recovery.ts +424 -0
- package/src/provision-roles.ts +49 -0
- package/src/runtime-posture.ts +132 -6
- package/src/schema.ts +2 -0
- package/src/session-control.ts +255 -16
- package/src/session-queue-commands.ts +10 -1
- package/src/session-tool-call-settlement.ts +6 -1
- package/dist/chunk-C2AFP7SB.js.map +0 -1
- package/dist/chunk-PYMY3AJW.js.map +0 -1
- package/dist/chunk-V3TRUXFJ.js.map +0 -1
- /package/dist/{chunk-O6FTELKH.js.map → chunk-FJHEWYV5.js.map} +0 -0
- /package/dist/{chunk-ZWRIPCTR.js.map → chunk-NRGUX3M5.js.map} +0 -0
- /package/dist/{chunk-TRBOWUG3.js.map → chunk-ORKVXHL2.js.map} +0 -0
- /package/dist/{chunk-H7MXYG7R.js.map → chunk-X6T2P3L3.js.map} +0 -0
- /package/dist/{chunk-HY3F5Q4E.js.map → chunk-Y44FKTYU.js.map} +0 -0
package/src/provision-roles.ts
CHANGED
|
@@ -533,6 +533,31 @@ async function grantAppRoleIfSchemaExists(
|
|
|
533
533
|
]
|
|
534
534
|
.map(literal)
|
|
535
535
|
.join(", ")}]`;
|
|
536
|
+
const managedAuthSessionSetRoutines = `ARRAY[${[
|
|
537
|
+
"get_canonical_human_exact_login_binding(text,text)",
|
|
538
|
+
"managed_auth_session_set_authority_state(text)",
|
|
539
|
+
"managed_auth_session_set_snapshot(text,text,boolean,boolean,boolean)",
|
|
540
|
+
"managed_auth_session_set_bootstrap(text,text,text,text,uuid,text,bigint,bigint)",
|
|
541
|
+
"managed_auth_session_set_begin_transaction(text,text,text,uuid,text,bigint,uuid,bigint,text,text,uuid,uuid,text,timestamp with time zone)",
|
|
542
|
+
"managed_auth_session_set_complete_transaction(text,text,uuid,text,bigint,bigint,uuid,text,text,text)",
|
|
543
|
+
"managed_auth_session_set_mutate(text,text,uuid,text,bigint,bigint,text,uuid,uuid,uuid,text,text)",
|
|
544
|
+
"managed_auth_actor_mutation_fence(text,bigint,uuid)",
|
|
545
|
+
"managed_auth_actor_mutation_lease_acquire(text,bigint,uuid,integer)",
|
|
546
|
+
"managed_auth_actor_mutation_lease_release(text,uuid)",
|
|
547
|
+
"managed_auth_actor_mutation_lease_validate(text,bigint,uuid)",
|
|
548
|
+
"managed_auth_adopted_session_snapshot(text)",
|
|
549
|
+
"managed_auth_isolated_session_reap(integer)",
|
|
550
|
+
"managed_auth_expired_session_set_reap(integer)",
|
|
551
|
+
"managed_auth_session_set_operation_receipt(text,uuid,text,text)",
|
|
552
|
+
]
|
|
553
|
+
.map(literal)
|
|
554
|
+
.join(", ")}]`;
|
|
555
|
+
const organizationRecoveryRoutines = `ARRAY[${[
|
|
556
|
+
"get_organization_recovery_overview(uuid,text,jsonb,text,text)",
|
|
557
|
+
"organization_recovery_command(jsonb)",
|
|
558
|
+
]
|
|
559
|
+
.map(literal)
|
|
560
|
+
.join(", ")}]`;
|
|
536
561
|
await sql.unsafe(`
|
|
537
562
|
DO $$
|
|
538
563
|
DECLARE
|
|
@@ -1226,6 +1251,30 @@ BEGIN
|
|
|
1226
1251
|
${literal(role)}
|
|
1227
1252
|
);
|
|
1228
1253
|
END IF;
|
|
1254
|
+
FOREACH routine_signature IN ARRAY ${managedAuthSessionSetRoutines} LOOP
|
|
1255
|
+
IF to_regprocedure(format('%I.%s', ${literal(schema)}, routine_signature)) IS NOT NULL THEN
|
|
1256
|
+
EXECUTE format(
|
|
1257
|
+
'REVOKE ALL ON FUNCTION %I.%s FROM PUBLIC',
|
|
1258
|
+
${literal(schema)}, routine_signature
|
|
1259
|
+
);
|
|
1260
|
+
EXECUTE format(
|
|
1261
|
+
'GRANT EXECUTE ON FUNCTION %I.%s TO %I',
|
|
1262
|
+
${literal(schema)}, routine_signature, ${literal(role)}
|
|
1263
|
+
);
|
|
1264
|
+
END IF;
|
|
1265
|
+
END LOOP;
|
|
1266
|
+
FOREACH routine_signature IN ARRAY ${organizationRecoveryRoutines} LOOP
|
|
1267
|
+
IF to_regprocedure(format('%I.%s', ${literal(schema)}, routine_signature)) IS NOT NULL THEN
|
|
1268
|
+
EXECUTE format(
|
|
1269
|
+
'REVOKE ALL ON FUNCTION %I.%s FROM PUBLIC',
|
|
1270
|
+
${literal(schema)}, routine_signature
|
|
1271
|
+
);
|
|
1272
|
+
EXECUTE format(
|
|
1273
|
+
'GRANT EXECUTE ON FUNCTION %I.%s TO %I',
|
|
1274
|
+
${literal(schema)}, routine_signature, ${literal(role)}
|
|
1275
|
+
);
|
|
1276
|
+
END IF;
|
|
1277
|
+
END LOOP;
|
|
1229
1278
|
-- Migration 0225 creates these target-schema-local session visibility
|
|
1230
1279
|
-- capabilities before opengeni_app may exist. Re-converge their exact
|
|
1231
1280
|
-- EXECUTE grants for migrate-then-provision installs without granting
|
package/src/runtime-posture.ts
CHANGED
|
@@ -324,6 +324,49 @@ const CANONICAL_HUMAN_IDENTITY_AUTHORITY_TABLES = [
|
|
|
324
324
|
"canonical_human_login_bindings",
|
|
325
325
|
"canonical_human_identity_operations",
|
|
326
326
|
] as const;
|
|
327
|
+
const MANAGED_AUTH_SESSION_SET_ROUTINES = [
|
|
328
|
+
"get_canonical_human_exact_login_binding(text, text)",
|
|
329
|
+
"managed_auth_session_set_authority_state(text)",
|
|
330
|
+
"managed_auth_session_set_snapshot(text, text, boolean, boolean, boolean)",
|
|
331
|
+
"managed_auth_session_set_bootstrap(text, text, text, text, uuid, text, bigint, bigint)",
|
|
332
|
+
"managed_auth_session_set_begin_transaction(text, text, text, uuid, text, bigint, uuid, bigint, text, text, uuid, uuid, text, timestamp with time zone)",
|
|
333
|
+
"managed_auth_session_set_complete_transaction(text, text, uuid, text, bigint, bigint, uuid, text, text, text)",
|
|
334
|
+
"managed_auth_session_set_mutate(text, text, uuid, text, bigint, bigint, text, uuid, uuid, uuid, text, text)",
|
|
335
|
+
"managed_auth_actor_mutation_fence(text, bigint, uuid)",
|
|
336
|
+
"managed_auth_actor_mutation_lease_acquire(text, bigint, uuid, integer)",
|
|
337
|
+
"managed_auth_actor_mutation_lease_release(text, uuid)",
|
|
338
|
+
"managed_auth_actor_mutation_lease_validate(text, bigint, uuid)",
|
|
339
|
+
"managed_auth_adopted_session_snapshot(text)",
|
|
340
|
+
"managed_auth_isolated_session_reap(integer)",
|
|
341
|
+
"managed_auth_expired_session_set_reap(integer)",
|
|
342
|
+
"managed_auth_session_set_operation_receipt(text, uuid, text, text)",
|
|
343
|
+
] as const;
|
|
344
|
+
const MANAGED_AUTH_SESSION_SET_AUTHORITY_TABLES = [
|
|
345
|
+
"managed_auth_actor_mutation_leases",
|
|
346
|
+
"managed_auth_browser_installations",
|
|
347
|
+
"managed_auth_session_sets",
|
|
348
|
+
"managed_auth_login_slots",
|
|
349
|
+
"managed_auth_login_return_intents",
|
|
350
|
+
"managed_auth_login_transaction_rate_limits",
|
|
351
|
+
"managed_auth_login_transactions",
|
|
352
|
+
"managed_auth_session_set_operations",
|
|
353
|
+
] as const;
|
|
354
|
+
const ORGANIZATION_RECOVERY_ROUTINES = [
|
|
355
|
+
"get_organization_recovery_overview(uuid, text, jsonb, text, text)",
|
|
356
|
+
"organization_recovery_command(jsonb)",
|
|
357
|
+
] as const;
|
|
358
|
+
const ORGANIZATION_RECOVERY_AUTHORITY_TABLES = [
|
|
359
|
+
"organization_recovery_approvals",
|
|
360
|
+
"organization_recovery_command_receipts",
|
|
361
|
+
"organization_recovery_custodian_acceptances",
|
|
362
|
+
"organization_recovery_custodians",
|
|
363
|
+
"organization_recovery_events",
|
|
364
|
+
"organization_recovery_notification_attempts",
|
|
365
|
+
"organization_recovery_notification_outbox",
|
|
366
|
+
"organization_recovery_operations",
|
|
367
|
+
"organization_recovery_policies",
|
|
368
|
+
"organization_recovery_policy_heads",
|
|
369
|
+
] as const;
|
|
327
370
|
const SESSION_PRIVATE_ACTOR_VISIBLE_ROUTINE =
|
|
328
371
|
"session_private_actor_visible(uuid, uuid, uuid, text)";
|
|
329
372
|
const SESSION_REFERENCE_VISIBLE_ROUTINE = "session_reference_visible(uuid, uuid, uuid)";
|
|
@@ -511,6 +554,8 @@ export const RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES = [
|
|
|
511
554
|
...SCOPED_COMPUTE_AUTHORITY_ROUTINES,
|
|
512
555
|
...SCHEDULED_PERSONAL_RESOURCE_ROUTINES,
|
|
513
556
|
...CANONICAL_HUMAN_IDENTITY_ROUTINES,
|
|
557
|
+
...MANAGED_AUTH_SESSION_SET_ROUTINES,
|
|
558
|
+
...ORGANIZATION_RECOVERY_ROUTINES,
|
|
514
559
|
...TASK_NOTE_CAPABILITY_ROUTINES,
|
|
515
560
|
SESSION_PRIVATE_ACTOR_VISIBLE_ROUTINE,
|
|
516
561
|
SESSION_REFERENCE_VISIBLE_ROUTINE,
|
|
@@ -702,6 +747,14 @@ export const FORCE_RLS_TABLES = [
|
|
|
702
747
|
"machine_metrics_latest",
|
|
703
748
|
"machine_metrics_series",
|
|
704
749
|
"machine_removal_operations",
|
|
750
|
+
"managed_auth_actor_mutation_leases",
|
|
751
|
+
"managed_auth_browser_installations",
|
|
752
|
+
"managed_auth_login_return_intents",
|
|
753
|
+
"managed_auth_login_slots",
|
|
754
|
+
"managed_auth_login_transaction_rate_limits",
|
|
755
|
+
"managed_auth_login_transactions",
|
|
756
|
+
"managed_auth_session_set_operations",
|
|
757
|
+
"managed_auth_session_sets",
|
|
705
758
|
"memory_slack_publication_configurations",
|
|
706
759
|
"memory_slack_publication_receipts",
|
|
707
760
|
"memory_slack_publications",
|
|
@@ -716,6 +769,16 @@ export const FORCE_RLS_TABLES = [
|
|
|
716
769
|
"organization_private_session_setting_events",
|
|
717
770
|
"organization_private_session_settings",
|
|
718
771
|
"organization_profile_events",
|
|
772
|
+
"organization_recovery_approvals",
|
|
773
|
+
"organization_recovery_command_receipts",
|
|
774
|
+
"organization_recovery_custodian_acceptances",
|
|
775
|
+
"organization_recovery_custodians",
|
|
776
|
+
"organization_recovery_events",
|
|
777
|
+
"organization_recovery_notification_attempts",
|
|
778
|
+
"organization_recovery_notification_outbox",
|
|
779
|
+
"organization_recovery_operations",
|
|
780
|
+
"organization_recovery_policies",
|
|
781
|
+
"organization_recovery_policy_heads",
|
|
719
782
|
"organization_shared_workspace_administration_capabilities",
|
|
720
783
|
"organization_user_resource_authorities",
|
|
721
784
|
"organization_user_resource_grants",
|
|
@@ -1202,6 +1265,14 @@ export const PROTECTED_NO_DIRECT_DML_TABLES = [
|
|
|
1202
1265
|
"host_export_cursor_state",
|
|
1203
1266
|
"host_export_dead_letters",
|
|
1204
1267
|
"host_export_outbox",
|
|
1268
|
+
"managed_auth_actor_mutation_leases",
|
|
1269
|
+
"managed_auth_browser_installations",
|
|
1270
|
+
"managed_auth_login_return_intents",
|
|
1271
|
+
"managed_auth_login_slots",
|
|
1272
|
+
"managed_auth_login_transaction_rate_limits",
|
|
1273
|
+
"managed_auth_login_transactions",
|
|
1274
|
+
"managed_auth_session_set_operations",
|
|
1275
|
+
"managed_auth_session_sets",
|
|
1205
1276
|
"organization_invitation_binding_events",
|
|
1206
1277
|
"organization_membership_invitations",
|
|
1207
1278
|
"organization_membership_lifecycle_events",
|
|
@@ -1210,6 +1281,16 @@ export const PROTECTED_NO_DIRECT_DML_TABLES = [
|
|
|
1210
1281
|
"organization_private_session_setting_events",
|
|
1211
1282
|
"organization_private_session_settings",
|
|
1212
1283
|
"organization_profile_events",
|
|
1284
|
+
"organization_recovery_approvals",
|
|
1285
|
+
"organization_recovery_command_receipts",
|
|
1286
|
+
"organization_recovery_custodian_acceptances",
|
|
1287
|
+
"organization_recovery_custodians",
|
|
1288
|
+
"organization_recovery_events",
|
|
1289
|
+
"organization_recovery_notification_attempts",
|
|
1290
|
+
"organization_recovery_notification_outbox",
|
|
1291
|
+
"organization_recovery_operations",
|
|
1292
|
+
"organization_recovery_policies",
|
|
1293
|
+
"organization_recovery_policy_heads",
|
|
1213
1294
|
"organization_shared_workspace_administration_capabilities",
|
|
1214
1295
|
"organization_user_resource_authorities",
|
|
1215
1296
|
"organization_user_resource_grants",
|
|
@@ -1301,6 +1382,8 @@ export type RuntimeDatabasePostureOptions = {
|
|
|
1301
1382
|
protectedTables?: readonly string[];
|
|
1302
1383
|
tablePrivileges?: RuntimeTablePrivilegeContract;
|
|
1303
1384
|
protectedNoDirectDmlTables?: readonly string[];
|
|
1385
|
+
targetSchemaCapabilityRoutines?: readonly string[];
|
|
1386
|
+
targetSchemaForbiddenRoutines?: readonly string[];
|
|
1304
1387
|
organizationTenancyCanonicalActivationEnabled?: boolean;
|
|
1305
1388
|
};
|
|
1306
1389
|
|
|
@@ -1436,6 +1519,10 @@ export async function inspectRuntimeDatabasePosture(
|
|
|
1436
1519
|
options: RuntimeDatabasePostureOptions,
|
|
1437
1520
|
): Promise<RuntimeDatabasePosture> {
|
|
1438
1521
|
const targetSchema = options.targetSchema?.trim() || "public";
|
|
1522
|
+
const targetSchemaCapabilityRoutines =
|
|
1523
|
+
options.targetSchemaCapabilityRoutines ?? RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES;
|
|
1524
|
+
const targetSchemaForbiddenRoutines =
|
|
1525
|
+
options.targetSchemaForbiddenRoutines ?? RUNTIME_TARGET_SCHEMA_FORBIDDEN_ROUTINES;
|
|
1439
1526
|
|
|
1440
1527
|
return await db.transaction(
|
|
1441
1528
|
async (tx) => {
|
|
@@ -1710,10 +1797,9 @@ export async function inspectRuntimeDatabasePosture(
|
|
|
1710
1797
|
and (p.proname || '(' || pg_catalog.oidvectortypes(p.proargtypes) || ')') = any(
|
|
1711
1798
|
array[
|
|
1712
1799
|
${sql.join(
|
|
1713
|
-
[
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
].map((name) => sql`${name}`),
|
|
1800
|
+
[...targetSchemaCapabilityRoutines, ...targetSchemaForbiddenRoutines].map(
|
|
1801
|
+
(name) => sql`${name}`,
|
|
1802
|
+
),
|
|
1717
1803
|
sql`, `,
|
|
1718
1804
|
)}
|
|
1719
1805
|
]::text[]
|
|
@@ -1826,6 +1912,10 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
1826
1912
|
options.protectedNoDirectDmlTables ??
|
|
1827
1913
|
(options.protectedTables ? [] : PROTECTED_NO_DIRECT_DML_TABLES),
|
|
1828
1914
|
);
|
|
1915
|
+
const targetSchemaCapabilityRoutines =
|
|
1916
|
+
options.targetSchemaCapabilityRoutines ?? RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES;
|
|
1917
|
+
const targetSchemaForbiddenRoutines =
|
|
1918
|
+
options.targetSchemaForbiddenRoutines ?? RUNTIME_TARGET_SCHEMA_FORBIDDEN_ROUTINES;
|
|
1829
1919
|
|
|
1830
1920
|
if (identity.currentUser !== expectedRole || identity.sessionUser !== expectedRole) {
|
|
1831
1921
|
violations.push(
|
|
@@ -1956,7 +2046,7 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
1956
2046
|
}
|
|
1957
2047
|
|
|
1958
2048
|
const targetSchemaOwner = posture.schemas.find((schema) => schema.name === targetSchema)?.owner;
|
|
1959
|
-
for (const forbiddenRoutine of
|
|
2049
|
+
for (const forbiddenRoutine of targetSchemaForbiddenRoutines) {
|
|
1960
2050
|
const matches = posture.targetRoutines.filter((routine) => routine.name === forbiddenRoutine);
|
|
1961
2051
|
if (matches.length !== 1) {
|
|
1962
2052
|
violations.push(
|
|
@@ -1983,7 +2073,7 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
1983
2073
|
violations.push(`PUBLIC has forbidden owner-internal helper ${routine.name}`);
|
|
1984
2074
|
}
|
|
1985
2075
|
}
|
|
1986
|
-
for (const expectedRoutine of
|
|
2076
|
+
for (const expectedRoutine of targetSchemaCapabilityRoutines) {
|
|
1987
2077
|
const matches = posture.targetRoutines.filter((routine) => routine.name === expectedRoutine);
|
|
1988
2078
|
if (matches.length !== 1) {
|
|
1989
2079
|
violations.push(
|
|
@@ -2252,6 +2342,42 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
2252
2342
|
);
|
|
2253
2343
|
}
|
|
2254
2344
|
}
|
|
2345
|
+
} else if ((ORGANIZATION_RECOVERY_ROUTINES as readonly string[]).includes(routine.name)) {
|
|
2346
|
+
const authorityTables = ORGANIZATION_RECOVERY_AUTHORITY_TABLES.filter((tableName) =>
|
|
2347
|
+
tableByName.has(tableName),
|
|
2348
|
+
).map((tableName) => tableByName.get(tableName)!);
|
|
2349
|
+
const authorityOwners = new Set(authorityTables.map((table) => table.owner));
|
|
2350
|
+
if (authorityTables.length !== ORGANIZATION_RECOVERY_AUTHORITY_TABLES.length) {
|
|
2351
|
+
violations.push(
|
|
2352
|
+
`target-schema runtime capability ${routine.name} organization recovery authority tables are missing`,
|
|
2353
|
+
);
|
|
2354
|
+
} else if (authorityOwners.size !== 1) {
|
|
2355
|
+
violations.push(
|
|
2356
|
+
`target-schema runtime capability ${routine.name} authority table owners do not match: ${authorityTables.map((table) => `${table.name}=${table.owner}`).join(", ")}`,
|
|
2357
|
+
);
|
|
2358
|
+
} else if (routine.owner !== authorityTables[0]!.owner) {
|
|
2359
|
+
violations.push(
|
|
2360
|
+
`target-schema runtime capability ${routine.name} owner ${routine.owner} does not match authority table owner ${authorityTables[0]!.owner}`,
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
} else if ((MANAGED_AUTH_SESSION_SET_ROUTINES as readonly string[]).includes(routine.name)) {
|
|
2364
|
+
const authorityTables = MANAGED_AUTH_SESSION_SET_AUTHORITY_TABLES.filter((tableName) =>
|
|
2365
|
+
tableByName.has(tableName),
|
|
2366
|
+
).map((tableName) => tableByName.get(tableName)!);
|
|
2367
|
+
const authorityOwners = new Set(authorityTables.map((table) => table.owner));
|
|
2368
|
+
if (authorityTables.length !== MANAGED_AUTH_SESSION_SET_AUTHORITY_TABLES.length) {
|
|
2369
|
+
violations.push(
|
|
2370
|
+
`target-schema runtime capability ${routine.name} managed auth session-set authority tables are missing`,
|
|
2371
|
+
);
|
|
2372
|
+
} else if (authorityOwners.size !== 1) {
|
|
2373
|
+
violations.push(
|
|
2374
|
+
`target-schema runtime capability ${routine.name} authority table owners do not match: ${authorityTables.map((table) => `${table.name}=${table.owner}`).join(", ")}`,
|
|
2375
|
+
);
|
|
2376
|
+
} else if (routine.owner !== authorityTables[0]!.owner) {
|
|
2377
|
+
violations.push(
|
|
2378
|
+
`target-schema runtime capability ${routine.name} owner ${routine.owner} does not match authority table owner ${authorityTables[0]!.owner}`,
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2255
2381
|
} else if ((CANONICAL_HUMAN_IDENTITY_ROUTINES as readonly string[]).includes(routine.name)) {
|
|
2256
2382
|
const authorityTables = CANONICAL_HUMAN_IDENTITY_AUTHORITY_TABLES.filter((tableName) =>
|
|
2257
2383
|
tableByName.has(tableName),
|
package/src/schema.ts
CHANGED
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
import { losslessCodecVersion, losslessJsonb, losslessText } from "./lossless-columns";
|
|
47
47
|
|
|
48
48
|
export * from "./editable-artifacts-schema";
|
|
49
|
+
export * from "./managed-auth-session-set-schema";
|
|
50
|
+
export * from "./organization-recovery-schema";
|
|
49
51
|
|
|
50
52
|
const vector = customType<{ data: number[]; driverData: string }>({
|
|
51
53
|
dataType() {
|
package/src/session-control.ts
CHANGED
|
@@ -2224,8 +2224,8 @@ export async function updateSessionCommandReceiptResult(
|
|
|
2224
2224
|
export type SessionControlMutationResult = {
|
|
2225
2225
|
receipt: SessionCommandReceiptRow;
|
|
2226
2226
|
control: EffectiveSessionControl;
|
|
2227
|
-
sessionControlEventId: string;
|
|
2228
|
-
workspaceControlEventId: string;
|
|
2227
|
+
sessionControlEventId: string | null;
|
|
2228
|
+
workspaceControlEventId: string | null;
|
|
2229
2229
|
interruptionCount: number;
|
|
2230
2230
|
backgroundCommandCount: number;
|
|
2231
2231
|
wakeCount: number;
|
|
@@ -2233,6 +2233,7 @@ export type SessionControlMutationResult = {
|
|
|
2233
2233
|
cancelledTurnCount: number;
|
|
2234
2234
|
affectedSessionEvents: Array<{ sessionId: string; eventIds: string[] }>;
|
|
2235
2235
|
workflowWake: SessionControlWorkflowWake | null;
|
|
2236
|
+
outcome: "changed" | "unchanged" | "replayed";
|
|
2236
2237
|
replay: boolean;
|
|
2237
2238
|
};
|
|
2238
2239
|
|
|
@@ -2278,6 +2279,115 @@ async function pendingSessionControlWorkflowWake(
|
|
|
2278
2279
|
};
|
|
2279
2280
|
}
|
|
2280
2281
|
|
|
2282
|
+
async function sessionPauseEffectsNeeded(
|
|
2283
|
+
db: Database,
|
|
2284
|
+
input: { workspaceId: string; sessionId: string; directPauseRevision: number },
|
|
2285
|
+
): Promise<boolean> {
|
|
2286
|
+
const rows = await db.execute<{ needed: boolean }>(sql`
|
|
2287
|
+
with recursive subtree(id, path, cycle) as (
|
|
2288
|
+
select session.id, array[session.id]::uuid[], false
|
|
2289
|
+
from ${schema.sessions} session
|
|
2290
|
+
where session.workspace_id = ${input.workspaceId}
|
|
2291
|
+
and session.id = ${input.sessionId}
|
|
2292
|
+
union all
|
|
2293
|
+
select child.id, parent.path || child.id, child.id = any(parent.path)
|
|
2294
|
+
from subtree parent
|
|
2295
|
+
join ${schema.sessions} child
|
|
2296
|
+
on child.workspace_id = ${input.workspaceId}
|
|
2297
|
+
and child.parent_session_id = parent.id
|
|
2298
|
+
where not parent.cycle
|
|
2299
|
+
and cardinality(parent.path) <= ${SESSION_ANCESTRY_LIMIT}
|
|
2300
|
+
), scoped_sessions as (
|
|
2301
|
+
select session.id, session.subtree_run_override_revision
|
|
2302
|
+
from subtree
|
|
2303
|
+
join ${schema.sessions} session
|
|
2304
|
+
on session.workspace_id = ${input.workspaceId}
|
|
2305
|
+
and session.id = subtree.id
|
|
2306
|
+
where not subtree.cycle
|
|
2307
|
+
)
|
|
2308
|
+
select exists (
|
|
2309
|
+
select 1
|
|
2310
|
+
from scoped_sessions session
|
|
2311
|
+
where session.subtree_run_override_revision > ${input.directPauseRevision}
|
|
2312
|
+
union all
|
|
2313
|
+
select 1
|
|
2314
|
+
from ${schema.sessionTurnAttempts} attempt
|
|
2315
|
+
join scoped_sessions session on session.id = attempt.session_id
|
|
2316
|
+
where attempt.workspace_id = ${input.workspaceId}
|
|
2317
|
+
and attempt.state in ('claimed', 'running')
|
|
2318
|
+
and not exists (
|
|
2319
|
+
select 1
|
|
2320
|
+
from ${schema.sessionAttemptInterruptions} interruption
|
|
2321
|
+
where interruption.workspace_id = attempt.workspace_id
|
|
2322
|
+
and interruption.attempt_id = attempt.id
|
|
2323
|
+
and interruption.kind in ('session_pause', 'workspace_pause')
|
|
2324
|
+
and interruption.state in ('pending', 'delivered', 'acknowledged')
|
|
2325
|
+
)
|
|
2326
|
+
union all
|
|
2327
|
+
select 1
|
|
2328
|
+
from ${schema.sessionBackgroundCommands} command
|
|
2329
|
+
join scoped_sessions session on session.id = command.session_id
|
|
2330
|
+
where command.workspace_id = ${input.workspaceId}
|
|
2331
|
+
and command.state = 'running'
|
|
2332
|
+
) as needed
|
|
2333
|
+
`);
|
|
2334
|
+
return rows[0]?.needed ?? false;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
async function workspacePauseEffectsNeeded(
|
|
2338
|
+
db: Database,
|
|
2339
|
+
input: { workspaceId: string; workspacePauseRevision: number },
|
|
2340
|
+
): Promise<boolean> {
|
|
2341
|
+
const rows = await db.execute<{ needed: boolean }>(sql`
|
|
2342
|
+
select exists (
|
|
2343
|
+
select 1
|
|
2344
|
+
from ${schema.sessions} session
|
|
2345
|
+
where session.workspace_id = ${input.workspaceId}
|
|
2346
|
+
and session.subtree_run_override_revision > ${input.workspacePauseRevision}
|
|
2347
|
+
union all
|
|
2348
|
+
select 1
|
|
2349
|
+
from ${schema.sessionTurnAttempts} attempt
|
|
2350
|
+
where attempt.workspace_id = ${input.workspaceId}
|
|
2351
|
+
and attempt.state in ('claimed', 'running')
|
|
2352
|
+
and not exists (
|
|
2353
|
+
select 1
|
|
2354
|
+
from ${schema.sessionAttemptInterruptions} interruption
|
|
2355
|
+
where interruption.workspace_id = attempt.workspace_id
|
|
2356
|
+
and interruption.attempt_id = attempt.id
|
|
2357
|
+
and interruption.kind in ('session_pause', 'workspace_pause')
|
|
2358
|
+
and interruption.state in ('pending', 'delivered', 'acknowledged')
|
|
2359
|
+
)
|
|
2360
|
+
union all
|
|
2361
|
+
select 1
|
|
2362
|
+
from ${schema.sessionBackgroundCommands} command
|
|
2363
|
+
where command.workspace_id = ${input.workspaceId}
|
|
2364
|
+
and command.state = 'running'
|
|
2365
|
+
) as needed
|
|
2366
|
+
`);
|
|
2367
|
+
return rows[0]?.needed ?? false;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
async function continuableWakeRepairNeeded(
|
|
2371
|
+
db: Database,
|
|
2372
|
+
input: { workspaceId: string; rootSessionId: string | null },
|
|
2373
|
+
): Promise<boolean> {
|
|
2374
|
+
const rows = await db.execute<{ needed: boolean }>(sql`
|
|
2375
|
+
select exists (
|
|
2376
|
+
select 1
|
|
2377
|
+
from opengeni_private.list_continuable_sessions(
|
|
2378
|
+
${input.workspaceId}::uuid,
|
|
2379
|
+
${input.rootSessionId}::uuid
|
|
2380
|
+
) eligible
|
|
2381
|
+
left join ${schema.sessionWorkflowWakeOutbox} wake
|
|
2382
|
+
on wake.workspace_id = eligible.workspace_id
|
|
2383
|
+
and wake.session_id = eligible.session_id
|
|
2384
|
+
where wake.session_id is null
|
|
2385
|
+
or wake.wake_revision <= wake.delivered_revision
|
|
2386
|
+
) as needed
|
|
2387
|
+
`);
|
|
2388
|
+
return rows[0]?.needed ?? false;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2281
2391
|
async function loadSessionSubtreeIds(
|
|
2282
2392
|
db: Database,
|
|
2283
2393
|
workspaceId: string,
|
|
@@ -3017,13 +3127,23 @@ export async function mutateSessionControlInTransaction(
|
|
|
3017
3127
|
operationKey: input.operationKey,
|
|
3018
3128
|
canonicalRequestHash: hash,
|
|
3019
3129
|
});
|
|
3020
|
-
if (reserved.replay
|
|
3021
|
-
const
|
|
3022
|
-
|
|
3130
|
+
if (reserved.replay) {
|
|
3131
|
+
const storedOutcome = reserved.receipt.result.outcome;
|
|
3132
|
+
const unchanged =
|
|
3133
|
+
storedOutcome === "unchanged" && reserved.receipt.appliedControlRevision === null;
|
|
3134
|
+
if (!unchanged && reserved.receipt.appliedControlRevision === null) {
|
|
3135
|
+
throw new SessionControlInvariantError(
|
|
3136
|
+
"Replayed session control receipt has no applied control revision",
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
3139
|
+
const workspaceControlEventId = unchanged
|
|
3140
|
+
? null
|
|
3141
|
+
: String(reserved.receipt.result.workspaceControlEventId ?? "");
|
|
3142
|
+
if (!unchanged && !workspaceControlEventId) {
|
|
3023
3143
|
throw new SessionControlInvariantError("Replayed session control receipt has no event");
|
|
3024
3144
|
}
|
|
3025
|
-
const sessionControlEventId = String(reserved.receipt.result.eventId ?? "");
|
|
3026
|
-
if (!sessionControlEventId) {
|
|
3145
|
+
const sessionControlEventId = unchanged ? null : String(reserved.receipt.result.eventId ?? "");
|
|
3146
|
+
if (!unchanged && !sessionControlEventId) {
|
|
3027
3147
|
throw new SessionControlInvariantError(
|
|
3028
3148
|
"Replayed session control receipt has no session event",
|
|
3029
3149
|
);
|
|
@@ -3046,13 +3166,16 @@ export async function mutateSessionControlInTransaction(
|
|
|
3046
3166
|
sessionId: string;
|
|
3047
3167
|
eventIds: string[];
|
|
3048
3168
|
}>)
|
|
3049
|
-
:
|
|
3169
|
+
: sessionControlEventId
|
|
3170
|
+
? [{ sessionId: input.sessionId, eventIds: [sessionControlEventId] }]
|
|
3171
|
+
: [],
|
|
3050
3172
|
workflowWake: await pendingSessionControlWorkflowWake(
|
|
3051
3173
|
db,
|
|
3052
3174
|
input.workspaceId,
|
|
3053
3175
|
input.sessionId,
|
|
3054
3176
|
wakeCount,
|
|
3055
3177
|
),
|
|
3178
|
+
outcome: "replayed",
|
|
3056
3179
|
replay: true,
|
|
3057
3180
|
};
|
|
3058
3181
|
}
|
|
@@ -3074,6 +3197,59 @@ export async function mutateSessionControlInTransaction(
|
|
|
3074
3197
|
throw new SessionControlConflictError();
|
|
3075
3198
|
}
|
|
3076
3199
|
|
|
3200
|
+
if (input.action !== "cancel") {
|
|
3201
|
+
const targetSession = locks.sessions.find((session) => session.id === input.sessionId);
|
|
3202
|
+
if (!targetSession) {
|
|
3203
|
+
throw new SessionControlInvariantError(`Session ${input.sessionId} disappeared`);
|
|
3204
|
+
}
|
|
3205
|
+
const directPauseRevision = asSafeRevision(
|
|
3206
|
+
targetSession.directPauseRevision,
|
|
3207
|
+
"direct pause revision",
|
|
3208
|
+
);
|
|
3209
|
+
const changed =
|
|
3210
|
+
input.action === "pause"
|
|
3211
|
+
? targetSession.directControlState !== "paused" ||
|
|
3212
|
+
directPauseRevision === null ||
|
|
3213
|
+
(await sessionPauseEffectsNeeded(db, {
|
|
3214
|
+
workspaceId: input.workspaceId,
|
|
3215
|
+
sessionId: input.sessionId,
|
|
3216
|
+
directPauseRevision,
|
|
3217
|
+
}))
|
|
3218
|
+
: before.state === "paused" ||
|
|
3219
|
+
(await continuableWakeRepairNeeded(db, {
|
|
3220
|
+
workspaceId: input.workspaceId,
|
|
3221
|
+
rootSessionId: input.sessionId,
|
|
3222
|
+
}));
|
|
3223
|
+
if (!changed) {
|
|
3224
|
+
const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
|
|
3225
|
+
result: {
|
|
3226
|
+
outcome: "unchanged",
|
|
3227
|
+
interruptionCount: 0,
|
|
3228
|
+
backgroundCommandCount: 0,
|
|
3229
|
+
wakeCount: 0,
|
|
3230
|
+
cancelledSessionCount: 0,
|
|
3231
|
+
cancelledTurnCount: 0,
|
|
3232
|
+
affectedSessionEvents: [],
|
|
3233
|
+
},
|
|
3234
|
+
});
|
|
3235
|
+
return {
|
|
3236
|
+
receipt,
|
|
3237
|
+
control: before,
|
|
3238
|
+
sessionControlEventId: null,
|
|
3239
|
+
workspaceControlEventId: null,
|
|
3240
|
+
interruptionCount: 0,
|
|
3241
|
+
backgroundCommandCount: 0,
|
|
3242
|
+
wakeCount: 0,
|
|
3243
|
+
cancelledSessionCount: 0,
|
|
3244
|
+
cancelledTurnCount: 0,
|
|
3245
|
+
affectedSessionEvents: [],
|
|
3246
|
+
workflowWake: null,
|
|
3247
|
+
outcome: "unchanged",
|
|
3248
|
+
replay: false,
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3077
3253
|
const revision = nextRevision(workspace);
|
|
3078
3254
|
await advanceWorkspaceRevision(db, input.workspaceId, revision);
|
|
3079
3255
|
const updatedRows = await db
|
|
@@ -3281,6 +3457,7 @@ export async function mutateSessionControlInTransaction(
|
|
|
3281
3457
|
const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
|
|
3282
3458
|
controlRevision: revision,
|
|
3283
3459
|
result: {
|
|
3460
|
+
outcome: "changed",
|
|
3284
3461
|
interruptionCount,
|
|
3285
3462
|
backgroundCommandCount,
|
|
3286
3463
|
wakeCount,
|
|
@@ -3311,6 +3488,7 @@ export async function mutateSessionControlInTransaction(
|
|
|
3311
3488
|
input.sessionId,
|
|
3312
3489
|
wakeCount,
|
|
3313
3490
|
),
|
|
3491
|
+
outcome: "changed",
|
|
3314
3492
|
replay: false,
|
|
3315
3493
|
};
|
|
3316
3494
|
}
|
|
@@ -3406,11 +3584,12 @@ export async function autoResumeSessionBranchInTransaction(
|
|
|
3406
3584
|
export type WorkspaceControlMutationResult = {
|
|
3407
3585
|
receipt: SessionCommandReceiptRow;
|
|
3408
3586
|
revision: number;
|
|
3409
|
-
workspaceControlEventId: string;
|
|
3587
|
+
workspaceControlEventId: string | null;
|
|
3410
3588
|
workspaceState: EffectiveControlState;
|
|
3411
3589
|
interruptionCount: number;
|
|
3412
3590
|
backgroundCommandCount: number;
|
|
3413
3591
|
wakeCount: number;
|
|
3592
|
+
outcome: "changed" | "unchanged" | "replayed";
|
|
3414
3593
|
replay: boolean;
|
|
3415
3594
|
};
|
|
3416
3595
|
|
|
@@ -3449,19 +3628,33 @@ export async function mutateWorkspaceControlInTransaction(
|
|
|
3449
3628
|
operationKey: input.operationKey,
|
|
3450
3629
|
canonicalRequestHash: hash,
|
|
3451
3630
|
});
|
|
3452
|
-
if (reserved.replay
|
|
3453
|
-
const
|
|
3454
|
-
|
|
3631
|
+
if (reserved.replay) {
|
|
3632
|
+
const storedOutcome = reserved.receipt.result.outcome;
|
|
3633
|
+
const unchanged =
|
|
3634
|
+
storedOutcome === "unchanged" && reserved.receipt.appliedControlRevision === null;
|
|
3635
|
+
if (!unchanged && reserved.receipt.appliedControlRevision === null) {
|
|
3636
|
+
throw new SessionControlInvariantError(
|
|
3637
|
+
"Replayed workspace control receipt has no applied control revision",
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
3640
|
+
const workspaceControlEventId = unchanged
|
|
3641
|
+
? null
|
|
3642
|
+
: String(reserved.receipt.result.workspaceControlEventId ?? "");
|
|
3643
|
+
if (!unchanged && !workspaceControlEventId) {
|
|
3455
3644
|
throw new SessionControlInvariantError("Replayed workspace control receipt has no event");
|
|
3456
3645
|
}
|
|
3457
3646
|
return {
|
|
3458
3647
|
receipt: reserved.receipt,
|
|
3459
|
-
revision:
|
|
3460
|
-
|
|
3461
|
-
|
|
3648
|
+
revision:
|
|
3649
|
+
reserved.receipt.appliedControlRevision === null
|
|
3650
|
+
? currentRevision
|
|
3651
|
+
: Number(reserved.receipt.appliedControlRevision),
|
|
3652
|
+
workspaceControlEventId: workspaceControlEventId || null,
|
|
3653
|
+
workspaceState: workspace.workspaceState === "paused" ? "paused" : "active",
|
|
3462
3654
|
interruptionCount: Number(reserved.receipt.result.interruptionCount ?? 0),
|
|
3463
3655
|
backgroundCommandCount: Number(reserved.receipt.result.backgroundCommandCount ?? 0),
|
|
3464
3656
|
wakeCount: Number(reserved.receipt.result.wakeCount ?? 0),
|
|
3657
|
+
outcome: "replayed",
|
|
3465
3658
|
replay: true,
|
|
3466
3659
|
};
|
|
3467
3660
|
}
|
|
@@ -3469,6 +3662,45 @@ export async function mutateWorkspaceControlInTransaction(
|
|
|
3469
3662
|
if (input.expectedRevision !== currentRevision) throw new SessionControlConflictError();
|
|
3470
3663
|
}
|
|
3471
3664
|
|
|
3665
|
+
const workspacePauseRevision = asSafeRevision(
|
|
3666
|
+
workspace.workspacePauseRevision,
|
|
3667
|
+
"workspace pause revision",
|
|
3668
|
+
);
|
|
3669
|
+
const changed =
|
|
3670
|
+
input.action === "pause"
|
|
3671
|
+
? workspace.workspaceState !== "paused" ||
|
|
3672
|
+
workspacePauseRevision === null ||
|
|
3673
|
+
(await workspacePauseEffectsNeeded(db, {
|
|
3674
|
+
workspaceId: input.workspaceId,
|
|
3675
|
+
workspacePauseRevision,
|
|
3676
|
+
}))
|
|
3677
|
+
: workspace.workspaceState !== "active" ||
|
|
3678
|
+
(await continuableWakeRepairNeeded(db, {
|
|
3679
|
+
workspaceId: input.workspaceId,
|
|
3680
|
+
rootSessionId: null,
|
|
3681
|
+
}));
|
|
3682
|
+
if (!changed) {
|
|
3683
|
+
const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
|
|
3684
|
+
result: {
|
|
3685
|
+
outcome: "unchanged",
|
|
3686
|
+
interruptionCount: 0,
|
|
3687
|
+
backgroundCommandCount: 0,
|
|
3688
|
+
wakeCount: 0,
|
|
3689
|
+
},
|
|
3690
|
+
});
|
|
3691
|
+
return {
|
|
3692
|
+
receipt,
|
|
3693
|
+
revision: currentRevision,
|
|
3694
|
+
workspaceControlEventId: null,
|
|
3695
|
+
workspaceState: workspace.workspaceState === "paused" ? "paused" : "active",
|
|
3696
|
+
interruptionCount: 0,
|
|
3697
|
+
backgroundCommandCount: 0,
|
|
3698
|
+
wakeCount: 0,
|
|
3699
|
+
outcome: "unchanged",
|
|
3700
|
+
replay: false,
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3472
3704
|
const revision = nextRevision(workspace);
|
|
3473
3705
|
const actor =
|
|
3474
3706
|
input.actor.type === "agent_attempt"
|
|
@@ -3533,7 +3765,13 @@ export async function mutateWorkspaceControlInTransaction(
|
|
|
3533
3765
|
});
|
|
3534
3766
|
const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
|
|
3535
3767
|
controlRevision: revision,
|
|
3536
|
-
result: {
|
|
3768
|
+
result: {
|
|
3769
|
+
outcome: "changed",
|
|
3770
|
+
interruptionCount,
|
|
3771
|
+
backgroundCommandCount,
|
|
3772
|
+
wakeCount,
|
|
3773
|
+
workspaceControlEventId,
|
|
3774
|
+
},
|
|
3537
3775
|
});
|
|
3538
3776
|
return {
|
|
3539
3777
|
receipt,
|
|
@@ -3543,6 +3781,7 @@ export async function mutateWorkspaceControlInTransaction(
|
|
|
3543
3781
|
interruptionCount,
|
|
3544
3782
|
backgroundCommandCount,
|
|
3545
3783
|
wakeCount,
|
|
3784
|
+
outcome: "changed",
|
|
3546
3785
|
replay: false,
|
|
3547
3786
|
};
|
|
3548
3787
|
}
|