@opengeni/db 3.4.0-canary.2 → 3.6.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 (67) hide show
  1. package/dist/canonical-human-identities.d.ts +20 -0
  2. package/dist/canonical-human-identities.js +7 -3
  3. package/dist/{chunk-ZWRIPCTR.js → chunk-2NT476HD.js} +2 -2
  4. package/dist/{chunk-PYMY3AJW.js → chunk-744DFC65.js} +202 -7
  5. package/dist/chunk-744DFC65.js.map +1 -0
  6. package/dist/{chunk-O6FTELKH.js → chunk-CDMMSUWJ.js} +3 -3
  7. package/dist/chunk-G3732M3T.js +299 -0
  8. package/dist/chunk-G3732M3T.js.map +1 -0
  9. package/dist/{chunk-TRBOWUG3.js → chunk-IW6O6G7R.js} +2 -2
  10. package/dist/{chunk-HY3F5Q4E.js → chunk-Q5YUGQVK.js} +3 -3
  11. package/dist/{chunk-V3TRUXFJ.js → chunk-UGDVSQ6U.js} +72 -19
  12. package/dist/chunk-UGDVSQ6U.js.map +1 -0
  13. package/dist/{chunk-C2AFP7SB.js → chunk-UJ2EW3QI.js} +6664 -6051
  14. package/dist/chunk-UJ2EW3QI.js.map +1 -0
  15. package/dist/{chunk-H7MXYG7R.js → chunk-WW7AJ5IC.js} +2 -2
  16. package/dist/editable-artifact-durable-export.js +2 -2
  17. package/dist/editable-artifacts.js +3 -3
  18. package/dist/index.d.ts +44 -6
  19. package/dist/index.js +2914 -1535
  20. package/dist/index.js.map +1 -1
  21. package/dist/managed-auth-session-set-schema.d.ts +1399 -0
  22. package/dist/managed-auth-session-sets.d.ts +145 -0
  23. package/dist/managed-auth-session-sets.js +50 -0
  24. package/dist/managed-auth-session-sets.js.map +1 -0
  25. package/dist/organization-recovery-schema.d.ts +2144 -0
  26. package/dist/organization-recovery.d.ts +186 -0
  27. package/dist/provision-roles.js +1 -1
  28. package/dist/runtime-posture.d.ts +7 -4
  29. package/dist/schema.d.ts +9 -6
  30. package/dist/schema.js +43 -1
  31. package/dist/session-background-commands.js +3 -3
  32. package/dist/session-control.d.ts +5 -3
  33. package/dist/session-queue-commands.d.ts +2 -0
  34. package/dist/session-tenancy.js +3 -3
  35. package/dist/video-generation.js +3 -3
  36. package/dist/work-claims-schema.d.ts +975 -0
  37. package/dist/work-claims.d.ts +83 -0
  38. package/drizzle/0362_managed_auth_session_sets.sql +2347 -0
  39. package/drizzle/0363_organization_recovery_custody.sql +2418 -0
  40. package/drizzle/0364_workspace_learning_policy_snapshot_lock_order.sql +221 -0
  41. package/drizzle/0365_permission_scoped_work_claims.sql +1306 -0
  42. package/drizzle/0366_session_discovery_title_search_index.sql +5 -0
  43. package/drizzle/0367_session_discovery_goal_search_index.sql +5 -0
  44. package/drizzle/0368_session_discovery_claim_search_index.sql +6 -0
  45. package/package.json +10 -6
  46. package/src/canonical-human-identities.ts +105 -27
  47. package/src/index.ts +1471 -433
  48. package/src/managed-auth-session-set-schema.ts +176 -0
  49. package/src/managed-auth-session-sets.ts +430 -0
  50. package/src/organization-recovery-schema.ts +277 -0
  51. package/src/organization-recovery.ts +424 -0
  52. package/src/provision-roles.ts +67 -0
  53. package/src/runtime-posture.ts +144 -6
  54. package/src/schema.ts +3 -0
  55. package/src/session-control.ts +255 -16
  56. package/src/session-queue-commands.ts +10 -1
  57. package/src/session-tool-call-settlement.ts +6 -1
  58. package/src/work-claims-schema.ts +148 -0
  59. package/src/work-claims.ts +200 -0
  60. package/dist/chunk-C2AFP7SB.js.map +0 -1
  61. package/dist/chunk-PYMY3AJW.js.map +0 -1
  62. package/dist/chunk-V3TRUXFJ.js.map +0 -1
  63. /package/dist/{chunk-ZWRIPCTR.js.map → chunk-2NT476HD.js.map} +0 -0
  64. /package/dist/{chunk-O6FTELKH.js.map → chunk-CDMMSUWJ.js.map} +0 -0
  65. /package/dist/{chunk-TRBOWUG3.js.map → chunk-IW6O6G7R.js.map} +0 -0
  66. /package/dist/{chunk-HY3F5Q4E.js.map → chunk-Q5YUGQVK.js.map} +0 -0
  67. /package/dist/{chunk-H7MXYG7R.js.map → chunk-WW7AJ5IC.js.map} +0 -0
