@opengeni/db 3.6.0 → 3.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-744DFC65.js → chunk-4NAR7FBA.js} +92 -6
- package/dist/chunk-4NAR7FBA.js.map +1 -0
- package/dist/{chunk-IW6O6G7R.js → chunk-5NNCGFQY.js} +2 -2
- package/dist/{chunk-G3732M3T.js → chunk-DZCYW2WT.js} +2 -2
- package/dist/{chunk-WW7AJ5IC.js → chunk-F6PBS6WE.js} +5 -7
- package/dist/chunk-F6PBS6WE.js.map +1 -0
- package/dist/{chunk-UJ2EW3QI.js → chunk-O56DM5GZ.js} +340 -85
- package/dist/chunk-O56DM5GZ.js.map +1 -0
- package/dist/{chunk-CDMMSUWJ.js → chunk-S3TDDCS2.js} +3 -3
- package/dist/{chunk-Q5YUGQVK.js → chunk-SZAWDXQC.js} +3 -3
- package/dist/{chunk-UGDVSQ6U.js → chunk-U4WPPEBH.js} +2 -2
- package/dist/{chunk-2NT476HD.js → chunk-US4IS6IV.js} +2 -2
- package/dist/codex-token-resolver.d.ts +12 -1
- package/dist/company-profile-agent-admin.d.ts +4 -2
- package/dist/company-profile-agent-policy.d.ts +20 -0
- package/dist/company-profile-schema.d.ts +573 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +382 -18
- package/dist/index.js +4513 -2065
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-human-provisioning.d.ts +3 -3
- package/dist/memory-domain.d.ts +5 -3
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +725 -4
- package/dist/schema.js +13 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +40 -5
- package/dist/session-queue-commands.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/user-resource-authority.d.ts +8 -0
- package/dist/video-generation.js +3 -3
- package/drizzle/0369_local_human_personal_authority.sql +350 -0
- package/drizzle/0370_workspace_member_management_scope.sql +98 -0
- package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
- package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
- package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
- package/drizzle/0374_session_event_cursors.sql +315 -0
- package/drizzle/0375_session_recovery_observability.sql +73 -0
- package/drizzle/0376_organization_workspace_inventory.sql +88 -0
- package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
- package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
- package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
- package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
- package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
- package/drizzle/0382_organization_api_key_provenance.sql +91 -0
- package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
- package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
- package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
- package/drizzle/0386_local_organization_administration.sql +157 -0
- package/package.json +5 -5
- package/src/codex-token-resolver.ts +26 -1
- package/src/company-brain-governed-writes.ts +30 -1
- package/src/company-profile-agent-admin.ts +68 -1
- package/src/company-profile-agent-policy.ts +126 -0
- package/src/company-profile-schema.ts +115 -1
- package/src/database.ts +3 -5
- package/src/index.ts +3626 -456
- package/src/managed-human-provisioning.ts +3 -3
- package/src/memory-domain.ts +9 -4
- package/src/organization-membership-lifecycle.ts +0 -1
- package/src/provision-roles.ts +83 -4
- package/src/runtime-posture.ts +22 -0
- package/src/schema.ts +161 -8
- package/src/session-control.ts +177 -59
- package/src/session-queue-commands.ts +146 -40
- package/src/session-tool-call-settlement.ts +34 -7
- package/src/user-resource-authority.ts +56 -0
- package/dist/chunk-744DFC65.js.map +0 -1
- package/dist/chunk-UJ2EW3QI.js.map +0 -1
- package/dist/chunk-WW7AJ5IC.js.map +0 -1
- /package/dist/{chunk-IW6O6G7R.js.map → chunk-5NNCGFQY.js.map} +0 -0
- /package/dist/{chunk-G3732M3T.js.map → chunk-DZCYW2WT.js.map} +0 -0
- /package/dist/{chunk-CDMMSUWJ.js.map → chunk-S3TDDCS2.js.map} +0 -0
- /package/dist/{chunk-Q5YUGQVK.js.map → chunk-SZAWDXQC.js.map} +0 -0
- /package/dist/{chunk-UGDVSQ6U.js.map → chunk-U4WPPEBH.js.map} +0 -0
- /package/dist/{chunk-2NT476HD.js.map → chunk-US4IS6IV.js.map} +0 -0
package/src/session-control.ts
CHANGED
|
@@ -104,6 +104,18 @@ export type EffectiveSessionControl = {
|
|
|
104
104
|
} | null;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* The bounded control projection required by a locked activity-write fence.
|
|
109
|
+
* Settlement and background-command summaries are read-model concerns and must
|
|
110
|
+
* not extend the session-event critical section when admission only needs the
|
|
111
|
+
* effective run state and blocker scope.
|
|
112
|
+
*/
|
|
113
|
+
export type SessionWriteAdmissionControl = {
|
|
114
|
+
state: EffectiveControlState;
|
|
115
|
+
controlVersion: number;
|
|
116
|
+
primaryBlockerKind: EffectiveControlBlocker["kind"] | null;
|
|
117
|
+
};
|
|
118
|
+
|
|
107
119
|
type SettlementAttemptCounts = {
|
|
108
120
|
attemptCount: number;
|
|
109
121
|
interruptionPendingCount: number;
|
|
@@ -627,6 +639,13 @@ export type SessionEventWriteLockInput = {
|
|
|
627
639
|
controlLock: WorkspaceControlLockMode | "already_locked" | "none";
|
|
628
640
|
/** Used only when a staged caller already established the workspace prefix. */
|
|
629
641
|
workspaceLock?: "key_share" | "already_locked";
|
|
642
|
+
/**
|
|
643
|
+
* Semantic writers retain FOR NO KEY UPDATE. Cursor-authoritative raw-only
|
|
644
|
+
* appends need only the FK-compatible identity hold; KEY SHARE is compatible
|
|
645
|
+
* with semantic NO KEY UPDATE and therefore removes the wide session row as
|
|
646
|
+
* their serialization mutex without inverting the rolling lock order.
|
|
647
|
+
*/
|
|
648
|
+
sessionLock?: "no_key_update" | "key_share";
|
|
630
649
|
sessionIds?: string[];
|
|
631
650
|
turnIds?: string[];
|
|
632
651
|
attemptIds?: string[];
|
|
@@ -636,6 +655,7 @@ export type SessionEventWriteLocks = {
|
|
|
636
655
|
control: WorkspaceControlRow | null;
|
|
637
656
|
workspace: typeof schema.workspaces.$inferSelect | null;
|
|
638
657
|
sessions: Array<typeof schema.sessions.$inferSelect>;
|
|
658
|
+
cursors: Array<typeof schema.sessionEventCursors.$inferSelect>;
|
|
639
659
|
turns: Array<typeof schema.sessionTurns.$inferSelect>;
|
|
640
660
|
attempts: Array<typeof schema.sessionTurnAttempts.$inferSelect>;
|
|
641
661
|
};
|
|
@@ -646,7 +666,8 @@ export type SessionEventWriteLocks = {
|
|
|
646
666
|
* workspace-control advisory lock + workspace_inference_controls row
|
|
647
667
|
* (when control-aware; see `lockWorkspaceInferenceControl`)
|
|
648
668
|
* -> actual workspaces row FOR KEY SHARE
|
|
649
|
-
* -> session rows FOR NO KEY UPDATE, UUID ordered
|
|
669
|
+
* -> session rows FOR NO KEY UPDATE (semantic) or KEY SHARE (raw), UUID ordered
|
|
670
|
+
* -> session event cursor rows FOR UPDATE, UUID ordered
|
|
650
671
|
* -> exact turn rows FOR UPDATE, UUID ordered
|
|
651
672
|
* -> exact attempt rows FOR UPDATE, UUID ordered
|
|
652
673
|
*
|
|
@@ -661,10 +682,12 @@ export type SessionEventWriteLocks = {
|
|
|
661
682
|
* `FOR UPDATE` lock serialized unrelated sessions and inverted the activity
|
|
662
683
|
* path's session -> implicit workspace-FK edge.
|
|
663
684
|
*
|
|
664
|
-
* `FOR NO KEY UPDATE` is equally deliberate for
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
685
|
+
* `FOR NO KEY UPDATE` is equally deliberate for semantic session writers.
|
|
686
|
+
* Cursor-authoritative raw-only writers use `FOR KEY SHARE`: it preserves the
|
|
687
|
+
* session identity against deletion while remaining compatible with semantic
|
|
688
|
+
* `FOR NO KEY UPDATE`. Keeping this rolling session-before-cursor order avoids
|
|
689
|
+
* an inversion with old binaries and quiescent cascade deletion; the compact
|
|
690
|
+
* cursor row, not the wide session row, is the raw ordering mutex.
|
|
668
691
|
*
|
|
669
692
|
* Complex lifecycle transactions may acquire allocator/control locks before
|
|
670
693
|
* this helper and may discover exact turn IDs only after locking the session.
|
|
@@ -698,7 +721,8 @@ export async function lockSessionEventWriteRows(
|
|
|
698
721
|
}
|
|
699
722
|
|
|
700
723
|
const sessionIds = [...new Set(input.sessionIds ?? [])].sort();
|
|
701
|
-
const
|
|
724
|
+
const sessionLock = input.sessionLock ?? "no_key_update";
|
|
725
|
+
let sessions =
|
|
702
726
|
sessionIds.length > 0
|
|
703
727
|
? await db
|
|
704
728
|
.select()
|
|
@@ -710,9 +734,73 @@ export async function lockSessionEventWriteRows(
|
|
|
710
734
|
),
|
|
711
735
|
)
|
|
712
736
|
.orderBy(schema.sessions.id)
|
|
713
|
-
.for("no key update")
|
|
737
|
+
.for(sessionLock === "key_share" ? "key share" : "no key update")
|
|
714
738
|
: [];
|
|
715
739
|
|
|
740
|
+
const cursors =
|
|
741
|
+
sessions.length > 0
|
|
742
|
+
? await db
|
|
743
|
+
.select()
|
|
744
|
+
.from(schema.sessionEventCursors)
|
|
745
|
+
.where(
|
|
746
|
+
and(
|
|
747
|
+
eq(schema.sessionEventCursors.workspaceId, input.workspaceId),
|
|
748
|
+
inArray(
|
|
749
|
+
schema.sessionEventCursors.sessionId,
|
|
750
|
+
sessions.map((session) => session.id),
|
|
751
|
+
),
|
|
752
|
+
),
|
|
753
|
+
)
|
|
754
|
+
.orderBy(schema.sessionEventCursors.sessionId)
|
|
755
|
+
.for("update")
|
|
756
|
+
: [];
|
|
757
|
+
if (cursors.length !== sessions.length) {
|
|
758
|
+
throw new SessionControlInvariantError(
|
|
759
|
+
`Session event cursor lock set was incomplete for workspace ${input.workspaceId}`,
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
if (sessionLock === "key_share" && sessions.length > 0) {
|
|
763
|
+
// KEY SHARE is intentionally compatible with a semantic writer's NO KEY
|
|
764
|
+
// UPDATE. If that writer reached the cursor first it may have committed a
|
|
765
|
+
// control/authority/session-state change after our initial identity read.
|
|
766
|
+
// Re-read only after owning the cursor so the attempt fence evaluates the
|
|
767
|
+
// latest committed session state while later semantic writers wait here.
|
|
768
|
+
sessions = await db
|
|
769
|
+
.select()
|
|
770
|
+
.from(schema.sessions)
|
|
771
|
+
.where(
|
|
772
|
+
and(
|
|
773
|
+
eq(schema.sessions.workspaceId, input.workspaceId),
|
|
774
|
+
inArray(schema.sessions.id, sessionIds),
|
|
775
|
+
),
|
|
776
|
+
)
|
|
777
|
+
.orderBy(schema.sessions.id)
|
|
778
|
+
.for("key share");
|
|
779
|
+
}
|
|
780
|
+
const canonicalSessions = sessions.map((session, index) => {
|
|
781
|
+
const cursor = cursors[index]!;
|
|
782
|
+
if (
|
|
783
|
+
cursor.sessionId !== session.id ||
|
|
784
|
+
cursor.accountId !== session.accountId ||
|
|
785
|
+
cursor.workspaceId !== session.workspaceId
|
|
786
|
+
) {
|
|
787
|
+
throw new SessionControlInvariantError(
|
|
788
|
+
`Session event cursor identity failed for session ${session.id}`,
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
// The cursor is the allocation authority. The wide session column remains
|
|
792
|
+
// a rolling compatibility/read projection until every reader is cut over.
|
|
793
|
+
// It may lag after the later pure-append activation, but it must never lead
|
|
794
|
+
// the durable cursor or an old writer could have committed an unverified
|
|
795
|
+
// sequence.
|
|
796
|
+
if (cursor.lastSequence < session.lastSequence) {
|
|
797
|
+
throw new SessionControlInvariantError(
|
|
798
|
+
`Session event cursor is behind session projection for session ${session.id}`,
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
return { ...session, lastSequence: cursor.lastSequence };
|
|
802
|
+
});
|
|
803
|
+
|
|
716
804
|
const turnIds = [...new Set(input.turnIds ?? [])].sort();
|
|
717
805
|
const turns =
|
|
718
806
|
turnIds.length > 0
|
|
@@ -745,7 +833,7 @@ export async function lockSessionEventWriteRows(
|
|
|
745
833
|
.for("update")
|
|
746
834
|
: [];
|
|
747
835
|
|
|
748
|
-
return { control, workspace, sessions, turns, attempts };
|
|
836
|
+
return { control, workspace, sessions: canonicalSessions, cursors, turns, attempts };
|
|
749
837
|
}
|
|
750
838
|
|
|
751
839
|
export async function registerSessionTurnAttemptClaim(
|
|
@@ -1337,6 +1425,10 @@ async function settlementAttemptCounts(
|
|
|
1337
1425
|
workspaceId: string,
|
|
1338
1426
|
sessionIds: string[],
|
|
1339
1427
|
): Promise<Map<string, SettlementAttemptCounts>> {
|
|
1428
|
+
// Drive the projection from the requested targets through the indexed
|
|
1429
|
+
// parent_session_id edge. The inverse plan scanned every interruption in a
|
|
1430
|
+
// workspace before walking upward, making one session read proportional to
|
|
1431
|
+
// a high-volume orchestrator workspace's entire interruption history.
|
|
1340
1432
|
const rows = await db.execute<{
|
|
1341
1433
|
sessionId: string;
|
|
1342
1434
|
attemptCount: number | string;
|
|
@@ -1344,9 +1436,24 @@ async function settlementAttemptCounts(
|
|
|
1344
1436
|
quiescencePendingCount: number | string;
|
|
1345
1437
|
}>(sql`
|
|
1346
1438
|
with recursive targets(id) as (values ${targetValues(sessionIds)}),
|
|
1347
|
-
|
|
1439
|
+
descendant_sessions(target_id, session_id, depth, path) as (
|
|
1440
|
+
select target.id, target.id, 0::integer, array[target.id]::uuid[]
|
|
1441
|
+
from targets target
|
|
1442
|
+
union all
|
|
1443
|
+
select
|
|
1444
|
+
descendant.target_id,
|
|
1445
|
+
child.id,
|
|
1446
|
+
descendant.depth + 1,
|
|
1447
|
+
descendant.path || child.id
|
|
1448
|
+
from descendant_sessions descendant
|
|
1449
|
+
join ${schema.sessions} child
|
|
1450
|
+
on child.workspace_id = ${workspaceId}
|
|
1451
|
+
and child.parent_session_id = descendant.session_id
|
|
1452
|
+
where not child.id = any(descendant.path)
|
|
1453
|
+
and descendant.depth < ${SESSION_ANCESTRY_LIMIT}
|
|
1454
|
+
), interruptions as (
|
|
1348
1455
|
select
|
|
1349
|
-
|
|
1456
|
+
descendant.target_id,
|
|
1350
1457
|
interruption.attempt_id,
|
|
1351
1458
|
bool_or(interruption.state in ('pending', 'delivered', 'acknowledged'))
|
|
1352
1459
|
as interruption_pending,
|
|
@@ -1354,62 +1461,31 @@ async function settlementAttemptCounts(
|
|
|
1354
1461
|
interruption.state in ('settled', 'rejected_stale')
|
|
1355
1462
|
and attempt.quiesced_at is null
|
|
1356
1463
|
) as quiescence_pending
|
|
1357
|
-
from
|
|
1464
|
+
from descendant_sessions descendant
|
|
1465
|
+
join ${schema.sessionAttemptInterruptions} interruption
|
|
1466
|
+
on interruption.workspace_id = ${workspaceId}
|
|
1467
|
+
and interruption.session_id = descendant.session_id
|
|
1358
1468
|
join ${schema.sessionTurnAttempts} attempt
|
|
1359
1469
|
on attempt.workspace_id = interruption.workspace_id
|
|
1360
1470
|
and attempt.id = interruption.attempt_id
|
|
1361
|
-
where
|
|
1362
|
-
and (
|
|
1471
|
+
where (
|
|
1363
1472
|
interruption.state in ('pending', 'delivered', 'acknowledged')
|
|
1364
1473
|
or (
|
|
1365
1474
|
interruption.state in ('settled', 'rejected_stale')
|
|
1366
1475
|
and attempt.quiesced_at is null
|
|
1367
1476
|
)
|
|
1368
1477
|
)
|
|
1369
|
-
group by
|
|
1370
|
-
), interruption_ancestry(
|
|
1371
|
-
session_id,
|
|
1372
|
-
ancestor_id,
|
|
1373
|
-
attempt_id,
|
|
1374
|
-
interruption_pending,
|
|
1375
|
-
quiescence_pending,
|
|
1376
|
-
depth,
|
|
1377
|
-
path
|
|
1378
|
-
) as (
|
|
1379
|
-
select
|
|
1380
|
-
interruption.session_id,
|
|
1381
|
-
interruption.session_id,
|
|
1382
|
-
interruption.attempt_id,
|
|
1383
|
-
interruption.interruption_pending,
|
|
1384
|
-
interruption.quiescence_pending,
|
|
1385
|
-
0::integer,
|
|
1386
|
-
array[interruption.session_id]::uuid[]
|
|
1387
|
-
from interruptions interruption
|
|
1388
|
-
union all
|
|
1389
|
-
select
|
|
1390
|
-
ancestry.session_id,
|
|
1391
|
-
current.parent_session_id,
|
|
1392
|
-
ancestry.attempt_id,
|
|
1393
|
-
ancestry.interruption_pending,
|
|
1394
|
-
ancestry.quiescence_pending,
|
|
1395
|
-
ancestry.depth + 1,
|
|
1396
|
-
ancestry.path || current.parent_session_id
|
|
1397
|
-
from interruption_ancestry ancestry
|
|
1398
|
-
join ${schema.sessions} current
|
|
1399
|
-
on current.workspace_id = ${workspaceId} and current.id = ancestry.ancestor_id
|
|
1400
|
-
where current.parent_session_id is not null
|
|
1401
|
-
and not current.parent_session_id = any(ancestry.path)
|
|
1402
|
-
and ancestry.depth < ${SESSION_ANCESTRY_LIMIT}
|
|
1478
|
+
group by descendant.target_id, interruption.attempt_id
|
|
1403
1479
|
)
|
|
1404
1480
|
select
|
|
1405
1481
|
target.id as "sessionId",
|
|
1406
|
-
count(
|
|
1407
|
-
count(
|
|
1408
|
-
filter (where
|
|
1409
|
-
count(
|
|
1410
|
-
filter (where
|
|
1482
|
+
count(interruption.attempt_id)::integer as "attemptCount",
|
|
1483
|
+
count(interruption.attempt_id)
|
|
1484
|
+
filter (where interruption.interruption_pending)::integer as "interruptionPendingCount",
|
|
1485
|
+
count(interruption.attempt_id)
|
|
1486
|
+
filter (where interruption.quiescence_pending)::integer as "quiescencePendingCount"
|
|
1411
1487
|
from targets target
|
|
1412
|
-
join
|
|
1488
|
+
join interruptions interruption on interruption.target_id = target.id
|
|
1413
1489
|
group by target.id
|
|
1414
1490
|
`);
|
|
1415
1491
|
return new Map(
|
|
@@ -1500,6 +1576,46 @@ export async function evaluateSessionControls(
|
|
|
1500
1576
|
return result;
|
|
1501
1577
|
}
|
|
1502
1578
|
|
|
1579
|
+
/**
|
|
1580
|
+
* Evaluate only the control facts needed to admit an already locked session
|
|
1581
|
+
* write. The workspace-control row must be reused when the caller established
|
|
1582
|
+
* the canonical prefix before session/turn/attempt locks.
|
|
1583
|
+
*
|
|
1584
|
+
* Deliberately omit settlementAttemptCounts and
|
|
1585
|
+
* stoppingBackgroundCommandCounts: those recursive summaries do not affect
|
|
1586
|
+
* effective pause state and their results are discarded by the write fence.
|
|
1587
|
+
*/
|
|
1588
|
+
export async function evaluateSessionWriteAdmissionControl(
|
|
1589
|
+
db: Database,
|
|
1590
|
+
workspaceId: string,
|
|
1591
|
+
sessionId: string,
|
|
1592
|
+
options: {
|
|
1593
|
+
lock?: WorkspaceControlLockMode;
|
|
1594
|
+
workspaceControl?: WorkspaceControlRow | undefined;
|
|
1595
|
+
} = {},
|
|
1596
|
+
): Promise<SessionWriteAdmissionControl> {
|
|
1597
|
+
if (options.workspaceControl && options.workspaceControl.workspaceId !== workspaceId) {
|
|
1598
|
+
throw new SessionControlInvariantError(
|
|
1599
|
+
`Locked workspace control ${options.workspaceControl.workspaceId} does not match ${workspaceId}`,
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
const workspace =
|
|
1603
|
+
options.workspaceControl ??
|
|
1604
|
+
(await lockWorkspaceInferenceControl(db, workspaceId, options.lock ?? "share"));
|
|
1605
|
+
const projected = projectEffectiveControl(
|
|
1606
|
+
workspace,
|
|
1607
|
+
sessionId,
|
|
1608
|
+
await loadTargetAncestryRows(db, workspaceId, [sessionId]),
|
|
1609
|
+
NO_SETTLEMENT_ATTEMPTS,
|
|
1610
|
+
0,
|
|
1611
|
+
);
|
|
1612
|
+
return {
|
|
1613
|
+
state: projected.state,
|
|
1614
|
+
controlVersion: projected.controlVersion,
|
|
1615
|
+
primaryBlockerKind: projected.primaryBlocker?.kind ?? null,
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1503
1619
|
type SessionDiscoveryControlRow = {
|
|
1504
1620
|
targetId: string;
|
|
1505
1621
|
ancestryCount: number | string;
|
|
@@ -3250,6 +3366,11 @@ export async function mutateSessionControlInTransaction(
|
|
|
3250
3366
|
}
|
|
3251
3367
|
}
|
|
3252
3368
|
|
|
3369
|
+
const targetSession = locks.sessions.find((session) => session.id === input.sessionId);
|
|
3370
|
+
if (!targetSession) {
|
|
3371
|
+
throw new SessionControlInvariantError(`Session ${input.sessionId} disappeared`);
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3253
3374
|
const revision = nextRevision(workspace);
|
|
3254
3375
|
await advanceWorkspaceRevision(db, input.workspaceId, revision);
|
|
3255
3376
|
const updatedRows = await db
|
|
@@ -3290,10 +3411,7 @@ export async function mutateSessionControlInTransaction(
|
|
|
3290
3411
|
: eq(schema.sessions.id, input.sessionId),
|
|
3291
3412
|
),
|
|
3292
3413
|
)
|
|
3293
|
-
.returning({
|
|
3294
|
-
id: schema.sessions.id,
|
|
3295
|
-
lastSequence: schema.sessions.lastSequence,
|
|
3296
|
-
});
|
|
3414
|
+
.returning({ id: schema.sessions.id });
|
|
3297
3415
|
const updated = updatedRows.find((row) => row.id === input.sessionId);
|
|
3298
3416
|
if (!updated) throw new SessionControlInvariantError(`Session ${input.sessionId} disappeared`);
|
|
3299
3417
|
|
|
@@ -3356,7 +3474,7 @@ export async function mutateSessionControlInTransaction(
|
|
|
3356
3474
|
accountId: input.accountId,
|
|
3357
3475
|
workspaceId: input.workspaceId,
|
|
3358
3476
|
sessionId: input.sessionId,
|
|
3359
|
-
sequence:
|
|
3477
|
+
sequence: targetSession.lastSequence + 1,
|
|
3360
3478
|
type:
|
|
3361
3479
|
input.action === "pause" || input.action === "cancel"
|
|
3362
3480
|
? "session.control.paused"
|
|
@@ -3380,7 +3498,7 @@ export async function mutateSessionControlInTransaction(
|
|
|
3380
3498
|
throw new SessionControlInvariantError("Session control event was not inserted");
|
|
3381
3499
|
await db
|
|
3382
3500
|
.update(schema.sessions)
|
|
3383
|
-
.set({ lastSequence:
|
|
3501
|
+
.set({ lastSequence: targetSession.lastSequence + 1, updatedAt: new Date() })
|
|
3384
3502
|
.where(eq(schema.sessions.id, input.sessionId));
|
|
3385
3503
|
await db.insert(schema.auditEvents).values(
|
|
3386
3504
|
withLosslessContentWriteVersion(
|