@opengeni/db 0.27.9 → 0.28.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/{chunk-2JFRTKTG.js → chunk-DBS5HPEW.js} +11 -1
- package/dist/chunk-DBS5HPEW.js.map +1 -0
- package/dist/{chunk-NX2JNJJP.js → chunk-HT5T62CC.js} +2538 -2283
- package/dist/chunk-HT5T62CC.js.map +1 -0
- package/dist/index.d.ts +69 -36
- package/dist/index.js +4146 -2924
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +129 -96
- package/dist/schema.js +19 -1
- package/dist/transcription-recordings-schema.d.ts +1226 -0
- package/dist/transcription-recordings.d.ts +228 -0
- package/drizzle/0170_resumable_transcription_recordings.sql +440 -0
- package/drizzle/0172_retire_model_visible_github_token.sql +86 -0
- package/drizzle/0173_codex_auth_boundaries.sql +107 -0
- package/drizzle/0174_session_wake_live_interruption.sql +80 -0
- package/drizzle/0175_resumable_transcription_provider_deadline.sql +24 -0
- package/package.json +4 -4
- package/src/index.ts +585 -245
- package/src/runtime-posture.ts +10 -0
- package/src/schema.ts +63 -51
- package/src/transcription-recordings-schema.ts +250 -0
- package/src/transcription-recordings.ts +1508 -0
- package/dist/chunk-2JFRTKTG.js.map +0 -1
- package/dist/chunk-NX2JNJJP.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -11473,11 +11473,10 @@ export async function upsertCodexSubscriptionCredential(
|
|
|
11473
11473
|
// it when still null) so a re-connect never clobbers a rename.
|
|
11474
11474
|
accountEmail: input.accountEmail ?? null,
|
|
11475
11475
|
label: sql`coalesce(${schema.codexSubscriptionCredentials.label}, ${input.label ?? null})`,
|
|
11476
|
-
//
|
|
11477
|
-
//
|
|
11478
|
-
//
|
|
11479
|
-
|
|
11480
|
-
connectedBySubjectId: input.connectedBySubjectId ?? null,
|
|
11476
|
+
// Reconnect refreshes credential material, never ownership. A row
|
|
11477
|
+
// without an owner may be claimed by its first direct managed human;
|
|
11478
|
+
// after that, disconnect is the explicit ownership-reset boundary.
|
|
11479
|
+
connectedBySubjectId: sql`coalesce(${schema.codexSubscriptionCredentials.connectedBySubjectId}, ${input.connectedBySubjectId ?? null})`,
|
|
11481
11480
|
status: "active",
|
|
11482
11481
|
lastError: null,
|
|
11483
11482
|
version: sql`${schema.codexSubscriptionCredentials.version} + 1`,
|
|
@@ -11503,6 +11502,333 @@ export async function upsertCodexSubscriptionCredential(
|
|
|
11503
11502
|
);
|
|
11504
11503
|
}
|
|
11505
11504
|
|
|
11505
|
+
export type CodexAppsSettings = {
|
|
11506
|
+
credentialId: string | null;
|
|
11507
|
+
version: number;
|
|
11508
|
+
designatedAt: Date | null;
|
|
11509
|
+
};
|
|
11510
|
+
|
|
11511
|
+
export async function getCodexAppsSettings(
|
|
11512
|
+
db: Database,
|
|
11513
|
+
workspaceId: string,
|
|
11514
|
+
): Promise<CodexAppsSettings> {
|
|
11515
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
11516
|
+
const [row] = await scopedDb
|
|
11517
|
+
.select({
|
|
11518
|
+
credentialId: schema.codexAppsSettings.credentialId,
|
|
11519
|
+
version: schema.codexAppsSettings.version,
|
|
11520
|
+
designatedAt: schema.codexAppsSettings.designatedAt,
|
|
11521
|
+
})
|
|
11522
|
+
.from(schema.codexAppsSettings)
|
|
11523
|
+
.where(eq(schema.codexAppsSettings.workspaceId, workspaceId))
|
|
11524
|
+
.limit(1);
|
|
11525
|
+
return row ?? { credentialId: null, version: 0, designatedAt: null };
|
|
11526
|
+
});
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11529
|
+
export type CodexAppsCredentialAuthorization = {
|
|
11530
|
+
credentialId: string;
|
|
11531
|
+
ownerSubjectId: string;
|
|
11532
|
+
};
|
|
11533
|
+
|
|
11534
|
+
function canManageCodexApps(permissions: unknown): boolean {
|
|
11535
|
+
return (
|
|
11536
|
+
Array.isArray(permissions) &&
|
|
11537
|
+
(permissions.includes("connections:write") || permissions.includes("workspace:admin"))
|
|
11538
|
+
);
|
|
11539
|
+
}
|
|
11540
|
+
|
|
11541
|
+
export class CodexAppsAuthorizationRevokedError extends Error {
|
|
11542
|
+
constructor() {
|
|
11543
|
+
super("Codex Apps authorization is no longer active");
|
|
11544
|
+
this.name = "CodexAppsAuthorizationRevokedError";
|
|
11545
|
+
}
|
|
11546
|
+
}
|
|
11547
|
+
|
|
11548
|
+
/** Exact Apps credential and owner, independent of every inference-capacity field. */
|
|
11549
|
+
export async function getCodexAppsCredentialAuthorizationForRun(
|
|
11550
|
+
db: Database,
|
|
11551
|
+
workspaceId: string,
|
|
11552
|
+
): Promise<CodexAppsCredentialAuthorization | null> {
|
|
11553
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
11554
|
+
const [row] = await scopedDb
|
|
11555
|
+
.select({
|
|
11556
|
+
credentialId: schema.codexAppsSettings.credentialId,
|
|
11557
|
+
ownerSubjectId: schema.codexSubscriptionCredentials.connectedBySubjectId,
|
|
11558
|
+
})
|
|
11559
|
+
.from(schema.codexAppsSettings)
|
|
11560
|
+
.innerJoin(
|
|
11561
|
+
schema.codexSubscriptionCredentials,
|
|
11562
|
+
and(
|
|
11563
|
+
eq(schema.codexSubscriptionCredentials.id, schema.codexAppsSettings.credentialId),
|
|
11564
|
+
eq(schema.codexSubscriptionCredentials.accountId, schema.codexAppsSettings.accountId),
|
|
11565
|
+
eq(schema.codexSubscriptionCredentials.workspaceId, workspaceId),
|
|
11566
|
+
eq(schema.codexSubscriptionCredentials.status, "active"),
|
|
11567
|
+
),
|
|
11568
|
+
)
|
|
11569
|
+
.where(eq(schema.codexAppsSettings.workspaceId, workspaceId))
|
|
11570
|
+
.limit(1);
|
|
11571
|
+
return row?.credentialId && row.ownerSubjectId
|
|
11572
|
+
? { credentialId: row.credentialId, ownerSubjectId: row.ownerSubjectId }
|
|
11573
|
+
: null;
|
|
11574
|
+
});
|
|
11575
|
+
}
|
|
11576
|
+
|
|
11577
|
+
/**
|
|
11578
|
+
* Hold the exact designation, credential, and owner-membership authorization
|
|
11579
|
+
* stable through one outbound Apps request. Clear/disconnect operations share
|
|
11580
|
+
* the workspace advisory lock; credential or membership revocation must wait on
|
|
11581
|
+
* the row locks. The callback deliberately runs inside this short transaction so
|
|
11582
|
+
* no revocation can commit in the gap between a final check and network dispatch.
|
|
11583
|
+
*/
|
|
11584
|
+
export async function withCodexAppsRequestAuthorization<T>(
|
|
11585
|
+
db: Database,
|
|
11586
|
+
input: { workspaceId: string; credentialId: string },
|
|
11587
|
+
use: () => Promise<T>,
|
|
11588
|
+
): Promise<T> {
|
|
11589
|
+
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
11590
|
+
await scopedDb.execute(
|
|
11591
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`codex-apps-settings:${input.workspaceId}`}, 0))`,
|
|
11592
|
+
);
|
|
11593
|
+
const [designation] = await scopedDb
|
|
11594
|
+
.select({ credentialId: schema.codexAppsSettings.credentialId })
|
|
11595
|
+
.from(schema.codexAppsSettings)
|
|
11596
|
+
.where(eq(schema.codexAppsSettings.workspaceId, input.workspaceId))
|
|
11597
|
+
.for("share")
|
|
11598
|
+
.limit(1);
|
|
11599
|
+
if (designation?.credentialId !== input.credentialId) {
|
|
11600
|
+
throw new CodexAppsAuthorizationRevokedError();
|
|
11601
|
+
}
|
|
11602
|
+
const [credential] = await scopedDb
|
|
11603
|
+
.select({
|
|
11604
|
+
ownerSubjectId: schema.codexSubscriptionCredentials.connectedBySubjectId,
|
|
11605
|
+
status: schema.codexSubscriptionCredentials.status,
|
|
11606
|
+
})
|
|
11607
|
+
.from(schema.codexSubscriptionCredentials)
|
|
11608
|
+
.where(
|
|
11609
|
+
and(
|
|
11610
|
+
eq(schema.codexSubscriptionCredentials.workspaceId, input.workspaceId),
|
|
11611
|
+
eq(schema.codexSubscriptionCredentials.id, input.credentialId),
|
|
11612
|
+
),
|
|
11613
|
+
)
|
|
11614
|
+
.for("share")
|
|
11615
|
+
.limit(1);
|
|
11616
|
+
if (!credential?.ownerSubjectId || credential.status !== "active") {
|
|
11617
|
+
throw new CodexAppsAuthorizationRevokedError();
|
|
11618
|
+
}
|
|
11619
|
+
const [membership] = await scopedDb
|
|
11620
|
+
.select({ permissions: schema.workspaceMemberships.permissions })
|
|
11621
|
+
.from(schema.workspaceMemberships)
|
|
11622
|
+
.where(
|
|
11623
|
+
and(
|
|
11624
|
+
eq(schema.workspaceMemberships.workspaceId, input.workspaceId),
|
|
11625
|
+
eq(schema.workspaceMemberships.subjectId, credential.ownerSubjectId),
|
|
11626
|
+
),
|
|
11627
|
+
)
|
|
11628
|
+
.for("share")
|
|
11629
|
+
.limit(1);
|
|
11630
|
+
if (!canManageCodexApps(membership?.permissions)) {
|
|
11631
|
+
throw new CodexAppsAuthorizationRevokedError();
|
|
11632
|
+
}
|
|
11633
|
+
return await use();
|
|
11634
|
+
});
|
|
11635
|
+
}
|
|
11636
|
+
|
|
11637
|
+
export type DesignateCodexAppsCredentialResult =
|
|
11638
|
+
| ({ kind: "updated" } & CodexAppsSettings)
|
|
11639
|
+
| ({ kind: "conflict" | "already_designated" } & CodexAppsSettings)
|
|
11640
|
+
| { kind: "not_found" }
|
|
11641
|
+
| { kind: "not_owner" }
|
|
11642
|
+
| { kind: "forbidden" }
|
|
11643
|
+
| { kind: "unavailable" };
|
|
11644
|
+
|
|
11645
|
+
export async function designateCodexAppsCredential(
|
|
11646
|
+
db: Database,
|
|
11647
|
+
input: {
|
|
11648
|
+
accountId: string;
|
|
11649
|
+
workspaceId: string;
|
|
11650
|
+
credentialId: string;
|
|
11651
|
+
subjectId: string;
|
|
11652
|
+
expectedVersion: number;
|
|
11653
|
+
},
|
|
11654
|
+
): Promise<DesignateCodexAppsCredentialResult> {
|
|
11655
|
+
return await withRlsContext(
|
|
11656
|
+
db,
|
|
11657
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
11658
|
+
async (scopedDb) => {
|
|
11659
|
+
await scopedDb.execute(
|
|
11660
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`codex-apps-settings:${input.workspaceId}`}, 0))`,
|
|
11661
|
+
);
|
|
11662
|
+
const [currentRow] = await scopedDb
|
|
11663
|
+
.select()
|
|
11664
|
+
.from(schema.codexAppsSettings)
|
|
11665
|
+
.where(eq(schema.codexAppsSettings.workspaceId, input.workspaceId))
|
|
11666
|
+
.for("update")
|
|
11667
|
+
.limit(1);
|
|
11668
|
+
const current: CodexAppsSettings = currentRow
|
|
11669
|
+
? {
|
|
11670
|
+
credentialId: currentRow.credentialId,
|
|
11671
|
+
version: currentRow.version,
|
|
11672
|
+
designatedAt: currentRow.designatedAt,
|
|
11673
|
+
}
|
|
11674
|
+
: { credentialId: null, version: 0, designatedAt: null };
|
|
11675
|
+
if (current.version !== input.expectedVersion) return { kind: "conflict", ...current };
|
|
11676
|
+
if (current.credentialId !== null) return { kind: "already_designated", ...current };
|
|
11677
|
+
|
|
11678
|
+
const [credential] = await scopedDb
|
|
11679
|
+
.select({
|
|
11680
|
+
connectedBySubjectId: schema.codexSubscriptionCredentials.connectedBySubjectId,
|
|
11681
|
+
status: schema.codexSubscriptionCredentials.status,
|
|
11682
|
+
})
|
|
11683
|
+
.from(schema.codexSubscriptionCredentials)
|
|
11684
|
+
.where(
|
|
11685
|
+
and(
|
|
11686
|
+
eq(schema.codexSubscriptionCredentials.accountId, input.accountId),
|
|
11687
|
+
eq(schema.codexSubscriptionCredentials.workspaceId, input.workspaceId),
|
|
11688
|
+
eq(schema.codexSubscriptionCredentials.id, input.credentialId),
|
|
11689
|
+
),
|
|
11690
|
+
)
|
|
11691
|
+
.for("update")
|
|
11692
|
+
.limit(1);
|
|
11693
|
+
if (!credential) return { kind: "not_found" };
|
|
11694
|
+
if (credential.connectedBySubjectId !== input.subjectId) return { kind: "not_owner" };
|
|
11695
|
+
if (credential.status !== "active") return { kind: "unavailable" };
|
|
11696
|
+
|
|
11697
|
+
const [membership] = await scopedDb
|
|
11698
|
+
.select({ permissions: schema.workspaceMemberships.permissions })
|
|
11699
|
+
.from(schema.workspaceMemberships)
|
|
11700
|
+
.where(
|
|
11701
|
+
and(
|
|
11702
|
+
eq(schema.workspaceMemberships.accountId, input.accountId),
|
|
11703
|
+
eq(schema.workspaceMemberships.workspaceId, input.workspaceId),
|
|
11704
|
+
eq(schema.workspaceMemberships.subjectId, input.subjectId),
|
|
11705
|
+
),
|
|
11706
|
+
)
|
|
11707
|
+
.for("update")
|
|
11708
|
+
.limit(1);
|
|
11709
|
+
if (!canManageCodexApps(membership?.permissions)) return { kind: "forbidden" };
|
|
11710
|
+
|
|
11711
|
+
const now = new Date();
|
|
11712
|
+
const version = current.version + 1;
|
|
11713
|
+
const [updated] = await scopedDb
|
|
11714
|
+
.insert(schema.codexAppsSettings)
|
|
11715
|
+
.values({
|
|
11716
|
+
accountId: input.accountId,
|
|
11717
|
+
workspaceId: input.workspaceId,
|
|
11718
|
+
credentialId: input.credentialId,
|
|
11719
|
+
version,
|
|
11720
|
+
designatedAt: now,
|
|
11721
|
+
updatedAt: now,
|
|
11722
|
+
})
|
|
11723
|
+
.onConflictDoUpdate({
|
|
11724
|
+
target: schema.codexAppsSettings.workspaceId,
|
|
11725
|
+
set: {
|
|
11726
|
+
credentialId: input.credentialId,
|
|
11727
|
+
version,
|
|
11728
|
+
designatedAt: now,
|
|
11729
|
+
updatedAt: now,
|
|
11730
|
+
},
|
|
11731
|
+
})
|
|
11732
|
+
.returning({
|
|
11733
|
+
credentialId: schema.codexAppsSettings.credentialId,
|
|
11734
|
+
version: schema.codexAppsSettings.version,
|
|
11735
|
+
designatedAt: schema.codexAppsSettings.designatedAt,
|
|
11736
|
+
});
|
|
11737
|
+
if (!updated?.credentialId || !updated.designatedAt) {
|
|
11738
|
+
throw new Error("Codex Apps designation was not persisted");
|
|
11739
|
+
}
|
|
11740
|
+
await scopedDb.insert(schema.auditEvents).values({
|
|
11741
|
+
accountId: input.accountId,
|
|
11742
|
+
workspaceId: input.workspaceId,
|
|
11743
|
+
subjectId: input.subjectId,
|
|
11744
|
+
action: "codex_apps.designated",
|
|
11745
|
+
targetType: "codex_subscription_credential",
|
|
11746
|
+
targetId: input.credentialId,
|
|
11747
|
+
metadata: { version },
|
|
11748
|
+
});
|
|
11749
|
+
return { kind: "updated", ...updated };
|
|
11750
|
+
},
|
|
11751
|
+
);
|
|
11752
|
+
}
|
|
11753
|
+
|
|
11754
|
+
export type ClearCodexAppsCredentialResult = {
|
|
11755
|
+
kind: "updated" | "unchanged" | "conflict" | "forbidden";
|
|
11756
|
+
} & CodexAppsSettings;
|
|
11757
|
+
|
|
11758
|
+
export async function clearCodexAppsCredential(
|
|
11759
|
+
db: Database,
|
|
11760
|
+
input: {
|
|
11761
|
+
accountId: string;
|
|
11762
|
+
workspaceId: string;
|
|
11763
|
+
subjectId: string;
|
|
11764
|
+
expectedVersion: number;
|
|
11765
|
+
},
|
|
11766
|
+
): Promise<ClearCodexAppsCredentialResult> {
|
|
11767
|
+
return await withRlsContext(
|
|
11768
|
+
db,
|
|
11769
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
11770
|
+
async (scopedDb) => {
|
|
11771
|
+
await scopedDb.execute(
|
|
11772
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`codex-apps-settings:${input.workspaceId}`}, 0))`,
|
|
11773
|
+
);
|
|
11774
|
+
const [row] = await scopedDb
|
|
11775
|
+
.select()
|
|
11776
|
+
.from(schema.codexAppsSettings)
|
|
11777
|
+
.where(eq(schema.codexAppsSettings.workspaceId, input.workspaceId))
|
|
11778
|
+
.for("update")
|
|
11779
|
+
.limit(1);
|
|
11780
|
+
const current: CodexAppsSettings = row
|
|
11781
|
+
? { credentialId: row.credentialId, version: row.version, designatedAt: row.designatedAt }
|
|
11782
|
+
: { credentialId: null, version: 0, designatedAt: null };
|
|
11783
|
+
const [membership] = await scopedDb
|
|
11784
|
+
.select({ permissions: schema.workspaceMemberships.permissions })
|
|
11785
|
+
.from(schema.workspaceMemberships)
|
|
11786
|
+
.where(
|
|
11787
|
+
and(
|
|
11788
|
+
eq(schema.workspaceMemberships.accountId, input.accountId),
|
|
11789
|
+
eq(schema.workspaceMemberships.workspaceId, input.workspaceId),
|
|
11790
|
+
eq(schema.workspaceMemberships.subjectId, input.subjectId),
|
|
11791
|
+
),
|
|
11792
|
+
)
|
|
11793
|
+
.for("update")
|
|
11794
|
+
.limit(1);
|
|
11795
|
+
if (!canManageCodexApps(membership?.permissions)) {
|
|
11796
|
+
return { kind: "forbidden", ...current };
|
|
11797
|
+
}
|
|
11798
|
+
if (current.version !== input.expectedVersion) return { kind: "conflict", ...current };
|
|
11799
|
+
if (current.credentialId === null) return { kind: "unchanged", ...current };
|
|
11800
|
+
|
|
11801
|
+
const now = new Date();
|
|
11802
|
+
const version = current.version + 1;
|
|
11803
|
+
const [updated] = await scopedDb
|
|
11804
|
+
.update(schema.codexAppsSettings)
|
|
11805
|
+
.set({
|
|
11806
|
+
credentialId: null,
|
|
11807
|
+
version,
|
|
11808
|
+
designatedAt: null,
|
|
11809
|
+
updatedAt: now,
|
|
11810
|
+
})
|
|
11811
|
+
.where(eq(schema.codexAppsSettings.workspaceId, input.workspaceId))
|
|
11812
|
+
.returning({
|
|
11813
|
+
credentialId: schema.codexAppsSettings.credentialId,
|
|
11814
|
+
version: schema.codexAppsSettings.version,
|
|
11815
|
+
designatedAt: schema.codexAppsSettings.designatedAt,
|
|
11816
|
+
});
|
|
11817
|
+
if (!updated) throw new Error("Codex Apps designation clear was not persisted");
|
|
11818
|
+
await scopedDb.insert(schema.auditEvents).values({
|
|
11819
|
+
accountId: input.accountId,
|
|
11820
|
+
workspaceId: input.workspaceId,
|
|
11821
|
+
subjectId: input.subjectId,
|
|
11822
|
+
action: "codex_apps.cleared",
|
|
11823
|
+
targetType: "codex_subscription_credential",
|
|
11824
|
+
targetId: current.credentialId,
|
|
11825
|
+
metadata: { version },
|
|
11826
|
+
});
|
|
11827
|
+
return { kind: "updated", ...updated };
|
|
11828
|
+
},
|
|
11829
|
+
);
|
|
11830
|
+
}
|
|
11831
|
+
|
|
11506
11832
|
/**
|
|
11507
11833
|
* The ONLY decrypt-read accessor. Fails closed. Never call from an API route that
|
|
11508
11834
|
* returns the result.
|
|
@@ -11989,11 +12315,6 @@ export type CodexAccountStatus = {
|
|
|
11989
12315
|
// P3 rotation cooldown: when set and in the future, this account is cooling-down
|
|
11990
12316
|
// (rotated-off after a usage cap) and the engine skips it. null ⇒ not cooling.
|
|
11991
12317
|
exhaustedUntil: Date | null;
|
|
11992
|
-
// P4 connector-aware rotation: the ORIGINAL-dotted connector namespaces this
|
|
11993
|
-
// account exposes via codex_apps (github/gmail/linear/…). null ⇒ never probed
|
|
11994
|
-
// (the ranker treats it as unknown: never credited as covering, never excluded).
|
|
11995
|
-
connectorNamespaces: string[] | null;
|
|
11996
|
-
connectorsCheckedAt: Date | null;
|
|
11997
12318
|
};
|
|
11998
12319
|
|
|
11999
12320
|
/**
|
|
@@ -12112,8 +12433,6 @@ type CodexLeaseCandidateRow = {
|
|
|
12112
12433
|
secondary_reset_at: Date | string | null;
|
|
12113
12434
|
usage_checked_at: Date | string | null;
|
|
12114
12435
|
exhausted_until: Date | string | null;
|
|
12115
|
-
connector_namespaces: string[] | null;
|
|
12116
|
-
connectors_checked_at: Date | string | null;
|
|
12117
12436
|
selection_count: number;
|
|
12118
12437
|
last_selected_at: Date | string | null;
|
|
12119
12438
|
active_lease_count: number;
|
|
@@ -12146,8 +12465,6 @@ function mapCodexLeaseCandidate(
|
|
|
12146
12465
|
secondaryResetAt: codexMetadataDate(row.secondary_reset_at),
|
|
12147
12466
|
usageCheckedAt: codexMetadataDate(row.usage_checked_at),
|
|
12148
12467
|
exhaustedUntil: codexMetadataDate(row.exhausted_until),
|
|
12149
|
-
connectorNamespaces: row.connector_namespaces,
|
|
12150
|
-
connectorsCheckedAt: codexMetadataDate(row.connectors_checked_at),
|
|
12151
12468
|
selectionCount: Number(row.selection_count),
|
|
12152
12469
|
lastSelectedAt: codexMetadataDate(row.last_selected_at),
|
|
12153
12470
|
activeLeaseCount: Number(row.active_lease_count),
|
|
@@ -12207,8 +12524,6 @@ async function listCodexLeaseCandidatesInTransaction(
|
|
|
12207
12524
|
c.secondary_reset_at,
|
|
12208
12525
|
c.usage_checked_at,
|
|
12209
12526
|
c.exhausted_until,
|
|
12210
|
-
c.connector_namespaces,
|
|
12211
|
-
c.connectors_checked_at,
|
|
12212
12527
|
c.selection_count,
|
|
12213
12528
|
c.last_selected_at,
|
|
12214
12529
|
count(l.id) filter (
|
|
@@ -12255,8 +12570,6 @@ export async function acquireCodexCredentialLease<
|
|
|
12255
12570
|
holderId: string;
|
|
12256
12571
|
/** Pins must not move the workspace-global cursor. */
|
|
12257
12572
|
advanceActivePointer: boolean;
|
|
12258
|
-
/** Exact frozen credential for this same durable turn, if it is resuming. */
|
|
12259
|
-
continuationCredentialId?: string | null;
|
|
12260
12573
|
/**
|
|
12261
12574
|
* Optional downstream parser for private accepted-turn policy metadata.
|
|
12262
12575
|
* It is pure, runs under the turn/rotation transaction, and must not query
|
|
@@ -12265,8 +12578,8 @@ export async function acquireCodexCredentialLease<
|
|
|
12265
12578
|
resolvePolicyScope?: CodexCredentialLeasePolicyScopeResolver<TPolicyScope>;
|
|
12266
12579
|
/**
|
|
12267
12580
|
* Optional downstream membership policy for NEW allocations only. A live
|
|
12268
|
-
* lease
|
|
12269
|
-
*
|
|
12581
|
+
* exact-turn lease is offered to the selector against the complete workspace
|
|
12582
|
+
* rows first and can never be filtered out here.
|
|
12270
12583
|
*/
|
|
12271
12584
|
filterNewAllocationCandidates?: CodexCredentialLeaseCandidateFilter<
|
|
12272
12585
|
TPolicyScope,
|
|
@@ -12332,23 +12645,6 @@ export async function acquireCodexCredentialLease<
|
|
|
12332
12645
|
throw new Error(`Session turn not found for Codex lease: ${input.turnId}`);
|
|
12333
12646
|
}
|
|
12334
12647
|
const policyScope = input.resolvePolicyScope?.(turns[0].metadata ?? {}) ?? null;
|
|
12335
|
-
const continuationRows = input.continuationCredentialId
|
|
12336
|
-
? await tx.execute(
|
|
12337
|
-
sql<{ frozen_codex_credential_id: string | null }>`
|
|
12338
|
-
select frozen_codex_credential_id
|
|
12339
|
-
from agent_run_states
|
|
12340
|
-
where account_id = ${input.accountId}
|
|
12341
|
-
and workspace_id = ${input.workspaceId}
|
|
12342
|
-
and turn_id = ${input.turnId}
|
|
12343
|
-
order by state_version desc
|
|
12344
|
-
limit 1
|
|
12345
|
-
`,
|
|
12346
|
-
)
|
|
12347
|
-
: [];
|
|
12348
|
-
const validatedContinuationCredentialId =
|
|
12349
|
-
continuationRows[0]?.frozen_codex_credential_id === input.continuationCredentialId
|
|
12350
|
-
? input.continuationCredentialId
|
|
12351
|
-
: null;
|
|
12352
12648
|
const activeCredentialId = settingsRow.active_credential_id;
|
|
12353
12649
|
const rotationEnabled = settingsRow.rotation_enabled;
|
|
12354
12650
|
// Fail closed on a torn/manual legacy write. The user-intent bit and the
|
|
@@ -12390,7 +12686,7 @@ export async function acquireCodexCredentialLease<
|
|
|
12390
12686
|
activeCredentialId,
|
|
12391
12687
|
excludeTurnId: input.turnId,
|
|
12392
12688
|
});
|
|
12393
|
-
const sameTurnCredentialId = existingCredentialId
|
|
12689
|
+
const sameTurnCredentialId = existingCredentialId;
|
|
12394
12690
|
const selectionContext = (
|
|
12395
12691
|
accounts: CodexLeaseAccountStatus[],
|
|
12396
12692
|
unavailableDiagnostics: readonly TUnavailableDiagnostic[],
|
|
@@ -12414,10 +12710,9 @@ export async function acquireCodexCredentialLease<
|
|
|
12414
12710
|
}
|
|
12415
12711
|
}
|
|
12416
12712
|
|
|
12417
|
-
//
|
|
12713
|
+
// A live exact-turn lease is resolved before any future pool membership
|
|
12418
12714
|
// filter. The normal selector still owns health validation: a quarantined
|
|
12419
|
-
//
|
|
12420
|
-
// being reused blindly.
|
|
12715
|
+
// row falls through to scoped new acquisition rather than being reused.
|
|
12421
12716
|
if (!selected) {
|
|
12422
12717
|
const filtered = filterCodexLeaseCandidatesForPolicy(
|
|
12423
12718
|
allAccounts,
|
|
@@ -12454,11 +12749,7 @@ export async function acquireCodexCredentialLease<
|
|
|
12454
12749
|
if (!selectedAccount) {
|
|
12455
12750
|
throw new Error("Codex lease selector returned a credential outside the workspace pool");
|
|
12456
12751
|
}
|
|
12457
|
-
if (
|
|
12458
|
-
!selectedAccount.allocatorEnabled &&
|
|
12459
|
-
selectedAccount.id !== existingCredentialId &&
|
|
12460
|
-
selectedAccount.id !== validatedContinuationCredentialId
|
|
12461
|
-
) {
|
|
12752
|
+
if (!selectedAccount.allocatorEnabled && selectedAccount.id !== existingCredentialId) {
|
|
12462
12753
|
throw new Error("Codex lease selector returned a credential disabled for new allocations");
|
|
12463
12754
|
}
|
|
12464
12755
|
|
|
@@ -13873,9 +14164,6 @@ export async function listCodexAccountStatuses(
|
|
|
13873
14164
|
secondaryResetAt: schema.codexSubscriptionCredentials.secondaryResetAt,
|
|
13874
14165
|
usageCheckedAt: schema.codexSubscriptionCredentials.usageCheckedAt,
|
|
13875
14166
|
exhaustedUntil: schema.codexSubscriptionCredentials.exhaustedUntil,
|
|
13876
|
-
// P4 connector-set cache — metadata-only, rides along on this read.
|
|
13877
|
-
connectorNamespaces: schema.codexSubscriptionCredentials.connectorNamespaces,
|
|
13878
|
-
connectorsCheckedAt: schema.codexSubscriptionCredentials.connectorsCheckedAt,
|
|
13879
14167
|
})
|
|
13880
14168
|
.from(schema.codexSubscriptionCredentials)
|
|
13881
14169
|
.where(eq(schema.codexSubscriptionCredentials.workspaceId, workspaceId))
|
|
@@ -13893,7 +14181,6 @@ export async function listCodexAccountStatuses(
|
|
|
13893
14181
|
secondaryResetAt: codexMetadataDate(row.secondaryResetAt),
|
|
13894
14182
|
usageCheckedAt: codexMetadataDate(row.usageCheckedAt),
|
|
13895
14183
|
exhaustedUntil: codexMetadataDate(row.exhaustedUntil),
|
|
13896
|
-
connectorsCheckedAt: codexMetadataDate(row.connectorsCheckedAt),
|
|
13897
14184
|
isActive: row.id === activeId,
|
|
13898
14185
|
}));
|
|
13899
14186
|
});
|
|
@@ -15156,46 +15443,6 @@ export async function countConsecutiveReactiveRotations(
|
|
|
15156
15443
|
});
|
|
15157
15444
|
}
|
|
15158
15445
|
|
|
15159
|
-
/**
|
|
15160
|
-
* P4 connector-set cache writer: persist the set of ORIGINAL-dotted connector
|
|
15161
|
-
* namespaces a SPECIFIC credential exposes via codex_apps (+ the freshness clock).
|
|
15162
|
-
* Modeled byte-for-byte on recordCodexAccountUsage / setCodexCredentialExhausted:
|
|
15163
|
-
* RLS-scoped, guarded by (id, workspace_id), and — critically — NO `version` bump and
|
|
15164
|
-
* NO `updatedAt` touch, so it can never race the (id, version) token-refresh CAS.
|
|
15165
|
-
*
|
|
15166
|
-
* The CALLER must only invoke this with a NON-EMPTY set: codex_apps connects
|
|
15167
|
-
* best-effort (a transient failure yields an empty tools/list), and overwriting a
|
|
15168
|
-
* known non-empty set with [] would falsely "drop" coverage on a flaky turn. A
|
|
15169
|
-
* genuinely connector-less account stays null (the ranker treats null as unknown).
|
|
15170
|
-
* Returns true iff a row was updated (false ⇒ the credential was disconnected under us).
|
|
15171
|
-
*/
|
|
15172
|
-
export async function recordCodexAccountConnectors(
|
|
15173
|
-
db: Database,
|
|
15174
|
-
workspaceId: string,
|
|
15175
|
-
credentialId: string,
|
|
15176
|
-
namespaces: string[],
|
|
15177
|
-
): Promise<boolean> {
|
|
15178
|
-
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15179
|
-
const updated = await scopedDb
|
|
15180
|
-
.update(schema.codexSubscriptionCredentials)
|
|
15181
|
-
.set({
|
|
15182
|
-
connectorNamespaces: namespaces,
|
|
15183
|
-
connectorsCheckedAt: new Date(),
|
|
15184
|
-
// NB: no `version` bump and no `updatedAt` touch — connector set is non-credential
|
|
15185
|
-
// metadata and must NOT race the (id, version) refresh CAS (same discipline as
|
|
15186
|
-
// recordCodexAccountUsage / setCodexCredentialExhausted).
|
|
15187
|
-
})
|
|
15188
|
-
.where(
|
|
15189
|
-
and(
|
|
15190
|
-
eq(schema.codexSubscriptionCredentials.id, credentialId),
|
|
15191
|
-
eq(schema.codexSubscriptionCredentials.workspaceId, workspaceId),
|
|
15192
|
-
),
|
|
15193
|
-
)
|
|
15194
|
-
.returning({ id: schema.codexSubscriptionCredentials.id });
|
|
15195
|
-
return updated.length > 0;
|
|
15196
|
-
});
|
|
15197
|
-
}
|
|
15198
|
-
|
|
15199
15446
|
/** The supported rotation strategies (P3). */
|
|
15200
15447
|
export const CODEX_ROTATION_STRATEGIES = [
|
|
15201
15448
|
"most_remaining",
|
|
@@ -15406,6 +15653,7 @@ export async function disconnectCodexAccount(
|
|
|
15406
15653
|
db: Database,
|
|
15407
15654
|
workspaceId: string,
|
|
15408
15655
|
credentialId: string,
|
|
15656
|
+
actorSubjectId: string | null = null,
|
|
15409
15657
|
): Promise<{
|
|
15410
15658
|
removed: boolean;
|
|
15411
15659
|
newActiveCredentialId: string | null;
|
|
@@ -15417,8 +15665,20 @@ export async function disconnectCodexAccount(
|
|
|
15417
15665
|
where workspace_id = ${workspaceId}
|
|
15418
15666
|
for update
|
|
15419
15667
|
`);
|
|
15668
|
+
await scopedDb.execute(
|
|
15669
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`codex-apps-settings:${workspaceId}`}, 0))`,
|
|
15670
|
+
);
|
|
15671
|
+
const [appsSettings] = await scopedDb
|
|
15672
|
+
.select()
|
|
15673
|
+
.from(schema.codexAppsSettings)
|
|
15674
|
+
.where(eq(schema.codexAppsSettings.workspaceId, workspaceId))
|
|
15675
|
+
.for("update")
|
|
15676
|
+
.limit(1);
|
|
15420
15677
|
const [credential] = await scopedDb
|
|
15421
|
-
.select({
|
|
15678
|
+
.select({
|
|
15679
|
+
id: schema.codexSubscriptionCredentials.id,
|
|
15680
|
+
accountId: schema.codexSubscriptionCredentials.accountId,
|
|
15681
|
+
})
|
|
15422
15682
|
.from(schema.codexSubscriptionCredentials)
|
|
15423
15683
|
.where(
|
|
15424
15684
|
and(
|
|
@@ -15460,6 +15720,27 @@ export async function disconnectCodexAccount(
|
|
|
15460
15720
|
blockedByUnresolvedRedemption: true,
|
|
15461
15721
|
};
|
|
15462
15722
|
}
|
|
15723
|
+
if (appsSettings?.credentialId === credentialId) {
|
|
15724
|
+
const version = appsSettings.version + 1;
|
|
15725
|
+
await scopedDb
|
|
15726
|
+
.update(schema.codexAppsSettings)
|
|
15727
|
+
.set({
|
|
15728
|
+
credentialId: null,
|
|
15729
|
+
version,
|
|
15730
|
+
designatedAt: null,
|
|
15731
|
+
updatedAt: new Date(),
|
|
15732
|
+
})
|
|
15733
|
+
.where(eq(schema.codexAppsSettings.id, appsSettings.id));
|
|
15734
|
+
await scopedDb.insert(schema.auditEvents).values({
|
|
15735
|
+
accountId: credential.accountId,
|
|
15736
|
+
workspaceId,
|
|
15737
|
+
subjectId: actorSubjectId,
|
|
15738
|
+
action: "codex_apps.cleared_on_disconnect",
|
|
15739
|
+
targetType: "codex_subscription_credential",
|
|
15740
|
+
targetId: credentialId,
|
|
15741
|
+
metadata: { version },
|
|
15742
|
+
});
|
|
15743
|
+
}
|
|
15463
15744
|
const removedRows = await scopedDb
|
|
15464
15745
|
.delete(schema.codexSubscriptionCredentials)
|
|
15465
15746
|
.where(
|
|
@@ -15512,10 +15793,23 @@ export async function disconnectCodexAccount(
|
|
|
15512
15793
|
export async function disconnectAllCodexAccounts(
|
|
15513
15794
|
db: Database,
|
|
15514
15795
|
workspaceId: string,
|
|
15796
|
+
actorSubjectId: string | null = null,
|
|
15515
15797
|
): Promise<{ removed: number; blockedCredentialIds: string[] }> {
|
|
15516
15798
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
15799
|
+
await scopedDb.execute(
|
|
15800
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`codex-apps-settings:${workspaceId}`}, 0))`,
|
|
15801
|
+
);
|
|
15802
|
+
const [appsSettings] = await scopedDb
|
|
15803
|
+
.select()
|
|
15804
|
+
.from(schema.codexAppsSettings)
|
|
15805
|
+
.where(eq(schema.codexAppsSettings.workspaceId, workspaceId))
|
|
15806
|
+
.for("update")
|
|
15807
|
+
.limit(1);
|
|
15517
15808
|
const credentials = await scopedDb
|
|
15518
|
-
.select({
|
|
15809
|
+
.select({
|
|
15810
|
+
id: schema.codexSubscriptionCredentials.id,
|
|
15811
|
+
accountId: schema.codexSubscriptionCredentials.accountId,
|
|
15812
|
+
})
|
|
15519
15813
|
.from(schema.codexSubscriptionCredentials)
|
|
15520
15814
|
.where(eq(schema.codexSubscriptionCredentials.workspaceId, workspaceId))
|
|
15521
15815
|
.orderBy(asc(schema.codexSubscriptionCredentials.id))
|
|
@@ -15538,6 +15832,27 @@ export async function disconnectAllCodexAccounts(
|
|
|
15538
15832
|
blockedCredentialIds: blocked.map((row) => row.credentialId).sort(),
|
|
15539
15833
|
};
|
|
15540
15834
|
}
|
|
15835
|
+
if (appsSettings?.credentialId) {
|
|
15836
|
+
const version = appsSettings.version + 1;
|
|
15837
|
+
await scopedDb
|
|
15838
|
+
.update(schema.codexAppsSettings)
|
|
15839
|
+
.set({
|
|
15840
|
+
credentialId: null,
|
|
15841
|
+
version,
|
|
15842
|
+
designatedAt: null,
|
|
15843
|
+
updatedAt: new Date(),
|
|
15844
|
+
})
|
|
15845
|
+
.where(eq(schema.codexAppsSettings.id, appsSettings.id));
|
|
15846
|
+
await scopedDb.insert(schema.auditEvents).values({
|
|
15847
|
+
accountId: credentials[0]!.accountId,
|
|
15848
|
+
workspaceId,
|
|
15849
|
+
subjectId: actorSubjectId,
|
|
15850
|
+
action: "codex_apps.cleared_on_disconnect",
|
|
15851
|
+
targetType: "codex_subscription_credential",
|
|
15852
|
+
targetId: appsSettings.credentialId,
|
|
15853
|
+
metadata: { version },
|
|
15854
|
+
});
|
|
15855
|
+
}
|
|
15541
15856
|
const rows = await scopedDb
|
|
15542
15857
|
.delete(schema.codexSubscriptionCredentials)
|
|
15543
15858
|
.where(eq(schema.codexSubscriptionCredentials.workspaceId, workspaceId))
|
|
@@ -17117,6 +17432,18 @@ async function lockSessionCreateIdempotencyKey(
|
|
|
17117
17432
|
);
|
|
17118
17433
|
}
|
|
17119
17434
|
|
|
17435
|
+
async function lockAgentSessionCreate(tx: Database, input: SessionCreateInput): Promise<void> {
|
|
17436
|
+
const actorSessionId = input.createdByActor?.sessionId;
|
|
17437
|
+
if (!actorSessionId) return;
|
|
17438
|
+
// Agent-originated child creation first reads the parent under FOR SHARE,
|
|
17439
|
+
// then verifies the live attempt under FOR UPDATE. Serialize creates from
|
|
17440
|
+
// the same parent session so concurrent tool calls cannot deadlock while
|
|
17441
|
+
// upgrading that row lock. Unrelated sessions retain full concurrency.
|
|
17442
|
+
await tx.execute(
|
|
17443
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`agent-session-create:${input.workspaceId}:${actorSessionId}`}, 0))`,
|
|
17444
|
+
);
|
|
17445
|
+
}
|
|
17446
|
+
|
|
17120
17447
|
async function existingSpawnDenialForKey(
|
|
17121
17448
|
tx: Database,
|
|
17122
17449
|
workspaceId: string,
|
|
@@ -17222,6 +17549,8 @@ async function createSessionInTransaction(
|
|
|
17222
17549
|
}
|
|
17223
17550
|
}
|
|
17224
17551
|
|
|
17552
|
+
await lockAgentSessionCreate(tx, input);
|
|
17553
|
+
|
|
17225
17554
|
const decision = await resolveSessionDepthDecision(tx, input, id, workspace, deploymentPolicy);
|
|
17226
17555
|
if (decision.denied) {
|
|
17227
17556
|
return {
|
|
@@ -20250,11 +20579,6 @@ export async function getLatestRunState(
|
|
|
20250
20579
|
turnId: string | null;
|
|
20251
20580
|
serializedRunState: string;
|
|
20252
20581
|
pendingApprovals: unknown[];
|
|
20253
|
-
// The codex account that froze this state (pin > workspace-active), or null
|
|
20254
|
-
// when frozen on the non-codex path / before the column existed. The replay
|
|
20255
|
-
// path compares it to the resuming turn's codex account to decide whether the
|
|
20256
|
-
// blob's account-bound reasoning must be neutralized before being replayed.
|
|
20257
|
-
frozenCodexCredentialId: string | null;
|
|
20258
20582
|
providerArtifactInvalidatedAt: Date | null;
|
|
20259
20583
|
} | null> {
|
|
20260
20584
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
@@ -20275,7 +20599,6 @@ export async function getLatestRunState(
|
|
|
20275
20599
|
turnId: row.turnId ?? null,
|
|
20276
20600
|
serializedRunState: row.serializedRunState,
|
|
20277
20601
|
pendingApprovals: row.pendingApprovals,
|
|
20278
|
-
frozenCodexCredentialId: row.frozenCodexCredentialId ?? null,
|
|
20279
20602
|
providerArtifactInvalidatedAt: row.providerArtifactInvalidatedAt ?? null,
|
|
20280
20603
|
}
|
|
20281
20604
|
: null;
|
|
@@ -21032,10 +21355,6 @@ export async function appendSessionHistoryItems(
|
|
|
21032
21355
|
turnId: string;
|
|
21033
21356
|
expectedExecutionGeneration: number;
|
|
21034
21357
|
expectedAttemptId: string;
|
|
21035
|
-
// The codex account that produced these items (the turn's resolved credential
|
|
21036
|
-
// id), or null/undefined on the non-codex path. Stored verbatim so the read
|
|
21037
|
-
// path can strip cross-account reasoning.encrypted_content blobs per turn.
|
|
21038
|
-
producerCodexCredentialId?: string | null;
|
|
21039
21358
|
modelToolOutputTruncationTokens?: number;
|
|
21040
21359
|
items: Array<{ position: number; item: Record<string, unknown> }>;
|
|
21041
21360
|
},
|
|
@@ -21064,7 +21383,6 @@ export async function appendSessionHistoryItems(
|
|
|
21064
21383
|
workspaceId: input.workspaceId,
|
|
21065
21384
|
sessionId: input.sessionId,
|
|
21066
21385
|
turnId: input.turnId,
|
|
21067
|
-
producerCodexCredentialId: input.producerCodexCredentialId ?? null,
|
|
21068
21386
|
position: entry.position,
|
|
21069
21387
|
// This is the canonical model-memory boundary. The pending-call
|
|
21070
21388
|
// ledger and audit event may retain their separate raw/preview
|
|
@@ -21443,18 +21761,18 @@ export async function getActiveSessionHistoryItems(
|
|
|
21443
21761
|
sessionId: string,
|
|
21444
21762
|
): Promise<
|
|
21445
21763
|
Array<{
|
|
21764
|
+
id: string;
|
|
21446
21765
|
position: number;
|
|
21447
21766
|
item: Record<string, unknown>;
|
|
21448
|
-
producerCodexCredentialId: string | null;
|
|
21449
21767
|
providerArtifactInvalidatedAt: Date | null;
|
|
21450
21768
|
}>
|
|
21451
21769
|
> {
|
|
21452
21770
|
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
21453
21771
|
const rows = await scopedDb
|
|
21454
21772
|
.select({
|
|
21773
|
+
id: schema.sessionHistoryItems.id,
|
|
21455
21774
|
position: schema.sessionHistoryItems.position,
|
|
21456
21775
|
item: schema.sessionHistoryItems.item,
|
|
21457
|
-
producerCodexCredentialId: schema.sessionHistoryItems.producerCodexCredentialId,
|
|
21458
21776
|
providerArtifactInvalidatedAt: schema.sessionHistoryItems.providerArtifactInvalidatedAt,
|
|
21459
21777
|
})
|
|
21460
21778
|
.from(schema.sessionHistoryItems)
|
|
@@ -21649,11 +21967,8 @@ export async function applyContextCompaction(
|
|
|
21649
21967
|
expectedAttemptId: string;
|
|
21650
21968
|
replacementItems: Array<Record<string, unknown>>;
|
|
21651
21969
|
summaryItem: Record<string, unknown>;
|
|
21652
|
-
replacementInputTokens: number;
|
|
21653
21970
|
clearRequestedCompaction?: boolean;
|
|
21654
21971
|
eventPayload?: Record<string, unknown>;
|
|
21655
|
-
/** Tag inserted history rows with the Codex credential that produced them. */
|
|
21656
|
-
producerCodexCredentialId?: string | null;
|
|
21657
21972
|
},
|
|
21658
21973
|
): Promise<ApplyContextCompactionResult> {
|
|
21659
21974
|
return await withRlsContext(
|
|
@@ -21703,9 +22018,6 @@ export async function applyContextCompaction(
|
|
|
21703
22018
|
position: supersededFrom + index,
|
|
21704
22019
|
item: sanitizeModelPayload(item),
|
|
21705
22020
|
active: true,
|
|
21706
|
-
...(input.producerCodexCredentialId
|
|
21707
|
-
? { producerCodexCredentialId: input.producerCodexCredentialId }
|
|
21708
|
-
: {}),
|
|
21709
22021
|
})),
|
|
21710
22022
|
);
|
|
21711
22023
|
}
|
|
@@ -21718,9 +22030,6 @@ export async function applyContextCompaction(
|
|
|
21718
22030
|
position: summaryPosition,
|
|
21719
22031
|
item: sanitizeModelPayload(input.summaryItem),
|
|
21720
22032
|
active: true,
|
|
21721
|
-
...(input.producerCodexCredentialId
|
|
21722
|
-
? { producerCodexCredentialId: input.producerCodexCredentialId }
|
|
21723
|
-
: {}),
|
|
21724
22033
|
});
|
|
21725
22034
|
const insertedEvents = input.eventPayload
|
|
21726
22035
|
? await tx
|
|
@@ -21746,7 +22055,10 @@ export async function applyContextCompaction(
|
|
|
21746
22055
|
await tx
|
|
21747
22056
|
.update(schema.sessions)
|
|
21748
22057
|
.set({
|
|
21749
|
-
|
|
22058
|
+
// The active model input changed without an ordinary provider call.
|
|
22059
|
+
// Keep this field provider-only and let the first successful
|
|
22060
|
+
// post-compaction response install the next authoritative count.
|
|
22061
|
+
lastInputTokens: null,
|
|
21750
22062
|
...(input.clearRequestedCompaction ? { compactRequested: false } : {}),
|
|
21751
22063
|
...(insertedEvents.length > 0
|
|
21752
22064
|
? {
|
|
@@ -21969,8 +22281,9 @@ export async function nextSessionHistoryPosition(
|
|
|
21969
22281
|
}
|
|
21970
22282
|
|
|
21971
22283
|
/**
|
|
21972
|
-
*
|
|
21973
|
-
*
|
|
22284
|
+
* Replace the input-token count for the most recent authoritative terminal
|
|
22285
|
+
* response. Null means that response supplied no usable final-call count, so
|
|
22286
|
+
* the next turn must not reuse an older response's value for compaction.
|
|
21974
22287
|
*/
|
|
21975
22288
|
export async function setSessionLastInputTokensForTurnAttempt(
|
|
21976
22289
|
db: Database,
|
|
@@ -21980,7 +22293,7 @@ export async function setSessionLastInputTokensForTurnAttempt(
|
|
|
21980
22293
|
turnId: string;
|
|
21981
22294
|
expectedExecutionGeneration: number;
|
|
21982
22295
|
expectedAttemptId: string;
|
|
21983
|
-
lastInputTokens: number;
|
|
22296
|
+
lastInputTokens: number | null;
|
|
21984
22297
|
},
|
|
21985
22298
|
): Promise<boolean> {
|
|
21986
22299
|
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
@@ -22038,8 +22351,8 @@ export class SessionContextBusyError extends Error {
|
|
|
22038
22351
|
* reserved for an approval that paused mid-turn, and the API forbids a
|
|
22039
22352
|
* clear while such an approval or active turn exists.
|
|
22040
22353
|
*
|
|
22041
|
-
* Also
|
|
22042
|
-
*
|
|
22354
|
+
* Also clears last_input_tokens because no provider has observed the new
|
|
22355
|
+
* context yet.
|
|
22043
22356
|
*
|
|
22044
22357
|
* Idempotent: a re-run supersedes the (now sole, already-marker) active row,
|
|
22045
22358
|
* inserts another marker at the next position. The post-condition (one active
|
|
@@ -22131,7 +22444,7 @@ export async function clearSessionContext(
|
|
|
22131
22444
|
|
|
22132
22445
|
await tx
|
|
22133
22446
|
.update(schema.sessions)
|
|
22134
|
-
.set({ lastInputTokens:
|
|
22447
|
+
.set({ lastInputTokens: null, updatedAt: new Date() })
|
|
22135
22448
|
.where(
|
|
22136
22449
|
and(
|
|
22137
22450
|
eq(schema.sessions.workspaceId, input.workspaceId),
|
|
@@ -28857,67 +29170,87 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
28857
29170
|
},
|
|
28858
29171
|
): Promise<void> {
|
|
28859
29172
|
const operation = normalizeWorkspaceMutationOperation(input.operation);
|
|
28860
|
-
const
|
|
28861
|
-
|
|
29173
|
+
const settleOnce = async (): Promise<SandboxWorkspaceMutationSettlementResult> =>
|
|
29174
|
+
await withRlsContext(
|
|
29175
|
+
db,
|
|
29176
|
+
{
|
|
29177
|
+
accountId: authorityInput.accountId,
|
|
29178
|
+
workspaceId: authorityInput.workspaceId,
|
|
29179
|
+
},
|
|
29180
|
+
async (scopedDb) =>
|
|
29181
|
+
await scopedDb.transaction(async (txRaw) => {
|
|
29182
|
+
const tx = txRaw as unknown as Database;
|
|
29183
|
+
// Admission and settlement must take the same canonical ownership
|
|
29184
|
+
// prefix before either touches the lease. The former order
|
|
29185
|
+
// (admission row -> authority -> lease) deadlocked a completed
|
|
29186
|
+
// parallel exec settlement against retained-process promotion
|
|
29187
|
+
// (admission row -> lease -> authority). PostgreSQL then rolled
|
|
29188
|
+
// back the losing admission settlement and permanently blocked
|
|
29189
|
+
// checkpoint capture.
|
|
29190
|
+
//
|
|
29191
|
+
// Preserve physical-settlement semantics when mutable authority
|
|
29192
|
+
// is stale: retain the typed fence, settle the exact immutable
|
|
29193
|
+
// admission below, commit, and only then reject its output.
|
|
29194
|
+
let authority: LockedWorkspaceMutationAuthority | null = null;
|
|
29195
|
+
let authorityFailure: SandboxWorkspaceMutationSettlementResult | null = null;
|
|
29196
|
+
try {
|
|
29197
|
+
authority = await lockWorkspaceMutationAuthorityTx(tx, authorityInput);
|
|
29198
|
+
} catch (error) {
|
|
29199
|
+
const failure = workspaceMutationAuthorityFailure(error);
|
|
29200
|
+
if (!failure) throw error;
|
|
29201
|
+
authorityFailure = failure;
|
|
29202
|
+
}
|
|
29203
|
+
|
|
29204
|
+
const actorKind = authorityInput.kind;
|
|
29205
|
+
const actorId =
|
|
29206
|
+
authorityInput.kind === "turn"
|
|
29207
|
+
? authorityInput.attemptId
|
|
29208
|
+
: authorityInput.kind === "direct"
|
|
29209
|
+
? authorityInput.requestId
|
|
29210
|
+
: authorityInput.processId;
|
|
29211
|
+
const admission = await selectExactAdmissionForUpdate(tx, {
|
|
29212
|
+
accountId: authorityInput.accountId,
|
|
29213
|
+
workspaceId: authorityInput.workspaceId,
|
|
29214
|
+
admissionId: input.admission.id,
|
|
29215
|
+
actorKind,
|
|
29216
|
+
actorId,
|
|
29217
|
+
sessionId: authorityInput.sessionId,
|
|
29218
|
+
admittedWorkspaceGeneration: input.admission.workspaceGeneration,
|
|
29219
|
+
operation,
|
|
29220
|
+
});
|
|
29221
|
+
if (
|
|
29222
|
+
!admission ||
|
|
29223
|
+
!admissionMatchesSnapshot(admission, input.admission) ||
|
|
29224
|
+
!admissionSnapshotMatchesAuthorityInput(input.admission, authorityInput) ||
|
|
29225
|
+
admission.provider_outcome === "retained" ||
|
|
29226
|
+
(admission.provider_outcome && admission.provider_outcome !== input.outcome)
|
|
29227
|
+
) {
|
|
29228
|
+
return {
|
|
29229
|
+
failure: "admission_fenced" as const,
|
|
29230
|
+
detail: "Workspace mutation settlement did not match its exact durable admission",
|
|
29231
|
+
};
|
|
29232
|
+
}
|
|
29233
|
+
if (!admission.settled_at) {
|
|
29234
|
+
await tx.execute(sql`
|
|
29235
|
+
update sandbox_workspace_mutation_admissions set
|
|
29236
|
+
provider_outcome = ${input.outcome}, settled_at = now()
|
|
29237
|
+
where id = ${input.admission.id} and settled_at is null
|
|
29238
|
+
`);
|
|
29239
|
+
}
|
|
29240
|
+
if (input.outcome === "rejected") return { failure: null };
|
|
29241
|
+
if (authorityFailure) return authorityFailure;
|
|
29242
|
+
if (!authority) {
|
|
29243
|
+
throw new Error("Workspace mutation settlement lost its locked authority");
|
|
29244
|
+
}
|
|
29245
|
+
return await verifyResolvedAdmissionAuthority(tx, authority, admission);
|
|
29246
|
+
}),
|
|
29247
|
+
);
|
|
29248
|
+
const settlement = await runIdempotentPersistenceTransaction(
|
|
28862
29249
|
{
|
|
28863
|
-
|
|
28864
|
-
|
|
29250
|
+
stage: "sandbox_workspace_mutation_settlement",
|
|
29251
|
+
maxAttempts: 5,
|
|
28865
29252
|
},
|
|
28866
|
-
|
|
28867
|
-
await scopedDb.transaction(async (txRaw) => {
|
|
28868
|
-
const tx = txRaw as unknown as Database;
|
|
28869
|
-
const actorKind = authorityInput.kind;
|
|
28870
|
-
const actorId =
|
|
28871
|
-
authorityInput.kind === "turn"
|
|
28872
|
-
? authorityInput.attemptId
|
|
28873
|
-
: authorityInput.kind === "direct"
|
|
28874
|
-
? authorityInput.requestId
|
|
28875
|
-
: authorityInput.processId;
|
|
28876
|
-
const admission = await selectExactAdmissionForUpdate(tx, {
|
|
28877
|
-
accountId: authorityInput.accountId,
|
|
28878
|
-
workspaceId: authorityInput.workspaceId,
|
|
28879
|
-
admissionId: input.admission.id,
|
|
28880
|
-
actorKind,
|
|
28881
|
-
actorId,
|
|
28882
|
-
sessionId: authorityInput.sessionId,
|
|
28883
|
-
admittedWorkspaceGeneration: input.admission.workspaceGeneration,
|
|
28884
|
-
operation,
|
|
28885
|
-
});
|
|
28886
|
-
if (
|
|
28887
|
-
!admission ||
|
|
28888
|
-
!admissionMatchesSnapshot(admission, input.admission) ||
|
|
28889
|
-
!admissionSnapshotMatchesAuthorityInput(input.admission, authorityInput) ||
|
|
28890
|
-
admission.provider_outcome === "retained" ||
|
|
28891
|
-
(admission.provider_outcome && admission.provider_outcome !== input.outcome)
|
|
28892
|
-
) {
|
|
28893
|
-
return {
|
|
28894
|
-
failure: "admission_fenced" as const,
|
|
28895
|
-
detail: "Workspace mutation settlement did not match its exact durable admission",
|
|
28896
|
-
};
|
|
28897
|
-
}
|
|
28898
|
-
if (!admission.settled_at) {
|
|
28899
|
-
await tx.execute(sql`
|
|
28900
|
-
update sandbox_workspace_mutation_admissions set
|
|
28901
|
-
provider_outcome = ${input.outcome}, settled_at = now()
|
|
28902
|
-
where id = ${input.admission.id} and settled_at is null
|
|
28903
|
-
`);
|
|
28904
|
-
}
|
|
28905
|
-
if (input.outcome === "rejected") return { failure: null };
|
|
28906
|
-
// The provider has already returned. Lock and settle its immutable
|
|
28907
|
-
// admission before consulting mutable turn/route/process authority, so
|
|
28908
|
-
// a stale-authority rejection cannot roll the physical settlement back
|
|
28909
|
-
// and strand archive capture. Only authority-fence errors are converted
|
|
28910
|
-
// to a post-commit rejection; database failures still abort normally.
|
|
28911
|
-
let authority: LockedWorkspaceMutationAuthority;
|
|
28912
|
-
try {
|
|
28913
|
-
authority = await lockWorkspaceMutationAuthorityTx(tx, authorityInput);
|
|
28914
|
-
} catch (error) {
|
|
28915
|
-
const failure = workspaceMutationAuthorityFailure(error);
|
|
28916
|
-
if (failure) return failure;
|
|
28917
|
-
throw error;
|
|
28918
|
-
}
|
|
28919
|
-
return await verifyResolvedAdmissionAuthority(tx, authority, admission);
|
|
28920
|
-
}),
|
|
29253
|
+
settleOnce,
|
|
28921
29254
|
);
|
|
28922
29255
|
if (settlement.failure !== null) {
|
|
28923
29256
|
throw new SandboxWorkspaceMutationFencedError(settlement.failure, settlement.detail);
|
|
@@ -29116,6 +29449,19 @@ export async function retainWorkspaceMutationProcess(
|
|
|
29116
29449
|
async (scopedDb) =>
|
|
29117
29450
|
await scopedDb.transaction(async (txRaw) => {
|
|
29118
29451
|
const tx = txRaw as unknown as Database;
|
|
29452
|
+
// Use the same ownership prefix as mutation admission and settlement.
|
|
29453
|
+
// A stale authority still cannot strand a provider process: remember
|
|
29454
|
+
// the fence, durably promote the exact process, then reject its output
|
|
29455
|
+
// after this transaction commits.
|
|
29456
|
+
let authority: LockedWorkspaceMutationAuthority | null = null;
|
|
29457
|
+
let authorityFailure: SandboxWorkspaceMutationSettlementResult | null = null;
|
|
29458
|
+
try {
|
|
29459
|
+
authority = await lockWorkspaceMutationAuthorityTx(tx, authorityInput);
|
|
29460
|
+
} catch (error) {
|
|
29461
|
+
const failure = workspaceMutationAuthorityFailure(error);
|
|
29462
|
+
if (!failure) throw error;
|
|
29463
|
+
authorityFailure = failure;
|
|
29464
|
+
}
|
|
29119
29465
|
const actorKind = authorityInput.kind;
|
|
29120
29466
|
const actorId =
|
|
29121
29467
|
authorityInput.kind === "turn" ? authorityInput.attemptId : authorityInput.requestId;
|
|
@@ -29276,16 +29622,13 @@ export async function retainWorkspaceMutationProcess(
|
|
|
29276
29622
|
}
|
|
29277
29623
|
|
|
29278
29624
|
// A yielded provider process is already a physical outcome. Persist its
|
|
29279
|
-
// exact route and non-TTL holder
|
|
29280
|
-
// a route/turn
|
|
29281
|
-
|
|
29282
|
-
|
|
29283
|
-
|
|
29284
|
-
|
|
29285
|
-
|
|
29286
|
-
const failure = workspaceMutationAuthorityFailure(error);
|
|
29287
|
-
if (failure) return { process: mapRetainedProcess(process!), failure };
|
|
29288
|
-
throw error;
|
|
29625
|
+
// exact route and non-TTL holder even when the earlier authority check
|
|
29626
|
+
// found a stale route/turn, then report staleness after commit.
|
|
29627
|
+
if (authorityFailure) {
|
|
29628
|
+
return { process: mapRetainedProcess(process!), failure: authorityFailure };
|
|
29629
|
+
}
|
|
29630
|
+
if (!authority) {
|
|
29631
|
+
throw new Error("Retained process promotion lost its locked authority");
|
|
29289
29632
|
}
|
|
29290
29633
|
const identity = await verifyResolvedAdmissionAuthority(tx, authority, admission);
|
|
29291
29634
|
return { process: mapRetainedProcess(process!), failure: identity };
|
|
@@ -34416,11 +34759,6 @@ export async function saveRunState(
|
|
|
34416
34759
|
expectedAttemptId: string;
|
|
34417
34760
|
serializedRunState: string;
|
|
34418
34761
|
pendingApprovals: unknown[];
|
|
34419
|
-
// The codex account freezing this state (the turn's resolved credential id),
|
|
34420
|
-
// or null on a non-codex turn. Stamped so a resume on a DIFFERENT codex
|
|
34421
|
-
// account can strip the blob's account-bound reasoning. Defaults null so
|
|
34422
|
-
// every legacy caller (and the non-codex path) is byte-identical.
|
|
34423
|
-
frozenCodexCredentialId?: string | null;
|
|
34424
34762
|
},
|
|
34425
34763
|
): Promise<boolean> {
|
|
34426
34764
|
return await withRlsContext(
|
|
@@ -34455,7 +34793,6 @@ export async function saveRunState(
|
|
|
34455
34793
|
stateVersion: Number(maxVersion) + 1,
|
|
34456
34794
|
serializedRunState: input.serializedRunState,
|
|
34457
34795
|
pendingApprovals: input.pendingApprovals,
|
|
34458
|
-
frozenCodexCredentialId: input.frozenCodexCredentialId ?? null,
|
|
34459
34796
|
});
|
|
34460
34797
|
return true;
|
|
34461
34798
|
});
|
|
@@ -37165,7 +37502,6 @@ export async function claimSessionWorkForAttempt(
|
|
|
37165
37502
|
turnId,
|
|
37166
37503
|
position: Number(position),
|
|
37167
37504
|
item: sanitizeModelPayload(delivered.historyItem),
|
|
37168
|
-
producerCodexCredentialId: null,
|
|
37169
37505
|
});
|
|
37170
37506
|
};
|
|
37171
37507
|
|
|
@@ -38213,7 +38549,6 @@ export async function claimSessionWorkForAttempt(
|
|
|
38213
38549
|
row.prompt,
|
|
38214
38550
|
Array.isArray(row.resources) ? (row.resources as ResourceRef[]) : [],
|
|
38215
38551
|
),
|
|
38216
|
-
producerCodexCredentialId: null,
|
|
38217
38552
|
});
|
|
38218
38553
|
const providerDelegatedTurn = isSessionRealtimeDelegationTurnMetadata(row.metadata);
|
|
38219
38554
|
// Cross-session updates are already projected through
|
|
@@ -39192,8 +39527,10 @@ export async function peekSessionWork(
|
|
|
39192
39527
|
attemptId: interruption.attemptId,
|
|
39193
39528
|
};
|
|
39194
39529
|
}
|
|
39195
|
-
|
|
39196
|
-
|
|
39530
|
+
// Physical quiescence finishes an already-accepted interruption; it is not
|
|
39531
|
+
// new session work. Reconcile the missing receipt even while control stays
|
|
39532
|
+
// paused, otherwise the pause itself can strand this session and every
|
|
39533
|
+
// ancestor behind a permanent `settlement: stopping` projection.
|
|
39197
39534
|
const awaitingQuiescence = await nextSessionAttemptAwaitingQuiescence(
|
|
39198
39535
|
scopedDb,
|
|
39199
39536
|
workspaceId,
|
|
@@ -39206,6 +39543,8 @@ export async function peekSessionWork(
|
|
|
39206
39543
|
};
|
|
39207
39544
|
}
|
|
39208
39545
|
|
|
39546
|
+
if (effectiveControl.state !== "active") return { kind: "idle" };
|
|
39547
|
+
|
|
39209
39548
|
const [capacityWait] = await scopedDb
|
|
39210
39549
|
.select()
|
|
39211
39550
|
.from(schema.codexCapacityWaiters)
|
|
@@ -39849,7 +40188,6 @@ export type ApplySessionTurnSettlementInput = {
|
|
|
39849
40188
|
runState?: {
|
|
39850
40189
|
serializedRunState: string;
|
|
39851
40190
|
pendingApprovals: unknown[];
|
|
39852
|
-
frozenCodexCredentialId?: string | null;
|
|
39853
40191
|
humanInputRequests?: Array<{
|
|
39854
40192
|
id: string;
|
|
39855
40193
|
toolCallId: string;
|
|
@@ -40078,7 +40416,6 @@ export async function applySessionTurnSettlement(
|
|
|
40078
40416
|
stateVersion: Number(maxVersion) + 1,
|
|
40079
40417
|
serializedRunState: input.runState.serializedRunState,
|
|
40080
40418
|
pendingApprovals: input.runState.pendingApprovals,
|
|
40081
|
-
frozenCodexCredentialId: input.runState.frozenCodexCredentialId ?? null,
|
|
40082
40419
|
});
|
|
40083
40420
|
if (humanInputRequests.length > 0) {
|
|
40084
40421
|
for (const request of humanInputRequests) {
|
|
@@ -41063,7 +41400,8 @@ export type RequestSessionTurnRecoveryInput = {
|
|
|
41063
41400
|
providerRecoveryCount?: number;
|
|
41064
41401
|
fromStatuses?: SessionTurnStatus[];
|
|
41065
41402
|
providerArtifactInvalidation?: {
|
|
41066
|
-
|
|
41403
|
+
historyItemIds: string[];
|
|
41404
|
+
runStateId?: string;
|
|
41067
41405
|
reason: "encrypted_content_rejected";
|
|
41068
41406
|
};
|
|
41069
41407
|
};
|
|
@@ -41166,47 +41504,36 @@ export async function requestSessionTurnRecovery(
|
|
|
41166
41504
|
}
|
|
41167
41505
|
let providerArtifactsInvalidated = 0;
|
|
41168
41506
|
if (input.providerArtifactInvalidation) {
|
|
41169
|
-
const
|
|
41170
|
-
|
|
41171
|
-
.
|
|
41172
|
-
|
|
41173
|
-
|
|
41174
|
-
|
|
41175
|
-
|
|
41176
|
-
|
|
41177
|
-
|
|
41178
|
-
|
|
41179
|
-
|
|
41180
|
-
|
|
41181
|
-
|
|
41182
|
-
|
|
41183
|
-
|
|
41184
|
-
|
|
41185
|
-
|
|
41186
|
-
|
|
41187
|
-
|
|
41188
|
-
|
|
41189
|
-
|
|
41190
|
-
|
|
41191
|
-
|
|
41192
|
-
|
|
41193
|
-
|
|
41194
|
-
|
|
41195
|
-
|
|
41196
|
-
|
|
41197
|
-
|
|
41198
|
-
|
|
41199
|
-
eq(schema.agentRunStates.turnId, input.turnId),
|
|
41200
|
-
eq(
|
|
41201
|
-
schema.agentRunStates.frozenCodexCredentialId,
|
|
41202
|
-
input.providerArtifactInvalidation.codexCredentialId,
|
|
41203
|
-
),
|
|
41204
|
-
isNull(schema.agentRunStates.providerArtifactInvalidatedAt),
|
|
41205
|
-
),
|
|
41206
|
-
)
|
|
41207
|
-
.orderBy(desc(schema.agentRunStates.stateVersion))
|
|
41208
|
-
.limit(1);
|
|
41209
|
-
const invalidatedRunState = latestRunState
|
|
41507
|
+
const historyItemIds = [...new Set(input.providerArtifactInvalidation.historyItemIds)];
|
|
41508
|
+
const invalidatedHistory =
|
|
41509
|
+
historyItemIds.length > 0
|
|
41510
|
+
? await tx
|
|
41511
|
+
.update(schema.sessionHistoryItems)
|
|
41512
|
+
.set({
|
|
41513
|
+
providerArtifactInvalidatedAt: now,
|
|
41514
|
+
providerArtifactInvalidationReason: input.providerArtifactInvalidation.reason,
|
|
41515
|
+
providerArtifactInvalidatedByAttemptId: input.attemptId,
|
|
41516
|
+
})
|
|
41517
|
+
.where(
|
|
41518
|
+
and(
|
|
41519
|
+
eq(schema.sessionHistoryItems.accountId, session.accountId),
|
|
41520
|
+
eq(schema.sessionHistoryItems.workspaceId, workspaceId),
|
|
41521
|
+
eq(schema.sessionHistoryItems.sessionId, input.sessionId),
|
|
41522
|
+
eq(schema.sessionHistoryItems.active, true),
|
|
41523
|
+
inArray(schema.sessionHistoryItems.id, historyItemIds),
|
|
41524
|
+
isNull(schema.sessionHistoryItems.providerArtifactInvalidatedAt),
|
|
41525
|
+
sql`${schema.sessionHistoryItems.item} ->> 'type' in ('reasoning', 'compaction')`,
|
|
41526
|
+
sql`(
|
|
41527
|
+
nullif(${schema.sessionHistoryItems.item} ->> 'encrypted_content', '') is not null
|
|
41528
|
+
or nullif(${schema.sessionHistoryItems.item} ->> 'encryptedContent', '') is not null
|
|
41529
|
+
or nullif(${schema.sessionHistoryItems.item} -> 'providerData' ->> 'encrypted_content', '') is not null
|
|
41530
|
+
or nullif(${schema.sessionHistoryItems.item} -> 'providerData' ->> 'encryptedContent', '') is not null
|
|
41531
|
+
)`,
|
|
41532
|
+
),
|
|
41533
|
+
)
|
|
41534
|
+
.returning({ id: schema.sessionHistoryItems.id })
|
|
41535
|
+
: [];
|
|
41536
|
+
const invalidatedRunState = input.providerArtifactInvalidation.runStateId
|
|
41210
41537
|
? await tx
|
|
41211
41538
|
.update(schema.agentRunStates)
|
|
41212
41539
|
.set({
|
|
@@ -41216,8 +41543,20 @@ export async function requestSessionTurnRecovery(
|
|
|
41216
41543
|
})
|
|
41217
41544
|
.where(
|
|
41218
41545
|
and(
|
|
41219
|
-
eq(schema.agentRunStates.
|
|
41546
|
+
eq(schema.agentRunStates.accountId, session.accountId),
|
|
41547
|
+
eq(schema.agentRunStates.workspaceId, workspaceId),
|
|
41548
|
+
eq(schema.agentRunStates.sessionId, input.sessionId),
|
|
41549
|
+
eq(schema.agentRunStates.turnId, input.turnId),
|
|
41550
|
+
eq(schema.agentRunStates.id, input.providerArtifactInvalidation.runStateId),
|
|
41220
41551
|
isNull(schema.agentRunStates.providerArtifactInvalidatedAt),
|
|
41552
|
+
sql`${schema.agentRunStates.stateVersion} = (
|
|
41553
|
+
select max(latest.state_version)
|
|
41554
|
+
from agent_run_states latest
|
|
41555
|
+
where latest.account_id = ${session.accountId}
|
|
41556
|
+
and latest.workspace_id = ${workspaceId}
|
|
41557
|
+
and latest.session_id = ${input.sessionId}
|
|
41558
|
+
and latest.turn_id = ${input.turnId}
|
|
41559
|
+
)`,
|
|
41221
41560
|
),
|
|
41222
41561
|
)
|
|
41223
41562
|
.returning({ id: schema.agentRunStates.id })
|
|
@@ -44835,3 +45174,4 @@ export {
|
|
|
44835
45174
|
} from "./connection-token-resolver";
|
|
44836
45175
|
|
|
44837
45176
|
export * from "./workspace-artifacts";
|
|
45177
|
+
export * from "./transcription-recordings";
|