@opengeni/db 1.3.0 → 1.4.2
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/capability-reconciliation.js +2 -2
- package/dist/{chunk-7QWWTVYH.js → chunk-5ROHLEN6.js} +29 -4
- package/dist/chunk-5ROHLEN6.js.map +1 -0
- package/dist/{chunk-AQMH2ZEH.js → chunk-A4J7EECR.js} +2 -2
- package/dist/{chunk-VQX54TK3.js → chunk-B5C5SBSI.js} +3 -3
- package/dist/{chunk-OI6YZXTU.js → chunk-DKFEUGM4.js} +2 -2
- package/dist/{chunk-HYBQABBJ.js → chunk-HUKBIZGH.js} +2 -2
- package/dist/{chunk-LRSIODCQ.js → chunk-IT3EYY2C.js} +6 -4
- package/dist/{chunk-LRSIODCQ.js.map → chunk-IT3EYY2C.js.map} +1 -1
- package/dist/{chunk-DSNEIW5E.js → chunk-JL6TBB76.js} +2 -1
- package/dist/chunk-JL6TBB76.js.map +1 -0
- package/dist/{chunk-OEFNTGR3.js → chunk-JWBEE4CL.js} +4 -4
- package/dist/{chunk-BRVBLQAL.js → chunk-K5MTNKJU.js} +4 -4
- package/dist/{chunk-6UKNJ43T.js → chunk-NZ2SP55O.js} +2 -2
- package/dist/{chunk-3XHC5KC5.js → chunk-OKCBARX6.js} +2 -2
- package/dist/{chunk-PNWWNEDM.js → chunk-OV5AUIBA.js} +2 -2
- package/dist/{chunk-BID3CNN3.js → chunk-TODVDGMO.js} +36 -18
- package/dist/{chunk-BID3CNN3.js.map → chunk-TODVDGMO.js.map} +1 -1
- package/dist/{chunk-6S5T6FSP.js → chunk-W75DRYG3.js} +5 -5
- package/dist/{chunk-7S3O33NM.js → chunk-Y72DP3JF.js} +2 -2
- package/dist/codemode-approvals.d.ts +7 -1
- package/dist/connect-attempts.js +3 -3
- package/dist/connection-metadata.d.ts +416 -0
- package/dist/credit-balances.d.ts +25 -4
- package/dist/database.d.ts +7 -1
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +51 -8
- package/dist/index.js +10826 -10129
- package/dist/index.js.map +1 -1
- package/dist/legacy-tool-action-reviews.d.ts +75 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/modal-native-live-origin.d.ts +75 -0
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-slack-bots.d.ts +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +21 -21
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/sandbox-interaction-idle.d.ts +22 -0
- package/dist/schema.js +1 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +5 -5
- package/dist/session-realtime-billing.d.ts +48 -0
- package/dist/session-realtime.d.ts +8 -2
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/voice-transcription-settlement.d.ts +36 -0
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0623_voice_transcription_attribution.sql +128 -0
- package/drizzle/0624_azure_live_realtime_model.sql +30 -0
- package/drizzle/0625_transcription_billing_refusal_codes.sql +29 -0
- package/drizzle/0632_modal_native_live_origin.sql +239 -0
- package/package.json +7 -7
- package/src/codemode-approvals.ts +52 -22
- package/src/codemode-operations.ts +198 -181
- package/src/connection-metadata.ts +166 -0
- package/src/credit-balances.ts +58 -17
- package/src/database.ts +9 -0
- package/src/index.ts +540 -412
- package/src/insights-unified.ts +2 -1
- package/src/legacy-tool-action-reviews.ts +242 -0
- package/src/modal-native-live-origin.ts +148 -0
- package/src/organization-slack-bots.ts +1 -1
- package/src/provision-roles.ts +12 -0
- package/src/runtime-posture.ts +32 -3
- package/src/sandbox-interaction-idle.ts +91 -0
- package/src/schema.ts +1 -0
- package/src/session-realtime-billing.ts +135 -0
- package/src/session-realtime-ledger.ts +9 -4
- package/src/session-realtime.ts +20 -2
- package/src/tool-action-reviews.ts +129 -18
- package/src/transcription-recordings.ts +3 -1
- package/src/voice-transcription-settlement.ts +94 -0
- package/dist/chunk-7QWWTVYH.js.map +0 -1
- package/dist/chunk-DSNEIW5E.js.map +0 -1
- /package/dist/{chunk-AQMH2ZEH.js.map → chunk-A4J7EECR.js.map} +0 -0
- /package/dist/{chunk-VQX54TK3.js.map → chunk-B5C5SBSI.js.map} +0 -0
- /package/dist/{chunk-OI6YZXTU.js.map → chunk-DKFEUGM4.js.map} +0 -0
- /package/dist/{chunk-HYBQABBJ.js.map → chunk-HUKBIZGH.js.map} +0 -0
- /package/dist/{chunk-OEFNTGR3.js.map → chunk-JWBEE4CL.js.map} +0 -0
- /package/dist/{chunk-BRVBLQAL.js.map → chunk-K5MTNKJU.js.map} +0 -0
- /package/dist/{chunk-6UKNJ43T.js.map → chunk-NZ2SP55O.js.map} +0 -0
- /package/dist/{chunk-3XHC5KC5.js.map → chunk-OKCBARX6.js.map} +0 -0
- /package/dist/{chunk-PNWWNEDM.js.map → chunk-OV5AUIBA.js.map} +0 -0
- /package/dist/{chunk-6S5T6FSP.js.map → chunk-W75DRYG3.js.map} +0 -0
- /package/dist/{chunk-7S3O33NM.js.map → chunk-Y72DP3JF.js.map} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
export * from "./organization-slack-bots";
|
|
2
|
+
export * from "./voice-transcription-settlement";
|
|
3
|
+
import {
|
|
4
|
+
connectionMetadataColumns,
|
|
5
|
+
connectionSubjectVisibility,
|
|
6
|
+
getConnectionMetadata,
|
|
7
|
+
listConnectionsMetadata,
|
|
8
|
+
mapConnectionMetadata,
|
|
9
|
+
withConnectionSubjectRls,
|
|
10
|
+
type ConnectionMetadataWithVerification,
|
|
11
|
+
} from "./connection-metadata";
|
|
12
|
+
export {
|
|
13
|
+
getConnectionMetadata,
|
|
14
|
+
listConnectionsMetadata,
|
|
15
|
+
type ConnectionMetadataWithVerification,
|
|
16
|
+
} from "./connection-metadata";
|
|
2
17
|
export { readCreditPromotionPolicy } from "./credit-promotion-policy";
|
|
3
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
getBillingBalance,
|
|
20
|
+
getSpendableCreditBalance,
|
|
21
|
+
planCreditDebit,
|
|
22
|
+
planPostUseCreditAllocations,
|
|
23
|
+
type CreditUsage,
|
|
24
|
+
} from "./credit-balances";
|
|
4
25
|
export {
|
|
5
26
|
getBillingBalance,
|
|
6
27
|
getSpendableCreditBalance,
|
|
7
28
|
spendableCreditMicros,
|
|
29
|
+
VOICE_CREDIT_USAGE,
|
|
30
|
+
type CreditUsage,
|
|
8
31
|
} from "./credit-balances";
|
|
9
32
|
import {
|
|
10
33
|
lockTurnAttemptWriteFenceTx,
|
|
@@ -37,6 +60,12 @@ import {
|
|
|
37
60
|
inboxOrigin,
|
|
38
61
|
} from "./inbox-execution-context";
|
|
39
62
|
import { parentOutboxAuthorityTx } from "./child-outbox-authority";
|
|
63
|
+
export {
|
|
64
|
+
lockLiveNativeOriginalOriginTx,
|
|
65
|
+
type ModalNativeLiveOriginScope,
|
|
66
|
+
type ModalNativeLiveOriginProjection,
|
|
67
|
+
type ModalNativeLiveOriginResult,
|
|
68
|
+
} from "./modal-native-live-origin";
|
|
40
69
|
export { SubscriptionAccountChangedError } from "./subscription-account-conflict";
|
|
41
70
|
import type { GoalAdmissionPausedReason } from "@opengeni/contracts";
|
|
42
71
|
import {
|
|
@@ -802,6 +831,7 @@ export * from "./session-queue-commands";
|
|
|
802
831
|
export * from "./session-realtime";
|
|
803
832
|
export * from "./session-realtime-context";
|
|
804
833
|
export * from "./session-realtime-ledger";
|
|
834
|
+
export * from "./session-realtime-billing";
|
|
805
835
|
export * from "./new-session-drafts";
|
|
806
836
|
export * from "./workspace-instruction-policies";
|
|
807
837
|
export * from "./company-profile";
|
|
@@ -5862,6 +5892,11 @@ export async function applyCreditDebitAfterUse(
|
|
|
5862
5892
|
idempotencyKey: string;
|
|
5863
5893
|
metadata?: Record<string, unknown>;
|
|
5864
5894
|
occurredAt?: Date;
|
|
5895
|
+
/**
|
|
5896
|
+
* What the charge paid for. When set, grants covering it pay first (oldest
|
|
5897
|
+
* first) and general credit takes the rest. Omitted: general credit only.
|
|
5898
|
+
*/
|
|
5899
|
+
usage?: CreditUsage;
|
|
5865
5900
|
},
|
|
5866
5901
|
): Promise<{ balance: BillingBalance; debitedMicros: number }> {
|
|
5867
5902
|
if (!Number.isSafeInteger(input.amountMicros) || input.amountMicros <= 0) {
|
|
@@ -5871,6 +5906,17 @@ export async function applyCreditDebitAfterUse(
|
|
|
5871
5906
|
db,
|
|
5872
5907
|
{ accountId: input.accountId, workspaceId: input.workspaceId ?? null },
|
|
5873
5908
|
async (scopedDb) => {
|
|
5909
|
+
let allocations: { grantEntryId: string; amountMicros: number }[] = [];
|
|
5910
|
+
if (input.usage) {
|
|
5911
|
+
// Same account lock as the model debit, so grant remainders are not
|
|
5912
|
+
// allocated twice by concurrent charges.
|
|
5913
|
+
await scopedDb.execute(sql`select pg_advisory_xact_lock(hashtext(${input.accountId}))`);
|
|
5914
|
+
allocations = planPostUseCreditAllocations(
|
|
5915
|
+
await getBillingBalance(scopedDb, input.accountId),
|
|
5916
|
+
input.amountMicros,
|
|
5917
|
+
input.usage,
|
|
5918
|
+
);
|
|
5919
|
+
}
|
|
5874
5920
|
const inserted = await scopedDb
|
|
5875
5921
|
.insert(schema.creditLedgerEntries)
|
|
5876
5922
|
.values({
|
|
@@ -5911,6 +5957,14 @@ export async function applyCreditDebitAfterUse(
|
|
|
5911
5957
|
"post-use credit debit idempotency key conflicts with a different charge",
|
|
5912
5958
|
);
|
|
5913
5959
|
}
|
|
5960
|
+
} else if (allocations.length) {
|
|
5961
|
+
await scopedDb.insert(schema.creditDebitAllocations).values(
|
|
5962
|
+
allocations.map((allocation) => ({
|
|
5963
|
+
...allocation,
|
|
5964
|
+
accountId: input.accountId,
|
|
5965
|
+
debitEntryId: inserted[0]!.id,
|
|
5966
|
+
})),
|
|
5967
|
+
);
|
|
5914
5968
|
}
|
|
5915
5969
|
return {
|
|
5916
5970
|
balance: await getBillingBalance(scopedDb, input.accountId),
|
|
@@ -6545,12 +6599,6 @@ export class PersonalGitHubRepositorySelectionUnavailableError extends Error {
|
|
|
6545
6599
|
}
|
|
6546
6600
|
}
|
|
6547
6601
|
|
|
6548
|
-
/** Server-owned verification facts; public schemas expose them read-only and nullable. */
|
|
6549
|
-
export type ConnectionMetadataWithVerification = ConnectionMetadata & {
|
|
6550
|
-
verifiedInstallAt: string | null;
|
|
6551
|
-
verifiedInstallVersion: number | null;
|
|
6552
|
-
};
|
|
6553
|
-
|
|
6554
6602
|
export type SlackBotPostOperation = {
|
|
6555
6603
|
id: string;
|
|
6556
6604
|
accountId: string;
|
|
@@ -10631,37 +10679,6 @@ export function mcpServerIdForCapability(
|
|
|
10631
10679
|
return `cap-${body}-${shortHash(capabilityId)}`;
|
|
10632
10680
|
}
|
|
10633
10681
|
|
|
10634
|
-
const connectionMetadataColumns = {
|
|
10635
|
-
id: schema.connections.id,
|
|
10636
|
-
authorityId: schema.connections.authorityId,
|
|
10637
|
-
authorityGeneration: schema.connections.authorityGeneration,
|
|
10638
|
-
accountId: schema.connections.accountId,
|
|
10639
|
-
workspaceId: schema.connections.workspaceId,
|
|
10640
|
-
subjectId: schema.connections.subjectId,
|
|
10641
|
-
providerDomain: schema.connections.providerDomain,
|
|
10642
|
-
kind: schema.connections.kind,
|
|
10643
|
-
status: schema.connections.status,
|
|
10644
|
-
grantedScopes: schema.connections.grantedScopes,
|
|
10645
|
-
expiresAt: schema.connections.expiresAt,
|
|
10646
|
-
lastRefreshAt: schema.connections.lastRefreshAt,
|
|
10647
|
-
lastUsedAt: schema.connections.lastUsedAt,
|
|
10648
|
-
lastError: schema.connections.lastError,
|
|
10649
|
-
version: schema.connections.version,
|
|
10650
|
-
verifiedInstallAt: schema.connections.verifiedInstallAt,
|
|
10651
|
-
verifiedInstallVersion: schema.connections.verifiedInstallVersion,
|
|
10652
|
-
metadata: schema.connections.metadata,
|
|
10653
|
-
createdBySubjectId: schema.connections.createdBySubjectId,
|
|
10654
|
-
updatedBySubjectId: schema.connections.updatedBySubjectId,
|
|
10655
|
-
createdAt: schema.connections.createdAt,
|
|
10656
|
-
updatedAt: schema.connections.updatedAt,
|
|
10657
|
-
};
|
|
10658
|
-
|
|
10659
|
-
function connectionSubjectVisibility(subjectId?: string | null): SQL {
|
|
10660
|
-
return subjectId
|
|
10661
|
-
? or(isNull(schema.connections.subjectId), eq(schema.connections.subjectId, subjectId))!
|
|
10662
|
-
: isNull(schema.connections.subjectId);
|
|
10663
|
-
}
|
|
10664
|
-
|
|
10665
10682
|
function connectionExactSubject(subjectId?: string | null): SQL {
|
|
10666
10683
|
return subjectId
|
|
10667
10684
|
? eq(schema.connections.subjectId, subjectId)
|
|
@@ -10683,17 +10700,6 @@ function personalSlackCanonicalConnectionOrder(): SQL[] {
|
|
|
10683
10700
|
];
|
|
10684
10701
|
}
|
|
10685
10702
|
|
|
10686
|
-
async function withConnectionSubjectRls<T>(
|
|
10687
|
-
db: Database,
|
|
10688
|
-
workspaceId: string,
|
|
10689
|
-
subjectId: string | null | undefined,
|
|
10690
|
-
fn: (db: Database) => Promise<T>,
|
|
10691
|
-
): Promise<T> {
|
|
10692
|
-
return subjectId
|
|
10693
|
-
? await withWorkspaceSubjectRls(db, workspaceId, subjectId, fn)
|
|
10694
|
-
: await withWorkspaceRls(db, workspaceId, fn);
|
|
10695
|
-
}
|
|
10696
|
-
|
|
10697
10703
|
const connectionAccessPolicyColumns = {
|
|
10698
10704
|
allowedModelIds: schema.connections.allowedModelIds,
|
|
10699
10705
|
allowedWorkspaceIds: schema.connections.allowedWorkspaceIds,
|
|
@@ -11414,51 +11420,6 @@ export async function persistProviderOAuthConnection(
|
|
|
11414
11420
|
);
|
|
11415
11421
|
}
|
|
11416
11422
|
|
|
11417
|
-
export async function listConnectionsMetadata(
|
|
11418
|
-
db: Database,
|
|
11419
|
-
workspaceId: string,
|
|
11420
|
-
subjectId?: string | null,
|
|
11421
|
-
): Promise<ConnectionMetadataWithVerification[]> {
|
|
11422
|
-
return await withConnectionSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
11423
|
-
const rows = await scopedDb
|
|
11424
|
-
.select(connectionMetadataColumns)
|
|
11425
|
-
.from(schema.connections)
|
|
11426
|
-
.where(
|
|
11427
|
-
and(
|
|
11428
|
-
eq(schema.connections.workspaceId, workspaceId),
|
|
11429
|
-
connectionSubjectVisibility(subjectId),
|
|
11430
|
-
),
|
|
11431
|
-
)
|
|
11432
|
-
// Legacy rows can share created_at. UUID DESC is the immutable stable
|
|
11433
|
-
// tie-breaker, so every caller that intentionally selects the first row
|
|
11434
|
-
// collapses duplicates in the same documented direction.
|
|
11435
|
-
.orderBy(desc(schema.connections.createdAt), desc(schema.connections.id));
|
|
11436
|
-
return rows.map(mapConnectionMetadata);
|
|
11437
|
-
});
|
|
11438
|
-
}
|
|
11439
|
-
|
|
11440
|
-
export async function getConnectionMetadata(
|
|
11441
|
-
db: Database,
|
|
11442
|
-
workspaceId: string,
|
|
11443
|
-
connectionId: string,
|
|
11444
|
-
subjectId?: string | null,
|
|
11445
|
-
): Promise<ConnectionMetadataWithVerification | null> {
|
|
11446
|
-
return await withConnectionSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
11447
|
-
const [row] = await scopedDb
|
|
11448
|
-
.select(connectionMetadataColumns)
|
|
11449
|
-
.from(schema.connections)
|
|
11450
|
-
.where(
|
|
11451
|
-
and(
|
|
11452
|
-
eq(schema.connections.workspaceId, workspaceId),
|
|
11453
|
-
eq(schema.connections.id, connectionId),
|
|
11454
|
-
connectionSubjectVisibility(subjectId),
|
|
11455
|
-
),
|
|
11456
|
-
)
|
|
11457
|
-
.limit(1);
|
|
11458
|
-
return row ? mapConnectionMetadata(row) : null;
|
|
11459
|
-
});
|
|
11460
|
-
}
|
|
11461
|
-
|
|
11462
11423
|
function personalGitHubRepositorySelectionStateFromDatabase(
|
|
11463
11424
|
value: unknown,
|
|
11464
11425
|
): PersonalGitHubRepositorySelectionState | null {
|
|
@@ -33380,6 +33341,53 @@ async function priorConnectorActionPreparation(
|
|
|
33380
33341
|
};
|
|
33381
33342
|
}
|
|
33382
33343
|
|
|
33344
|
+
/** Exact rows of one attempt whose canonical write prefix a ledger write takes. */
|
|
33345
|
+
type ConnectorActionLedgerPrefix = (scope: {
|
|
33346
|
+
sessionId: string;
|
|
33347
|
+
turnId: string;
|
|
33348
|
+
attemptId: string;
|
|
33349
|
+
}) => Promise<void>;
|
|
33350
|
+
|
|
33351
|
+
/**
|
|
33352
|
+
* One idempotent connector-ledger transaction. Before its first ledger write it
|
|
33353
|
+
* takes the canonical session event-write prefix (workspace KEY SHARE, session,
|
|
33354
|
+
* event cursor, exact turn, exact attempt): a ledger row's attempt FK otherwise
|
|
33355
|
+
* KEY SHAREs the attempt outside that order while parallel tool calls of the
|
|
33356
|
+
* same attempt append events and settle receipts. Reads before the prefix take
|
|
33357
|
+
* no row locks. The whole transaction retries on 40P01/40001; it never contains
|
|
33358
|
+
* a provider effect, so a retry cannot replay one.
|
|
33359
|
+
*/
|
|
33360
|
+
async function runConnectorActionLedgerTransaction<T>(
|
|
33361
|
+
db: Database,
|
|
33362
|
+
input: { accountId: string; workspaceId: string; stage: string; correlationId: string },
|
|
33363
|
+
fn: (tx: Database, lockPrefix: ConnectorActionLedgerPrefix) => Promise<T>,
|
|
33364
|
+
): Promise<T> {
|
|
33365
|
+
return await retryRlsPersistence(
|
|
33366
|
+
db,
|
|
33367
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
33368
|
+
{ stage: input.stage, correlationId: input.correlationId },
|
|
33369
|
+
async (scopedDb) =>
|
|
33370
|
+
await scopedDb.transaction(async (txRaw) => {
|
|
33371
|
+
const tx = txRaw as unknown as Database;
|
|
33372
|
+
let locked: string | null = null;
|
|
33373
|
+
return await fn(tx, async (scope) => {
|
|
33374
|
+
const key = `${scope.sessionId}:${scope.turnId}:${scope.attemptId}`;
|
|
33375
|
+
if (locked === key) return;
|
|
33376
|
+
if (locked) throw new Error("connector ledger transaction spans two attempts");
|
|
33377
|
+
await lockSessionEventWriteRows(tx, {
|
|
33378
|
+
workspaceId: input.workspaceId,
|
|
33379
|
+
controlLock: "none",
|
|
33380
|
+
sessionLock: "key_share",
|
|
33381
|
+
sessionIds: [scope.sessionId],
|
|
33382
|
+
turnIds: [scope.turnId],
|
|
33383
|
+
attemptIds: [scope.attemptId],
|
|
33384
|
+
});
|
|
33385
|
+
locked = key;
|
|
33386
|
+
});
|
|
33387
|
+
}),
|
|
33388
|
+
);
|
|
33389
|
+
}
|
|
33390
|
+
|
|
33383
33391
|
export async function prepareConnectorActionApproval(
|
|
33384
33392
|
db: Database,
|
|
33385
33393
|
identity: ConnectorActionAttemptIdentity,
|
|
@@ -33389,67 +33397,66 @@ export async function prepareConnectorActionApproval(
|
|
|
33389
33397
|
if (!normalized.connectionId) {
|
|
33390
33398
|
return { managed: false, decision: "unmanaged" };
|
|
33391
33399
|
}
|
|
33392
|
-
return await
|
|
33400
|
+
return await runConnectorActionLedgerTransaction(
|
|
33393
33401
|
db,
|
|
33394
|
-
{
|
|
33395
|
-
|
|
33396
|
-
|
|
33397
|
-
|
|
33398
|
-
|
|
33399
|
-
|
|
33400
|
-
|
|
33401
|
-
|
|
33402
|
-
|
|
33403
|
-
|
|
33404
|
-
|
|
33405
|
-
|
|
33406
|
-
|
|
33407
|
-
|
|
33408
|
-
|
|
33409
|
-
|
|
33410
|
-
|
|
33411
|
-
|
|
33412
|
-
|
|
33413
|
-
|
|
33414
|
-
|
|
33415
|
-
|
|
33416
|
-
|
|
33417
|
-
|
|
33418
|
-
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
return {
|
|
33423
|
-
managed: true,
|
|
33424
|
-
decision,
|
|
33425
|
-
actionFingerprint: durable.actionFingerprint,
|
|
33426
|
-
} as const;
|
|
33427
|
-
}
|
|
33428
|
-
const { row, inserted } = await insertConnectorActionRequest(tx as unknown as Database, {
|
|
33429
|
-
identity,
|
|
33430
|
-
invocation: durable,
|
|
33431
|
-
resolved,
|
|
33432
|
-
status: decision === "ask" ? "pending" : "blocked",
|
|
33433
|
-
});
|
|
33434
|
-
if (inserted) {
|
|
33435
|
-
await insertConnectorActionAudit(tx as unknown as Database, {
|
|
33436
|
-
row,
|
|
33437
|
-
action:
|
|
33438
|
-
decision === "ask"
|
|
33439
|
-
? "connector.action.approval_requested"
|
|
33440
|
-
: "connector.action.blocked",
|
|
33441
|
-
subjectId: identity.initiator.subjectId,
|
|
33442
|
-
extra: { outcome: decision },
|
|
33443
|
-
});
|
|
33444
|
-
}
|
|
33402
|
+
{
|
|
33403
|
+
accountId: identity.accountId,
|
|
33404
|
+
workspaceId: identity.workspaceId,
|
|
33405
|
+
stage: "connector_action_prepare",
|
|
33406
|
+
correlationId: normalized.approvalId,
|
|
33407
|
+
},
|
|
33408
|
+
async (tx, lockPrefix) => {
|
|
33409
|
+
const snapshot = await connectorActionAttemptSnapshot(tx, identity);
|
|
33410
|
+
const prior = await priorConnectorActionPreparation(tx, identity, normalized);
|
|
33411
|
+
if (prior) return prior;
|
|
33412
|
+
const resolved = resolveConnectorActionPolicy(snapshot, {
|
|
33413
|
+
connectionId: normalized.connectionId!,
|
|
33414
|
+
serverId: normalized.serverId,
|
|
33415
|
+
toolName: normalized.toolName,
|
|
33416
|
+
actionName: normalized.policyActionSelector,
|
|
33417
|
+
...(normalized.defaultDecision !== undefined
|
|
33418
|
+
? { defaultDecision: normalized.defaultDecision }
|
|
33419
|
+
: {}),
|
|
33420
|
+
});
|
|
33421
|
+
if (!resolved.managed) return { managed: false, decision: "unmanaged" } as const;
|
|
33422
|
+
const durable = durableConnectorActionInvocation(
|
|
33423
|
+
identity,
|
|
33424
|
+
{ ...normalized, connectionId: normalized.connectionId! },
|
|
33425
|
+
resolved,
|
|
33426
|
+
);
|
|
33427
|
+
const decision = connectorActionPolicyDecision(resolved);
|
|
33428
|
+
recordToolApproval(decision, resolved.source);
|
|
33429
|
+
if (decision === "allow") {
|
|
33445
33430
|
return {
|
|
33446
33431
|
managed: true,
|
|
33447
33432
|
decision,
|
|
33448
|
-
|
|
33449
|
-
requestId: row.id,
|
|
33450
|
-
actionFingerprint: row.actionFingerprint,
|
|
33433
|
+
actionFingerprint: durable.actionFingerprint,
|
|
33451
33434
|
} as const;
|
|
33452
|
-
}
|
|
33435
|
+
}
|
|
33436
|
+
await lockPrefix(identity);
|
|
33437
|
+
const { row, inserted } = await insertConnectorActionRequest(tx, {
|
|
33438
|
+
identity,
|
|
33439
|
+
invocation: durable,
|
|
33440
|
+
resolved,
|
|
33441
|
+
status: decision === "ask" ? "pending" : "blocked",
|
|
33442
|
+
});
|
|
33443
|
+
if (inserted) {
|
|
33444
|
+
await insertConnectorActionAudit(tx, {
|
|
33445
|
+
row,
|
|
33446
|
+
action:
|
|
33447
|
+
decision === "ask" ? "connector.action.approval_requested" : "connector.action.blocked",
|
|
33448
|
+
subjectId: identity.initiator.subjectId,
|
|
33449
|
+
extra: { outcome: decision },
|
|
33450
|
+
});
|
|
33451
|
+
}
|
|
33452
|
+
return {
|
|
33453
|
+
managed: true,
|
|
33454
|
+
decision,
|
|
33455
|
+
approvalStatus: row.status,
|
|
33456
|
+
requestId: row.id,
|
|
33457
|
+
actionFingerprint: row.actionFingerprint,
|
|
33458
|
+
} as const;
|
|
33459
|
+
},
|
|
33453
33460
|
);
|
|
33454
33461
|
}
|
|
33455
33462
|
|
|
@@ -33503,13 +33510,28 @@ export async function beginConnectorActionExecution(
|
|
|
33503
33510
|
if (!normalized.connectionId) {
|
|
33504
33511
|
return { allowed: true, managed: false };
|
|
33505
33512
|
}
|
|
33506
|
-
|
|
33513
|
+
const resolveFrom = (snapshot: ConnectorActionPolicySnapshotEntry[]) =>
|
|
33514
|
+
resolveConnectorActionPolicy(snapshot, {
|
|
33515
|
+
connectionId: normalized.connectionId!,
|
|
33516
|
+
serverId: normalized.serverId,
|
|
33517
|
+
toolName: normalized.toolName,
|
|
33518
|
+
actionName: normalized.policyActionSelector,
|
|
33519
|
+
...(normalized.defaultDecision !== undefined
|
|
33520
|
+
? { defaultDecision: normalized.defaultDecision }
|
|
33521
|
+
: {}),
|
|
33522
|
+
});
|
|
33523
|
+
return await runConnectorActionLedgerTransaction(
|
|
33507
33524
|
db,
|
|
33508
|
-
{
|
|
33509
|
-
|
|
33510
|
-
|
|
33511
|
-
|
|
33512
|
-
|
|
33525
|
+
{
|
|
33526
|
+
accountId: identity.accountId,
|
|
33527
|
+
workspaceId: identity.workspaceId,
|
|
33528
|
+
stage: "connector_action_begin",
|
|
33529
|
+
correlationId: normalized.approvalId,
|
|
33530
|
+
},
|
|
33531
|
+
async (tx, lockPrefix) => {
|
|
33532
|
+
const snapshot = await connectorActionAttemptSnapshot(tx, identity);
|
|
33533
|
+
const existingRequest = () =>
|
|
33534
|
+
tx
|
|
33513
33535
|
.select()
|
|
33514
33536
|
.from(schema.connectorActionRequests)
|
|
33515
33537
|
.where(
|
|
@@ -33519,73 +33541,89 @@ export async function beginConnectorActionExecution(
|
|
|
33519
33541
|
eq(schema.connectorActionRequests.turnId, identity.turnId),
|
|
33520
33542
|
eq(schema.connectorActionRequests.approvalId, normalized.approvalId),
|
|
33521
33543
|
),
|
|
33522
|
-
)
|
|
33523
|
-
.for("update")
|
|
33524
|
-
.limit(1);
|
|
33525
|
-
if (existing && !connectorActionRequestMatchesLogicalCall(existing, identity, normalized)) {
|
|
33526
|
-
throw new Error("connector action approval id conflicts with different immutable inputs");
|
|
33527
|
-
}
|
|
33528
|
-
let row = existing;
|
|
33529
|
-
let inserted = false;
|
|
33530
|
-
if (!row) {
|
|
33531
|
-
const resolved = resolveConnectorActionPolicy(snapshot, {
|
|
33532
|
-
connectionId: normalized.connectionId!,
|
|
33533
|
-
serverId: normalized.serverId,
|
|
33534
|
-
toolName: normalized.toolName,
|
|
33535
|
-
actionName: normalized.policyActionSelector,
|
|
33536
|
-
...(normalized.defaultDecision !== undefined
|
|
33537
|
-
? { defaultDecision: normalized.defaultDecision }
|
|
33538
|
-
: {}),
|
|
33539
|
-
});
|
|
33540
|
-
if (!resolved.managed) return { allowed: true, managed: false } as const;
|
|
33541
|
-
const durable = durableConnectorActionInvocation(
|
|
33542
|
-
identity,
|
|
33543
|
-
{ ...normalized, connectionId: normalized.connectionId! },
|
|
33544
|
-
resolved,
|
|
33545
33544
|
);
|
|
33546
|
-
|
|
33547
|
-
|
|
33548
|
-
|
|
33549
|
-
|
|
33550
|
-
|
|
33551
|
-
|
|
33552
|
-
|
|
33553
|
-
|
|
33554
|
-
|
|
33555
|
-
|
|
33556
|
-
|
|
33557
|
-
|
|
33558
|
-
|
|
33559
|
-
decision === "ask"
|
|
33560
|
-
? "connector.action.approval_requested"
|
|
33561
|
-
: decision === "block"
|
|
33562
|
-
? "connector.action.blocked"
|
|
33563
|
-
: "connector.action.execution_started",
|
|
33564
|
-
subjectId: identity.initiator.subjectId,
|
|
33565
|
-
extra: { outcome: decision === "allow" ? "started" : decision },
|
|
33566
|
-
});
|
|
33567
|
-
}
|
|
33545
|
+
const [unlocked] = await existingRequest().limit(1);
|
|
33546
|
+
if (!unlocked) {
|
|
33547
|
+
const resolved = resolveFrom(snapshot);
|
|
33548
|
+
if (!resolved.managed) return { allowed: true, managed: false } as const;
|
|
33549
|
+
// A recommended Allow with no explicit rule is ordinary tool use, not
|
|
33550
|
+
// a reviewed action: no ledger row, audit pair, or row lock. Every Ask,
|
|
33551
|
+
// Block, explicit rule, and connector write keeps its execute-once record.
|
|
33552
|
+
if (
|
|
33553
|
+
resolved.source === "default" &&
|
|
33554
|
+
connectorActionPolicyDecision(resolved) === "allow" &&
|
|
33555
|
+
normalized.approvalMode === "connector"
|
|
33556
|
+
) {
|
|
33557
|
+
return { allowed: true, managed: false } as const;
|
|
33568
33558
|
}
|
|
33569
|
-
|
|
33570
|
-
|
|
33571
|
-
|
|
33572
|
-
|
|
33573
|
-
|
|
33574
|
-
|
|
33575
|
-
|
|
33576
|
-
|
|
33577
|
-
|
|
33578
|
-
|
|
33579
|
-
|
|
33580
|
-
|
|
33581
|
-
|
|
33582
|
-
|
|
33583
|
-
|
|
33559
|
+
}
|
|
33560
|
+
await lockPrefix(identity);
|
|
33561
|
+
const [existing] = await existingRequest().for("update").limit(1);
|
|
33562
|
+
if (existing && !connectorActionRequestMatchesLogicalCall(existing, identity, normalized)) {
|
|
33563
|
+
throw new Error("connector action approval id conflicts with different immutable inputs");
|
|
33564
|
+
}
|
|
33565
|
+
let row = existing;
|
|
33566
|
+
let inserted = false;
|
|
33567
|
+
if (!row) {
|
|
33568
|
+
const resolved = resolveFrom(snapshot);
|
|
33569
|
+
if (!resolved.managed) return { allowed: true, managed: false } as const;
|
|
33570
|
+
const decision = connectorActionPolicyDecision(resolved);
|
|
33571
|
+
const durable = durableConnectorActionInvocation(
|
|
33572
|
+
identity,
|
|
33573
|
+
{ ...normalized, connectionId: normalized.connectionId! },
|
|
33574
|
+
resolved,
|
|
33575
|
+
);
|
|
33576
|
+
const created = await insertConnectorActionRequest(tx, {
|
|
33577
|
+
identity,
|
|
33578
|
+
invocation: durable,
|
|
33579
|
+
resolved,
|
|
33580
|
+
status: decision === "ask" ? "pending" : decision === "block" ? "blocked" : "executing",
|
|
33581
|
+
});
|
|
33582
|
+
row = created.row;
|
|
33583
|
+
inserted = created.inserted;
|
|
33584
|
+
if (inserted) {
|
|
33585
|
+
await insertConnectorActionAudit(tx, {
|
|
33584
33586
|
row,
|
|
33585
|
-
action:
|
|
33587
|
+
action:
|
|
33588
|
+
decision === "ask"
|
|
33589
|
+
? "connector.action.approval_requested"
|
|
33590
|
+
: decision === "block"
|
|
33591
|
+
? "connector.action.blocked"
|
|
33592
|
+
: "connector.action.execution_started",
|
|
33586
33593
|
subjectId: identity.initiator.subjectId,
|
|
33587
|
-
extra: { outcome: "started" },
|
|
33594
|
+
extra: { outcome: decision === "allow" ? "started" : decision },
|
|
33588
33595
|
});
|
|
33596
|
+
}
|
|
33597
|
+
}
|
|
33598
|
+
if (row.status === "approved") {
|
|
33599
|
+
const [executing] = await tx
|
|
33600
|
+
.update(schema.connectorActionRequests)
|
|
33601
|
+
.set({
|
|
33602
|
+
status: "executing",
|
|
33603
|
+
executionAttemptId: identity.attemptId,
|
|
33604
|
+
executionAttemptGeneration: identity.executionGeneration,
|
|
33605
|
+
executionStartedAt: new Date(),
|
|
33606
|
+
updatedAt: new Date(),
|
|
33607
|
+
})
|
|
33608
|
+
.where(eq(schema.connectorActionRequests.id, row.id))
|
|
33609
|
+
.returning();
|
|
33610
|
+
if (!executing) throw new Error("Approved connector action request disappeared");
|
|
33611
|
+
row = executing;
|
|
33612
|
+
await insertConnectorActionAudit(tx, {
|
|
33613
|
+
row,
|
|
33614
|
+
action: "connector.action.execution_started",
|
|
33615
|
+
subjectId: identity.initiator.subjectId,
|
|
33616
|
+
extra: { outcome: "started" },
|
|
33617
|
+
});
|
|
33618
|
+
return {
|
|
33619
|
+
allowed: true,
|
|
33620
|
+
managed: true,
|
|
33621
|
+
requestId: row.id,
|
|
33622
|
+
actionFingerprint: row.actionFingerprint,
|
|
33623
|
+
} as const;
|
|
33624
|
+
}
|
|
33625
|
+
if (row.status === "executing") {
|
|
33626
|
+
if (inserted) {
|
|
33589
33627
|
return {
|
|
33590
33628
|
allowed: true,
|
|
33591
33629
|
managed: true,
|
|
@@ -33593,58 +33631,49 @@ export async function beginConnectorActionExecution(
|
|
|
33593
33631
|
actionFingerprint: row.actionFingerprint,
|
|
33594
33632
|
} as const;
|
|
33595
33633
|
}
|
|
33596
|
-
|
|
33597
|
-
|
|
33598
|
-
|
|
33599
|
-
|
|
33600
|
-
|
|
33601
|
-
|
|
33602
|
-
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
.where(eq(schema.connectorActionRequests.id, row.id))
|
|
33614
|
-
.returning();
|
|
33615
|
-
if (!uncertain) throw new Error("Executing connector action request disappeared");
|
|
33616
|
-
await insertConnectorActionAudit(tx as unknown as Database, {
|
|
33617
|
-
row: uncertain,
|
|
33618
|
-
action: "connector.action.execution_uncertain",
|
|
33619
|
-
subjectId: identity.initiator.subjectId,
|
|
33620
|
-
extra: { outcome: "retry_denied" },
|
|
33621
|
-
});
|
|
33622
|
-
return {
|
|
33623
|
-
allowed: false,
|
|
33624
|
-
managed: true,
|
|
33625
|
-
reason: "uncertain_retry",
|
|
33626
|
-
requestId: uncertain.id,
|
|
33627
|
-
actionFingerprint: uncertain.actionFingerprint,
|
|
33628
|
-
} as const;
|
|
33629
|
-
}
|
|
33630
|
-
const reason =
|
|
33631
|
-
row.status === "pending"
|
|
33632
|
-
? "approval_required"
|
|
33633
|
-
: row.status === "rejected"
|
|
33634
|
-
? "rejected"
|
|
33635
|
-
: row.status === "blocked"
|
|
33636
|
-
? "blocked"
|
|
33637
|
-
: row.status === "failed"
|
|
33638
|
-
? "not_executed"
|
|
33639
|
-
: "already_executed";
|
|
33634
|
+
const [uncertain] = await tx
|
|
33635
|
+
.update(schema.connectorActionRequests)
|
|
33636
|
+
.set({
|
|
33637
|
+
status: "uncertain",
|
|
33638
|
+
outcome: "retry_after_execution_started",
|
|
33639
|
+
executionFinishedAt: new Date(),
|
|
33640
|
+
updatedAt: new Date(),
|
|
33641
|
+
})
|
|
33642
|
+
.where(eq(schema.connectorActionRequests.id, row.id))
|
|
33643
|
+
.returning();
|
|
33644
|
+
if (!uncertain) throw new Error("Executing connector action request disappeared");
|
|
33645
|
+
await insertConnectorActionAudit(tx, {
|
|
33646
|
+
row: uncertain,
|
|
33647
|
+
action: "connector.action.execution_uncertain",
|
|
33648
|
+
subjectId: identity.initiator.subjectId,
|
|
33649
|
+
extra: { outcome: "retry_denied" },
|
|
33650
|
+
});
|
|
33640
33651
|
return {
|
|
33641
33652
|
allowed: false,
|
|
33642
33653
|
managed: true,
|
|
33643
|
-
reason,
|
|
33644
|
-
requestId:
|
|
33645
|
-
actionFingerprint:
|
|
33654
|
+
reason: "uncertain_retry",
|
|
33655
|
+
requestId: uncertain.id,
|
|
33656
|
+
actionFingerprint: uncertain.actionFingerprint,
|
|
33646
33657
|
} as const;
|
|
33647
|
-
}
|
|
33658
|
+
}
|
|
33659
|
+
const reason =
|
|
33660
|
+
row.status === "pending"
|
|
33661
|
+
? "approval_required"
|
|
33662
|
+
: row.status === "rejected"
|
|
33663
|
+
? "rejected"
|
|
33664
|
+
: row.status === "blocked"
|
|
33665
|
+
? "blocked"
|
|
33666
|
+
: row.status === "failed"
|
|
33667
|
+
? "not_executed"
|
|
33668
|
+
: "already_executed";
|
|
33669
|
+
return {
|
|
33670
|
+
allowed: false,
|
|
33671
|
+
managed: true,
|
|
33672
|
+
reason,
|
|
33673
|
+
requestId: row.id,
|
|
33674
|
+
actionFingerprint: row.actionFingerprint,
|
|
33675
|
+
} as const;
|
|
33676
|
+
},
|
|
33648
33677
|
);
|
|
33649
33678
|
}
|
|
33650
33679
|
|
|
@@ -33658,12 +33687,17 @@ export async function completeConnectorActionExecution(
|
|
|
33658
33687
|
outcome: "completed" | "uncertain" | "not_executed";
|
|
33659
33688
|
},
|
|
33660
33689
|
): Promise<void> {
|
|
33661
|
-
await
|
|
33690
|
+
await runConnectorActionLedgerTransaction(
|
|
33662
33691
|
db,
|
|
33663
|
-
{
|
|
33664
|
-
|
|
33665
|
-
|
|
33666
|
-
|
|
33692
|
+
{
|
|
33693
|
+
accountId: input.accountId,
|
|
33694
|
+
workspaceId: input.workspaceId,
|
|
33695
|
+
stage: "connector_action_complete",
|
|
33696
|
+
correlationId: input.requestId,
|
|
33697
|
+
},
|
|
33698
|
+
async (tx, lockPrefix) => {
|
|
33699
|
+
const request = () =>
|
|
33700
|
+
tx
|
|
33667
33701
|
.select()
|
|
33668
33702
|
.from(schema.connectorActionRequests)
|
|
33669
33703
|
.where(
|
|
@@ -33672,43 +33706,51 @@ export async function completeConnectorActionExecution(
|
|
|
33672
33706
|
eq(schema.connectorActionRequests.id, input.requestId),
|
|
33673
33707
|
eq(schema.connectorActionRequests.executionAttemptId, input.attemptId),
|
|
33674
33708
|
),
|
|
33675
|
-
)
|
|
33676
|
-
|
|
33677
|
-
|
|
33678
|
-
|
|
33679
|
-
|
|
33680
|
-
|
|
33681
|
-
|
|
33682
|
-
|
|
33683
|
-
|
|
33684
|
-
|
|
33685
|
-
|
|
33686
|
-
|
|
33687
|
-
|
|
33688
|
-
|
|
33689
|
-
|
|
33690
|
-
|
|
33691
|
-
|
|
33692
|
-
|
|
33693
|
-
|
|
33694
|
-
|
|
33695
|
-
|
|
33696
|
-
|
|
33697
|
-
|
|
33698
|
-
|
|
33699
|
-
|
|
33700
|
-
|
|
33701
|
-
|
|
33702
|
-
|
|
33703
|
-
|
|
33704
|
-
|
|
33705
|
-
|
|
33706
|
-
|
|
33707
|
-
|
|
33708
|
-
|
|
33709
|
-
|
|
33710
|
-
|
|
33711
|
-
|
|
33709
|
+
);
|
|
33710
|
+
const [located] = await request().limit(1);
|
|
33711
|
+
if (!located) throw new Error("Connector action request not found for completion");
|
|
33712
|
+
// Settlement is not admission: it takes the canonical prefix but never the
|
|
33713
|
+
// Pause/Steer write fence, so an interrupted attempt still settles.
|
|
33714
|
+
await lockPrefix({
|
|
33715
|
+
sessionId: located.sessionId,
|
|
33716
|
+
turnId: located.turnId,
|
|
33717
|
+
attemptId: input.attemptId,
|
|
33718
|
+
});
|
|
33719
|
+
const [existing] = await request().for("update").limit(1);
|
|
33720
|
+
if (!existing) throw new Error("Connector action request not found for completion");
|
|
33721
|
+
// A not-executed completion is a terminal failure whose provider
|
|
33722
|
+
// request never happened; it keeps the existing status vocabulary.
|
|
33723
|
+
// Reaching the terminal status is idempotent even when a concurrent
|
|
33724
|
+
// begin already settled the row with a different uncertain outcome
|
|
33725
|
+
// (retry_after_execution_started).
|
|
33726
|
+
const terminalStatus = input.outcome === "not_executed" ? "failed" : input.outcome;
|
|
33727
|
+
if (existing.status === terminalStatus) return;
|
|
33728
|
+
if (existing.status !== "executing") {
|
|
33729
|
+
throw new Error(`Connector action request cannot complete from ${existing.status}`);
|
|
33730
|
+
}
|
|
33731
|
+
const [row] = await tx
|
|
33732
|
+
.update(schema.connectorActionRequests)
|
|
33733
|
+
.set({
|
|
33734
|
+
status: terminalStatus,
|
|
33735
|
+
outcome: input.outcome,
|
|
33736
|
+
executionFinishedAt: new Date(),
|
|
33737
|
+
updatedAt: new Date(),
|
|
33738
|
+
})
|
|
33739
|
+
.where(eq(schema.connectorActionRequests.id, existing.id))
|
|
33740
|
+
.returning();
|
|
33741
|
+
if (!row) throw new Error("Connector action request disappeared during completion");
|
|
33742
|
+
await insertConnectorActionAudit(tx, {
|
|
33743
|
+
row,
|
|
33744
|
+
action:
|
|
33745
|
+
input.outcome === "completed"
|
|
33746
|
+
? "connector.action.execution_completed"
|
|
33747
|
+
: input.outcome === "not_executed"
|
|
33748
|
+
? "connector.action.execution_not_executed"
|
|
33749
|
+
: "connector.action.execution_uncertain",
|
|
33750
|
+
subjectId: row.initiatorSubjectId,
|
|
33751
|
+
extra: { outcome: input.outcome },
|
|
33752
|
+
});
|
|
33753
|
+
},
|
|
33712
33754
|
);
|
|
33713
33755
|
}
|
|
33714
33756
|
|
|
@@ -43174,9 +43216,12 @@ export async function appendSessionHistoryItems(
|
|
|
43174
43216
|
if (input.items.length === 0) {
|
|
43175
43217
|
return true;
|
|
43176
43218
|
}
|
|
43177
|
-
|
|
43219
|
+
// One idempotent transaction: a deadlock/serialization victim rolled back
|
|
43220
|
+
// entirely, so it retries as a unit; it contains no external effect.
|
|
43221
|
+
return await retryRlsPersistence(
|
|
43178
43222
|
db,
|
|
43179
43223
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
43224
|
+
{ stage: "session_history_append", correlationId: input.expectedAttemptId },
|
|
43180
43225
|
async (scopedDb) => {
|
|
43181
43226
|
return await scopedDb.transaction(async (tx) => {
|
|
43182
43227
|
const allowed = await lockTurnAttemptWriteFenceTx(tx, {
|
|
@@ -43490,9 +43535,12 @@ export async function recordPendingSessionToolCallResult(
|
|
|
43490
43535
|
};
|
|
43491
43536
|
},
|
|
43492
43537
|
): Promise<{ accepted: boolean; recorded: boolean }> {
|
|
43493
|
-
|
|
43538
|
+
// One idempotent transaction: a deadlock/serialization victim rolled back
|
|
43539
|
+
// entirely, so it retries as a unit; it contains no external effect.
|
|
43540
|
+
return await retryRlsPersistence(
|
|
43494
43541
|
db,
|
|
43495
43542
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
43543
|
+
{ stage: "session_pending_tool_call_result", correlationId: input.callId },
|
|
43496
43544
|
async (scopedDb) =>
|
|
43497
43545
|
await scopedDb.transaction(async (tx) => {
|
|
43498
43546
|
const fence = await lockTurnAttemptWriteFenceTx(tx, {
|
|
@@ -55389,7 +55437,13 @@ export async function confirmDrainCold(
|
|
|
55389
55437
|
/** The idle window named in contained commands' agent notice. */
|
|
55390
55438
|
idleCommandContainmentMs?: number | undefined;
|
|
55391
55439
|
},
|
|
55392
|
-
): Promise<{
|
|
55440
|
+
): Promise<{
|
|
55441
|
+
wentCold: boolean;
|
|
55442
|
+
/** The missing provider was an unpublished warming replacement: its loss
|
|
55443
|
+
* recorded no lost workspace (see `unpublishedWarmingLoss`). */
|
|
55444
|
+
unpublishedProviderLost?: boolean;
|
|
55445
|
+
backgroundCommandEvents?: SessionEvent[];
|
|
55446
|
+
}> {
|
|
55393
55447
|
const backgroundCommandEvents: SessionEvent[] = [];
|
|
55394
55448
|
// Settling a command appends its terminal session event and agent input, so
|
|
55395
55449
|
// the cold commit runs inside the session activity gate.
|
|
@@ -55512,8 +55566,23 @@ export async function confirmDrainCold(
|
|
|
55512
55566
|
const hasArchive = current.archive.status !== "none";
|
|
55513
55567
|
const archiveComplete = hasCompleteWorkspaceArchive(row);
|
|
55514
55568
|
const now = new Date().toISOString();
|
|
55569
|
+
// A warming replacement whose creator died (worker shutdown/crash)
|
|
55570
|
+
// before commitWarmingToWarm was never published: no holder attached
|
|
55571
|
+
// to it and no workspace mutation can be admitted on a non-warm lease.
|
|
55572
|
+
// The reaper's warming-death drain (c2) is the only way such a box
|
|
55573
|
+
// reaches here. Its disappearance therefore loses nothing beyond what
|
|
55574
|
+
// the pre-warming cold row already recorded; mirror failWarmingToCold
|
|
55575
|
+
// instead of minting a lost-workspace verdict that would block every
|
|
55576
|
+
// later turn of the session. A published box keeps the loss contract.
|
|
55577
|
+
const unpublishedWarmingLoss =
|
|
55578
|
+
input.providerMissingBeforeCapture === true &&
|
|
55579
|
+
current.provider.status === "creating" &&
|
|
55580
|
+
current.provider.instanceId !== null &&
|
|
55581
|
+
current.provider.instanceId === row.instance_id;
|
|
55582
|
+
const workspaceLost =
|
|
55583
|
+
input.providerMissingBeforeCapture === true && !unpublishedWarmingLoss;
|
|
55515
55584
|
const lateArchiveCapture =
|
|
55516
|
-
|
|
55585
|
+
workspaceLost &&
|
|
55517
55586
|
row.archive_capture_id !== null &&
|
|
55518
55587
|
row.archive_capture_provider_request_id !== null &&
|
|
55519
55588
|
row.archive_capture_generation !== null &&
|
|
@@ -55538,18 +55607,27 @@ export async function confirmDrainCold(
|
|
|
55538
55607
|
? "pending"
|
|
55539
55608
|
: hasArchive
|
|
55540
55609
|
? "degraded"
|
|
55541
|
-
:
|
|
55610
|
+
: workspaceLost || freshPending
|
|
55542
55611
|
? "unrecoverable"
|
|
55543
55612
|
: "not_required";
|
|
55544
55613
|
const recovery: SandboxRecoveryState = {
|
|
55545
|
-
provider:
|
|
55546
|
-
|
|
55547
|
-
|
|
55548
|
-
|
|
55549
|
-
|
|
55550
|
-
|
|
55551
|
-
|
|
55552
|
-
|
|
55614
|
+
provider: unpublishedWarmingLoss
|
|
55615
|
+
? {
|
|
55616
|
+
// Same shape failWarmingToCold records for a failed replacement:
|
|
55617
|
+
// proves nothing about the workspace, never a loss record.
|
|
55618
|
+
status: "not_created",
|
|
55619
|
+
instanceId: row.instance_id,
|
|
55620
|
+
observedAt: now,
|
|
55621
|
+
diagnostic: REPLACEMENT_FAILED_DIAGNOSTIC,
|
|
55622
|
+
}
|
|
55623
|
+
: {
|
|
55624
|
+
status: workspaceLost ? "missing" : "not_created",
|
|
55625
|
+
instanceId: workspaceLost ? row.instance_id : null,
|
|
55626
|
+
observedAt: now,
|
|
55627
|
+
...(workspaceLost
|
|
55628
|
+
? { diagnostic: "provider_not_found_before_workspace_capture" }
|
|
55629
|
+
: {}),
|
|
55630
|
+
},
|
|
55553
55631
|
archive: current.archive,
|
|
55554
55632
|
restore: {
|
|
55555
55633
|
status: restoreStatus,
|
|
@@ -55589,9 +55667,7 @@ export async function confirmDrainCold(
|
|
|
55589
55667
|
archiveOnlyResumeState(
|
|
55590
55668
|
row,
|
|
55591
55669
|
recovery,
|
|
55592
|
-
|
|
55593
|
-
? providerLossRecordForLostRow(row, "drain_probe", now)
|
|
55594
|
-
: undefined,
|
|
55670
|
+
workspaceLost ? providerLossRecordForLostRow(row, "drain_probe", now) : undefined,
|
|
55595
55671
|
),
|
|
55596
55672
|
)
|
|
55597
55673
|
: null;
|
|
@@ -55640,7 +55716,7 @@ export async function confirmDrainCold(
|
|
|
55640
55716
|
and archive_capture_id is not distinct from ${input.expectedCaptureId ?? null}::uuid
|
|
55641
55717
|
returning id
|
|
55642
55718
|
`);
|
|
55643
|
-
if (rows.length > 0 &&
|
|
55719
|
+
if (rows.length > 0 && workspaceLost) {
|
|
55644
55720
|
const eventId = crypto.randomUUID();
|
|
55645
55721
|
await tx.insert(schema.auditEvents).values(
|
|
55646
55722
|
withLosslessContentWriteVersion(
|
|
@@ -55670,7 +55746,10 @@ export async function confirmDrainCold(
|
|
|
55670
55746
|
if (rows.length > 0 && row.rotation_requested_at !== null) {
|
|
55671
55747
|
await wakeSandboxLifecycleWaitersTx(tx, input);
|
|
55672
55748
|
}
|
|
55673
|
-
return {
|
|
55749
|
+
return {
|
|
55750
|
+
wentCold: rows.length > 0,
|
|
55751
|
+
...(rows.length > 0 && unpublishedWarmingLoss ? { unpublishedProviderLost: true } : {}),
|
|
55752
|
+
};
|
|
55674
55753
|
},
|
|
55675
55754
|
);
|
|
55676
55755
|
});
|
|
@@ -55775,6 +55854,65 @@ export type SandboxWorkspaceMutationAdmission = {
|
|
|
55775
55854
|
|
|
55776
55855
|
export type SandboxWorkspaceMutationProviderOutcome = "resolved" | "rejected";
|
|
55777
55856
|
|
|
55857
|
+
/** Evidence from a committed exact admission settlement. It establishes the
|
|
55858
|
+
* physical provider outcome only; mutable output authority remains rejected. */
|
|
55859
|
+
export type SandboxWorkspaceMutationPhysicalSettlement = Readonly<{
|
|
55860
|
+
accountId: string;
|
|
55861
|
+
workspaceId: string;
|
|
55862
|
+
admission: Readonly<SandboxWorkspaceMutationAdmission>;
|
|
55863
|
+
operation: string;
|
|
55864
|
+
outcome: "resolved";
|
|
55865
|
+
}>;
|
|
55866
|
+
|
|
55867
|
+
/** Durable terminal truth of the retained process whose concurrent settlement
|
|
55868
|
+
* fenced a physically settled mutation's output. */
|
|
55869
|
+
export type SandboxRetainedProcessTerminalState = Readonly<{
|
|
55870
|
+
state: "exited" | "lost";
|
|
55871
|
+
exitCode: number | null;
|
|
55872
|
+
}>;
|
|
55873
|
+
|
|
55874
|
+
/** The exact provider outcome committed before its output failed an acceptance
|
|
55875
|
+
* fence. A generic admission fence or failed transaction carries no receipt.
|
|
55876
|
+
* This error never authorizes accepting output or replaying provider work. */
|
|
55877
|
+
export class SandboxWorkspaceMutationOutputRejectedError extends SandboxWorkspaceMutationFencedError {
|
|
55878
|
+
readonly name = "SandboxWorkspaceMutationOutputRejectedError";
|
|
55879
|
+
readonly physicalSettlement: SandboxWorkspaceMutationPhysicalSettlement;
|
|
55880
|
+
/** Non-null only when the fence was the retained process itself having
|
|
55881
|
+
* already reached a durable terminal state (for example the reaper settled
|
|
55882
|
+
* the exit between admission and settlement). The output stays rejected;
|
|
55883
|
+
* callers may report this stored terminal truth instead of failing. */
|
|
55884
|
+
readonly retainedProcessTerminal: SandboxRetainedProcessTerminalState | null;
|
|
55885
|
+
|
|
55886
|
+
constructor(
|
|
55887
|
+
code: SandboxWorkspaceMutationFencedError["code"],
|
|
55888
|
+
message: string,
|
|
55889
|
+
physicalSettlement: SandboxWorkspaceMutationPhysicalSettlement,
|
|
55890
|
+
retainedProcessTerminal: SandboxRetainedProcessTerminalState | null = null,
|
|
55891
|
+
) {
|
|
55892
|
+
super(code, message);
|
|
55893
|
+
this.physicalSettlement = Object.freeze({
|
|
55894
|
+
...physicalSettlement,
|
|
55895
|
+
admission: Object.freeze({ ...physicalSettlement.admission }),
|
|
55896
|
+
});
|
|
55897
|
+
this.retainedProcessTerminal = retainedProcessTerminal
|
|
55898
|
+
? Object.freeze({
|
|
55899
|
+
state: retainedProcessTerminal.state,
|
|
55900
|
+
exitCode: retainedProcessTerminal.exitCode,
|
|
55901
|
+
})
|
|
55902
|
+
: null;
|
|
55903
|
+
}
|
|
55904
|
+
|
|
55905
|
+
matchesPhysicalSettlement(input: {
|
|
55906
|
+
accountId: string;
|
|
55907
|
+
workspaceId: string;
|
|
55908
|
+
admission: SandboxWorkspaceMutationAdmission;
|
|
55909
|
+
operation: string;
|
|
55910
|
+
outcome: SandboxWorkspaceMutationProviderOutcome;
|
|
55911
|
+
}): boolean {
|
|
55912
|
+
return isDeepStrictEqual(this.physicalSettlement, input);
|
|
55913
|
+
}
|
|
55914
|
+
}
|
|
55915
|
+
|
|
55778
55916
|
export type SandboxRetainedProcessState = "active" | "exited" | "lost";
|
|
55779
55917
|
|
|
55780
55918
|
export type SandboxRetainedProcess = {
|
|
@@ -55953,6 +56091,8 @@ type SandboxWorkspaceMutationSettlementResult =
|
|
|
55953
56091
|
| "authority_unattributed"
|
|
55954
56092
|
| "authority_revoked";
|
|
55955
56093
|
detail: string;
|
|
56094
|
+
physicallySettled?: true;
|
|
56095
|
+
retainedProcessTerminal?: SandboxRetainedProcessTerminalState;
|
|
55956
56096
|
};
|
|
55957
56097
|
|
|
55958
56098
|
type TurnWorkspaceMutationAuthority = {
|
|
@@ -57230,6 +57370,13 @@ function workspaceMutationAuthorityFailure(
|
|
|
57230
57370
|
) {
|
|
57231
57371
|
return null;
|
|
57232
57372
|
}
|
|
57373
|
+
if (error instanceof SandboxRetainedProcessTerminalError) {
|
|
57374
|
+
return {
|
|
57375
|
+
failure: error.code,
|
|
57376
|
+
detail: error.message,
|
|
57377
|
+
retainedProcessTerminal: { state: error.state, exitCode: error.exitCode },
|
|
57378
|
+
};
|
|
57379
|
+
}
|
|
57233
57380
|
return { failure: error.code, detail: error.message };
|
|
57234
57381
|
}
|
|
57235
57382
|
|
|
@@ -57308,6 +57455,10 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57308
57455
|
},
|
|
57309
57456
|
): Promise<void> {
|
|
57310
57457
|
const operation = normalizeWorkspaceMutationOperation(input.operation);
|
|
57458
|
+
const admissionSnapshot = { ...input.admission };
|
|
57459
|
+
const outcome = input.outcome;
|
|
57460
|
+
// Savepoint release cannot prove an outer caller-owned transaction committed.
|
|
57461
|
+
const ownsCommit = typeof (db as Database & { rollback?: unknown }).rollback !== "function";
|
|
57311
57462
|
const settleOnce = async (): Promise<SandboxWorkspaceMutationSettlementResult> =>
|
|
57312
57463
|
await withRlsContext(
|
|
57313
57464
|
db,
|
|
@@ -57349,19 +57500,20 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57349
57500
|
const admission = await selectExactAdmissionForUpdate(tx, {
|
|
57350
57501
|
accountId: authorityInput.accountId,
|
|
57351
57502
|
workspaceId: authorityInput.workspaceId,
|
|
57352
|
-
admissionId:
|
|
57503
|
+
admissionId: admissionSnapshot.id,
|
|
57353
57504
|
actorKind,
|
|
57354
57505
|
actorId,
|
|
57355
57506
|
sessionId: authorityInput.sessionId,
|
|
57356
|
-
admittedWorkspaceGeneration:
|
|
57507
|
+
admittedWorkspaceGeneration: admissionSnapshot.workspaceGeneration,
|
|
57357
57508
|
operation,
|
|
57358
57509
|
});
|
|
57359
57510
|
if (
|
|
57360
57511
|
!admission ||
|
|
57361
|
-
!admissionMatchesSnapshot(admission,
|
|
57362
|
-
!
|
|
57512
|
+
!admissionMatchesSnapshot(admission, admissionSnapshot) ||
|
|
57513
|
+
!admissionMatchesAuthorityInput(admission, authorityInput) ||
|
|
57363
57514
|
admission.provider_outcome === "retained" ||
|
|
57364
|
-
(admission.provider_outcome && admission.provider_outcome !==
|
|
57515
|
+
(admission.provider_outcome && admission.provider_outcome !== outcome) ||
|
|
57516
|
+
(admission.settled_at && admission.provider_outcome !== outcome)
|
|
57365
57517
|
) {
|
|
57366
57518
|
return {
|
|
57367
57519
|
failure: "admission_fenced" as const,
|
|
@@ -57374,11 +57526,21 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57374
57526
|
authorityInput.workspaceId,
|
|
57375
57527
|
authorityInput.sessionId,
|
|
57376
57528
|
);
|
|
57377
|
-
await tx.execute
|
|
57529
|
+
const [physical] = await tx.execute<{
|
|
57530
|
+
provider_outcome: string;
|
|
57531
|
+
settled_at: Date;
|
|
57532
|
+
}>(sql`
|
|
57378
57533
|
update sandbox_workspace_mutation_admissions set
|
|
57379
|
-
provider_outcome = ${
|
|
57380
|
-
where id = ${
|
|
57534
|
+
provider_outcome = ${outcome}, settled_at = now()
|
|
57535
|
+
where id = ${admissionSnapshot.id} and settled_at is null
|
|
57536
|
+
returning provider_outcome, settled_at
|
|
57381
57537
|
`);
|
|
57538
|
+
if (physical?.provider_outcome !== outcome || !physical.settled_at) {
|
|
57539
|
+
return {
|
|
57540
|
+
failure: "admission_fenced" as const,
|
|
57541
|
+
detail: "Workspace mutation settlement did not commit its exact provider outcome",
|
|
57542
|
+
};
|
|
57543
|
+
}
|
|
57382
57544
|
if (
|
|
57383
57545
|
pendingAttempt ||
|
|
57384
57546
|
(authorityInput.kind !== "direct" &&
|
|
@@ -57410,12 +57572,15 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57410
57572
|
});
|
|
57411
57573
|
}
|
|
57412
57574
|
}
|
|
57413
|
-
if (
|
|
57414
|
-
if (authorityFailure) return authorityFailure;
|
|
57575
|
+
if (outcome === "rejected") return { failure: null };
|
|
57576
|
+
if (authorityFailure) return { ...authorityFailure, physicallySettled: true };
|
|
57415
57577
|
if (!authority) {
|
|
57416
57578
|
throw new Error("Workspace mutation settlement lost its locked authority");
|
|
57417
57579
|
}
|
|
57418
|
-
|
|
57580
|
+
const acceptance = await verifyResolvedAdmissionAuthority(tx, authority, admission);
|
|
57581
|
+
return acceptance.failure === null
|
|
57582
|
+
? acceptance
|
|
57583
|
+
: { ...acceptance, physicallySettled: true };
|
|
57419
57584
|
}),
|
|
57420
57585
|
);
|
|
57421
57586
|
const settlement = await runIdempotentPersistenceTransaction(
|
|
@@ -57426,6 +57591,23 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57426
57591
|
settleOnce,
|
|
57427
57592
|
);
|
|
57428
57593
|
if (settlement.failure !== null) {
|
|
57594
|
+
if (settlement.physicallySettled && ownsCommit) {
|
|
57595
|
+
// This branch is reached only after the transaction committed a matching
|
|
57596
|
+
// terminal admission. A failed commit or contradictory receipt never
|
|
57597
|
+
// reaches it, even when the provider returned successfully.
|
|
57598
|
+
throw new SandboxWorkspaceMutationOutputRejectedError(
|
|
57599
|
+
settlement.failure,
|
|
57600
|
+
settlement.detail,
|
|
57601
|
+
{
|
|
57602
|
+
accountId: authorityInput.accountId,
|
|
57603
|
+
workspaceId: authorityInput.workspaceId,
|
|
57604
|
+
admission: admissionSnapshot,
|
|
57605
|
+
operation,
|
|
57606
|
+
outcome: "resolved",
|
|
57607
|
+
},
|
|
57608
|
+
settlement.retainedProcessTerminal ?? null,
|
|
57609
|
+
);
|
|
57610
|
+
}
|
|
57429
57611
|
throw new SandboxWorkspaceMutationFencedError(settlement.failure, settlement.detail);
|
|
57430
57612
|
}
|
|
57431
57613
|
}
|
|
@@ -85986,20 +86168,12 @@ export async function mutateAndAppendSessionEventsForTurnAttempt(
|
|
|
85986
86168
|
workspaceId,
|
|
85987
86169
|
activityGateOpen,
|
|
85988
86170
|
async (tx) => {
|
|
85989
|
-
|
|
85990
|
-
|
|
85991
|
-
|
|
85992
|
-
|
|
85993
|
-
|
|
85994
|
-
)
|
|
85995
|
-
if (!mutationApplied) {
|
|
85996
|
-
return {
|
|
85997
|
-
events: [],
|
|
85998
|
-
accepted: false,
|
|
85999
|
-
mutationApplied: false,
|
|
86000
|
-
canonicalStartupMilestones: [],
|
|
86001
|
-
};
|
|
86002
|
-
}
|
|
86171
|
+
// Canonical prefix BEFORE the caller's mutation. The mutation
|
|
86172
|
+
// locks a session-owned row (the Codemode journal row on
|
|
86173
|
+
// terminal settlement); taking it first and the session prefix
|
|
86174
|
+
// second inverted the order of every prefix holder that then
|
|
86175
|
+
// locks the same row (a Codemode re-submit or claim), which
|
|
86176
|
+
// deadlocked POST /codemode/calls in production (40P01).
|
|
86003
86177
|
const fence = await runSessionEventAppendPhase(
|
|
86004
86178
|
observer,
|
|
86005
86179
|
{ ...phaseInput, phase: "turn_attempt_fence" },
|
|
@@ -86020,6 +86194,20 @@ export async function mutateAndAppendSessionEventsForTurnAttempt(
|
|
|
86020
86194
|
if (!activityGateOpen && !fence.allowed) {
|
|
86021
86195
|
throw new SessionActivityGateEscalation();
|
|
86022
86196
|
}
|
|
86197
|
+
const mutationApplied = await runSessionEventAppendPhase(
|
|
86198
|
+
observer,
|
|
86199
|
+
{ ...phaseInput, phase: "mutation" },
|
|
86200
|
+
async () => await mutate(tx),
|
|
86201
|
+
(applied) => (applied ? "ok" : "mutation_skipped"),
|
|
86202
|
+
);
|
|
86203
|
+
if (!mutationApplied) {
|
|
86204
|
+
return {
|
|
86205
|
+
events: [],
|
|
86206
|
+
accepted: false,
|
|
86207
|
+
mutationApplied: false,
|
|
86208
|
+
canonicalStartupMilestones: [],
|
|
86209
|
+
};
|
|
86210
|
+
}
|
|
86023
86211
|
let sequence = session.lastSequence;
|
|
86024
86212
|
const now = new Date();
|
|
86025
86213
|
const usageSourceKey = (input: AppendEventInput): string | null => {
|
|
@@ -87622,67 +87810,6 @@ function projectInstallationConfig(config: Record<string, unknown>): Record<stri
|
|
|
87622
87810
|
return { ...rest, headerNames: Object.keys(headersEncrypted).sort() };
|
|
87623
87811
|
}
|
|
87624
87812
|
|
|
87625
|
-
function mapConnectionMetadata(row: {
|
|
87626
|
-
id: string;
|
|
87627
|
-
authorityId?: string | null;
|
|
87628
|
-
authorityGeneration: number;
|
|
87629
|
-
accountId: string;
|
|
87630
|
-
workspaceId: string;
|
|
87631
|
-
subjectId: string | null;
|
|
87632
|
-
providerDomain: string;
|
|
87633
|
-
kind: string;
|
|
87634
|
-
status: string;
|
|
87635
|
-
grantedScopes: string[];
|
|
87636
|
-
expiresAt: Date | null;
|
|
87637
|
-
lastRefreshAt: Date | null;
|
|
87638
|
-
lastUsedAt: Date | null;
|
|
87639
|
-
lastError: string | null;
|
|
87640
|
-
version: number;
|
|
87641
|
-
verifiedInstallAt: Date | null;
|
|
87642
|
-
verifiedInstallVersion: number | null;
|
|
87643
|
-
metadata: Record<string, unknown>;
|
|
87644
|
-
createdBySubjectId: string | null;
|
|
87645
|
-
updatedBySubjectId: string | null;
|
|
87646
|
-
createdAt: Date;
|
|
87647
|
-
updatedAt: Date;
|
|
87648
|
-
}): ConnectionMetadataWithVerification {
|
|
87649
|
-
const {
|
|
87650
|
-
[OPENROUTER_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _openRouterOperationId,
|
|
87651
|
-
[OPENROUTER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _openRouterOperationDigest,
|
|
87652
|
-
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _operationId,
|
|
87653
|
-
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _operationDigest,
|
|
87654
|
-
anthropicCredentialOperationId: _anthropicOperationId,
|
|
87655
|
-
anthropicCredentialOperationDigest: _anthropicOperationDigest,
|
|
87656
|
-
claude_subscriptionCredentialOperationId: _claudeOperationId,
|
|
87657
|
-
claude_subscriptionCredentialOperationDigest: _claudeOperationDigest,
|
|
87658
|
-
...publicMetadata
|
|
87659
|
-
} = row.metadata;
|
|
87660
|
-
return {
|
|
87661
|
-
id: row.id,
|
|
87662
|
-
...(row.subjectId !== null && row.authorityId ? { authorityId: row.authorityId } : {}),
|
|
87663
|
-
connectionAuthorityGeneration: row.authorityGeneration,
|
|
87664
|
-
accountId: row.accountId,
|
|
87665
|
-
workspaceId: row.workspaceId,
|
|
87666
|
-
subjectId: row.subjectId,
|
|
87667
|
-
providerDomain: row.providerDomain,
|
|
87668
|
-
kind: row.kind as ConnectionKind,
|
|
87669
|
-
status: row.status as ConnectionStatus,
|
|
87670
|
-
grantedScopes: row.grantedScopes,
|
|
87671
|
-
expiresAt: row.expiresAt?.toISOString() ?? null,
|
|
87672
|
-
lastRefreshAt: row.lastRefreshAt?.toISOString() ?? null,
|
|
87673
|
-
lastUsedAt: row.lastUsedAt?.toISOString() ?? null,
|
|
87674
|
-
lastError: row.lastError,
|
|
87675
|
-
version: row.version,
|
|
87676
|
-
verifiedInstallAt: row.verifiedInstallAt?.toISOString() ?? null,
|
|
87677
|
-
verifiedInstallVersion: row.verifiedInstallVersion,
|
|
87678
|
-
metadata: publicMetadata,
|
|
87679
|
-
createdBySubjectId: row.createdBySubjectId,
|
|
87680
|
-
updatedBySubjectId: row.updatedBySubjectId,
|
|
87681
|
-
createdAt: row.createdAt.toISOString(),
|
|
87682
|
-
updatedAt: row.updatedAt.toISOString(),
|
|
87683
|
-
};
|
|
87684
|
-
}
|
|
87685
|
-
|
|
87686
87813
|
function mapSlackBotPostOperation(
|
|
87687
87814
|
row: typeof schema.slackBotPostOperations.$inferSelect,
|
|
87688
87815
|
): SlackBotPostOperation {
|
|
@@ -88321,6 +88448,7 @@ export * from "./codemode-approvals";
|
|
|
88321
88448
|
export * from "./tool-action-reviews";
|
|
88322
88449
|
export * from "./browser-sessions";
|
|
88323
88450
|
export * from "./browser-deadline-checkpoints";
|
|
88451
|
+
export * from "./sandbox-interaction-idle";
|
|
88324
88452
|
export * from "./computer-sessions";
|
|
88325
88453
|
export * from "./browser-identities";
|
|
88326
88454
|
export * from "./browser-state-artifacts";
|