@opengeni/db 1.4.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 +2 -2
- package/dist/capability-reconciliation.js +1 -1
- package/dist/{chunk-7QWWTVYH.js → chunk-5ROHLEN6.js} +29 -4
- package/dist/chunk-5ROHLEN6.js.map +1 -0
- package/dist/{chunk-BA52ITIY.js → chunk-A4J7EECR.js} +2 -2
- package/dist/{chunk-3WUCM7YF.js → chunk-B5C5SBSI.js} +2 -2
- package/dist/{chunk-VCHK4D6S.js → chunk-DKFEUGM4.js} +2 -2
- package/dist/{chunk-EFANLEWI.js → chunk-HUKBIZGH.js} +2 -2
- package/dist/{chunk-27T4AP25.js → chunk-IT3EYY2C.js} +5 -3
- package/dist/{chunk-27T4AP25.js.map → chunk-IT3EYY2C.js.map} +1 -1
- package/dist/{chunk-NT7OGKYX.js → chunk-JWBEE4CL.js} +3 -3
- package/dist/{chunk-AMK3FNOW.js → chunk-K5MTNKJU.js} +3 -3
- package/dist/{chunk-GC5GUTV3.js → chunk-NZ2SP55O.js} +2 -2
- package/dist/{chunk-TRUEH4I2.js → chunk-OKCBARX6.js} +2 -2
- package/dist/{chunk-EMYA6XNE.js → chunk-OV5AUIBA.js} +2 -2
- package/dist/{chunk-T3CYF3DH.js → chunk-TODVDGMO.js} +35 -17
- package/dist/{chunk-T3CYF3DH.js.map → chunk-TODVDGMO.js.map} +1 -1
- package/dist/{chunk-CKDHKQS4.js → chunk-W75DRYG3.js} +4 -4
- package/dist/{chunk-UQELJ6OM.js → chunk-Y72DP3JF.js} +2 -2
- package/dist/codemode-approvals.d.ts +7 -1
- package/dist/connect-attempts.js +2 -2
- package/dist/credit-balances.d.ts +25 -4
- package/dist/database.d.ts +7 -1
- package/dist/editable-artifact-durable-export.js +1 -1
- package/dist/editable-artifacts.js +2 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +2778 -2508
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +2 -2
- package/dist/mcp-operations.js +1 -1
- package/dist/modal-native-live-origin.d.ts +75 -0
- package/dist/organization-integration-policy.js +2 -2
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +21 -21
- package/dist/retained-provider-commands.js +5 -5
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/sandbox-interaction-idle.d.ts +22 -0
- package/dist/session-background-commands.js +3 -3
- package/dist/session-command-output.js +4 -4
- package/dist/session-event-slices.js +2 -2
- package/dist/session-mcp-credential-rotation.js +4 -4
- package/dist/session-tenancy.js +2 -2
- package/dist/video-generation.js +2 -2
- package/dist/workspace-tool-defaults.js +3 -3
- 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/credit-balances.ts +58 -17
- package/src/database.ts +9 -0
- package/src/index.ts +449 -254
- package/src/modal-native-live-origin.ts +148 -0
- package/src/provision-roles.ts +12 -0
- package/src/runtime-posture.ts +32 -3
- package/src/sandbox-interaction-idle.ts +91 -0
- package/src/transcription-recordings.ts +3 -1
- package/dist/chunk-7QWWTVYH.js.map +0 -1
- /package/dist/{chunk-BA52ITIY.js.map → chunk-A4J7EECR.js.map} +0 -0
- /package/dist/{chunk-3WUCM7YF.js.map → chunk-B5C5SBSI.js.map} +0 -0
- /package/dist/{chunk-VCHK4D6S.js.map → chunk-DKFEUGM4.js.map} +0 -0
- /package/dist/{chunk-EFANLEWI.js.map → chunk-HUKBIZGH.js.map} +0 -0
- /package/dist/{chunk-NT7OGKYX.js.map → chunk-JWBEE4CL.js.map} +0 -0
- /package/dist/{chunk-AMK3FNOW.js.map → chunk-K5MTNKJU.js.map} +0 -0
- /package/dist/{chunk-GC5GUTV3.js.map → chunk-NZ2SP55O.js.map} +0 -0
- /package/dist/{chunk-TRUEH4I2.js.map → chunk-OKCBARX6.js.map} +0 -0
- /package/dist/{chunk-EMYA6XNE.js.map → chunk-OV5AUIBA.js.map} +0 -0
- /package/dist/{chunk-CKDHKQS4.js.map → chunk-W75DRYG3.js.map} +0 -0
- /package/dist/{chunk-UQELJ6OM.js.map → chunk-Y72DP3JF.js.map} +0 -0
package/src/index.ts
CHANGED
|
@@ -15,11 +15,19 @@ export {
|
|
|
15
15
|
type ConnectionMetadataWithVerification,
|
|
16
16
|
} from "./connection-metadata";
|
|
17
17
|
export { readCreditPromotionPolicy } from "./credit-promotion-policy";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
getBillingBalance,
|
|
20
|
+
getSpendableCreditBalance,
|
|
21
|
+
planCreditDebit,
|
|
22
|
+
planPostUseCreditAllocations,
|
|
23
|
+
type CreditUsage,
|
|
24
|
+
} from "./credit-balances";
|
|
19
25
|
export {
|
|
20
26
|
getBillingBalance,
|
|
21
27
|
getSpendableCreditBalance,
|
|
22
28
|
spendableCreditMicros,
|
|
29
|
+
VOICE_CREDIT_USAGE,
|
|
30
|
+
type CreditUsage,
|
|
23
31
|
} from "./credit-balances";
|
|
24
32
|
import {
|
|
25
33
|
lockTurnAttemptWriteFenceTx,
|
|
@@ -52,6 +60,12 @@ import {
|
|
|
52
60
|
inboxOrigin,
|
|
53
61
|
} from "./inbox-execution-context";
|
|
54
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";
|
|
55
69
|
export { SubscriptionAccountChangedError } from "./subscription-account-conflict";
|
|
56
70
|
import type { GoalAdmissionPausedReason } from "@opengeni/contracts";
|
|
57
71
|
import {
|
|
@@ -5878,6 +5892,11 @@ export async function applyCreditDebitAfterUse(
|
|
|
5878
5892
|
idempotencyKey: string;
|
|
5879
5893
|
metadata?: Record<string, unknown>;
|
|
5880
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;
|
|
5881
5900
|
},
|
|
5882
5901
|
): Promise<{ balance: BillingBalance; debitedMicros: number }> {
|
|
5883
5902
|
if (!Number.isSafeInteger(input.amountMicros) || input.amountMicros <= 0) {
|
|
@@ -5887,6 +5906,17 @@ export async function applyCreditDebitAfterUse(
|
|
|
5887
5906
|
db,
|
|
5888
5907
|
{ accountId: input.accountId, workspaceId: input.workspaceId ?? null },
|
|
5889
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
|
+
}
|
|
5890
5920
|
const inserted = await scopedDb
|
|
5891
5921
|
.insert(schema.creditLedgerEntries)
|
|
5892
5922
|
.values({
|
|
@@ -5927,6 +5957,14 @@ export async function applyCreditDebitAfterUse(
|
|
|
5927
5957
|
"post-use credit debit idempotency key conflicts with a different charge",
|
|
5928
5958
|
);
|
|
5929
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
|
+
);
|
|
5930
5968
|
}
|
|
5931
5969
|
return {
|
|
5932
5970
|
balance: await getBillingBalance(scopedDb, input.accountId),
|
|
@@ -33303,6 +33341,53 @@ async function priorConnectorActionPreparation(
|
|
|
33303
33341
|
};
|
|
33304
33342
|
}
|
|
33305
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
|
+
|
|
33306
33391
|
export async function prepareConnectorActionApproval(
|
|
33307
33392
|
db: Database,
|
|
33308
33393
|
identity: ConnectorActionAttemptIdentity,
|
|
@@ -33312,67 +33397,66 @@ export async function prepareConnectorActionApproval(
|
|
|
33312
33397
|
if (!normalized.connectionId) {
|
|
33313
33398
|
return { managed: false, decision: "unmanaged" };
|
|
33314
33399
|
}
|
|
33315
|
-
return await
|
|
33400
|
+
return await runConnectorActionLedgerTransaction(
|
|
33316
33401
|
db,
|
|
33317
|
-
{
|
|
33318
|
-
|
|
33319
|
-
|
|
33320
|
-
|
|
33321
|
-
|
|
33322
|
-
|
|
33323
|
-
|
|
33324
|
-
|
|
33325
|
-
|
|
33326
|
-
|
|
33327
|
-
|
|
33328
|
-
|
|
33329
|
-
|
|
33330
|
-
|
|
33331
|
-
|
|
33332
|
-
|
|
33333
|
-
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33337
|
-
|
|
33338
|
-
|
|
33339
|
-
|
|
33340
|
-
|
|
33341
|
-
|
|
33342
|
-
|
|
33343
|
-
|
|
33344
|
-
|
|
33345
|
-
return {
|
|
33346
|
-
managed: true,
|
|
33347
|
-
decision,
|
|
33348
|
-
actionFingerprint: durable.actionFingerprint,
|
|
33349
|
-
} as const;
|
|
33350
|
-
}
|
|
33351
|
-
const { row, inserted } = await insertConnectorActionRequest(tx as unknown as Database, {
|
|
33352
|
-
identity,
|
|
33353
|
-
invocation: durable,
|
|
33354
|
-
resolved,
|
|
33355
|
-
status: decision === "ask" ? "pending" : "blocked",
|
|
33356
|
-
});
|
|
33357
|
-
if (inserted) {
|
|
33358
|
-
await insertConnectorActionAudit(tx as unknown as Database, {
|
|
33359
|
-
row,
|
|
33360
|
-
action:
|
|
33361
|
-
decision === "ask"
|
|
33362
|
-
? "connector.action.approval_requested"
|
|
33363
|
-
: "connector.action.blocked",
|
|
33364
|
-
subjectId: identity.initiator.subjectId,
|
|
33365
|
-
extra: { outcome: decision },
|
|
33366
|
-
});
|
|
33367
|
-
}
|
|
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") {
|
|
33368
33430
|
return {
|
|
33369
33431
|
managed: true,
|
|
33370
33432
|
decision,
|
|
33371
|
-
|
|
33372
|
-
requestId: row.id,
|
|
33373
|
-
actionFingerprint: row.actionFingerprint,
|
|
33433
|
+
actionFingerprint: durable.actionFingerprint,
|
|
33374
33434
|
} as const;
|
|
33375
|
-
}
|
|
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
|
+
},
|
|
33376
33460
|
);
|
|
33377
33461
|
}
|
|
33378
33462
|
|
|
@@ -33426,13 +33510,28 @@ export async function beginConnectorActionExecution(
|
|
|
33426
33510
|
if (!normalized.connectionId) {
|
|
33427
33511
|
return { allowed: true, managed: false };
|
|
33428
33512
|
}
|
|
33429
|
-
|
|
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(
|
|
33430
33524
|
db,
|
|
33431
|
-
{
|
|
33432
|
-
|
|
33433
|
-
|
|
33434
|
-
|
|
33435
|
-
|
|
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
|
|
33436
33535
|
.select()
|
|
33437
33536
|
.from(schema.connectorActionRequests)
|
|
33438
33537
|
.where(
|
|
@@ -33442,73 +33541,89 @@ export async function beginConnectorActionExecution(
|
|
|
33442
33541
|
eq(schema.connectorActionRequests.turnId, identity.turnId),
|
|
33443
33542
|
eq(schema.connectorActionRequests.approvalId, normalized.approvalId),
|
|
33444
33543
|
),
|
|
33445
|
-
)
|
|
33446
|
-
.for("update")
|
|
33447
|
-
.limit(1);
|
|
33448
|
-
if (existing && !connectorActionRequestMatchesLogicalCall(existing, identity, normalized)) {
|
|
33449
|
-
throw new Error("connector action approval id conflicts with different immutable inputs");
|
|
33450
|
-
}
|
|
33451
|
-
let row = existing;
|
|
33452
|
-
let inserted = false;
|
|
33453
|
-
if (!row) {
|
|
33454
|
-
const resolved = resolveConnectorActionPolicy(snapshot, {
|
|
33455
|
-
connectionId: normalized.connectionId!,
|
|
33456
|
-
serverId: normalized.serverId,
|
|
33457
|
-
toolName: normalized.toolName,
|
|
33458
|
-
actionName: normalized.policyActionSelector,
|
|
33459
|
-
...(normalized.defaultDecision !== undefined
|
|
33460
|
-
? { defaultDecision: normalized.defaultDecision }
|
|
33461
|
-
: {}),
|
|
33462
|
-
});
|
|
33463
|
-
if (!resolved.managed) return { allowed: true, managed: false } as const;
|
|
33464
|
-
const durable = durableConnectorActionInvocation(
|
|
33465
|
-
identity,
|
|
33466
|
-
{ ...normalized, connectionId: normalized.connectionId! },
|
|
33467
|
-
resolved,
|
|
33468
33544
|
);
|
|
33469
|
-
|
|
33470
|
-
|
|
33471
|
-
|
|
33472
|
-
|
|
33473
|
-
|
|
33474
|
-
|
|
33475
|
-
|
|
33476
|
-
|
|
33477
|
-
|
|
33478
|
-
|
|
33479
|
-
|
|
33480
|
-
|
|
33481
|
-
|
|
33482
|
-
decision === "ask"
|
|
33483
|
-
? "connector.action.approval_requested"
|
|
33484
|
-
: decision === "block"
|
|
33485
|
-
? "connector.action.blocked"
|
|
33486
|
-
: "connector.action.execution_started",
|
|
33487
|
-
subjectId: identity.initiator.subjectId,
|
|
33488
|
-
extra: { outcome: decision === "allow" ? "started" : decision },
|
|
33489
|
-
});
|
|
33490
|
-
}
|
|
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;
|
|
33491
33558
|
}
|
|
33492
|
-
|
|
33493
|
-
|
|
33494
|
-
|
|
33495
|
-
|
|
33496
|
-
|
|
33497
|
-
|
|
33498
|
-
|
|
33499
|
-
|
|
33500
|
-
|
|
33501
|
-
|
|
33502
|
-
|
|
33503
|
-
|
|
33504
|
-
|
|
33505
|
-
|
|
33506
|
-
|
|
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, {
|
|
33507
33586
|
row,
|
|
33508
|
-
action:
|
|
33587
|
+
action:
|
|
33588
|
+
decision === "ask"
|
|
33589
|
+
? "connector.action.approval_requested"
|
|
33590
|
+
: decision === "block"
|
|
33591
|
+
? "connector.action.blocked"
|
|
33592
|
+
: "connector.action.execution_started",
|
|
33509
33593
|
subjectId: identity.initiator.subjectId,
|
|
33510
|
-
extra: { outcome: "started" },
|
|
33594
|
+
extra: { outcome: decision === "allow" ? "started" : decision },
|
|
33511
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) {
|
|
33512
33627
|
return {
|
|
33513
33628
|
allowed: true,
|
|
33514
33629
|
managed: true,
|
|
@@ -33516,58 +33631,49 @@ export async function beginConnectorActionExecution(
|
|
|
33516
33631
|
actionFingerprint: row.actionFingerprint,
|
|
33517
33632
|
} as const;
|
|
33518
33633
|
}
|
|
33519
|
-
|
|
33520
|
-
|
|
33521
|
-
|
|
33522
|
-
|
|
33523
|
-
|
|
33524
|
-
|
|
33525
|
-
|
|
33526
|
-
|
|
33527
|
-
|
|
33528
|
-
|
|
33529
|
-
|
|
33530
|
-
|
|
33531
|
-
|
|
33532
|
-
|
|
33533
|
-
|
|
33534
|
-
|
|
33535
|
-
|
|
33536
|
-
.where(eq(schema.connectorActionRequests.id, row.id))
|
|
33537
|
-
.returning();
|
|
33538
|
-
if (!uncertain) throw new Error("Executing connector action request disappeared");
|
|
33539
|
-
await insertConnectorActionAudit(tx as unknown as Database, {
|
|
33540
|
-
row: uncertain,
|
|
33541
|
-
action: "connector.action.execution_uncertain",
|
|
33542
|
-
subjectId: identity.initiator.subjectId,
|
|
33543
|
-
extra: { outcome: "retry_denied" },
|
|
33544
|
-
});
|
|
33545
|
-
return {
|
|
33546
|
-
allowed: false,
|
|
33547
|
-
managed: true,
|
|
33548
|
-
reason: "uncertain_retry",
|
|
33549
|
-
requestId: uncertain.id,
|
|
33550
|
-
actionFingerprint: uncertain.actionFingerprint,
|
|
33551
|
-
} as const;
|
|
33552
|
-
}
|
|
33553
|
-
const reason =
|
|
33554
|
-
row.status === "pending"
|
|
33555
|
-
? "approval_required"
|
|
33556
|
-
: row.status === "rejected"
|
|
33557
|
-
? "rejected"
|
|
33558
|
-
: row.status === "blocked"
|
|
33559
|
-
? "blocked"
|
|
33560
|
-
: row.status === "failed"
|
|
33561
|
-
? "not_executed"
|
|
33562
|
-
: "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
|
+
});
|
|
33563
33651
|
return {
|
|
33564
33652
|
allowed: false,
|
|
33565
33653
|
managed: true,
|
|
33566
|
-
reason,
|
|
33567
|
-
requestId:
|
|
33568
|
-
actionFingerprint:
|
|
33654
|
+
reason: "uncertain_retry",
|
|
33655
|
+
requestId: uncertain.id,
|
|
33656
|
+
actionFingerprint: uncertain.actionFingerprint,
|
|
33569
33657
|
} as const;
|
|
33570
|
-
}
|
|
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
|
+
},
|
|
33571
33677
|
);
|
|
33572
33678
|
}
|
|
33573
33679
|
|
|
@@ -33581,12 +33687,17 @@ export async function completeConnectorActionExecution(
|
|
|
33581
33687
|
outcome: "completed" | "uncertain" | "not_executed";
|
|
33582
33688
|
},
|
|
33583
33689
|
): Promise<void> {
|
|
33584
|
-
await
|
|
33690
|
+
await runConnectorActionLedgerTransaction(
|
|
33585
33691
|
db,
|
|
33586
|
-
{
|
|
33587
|
-
|
|
33588
|
-
|
|
33589
|
-
|
|
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
|
|
33590
33701
|
.select()
|
|
33591
33702
|
.from(schema.connectorActionRequests)
|
|
33592
33703
|
.where(
|
|
@@ -33595,43 +33706,51 @@ export async function completeConnectorActionExecution(
|
|
|
33595
33706
|
eq(schema.connectorActionRequests.id, input.requestId),
|
|
33596
33707
|
eq(schema.connectorActionRequests.executionAttemptId, input.attemptId),
|
|
33597
33708
|
),
|
|
33598
|
-
)
|
|
33599
|
-
|
|
33600
|
-
|
|
33601
|
-
|
|
33602
|
-
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33614
|
-
|
|
33615
|
-
|
|
33616
|
-
|
|
33617
|
-
|
|
33618
|
-
|
|
33619
|
-
|
|
33620
|
-
|
|
33621
|
-
|
|
33622
|
-
|
|
33623
|
-
|
|
33624
|
-
|
|
33625
|
-
|
|
33626
|
-
|
|
33627
|
-
|
|
33628
|
-
|
|
33629
|
-
|
|
33630
|
-
|
|
33631
|
-
|
|
33632
|
-
|
|
33633
|
-
|
|
33634
|
-
|
|
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
|
+
},
|
|
33635
33754
|
);
|
|
33636
33755
|
}
|
|
33637
33756
|
|
|
@@ -43097,9 +43216,12 @@ export async function appendSessionHistoryItems(
|
|
|
43097
43216
|
if (input.items.length === 0) {
|
|
43098
43217
|
return true;
|
|
43099
43218
|
}
|
|
43100
|
-
|
|
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(
|
|
43101
43222
|
db,
|
|
43102
43223
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
43224
|
+
{ stage: "session_history_append", correlationId: input.expectedAttemptId },
|
|
43103
43225
|
async (scopedDb) => {
|
|
43104
43226
|
return await scopedDb.transaction(async (tx) => {
|
|
43105
43227
|
const allowed = await lockTurnAttemptWriteFenceTx(tx, {
|
|
@@ -43413,9 +43535,12 @@ export async function recordPendingSessionToolCallResult(
|
|
|
43413
43535
|
};
|
|
43414
43536
|
},
|
|
43415
43537
|
): Promise<{ accepted: boolean; recorded: boolean }> {
|
|
43416
|
-
|
|
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(
|
|
43417
43541
|
db,
|
|
43418
43542
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
43543
|
+
{ stage: "session_pending_tool_call_result", correlationId: input.callId },
|
|
43419
43544
|
async (scopedDb) =>
|
|
43420
43545
|
await scopedDb.transaction(async (tx) => {
|
|
43421
43546
|
const fence = await lockTurnAttemptWriteFenceTx(tx, {
|
|
@@ -55312,7 +55437,13 @@ export async function confirmDrainCold(
|
|
|
55312
55437
|
/** The idle window named in contained commands' agent notice. */
|
|
55313
55438
|
idleCommandContainmentMs?: number | undefined;
|
|
55314
55439
|
},
|
|
55315
|
-
): 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
|
+
}> {
|
|
55316
55447
|
const backgroundCommandEvents: SessionEvent[] = [];
|
|
55317
55448
|
// Settling a command appends its terminal session event and agent input, so
|
|
55318
55449
|
// the cold commit runs inside the session activity gate.
|
|
@@ -55435,8 +55566,23 @@ export async function confirmDrainCold(
|
|
|
55435
55566
|
const hasArchive = current.archive.status !== "none";
|
|
55436
55567
|
const archiveComplete = hasCompleteWorkspaceArchive(row);
|
|
55437
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;
|
|
55438
55584
|
const lateArchiveCapture =
|
|
55439
|
-
|
|
55585
|
+
workspaceLost &&
|
|
55440
55586
|
row.archive_capture_id !== null &&
|
|
55441
55587
|
row.archive_capture_provider_request_id !== null &&
|
|
55442
55588
|
row.archive_capture_generation !== null &&
|
|
@@ -55461,18 +55607,27 @@ export async function confirmDrainCold(
|
|
|
55461
55607
|
? "pending"
|
|
55462
55608
|
: hasArchive
|
|
55463
55609
|
? "degraded"
|
|
55464
|
-
:
|
|
55610
|
+
: workspaceLost || freshPending
|
|
55465
55611
|
? "unrecoverable"
|
|
55466
55612
|
: "not_required";
|
|
55467
55613
|
const recovery: SandboxRecoveryState = {
|
|
55468
|
-
provider:
|
|
55469
|
-
|
|
55470
|
-
|
|
55471
|
-
|
|
55472
|
-
|
|
55473
|
-
|
|
55474
|
-
|
|
55475
|
-
|
|
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
|
+
},
|
|
55476
55631
|
archive: current.archive,
|
|
55477
55632
|
restore: {
|
|
55478
55633
|
status: restoreStatus,
|
|
@@ -55512,9 +55667,7 @@ export async function confirmDrainCold(
|
|
|
55512
55667
|
archiveOnlyResumeState(
|
|
55513
55668
|
row,
|
|
55514
55669
|
recovery,
|
|
55515
|
-
|
|
55516
|
-
? providerLossRecordForLostRow(row, "drain_probe", now)
|
|
55517
|
-
: undefined,
|
|
55670
|
+
workspaceLost ? providerLossRecordForLostRow(row, "drain_probe", now) : undefined,
|
|
55518
55671
|
),
|
|
55519
55672
|
)
|
|
55520
55673
|
: null;
|
|
@@ -55563,7 +55716,7 @@ export async function confirmDrainCold(
|
|
|
55563
55716
|
and archive_capture_id is not distinct from ${input.expectedCaptureId ?? null}::uuid
|
|
55564
55717
|
returning id
|
|
55565
55718
|
`);
|
|
55566
|
-
if (rows.length > 0 &&
|
|
55719
|
+
if (rows.length > 0 && workspaceLost) {
|
|
55567
55720
|
const eventId = crypto.randomUUID();
|
|
55568
55721
|
await tx.insert(schema.auditEvents).values(
|
|
55569
55722
|
withLosslessContentWriteVersion(
|
|
@@ -55593,7 +55746,10 @@ export async function confirmDrainCold(
|
|
|
55593
55746
|
if (rows.length > 0 && row.rotation_requested_at !== null) {
|
|
55594
55747
|
await wakeSandboxLifecycleWaitersTx(tx, input);
|
|
55595
55748
|
}
|
|
55596
|
-
return {
|
|
55749
|
+
return {
|
|
55750
|
+
wentCold: rows.length > 0,
|
|
55751
|
+
...(rows.length > 0 && unpublishedWarmingLoss ? { unpublishedProviderLost: true } : {}),
|
|
55752
|
+
};
|
|
55597
55753
|
},
|
|
55598
55754
|
);
|
|
55599
55755
|
});
|
|
@@ -55708,23 +55864,42 @@ export type SandboxWorkspaceMutationPhysicalSettlement = Readonly<{
|
|
|
55708
55864
|
outcome: "resolved";
|
|
55709
55865
|
}>;
|
|
55710
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
|
+
|
|
55711
55874
|
/** The exact provider outcome committed before its output failed an acceptance
|
|
55712
55875
|
* fence. A generic admission fence or failed transaction carries no receipt.
|
|
55713
55876
|
* This error never authorizes accepting output or replaying provider work. */
|
|
55714
55877
|
export class SandboxWorkspaceMutationOutputRejectedError extends SandboxWorkspaceMutationFencedError {
|
|
55715
55878
|
readonly name = "SandboxWorkspaceMutationOutputRejectedError";
|
|
55716
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;
|
|
55717
55885
|
|
|
55718
55886
|
constructor(
|
|
55719
55887
|
code: SandboxWorkspaceMutationFencedError["code"],
|
|
55720
55888
|
message: string,
|
|
55721
55889
|
physicalSettlement: SandboxWorkspaceMutationPhysicalSettlement,
|
|
55890
|
+
retainedProcessTerminal: SandboxRetainedProcessTerminalState | null = null,
|
|
55722
55891
|
) {
|
|
55723
55892
|
super(code, message);
|
|
55724
55893
|
this.physicalSettlement = Object.freeze({
|
|
55725
55894
|
...physicalSettlement,
|
|
55726
55895
|
admission: Object.freeze({ ...physicalSettlement.admission }),
|
|
55727
55896
|
});
|
|
55897
|
+
this.retainedProcessTerminal = retainedProcessTerminal
|
|
55898
|
+
? Object.freeze({
|
|
55899
|
+
state: retainedProcessTerminal.state,
|
|
55900
|
+
exitCode: retainedProcessTerminal.exitCode,
|
|
55901
|
+
})
|
|
55902
|
+
: null;
|
|
55728
55903
|
}
|
|
55729
55904
|
|
|
55730
55905
|
matchesPhysicalSettlement(input: {
|
|
@@ -55917,6 +56092,7 @@ type SandboxWorkspaceMutationSettlementResult =
|
|
|
55917
56092
|
| "authority_revoked";
|
|
55918
56093
|
detail: string;
|
|
55919
56094
|
physicallySettled?: true;
|
|
56095
|
+
retainedProcessTerminal?: SandboxRetainedProcessTerminalState;
|
|
55920
56096
|
};
|
|
55921
56097
|
|
|
55922
56098
|
type TurnWorkspaceMutationAuthority = {
|
|
@@ -57194,6 +57370,13 @@ function workspaceMutationAuthorityFailure(
|
|
|
57194
57370
|
) {
|
|
57195
57371
|
return null;
|
|
57196
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
|
+
}
|
|
57197
57380
|
return { failure: error.code, detail: error.message };
|
|
57198
57381
|
}
|
|
57199
57382
|
|
|
@@ -57412,13 +57595,18 @@ async function verifyWorkspaceMutationSettlementForAuthority(
|
|
|
57412
57595
|
// This branch is reached only after the transaction committed a matching
|
|
57413
57596
|
// terminal admission. A failed commit or contradictory receipt never
|
|
57414
57597
|
// reaches it, even when the provider returned successfully.
|
|
57415
|
-
throw new SandboxWorkspaceMutationOutputRejectedError(
|
|
57416
|
-
|
|
57417
|
-
|
|
57418
|
-
|
|
57419
|
-
|
|
57420
|
-
|
|
57421
|
-
|
|
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
|
+
);
|
|
57422
57610
|
}
|
|
57423
57611
|
throw new SandboxWorkspaceMutationFencedError(settlement.failure, settlement.detail);
|
|
57424
57612
|
}
|
|
@@ -85980,20 +86168,12 @@ export async function mutateAndAppendSessionEventsForTurnAttempt(
|
|
|
85980
86168
|
workspaceId,
|
|
85981
86169
|
activityGateOpen,
|
|
85982
86170
|
async (tx) => {
|
|
85983
|
-
|
|
85984
|
-
|
|
85985
|
-
|
|
85986
|
-
|
|
85987
|
-
|
|
85988
|
-
)
|
|
85989
|
-
if (!mutationApplied) {
|
|
85990
|
-
return {
|
|
85991
|
-
events: [],
|
|
85992
|
-
accepted: false,
|
|
85993
|
-
mutationApplied: false,
|
|
85994
|
-
canonicalStartupMilestones: [],
|
|
85995
|
-
};
|
|
85996
|
-
}
|
|
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).
|
|
85997
86177
|
const fence = await runSessionEventAppendPhase(
|
|
85998
86178
|
observer,
|
|
85999
86179
|
{ ...phaseInput, phase: "turn_attempt_fence" },
|
|
@@ -86014,6 +86194,20 @@ export async function mutateAndAppendSessionEventsForTurnAttempt(
|
|
|
86014
86194
|
if (!activityGateOpen && !fence.allowed) {
|
|
86015
86195
|
throw new SessionActivityGateEscalation();
|
|
86016
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
|
+
}
|
|
86017
86211
|
let sequence = session.lastSequence;
|
|
86018
86212
|
const now = new Date();
|
|
86019
86213
|
const usageSourceKey = (input: AppendEventInput): string | null => {
|
|
@@ -88254,6 +88448,7 @@ export * from "./codemode-approvals";
|
|
|
88254
88448
|
export * from "./tool-action-reviews";
|
|
88255
88449
|
export * from "./browser-sessions";
|
|
88256
88450
|
export * from "./browser-deadline-checkpoints";
|
|
88451
|
+
export * from "./sandbox-interaction-idle";
|
|
88257
88452
|
export * from "./computer-sessions";
|
|
88258
88453
|
export * from "./browser-identities";
|
|
88259
88454
|
export * from "./browser-state-artifacts";
|