@opengeni/db 4.2.2 → 4.3.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.
Files changed (103) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-S4Y42VSX.js → chunk-AI4NC6XW.js} +212 -7
  3. package/dist/chunk-AI4NC6XW.js.map +1 -0
  4. package/dist/{chunk-SNLIKELL.js → chunk-DVSLOUEI.js} +2 -2
  5. package/dist/{chunk-6GAPAYZR.js → chunk-F4BIHJ7J.js} +562 -86
  6. package/dist/chunk-F4BIHJ7J.js.map +1 -0
  7. package/dist/{chunk-B2KR3TEM.js → chunk-I4ITNMX2.js} +2 -2
  8. package/dist/{chunk-MGVOYVPS.js → chunk-PFBUHOI5.js} +3 -3
  9. package/dist/{chunk-6Y7ZDVJ5.js → chunk-UUPY6IGP.js} +2 -2
  10. package/dist/{chunk-FPTI4NP4.js → chunk-UVBPOGA7.js} +2 -2
  11. package/dist/{chunk-7WX3R3YL.js → chunk-XBXANJQS.js} +7 -2
  12. package/dist/chunk-XBXANJQS.js.map +1 -0
  13. package/dist/{chunk-6IQ4X35Y.js → chunk-YVYBAWBU.js} +46 -1
  14. package/dist/chunk-YVYBAWBU.js.map +1 -0
  15. package/dist/connect-attempts.d.ts +60 -0
  16. package/dist/connection-setup-authority.d.ts +10 -0
  17. package/dist/connection-token-resolver.d.ts +11 -2
  18. package/dist/editable-artifact-durable-export.js +2 -2
  19. package/dist/editable-artifacts.js +3 -3
  20. package/dist/external-identities.d.ts +16 -0
  21. package/dist/external-identity-links.d.ts +74 -0
  22. package/dist/external-link-work.d.ts +51 -0
  23. package/dist/host-mcp-binding-match.d.ts +5 -0
  24. package/dist/host-mcp-bindings.d.ts +73 -0
  25. package/dist/host-mcp-task-authority.d.ts +47 -0
  26. package/dist/host-selection-identity.d.ts +6 -0
  27. package/dist/index.d.ts +60 -35
  28. package/dist/index.js +15247 -13793
  29. package/dist/index.js.map +1 -1
  30. package/dist/live-session-attempt.d.ts +275 -0
  31. package/dist/managed-auth-session-sets.js +3 -3
  32. package/dist/memory-domain.d.ts +3 -3
  33. package/dist/provision-roles.js +1 -1
  34. package/dist/retained-provider-commands.js +2 -2
  35. package/dist/runtime-posture.d.ts +6 -6
  36. package/dist/scheduled-task-revision-authority.d.ts +11 -0
  37. package/dist/schema.d.ts +1279 -9
  38. package/dist/schema.js +11 -1
  39. package/dist/session-background-commands.js +4 -4
  40. package/dist/session-command-output.js +4 -4
  41. package/dist/session-event-slices.js +2 -2
  42. package/dist/session-queue-commands.d.ts +7 -0
  43. package/dist/session-tenancy.d.ts +2 -0
  44. package/dist/session-tenancy.js +3 -3
  45. package/dist/video-generation.js +3 -3
  46. package/dist/workspace-authority.d.ts +4 -4
  47. package/dist/workspace-tool-defaults.js +4 -4
  48. package/drizzle/0437_organization_scoped_external_workspaces.sql +71 -0
  49. package/drizzle/0438_durable_connect_attempts.sql +95 -0
  50. package/drizzle/0439_external_identity_provisioning.sql +128 -0
  51. package/drizzle/0440_external_workspace_member_removal.sql +59 -0
  52. package/drizzle/0441_external_identity_membership_lifecycle.sql +163 -0
  53. package/drizzle/0442_external_owning_user_authority.sql +81 -0
  54. package/drizzle/0443_host_mcp_binding_registry.sql +93 -0
  55. package/drizzle/0444_host_mcp_delegations.sql +129 -0
  56. package/drizzle/0445_host_mcp_turn_authorities.sql +145 -0
  57. package/drizzle/0446_host_mcp_causal_continuation.sql +126 -0
  58. package/drizzle/0447_host_mcp_task_authorities.sql +264 -0
  59. package/drizzle/0448_host_mcp_child_authority.sql +115 -0
  60. package/drizzle/0449_external_identity_link_lifecycle.sql +107 -0
  61. package/drizzle/0450_external_identity_link_work.sql +179 -0
  62. package/drizzle/0451_external_link_preview_and_permission_ceiling.sql +82 -0
  63. package/drizzle/0452_external_link_scheduled_origin.sql +34 -0
  64. package/drizzle/0453_host_mcp_native_owner.sql +35 -0
  65. package/drizzle/0454_connect_origin_authority.sql +42 -0
  66. package/drizzle/0455_external_link_inventory_labels.sql +44 -0
  67. package/drizzle/0456_social_connection_versions.sql +14 -0
  68. package/drizzle/0457_canonical_session_scope_subject.sql +46 -0
  69. package/drizzle/0458_skill_review_wire_compatibility.sql +36 -0
  70. package/package.json +6 -6
  71. package/src/connect-attempts.ts +317 -0
  72. package/src/connection-authority.ts +9 -0
  73. package/src/connection-setup-authority.ts +35 -0
  74. package/src/connection-token-resolver.ts +177 -48
  75. package/src/external-identities.ts +70 -0
  76. package/src/external-identity-links.ts +364 -0
  77. package/src/external-link-work.ts +228 -0
  78. package/src/host-mcp-binding-match.ts +33 -0
  79. package/src/host-mcp-bindings.ts +741 -0
  80. package/src/host-mcp-task-authority.ts +383 -0
  81. package/src/host-selection-identity.ts +29 -0
  82. package/src/index.ts +481 -151
  83. package/src/live-session-attempt.ts +85 -0
  84. package/src/managed-human-provisioning.ts +6 -1
  85. package/src/memory-domain.ts +6 -11
  86. package/src/provision-roles.ts +3 -0
  87. package/src/runtime-posture.ts +47 -0
  88. package/src/scheduled-task-revision-authority.ts +30 -0
  89. package/src/schema.ts +233 -7
  90. package/src/session-control.ts +14 -0
  91. package/src/session-queue-commands.ts +11 -0
  92. package/src/session-tenancy.ts +7 -0
  93. package/src/workspace-artifacts.ts +10 -4
  94. package/src/workspace-authority.ts +5 -5
  95. package/dist/chunk-6GAPAYZR.js.map +0 -1
  96. package/dist/chunk-6IQ4X35Y.js.map +0 -1
  97. package/dist/chunk-7WX3R3YL.js.map +0 -1
  98. package/dist/chunk-S4Y42VSX.js.map +0 -1
  99. /package/dist/{chunk-SNLIKELL.js.map → chunk-DVSLOUEI.js.map} +0 -0
  100. /package/dist/{chunk-B2KR3TEM.js.map → chunk-I4ITNMX2.js.map} +0 -0
  101. /package/dist/{chunk-MGVOYVPS.js.map → chunk-PFBUHOI5.js.map} +0 -0
  102. /package/dist/{chunk-6Y7ZDVJ5.js.map → chunk-UUPY6IGP.js.map} +0 -0
  103. /package/dist/{chunk-FPTI4NP4.js.map → chunk-UVBPOGA7.js.map} +0 -0