@@ -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 && reserved.receipt.appliedControlRevision !== null) {
3021
- const workspaceControlEventId = String(reserved.receipt.result.workspaceControlEventId ?? "");
3022
- if (!workspaceControlEventId) {
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
- : [{ sessionId: input.sessionId, eventIds: [sessionControlEventId] }],
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 && reserved.receipt.appliedControlRevision !== null) {
3453
- const workspaceControlEventId = String(reserved.receipt.result.workspaceControlEventId ?? "");
3454
- if (!workspaceControlEventId) {
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: Number(reserved.receipt.appliedControlRevision),
3460
- workspaceControlEventId,
3461
- workspaceState: input.action === "pause" ? "paused" : "active",
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: { interruptionCount, backgroundCommandCount, wakeCount, workspaceControlEventId },
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
  }
@@ -1535,6 +1535,8 @@ export async function submitHumanPromptInTransaction(
1535
1535
  annotations?: TimelineAnnotation[];
1536
1536
  modelContext?: string | null;
1537
1537
  resources: ResourceRef[];
1538
+ /** Actor-owned resources used only for the exact durable-draft fence. */
1539
+ composerDraftResources?: ResourceRef[];
1538
1540
  model?: string | null;
1539
1541
  reasoningEffort?: ReasoningEffort | null;
1540
1542
  latencyMode?: LatencyMode | null;
@@ -1601,6 +1603,11 @@ export async function submitHumanPromptInTransaction(
1601
1603
  annotations,
1602
1604
  modelContext: input.modelContext ?? null,
1603
1605
  resources: withCanonicalResourceMountPaths(input.resources),
1606
+ ...(input.composerDraftResources
1607
+ ? {
1608
+ composerDraftResources: withCanonicalResourceMountPaths(input.composerDraftResources),
1609
+ }
1610
+ : {}),
1604
1611
  model: input.model ?? null,
1605
1612
  reasoningEffort: input.reasoningEffort ?? null,
1606
1613
  latencyMode: input.latencyMode ?? null,
@@ -1777,7 +1784,9 @@ export async function submitHumanPromptInTransaction(
1777
1784
  canonicalSessionCommandHash({
1778
1785
  text: input.text,
1779
1786
  annotations: annotations.map(({ ordinal: _ordinal, ...annotation }) => annotation),
1780
- resources: withCanonicalResourceMountPaths(input.resources),
1787
+ resources: withCanonicalResourceMountPaths(
1788
+ input.composerDraftResources ?? input.resources,
1789
+ ),
1781
1790
  model: input.model ?? session.model,
1782
1791
  reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
1783
1792
  latencyMode: input.turnExecutionPolicy?.latencyMode ?? input.latencyMode ?? "standard",
@@ -15,7 +15,12 @@ export const TOOL_RESULT_TYPE_BY_CALL_TYPE: Readonly<Record<string, string>> = {
15
15
  };
16
16
 
17
17
  export function historyCallId(item: Record<string, unknown>): string | null {
18
- const value = item.callId ?? item.call_id ?? item.id;
18
+ const providerData =
19
+ item.providerData && typeof item.providerData === "object" && !Array.isArray(item.providerData)
20
+ ? (item.providerData as Record<string, unknown>)
21
+ : null;
22
+ const value =
23
+ item.callId ?? item.call_id ?? providerData?.callId ?? providerData?.call_id ?? item.id;
19
24
  return typeof value === "string" && value.length > 0 ? value : null;
20
25
  }
21
26
 
@@ -0,0 +1,148 @@
1
+ import { sql } from "drizzle-orm";
2
+ import {
3
+ customType,
4
+ index,
5
+ integer,
6
+ pgTable,
7
+ primaryKey,
8
+ text,
9
+ timestamp,
10
+ uniqueIndex,
11
+ uuid,
12
+ } from "drizzle-orm/pg-core";
13
+
14
+ const xid8 = customType<{ data: string; driverData: string }>({
15
+ dataType: () => "xid8",
16
+ });
17
+
18
+ // Exact lifecycle-only mutation, immutable revisions, FORCE RLS, and runtime
19
+ // grants are owned by the work-claim migration. The application role receives
20
+ // SELECT on the compact head plus EXECUTE on exact-attempt mutation routines,
21
+ // never direct DML authority.
22
+ export const sessionWorkClaims = pgTable(
23
+ "session_work_claims",
24
+ {
25
+ id: uuid("id").primaryKey().defaultRandom(),
26
+ accountId: uuid("account_id").notNull(),
27
+ workspaceId: uuid("workspace_id").notNull(),
28
+ sessionId: uuid("session_id").notNull(),
29
+ rootSessionId: uuid("root_session_id").notNull(),
30
+ subjectNamespace: text("subject_namespace").notNull(),
31
+ subjectType: text("subject_type").notNull(),
32
+ canonicalKey: text("canonical_key").notNull(),
33
+ subjectDigest: text("subject_digest").notNull(),
34
+ displayLabel: text("display_label"),
35
+ role: text("role").notNull(),
36
+ state: text("state").notNull().default("active"),
37
+ revision: integer("revision").notNull().default(1),
38
+ provenance: text("provenance").notNull(),
39
+ versionKind: text("version_kind"),
40
+ versionValue: text("version_value"),
41
+ observedAt: timestamp("observed_at", { withTimezone: true }).notNull(),
42
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
44
+ settledAt: timestamp("settled_at", { withTimezone: true }),
45
+ },
46
+ (table) => ({
47
+ workspaceIdentity: uniqueIndex("session_work_claims_workspace_id_uq").on(
48
+ table.workspaceId,
49
+ table.id,
50
+ ),
51
+ activeIdentity: uniqueIndex("session_work_claims_active_identity_uq")
52
+ .on(
53
+ table.workspaceId,
54
+ table.sessionId,
55
+ table.subjectNamespace,
56
+ table.subjectType,
57
+ table.subjectDigest,
58
+ table.role,
59
+ )
60
+ .where(sql`${table.state} = 'active'`),
61
+ sessionState: index("session_work_claims_session_state_idx").on(
62
+ table.workspaceId,
63
+ table.sessionId,
64
+ table.state,
65
+ table.updatedAt,
66
+ table.id,
67
+ ),
68
+ subjectState: index("session_work_claims_subject_state_idx").on(
69
+ table.workspaceId,
70
+ table.subjectNamespace,
71
+ table.subjectType,
72
+ table.subjectDigest,
73
+ table.state,
74
+ table.updatedAt,
75
+ table.id,
76
+ ),
77
+ }),
78
+ );
79
+
80
+ export const sessionWorkClaimRevisions = pgTable(
81
+ "session_work_claim_revisions",
82
+ {
83
+ id: uuid("id").primaryKey().defaultRandom(),
84
+ accountId: uuid("account_id").notNull(),
85
+ workspaceId: uuid("workspace_id").notNull(),
86
+ claimId: uuid("claim_id").notNull(),
87
+ sessionId: uuid("session_id").notNull(),
88
+ rootSessionId: uuid("root_session_id").notNull(),
89
+ operationId: uuid("operation_id").notNull(),
90
+ inputHash: text("input_hash").notNull(),
91
+ mutationKind: text("mutation_kind").notNull(),
92
+ priorRevision: integer("prior_revision"),
93
+ resultingRevision: integer("resulting_revision").notNull(),
94
+ subjectNamespace: text("subject_namespace").notNull(),
95
+ subjectType: text("subject_type").notNull(),
96
+ canonicalKey: text("canonical_key").notNull(),
97
+ subjectDigest: text("subject_digest").notNull(),
98
+ displayLabel: text("display_label"),
99
+ role: text("role").notNull(),
100
+ state: text("state").notNull(),
101
+ provenance: text("provenance").notNull(),
102
+ versionKind: text("version_kind"),
103
+ versionValue: text("version_value"),
104
+ observedAt: timestamp("observed_at", { withTimezone: true }).notNull(),
105
+ claimCreatedAt: timestamp("claim_created_at", { withTimezone: true }).notNull(),
106
+ claimUpdatedAt: timestamp("claim_updated_at", { withTimezone: true }).notNull(),
107
+ settledAt: timestamp("settled_at", { withTimezone: true }),
108
+ actorKind: text("actor_kind").notNull(),
109
+ actorSubjectId: text("actor_subject_id").notNull(),
110
+ actorSessionId: uuid("actor_session_id"),
111
+ actorTurnId: uuid("actor_turn_id"),
112
+ actorAttemptId: uuid("actor_attempt_id"),
113
+ actorExecutionGeneration: integer("actor_execution_generation"),
114
+ reason: text("reason"),
115
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
116
+ },
117
+ (table) => ({
118
+ operation: uniqueIndex("session_work_claim_revisions_workspace_operation_uq").on(
119
+ table.workspaceId,
120
+ table.operationId,
121
+ ),
122
+ claimRevision: uniqueIndex("session_work_claim_revisions_claim_revision_uq").on(
123
+ table.workspaceId,
124
+ table.claimId,
125
+ table.resultingRevision,
126
+ ),
127
+ sessionTimeline: index("session_work_claim_revisions_session_timeline_idx").on(
128
+ table.workspaceId,
129
+ table.sessionId,
130
+ table.createdAt,
131
+ table.id,
132
+ ),
133
+ }),
134
+ );
135
+
136
+ export const sessionWorkClaimWriteCapabilities = pgTable(
137
+ "session_work_claim_write_capabilities",
138
+ {
139
+ backendPid: integer("backend_pid").notNull(),
140
+ transactionId: xid8("transaction_id").notNull(),
141
+ capabilityId: uuid("capability_id").notNull(),
142
+ },
143
+ (table) => ({
144
+ identity: primaryKey({
145
+ columns: [table.backendPid, table.transactionId, table.capabilityId],
146
+ }),
147
+ }),
148
+ );