@opengeni/db 0.31.1 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-auth.d.ts +209 -1
- package/dist/browser-downloads.d.ts +51 -0
- package/dist/browser-identities.d.ts +11 -2
- package/dist/browser-sessions.d.ts +83 -1
- package/dist/browser-state-artifacts.d.ts +75 -1
- package/dist/canonical-human-identities.d.ts +48 -0
- package/dist/canonical-human-identities.js +28 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/capability-integrations.d.ts +8 -6
- package/dist/{chunk-MWDVXQOL.js → chunk-5KTIEDX7.js} +5773 -4212
- package/dist/chunk-5KTIEDX7.js.map +1 -0
- package/dist/chunk-FPVAD4CO.js +117 -0
- package/dist/chunk-FPVAD4CO.js.map +1 -0
- package/dist/chunk-GYO7ED2Q.js +259 -0
- package/dist/chunk-GYO7ED2Q.js.map +1 -0
- package/dist/{chunk-F5FT7BEZ.js → chunk-HSOWSYOX.js} +2 -2
- package/dist/{chunk-YHZXQ7HP.js → chunk-VKLB2ZGI.js} +446 -19
- package/dist/chunk-VKLB2ZGI.js.map +1 -0
- package/dist/{chunk-NBE6CHRI.js → chunk-VTPRIXKG.js} +49 -9
- package/dist/chunk-VTPRIXKG.js.map +1 -0
- package/dist/{chunk-ISI2TIUG.js → chunk-Z7VYMNPZ.js} +76 -4
- package/dist/chunk-Z7VYMNPZ.js.map +1 -0
- package/dist/database.d.ts +5 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +467 -29
- package/dist/index.js +23247 -16232
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +23 -7
- package/dist/integration-bindings.d.ts +24 -24
- package/dist/integration-facets.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +11 -0
- package/dist/interaction-schema.d.ts +698 -34
- package/dist/persistence-errors.d.ts +8 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +8 -7
- package/dist/schema.d.ts +9464 -5412
- package/dist/schema.js +51 -9
- package/dist/session-control.d.ts +29 -3
- package/dist/session-tenancy.d.ts +44 -0
- package/dist/session-tenancy.js +16 -0
- package/dist/session-tenancy.js.map +1 -0
- package/dist/slack-task-policy-schema.d.ts +847 -0
- package/dist/slack-task-policy.d.ts +65 -0
- package/dist/task-notes-schema.d.ts +924 -0
- package/dist/task-notes.d.ts +85 -0
- package/dist/video-generation.d.ts +9 -0
- package/dist/video-generation.js +5 -3
- package/dist/xai-subscription.d.ts +326 -0
- package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
- package/drizzle/0213_browser_interaction_authority.sql +67 -0
- package/drizzle/0214_browser_download_saves.sql +34 -0
- package/drizzle/0215_browser_controller_host.sql +385 -0
- package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
- package/drizzle/0217_external_browser_auth_operations.sql +22 -0
- package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
- package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
- package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
- package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
- package/drizzle/0220_session_channels.sql +54 -0
- package/drizzle/0221_sessions_channel_index.sql +5 -0
- package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
- package/drizzle/0222_sessions_channel_fk.sql +89 -0
- package/drizzle/0223_pending_tool_event_output.sql +55 -0
- package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
- package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
- package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
- package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
- package/drizzle/0227_slack_native_actions.sql +145 -0
- package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
- package/drizzle/0228_slack_task_policy.sql +400 -0
- package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
- package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
- package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
- package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
- package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
- package/drizzle/0234_xai_subscription_authority.sql +1139 -0
- package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
- package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
- package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
- package/drizzle/0237_interaction_transition_reaper.sql +263 -0
- package/drizzle/0238_goal_persistence_policy.sql +471 -0
- package/drizzle/0238_recover_unclaimed_session_turns.sql +307 -0
- package/drizzle/0238_supergrok_realtime_model.sql +29 -0
- package/drizzle/0239_supergrok_video_funding.sql +50 -0
- package/drizzle/0239_task_tree_notes.sql +990 -0
- package/package.json +13 -5
- package/src/browser-auth.ts +2600 -123
- package/src/browser-downloads.ts +517 -0
- package/src/browser-identities.ts +220 -3
- package/src/browser-sessions.ts +334 -45
- package/src/browser-state-artifacts.ts +402 -12
- package/src/canonical-human-identities.ts +317 -0
- package/src/capability-integrations.ts +121 -213
- package/src/database.ts +41 -1
- package/src/index.ts +4625 -411
- package/src/insights.ts +109 -28
- package/src/integration-bindings.ts +101 -101
- package/src/{integration-features.ts → integration-facets.ts} +187 -184
- package/src/interaction-revisions.ts +33 -3
- package/src/interaction-schema.ts +311 -8
- package/src/pack-components.ts +43 -43
- package/src/pack-installations.ts +0 -43
- package/src/persistence-errors.ts +60 -1
- package/src/plugin-packages.ts +9 -9
- package/src/provision-roles.ts +232 -0
- package/src/runtime-posture.ts +227 -14
- package/src/schema.ts +1188 -75
- package/src/session-control.ts +122 -37
- package/src/session-queue-commands.ts +78 -1
- package/src/session-tenancy.ts +188 -0
- package/src/session-tool-call-settlement.ts +9 -3
- package/src/slack-task-policy-schema.ts +130 -0
- package/src/slack-task-policy.ts +341 -0
- package/src/task-notes-schema.ts +135 -0
- package/src/task-notes.ts +178 -0
- package/src/video-generation.ts +90 -12
- package/src/workspace-artifacts.ts +1 -1
- package/src/xai-subscription.ts +1887 -0
- package/dist/chunk-ISI2TIUG.js.map +0 -1
- package/dist/chunk-MWDVXQOL.js.map +0 -1
- package/dist/chunk-NBE6CHRI.js.map +0 -1
- package/dist/chunk-YHZXQ7HP.js.map +0 -1
- package/dist/integration-features.d.ts +0 -99
- /package/dist/{chunk-F5FT7BEZ.js.map → chunk-HSOWSYOX.js.map} +0 -0
package/src/session-control.ts
CHANGED
|
@@ -200,7 +200,7 @@ export class SessionCommandIdempotencyError extends Error {
|
|
|
200
200
|
|
|
201
201
|
export class AgentCommandAuthorityError extends Error {
|
|
202
202
|
constructor(
|
|
203
|
-
readonly code: "CALLER_STALE" | "CALLER_INTERRUPTED" | "
|
|
203
|
+
readonly code: "CALLER_STALE" | "CALLER_INTERRUPTED" | "TARGET_NOT_VERTICAL" | "SELF_STEER",
|
|
204
204
|
message: string,
|
|
205
205
|
) {
|
|
206
206
|
super(message);
|
|
@@ -232,10 +232,12 @@ export async function assertAgentCommandAuthorityInTransaction(
|
|
|
232
232
|
attemptIds: [input.actor.attemptId],
|
|
233
233
|
});
|
|
234
234
|
const lockedSessions = authorityLocks.sessions;
|
|
235
|
-
|
|
235
|
+
const callerSession = lockedSessions.find((row) => row.id === input.actor.sessionId);
|
|
236
|
+
if (!callerSession) {
|
|
236
237
|
throw new AgentCommandAuthorityError("CALLER_STALE", "The calling session no longer exists");
|
|
237
238
|
}
|
|
238
|
-
|
|
239
|
+
const targetSession = lockedSessions.find((row) => row.id === input.targetSessionId);
|
|
240
|
+
if (!targetSession) {
|
|
239
241
|
throw new SessionControlInvariantError(`Target session not found: ${input.targetSessionId}`);
|
|
240
242
|
}
|
|
241
243
|
const rows = await db.execute<{
|
|
@@ -298,37 +300,21 @@ export async function assertAgentCommandAuthorityInTransaction(
|
|
|
298
300
|
if (input.action === "steer" && input.targetSessionId === input.actor.sessionId) {
|
|
299
301
|
throw new AgentCommandAuthorityError("SELF_STEER", "An agent cannot steer its own session");
|
|
300
302
|
}
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
union all
|
|
312
|
-
select parent.id, parent.parent_session_id, child.depth + 1,
|
|
313
|
-
child.path || parent.id, parent.id = any(child.path)
|
|
314
|
-
from caller_ancestry child
|
|
315
|
-
join ${schema.sessions} parent
|
|
316
|
-
on parent.workspace_id = ${input.workspaceId} and parent.id = child.parent_id
|
|
317
|
-
where child.depth < ${SESSION_ANCESTRY_LIMIT} and not child.cycle
|
|
318
|
-
)
|
|
319
|
-
select
|
|
320
|
-
coalesce(bool_or(id = ${input.targetSessionId}), false) as "containsCaller",
|
|
321
|
-
coalesce(bool_or(cycle), false) or coalesce(max(depth), 0) >= ${SESSION_ANCESTRY_LIMIT}
|
|
322
|
-
as invalid
|
|
323
|
-
from caller_ancestry
|
|
324
|
-
`);
|
|
325
|
-
if (ancestry[0]?.invalid) {
|
|
326
|
-
throw new SessionControlInvariantError("Caller ancestry is invalid");
|
|
327
|
-
}
|
|
328
|
-
if (ancestry[0]?.containsCaller) {
|
|
303
|
+
const targetsSelf = targetSession.id === callerSession.id;
|
|
304
|
+
const targetsDirectParent = callerSession.parentSessionId === targetSession.id;
|
|
305
|
+
const targetsDirectChild = targetSession.parentSessionId === callerSession.id;
|
|
306
|
+
const verticalTargetAllowed =
|
|
307
|
+
input.action === "goal"
|
|
308
|
+
? targetsSelf
|
|
309
|
+
: input.action === "message"
|
|
310
|
+
? targetsSelf || targetsDirectParent || targetsDirectChild
|
|
311
|
+
: targetsDirectChild;
|
|
312
|
+
if (!verticalTargetAllowed) {
|
|
329
313
|
throw new AgentCommandAuthorityError(
|
|
330
|
-
"
|
|
331
|
-
|
|
314
|
+
"TARGET_NOT_VERTICAL",
|
|
315
|
+
input.action === "message"
|
|
316
|
+
? "An agent may message only its own session, direct parent, or direct child"
|
|
317
|
+
: "An agent may control only a direct child session",
|
|
332
318
|
);
|
|
333
319
|
}
|
|
334
320
|
}
|
|
@@ -430,7 +416,7 @@ export type SessionEventWriteLocks = {
|
|
|
430
416
|
*
|
|
431
417
|
* workspace_inference_controls (when control-aware)
|
|
432
418
|
* -> actual workspaces row FOR KEY SHARE
|
|
433
|
-
* -> session rows FOR UPDATE, UUID ordered
|
|
419
|
+
* -> session rows FOR NO KEY UPDATE, UUID ordered
|
|
434
420
|
* -> exact turn rows FOR UPDATE, UUID ordered
|
|
435
421
|
* -> exact attempt rows FOR UPDATE, UUID ordered
|
|
436
422
|
*
|
|
@@ -439,6 +425,11 @@ export type SessionEventWriteLocks = {
|
|
|
439
425
|
* `FOR UPDATE` lock serialized unrelated sessions and inverted the activity
|
|
440
426
|
* path's session -> implicit workspace-FK edge.
|
|
441
427
|
*
|
|
428
|
+
* `FOR NO KEY UPDATE` is equally deliberate for sessions. Session primary and
|
|
429
|
+
* composite identity keys are immutable, so writers need to exclude only concurrent
|
|
430
|
+
* non-key mutation. Keeping FK `FOR KEY SHARE` checks compatible prevents the
|
|
431
|
+
* activity finalizer's workspace counter from participating in a lock cycle.
|
|
432
|
+
*
|
|
442
433
|
* Complex lifecycle transactions may acquire allocator/control locks before
|
|
443
434
|
* this helper and may discover exact turn IDs only after locking the session.
|
|
444
435
|
* They use the explicit `already_locked` stages, while retaining the same
|
|
@@ -478,7 +469,7 @@ export async function lockSessionEventWriteRows(
|
|
|
478
469
|
),
|
|
479
470
|
)
|
|
480
471
|
.orderBy(schema.sessions.id)
|
|
481
|
-
.for("update")
|
|
472
|
+
.for("no key update")
|
|
482
473
|
: [];
|
|
483
474
|
|
|
484
475
|
const turnIds = [...new Set(input.turnIds ?? [])].sort();
|
|
@@ -529,14 +520,24 @@ export async function registerSessionTurnAttemptClaim(
|
|
|
529
520
|
temporalWorkflowRunId: string;
|
|
530
521
|
temporalActivityId: string;
|
|
531
522
|
verifiedControlRevision: number;
|
|
523
|
+
authorityEpoch: number;
|
|
524
|
+
authorityVisibility: "user_private" | "workspace_shared";
|
|
525
|
+
authorityOwnerOrganizationMembershipId: string | null;
|
|
532
526
|
mcpApprovalPolicies: Record<string, SessionMcpApprovalPolicy>;
|
|
533
527
|
connectorActionPolicies: schema.ConnectorActionPolicySnapshotEntry[];
|
|
534
528
|
},
|
|
535
529
|
): Promise<typeof schema.sessionTurnAttempts.$inferSelect> {
|
|
530
|
+
const authoritySnapshot = assertSessionAuthoritySnapshot({
|
|
531
|
+
attemptId: input.id,
|
|
532
|
+
authorityEpoch: input.authorityEpoch,
|
|
533
|
+
authorityVisibility: input.authorityVisibility,
|
|
534
|
+
authorityOwnerOrganizationMembershipId: input.authorityOwnerOrganizationMembershipId,
|
|
535
|
+
});
|
|
536
536
|
const [inserted] = await db
|
|
537
537
|
.insert(schema.sessionTurnAttempts)
|
|
538
538
|
.values({
|
|
539
539
|
...input,
|
|
540
|
+
...authoritySnapshot,
|
|
540
541
|
state: "claimed",
|
|
541
542
|
})
|
|
542
543
|
// Only an idempotent replay of this exact preallocated attempt ID may
|
|
@@ -557,12 +558,22 @@ export async function registerSessionTurnAttemptClaim(
|
|
|
557
558
|
)
|
|
558
559
|
.for("update")
|
|
559
560
|
.limit(1);
|
|
561
|
+
const existingAuthoritySnapshot = existing
|
|
562
|
+
? assertSessionAuthoritySnapshot({
|
|
563
|
+
attemptId: existing.id,
|
|
564
|
+
authorityEpoch: existing.authorityEpoch,
|
|
565
|
+
authorityVisibility: existing.authorityVisibility,
|
|
566
|
+
authorityOwnerOrganizationMembershipId: existing.authorityOwnerOrganizationMembershipId,
|
|
567
|
+
})
|
|
568
|
+
: null;
|
|
560
569
|
if (
|
|
561
570
|
!existing ||
|
|
562
571
|
existing.accountId !== input.accountId ||
|
|
563
572
|
existing.sessionId !== input.sessionId ||
|
|
564
573
|
existing.turnId !== input.turnId ||
|
|
565
574
|
existing.executionGeneration !== input.executionGeneration ||
|
|
575
|
+
!existingAuthoritySnapshot ||
|
|
576
|
+
!sessionAuthoritySnapshotsEqual(existingAuthoritySnapshot, authoritySnapshot) ||
|
|
566
577
|
existing.temporalWorkflowId !== input.temporalWorkflowId ||
|
|
567
578
|
existing.temporalWorkflowRunId !== input.temporalWorkflowRunId ||
|
|
568
579
|
existing.temporalActivityId !== input.temporalActivityId ||
|
|
@@ -577,6 +588,80 @@ export async function registerSessionTurnAttemptClaim(
|
|
|
577
588
|
return existing;
|
|
578
589
|
}
|
|
579
590
|
|
|
591
|
+
export type SessionAuthoritySnapshot = {
|
|
592
|
+
authorityEpoch: number;
|
|
593
|
+
authorityVisibility: "user_private" | "workspace_shared";
|
|
594
|
+
authorityOwnerOrganizationMembershipId: string | null;
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Validate the complete accepted-attempt authority tuple. Missing or partial
|
|
599
|
+
* values are not legacy defaults: current claim writers must provide the
|
|
600
|
+
* exact locked session authority explicitly.
|
|
601
|
+
*/
|
|
602
|
+
export function assertSessionAuthoritySnapshot(input: {
|
|
603
|
+
attemptId?: string;
|
|
604
|
+
authorityEpoch: unknown;
|
|
605
|
+
authorityVisibility: unknown;
|
|
606
|
+
authorityOwnerOrganizationMembershipId: unknown;
|
|
607
|
+
}): SessionAuthoritySnapshot {
|
|
608
|
+
const valid =
|
|
609
|
+
Number.isSafeInteger(input.authorityEpoch) &&
|
|
610
|
+
(input.authorityEpoch as number) > 0 &&
|
|
611
|
+
(input.authorityVisibility === "user_private" ||
|
|
612
|
+
input.authorityVisibility === "workspace_shared") &&
|
|
613
|
+
((input.authorityVisibility === "user_private" &&
|
|
614
|
+
typeof input.authorityOwnerOrganizationMembershipId === "string") ||
|
|
615
|
+
(input.authorityVisibility === "workspace_shared" &&
|
|
616
|
+
(input.authorityOwnerOrganizationMembershipId === null ||
|
|
617
|
+
typeof input.authorityOwnerOrganizationMembershipId === "string")));
|
|
618
|
+
if (!valid) {
|
|
619
|
+
throw new SessionControlInvariantError(
|
|
620
|
+
`Attempt ${input.attemptId ?? "unknown"} has an invalid session authority snapshot`,
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
authorityEpoch: input.authorityEpoch as number,
|
|
625
|
+
authorityVisibility:
|
|
626
|
+
input.authorityVisibility as SessionAuthoritySnapshot["authorityVisibility"],
|
|
627
|
+
authorityOwnerOrganizationMembershipId: input.authorityOwnerOrganizationMembershipId as
|
|
628
|
+
| string
|
|
629
|
+
| null,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export function sessionAuthoritySnapshotMatchesSession(
|
|
634
|
+
snapshot: SessionAuthoritySnapshot,
|
|
635
|
+
session: Pick<
|
|
636
|
+
typeof schema.sessions.$inferSelect,
|
|
637
|
+
"authorityEpoch" | "visibility" | "ownerOrganizationMembershipId"
|
|
638
|
+
>,
|
|
639
|
+
): boolean {
|
|
640
|
+
return (
|
|
641
|
+
snapshot.authorityEpoch === session.authorityEpoch &&
|
|
642
|
+
snapshot.authorityVisibility === session.visibility &&
|
|
643
|
+
snapshot.authorityOwnerOrganizationMembershipId ===
|
|
644
|
+
(session.ownerOrganizationMembershipId ?? null)
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
export function sessionAuthoritySnapshotsEqual(
|
|
649
|
+
left: Pick<
|
|
650
|
+
SessionAuthoritySnapshot,
|
|
651
|
+
"authorityEpoch" | "authorityVisibility" | "authorityOwnerOrganizationMembershipId"
|
|
652
|
+
>,
|
|
653
|
+
right: Pick<
|
|
654
|
+
SessionAuthoritySnapshot,
|
|
655
|
+
"authorityEpoch" | "authorityVisibility" | "authorityOwnerOrganizationMembershipId"
|
|
656
|
+
>,
|
|
657
|
+
): boolean {
|
|
658
|
+
return (
|
|
659
|
+
left.authorityEpoch === right.authorityEpoch &&
|
|
660
|
+
left.authorityVisibility === right.authorityVisibility &&
|
|
661
|
+
left.authorityOwnerOrganizationMembershipId === right.authorityOwnerOrganizationMembershipId
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
580
665
|
/**
|
|
581
666
|
* Close the exact first-class owner while the caller still holds the owning
|
|
582
667
|
* session/turn locks. Every path that clears `session_turns.active_attempt_id`
|
|
@@ -1415,12 +1500,12 @@ export async function reserveSessionCommandReceipt(
|
|
|
1415
1500
|
if (
|
|
1416
1501
|
identityScope === "goal_operation" &&
|
|
1417
1502
|
(input.actor.type !== "agent_attempt" ||
|
|
1418
|
-
|
|
1503
|
+
!["goal.update", "goal.progress"].includes(input.action) ||
|
|
1419
1504
|
input.targetSessionId === null ||
|
|
1420
1505
|
input.targetTurnId !== null)
|
|
1421
1506
|
) {
|
|
1422
1507
|
throw new SessionControlInvariantError(
|
|
1423
|
-
"Target-scoped receipt identity is reserved for agent goal
|
|
1508
|
+
"Target-scoped receipt identity is reserved for agent goal commands",
|
|
1424
1509
|
);
|
|
1425
1510
|
}
|
|
1426
1511
|
const actorSubjectId = input.actor.type === "agent_attempt" ? null : input.actor.subjectId;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1,
|
|
3
|
+
XaiProviderAccountAuthoritySnapshotV1,
|
|
2
4
|
DraftTimelineAnnotations,
|
|
3
5
|
McpPersonalConnectionDelegations,
|
|
4
6
|
TimelineAnnotations,
|
|
@@ -32,6 +34,7 @@ import {
|
|
|
32
34
|
withLosslessContentWriteVersion,
|
|
33
35
|
} from "./lossless-json";
|
|
34
36
|
import { closePendingSessionToolCallsInTransaction } from "./session-tool-call-settlement";
|
|
37
|
+
import { cancelTurnInteractionInterventionsInTransaction } from "./browser-auth";
|
|
35
38
|
import {
|
|
36
39
|
assertAgentCommandAuthorityInTransaction,
|
|
37
40
|
autoResumeSessionBranchInTransaction,
|
|
@@ -61,6 +64,7 @@ import {
|
|
|
61
64
|
initiatorFromStorage,
|
|
62
65
|
type FrozenTurnInitiator,
|
|
63
66
|
} from "./turn-initiator";
|
|
67
|
+
import { resolveXaiProviderAccountAuthoritySnapshotForAcceptanceInTransaction } from "./xai-subscription";
|
|
64
68
|
|
|
65
69
|
type SessionEventInsertWithPayload = typeof schema.sessionEvents.$inferInsert & {
|
|
66
70
|
payload: unknown;
|
|
@@ -233,6 +237,43 @@ async function personalConnectionDelegationsForAgentActor(
|
|
|
233
237
|
return parsed.data.map((delegation) => ({ ...delegation }));
|
|
234
238
|
}
|
|
235
239
|
|
|
240
|
+
async function xaiAuthorityForAgentActor(
|
|
241
|
+
db: Database,
|
|
242
|
+
workspaceId: string,
|
|
243
|
+
actor: Extract<SessionCommandActor, { type: "agent_attempt" }>,
|
|
244
|
+
): Promise<{
|
|
245
|
+
snapshot: ReturnType<typeof XaiProviderAccountAuthoritySnapshotV1.parse>;
|
|
246
|
+
subjectId: string | null;
|
|
247
|
+
}> {
|
|
248
|
+
const [row] = await db
|
|
249
|
+
.select({
|
|
250
|
+
snapshot: schema.sessionTurns.xaiProviderAccountAuthoritySnapshot,
|
|
251
|
+
initiatingHumanSubjectId: schema.sessionTurns.initiatingHumanSubjectId,
|
|
252
|
+
initiatorKind: schema.sessionTurns.initiatorKind,
|
|
253
|
+
initiatorSubjectId: schema.sessionTurns.initiatorSubjectId,
|
|
254
|
+
})
|
|
255
|
+
.from(schema.sessionTurns)
|
|
256
|
+
.where(
|
|
257
|
+
and(
|
|
258
|
+
eq(schema.sessionTurns.workspaceId, workspaceId),
|
|
259
|
+
eq(schema.sessionTurns.sessionId, actor.sessionId),
|
|
260
|
+
eq(schema.sessionTurns.id, actor.turnId),
|
|
261
|
+
),
|
|
262
|
+
)
|
|
263
|
+
.limit(1);
|
|
264
|
+
if (!row) {
|
|
265
|
+
throw new SessionControlInvariantError(
|
|
266
|
+
`Agent xAI authority turn not found: ${actor.sessionId}/${actor.turnId}`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
snapshot: XaiProviderAccountAuthoritySnapshotV1.parse(row.snapshot),
|
|
271
|
+
subjectId:
|
|
272
|
+
row.initiatingHumanSubjectId ??
|
|
273
|
+
(row.initiatorKind === "subject" ? row.initiatorSubjectId : null),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
236
277
|
async function lockSession(
|
|
237
278
|
db: Database,
|
|
238
279
|
workspaceId: string,
|
|
@@ -344,6 +385,12 @@ export async function supersedeSessionCurrentDirectionInTransaction(
|
|
|
344
385
|
}
|
|
345
386
|
|
|
346
387
|
const now = new Date();
|
|
388
|
+
await cancelTurnInteractionInterventionsInTransaction(db, {
|
|
389
|
+
accountId: input.accountId,
|
|
390
|
+
workspaceId: input.workspaceId,
|
|
391
|
+
sessionId: input.sessionId,
|
|
392
|
+
turnId: current.id,
|
|
393
|
+
});
|
|
347
394
|
const closedTools = await closePendingSessionToolCallsInTransaction(db, {
|
|
348
395
|
accountId: input.accountId,
|
|
349
396
|
workspaceId: input.workspaceId,
|
|
@@ -392,7 +439,10 @@ export async function supersedeSessionCurrentDirectionInTransaction(
|
|
|
392
439
|
turnId: current.id,
|
|
393
440
|
turnGeneration: current.executionGeneration,
|
|
394
441
|
turnAssociation: "current",
|
|
395
|
-
payload: {
|
|
442
|
+
payload: {
|
|
443
|
+
requestId: request.id,
|
|
444
|
+
response: { outcome: "cancelled" },
|
|
445
|
+
},
|
|
396
446
|
occurredAt: now,
|
|
397
447
|
})),
|
|
398
448
|
"payload",
|
|
@@ -454,6 +504,17 @@ export async function supersedeSessionCurrentDirectionInTransaction(
|
|
|
454
504
|
eq(schema.codexCapacityWaiters.status, "waiting"),
|
|
455
505
|
),
|
|
456
506
|
);
|
|
507
|
+
await db
|
|
508
|
+
.update(schema.xaiCapacityWaiters)
|
|
509
|
+
.set({ status: "superseded", lastWakeReason: "steer", updatedAt: now })
|
|
510
|
+
.where(
|
|
511
|
+
and(
|
|
512
|
+
eq(schema.xaiCapacityWaiters.workspaceId, input.workspaceId),
|
|
513
|
+
eq(schema.xaiCapacityWaiters.sessionId, input.sessionId),
|
|
514
|
+
eq(schema.xaiCapacityWaiters.blockedTurnId, current.id),
|
|
515
|
+
eq(schema.xaiCapacityWaiters.status, "waiting"),
|
|
516
|
+
),
|
|
517
|
+
);
|
|
457
518
|
}
|
|
458
519
|
return {
|
|
459
520
|
interruptionCount: 0,
|
|
@@ -1652,6 +1713,15 @@ export async function submitHumanPromptInTransaction(
|
|
|
1652
1713
|
input.subjectLabel,
|
|
1653
1714
|
);
|
|
1654
1715
|
}
|
|
1716
|
+
const xaiProviderAccountAuthoritySnapshot = editedSourceTurn
|
|
1717
|
+
? XaiProviderAccountAuthoritySnapshotV1.parse(
|
|
1718
|
+
editedSourceTurn.xaiProviderAccountAuthoritySnapshot,
|
|
1719
|
+
)
|
|
1720
|
+
: input.actor.type === "human"
|
|
1721
|
+
? await resolveXaiProviderAccountAuthoritySnapshotForAcceptanceInTransaction(db, {
|
|
1722
|
+
workspaceId: input.workspaceId,
|
|
1723
|
+
})
|
|
1724
|
+
: WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1;
|
|
1655
1725
|
const acceptedEventId = crypto.randomUUID();
|
|
1656
1726
|
const turnId = crypto.randomUUID();
|
|
1657
1727
|
const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
|
|
@@ -1730,6 +1800,7 @@ export async function submitHumanPromptInTransaction(
|
|
|
1730
1800
|
personalConnectionDelegations: editedSourceTurn
|
|
1731
1801
|
? editedSourceTurn.personalConnectionDelegations
|
|
1732
1802
|
: (input.personalConnectionDelegations ?? []),
|
|
1803
|
+
xaiProviderAccountAuthoritySnapshot,
|
|
1733
1804
|
createdAt: now,
|
|
1734
1805
|
updatedAt: now,
|
|
1735
1806
|
},
|
|
@@ -2083,6 +2154,7 @@ export async function sendAgentMessageInTransaction(
|
|
|
2083
2154
|
input.workspaceId,
|
|
2084
2155
|
input.actor,
|
|
2085
2156
|
);
|
|
2157
|
+
const xaiAuthority = await xaiAuthorityForAgentActor(db, input.workspaceId, input.actor);
|
|
2086
2158
|
const session = await lockSession(db, input.workspaceId, input.targetSessionId);
|
|
2087
2159
|
if (session.status === "cancelled") {
|
|
2088
2160
|
throw new QueueCommandConflictError(
|
|
@@ -2124,8 +2196,10 @@ export async function sendAgentMessageInTransaction(
|
|
|
2124
2196
|
callerTurnId: input.actor.turnId,
|
|
2125
2197
|
callerAttemptId: input.actor.attemptId,
|
|
2126
2198
|
callerExecutionGeneration: input.actor.executionGeneration,
|
|
2199
|
+
...(xaiAuthority.subjectId ? { xaiAuthoritySubjectId: xaiAuthority.subjectId } : {}),
|
|
2127
2200
|
},
|
|
2128
2201
|
personalConnectionDelegations,
|
|
2202
|
+
xaiProviderAccountAuthoritySnapshot: xaiAuthority.snapshot,
|
|
2129
2203
|
state: "pending",
|
|
2130
2204
|
},
|
|
2131
2205
|
"summary",
|
|
@@ -2290,6 +2364,7 @@ export async function steerAgentSessionInTransaction(
|
|
|
2290
2364
|
input.workspaceId,
|
|
2291
2365
|
input.actor,
|
|
2292
2366
|
);
|
|
2367
|
+
const xaiAuthority = await xaiAuthorityForAgentActor(db, input.workspaceId, input.actor);
|
|
2293
2368
|
const resumed = await autoResumeSessionBranchInTransaction(db, {
|
|
2294
2369
|
workspaceId: input.workspaceId,
|
|
2295
2370
|
sessionId: input.targetSessionId,
|
|
@@ -2351,8 +2426,10 @@ export async function steerAgentSessionInTransaction(
|
|
|
2351
2426
|
callerTurnId: input.actor.turnId,
|
|
2352
2427
|
callerAttemptId: input.actor.attemptId,
|
|
2353
2428
|
callerExecutionGeneration: input.actor.executionGeneration,
|
|
2429
|
+
...(xaiAuthority.subjectId ? { xaiAuthoritySubjectId: xaiAuthority.subjectId } : {}),
|
|
2354
2430
|
},
|
|
2355
2431
|
personalConnectionDelegations,
|
|
2432
|
+
xaiProviderAccountAuthoritySnapshot: xaiAuthority.snapshot,
|
|
2356
2433
|
state: "pending",
|
|
2357
2434
|
},
|
|
2358
2435
|
"summary",
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
import {
|
|
4
|
+
rawRows,
|
|
5
|
+
type Database,
|
|
6
|
+
rlsContextForWorkspace,
|
|
7
|
+
withWorkspaceSubjectRls,
|
|
8
|
+
withWorkspaceSubjectSessionActivityRls,
|
|
9
|
+
} from "./database";
|
|
10
|
+
|
|
11
|
+
export type SessionTenancyVisibility = "user_private" | "workspace_shared";
|
|
12
|
+
|
|
13
|
+
export type TransitionSessionVisibilityInput = {
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
sessionId: string;
|
|
16
|
+
actorSubjectId: string;
|
|
17
|
+
targetVisibility: SessionTenancyVisibility;
|
|
18
|
+
expectedAuthorityEpoch: number;
|
|
19
|
+
operationKey: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TransitionSessionVisibilityResult = {
|
|
23
|
+
operationId: string;
|
|
24
|
+
visibility: SessionTenancyVisibility;
|
|
25
|
+
authorityEpoch: number;
|
|
26
|
+
ownerOrganizationMembershipId: string | null;
|
|
27
|
+
changed: boolean;
|
|
28
|
+
replay: boolean;
|
|
29
|
+
interruptedAttemptCount: number;
|
|
30
|
+
cancelledTurnCount: number;
|
|
31
|
+
cancelledUpdateCount: number;
|
|
32
|
+
pausedGoalCount: number;
|
|
33
|
+
revokedGrantCount: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ForkSessionContentInput = {
|
|
37
|
+
sourceWorkspaceId: string;
|
|
38
|
+
sourceSessionId: string;
|
|
39
|
+
actorSubjectId: string;
|
|
40
|
+
destinationWorkspaceId: string;
|
|
41
|
+
destinationVisibility: SessionTenancyVisibility;
|
|
42
|
+
operationKey: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type ForkSessionContentResult = {
|
|
46
|
+
operationId: string;
|
|
47
|
+
sessionId: string;
|
|
48
|
+
workspaceId: string;
|
|
49
|
+
visibility: SessionTenancyVisibility;
|
|
50
|
+
authorityEpoch: number;
|
|
51
|
+
copiedHistoryItemCount: number;
|
|
52
|
+
replay: boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export function canonicalSessionVisibilityTransitionHash(
|
|
56
|
+
input: Pick<
|
|
57
|
+
TransitionSessionVisibilityInput,
|
|
58
|
+
"sessionId" | "targetVisibility" | "expectedAuthorityEpoch"
|
|
59
|
+
>,
|
|
60
|
+
): string {
|
|
61
|
+
return createHash("sha256")
|
|
62
|
+
.update(
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
version: 1,
|
|
65
|
+
sessionId: input.sessionId,
|
|
66
|
+
targetVisibility: input.targetVisibility,
|
|
67
|
+
expectedAuthorityEpoch: input.expectedAuthorityEpoch,
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
.digest("hex");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function canonicalSessionForkHash(
|
|
74
|
+
input: Pick<
|
|
75
|
+
ForkSessionContentInput,
|
|
76
|
+
"sourceSessionId" | "destinationWorkspaceId" | "destinationVisibility"
|
|
77
|
+
>,
|
|
78
|
+
): string {
|
|
79
|
+
return createHash("sha256")
|
|
80
|
+
.update(
|
|
81
|
+
JSON.stringify({
|
|
82
|
+
version: 1,
|
|
83
|
+
sourceSessionId: input.sourceSessionId,
|
|
84
|
+
destinationWorkspaceId: input.destinationWorkspaceId,
|
|
85
|
+
destinationVisibility: input.destinationVisibility,
|
|
86
|
+
}),
|
|
87
|
+
)
|
|
88
|
+
.digest("hex");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function transitionSessionVisibility(
|
|
92
|
+
db: Database,
|
|
93
|
+
input: TransitionSessionVisibilityInput,
|
|
94
|
+
): Promise<TransitionSessionVisibilityResult> {
|
|
95
|
+
if (!Number.isSafeInteger(input.expectedAuthorityEpoch) || input.expectedAuthorityEpoch < 1) {
|
|
96
|
+
throw new Error("expectedAuthorityEpoch must be a positive safe integer");
|
|
97
|
+
}
|
|
98
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
99
|
+
const requestHash = canonicalSessionVisibilityTransitionHash(input);
|
|
100
|
+
const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);
|
|
101
|
+
return await withWorkspaceSubjectSessionActivityRls(
|
|
102
|
+
db,
|
|
103
|
+
input.workspaceId,
|
|
104
|
+
input.actorSubjectId,
|
|
105
|
+
async (scopedDb) => {
|
|
106
|
+
const rows = await rawRows<{
|
|
107
|
+
operationId: string;
|
|
108
|
+
visibility: SessionTenancyVisibility;
|
|
109
|
+
authorityEpoch: number;
|
|
110
|
+
ownerOrganizationMembershipId: string | null;
|
|
111
|
+
changed: boolean;
|
|
112
|
+
replay: boolean;
|
|
113
|
+
interruptedAttemptCount: number;
|
|
114
|
+
cancelledTurnCount: number;
|
|
115
|
+
cancelledUpdateCount: number;
|
|
116
|
+
pausedGoalCount: number;
|
|
117
|
+
revokedGrantCount: number;
|
|
118
|
+
}>(
|
|
119
|
+
scopedDb,
|
|
120
|
+
sql`select
|
|
121
|
+
operation_id as "operationId",
|
|
122
|
+
visibility,
|
|
123
|
+
authority_epoch as "authorityEpoch",
|
|
124
|
+
owner_organization_membership_id as "ownerOrganizationMembershipId",
|
|
125
|
+
changed,
|
|
126
|
+
replay,
|
|
127
|
+
interrupted_attempt_count as "interruptedAttemptCount",
|
|
128
|
+
cancelled_turn_count as "cancelledTurnCount",
|
|
129
|
+
cancelled_update_count as "cancelledUpdateCount",
|
|
130
|
+
paused_goal_count as "pausedGoalCount",
|
|
131
|
+
revoked_grant_count as "revokedGrantCount"
|
|
132
|
+
from transition_session_visibility(
|
|
133
|
+
${accountId}::uuid,
|
|
134
|
+
${input.workspaceId}::uuid,
|
|
135
|
+
${input.sessionId}::uuid,
|
|
136
|
+
${input.actorSubjectId},
|
|
137
|
+
${input.targetVisibility},
|
|
138
|
+
${input.expectedAuthorityEpoch},
|
|
139
|
+
${input.operationKey},
|
|
140
|
+
${requestHash}
|
|
141
|
+
)`,
|
|
142
|
+
);
|
|
143
|
+
const result = rows[0];
|
|
144
|
+
if (!result) throw new Error("Session visibility transition returned no result");
|
|
145
|
+
return result;
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function forkSessionContent(
|
|
151
|
+
db: Database,
|
|
152
|
+
input: ForkSessionContentInput,
|
|
153
|
+
): Promise<ForkSessionContentResult> {
|
|
154
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
155
|
+
const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);
|
|
156
|
+
const requestHash = canonicalSessionForkHash(input);
|
|
157
|
+
return await withWorkspaceSubjectRls(
|
|
158
|
+
db,
|
|
159
|
+
input.sourceWorkspaceId,
|
|
160
|
+
input.actorSubjectId,
|
|
161
|
+
async (scopedDb) => {
|
|
162
|
+
const rows = await rawRows<ForkSessionContentResult>(
|
|
163
|
+
scopedDb,
|
|
164
|
+
sql`select
|
|
165
|
+
operation_id as "operationId",
|
|
166
|
+
session_id as "sessionId",
|
|
167
|
+
workspace_id as "workspaceId",
|
|
168
|
+
visibility,
|
|
169
|
+
authority_epoch as "authorityEpoch",
|
|
170
|
+
copied_history_item_count as "copiedHistoryItemCount",
|
|
171
|
+
replay
|
|
172
|
+
from fork_session_content(
|
|
173
|
+
${accountId}::uuid,
|
|
174
|
+
${input.sourceWorkspaceId}::uuid,
|
|
175
|
+
${input.sourceSessionId}::uuid,
|
|
176
|
+
${input.actorSubjectId},
|
|
177
|
+
${input.destinationWorkspaceId}::uuid,
|
|
178
|
+
${input.destinationVisibility},
|
|
179
|
+
${input.operationKey},
|
|
180
|
+
${requestHash}
|
|
181
|
+
)`,
|
|
182
|
+
);
|
|
183
|
+
const result = rows[0];
|
|
184
|
+
if (!result) throw new Error("Session fork returned no result");
|
|
185
|
+
return result;
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
}
|
|
@@ -135,6 +135,10 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
135
135
|
row.resultItem === null
|
|
136
136
|
? null
|
|
137
137
|
: fromPostgresLosslessJson(row.resultItem, row.resultItemCodecVersion),
|
|
138
|
+
eventOutput:
|
|
139
|
+
row.eventOutput === null
|
|
140
|
+
? null
|
|
141
|
+
: fromPostgresLosslessJson(row.eventOutput, row.eventOutputCodecVersion),
|
|
138
142
|
}));
|
|
139
143
|
if (pending.length === 0) return { sequence: input.sequence, events: [], closed: 0 };
|
|
140
144
|
|
|
@@ -306,9 +310,11 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
306
310
|
},
|
|
307
311
|
],
|
|
308
312
|
}
|
|
309
|
-
:
|
|
310
|
-
resolution.
|
|
311
|
-
resolution.call.resultItem)
|
|
313
|
+
: resolution.call.eventOutput !== null
|
|
314
|
+
? resolution.call.eventOutput.value
|
|
315
|
+
: ((resolution.existingResult?.item ?? resolution.call.resultItem)?.output ??
|
|
316
|
+
resolution.existingResult?.item ??
|
|
317
|
+
resolution.call.resultItem),
|
|
312
318
|
recovery: {
|
|
313
319
|
interrupted: resolution.interrupted,
|
|
314
320
|
outcome: resolution.interrupted ? "unknown" : "durable_result_found",
|