package/src/index.ts CHANGED
@@ -39,6 +39,33 @@ import {
39
39
  import { releaseOrphanedSkillHeads, type SkillSourceReleaseReceipt } from "./skill-source-release";
40
40
  import type { SkillActor, SkillWriteReceipt } from "@opengeni/contracts";
41
41
  import { isDeepStrictEqual } from "node:util";
42
+ import {
43
+ normalizeHostCreateSelection,
44
+ metadataWithHostCreateSelection,
45
+ hostCreateSelectionFromMetadata,
46
+ type HostMcpCreateSelection,
47
+ } from "./host-selection-identity";
48
+ export * from "./connect-attempts";
49
+ export * from "./external-identities";
50
+ export * from "./external-identity-links";
51
+ export * from "./connection-setup-authority";
52
+ export * from "./external-link-work";
53
+ import {
54
+ inheritExternalLinkTurnAuthority,
55
+ captureScheduledExternalLinkTurnAuthority,
56
+ cloneExternalLinkTaskAuthority,
57
+ getExternalLinkTurnAuthorization,
58
+ } from "./external-link-work";
59
+ export * from "./host-mcp-bindings";
60
+ import {
61
+ inheritCausalHostMcpTurnAuthorities,
62
+ inheritChildHostMcpTurnAuthorities,
63
+ } from "./host-mcp-bindings";
64
+ export * from "./host-mcp-task-authority";
65
+ import {
66
+ cloneHostMcpTaskAuthorities,
67
+ captureScheduledHostMcpTurnAuthorities,
68
+ } from "./host-mcp-task-authority";
42
69
  import {
43
70
  SESSION_GOAL_PROGRESS_MAX_BYTES,
44
71
  SESSION_GOAL_RATIONALE_MAX_BYTES,
@@ -150,7 +177,7 @@ import type {
150
177
  SessionAgentAccess,
151
178
  SessionAgentAccessViewer,
152
179
  SessionAuthorizationListScope,
153
- SessionEndUser,
180
+ SessionScopeSubjectId,
154
181
  SessionMemoryScope,
155
182
  SessionListResponse,
156
183
  SessionTenancyPublicProjection,
@@ -245,6 +272,7 @@ import {
245
272
  SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND,
246
273
  parseWorkspaceArchiveDescriptor,
247
274
  parseWorkspaceArchiveObjectRef,
275
+ validateWorkspaceArchiveObjectRef,
248
276
  workspaceArchivePayloadPresent,
249
277
  omitInlineWorkspaceArchiveWhenObjectRefPresent,
250
278
  type WorkspaceArchiveObjectRef,
@@ -296,6 +324,7 @@ import {
296
324
  HostUsageExportBatch as HostUsageExportBatchContract,
297
325
  OPENGENI_HOST_EXPORT_SCHEMA_REVISION,
298
326
  HumanInputQuestion as HumanInputQuestionContract,
327
+ canonicalSkillReviewQuestion,
299
328
  SubmitHumanInputResponseRequest,
300
329
  TurnExecutionPolicyV1,
301
330
  CodexCredentialPolicySnapshotV1,
@@ -349,6 +378,7 @@ import {
349
378
  type SQLWrapper,
350
379
  } from "drizzle-orm";
351
380
  import type { PgTransactionConfig } from "drizzle-orm/pg-core";
381
+ import { getLiveSessionAttemptTurn } from "./live-session-attempt";
352
382
  import {
353
383
  creatorColumns,
354
384
  frozenInitiatorForCommandActor,
@@ -488,7 +518,6 @@ import {
488
518
  renderWorkspaceMemoryBlock,
489
519
  memoryTextForStorage,
490
520
  WORKSPACE_MEMORY_BLOCK_EMPTY,
491
- endUserMemorySubjectId,
492
521
  memoryReadScopesForAgentScope,
493
522
  memoryWriteScopeForAgentScope,
494
523
  normalizeMemoryScope,
@@ -644,6 +673,7 @@ export {
644
673
  type UserProfileLookup,
645
674
  } from "./database";
646
675
  export { withSessionRlsActorContext } from "./database";
676
+ export { normalizedHostCredentialHeaders } from "./connection-token-resolver";
647
677
  import {
648
678
  buildCodexTokenResolver as buildCodexTokenResolverCore,
649
679
  fetchCodexRateLimitResetCreditsForAccount as fetchCodexRateLimitResetCreditsForAccountCore,
@@ -1495,6 +1525,7 @@ export async function bootstrapWorkspace(
1495
1525
  and(
1496
1526
  eq(schema.workspaces.externalSource, input.workspaceExternalSource),
1497
1527
  eq(schema.workspaces.externalId, input.workspaceExternalId),
1528
+ eq(schema.workspaces.accountId, account.id),
1498
1529
  ),
1499
1530
  )
1500
1531
  .limit(1);
@@ -1508,7 +1539,11 @@ export async function bootstrapWorkspace(
1508
1539
  externalId: input.workspaceExternalId,
1509
1540
  })
1510
1541
  .onConflictDoUpdate({
1511
- target: [schema.workspaces.externalSource, schema.workspaces.externalId],
1542
+ target: [
1543
+ schema.workspaces.accountId,
1544
+ schema.workspaces.externalSource,
1545
+ schema.workspaces.externalId,
1546
+ ],
1512
1547
  set: {
1513
1548
  name: input.workspaceName,
1514
1549
  updatedAt: new Date(),
@@ -1976,6 +2011,7 @@ export async function ensureManagedAccessForUserWithOrganizationMemberships(
1976
2011
  and(
1977
2012
  eq(schema.workspaces.externalSource, "better-auth:user"),
1978
2013
  eq(schema.workspaces.externalId, `${input.userId}:default`),
2014
+ eq(schema.workspaces.accountId, account.id),
1979
2015
  ),
1980
2016
  )
1981
2017
  .limit(1);
@@ -1990,7 +2026,11 @@ export async function ensureManagedAccessForUserWithOrganizationMemberships(
1990
2026
  externalId: `${input.userId}:default`,
1991
2027
  })
1992
2028
  .onConflictDoUpdate({
1993
- target: [schema.workspaces.externalSource, schema.workspaces.externalId],
2029
+ target: [
2030
+ schema.workspaces.accountId,
2031
+ schema.workspaces.externalSource,
2032
+ schema.workspaces.externalId,
2033
+ ],
1994
2034
  // Preserve a later administrator rename on legacy fallback workspaces.
1995
2035
  set: { updatedAt: new Date() },
1996
2036
  })
@@ -2403,6 +2443,7 @@ export class WorkspaceExternalIdentityConflictError extends Error {
2403
2443
  export async function findWorkspaceByExternalIdentity(
2404
2444
  db: Database,
2405
2445
  input: {
2446
+ accountId: string;
2406
2447
  externalSource: string;
2407
2448
  externalId: string;
2408
2449
  },
@@ -2414,6 +2455,7 @@ export async function findWorkspaceByExternalIdentity(
2414
2455
  and(
2415
2456
  eq(schema.workspaces.externalSource, input.externalSource),
2416
2457
  eq(schema.workspaces.externalId, input.externalId),
2458
+ eq(schema.workspaces.accountId, input.accountId),
2417
2459
  ),
2418
2460
  )
2419
2461
  .limit(1);
@@ -2461,7 +2503,7 @@ async function assertWorkspaceCapacity(
2461
2503
 
2462
2504
  /**
2463
2505
  * Create an organization workspace exactly once for a stable external tenant
2464
- * identity. The global unique index is the concurrency arbiter. A replay never
2506
+ * identity. The organization-scoped unique index is the concurrency arbiter. A replay never
2465
2507
  * mutates presentation fields supplied by the original create.
2466
2508
  */
2467
2509
  export async function ensureWorkspaceByExternalIdentity(
@@ -2488,6 +2530,7 @@ export async function ensureWorkspaceByExternalIdentity(
2488
2530
  and(
2489
2531
  eq(schema.workspaces.externalSource, input.externalSource),
2490
2532
  eq(schema.workspaces.externalId, input.externalId),
2533
+ eq(schema.workspaces.accountId, input.accountId),
2491
2534
  ),
2492
2535
  )
2493
2536
  .limit(1);
@@ -2522,7 +2565,11 @@ export async function ensureWorkspaceByExternalIdentity(
2522
2565
  agentInstructions: input.agentInstructions ?? null,
2523
2566
  })
2524
2567
  .onConflictDoNothing({
2525
- target: [schema.workspaces.externalSource, schema.workspaces.externalId],
2568
+ target: [
2569
+ schema.workspaces.accountId,
2570
+ schema.workspaces.externalSource,
2571
+ schema.workspaces.externalId,
2572
+ ],
2526
2573
  })
2527
2574
  .returning();
2528
2575
 
@@ -2558,6 +2605,7 @@ export async function ensureWorkspaceByExternalIdentity(
2558
2605
  and(
2559
2606
  eq(schema.workspaces.externalSource, input.externalSource),
2560
2607
  eq(schema.workspaces.externalId, input.externalId),
2608
+ eq(schema.workspaces.accountId, input.accountId),
2561
2609
  ),
2562
2610
  )
2563
2611
  .limit(1);
@@ -5415,7 +5463,7 @@ export type AppendEventInput = {
5415
5463
  */
5416
5464
  export type ScheduledTaskCreatorSessionPolicy = {
5417
5465
  agentAccess: string | null;
5418
- endUser: { source: string; id: string } | null;
5466
+ scopeSubjectId: string | null;
5419
5467
  memoryScope: string | null;
5420
5468
  };
5421
5469
 
@@ -5458,6 +5506,8 @@ export type CreateScheduledTaskInput = {
5458
5506
  metadata: Record<string, unknown>;
5459
5507
  /** Trusted database-only admission seam. Throwing rolls the task creation back. */
5460
5508
  beforeCreateCommit?: (tx: Database) => Promise<void>;
5509
+ captureHostAuthority?: (tx: Database, task: ScheduledTask) => Promise<void>;
5510
+ captureLinkAuthority?: (tx: Database, task: ScheduledTask) => Promise<void>;
5461
5511
  };
5462
5512
 
5463
5513
  export type UpdateScheduledTaskInput = Partial<{
@@ -5482,6 +5532,8 @@ export type UpdateScheduledTaskInput = Partial<{
5482
5532
  authorityUpdatedByActor: AgentSessionCreationActor | null;
5483
5533
  /** Trusted database-only admission seam. Throwing rolls the task update back. */
5484
5534
  beforeUpdateCommit: (tx: Database) => Promise<void>;
5535
+ captureHostAuthority: (tx: Database, task: ScheduledTask) => Promise<void>;
5536
+ captureLinkAuthority: (tx: Database, task: ScheduledTask) => Promise<void>;
5485
5537
  }>;
5486
5538
 
5487
5539
  export type CreatePackInstallationInput = {
@@ -5566,6 +5618,7 @@ export type CreateSocialConnectionInput = {
5566
5618
  };
5567
5619
 
5568
5620
  export type UpsertSocialOAuthConnectionInput = {
5621
+ expectedConnection?: { id: string; version: number };
5569
5622
  accountId: string;
5570
5623
  workspaceId: string;
5571
5624
  subjectId?: string | null;
@@ -5682,6 +5735,8 @@ export type UpdateConnectionInput = {
5682
5735
  };
5683
5736
 
5684
5737
  export type PersistProviderOAuthConnectionInput = CreateConnectionInput & {
5738
+ /** Server continuation reauthorization; runs inside the credential transaction. */
5739
+ authorize?: (tx: Database) => Promise<void>;
5685
5740
  visibleToSubjectId: string;
5686
5741
  credentialRole: string;
5687
5742
  providerFamily: string;
@@ -10291,6 +10346,11 @@ export async function persistProviderOAuthConnection(
10291
10346
  return await scopedDb.transaction(async (txRaw) => {
10292
10347
  const tx = txRaw as unknown as Database;
10293
10348
  const ownerKey = input.subjectId ?? "workspace";
10349
+ if (input.authorize) {
10350
+ await input.authorize(tx);
10351
+ await setRlsContext(tx, { accountId: input.accountId, workspaceId: input.workspaceId });
10352
+ await setSubjectRlsContext(tx, input.subjectId ?? input.visibleToSubjectId ?? "");
10353
+ }
10294
10354
  if (input.requireLiveUserAuthority) {
10295
10355
  if (!input.subjectId) {
10296
10356
  throw new Error("Live user authority requires a subject-owned connection");
@@ -16274,6 +16334,40 @@ export async function upsertSocialOAuthConnection(
16274
16334
  { accountId: input.accountId, workspaceId: input.workspaceId },
16275
16335
  async (scopedDb) => {
16276
16336
  if (input.subjectId) await setSubjectRlsContext(scopedDb, input.subjectId);
16337
+ if (input.expectedConnection) {
16338
+ const [updated] = await scopedDb
16339
+ .update(schema.socialConnections)
16340
+ .set({
16341
+ accountHandle: input.accountHandle,
16342
+ accountName: input.accountName ?? null,
16343
+ status: "connected",
16344
+ scopes: input.scopes,
16345
+ credentialEncrypted: input.credentialEncrypted,
16346
+ tokenMetadata: input.tokenMetadata ?? {},
16347
+ updatedAt: new Date(),
16348
+ })
16349
+ .where(
16350
+ and(
16351
+ eq(schema.socialConnections.accountId, input.accountId),
16352
+ eq(schema.socialConnections.workspaceId, input.workspaceId),
16353
+ eq(schema.socialConnections.id, input.expectedConnection.id),
16354
+ eq(schema.socialConnections.version, input.expectedConnection.version),
16355
+ eq(schema.socialConnections.provider, input.provider),
16356
+ input.subjectId
16357
+ ? eq(schema.socialConnections.subjectId, input.subjectId)
16358
+ : isNull(schema.socialConnections.subjectId),
16359
+ input.externalAccountId
16360
+ ? eq(schema.socialConnections.externalAccountId, input.externalAccountId)
16361
+ : sql`false`,
16362
+ ),
16363
+ )
16364
+ .returning();
16365
+ if (!updated)
16366
+ throw new Error(
16367
+ "Social connection changed; authorize the same account again from current state",
16368
+ );
16369
+ return mapSocialConnection(updated);
16370
+ }
16277
16371
  const [row] = await scopedDb
16278
16372
  .insert(schema.socialConnections)
16279
16373
  .values({
@@ -16651,6 +16745,8 @@ export async function createScheduledTask(
16651
16745
  ${row.id}::uuid,
16652
16746
  ${row.authorityRevision}::bigint
16653
16747
  )`);
16748
+ await input.captureHostAuthority?.(scopedDb, mapScheduledTask(row));
16749
+ await input.captureLinkAuthority?.(scopedDb, mapScheduledTask(row));
16654
16750
  return mapScheduledTask(row);
16655
16751
  },
16656
16752
  );
@@ -16663,6 +16759,17 @@ export async function updateScheduledTask(
16663
16759
  input: UpdateScheduledTaskInput,
16664
16760
  ): Promise<ScheduledTask> {
16665
16761
  return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
16762
+ const [previousHostRevision] = await scopedDb
16763
+ .select({ authorityRevision: schema.scheduledTasks.authorityRevision })
16764
+ .from(schema.scheduledTasks)
16765
+ .where(
16766
+ and(
16767
+ eq(schema.scheduledTasks.workspaceId, workspaceId),
16768
+ eq(schema.scheduledTasks.id, taskId),
16769
+ ),
16770
+ )
16771
+ .for("update")
16772
+ .limit(1);
16666
16773
  if (
16667
16774
  input.refreshPersonalResourceAuthority &&
16668
16775
  input.clonePersonalResourceAuthorityFromRevision !== undefined
@@ -16772,7 +16879,26 @@ export async function updateScheduledTask(
16772
16879
  ${row.authorityRevision}::bigint
16773
16880
  )`);
16774
16881
  }
16775
- return mapScheduledTask(row);
16882
+ const mapped = mapScheduledTask(row);
16883
+ if (input.captureLinkAuthority) await input.captureLinkAuthority(scopedDb, mapped);
16884
+ else if (previousHostRevision)
16885
+ await cloneExternalLinkTaskAuthority(
16886
+ scopedDb,
16887
+ mapped,
16888
+ input.clonePersonalResourceAuthorityFromRevision ??
16889
+ input.cloneConnectionAuthorityFromRevision ??
16890
+ previousHostRevision.authorityRevision,
16891
+ );
16892
+ if (input.captureHostAuthority) await input.captureHostAuthority(scopedDb, mapped);
16893
+ else if (previousHostRevision)
16894
+ await cloneHostMcpTaskAuthorities(
16895
+ scopedDb,
16896
+ mapped,
16897
+ input.clonePersonalResourceAuthorityFromRevision ??
16898
+ input.cloneConnectionAuthorityFromRevision ??
16899
+ previousHostRevision.authorityRevision,
16900
+ );
16901
+ return mapped;
16776
16902
  });
16777
16903
  }
16778
16904
 
@@ -16973,8 +17099,11 @@ export async function getScheduledTaskCreatorPolicy(
16973
17099
  sessionPolicy: row.sessionPolicy
16974
17100
  ? {
16975
17101
  agentAccess: row.sessionPolicy.agentAccess ?? null,
16976
- endUser: row.sessionPolicy.endUser ?? null,
16977
- memoryScope: row.sessionPolicy.memoryScope ?? null,
17102
+ scopeSubjectId: row.sessionPolicy.scopeSubjectId ?? null,
17103
+ memoryScope:
17104
+ row.sessionPolicy.memoryScope === "session"
17105
+ ? "off"
17106
+ : (row.sessionPolicy.memoryScope ?? null),
16978
17107
  }
16979
17108
  : null,
16980
17109
  };
@@ -17722,6 +17851,19 @@ export async function materializeScheduledTaskReusableSessionFromRun(
17722
17851
  if (!row) {
17723
17852
  throw new Error("scheduled reusable-session materialization returned no revision");
17724
17853
  }
17854
+ const materializedTask = await getScheduledTask(scopedDb, input.workspaceId, input.taskId);
17855
+ if (!materializedTask || materializedTask.authorityRevision !== Number(row.authorityRevision))
17856
+ throw new Error("scheduled host materialization revision changed");
17857
+ await cloneHostMcpTaskAuthorities(
17858
+ scopedDb,
17859
+ materializedTask,
17860
+ input.sourceTaskAuthorityRevision,
17861
+ );
17862
+ await cloneExternalLinkTaskAuthority(
17863
+ scopedDb,
17864
+ materializedTask,
17865
+ input.sourceTaskAuthorityRevision,
17866
+ );
17725
17867
  return Number(row.authorityRevision);
17726
17868
  },
17727
17869
  );
@@ -17861,38 +18003,7 @@ export async function getScheduledTaskRevisionAuthoritySubject(
17861
18003
  );
17862
18004
  }
17863
18005
 
17864
- export async function getScheduledTaskRevisionAuthority(
17865
- db: Database,
17866
- input: {
17867
- accountId: string;
17868
- workspaceId: string;
17869
- taskId: string;
17870
- taskAuthorityRevision: number;
17871
- },
17872
- ): Promise<{
17873
- subjectId: string;
17874
- organizationMembershipId: string;
17875
- membershipAuthorizationRevision: number;
17876
- } | null> {
17877
- return await withRlsContext(
17878
- db,
17879
- { accountId: input.accountId, workspaceId: input.workspaceId },
17880
- async (scopedDb) => {
17881
- const [row] = await rawRows<{ authority: unknown }>(
17882
- scopedDb,
17883
- sql`select scheduled_task_revision_authority_snapshot(
17884
- ${input.accountId}::uuid,
17885
- ${input.workspaceId}::uuid,
17886
- ${input.taskId}::uuid,
17887
- ${input.taskAuthorityRevision}::bigint
17888
- ) as authority`,
17889
- );
17890
- const authority = row?.authority;
17891
- if (!authority) return null;
17892
- return ScheduledTaskRunAcceptedExecution.shape.causalHumanAuthority.parse(authority);
17893
- },
17894
- );
17895
- }
18006
+ export { getScheduledTaskRevisionAuthority } from "./scheduled-task-revision-authority";
17896
18007
 
17897
18008
  /** Failure settlement must not rewrite a source already committed as dispatched. */
17898
18009
  export async function markScheduledTaskRunFailedIfQueued(
@@ -22606,6 +22717,17 @@ async function lockOrganizationCodexSubscriptionSources(
22606
22717
  order by workspace_id
22607
22718
  `);
22608
22719
  const workspaceIds = rows.map((row: { workspace_id: string }) => row.workspace_id);
22720
+ for (const workspaceId of workspaceIds) {
22721
+ // Credential deletion clears session pins/last-used references through FK
22722
+ // SET NULL, including references in Personal or no-longer-inheriting
22723
+ // workspaces. Fence the complete authorized inventory before any source,
22724
+ // pool, or credential lock; the FK's session writes cannot acquire this
22725
+ // prefix after taking their row locks. Keep the UUID order across both
22726
+ // passes, matching ordinary workspace writers' tenancy -> source order.
22727
+ await scopedDb.execute(
22728
+ sql`select pg_advisory_xact_lock_shared(hashtextextended(${`session-tenancy:${workspaceId}`}, 0))`,
22729
+ );
22730
+ }
22609
22731
  for (const workspaceId of workspaceIds) {
22610
22732
  // Organization credential mutations can change automatic routing in every
22611
22733
  // inheriting workspace. Acquire all workspace source locks before the
@@ -31444,11 +31566,16 @@ async function setScheduledTaskAuthorityRlsContext(
31444
31566
  ): Promise<void> {
31445
31567
  const subjectId = frozen.initiator.subjectId.trim();
31446
31568
  if (!subjectId) throw new Error("scheduled task authority writer has no subject");
31569
+ // frozenSessionCreatorForInsert verified the agent attempt before reading
31570
+ // this separate causal-human field. Keep service audit attribution intact;
31571
+ // never substitute the session creator or parse a caller's provenance JSON.
31572
+ const causalHumanSubjectId =
31573
+ frozen.initiatingHumanSubjectId ?? (frozen.initiator.kind === "subject" ? subjectId : "");
31447
31574
  await tx.execute(sql`select
31448
31575
  set_config('opengeni.subject_id', ${subjectId}, true),
31449
31576
  set_config(
31450
31577
  'opengeni.initiating_human_subject_id',
31451
- ${frozen.initiator.kind === "subject" ? subjectId : ""},
31578
+ ${causalHumanSubjectId},
31452
31579
  true
31453
31580
  )`);
31454
31581
  }
@@ -31485,13 +31612,15 @@ export type SessionCreateInput = {
31485
31612
  /** Agent-to-agent reach (migration 0427); omitted means the workspace default. */
31486
31613
  agentAccess?: SessionAgentAccess;
31487
31614
  /** Opaque end-user label; omitted or null means none. */
31488
- endUser?: SessionEndUser | null;
31615
+ scopeSubjectId?: SessionScopeSubjectId | null;
31489
31616
  /** Typed Memory selector (migration 0427); omitted means the workspace layer. */
31490
31617
  memoryScope?: SessionMemoryScope;
31491
31618
  parentSessionId?: string | null;
31492
31619
  createIdempotencyKey?: string | null;
31493
31620
  /** Exact explicit installed-Skill selection used for keyed-create replay. */
31494
31621
  selectedInstalledSkillIds?: string[];
31622
+ /** Backend-only replay identity; does not admit host credential authority. */
31623
+ selectedHostMcpDelegations?: HostMcpCreateSelection[];
31495
31624
  sandboxGroupId?: string | null;
31496
31625
  sandboxOs?: SandboxOs;
31497
31626
  /** Exact accepted generated-session compaction policy; internal lifecycle callers only. */
@@ -31785,10 +31914,11 @@ type SessionCreateReplayIdentity = {
31785
31914
  visibility?: "user_private" | "workspace_shared";
31786
31915
  variableSetIds: string[];
31787
31916
  selectedInstalledSkillIds: string[];
31917
+ selectedHostMcpDelegations?: HostMcpCreateSelection[];
31788
31918
  initialPersonalResourceAttachmentIntent?: PersonalResourceAttachmentIntent | null;
31789
31919
  /** Access/memory scope of the retrying request; omitted means the defaults. */
31790
31920
  agentAccess?: SessionAgentAccess;
31791
- endUser?: SessionEndUser | null;
31921
+ scopeSubjectId?: SessionScopeSubjectId | null;
31792
31922
  memoryScope?: SessionMemoryScope;
31793
31923
  };
31794
31924
 
@@ -31823,6 +31953,12 @@ function assertSessionCreateReplayIdentity(
31823
31953
  existing: typeof schema.sessions.$inferSelect,
31824
31954
  input: SessionCreateReplayIdentity,
31825
31955
  ): void {
31956
+ if (
31957
+ stableJson(hostCreateSelectionFromMetadata(existing.metadata)) !==
31958
+ stableJson(normalizeHostCreateSelection(input.selectedHostMcpDelegations))
31959
+ ) {
31960
+ throw new SessionCreateIdempotencyConflictError();
31961
+ }
31826
31962
  if (
31827
31963
  stableJson(bundledSkillSelectionFromMetadata(existing.metadata) ?? null) !==
31828
31964
  stableJson(input.bundledSkillIds ? [...input.bundledSkillIds].sort() : null)
@@ -31856,8 +31992,7 @@ function assertSessionCreateReplayIdentity(
31856
31992
  if (
31857
31993
  existing.agentAccess !== (input.agentAccess ?? "workspace") ||
31858
31994
  existing.memoryScope !== (input.memoryScope ?? "workspace") ||
31859
- (existing.endUserSource ?? null) !== (input.endUser?.source ?? null) ||
31860
- (existing.endUserId ?? null) !== (input.endUser?.id ?? null)
31995
+ (input.scopeSubjectId !== undefined && sessionEndUserFromRow(existing) !== input.scopeSubjectId)
31861
31996
  ) {
31862
31997
  throw new SessionCreateIdempotencyConflictError();
31863
31998
  }
@@ -31954,7 +32089,10 @@ async function createSessionInTransaction(
31954
32089
  const variableSetId = variableSetIds.at(-1) ?? null;
31955
32090
  const selectedInstalledSkillIds = input.selectedInstalledSkillIds ?? [];
31956
32091
  const sessionMetadata = metadataWithSelectedInstalledSkillCreateIdentity(
31957
- withBundledSkillSelectionMetadata(input.metadata, input.bundledSkillIds),
32092
+ metadataWithHostCreateSelection(
32093
+ withBundledSkillSelectionMetadata(input.metadata, input.bundledSkillIds),
32094
+ input.selectedHostMcpDelegations,
32095
+ ),
31958
32096
  selectedInstalledSkillIds,
31959
32097
  );
31960
32098
  const createIdempotencyKey = input.createIdempotencyKey ?? null;
@@ -32009,10 +32147,11 @@ async function createSessionInTransaction(
32009
32147
  visibility: createRequestedVisibility,
32010
32148
  variableSetIds,
32011
32149
  selectedInstalledSkillIds: input.selectedInstalledSkillIds ?? [],
32150
+ selectedHostMcpDelegations: input.selectedHostMcpDelegations ?? [],
32012
32151
  initialPersonalResourceAttachmentIntent:
32013
32152
  input.initialPersonalResourceAttachmentIntent ?? null,
32014
32153
  ...(input.agentAccess ? { agentAccess: input.agentAccess } : {}),
32015
- ...(input.endUser !== undefined ? { endUser: input.endUser } : {}),
32154
+ ...(input.scopeSubjectId !== undefined ? { scopeSubjectId: input.scopeSubjectId } : {}),
32016
32155
  ...(input.memoryScope ? { memoryScope: input.memoryScope } : {}),
32017
32156
  });
32018
32157
  const grouped = await sessionMcpServerMetadataForSessions(tx, input.workspaceId, [
@@ -32169,8 +32308,17 @@ async function createSessionInTransaction(
32169
32308
  instructions: input.instructions ?? null,
32170
32309
  policyRole: input.policyRole ?? null,
32171
32310
  agentAccess: input.agentAccess ?? "workspace",
32172
- endUserSource: input.endUser?.source ?? null,
32173
- endUserId: input.endUser?.id ?? null,
32311
+ scopeSubjectId:
32312
+ input.scopeSubjectId !== undefined
32313
+ ? input.scopeSubjectId
32314
+ : frozenCreator.initiator.kind === "subject" &&
32315
+ /^(?:user:|external_user:)/u.test(frozenCreator.initiator.subjectId)
32316
+ ? frozenCreator.initiator.subjectId
32317
+ : null,
32318
+ // Retained columns are historical only. Canonical scope is derived
32319
+ // from the creator resolved under the admission locks above.
32320
+ endUserSource: null,
32321
+ endUserId: null,
32174
32322
  memoryScope: input.memoryScope ?? "workspace",
32175
32323
  parentSessionId: input.parentSessionId ?? null,
32176
32324
  parentTurnId,
@@ -32223,10 +32371,11 @@ async function createSessionInTransaction(
32223
32371
  visibility: createRequestedVisibility,
32224
32372
  variableSetIds,
32225
32373
  selectedInstalledSkillIds: input.selectedInstalledSkillIds ?? [],
32374
+ selectedHostMcpDelegations: input.selectedHostMcpDelegations ?? [],
32226
32375
  initialPersonalResourceAttachmentIntent:
32227
32376
  input.initialPersonalResourceAttachmentIntent ?? null,
32228
32377
  ...(input.agentAccess ? { agentAccess: input.agentAccess } : {}),
32229
- ...(input.endUser !== undefined ? { endUser: input.endUser } : {}),
32378
+ ...(input.scopeSubjectId !== undefined ? { scopeSubjectId: input.scopeSubjectId } : {}),
32230
32379
  ...(input.memoryScope ? { memoryScope: input.memoryScope } : {}),
32231
32380
  });
32232
32381
  const grouped = await sessionMcpServerMetadataForSessions(tx, input.workspaceId, [
@@ -32418,6 +32567,7 @@ export async function getInitializedSessionCreateReplay(
32418
32567
  visibility?: "user_private" | "workspace_shared";
32419
32568
  variableSetIds: string[];
32420
32569
  selectedInstalledSkillIds: string[];
32570
+ selectedHostMcpDelegations?: HostMcpCreateSelection[];
32421
32571
  initialPersonalResourceAttachmentIntent?: PersonalResourceAttachmentIntent | null;
32422
32572
  deferInitialTurn?: boolean;
32423
32573
  },
@@ -32876,7 +33026,7 @@ export type SessionAccessProjection = {
32876
33026
  sessionId: string;
32877
33027
  rootSessionId: string;
32878
33028
  agentAccess: SessionAgentAccess;
32879
- endUser: SessionEndUser | null;
33029
+ scopeSubjectId: SessionScopeSubjectId | null;
32880
33030
  memoryScope: SessionMemoryScope;
32881
33031
  };
32882
33032
 
@@ -32887,7 +33037,7 @@ export type SessionAuthorityProjection = SessionAccessProjection & {
32887
33037
 
32888
33038
  type SessionAccessRow = Pick<
32889
33039
  typeof schema.sessions.$inferSelect,
32890
- "agentAccess" | "endUserSource" | "endUserId" | "memoryScope"
33040
+ "agentAccess" | "scopeSubjectId" | "memoryScope"
32891
33041
  >;
32892
33042
 
32893
33043
  function sessionAgentAccessFromRow(row: Pick<SessionAccessRow, "agentAccess">): SessionAgentAccess {
@@ -32897,20 +33047,18 @@ function sessionAgentAccessFromRow(row: Pick<SessionAccessRow, "agentAccess">):
32897
33047
  }
32898
33048
 
32899
33049
  function sessionEndUserFromRow(
32900
- row: Pick<SessionAccessRow, "endUserSource" | "endUserId">,
32901
- ): SessionEndUser | null {
32902
- return row.endUserSource !== null &&
32903
- row.endUserSource !== undefined &&
32904
- row.endUserId !== null &&
32905
- row.endUserId !== undefined
32906
- ? { source: row.endUserSource, id: row.endUserId }
33050
+ row: Pick<SessionAccessRow, "scopeSubjectId">,
33051
+ ): SessionScopeSubjectId | null {
33052
+ return typeof row.scopeSubjectId === "string" &&
33053
+ /^(?:user:|external_user:)/u.test(row.scopeSubjectId)
33054
+ ? row.scopeSubjectId
32907
33055
  : null;
32908
33056
  }
32909
33057
 
32910
33058
  function sessionMemoryScopeFromRow(row: Pick<SessionAccessRow, "memoryScope">): SessionMemoryScope {
32911
- return row.memoryScope === "user" || row.memoryScope === "session" || row.memoryScope === "off"
32912
- ? row.memoryScope
32913
- : "workspace";
33059
+ // Legacy task-local Memory is no longer active. Never widen it to workspace.
33060
+ if (row.memoryScope === "session") return "off";
33061
+ return row.memoryScope === "user" || row.memoryScope === "off" ? row.memoryScope : "workspace";
32914
33062
  }
32915
33063
 
32916
33064
  export async function getSessionAuthorityProjection(
@@ -32926,8 +33074,7 @@ export async function getSessionAuthorityProjection(
32926
33074
  visibility: schema.sessions.visibility,
32927
33075
  ownerSubjectId: schema.sessions.ownerSubjectId,
32928
33076
  agentAccess: schema.sessions.agentAccess,
32929
- endUserSource: schema.sessions.endUserSource,
32930
- endUserId: schema.sessions.endUserId,
33077
+ scopeSubjectId: schema.sessions.scopeSubjectId,
32931
33078
  memoryScope: schema.sessions.memoryScope,
32932
33079
  })
32933
33080
  .from(schema.sessions)
@@ -32940,7 +33087,7 @@ export async function getSessionAuthorityProjection(
32940
33087
  visibility: row.visibility as SessionAuthorityProjection["visibility"],
32941
33088
  ownerSubjectId: row.ownerSubjectId ?? null,
32942
33089
  agentAccess: sessionAgentAccessFromRow(row),
32943
- endUser: sessionEndUserFromRow(row),
33090
+ scopeSubjectId: sessionEndUserFromRow(row),
32944
33091
  memoryScope: sessionMemoryScopeFromRow(row),
32945
33092
  };
32946
33093
  });
@@ -32958,33 +33105,52 @@ export async function getSessionAccessProjection(
32958
33105
  sessionId: authority.sessionId,
32959
33106
  rootSessionId: authority.rootSessionId,
32960
33107
  agentAccess: authority.agentAccess,
32961
- endUser: authority.endUser,
33108
+ scopeSubjectId: authority.scopeSubjectId,
32962
33109
  memoryScope: authority.memoryScope,
32963
33110
  };
32964
33111
  }
32965
33112
 
32966
33113
  /**
32967
33114
  * The typed Memory selector an agent on this session reads and writes. The
32968
- * end-user subject is the opaque `end_user:v1:<tuple hash>` label and the
32969
- * session selector is the lineage root, so one tree shares one private layer.
33115
+ * user subject comes from the verified active execution, not a conversation
33116
+ * label or its original creator. Task notes own temporary tree-local data.
32970
33117
  * Null when the session does not exist in the workspace.
32971
33118
  */
32972
33119
  export async function resolveSessionMemoryAgentScope(
32973
33120
  db: Database,
32974
33121
  workspaceId: string,
32975
33122
  sessionId: string,
33123
+ caller?: { turnId?: unknown; attemptId?: unknown; executionGeneration?: unknown },
32976
33124
  ): Promise<MemoryAgentScope | null> {
32977
33125
  const access = await getSessionAccessProjection(db, workspaceId, sessionId);
32978
33126
  if (!access) return null;
33127
+ if (access.memoryScope === "user") {
33128
+ const turn = await getActiveSessionTurnForExecution(db, workspaceId, sessionId);
33129
+ const subjectId =
33130
+ turn?.initiatingHumanSubjectId ??
33131
+ (turn?.initiator.kind === "subject" ? turn.initiator.subjectId : null);
33132
+ if (
33133
+ !turn ||
33134
+ !caller ||
33135
+ turn.id !== caller.turnId ||
33136
+ turn.activeAttemptId !== caller.attemptId ||
33137
+ turn.executionGeneration !== caller.executionGeneration ||
33138
+ !subjectId ||
33139
+ !/^(?:user:|external_user:)/u.test(subjectId)
33140
+ ) {
33141
+ return { mode: "off", userSubjectId: null, rootSessionId: access.rootSessionId };
33142
+ }
33143
+ return memoryAgentScopeForSessionAccess({ ...access, scopeSubjectId: subjectId });
33144
+ }
32979
33145
  return memoryAgentScopeForSessionAccess(access);
32980
33146
  }
32981
33147
 
32982
33148
  export function memoryAgentScopeForSessionAccess(
32983
- access: Pick<SessionAccessProjection, "rootSessionId" | "endUser" | "memoryScope">,
33149
+ access: Pick<SessionAccessProjection, "rootSessionId" | "scopeSubjectId" | "memoryScope">,
32984
33150
  ): MemoryAgentScope {
32985
33151
  return {
32986
33152
  mode: access.memoryScope,
32987
- endUserSubjectId: access.endUser ? endUserMemorySubjectId(access.endUser) : null,
33153
+ userSubjectId: access.scopeSubjectId,
32988
33154
  rootSessionId: access.rootSessionId,
32989
33155
  };
32990
33156
  }
@@ -33392,7 +33558,7 @@ export type SessionListFilterOptions = {
33392
33558
  /** Exclusive creation upper bound. */
33393
33559
  createdBefore?: Date;
33394
33560
  /** Exact opaque end-user label pair (both parts). */
33395
- endUser?: SessionEndUser;
33561
+ scopeSubjectId?: SessionScopeSubjectId;
33396
33562
  };
33397
33563
 
33398
33564
  export type ListSessionsForSubjectOptions = ListSessionsOptions &
@@ -33423,13 +33589,13 @@ export type ListSessionsForSubjectOptions = ListSessionsOptions &
33423
33589
  *
33424
33590
  * **This flag is the authorization.** The resolver it gates is not: it answers
33425
33591
  * "does subject X hold authority here" and establishes nothing about who the
33426
- * caller is. The exception is for "the canonical managed-cookie (Better Auth)
33427
- * session" only "Bearer/delegated principals, API keys, and account or
33428
- * organization administrators receive no personal-workspace access through that
33429
- * exception" — and a host-signed delegated bearer chooses its own `subjectId`,
33592
+ * caller is. The exception requires verified native-cookie or dedicated
33593
+ * external owning-user provenance. Unscoped service keys and account or
33594
+ * organization administrators receive no Personal access through it.
33595
+ * A host-signed delegated bearer chooses its own `subjectId`,
33430
33596
  * `principalKind`, `metadata.delegated`, and `serviceInitiator` claims, so no
33431
33597
  * inspection of the grant can carry that distinction. Set this ONLY from
33432
- * `AccessGrantAuthorization.canonicalManagedHumanSession` (`@opengeni/core`),
33598
+ * `hasVerifiedOwningUserAuthorization` (`@opengeni/core`),
33433
33599
  * which reflects HOW the request authenticated. Omitted/false keeps the
33434
33600
  * historical bare-membership fence exactly.
33435
33601
  */
@@ -34189,7 +34355,7 @@ function sessionFilters(
34189
34355
  | "updatedBefore"
34190
34356
  | "createdFrom"
34191
34357
  | "createdBefore"
34192
- | "endUser"
34358
+ | "scopeSubjectId"
34193
34359
  >,
34194
34360
  ): SQL[] {
34195
34361
  const filters: SQL[] = [
@@ -34258,43 +34424,30 @@ function sessionFilters(
34258
34424
  if (options.updatedBefore) filters.push(lt(schema.sessions.updatedAt, options.updatedBefore));
34259
34425
  if (options.createdFrom) filters.push(gte(schema.sessions.createdAt, options.createdFrom));
34260
34426
  if (options.createdBefore) filters.push(lt(schema.sessions.createdAt, options.createdBefore));
34261
- if (options.endUser) {
34262
- filters.push(
34263
- eq(schema.sessions.endUserSource, options.endUser.source),
34264
- eq(schema.sessions.endUserId, options.endUser.id),
34265
- );
34427
+ if (options.scopeSubjectId) {
34428
+ filters.push(eq(schema.sessions.scopeSubjectId, options.scopeSubjectId));
34266
34429
  }
34267
34430
  return filters;
34268
34431
  }
34269
34432
 
34270
34433
  /**
34271
34434
  * The agent-access predicate for one calling attempt (migration 0427). It
34272
- * mirrors the pairwise rule in the core seam exactly: a caller always keeps
34273
- * its own root tree; a `session` caller sees nothing else; a `user` caller
34274
- * additionally sees non-`session` sessions carrying its own end-user label; a
34275
- * `workspace` caller additionally sees every `workspace` session and the
34276
- * `user` sessions carrying its own label. A caller without a label can never
34277
- * match a labelled `user` session.
34435
+ * mirrors outgoing reach in the core seam: own tree, same canonical user, or
34436
+ * workspace. Target task scope never hides the conversation. This predicate
34437
+ * remains intersected with host scope and ordinary private-session visibility.
34278
34438
  */
34279
34439
  export function sessionAgentAccessViewerFilter(viewer: SessionAgentAccessViewer): SQL {
34280
34440
  const ownTree = eq(schema.sessions.rootSessionId, viewer.callerRootSessionId);
34281
- const sameEndUser = viewer.endUser
34282
- ? and(
34283
- eq(schema.sessions.endUserSource, viewer.endUser.source),
34284
- eq(schema.sessions.endUserId, viewer.endUser.id),
34285
- )!
34441
+ const sameUser = viewer.scopeSubjectId
34442
+ ? and(eq(schema.sessions.scopeSubjectId, viewer.scopeSubjectId))!
34286
34443
  : sql`false`;
34287
34444
  switch (viewer.agentAccess) {
34288
34445
  case "session":
34289
34446
  return ownTree;
34290
34447
  case "user":
34291
- return or(ownTree, and(ne(schema.sessions.agentAccess, "session"), sameEndUser)!)!;
34448
+ return or(ownTree, sameUser)!;
34292
34449
  case "workspace":
34293
- return or(
34294
- ownTree,
34295
- eq(schema.sessions.agentAccess, "workspace"),
34296
- and(eq(schema.sessions.agentAccess, "user"), sameEndUser)!,
34297
- )!;
34450
+ return sql`true`;
34298
34451
  }
34299
34452
  }
34300
34453
 
@@ -34431,7 +34584,7 @@ function sessionListFilterIdentity(options: SessionListFilterOptions): string {
34431
34584
  !options.updatedBefore &&
34432
34585
  !options.createdFrom &&
34433
34586
  !options.createdBefore &&
34434
- !options.endUser
34587
+ !options.scopeSubjectId
34435
34588
  ) {
34436
34589
  return "all";
34437
34590
  }
@@ -34443,7 +34596,7 @@ function sessionListFilterIdentity(options: SessionListFilterOptions): string {
34443
34596
  options.updatedBefore ? ["updatedBefore", options.updatedBefore.toISOString()] : null,
34444
34597
  options.createdFrom ? ["createdFrom", options.createdFrom.toISOString()] : null,
34445
34598
  options.createdBefore ? ["createdBefore", options.createdBefore.toISOString()] : null,
34446
- options.endUser ? ["endUser", options.endUser.source, options.endUser.id] : null,
34599
+ options.scopeSubjectId ? ["scopeSubjectId", options.scopeSubjectId] : null,
34447
34600
  ]);
34448
34601
  }
34449
34602
 
@@ -49787,6 +49940,15 @@ async function advanceWorkspaceGenerationForAuthorityOnce(
49787
49940
  await scopedDb.transaction(async (txRaw) => {
49788
49941
  const tx = txRaw as unknown as Database;
49789
49942
  const locked = await lockWorkspaceMutationAuthorityTx(tx, authority);
49943
+ // Keep the admission UPDATE and its rejection diagnostic on one lease
49944
+ // state. A concurrent capture release between these READ COMMITTED
49945
+ // statements otherwise turns a recoverable capture wait into a false
49946
+ // lease_fenced error. Take this after the canonical actor/attempt prefix.
49947
+ await tx.execute(sql`select id from sandbox_leases
49948
+ where account_id = ${locked.accountId}
49949
+ and workspace_id = ${locked.workspaceId}
49950
+ and sandbox_group_id = ${locked.sandboxGroupId}
49951
+ for update`);
49790
49952
  const rows = await tx.execute<{
49791
49953
  id: string;
49792
49954
  lease_id: string;
@@ -53287,13 +53449,30 @@ export async function adoptLegacyModalCheckpointArtifact(
53287
53449
  }
53288
53450
 
53289
53451
  function publishedWorkspaceArchiveFields(input: {
53452
+ accountId: string;
53453
+ workspaceId: string;
53454
+ sandboxGroupId: string;
53455
+ workspaceArchiveMeta: SandboxArchiveRevision;
53290
53456
  workspaceArchive?: string | null;
53291
53457
  workspaceArchiveRef?: WorkspaceArchiveObjectRef | null;
53292
53458
  }): {
53293
53459
  workspaceArchive?: string;
53294
53460
  workspaceArchiveRef?: WorkspaceArchiveObjectRef;
53295
53461
  } {
53296
- const ref = parseWorkspaceArchiveObjectRef(input.workspaceArchiveRef) ?? undefined;
53462
+ const ref =
53463
+ input.workspaceArchiveRef == null
53464
+ ? undefined
53465
+ : (validateWorkspaceArchiveObjectRef(input.workspaceArchiveRef, {
53466
+ accountId: input.accountId,
53467
+ workspaceId: input.workspaceId,
53468
+ sandboxGroupId: input.sandboxGroupId,
53469
+ descriptor: input.workspaceArchiveMeta,
53470
+ }) ?? undefined);
53471
+ if (input.workspaceArchiveRef != null && !ref) {
53472
+ throw new Error(
53473
+ "Invalid workspace archive object ref: publication scope or descriptor mismatch",
53474
+ );
53475
+ }
53297
53476
  const inline =
53298
53477
  typeof input.workspaceArchive === "string" && input.workspaceArchive.length > 0
53299
53478
  ? input.workspaceArchive
@@ -53314,6 +53493,30 @@ function publishedWorkspaceArchiveFields(input: {
53314
53493
  return { workspaceArchive: inline };
53315
53494
  }
53316
53495
 
53496
+ /** Object-candidate outcome from the publication transaction, independent of
53497
+ * lifecycle `wrote`. Absent for inline/provider receipts and pure CAS checks.
53498
+ * `unused` is NOT a retirement receipt: only the owner of a fresh candidate with
53499
+ * no other publication in flight may use it for immediate cleanup. Exceptions
53500
+ * give no disposition (commit may have happened). Legacy/shared keys and evicted
53501
+ * refs need durable no-reattachment evidence before deletion. */
53502
+ export type WorkspaceArchiveCandidateDisposition = "adopted" | "already_referenced" | "unused";
53503
+
53504
+ function workspaceArchiveCandidateFields(
53505
+ candidate: WorkspaceArchiveObjectRef | undefined,
53506
+ lockedResumeState: Record<string, unknown> | null | undefined,
53507
+ ): { candidateDisposition?: WorkspaceArchiveCandidateDisposition } {
53508
+ if (!candidate) return {};
53509
+ const sessionState = lockedResumeState?.sessionState as Record<string, unknown> | undefined;
53510
+ const referenced = [
53511
+ sessionState?.workspaceArchiveRef,
53512
+ sessionState?.workspaceArchivePrevRef,
53513
+ ].some((value) => {
53514
+ const ref = parseWorkspaceArchiveObjectRef(value);
53515
+ return ref?.key === candidate.key && ref.backend === candidate.backend;
53516
+ });
53517
+ return { candidateDisposition: referenced ? "already_referenced" : "unused" };
53518
+ }
53519
+
53317
53520
  export async function persistDrainSnapshot(
53318
53521
  db: Database,
53319
53522
  input: {
@@ -53350,12 +53553,14 @@ export async function persistDrainSnapshot(
53350
53553
  ): Promise<{
53351
53554
  wrote: boolean;
53352
53555
  archiveRevision: string | null;
53556
+ candidateDisposition?: WorkspaceArchiveCandidateDisposition;
53353
53557
  }> {
53354
53558
  const workspaceArchiveMeta =
53355
53559
  input.workspaceArchive === null ? null : parseArchiveRevision(input.workspaceArchiveMeta);
53356
53560
  if (input.workspaceArchive !== null && !workspaceArchiveMeta) {
53357
53561
  throw new Error("Invalid verified workspace archive descriptor");
53358
53562
  }
53563
+ const published = input.workspaceArchive === null ? null : publishedWorkspaceArchiveFields(input);
53359
53564
  if (
53360
53565
  workspaceArchiveMeta?.version === 2 &&
53361
53566
  (workspaceArchiveMeta.provider === "modal_snapshot_filesystem" ||
@@ -53414,10 +53619,15 @@ export async function persistDrainSnapshot(
53414
53619
  for update
53415
53620
  `);
53416
53621
  const row = guard[0];
53622
+ const candidateFields = workspaceArchiveCandidateFields(
53623
+ published?.workspaceArchiveRef,
53624
+ row?.resume_state,
53625
+ );
53417
53626
  if (!row) {
53418
53627
  return {
53419
53628
  wrote: false,
53420
53629
  archiveRevision: null,
53630
+ ...candidateFields,
53421
53631
  };
53422
53632
  }
53423
53633
  const sourceLeaseMatches =
@@ -53463,7 +53673,7 @@ export async function persistDrainSnapshot(
53463
53673
  lateReceipt.providerRequestId === input.providerRequestId &&
53464
53674
  !row.unsettled_mutation;
53465
53675
  if (!activePublication && !coldLatePublication) {
53466
- return { wrote: false, archiveRevision: null };
53676
+ return { wrote: false, archiveRevision: null, ...candidateFields };
53467
53677
  }
53468
53678
  const priorArchive = row.prior_archive ?? null;
53469
53679
  const priorArchivePrev = row.prior_archive_prev ?? null;
@@ -53481,7 +53691,7 @@ export async function persistDrainSnapshot(
53481
53691
  archiveRevision: null,
53482
53692
  };
53483
53693
  }
53484
- const published = publishedWorkspaceArchiveFields(input);
53694
+ if (!published) throw new Error("Missing workspace archive publication fields");
53485
53695
  const priorMeta = parseArchiveRevision(priorSessionState?.workspaceArchiveMeta);
53486
53696
  if (activePublication && row.archive_capture_published_at !== null) {
53487
53697
  // A predecessor and its successor may receive the same provider result.
@@ -53507,7 +53717,7 @@ export async function persistDrainSnapshot(
53507
53717
  )
53508
53718
  `);
53509
53719
  }
53510
- return { wrote: true, archiveRevision: priorMeta.revision };
53720
+ return { wrote: true, archiveRevision: priorMeta.revision, ...candidateFields };
53511
53721
  }
53512
53722
  const rotation = rotateWorkspaceArchives({
53513
53723
  resumeState: row.resume_state,
@@ -53568,11 +53778,13 @@ export async function persistDrainSnapshot(
53568
53778
  return {
53569
53779
  wrote: false,
53570
53780
  archiveRevision: null,
53781
+ ...candidateFields,
53571
53782
  };
53572
53783
  }
53573
53784
  return {
53574
53785
  wrote: true,
53575
53786
  archiveRevision: workspaceArchiveMeta?.revision ?? null,
53787
+ ...(published.workspaceArchiveRef ? { candidateDisposition: "adopted" as const } : {}),
53576
53788
  };
53577
53789
  },
53578
53790
  );
@@ -53971,6 +54183,7 @@ export async function persistWarmSnapshot(
53971
54183
  throttled: boolean;
53972
54184
  superseded: boolean;
53973
54185
  archiveRevision: string | null;
54186
+ candidateDisposition?: WorkspaceArchiveCandidateDisposition;
53974
54187
  }> {
53975
54188
  const capturedAtMs = input.capturedAtMs ?? Date.now();
53976
54189
  const workspaceArchiveMeta = parseArchiveRevision(input.workspaceArchiveMeta);
@@ -54040,6 +54253,26 @@ export async function persistWarmSnapshot(
54040
54253
  (attempt.outcome === "completed" ||
54041
54254
  attempt.outcome === "failed" ||
54042
54255
  attempt.outcome === "requires_action"));
54256
+ // Even a replay rejected by the attempt/epoch/capture guards may name
54257
+ // a committed current or previous object. Inspect those slots under the
54258
+ // same lease lock as publication, after the canonical workspace lock.
54259
+ // This conveys no retirement or no-future-reattachment authority.
54260
+ const candidateLease = published.workspaceArchiveRef
54261
+ ? await scopedDb.execute<{ resume_state: Record<string, unknown> | null }>(sql`
54262
+ select jsonb_build_object('sessionState', jsonb_build_object(
54263
+ 'workspaceArchiveRef', resume_state #> '{sessionState,workspaceArchiveRef}',
54264
+ 'workspaceArchivePrevRef', resume_state #> '{sessionState,workspaceArchivePrevRef}'
54265
+ )) as resume_state
54266
+ from sandbox_leases
54267
+ where workspace_id = ${input.workspaceId}
54268
+ and sandbox_group_id = ${input.sandboxGroupId}
54269
+ for update
54270
+ `)
54271
+ : [];
54272
+ const candidateFields = workspaceArchiveCandidateFields(
54273
+ published.workspaceArchiveRef,
54274
+ candidateLease[0]?.resume_state,
54275
+ );
54043
54276
  if (
54044
54277
  !attempt ||
54045
54278
  attempt.accountId !== input.accountId ||
@@ -54052,6 +54285,7 @@ export async function persistWarmSnapshot(
54052
54285
  throttled: false,
54053
54286
  superseded: true,
54054
54287
  archiveRevision: null,
54288
+ ...candidateFields,
54055
54289
  };
54056
54290
  }
54057
54291
  const guard = await scopedDb.execute<{
@@ -54109,6 +54343,7 @@ export async function persistWarmSnapshot(
54109
54343
  throttled: false,
54110
54344
  superseded: false,
54111
54345
  archiveRevision: null,
54346
+ ...candidateFields,
54112
54347
  };
54113
54348
  }
54114
54349
  const priorArchive = guard[0]!.prior_archive ?? null;
@@ -54129,6 +54364,7 @@ export async function persistWarmSnapshot(
54129
54364
  throttled: false,
54130
54365
  superseded: true,
54131
54366
  archiveRevision: null,
54367
+ ...candidateFields,
54132
54368
  };
54133
54369
  }
54134
54370
  if (
@@ -54141,6 +54377,7 @@ export async function persistWarmSnapshot(
54141
54377
  throttled: true,
54142
54378
  superseded: false,
54143
54379
  archiveRevision: null,
54380
+ ...candidateFields,
54144
54381
  };
54145
54382
  }
54146
54383
  const rotation = rotateWorkspaceArchives({
@@ -54162,6 +54399,7 @@ export async function persistWarmSnapshot(
54162
54399
  throttled: false,
54163
54400
  superseded: false,
54164
54401
  archiveRevision: null,
54402
+ ...candidateFields,
54165
54403
  };
54166
54404
  }
54167
54405
  const livenessGuard: "warm" | "draining" = rowLiveness;
@@ -54194,6 +54432,7 @@ export async function persistWarmSnapshot(
54194
54432
  throttled: false,
54195
54433
  superseded: false,
54196
54434
  archiveRevision: null,
54435
+ ...candidateFields,
54197
54436
  };
54198
54437
  }
54199
54438
  return {
@@ -54201,6 +54440,7 @@ export async function persistWarmSnapshot(
54201
54440
  throttled: false,
54202
54441
  superseded: false,
54203
54442
  archiveRevision: workspaceArchiveMeta.revision,
54443
+ ...(published.workspaceArchiveRef ? { candidateDisposition: "adopted" as const } : {}),
54204
54444
  };
54205
54445
  },
54206
54446
  );
@@ -54451,6 +54691,21 @@ async function commitWorkspaceCaptureRevision(
54451
54691
  return null;
54452
54692
  }
54453
54693
 
54694
+ // Capture yields when new work is accepted. Its last database call can
54695
+ // still be in flight when that cancellation arrives, so serialize this
54696
+ // fence with enqueue/claim on the session row and refuse older reviews.
54697
+ // Position is priority order, not chronology.
54698
+ const successor = await tx.execute<{ id: string }>(sql`
54699
+ select id from session_turns
54700
+ where workspace_id = ${input.workspaceId}
54701
+ and session_id = ${input.sessionId}
54702
+ and id <> ${input.turnId}
54703
+ and (created_at >= (select created_at from session_turns where id = ${input.turnId})
54704
+ or status in ('queued', 'running'))
54705
+ limit 1
54706
+ `);
54707
+ if (successor.length > 0) return null;
54708
+
54454
54709
  const capturedAt = input.capturedAt ?? new Date();
54455
54710
  const rows = await tx.execute<{ revision: number | string }>(sql`
54456
54711
  insert into workspace_captures
@@ -62258,6 +62513,10 @@ export type InitializeSessionStartInput = {
62258
62513
  /** Trusted create-session policy. Omitted only by legacy low-level callers. */
62259
62514
  turnExecutionPolicy?: TurnExecutionPolicyV1;
62260
62515
  createdEventPayload: Record<string, unknown>;
62516
+ /** Trusted backend-only capture for a newly inserted initial turn. Runs under
62517
+ * the canonical activity transaction; failure rolls back events and turn.
62518
+ * Never invoked on replay or deferred starts; not caller JSON authority. */
62519
+ captureInitialTurnAuthority?: (tx: Database, turnId: string) => Promise<void>;
62261
62520
  /** Sensitive-safe semantic title supplied by the trusted agent-child create
62262
62521
  * path. It is committed with the initial timeline, never as a bare row edit. */
62263
62522
  initialAutomaticTitle?: string | null;
@@ -62314,6 +62573,8 @@ export async function initializeSessionStartAtomically(
62314
62573
  db: Database,
62315
62574
  input: InitializeSessionStartInput,
62316
62575
  ): Promise<InitializeSessionStartResult> {
62576
+ if (input.deferInitialTurn && input.captureInitialTurnAuthority)
62577
+ throw new Error("Deferred session starts cannot capture initial turn authority");
62317
62578
  return await withSessionActivityRlsContext(
62318
62579
  db,
62319
62580
  { accountId: input.accountId, workspaceId: input.workspaceId },
@@ -62844,6 +63105,30 @@ export async function initializeSessionStartAtomically(
62844
63105
  });
62845
63106
 
62846
63107
  const initialPersonalResourceIntent = session.initialPersonalResourceAttachmentIntent;
63108
+ if (insertedTurn && input.captureInitialTurnAuthority)
63109
+ await input.captureInitialTurnAuthority(tx as unknown as Database, turn.id);
63110
+ if (insertedTurn && session.parentSessionId && session.parentTurnId)
63111
+ await inheritExternalLinkTurnAuthority(tx as unknown as Database, {
63112
+ accountId: session.accountId,
63113
+ workspaceId: input.workspaceId,
63114
+ sessionId: session.id,
63115
+ turnId: turn.id,
63116
+ sourceTurnId: session.parentTurnId,
63117
+ kind: "child",
63118
+ });
63119
+ if (
63120
+ insertedTurn &&
63121
+ session.parentSessionId &&
63122
+ session.parentTurnId &&
63123
+ turn.initiatingHumanSubjectId
63124
+ )
63125
+ await inheritChildHostMcpTurnAuthorities(tx as unknown as Database, {
63126
+ accountId: session.accountId,
63127
+ workspaceId: input.workspaceId,
63128
+ sessionId: session.id,
63129
+ turnId: turn.id,
63130
+ subjectId: turn.initiatingHumanSubjectId,
63131
+ });
62847
63132
  if (initialPersonalResourceIntent) {
62848
63133
  const attachmentInitiatingHumanSubjectId =
62849
63134
  turn.initiatingHumanSubjectId ??
@@ -65811,6 +66096,39 @@ export async function claimSessionWorkForAttempt(
65811
66096
  frozenGoalSnapshot,
65812
66097
  goalContinuationHistoryItem ?? scheduledOccurrenceHistoryItem,
65813
66098
  );
66099
+ if (scheduledTaskRunId) {
66100
+ await captureScheduledExternalLinkTurnAuthority(tx as unknown as Database, {
66101
+ accountId: session.accountId,
66102
+ workspaceId,
66103
+ sessionId,
66104
+ turnId: internalTurn.id,
66105
+ runId: scheduledTaskRunId,
66106
+ });
66107
+ await captureScheduledHostMcpTurnAuthorities(tx as unknown as Database, {
66108
+ accountId: session.accountId,
66109
+ workspaceId,
66110
+ sessionId,
66111
+ turnId: internalTurn.id,
66112
+ runId: scheduledTaskRunId,
66113
+ });
66114
+ } else if (causalHumanTurnId && initiatingHumanSubjectId) {
66115
+ await inheritExternalLinkTurnAuthority(tx as unknown as Database, {
66116
+ accountId: session.accountId,
66117
+ workspaceId,
66118
+ sessionId,
66119
+ turnId: internalTurn.id,
66120
+ sourceTurnId: causalHumanTurnId,
66121
+ kind: "causal",
66122
+ });
66123
+ await inheritCausalHostMcpTurnAuthorities(tx as unknown as Database, {
66124
+ accountId: session.accountId,
66125
+ workspaceId,
66126
+ sessionId,
66127
+ subjectId: initiatingHumanSubjectId,
66128
+ sourceTurnId: causalHumanTurnId,
66129
+ targetTurnId: internalTurn.id,
66130
+ });
66131
+ }
65814
66132
  // The batch is now durable model memory. Every child it reports on has
65815
66133
  // been carried to this turn's initiating human, so their read fence on
65816
66134
  // those children advances with the same commit.
@@ -70157,6 +70475,13 @@ export async function applySessionTurnSettlement(
70157
70475
  ...request,
70158
70476
  questions: request.questions.map((question) => {
70159
70477
  const parsed = HumanInputQuestionContract.parse(question);
70478
+ if (parsed.skillReview) {
70479
+ const canonical = canonicalSkillReviewQuestion(question);
70480
+ if (!canonical || request.questions.length !== 1 || request.allowSkip) {
70481
+ throw new Error("Skill review requires the exact dedicated confirmation contract");
70482
+ }
70483
+ return canonical;
70484
+ }
70160
70485
  return parsed.kind === "text" || parsed.allowOther
70161
70486
  ? parsed
70162
70487
  : { ...parsed, allowOther: true };
@@ -70226,6 +70551,40 @@ export async function applySessionTurnSettlement(
70226
70551
  );
70227
70552
  if (humanInputRequests.length > 0) {
70228
70553
  for (const request of humanInputRequests) {
70554
+ if (request.questions.length === 1 && request.questions[0]!.skillReview) {
70555
+ const [existing] = await tx
70556
+ .select()
70557
+ .from(schema.sessionHumanInputRequests)
70558
+ .where(
70559
+ and(
70560
+ eq(schema.sessionHumanInputRequests.accountId, session.accountId),
70561
+ eq(schema.sessionHumanInputRequests.workspaceId, workspaceId),
70562
+ eq(schema.sessionHumanInputRequests.sessionId, input.sessionId),
70563
+ eq(schema.sessionHumanInputRequests.turnId, input.turnId),
70564
+ eq(schema.sessionHumanInputRequests.toolCallId, request.toolCallId),
70565
+ ),
70566
+ )
70567
+ .for("update");
70568
+ if (existing) {
70569
+ const canonical =
70570
+ existing.questions.length === 1
70571
+ ? canonicalSkillReviewQuestion(existing.questions[0]!)
70572
+ : null;
70573
+ if (
70574
+ existing.id !== request.id ||
70575
+ !canonical ||
70576
+ stableJson(canonical) !== stableJson(request.questions[0])
70577
+ ) {
70578
+ throw new Error(
70579
+ `Human-input request ${request.id} changed contract before re-freeze`,
70580
+ );
70581
+ }
70582
+ // Preserve immutable displayed/audited bytes. The unchanged
70583
+ // UPSERT predicate below still fences pending status, skip and
70584
+ // exact deadline; only generation ownership can advance.
70585
+ request.questions = existing.questions;
70586
+ }
70587
+ }
70229
70588
  const [persistedRequest] = await tx
70230
70589
  .insert(schema.sessionHumanInputRequests)
70231
70590
  .values({
@@ -72293,6 +72652,15 @@ export async function getActiveSessionTurnForExecution(
72293
72652
  )
72294
72653
  .limit(1);
72295
72654
  if (!row) return null;
72655
+ const linked = await getExternalLinkTurnAuthorization(
72656
+ scopedDb,
72657
+ {
72658
+ accountId: row.turn.accountId,
72659
+ workspaceId,
72660
+ },
72661
+ row.turn.id,
72662
+ );
72663
+ if (linked && !linked.authorized) return null;
72296
72664
  await goalSnapshotForAcceptedTurnInTransaction(scopedDb, row.turn);
72297
72665
  return mapSessionTurnForExecution(row.turn);
72298
72666
  });
@@ -72305,50 +72673,10 @@ export async function getSessionTurnForAttempt(
72305
72673
  attemptId: string,
72306
72674
  ): Promise<SessionTurnForExecution | null> {
72307
72675
  return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
72308
- const [row] = await scopedDb
72309
- .select({ turn: schema.sessionTurns })
72310
- .from(schema.sessionTurnAttempts)
72311
- .innerJoin(
72312
- schema.sessionTurns,
72313
- and(
72314
- eq(schema.sessionTurns.workspaceId, schema.sessionTurnAttempts.workspaceId),
72315
- eq(schema.sessionTurns.id, schema.sessionTurnAttempts.turnId),
72316
- ),
72317
- )
72318
- .innerJoin(
72319
- schema.sessions,
72320
- and(
72321
- eq(schema.sessions.workspaceId, schema.sessionTurnAttempts.workspaceId),
72322
- eq(schema.sessions.id, schema.sessionTurnAttempts.sessionId),
72323
- ),
72324
- )
72325
- .where(
72326
- and(
72327
- eq(schema.sessionTurnAttempts.workspaceId, workspaceId),
72328
- eq(schema.sessionTurnAttempts.sessionId, sessionId),
72329
- eq(schema.sessionTurnAttempts.id, attemptId),
72330
- inArray(schema.sessionTurnAttempts.state, ["claimed", "running"]),
72331
- eq(schema.sessionTurns.activeAttemptId, attemptId),
72332
- eq(schema.sessions.activeTurnId, schema.sessionTurns.id),
72333
- inArray(schema.sessionTurns.status, [
72334
- "running",
72335
- "requires_action",
72336
- "recovering",
72337
- "waiting_capacity",
72338
- ]),
72339
- sql`not exists (
72340
- select 1
72341
- from ${schema.sessionAttemptInterruptions} interruption
72342
- where interruption.workspace_id = ${workspaceId}
72343
- and interruption.attempt_id = ${attemptId}
72344
- and interruption.state in ('pending', 'delivered', 'acknowledged')
72345
- )`,
72346
- ),
72347
- )
72348
- .limit(1);
72676
+ const row = await getLiveSessionAttemptTurn(scopedDb, workspaceId, sessionId, attemptId);
72349
72677
  if (!row) return null;
72350
- await goalSnapshotForAcceptedTurnInTransaction(scopedDb, row.turn);
72351
- return mapSessionTurnForExecution(row.turn);
72678
+ await goalSnapshotForAcceptedTurnInTransaction(scopedDb, row);
72679
+ return mapSessionTurnForExecution(row);
72352
72680
  });
72353
72681
  }
72354
72682
 
@@ -76755,7 +77083,7 @@ function mapSession(
76755
77083
  instructions: row.instructions ?? null,
76756
77084
  policyRole: row.policyRole ?? null,
76757
77085
  agentAccess: sessionAgentAccessFromRow(row),
76758
- endUser: sessionEndUserFromRow(row),
77086
+ scopeSubjectId: sessionEndUserFromRow(row),
76759
77087
  memoryScope: sessionMemoryScopeFromRow(row),
76760
77088
  resources: row.resources as ResourceRef[],
76761
77089
  skills: StoredSessionSkills.parse(row.skills ?? []),
@@ -77543,6 +77871,7 @@ function mapSocialConnection(
77543
77871
  ): SocialConnection {
77544
77872
  return {
77545
77873
  id: row.id,
77874
+ version: row.version,
77546
77875
  accountId: row.accountId,
77547
77876
  workspaceId: row.workspaceId,
77548
77877
  provider: row.provider as SocialProvider,
@@ -77919,6 +78248,7 @@ export {
77919
78248
 
77920
78249
  export {
77921
78250
  buildHostConnectionTokenResolver,
78251
+ buildHostGatewayConnectionTokenResolver,
77922
78252
  ConnectionRefreshHttpError,
77923
78253
  HostMcpCredentialBindingError,
77924
78254
  HostMcpCredentialScopeError,