@opengeni/db 6.0.1 → 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-sessions.d.ts +26 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/{chunk-4RDUZ5UV.js → chunk-4ZQFT5II.js} +2 -2
- package/dist/{chunk-6ORJPCYW.js → chunk-5UOWNKPP.js} +2 -2
- package/dist/{chunk-4CCFFLDZ.js → chunk-BG46J5HF.js} +13 -2
- package/dist/chunk-BG46J5HF.js.map +1 -0
- package/dist/{chunk-CSVHCF3R.js → chunk-IB3SW3JW.js} +11 -4
- package/dist/{chunk-CSVHCF3R.js.map → chunk-IB3SW3JW.js.map} +1 -1
- package/dist/{chunk-GSB4ISHE.js → chunk-IIPWNNVR.js} +4 -1
- package/dist/chunk-IIPWNNVR.js.map +1 -0
- package/dist/{chunk-MAMBI2HR.js → chunk-JPYN5XKV.js} +39 -3
- package/dist/chunk-JPYN5XKV.js.map +1 -0
- package/dist/{chunk-LSLNMENV.js → chunk-KZXWUXWV.js} +4 -4
- package/dist/{chunk-LO6BVXXH.js → chunk-LD52IFS3.js} +2 -2
- package/dist/{chunk-JILAWE26.js → chunk-M5Z6II2P.js} +2 -2
- package/dist/{chunk-PGIFHEBA.js → chunk-RVEOGRMF.js} +2 -2
- package/dist/{chunk-H7BC444C.js → chunk-SRAMNVHT.js} +17 -1
- package/dist/chunk-SRAMNVHT.js.map +1 -0
- package/dist/{chunk-XK6HXON2.js → chunk-UXFBXCHM.js} +2 -2
- package/dist/{chunk-RXH5CFG3.js → chunk-VE2XK2KD.js} +12 -8
- package/dist/{chunk-RXH5CFG3.js.map → chunk-VE2XK2KD.js.map} +1 -1
- package/dist/{chunk-FULLE5CF.js → chunk-WT7HG43Y.js} +3 -3
- package/dist/{chunk-I7HQL6FK.js → chunk-ZM5BA3TD.js} +3 -3
- package/dist/connect-attempts.js +3 -3
- package/dist/database.d.ts +8 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +69 -20
- package/dist/index.js +633 -202
- package/dist/index.js.map +1 -1
- package/dist/knowledge-entries.d.ts +2 -0
- package/dist/knowledge-indexing.d.ts +17 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-user-setup.d.ts +1 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.d.ts +10 -1
- package/dist/migrate.js +3 -1
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-membership-lifecycle.d.ts +1 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +4 -2
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/schema.d.ts +25 -8
- 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 +4 -4
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0508_deadline_command_workspace_capture.sql +75 -0
- package/drizzle/0509_verified_signup_trial_credits.sql +76 -0
- package/drizzle/0510_knowledge_index_funding_wait.sql +174 -0
- package/drizzle/0511_knowledge_visible_index_status.sql +56 -0
- package/drizzle/0512_insights_fact_reads_hash_join.sql +31 -0
- package/drizzle/0513_message_fork_prefix_compaction.sql +85 -0
- package/drizzle/0514_failed_session_variable_set_attach.sql +25 -0
- package/package.json +5 -5
- package/src/browser-sessions.ts +71 -1
- package/src/database.ts +18 -0
- package/src/index.ts +706 -252
- package/src/insights.ts +44 -36
- package/src/knowledge-entries.ts +43 -2
- package/src/knowledge-indexing.ts +79 -1
- package/src/managed-user-setup.ts +18 -1
- package/src/migrate.ts +58 -1
- package/src/organization-membership-lifecycle.ts +11 -0
- package/src/provision-roles.ts +12 -0
- package/src/retained-provider-commands.ts +16 -6
- package/src/runtime-posture.ts +4 -0
- package/src/schema.ts +3 -0
- package/dist/chunk-4CCFFLDZ.js.map +0 -1
- package/dist/chunk-GSB4ISHE.js.map +0 -1
- package/dist/chunk-H7BC444C.js.map +0 -1
- package/dist/chunk-MAMBI2HR.js.map +0 -1
- /package/dist/{chunk-4RDUZ5UV.js.map → chunk-4ZQFT5II.js.map} +0 -0
- /package/dist/{chunk-6ORJPCYW.js.map → chunk-5UOWNKPP.js.map} +0 -0
- /package/dist/{chunk-LSLNMENV.js.map → chunk-KZXWUXWV.js.map} +0 -0
- /package/dist/{chunk-LO6BVXXH.js.map → chunk-LD52IFS3.js.map} +0 -0
- /package/dist/{chunk-JILAWE26.js.map → chunk-M5Z6II2P.js.map} +0 -0
- /package/dist/{chunk-PGIFHEBA.js.map → chunk-RVEOGRMF.js.map} +0 -0
- /package/dist/{chunk-XK6HXON2.js.map → chunk-UXFBXCHM.js.map} +0 -0
- /package/dist/{chunk-FULLE5CF.js.map → chunk-WT7HG43Y.js.map} +0 -0
- /package/dist/{chunk-I7HQL6FK.js.map → chunk-ZM5BA3TD.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
retainedProviderCommandPersistence
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VE2XK2KD.js";
|
|
4
4
|
import {
|
|
5
5
|
readSessionMessageSliceInScope
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WT7HG43Y.js";
|
|
7
7
|
import {
|
|
8
8
|
FORCE_RLS_TABLES,
|
|
9
9
|
NON_RLS_RUNTIME_TABLES,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
isRetryableRuntimeDatabaseStartupError,
|
|
29
29
|
provisionRoles,
|
|
30
30
|
runtimeDatabaseReadyCheck
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-SRAMNVHT.js";
|
|
32
32
|
import {
|
|
33
33
|
CanonicalHumanIdentityAuthorityError,
|
|
34
34
|
CanonicalHumanIdentityConflictError,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
getCanonicalHumanIdentityProjection,
|
|
43
43
|
synchronizeCanonicalHumanLoginBindings,
|
|
44
44
|
validateCanonicalHumanSession
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-RVEOGRMF.js";
|
|
46
46
|
import {
|
|
47
47
|
EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
|
|
48
48
|
EDITABLE_ARTIFACT_KERNEL_TAIL_MAX_BYTES,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
listEditableArtifactIdsForSession,
|
|
55
55
|
touchEditableArtifactSessionLink,
|
|
56
56
|
transactionallyAuthorizeEditableArtifactActor
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-5UOWNKPP.js";
|
|
58
58
|
import {
|
|
59
59
|
SessionTenancyAccessError,
|
|
60
60
|
SessionTenancyConflictError,
|
|
@@ -71,8 +71,8 @@ import {
|
|
|
71
71
|
replayAppliedSessionFork,
|
|
72
72
|
sessionTenancyProductActivated,
|
|
73
73
|
transitionSessionVisibility
|
|
74
|
-
} from "./chunk-
|
|
75
|
-
import "./chunk-
|
|
74
|
+
} from "./chunk-M5Z6II2P.js";
|
|
75
|
+
import "./chunk-KZXWUXWV.js";
|
|
76
76
|
import {
|
|
77
77
|
ConnectAttemptConflictError,
|
|
78
78
|
ConnectAttemptNotFoundError,
|
|
@@ -82,11 +82,11 @@ import {
|
|
|
82
82
|
getConnectAttempt,
|
|
83
83
|
getConnectBeginReplay,
|
|
84
84
|
listPendingConnectAttempts
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-UXFBXCHM.js";
|
|
86
86
|
import {
|
|
87
87
|
withOrganizationIntegrationAcquisition,
|
|
88
88
|
withOrganizationIntegrationPolicyFence
|
|
89
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-LD52IFS3.js";
|
|
90
90
|
import {
|
|
91
91
|
AgentCommandAuthorityError,
|
|
92
92
|
CHILD_LIFECYCLE_DEDUPE_PREFIX,
|
|
@@ -220,7 +220,7 @@ import {
|
|
|
220
220
|
updateSessionCommandReceiptResult,
|
|
221
221
|
workspaceControlLockBudget,
|
|
222
222
|
workspaceControlRequestLockTimeoutMs
|
|
223
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-IB3SW3JW.js";
|
|
224
224
|
import {
|
|
225
225
|
ACTIVE_VIDEO_GENERATION_STATUSES,
|
|
226
226
|
VideoGenerationCapacityError,
|
|
@@ -252,7 +252,7 @@ import {
|
|
|
252
252
|
settleVideoGenerationFailure,
|
|
253
253
|
settleVideoGenerationReady,
|
|
254
254
|
updateWorkspaceVideoGenerationPolicy
|
|
255
|
-
} from "./chunk-
|
|
255
|
+
} from "./chunk-ZM5BA3TD.js";
|
|
256
256
|
import {
|
|
257
257
|
external_exports
|
|
258
258
|
} from "./chunk-4UJCXBR2.js";
|
|
@@ -278,12 +278,13 @@ import {
|
|
|
278
278
|
reapManagedAuthIsolatedSessions,
|
|
279
279
|
releaseManagedAuthActorMutationLease,
|
|
280
280
|
validateManagedAuthActorMutationLease
|
|
281
|
-
} from "./chunk-
|
|
281
|
+
} from "./chunk-4ZQFT5II.js";
|
|
282
282
|
import {
|
|
283
283
|
SandboxProviderReadLockUnavailableError,
|
|
284
284
|
SessionEventPersistenceError,
|
|
285
285
|
createDb,
|
|
286
286
|
currentSessionAttachmentReadAccess,
|
|
287
|
+
currentSessionRlsActorIdentityKey,
|
|
287
288
|
databaseFailureCode,
|
|
288
289
|
dbBindingFor,
|
|
289
290
|
isDatabasePersistenceFailure,
|
|
@@ -316,7 +317,7 @@ import {
|
|
|
316
317
|
withWorkspaceSubjectRls,
|
|
317
318
|
withWorkspaceSubjectSessionActivityRls,
|
|
318
319
|
withWorkspaceUsageLock
|
|
319
|
-
} from "./chunk-
|
|
320
|
+
} from "./chunk-BG46J5HF.js";
|
|
320
321
|
import {
|
|
321
322
|
agentRunStates,
|
|
322
323
|
apiKeys,
|
|
@@ -552,11 +553,11 @@ import {
|
|
|
552
553
|
xaiRotationSettings,
|
|
553
554
|
xaiSessionAccountPins,
|
|
554
555
|
xaiSubscriptionCredentials
|
|
555
|
-
} from "./chunk-
|
|
556
|
+
} from "./chunk-IIPWNNVR.js";
|
|
556
557
|
import {
|
|
557
558
|
migrate,
|
|
558
559
|
runMigrations
|
|
559
|
-
} from "./chunk-
|
|
560
|
+
} from "./chunk-JPYN5XKV.js";
|
|
560
561
|
import {
|
|
561
562
|
LOSSLESS_CONTENT_CODEC_VERSION,
|
|
562
563
|
LOSSLESS_JSON_STRING_PREFIX,
|
|
@@ -8407,6 +8408,7 @@ import {
|
|
|
8407
8408
|
import {
|
|
8408
8409
|
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2,
|
|
8409
8410
|
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS as SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS2,
|
|
8411
|
+
SANDBOX_DEADLINE_COMMAND_STOP_GRACE_MS,
|
|
8410
8412
|
WORKSPACE_OPENROUTER_CONNECTION_DOMAIN as WORKSPACE_OPENROUTER_CONNECTION_DOMAIN2,
|
|
8411
8413
|
WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
8412
8414
|
VERCEL_AI_GATEWAY_CONNECTION_DOMAIN as VERCEL_AI_GATEWAY_CONNECTION_DOMAIN2,
|
|
@@ -24422,10 +24424,23 @@ async function completeSelfServiceOrganizationSetup(db, input) {
|
|
|
24422
24424
|
return await db.transaction(async (tx) => {
|
|
24423
24425
|
const txDb = tx;
|
|
24424
24426
|
await setSubjectRlsContext(txDb, input.actorSubjectId);
|
|
24427
|
+
await rawRows(
|
|
24428
|
+
txDb,
|
|
24429
|
+
sql47`select pg_catalog.set_config(
|
|
24430
|
+
'opengeni.verified_signup_trial_enabled',
|
|
24431
|
+
${input.trialCreditsEnabled === true ? "on" : "off"}, true
|
|
24432
|
+
)`
|
|
24433
|
+
);
|
|
24425
24434
|
const [row] = await rawRows(
|
|
24426
24435
|
txDb,
|
|
24427
24436
|
sql47`select complete_self_service_organization_setup(
|
|
24428
|
-
${JSON.stringify(
|
|
24437
|
+
${JSON.stringify({
|
|
24438
|
+
authUserId: input.authUserId,
|
|
24439
|
+
actorSubjectId: input.actorSubjectId,
|
|
24440
|
+
organizationName: input.organizationName,
|
|
24441
|
+
operationId: input.operationId,
|
|
24442
|
+
requestFingerprint: input.requestFingerprint
|
|
24443
|
+
})}::jsonb
|
|
24429
24444
|
) as result`
|
|
24430
24445
|
);
|
|
24431
24446
|
return CompleteSelfServiceOrganizationSetupResponse.parse(row?.result);
|
|
@@ -29496,36 +29511,36 @@ async function countScheduledTaskFires(db, input) {
|
|
|
29496
29511
|
async function aggregateSessionDepth(db, workspaceId) {
|
|
29497
29512
|
const context = await rlsContextForWorkspace(db, workspaceId);
|
|
29498
29513
|
return await withRlsContext(db, context, async (scopedDb) => {
|
|
29499
|
-
const buckets = await scopedDb.select({
|
|
29514
|
+
const buckets = (await scopedDb.select({
|
|
29500
29515
|
depth: sessions.nestedAgentDepth,
|
|
29501
29516
|
sessions: sql59`count(*)::int`
|
|
29502
|
-
}).from(sessions).where(eq41(sessions.workspaceId, workspaceId)).groupBy(sessions.nestedAgentDepth).orderBy(sessions.nestedAgentDepth);
|
|
29503
|
-
const
|
|
29504
|
-
|
|
29505
|
-
|
|
29506
|
-
|
|
29507
|
-
|
|
29508
|
-
}).from(sessions).where(eq41(sessions.workspaceId, workspaceId));
|
|
29509
|
-
const [deepest] = await scopedDb.select({
|
|
29517
|
+
}).from(sessions).where(eq41(sessions.workspaceId, workspaceId)).groupBy(sessions.nestedAgentDepth).orderBy(sessions.nestedAgentDepth)).map((row) => ({ depth: row.depth, sessions: Number(row.sessions) }));
|
|
29518
|
+
const sessionsTouched = buckets.reduce((sum, bucket) => sum + bucket.sessions, 0);
|
|
29519
|
+
const rootSessions = buckets.find((bucket) => bucket.depth === 0)?.sessions ?? 0;
|
|
29520
|
+
const deepestDepth = buckets.reduce((max, bucket) => Math.max(max, bucket.depth), 0);
|
|
29521
|
+
const avgDepth = sessionsTouched > 0 ? buckets.reduce((sum, bucket) => sum + bucket.depth * bucket.sessions, 0) / sessionsTouched : 0;
|
|
29522
|
+
const [deepest] = sessionsTouched > 0 ? await scopedDb.select({
|
|
29510
29523
|
id: sessions.id,
|
|
29511
29524
|
title: sessions.title,
|
|
29512
29525
|
depth: sessions.nestedAgentDepth
|
|
29513
|
-
}).from(sessions).where(
|
|
29526
|
+
}).from(sessions).where(
|
|
29527
|
+
and40(
|
|
29528
|
+
eq41(sessions.workspaceId, workspaceId),
|
|
29529
|
+
eq41(sessions.nestedAgentDepth, deepestDepth)
|
|
29530
|
+
)
|
|
29531
|
+
).orderBy(desc13(sessions.updatedAt)).limit(1) : [];
|
|
29514
29532
|
const [goals] = await scopedDb.select({
|
|
29515
29533
|
active: sql59`count(*) filter (where ${sessionGoals.status} = 'active')::int`,
|
|
29516
29534
|
completed: sql59`count(*) filter (where ${sessionGoals.status} = 'completed')::int`
|
|
29517
29535
|
}).from(sessionGoals).where(eq41(sessionGoals.workspaceId, workspaceId));
|
|
29518
29536
|
return {
|
|
29519
|
-
buckets
|
|
29520
|
-
|
|
29521
|
-
|
|
29522
|
-
|
|
29523
|
-
sessionsTouched: Number(stats?.sessionsTouched ?? 0),
|
|
29524
|
-
rootSessions: Number(stats?.rootSessions ?? 0),
|
|
29525
|
-
deepestDepth: Number(stats?.deepestDepth ?? 0),
|
|
29537
|
+
buckets,
|
|
29538
|
+
sessionsTouched,
|
|
29539
|
+
rootSessions,
|
|
29540
|
+
deepestDepth,
|
|
29526
29541
|
deepestSessionId: deepest?.id ?? null,
|
|
29527
29542
|
deepestSessionTitle: deepest?.title?.trim() || "",
|
|
29528
|
-
avgDepth
|
|
29543
|
+
avgDepth,
|
|
29529
29544
|
goalsActive: Number(goals?.active ?? 0),
|
|
29530
29545
|
goalsCompleted: Number(goals?.completed ?? 0)
|
|
29531
29546
|
};
|
|
@@ -36849,6 +36864,9 @@ function associationFromRow(row) {
|
|
|
36849
36864
|
lastUsedAt: iso13(row.lastUsedAt)
|
|
36850
36865
|
};
|
|
36851
36866
|
}
|
|
36867
|
+
function readStoredBrowserCapabilities(value) {
|
|
36868
|
+
return BrowserSessionCapabilities.parse({ permissions: false, ...value });
|
|
36869
|
+
}
|
|
36852
36870
|
function browserSessionFromRows(row, associations) {
|
|
36853
36871
|
return BrowserSession.parse({
|
|
36854
36872
|
id: row.id,
|
|
@@ -36866,7 +36884,7 @@ function browserSessionFromRows(row, associations) {
|
|
|
36866
36884
|
baseRevisionId: row.baseRevisionId,
|
|
36867
36885
|
networkRouteId: row.networkRouteId,
|
|
36868
36886
|
linkedComputerSessionId: row.linkedComputerSessionId,
|
|
36869
|
-
capabilities: row.capabilities,
|
|
36887
|
+
capabilities: readStoredBrowserCapabilities(row.capabilities),
|
|
36870
36888
|
associations: associations.map(associationFromRow),
|
|
36871
36889
|
createdBySubjectId: row.createdBySubjectId,
|
|
36872
36890
|
createdAt: iso13(row.createdAt),
|
|
@@ -37681,6 +37699,50 @@ async function prepareBrowserSessionSuspend(db, input) {
|
|
|
37681
37699
|
}
|
|
37682
37700
|
});
|
|
37683
37701
|
}
|
|
37702
|
+
async function failPreparedBrowserSessionSuspend(db, input) {
|
|
37703
|
+
const error = InteractionError.parse(input.error);
|
|
37704
|
+
return await withRlsContext(
|
|
37705
|
+
db,
|
|
37706
|
+
input,
|
|
37707
|
+
async (scopedDb) => await scopedDb.transaction(async (txRaw) => {
|
|
37708
|
+
const tx = txRaw;
|
|
37709
|
+
await lockOperation3(tx, input.workspaceId, input.operationId);
|
|
37710
|
+
const operation = await loadOperation2(tx, input.workspaceId, input.operationId);
|
|
37711
|
+
assertOperationResource(operation, input.browserSessionId, "suspend");
|
|
37712
|
+
if (operation.state !== "prepared" || operation.dispatchedAt) return null;
|
|
37713
|
+
await lockBrowserSession(tx, input.workspaceId, input.browserSessionId);
|
|
37714
|
+
const now = /* @__PURE__ */ new Date();
|
|
37715
|
+
const [sessionRow] = await tx.update(browserSessions).set({ lifecycle: "active", failureCode: null, updatedAt: now }).where(
|
|
37716
|
+
and50(
|
|
37717
|
+
eq51(browserSessions.workspaceId, input.workspaceId),
|
|
37718
|
+
eq51(browserSessions.id, input.browserSessionId),
|
|
37719
|
+
eq51(browserSessions.lifecycle, "suspending")
|
|
37720
|
+
)
|
|
37721
|
+
).returning();
|
|
37722
|
+
if (!sessionRow) {
|
|
37723
|
+
throw new BrowserSessionStateError("Prepared BrowserSession suspension changed state");
|
|
37724
|
+
}
|
|
37725
|
+
const [operationRow] = await tx.update(interactionOperations).set({
|
|
37726
|
+
state: "failed",
|
|
37727
|
+
errorCode: error.code,
|
|
37728
|
+
errorMessage: error.message,
|
|
37729
|
+
errorRetryable: error.retryable,
|
|
37730
|
+
errorDetails: error.details ?? null,
|
|
37731
|
+
settledAt: now,
|
|
37732
|
+
updatedAt: now
|
|
37733
|
+
}).where(eq51(interactionOperations.operationId, input.operationId)).returning();
|
|
37734
|
+
if (!operationRow) throw new Error("BrowserSession failure receipt was lost");
|
|
37735
|
+
const associations = await loadAssociations(tx, input.workspaceId, [
|
|
37736
|
+
input.browserSessionId
|
|
37737
|
+
]);
|
|
37738
|
+
await advanceWorkspaceInteractionRevision(tx, input.accountId, input.workspaceId);
|
|
37739
|
+
return BrowserSessionMutationResponse.parse({
|
|
37740
|
+
session: browserSessionFromRows(sessionRow, associations),
|
|
37741
|
+
operation: operationReceipt(operationRow, false)
|
|
37742
|
+
});
|
|
37743
|
+
})
|
|
37744
|
+
);
|
|
37745
|
+
}
|
|
37684
37746
|
async function prepareBrowserSessionResume(db, input) {
|
|
37685
37747
|
return await prepareBrowserSessionLifecycleTransition(db, input, {
|
|
37686
37748
|
kind: "resume",
|
|
@@ -42164,6 +42226,7 @@ import {
|
|
|
42164
42226
|
KnowledgeEntryRestoreRequest,
|
|
42165
42227
|
KnowledgeEntrySaveRequest,
|
|
42166
42228
|
KnowledgeEntrySummary,
|
|
42229
|
+
KnowledgeIndexStatus,
|
|
42167
42230
|
KnowledgeEntryWriteReceipt
|
|
42168
42231
|
} from "@opengeni/contracts";
|
|
42169
42232
|
import { sql as sql84 } from "drizzle-orm";
|
|
@@ -42351,6 +42414,27 @@ function decodeProjection(value, full) {
|
|
|
42351
42414
|
}
|
|
42352
42415
|
});
|
|
42353
42416
|
}
|
|
42417
|
+
async function withIndexStatus(db, context, entries, view) {
|
|
42418
|
+
if (view === "archived" || view === "rejected") return entries;
|
|
42419
|
+
const items = entries.filter(
|
|
42420
|
+
(entry) => ("entry" in entry.revision ? entry.revision.entry.kind : entry.revision.kind) === "source"
|
|
42421
|
+
).map((entry) => ({ entryId: entry.id, revisionId: entry.revision.id }));
|
|
42422
|
+
if (!items.length) return entries;
|
|
42423
|
+
const statuses = await inKnowledgeContext(db, context, async (tx) => {
|
|
42424
|
+
const [row] = await rawRows(
|
|
42425
|
+
tx,
|
|
42426
|
+
sql84`SELECT knowledge_visible_index_status(${context.accountId}::uuid,
|
|
42427
|
+
${context.workspaceId}::uuid,${JSON.stringify(context.actor)}::jsonb,
|
|
42428
|
+
${JSON.stringify(items)}::jsonb,${view}) AS result`
|
|
42429
|
+
);
|
|
42430
|
+
return external_exports.array(external_exports.object({ entryId: external_exports.uuid(), revisionId: external_exports.uuid(), status: KnowledgeIndexStatus })).parse(row?.result);
|
|
42431
|
+
});
|
|
42432
|
+
const byRevision = new Map(statuses.map((item) => [item.revisionId, item.status]));
|
|
42433
|
+
return entries.map((entry) => {
|
|
42434
|
+
const indexStatus = byRevision.get(entry.revision.id);
|
|
42435
|
+
return indexStatus ? { ...entry, indexStatus } : entry;
|
|
42436
|
+
});
|
|
42437
|
+
}
|
|
42354
42438
|
async function read(db, context, request) {
|
|
42355
42439
|
return inKnowledgeContext(db, context, async (tx) => {
|
|
42356
42440
|
const [row] = await rawRows(
|
|
@@ -42369,7 +42453,9 @@ async function getKnowledgeEntry(db, context, entryId, options = {}) {
|
|
|
42369
42453
|
view: external_exports.enum(["published", "needs_review", "archived", "rejected"]).optional()
|
|
42370
42454
|
}).strict().parse({ entryId, ...options });
|
|
42371
42455
|
const rows = await read(db, context, { ...request, operation: "get", limit: 1 });
|
|
42372
|
-
|
|
42456
|
+
if (!rows[0]) return null;
|
|
42457
|
+
const record4 = decodeProjection(rows[0], true);
|
|
42458
|
+
return (await withIndexStatus(db, context, [record4], request.view ?? "published"))[0];
|
|
42373
42459
|
}
|
|
42374
42460
|
var Cursor2 = external_exports.object({
|
|
42375
42461
|
v: external_exports.literal(2),
|
|
@@ -42402,7 +42488,12 @@ async function listKnowledgeEntries(db, context, input = {}, embedding) {
|
|
|
42402
42488
|
embeddingModel: embedding.model
|
|
42403
42489
|
} : {}
|
|
42404
42490
|
});
|
|
42405
|
-
const entries =
|
|
42491
|
+
const entries = await withIndexStatus(
|
|
42492
|
+
db,
|
|
42493
|
+
context,
|
|
42494
|
+
rows.slice(0, request.limit).map((row) => decodeProjection(row, false)),
|
|
42495
|
+
request.view
|
|
42496
|
+
);
|
|
42406
42497
|
return {
|
|
42407
42498
|
entries,
|
|
42408
42499
|
nextCursor: rows.length > request.limit && entries.length ? Buffer.from(
|
|
@@ -42719,6 +42810,37 @@ var Claim = external_exports.object({
|
|
|
42719
42810
|
generation: external_exports.number().int().positive(),
|
|
42720
42811
|
nextIndex: external_exports.number().int().nonnegative()
|
|
42721
42812
|
});
|
|
42813
|
+
async function knowledgeIndexBillingActivationTime(db) {
|
|
42814
|
+
const [row] = await rawRows(
|
|
42815
|
+
db,
|
|
42816
|
+
sql85`SELECT clock_timestamp() AS "activatedAt"`
|
|
42817
|
+
);
|
|
42818
|
+
return external_exports.coerce.date().parse(row?.activatedAt);
|
|
42819
|
+
}
|
|
42820
|
+
async function freezeKnowledgeIndexBillingMode(db, raw2, requested, activatedAt, rateMicrosPerMillionBytes) {
|
|
42821
|
+
const claim = Claim.parse(raw2);
|
|
42822
|
+
return withRlsContext(db, { accountId: claim.accountId }, async (tx) => {
|
|
42823
|
+
const [row] = await rawRows(
|
|
42824
|
+
tx,
|
|
42825
|
+
sql85`SELECT knowledge_index_billing_policy(${claim.accountId}::uuid,
|
|
42826
|
+
${claim.revisionId}::uuid,${claim.leaseId}::uuid,${requested},
|
|
42827
|
+
${activatedAt.toISOString()}::timestamptz,
|
|
42828
|
+
${rateMicrosPerMillionBytes}::bigint) AS policy`
|
|
42829
|
+
);
|
|
42830
|
+
return external_exports.object({
|
|
42831
|
+
mode: external_exports.enum(["usage_only", "shadow", "credits", "awaiting_review", "obsolete"]),
|
|
42832
|
+
rateMicrosPerMillionBytes: external_exports.number().int().nonnegative()
|
|
42833
|
+
}).parse(row?.policy);
|
|
42834
|
+
});
|
|
42835
|
+
}
|
|
42836
|
+
async function withKnowledgeQueryAccountLock(db, accountId, workspaceId, fn) {
|
|
42837
|
+
return withWorkspaceUsageLock(db, workspaceId, async (tx) => {
|
|
42838
|
+
await tx.execute(
|
|
42839
|
+
sql85`SELECT pg_advisory_xact_lock(hashtext(${`knowledge-query-account:${accountId}`}))`
|
|
42840
|
+
);
|
|
42841
|
+
return fn(tx);
|
|
42842
|
+
});
|
|
42843
|
+
}
|
|
42722
42844
|
async function claimKnowledgeIndexJobs(db, input) {
|
|
42723
42845
|
const request = external_exports.object({
|
|
42724
42846
|
model: Claim.shape.model,
|
|
@@ -42785,6 +42907,28 @@ async function completeKnowledgeIndexJob(db, claim, expectedNextIndex) {
|
|
|
42785
42907
|
async function deferKnowledgeIndexJob(db, claim) {
|
|
42786
42908
|
return work(db, claim, { operation: "fail" });
|
|
42787
42909
|
}
|
|
42910
|
+
async function waitKnowledgeIndexForFunding(db, raw2) {
|
|
42911
|
+
const claim = Claim.parse(raw2);
|
|
42912
|
+
return withRlsContext(db, { accountId: claim.accountId }, async (tx) => {
|
|
42913
|
+
const [row] = await rawRows(
|
|
42914
|
+
tx,
|
|
42915
|
+
sql85`SELECT knowledge_index_wait_for_funding(${claim.accountId}::uuid,
|
|
42916
|
+
${claim.revisionId}::uuid,${claim.leaseId}::uuid) AS result`
|
|
42917
|
+
);
|
|
42918
|
+
return external_exports.object({ status: external_exports.enum(["pending", "obsolete"]) }).parse(row?.result);
|
|
42919
|
+
});
|
|
42920
|
+
}
|
|
42921
|
+
async function guardPaidKnowledgeIndexPublication(db, raw2) {
|
|
42922
|
+
const claim = Claim.parse(raw2);
|
|
42923
|
+
return withRlsContext(db, { accountId: claim.accountId }, async (tx) => {
|
|
42924
|
+
const [row] = await rawRows(
|
|
42925
|
+
tx,
|
|
42926
|
+
sql85`SELECT knowledge_index_paid_publication_guard(${claim.accountId}::uuid,
|
|
42927
|
+
${claim.revisionId}::uuid,${claim.leaseId}::uuid) AS status`
|
|
42928
|
+
);
|
|
42929
|
+
return external_exports.enum(["published", "awaiting_review", "obsolete"]).parse(row?.status);
|
|
42930
|
+
});
|
|
42931
|
+
}
|
|
42788
42932
|
async function continueKnowledgeIndexJob(db, claim) {
|
|
42789
42933
|
return work(db, claim, { operation: "continue" });
|
|
42790
42934
|
}
|
|
@@ -45702,6 +45846,47 @@ async function applyCreditDebitUpToBalance(db, input) {
|
|
|
45702
45846
|
}
|
|
45703
45847
|
);
|
|
45704
45848
|
}
|
|
45849
|
+
async function applyCreditDebitAfterUse(db, input) {
|
|
45850
|
+
if (!Number.isSafeInteger(input.amountMicros) || input.amountMicros <= 0) {
|
|
45851
|
+
throw new Error("post-use credit debit requires a positive, safe integer micro amount");
|
|
45852
|
+
}
|
|
45853
|
+
return await withRlsContext(
|
|
45854
|
+
db,
|
|
45855
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId ?? null },
|
|
45856
|
+
async (scopedDb) => {
|
|
45857
|
+
const inserted = await scopedDb.insert(creditLedgerEntries).values({
|
|
45858
|
+
accountId: input.accountId,
|
|
45859
|
+
workspaceId: input.workspaceId ?? null,
|
|
45860
|
+
type: input.type,
|
|
45861
|
+
amountMicros: -input.amountMicros,
|
|
45862
|
+
sourceType: input.sourceType,
|
|
45863
|
+
sourceId: input.sourceId,
|
|
45864
|
+
idempotencyKey: input.idempotencyKey,
|
|
45865
|
+
metadata: input.metadata ?? {},
|
|
45866
|
+
occurredAt: input.occurredAt ?? /* @__PURE__ */ new Date()
|
|
45867
|
+
}).onConflictDoNothing({ target: creditLedgerEntries.idempotencyKey }).returning({ id: creditLedgerEntries.id });
|
|
45868
|
+
if (inserted.length === 0) {
|
|
45869
|
+
const [prior] = await scopedDb.select({
|
|
45870
|
+
accountId: creditLedgerEntries.accountId,
|
|
45871
|
+
workspaceId: creditLedgerEntries.workspaceId,
|
|
45872
|
+
type: creditLedgerEntries.type,
|
|
45873
|
+
amountMicros: creditLedgerEntries.amountMicros,
|
|
45874
|
+
sourceType: creditLedgerEntries.sourceType,
|
|
45875
|
+
sourceId: creditLedgerEntries.sourceId
|
|
45876
|
+
}).from(creditLedgerEntries).where(eq59(creditLedgerEntries.idempotencyKey, input.idempotencyKey));
|
|
45877
|
+
if (!prior || prior.accountId !== input.accountId || prior.workspaceId !== (input.workspaceId ?? null) || prior.type !== input.type || prior.amountMicros !== -input.amountMicros || prior.sourceType !== input.sourceType || prior.sourceId !== input.sourceId) {
|
|
45878
|
+
throw new Error(
|
|
45879
|
+
"post-use credit debit idempotency key conflicts with a different charge"
|
|
45880
|
+
);
|
|
45881
|
+
}
|
|
45882
|
+
}
|
|
45883
|
+
return {
|
|
45884
|
+
balance: await getBillingBalance(scopedDb, input.accountId),
|
|
45885
|
+
debitedMicros: inserted.length ? input.amountMicros : 0
|
|
45886
|
+
};
|
|
45887
|
+
}
|
|
45888
|
+
);
|
|
45889
|
+
}
|
|
45705
45890
|
async function hasCreditLedgerEntry(db, accountId, idempotencyKey) {
|
|
45706
45891
|
return await withAccountRls(db, accountId, async (scopedDb) => {
|
|
45707
45892
|
const [row] = await scopedDb.select({ id: creditLedgerEntries.id }).from(creditLedgerEntries).where(
|
|
@@ -46420,6 +46605,24 @@ async function getRetainedScreenshotArtifact(db, workspaceId, sessionId, artifac
|
|
|
46420
46605
|
return row ? mapRetainedScreenshotArtifact(row.artifact, row.file) : null;
|
|
46421
46606
|
});
|
|
46422
46607
|
}
|
|
46608
|
+
async function getRetainedScreenshotArtifactForToolCall(db, workspaceId, sessionId, toolCallId) {
|
|
46609
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
46610
|
+
const rows = await scopedDb.select({ artifact: retainedScreenshotArtifacts, file: files }).from(retainedScreenshotArtifacts).innerJoin(
|
|
46611
|
+
files,
|
|
46612
|
+
and58(
|
|
46613
|
+
eq59(files.workspaceId, retainedScreenshotArtifacts.workspaceId),
|
|
46614
|
+
eq59(files.id, retainedScreenshotArtifacts.artifactId)
|
|
46615
|
+
)
|
|
46616
|
+
).where(
|
|
46617
|
+
and58(
|
|
46618
|
+
eq59(retainedScreenshotArtifacts.workspaceId, workspaceId),
|
|
46619
|
+
eq59(retainedScreenshotArtifacts.sessionId, sessionId),
|
|
46620
|
+
eq59(retainedScreenshotArtifacts.toolCallId, toolCallId)
|
|
46621
|
+
)
|
|
46622
|
+
).limit(2);
|
|
46623
|
+
return rows.length === 1 ? mapRetainedScreenshotArtifact(rows[0].artifact, rows[0].file) : null;
|
|
46624
|
+
});
|
|
46625
|
+
}
|
|
46423
46626
|
async function claimRetainedScreenshotMaintenance(db, input) {
|
|
46424
46627
|
const rows = await rawRows(
|
|
46425
46628
|
db,
|
|
@@ -67818,74 +68021,129 @@ function assertPendingToolOutputPolicyMatches(stored, supplied, callId) {
|
|
|
67818
68021
|
);
|
|
67819
68022
|
}
|
|
67820
68023
|
}
|
|
68024
|
+
function pendingToolCallItemsEqual(left, right) {
|
|
68025
|
+
if (left === right) return true;
|
|
68026
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object")
|
|
68027
|
+
return false;
|
|
68028
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
68029
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => pendingToolCallItemsEqual(value, right[index]));
|
|
68030
|
+
}
|
|
68031
|
+
const keys = Object.keys(left);
|
|
68032
|
+
return keys.length === Object.keys(right).length && keys.every(
|
|
68033
|
+
(key2) => Object.hasOwn(right, key2) && pendingToolCallItemsEqual(
|
|
68034
|
+
left[key2],
|
|
68035
|
+
right[key2]
|
|
68036
|
+
)
|
|
68037
|
+
);
|
|
68038
|
+
}
|
|
67821
68039
|
async function registerPendingSessionToolCall(db, input) {
|
|
67822
|
-
|
|
67823
|
-
|
|
67824
|
-
{
|
|
67825
|
-
|
|
67826
|
-
|
|
67827
|
-
workspaceId: input.workspaceId,
|
|
67828
|
-
|
|
67829
|
-
|
|
67830
|
-
executionGeneration: input.executionGeneration,
|
|
67831
|
-
attemptId: input.attemptId
|
|
67832
|
-
});
|
|
67833
|
-
if (!fence.allowed) return { accepted: false, registered: false };
|
|
67834
|
-
const inserted = await tx.insert(sessionPendingToolCalls).values(
|
|
67835
|
-
withLosslessContentWriteVersion(
|
|
67836
|
-
{
|
|
67837
|
-
accountId: input.accountId,
|
|
68040
|
+
const correlationId = crypto.randomUUID();
|
|
68041
|
+
const registerOnce = async (attempt) => {
|
|
68042
|
+
try {
|
|
68043
|
+
return await withRlsContext(
|
|
68044
|
+
db,
|
|
68045
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
68046
|
+
async (scopedDb) => await scopedDb.transaction(async (tx) => {
|
|
68047
|
+
const fence = await lockTurnAttemptWriteFenceTx(tx, {
|
|
67838
68048
|
workspaceId: input.workspaceId,
|
|
67839
68049
|
sessionId: input.sessionId,
|
|
67840
68050
|
turnId: input.turnId,
|
|
67841
68051
|
executionGeneration: input.executionGeneration,
|
|
67842
|
-
attemptId: input.attemptId
|
|
67843
|
-
|
|
67844
|
-
|
|
67845
|
-
|
|
67846
|
-
|
|
67847
|
-
|
|
67848
|
-
|
|
67849
|
-
|
|
67850
|
-
|
|
67851
|
-
|
|
67852
|
-
|
|
67853
|
-
|
|
67854
|
-
|
|
67855
|
-
|
|
67856
|
-
|
|
67857
|
-
|
|
67858
|
-
|
|
67859
|
-
|
|
67860
|
-
|
|
67861
|
-
|
|
67862
|
-
|
|
67863
|
-
|
|
67864
|
-
|
|
67865
|
-
|
|
67866
|
-
|
|
67867
|
-
|
|
67868
|
-
)
|
|
67869
|
-
|
|
67870
|
-
|
|
67871
|
-
|
|
67872
|
-
|
|
67873
|
-
|
|
67874
|
-
|
|
67875
|
-
|
|
67876
|
-
|
|
67877
|
-
|
|
67878
|
-
|
|
67879
|
-
|
|
67880
|
-
|
|
67881
|
-
|
|
67882
|
-
|
|
67883
|
-
|
|
67884
|
-
|
|
68052
|
+
attemptId: input.attemptId
|
|
68053
|
+
});
|
|
68054
|
+
if (!fence.allowed) return { accepted: false, registered: false };
|
|
68055
|
+
const inserted = await tx.insert(sessionPendingToolCalls).values(
|
|
68056
|
+
withLosslessContentWriteVersion(
|
|
68057
|
+
{
|
|
68058
|
+
accountId: input.accountId,
|
|
68059
|
+
workspaceId: input.workspaceId,
|
|
68060
|
+
sessionId: input.sessionId,
|
|
68061
|
+
turnId: input.turnId,
|
|
68062
|
+
executionGeneration: input.executionGeneration,
|
|
68063
|
+
attemptId: input.attemptId,
|
|
68064
|
+
callId: input.callId,
|
|
68065
|
+
callType: input.callType,
|
|
68066
|
+
callItem: input.callItem,
|
|
68067
|
+
modelToolOutputTruncationTokens: input.modelToolOutputTruncationTokens ?? null
|
|
68068
|
+
},
|
|
68069
|
+
"callItem",
|
|
68070
|
+
"callItemCodecVersion"
|
|
68071
|
+
)
|
|
68072
|
+
).onConflictDoNothing({
|
|
68073
|
+
target: [
|
|
68074
|
+
sessionPendingToolCalls.workspaceId,
|
|
68075
|
+
sessionPendingToolCalls.turnId,
|
|
68076
|
+
sessionPendingToolCalls.callId
|
|
68077
|
+
]
|
|
68078
|
+
}).returning({ id: sessionPendingToolCalls.id });
|
|
68079
|
+
if (inserted.length === 0) {
|
|
68080
|
+
const [pending] = await tx.select({
|
|
68081
|
+
id: sessionPendingToolCalls.id,
|
|
68082
|
+
accountId: sessionPendingToolCalls.accountId,
|
|
68083
|
+
callType: sessionPendingToolCalls.callType,
|
|
68084
|
+
callItem: sessionPendingToolCalls.callItem,
|
|
68085
|
+
callItemCodecVersion: sessionPendingToolCalls.callItemCodecVersion,
|
|
68086
|
+
modelToolOutputTruncationTokens: sessionPendingToolCalls.modelToolOutputTruncationTokens
|
|
68087
|
+
}).from(sessionPendingToolCalls).where(
|
|
68088
|
+
and58(
|
|
68089
|
+
eq59(sessionPendingToolCalls.workspaceId, input.workspaceId),
|
|
68090
|
+
eq59(sessionPendingToolCalls.sessionId, input.sessionId),
|
|
68091
|
+
eq59(sessionPendingToolCalls.turnId, input.turnId),
|
|
68092
|
+
eq59(sessionPendingToolCalls.callId, input.callId)
|
|
68093
|
+
)
|
|
68094
|
+
).for("update").limit(1);
|
|
68095
|
+
if (!pending || pending.accountId !== input.accountId || pending.callType !== input.callType || !pendingToolCallItemsEqual(
|
|
68096
|
+
fromPostgresLosslessJson(pending.callItem, pending.callItemCodecVersion),
|
|
68097
|
+
input.callItem
|
|
68098
|
+
)) {
|
|
68099
|
+
throw new Error("Pending tool receipt conflicts with the registered call");
|
|
68100
|
+
}
|
|
68101
|
+
assertPendingToolOutputPolicyMatches(
|
|
68102
|
+
pending.modelToolOutputTruncationTokens,
|
|
68103
|
+
input.modelToolOutputTruncationTokens,
|
|
68104
|
+
input.callId
|
|
68105
|
+
);
|
|
68106
|
+
if (pending.modelToolOutputTruncationTokens === null && input.modelToolOutputTruncationTokens !== void 0) {
|
|
68107
|
+
await tx.update(sessionPendingToolCalls).set({
|
|
68108
|
+
modelToolOutputTruncationTokens: input.modelToolOutputTruncationTokens
|
|
68109
|
+
}).where(eq59(sessionPendingToolCalls.id, pending.id));
|
|
68110
|
+
}
|
|
68111
|
+
}
|
|
68112
|
+
return { accepted: true, registered: inserted.length === 1 };
|
|
68113
|
+
})
|
|
68114
|
+
);
|
|
68115
|
+
} catch (error) {
|
|
68116
|
+
if (!isRetryableDatabaseTransportFailure(error)) throw error;
|
|
68117
|
+
return { transportFailure: error, attempt };
|
|
68118
|
+
}
|
|
68119
|
+
};
|
|
68120
|
+
const result = await runIdempotentPersistenceTransaction(
|
|
68121
|
+
{
|
|
68122
|
+
stage: "pending_tool_registration",
|
|
68123
|
+
correlationId,
|
|
68124
|
+
maxAttempts: 3,
|
|
68125
|
+
onRetry: async ({ attempt }) => {
|
|
68126
|
+
await new Promise((resolve) => setTimeout(resolve, 25 * attempt));
|
|
67885
68127
|
}
|
|
67886
|
-
|
|
67887
|
-
|
|
68128
|
+
},
|
|
68129
|
+
registerOnce
|
|
67888
68130
|
);
|
|
68131
|
+
if ("transportFailure" in result) {
|
|
68132
|
+
throw new SessionEventPersistenceError(
|
|
68133
|
+
{
|
|
68134
|
+
code: "db_failure",
|
|
68135
|
+
sqlState: null,
|
|
68136
|
+
stage: "pending_tool_registration",
|
|
68137
|
+
eventTypes: [],
|
|
68138
|
+
correlationId,
|
|
68139
|
+
attempts: result.attempt,
|
|
68140
|
+
retryOutcome: "not_retryable",
|
|
68141
|
+
database: safeDatabaseErrorFacts(result.transportFailure)
|
|
68142
|
+
},
|
|
68143
|
+
result.transportFailure
|
|
68144
|
+
);
|
|
68145
|
+
}
|
|
68146
|
+
return result;
|
|
67889
68147
|
}
|
|
67890
68148
|
async function recordPendingSessionToolCallResult(db, input) {
|
|
67891
68149
|
return await withRlsContext(
|
|
@@ -69482,6 +69740,54 @@ var SandboxViewerAdmissionBlockedError = class extends Error {
|
|
|
69482
69740
|
}
|
|
69483
69741
|
name = "SandboxViewerAdmissionBlockedError";
|
|
69484
69742
|
};
|
|
69743
|
+
var SandboxPaidComputeAdmissionError = class extends Error {
|
|
69744
|
+
constructor(workspaceId, sandboxGroupId) {
|
|
69745
|
+
super("Insufficient OpenGeni credits to admit paid sandbox compute");
|
|
69746
|
+
this.workspaceId = workspaceId;
|
|
69747
|
+
this.sandboxGroupId = sandboxGroupId;
|
|
69748
|
+
}
|
|
69749
|
+
name = "SandboxPaidComputeAdmissionError";
|
|
69750
|
+
};
|
|
69751
|
+
function warmBillingSnapshot(row) {
|
|
69752
|
+
const value = row.resume_state?.opengeniWarmBilling;
|
|
69753
|
+
if (!value || typeof value !== "object") return null;
|
|
69754
|
+
const snapshot = value;
|
|
69755
|
+
return (snapshot.mode === "usage_only" || snapshot.mode === "shadow" || snapshot.mode === "credits") && Number.isFinite(snapshot.rateMicrosPerSecond) && snapshot.rateMicrosPerSecond >= 0 && (snapshot.stopChargeAt === void 0 || typeof snapshot.stopChargeAt === "string" && Number.isFinite(Date.parse(snapshot.stopChargeAt))) && snapshot.accountId === row.account_id && snapshot.workspaceId === row.workspace_id ? snapshot : null;
|
|
69756
|
+
}
|
|
69757
|
+
async function markWarmBillingStopCutoff(db, input) {
|
|
69758
|
+
return withRlsContext(
|
|
69759
|
+
db,
|
|
69760
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
69761
|
+
async (scopedDb) => scopedDb.transaction(async (txRaw) => {
|
|
69762
|
+
const tx = txRaw;
|
|
69763
|
+
const [row] = await tx.execute(sql87`
|
|
69764
|
+
select * from sandbox_leases where workspace_id = ${input.workspaceId}
|
|
69765
|
+
and sandbox_group_id = ${input.sandboxGroupId} for update
|
|
69766
|
+
`);
|
|
69767
|
+
if (!row || Number(row.lease_epoch) !== input.expectedEpoch || row.liveness !== "draining" || Number(row.refcount) !== 0 && !row.unobservable_command_drain_ids?.length)
|
|
69768
|
+
return null;
|
|
69769
|
+
const snapshot = warmBillingSnapshot(row);
|
|
69770
|
+
if (row.resume_state?.opengeniWarmBilling && !snapshot)
|
|
69771
|
+
throw new Error("sandbox warm billing snapshot is invalid");
|
|
69772
|
+
if (snapshot?.stopChargeAt) return new Date(snapshot.stopChargeAt);
|
|
69773
|
+
const [stamped] = await tx.execute(sql87`
|
|
69774
|
+
update sandbox_leases set
|
|
69775
|
+
resume_state = jsonb_set(coalesce(resume_state, '{}'::jsonb),
|
|
69776
|
+
'{opengeniWarmBilling}',
|
|
69777
|
+
coalesce(resume_state->'opengeniWarmBilling',
|
|
69778
|
+
jsonb_build_object('mode', 'usage_only', 'rateMicrosPerSecond', 0,
|
|
69779
|
+
'accountId', account_id, 'workspaceId', workspace_id)) ||
|
|
69780
|
+
jsonb_build_object('stopChargeAt', clock_timestamp()), true),
|
|
69781
|
+
updated_at = now()
|
|
69782
|
+
where id = ${row.id}
|
|
69783
|
+
returning resume_state #>> '{opengeniWarmBilling,stopChargeAt}' as cutoff
|
|
69784
|
+
`);
|
|
69785
|
+
if (!stamped || !Number.isFinite(Date.parse(stamped.cutoff)))
|
|
69786
|
+
throw new Error("sandbox warm billing stop cutoff was not saved");
|
|
69787
|
+
return new Date(stamped.cutoff);
|
|
69788
|
+
})
|
|
69789
|
+
);
|
|
69790
|
+
}
|
|
69485
69791
|
var SandboxLeaseRecoveryBlockedError = class extends Error {
|
|
69486
69792
|
constructor(sandboxGroupId, leaseEpoch, code, recovery) {
|
|
69487
69793
|
super(
|
|
@@ -69892,6 +70198,10 @@ async function acquireLeaseOnce(db, input) {
|
|
|
69892
70198
|
const os = input.os ?? "linux";
|
|
69893
70199
|
const subjectId = input.subjectId ?? null;
|
|
69894
70200
|
const warmingLeaseTtlMs = input.warmingLeaseTtlMs ?? input.leaseTtlMs;
|
|
70201
|
+
const warmPolicy = input.warmBilling ?? { mode: "usage_only", rateMicrosPerSecond: 0 };
|
|
70202
|
+
if (!Number.isFinite(warmPolicy.rateMicrosPerSecond) || warmPolicy.rateMicrosPerSecond < 0) {
|
|
70203
|
+
throw new Error("sandbox warm rate must be finite and non-negative");
|
|
70204
|
+
}
|
|
69895
70205
|
return await withRlsContext(
|
|
69896
70206
|
db,
|
|
69897
70207
|
{ accountId, workspaceId },
|
|
@@ -69927,6 +70237,24 @@ async function acquireLeaseOnce(db, input) {
|
|
|
69927
70237
|
const row = rows[0];
|
|
69928
70238
|
if (!row) throw new Error(`Lease row vanished post-insert: ${sandboxGroupId}`);
|
|
69929
70239
|
const liveness = row.liveness;
|
|
70240
|
+
const existingSnapshot = warmBillingSnapshot(row);
|
|
70241
|
+
if (row.resume_state?.opengeniWarmBilling && !existingSnapshot) {
|
|
70242
|
+
throw new Error("sandbox warm billing snapshot is invalid");
|
|
70243
|
+
}
|
|
70244
|
+
const activeMode = input.warmBilling?.mode ?? existingSnapshot?.mode ?? "usage_only";
|
|
70245
|
+
const activeRate = liveness !== "cold" && existingSnapshot?.mode === "credits" ? existingSnapshot.rateMicrosPerSecond : input.warmBilling?.rateMicrosPerSecond ?? existingSnapshot?.rateMicrosPerSecond ?? 0;
|
|
70246
|
+
if (activeMode === "credits" && activeRate > 0 && (liveness === "cold" || existingSnapshot?.mode === "credits" && existingSnapshot.rateMicrosPerSecond > 0)) {
|
|
70247
|
+
const alreadyHeld = await tx.execute(sql87`
|
|
70248
|
+
select exists (select 1 from sandbox_lease_holders
|
|
70249
|
+
where lease_id = ${row.id} and kind = ${kind} and holder_id = ${holderId}) as held
|
|
70250
|
+
`);
|
|
70251
|
+
if (!alreadyHeld[0]?.held) {
|
|
70252
|
+
const balance = await getBillingBalance(tx, accountId);
|
|
70253
|
+
if (balance.balanceMicros <= 0) {
|
|
70254
|
+
throw new SandboxPaidComputeAdmissionError(workspaceId, sandboxGroupId);
|
|
70255
|
+
}
|
|
70256
|
+
}
|
|
70257
|
+
}
|
|
69930
70258
|
if ((kind === "viewer" || kind === "interaction") && Number(row.turn_holders) === 0) {
|
|
69931
70259
|
const workspaceRows = await tx.execute(sql87`
|
|
69932
70260
|
select sandbox_viewer_force_drain_reason
|
|
@@ -69934,7 +70262,7 @@ async function acquireLeaseOnce(db, input) {
|
|
|
69934
70262
|
where id = ${workspaceId}
|
|
69935
70263
|
`);
|
|
69936
70264
|
const reason = workspaceRows[0]?.sandbox_viewer_force_drain_reason ?? null;
|
|
69937
|
-
if (reason
|
|
70265
|
+
if (reason === "warm_cap") {
|
|
69938
70266
|
throw new SandboxViewerAdmissionBlockedError(workspaceId, sandboxGroupId, reason);
|
|
69939
70267
|
}
|
|
69940
70268
|
}
|
|
@@ -69999,6 +70327,14 @@ async function acquireLeaseOnce(db, input) {
|
|
|
69999
70327
|
};
|
|
70000
70328
|
}
|
|
70001
70329
|
if (liveness === "draining") {
|
|
70330
|
+
if (warmBillingSnapshot(row)?.stopChargeAt) {
|
|
70331
|
+
await tx.execute(sql87`
|
|
70332
|
+
update sandbox_leases
|
|
70333
|
+
set resume_state = resume_state #- '{opengeniWarmBilling,stopChargeAt}',
|
|
70334
|
+
updated_at = now()
|
|
70335
|
+
where id = ${row.id}
|
|
70336
|
+
`);
|
|
70337
|
+
}
|
|
70002
70338
|
await upsertLeaseHolder(tx, row.id, accountId, workspaceId, kind, holderId, subjectId, {
|
|
70003
70339
|
subjectId: input.viewerSubjectId ?? null,
|
|
70004
70340
|
authorityEpoch: input.viewerAuthorityEpoch ?? null
|
|
@@ -70018,6 +70354,13 @@ async function acquireLeaseOnce(db, input) {
|
|
|
70018
70354
|
const casRows = await tx.execute(sql87`
|
|
70019
70355
|
update sandbox_leases set
|
|
70020
70356
|
liveness = 'warming',
|
|
70357
|
+
${input.warmBilling ? sql87`resume_state = coalesce(resume_state, '{}'::jsonb) ||
|
|
70358
|
+
jsonb_build_object('opengeniWarmBilling', ${JSON.stringify({
|
|
70359
|
+
mode: warmPolicy.mode,
|
|
70360
|
+
rateMicrosPerSecond: warmPolicy.rateMicrosPerSecond,
|
|
70361
|
+
accountId,
|
|
70362
|
+
workspaceId
|
|
70363
|
+
})}::jsonb),` : sql87``}
|
|
70021
70364
|
${image !== null ? sql87`image = ${image},` : sql87``}
|
|
70022
70365
|
${rigVersionId !== null ? sql87`rig_version_id = ${rigVersionId},` : sql87``}
|
|
70023
70366
|
updated_at = now()
|
|
@@ -70986,7 +71329,11 @@ async function commitWarmingToWarm(db, input) {
|
|
|
70986
71329
|
reason: "archive_generation_mismatch"
|
|
70987
71330
|
};
|
|
70988
71331
|
}
|
|
70989
|
-
const
|
|
71332
|
+
const [warmClock] = await tx.execute(sql87`
|
|
71333
|
+
select clock_timestamp() as completed_at
|
|
71334
|
+
`);
|
|
71335
|
+
if (!warmClock) throw new Error("sandbox warm-transition clock unavailable");
|
|
71336
|
+
const completedAt = new Date(warmClock.completed_at).toISOString();
|
|
70990
71337
|
const recovery = rematerialization ? {
|
|
70991
71338
|
provider: {
|
|
70992
71339
|
status: "exists",
|
|
@@ -71030,7 +71377,15 @@ async function commitWarmingToWarm(db, input) {
|
|
|
71030
71377
|
input.resumeState ?? null,
|
|
71031
71378
|
row.resume_state
|
|
71032
71379
|
);
|
|
71033
|
-
const resumeStateJson = JSON.stringify(
|
|
71380
|
+
const resumeStateJson = JSON.stringify(
|
|
71381
|
+
resumeStateWithRecovery(
|
|
71382
|
+
{
|
|
71383
|
+
...withArchives ?? {},
|
|
71384
|
+
...row.resume_state?.opengeniWarmBilling ? { opengeniWarmBilling: row.resume_state.opengeniWarmBilling } : {}
|
|
71385
|
+
},
|
|
71386
|
+
recovery
|
|
71387
|
+
)
|
|
71388
|
+
);
|
|
71034
71389
|
const updated = await tx.execute(sql87`
|
|
71035
71390
|
update sandbox_leases set
|
|
71036
71391
|
liveness = 'warm',
|
|
@@ -71118,7 +71473,15 @@ async function recordWarmingSandboxCreated(db, input) {
|
|
|
71118
71473
|
input.resumeState ?? null,
|
|
71119
71474
|
row.resume_state
|
|
71120
71475
|
);
|
|
71121
|
-
const resumeStateJson = JSON.stringify(
|
|
71476
|
+
const resumeStateJson = JSON.stringify(
|
|
71477
|
+
resumeStateWithRecovery(
|
|
71478
|
+
{
|
|
71479
|
+
...withArchives ?? {},
|
|
71480
|
+
...row.resume_state?.opengeniWarmBilling ? { opengeniWarmBilling: row.resume_state.opengeniWarmBilling } : {}
|
|
71481
|
+
},
|
|
71482
|
+
recovery
|
|
71483
|
+
)
|
|
71484
|
+
);
|
|
71122
71485
|
const warmingTtlMs = input.warmingLeaseTtlMs ?? input.leaseTtlMs;
|
|
71123
71486
|
const providerCreatedAt = input.providerCreatedAt ?? null;
|
|
71124
71487
|
const providerDeadlineAt = input.providerDeadlineAt ?? null;
|
|
@@ -72638,7 +73001,7 @@ async function heartbeatLeaseHolderStatus(db, input) {
|
|
|
72638
73001
|
async (scopedDb) => await scopedDb.transaction(async (txRaw) => {
|
|
72639
73002
|
const tx = txRaw;
|
|
72640
73003
|
const leases = await tx.execute(sql87`
|
|
72641
|
-
select id, lease_epoch, liveness, rotation_requested_at from sandbox_leases
|
|
73004
|
+
select id, account_id, workspace_id, resume_state, lease_epoch, liveness, rotation_requested_at from sandbox_leases
|
|
72642
73005
|
where workspace_id = ${input.workspaceId}
|
|
72643
73006
|
and sandbox_group_id = ${input.sandboxGroupId}
|
|
72644
73007
|
for update
|
|
@@ -72663,6 +73026,19 @@ async function heartbeatLeaseHolderStatus(db, input) {
|
|
|
72663
73026
|
fence: "holder_gone"
|
|
72664
73027
|
};
|
|
72665
73028
|
}
|
|
73029
|
+
const snapshot = warmBillingSnapshot(lease);
|
|
73030
|
+
if (lease.resume_state?.opengeniWarmBilling && !snapshot) {
|
|
73031
|
+
throw new Error("sandbox warm billing snapshot is invalid");
|
|
73032
|
+
}
|
|
73033
|
+
if (Number(lease.lease_epoch) !== input.expectedEpoch)
|
|
73034
|
+
return { holderAlive: true, leaseExtended: false, fence: "epoch" };
|
|
73035
|
+
if (lease.liveness !== "warm" && lease.liveness !== "warming")
|
|
73036
|
+
return { holderAlive: true, leaseExtended: false, fence: "liveness" };
|
|
73037
|
+
if (lease.rotation_requested_at !== null)
|
|
73038
|
+
return { holderAlive: true, leaseExtended: false, fence: "rotation_requested" };
|
|
73039
|
+
if ((input.billingMode ?? snapshot?.mode) === "credits" && snapshot?.mode === "credits" && snapshot.rateMicrosPerSecond > 0 && (await getBillingBalance(tx, input.accountId)).balanceMicros <= 0) {
|
|
73040
|
+
return { holderAlive: true, leaseExtended: false, fence: "funding" };
|
|
73041
|
+
}
|
|
72666
73042
|
const leaseRows = await tx.execute(sql87`
|
|
72667
73043
|
update sandbox_leases set
|
|
72668
73044
|
expires_at = now() + (${String(input.leaseTtlMs)} || ' milliseconds')::interval,
|
|
@@ -73125,6 +73501,8 @@ async function enrollUnobservableCommandIdleDrain(db, input) {
|
|
|
73125
73501
|
'quarantined_provider_binding_mismatch')
|
|
73126
73502
|
`);
|
|
73127
73503
|
}
|
|
73504
|
+
const deadlineStopGraceMs = SANDBOX_DEADLINE_COMMAND_STOP_GRACE_MS;
|
|
73505
|
+
const deadlineRotation = initial.rotationReason === "provider_deadline" && initial.rotationRequestedAt !== null;
|
|
73128
73506
|
const processes = await rawRows(
|
|
73129
73507
|
tx,
|
|
73130
73508
|
sql87`
|
|
@@ -73151,10 +73529,43 @@ async function enrollUnobservableCommandIdleDrain(db, input) {
|
|
|
73151
73529
|
(${input.idleGraceMs}::bigint * interval '1 millisecond')
|
|
73152
73530
|
)
|
|
73153
73531
|
)
|
|
73532
|
+
or (
|
|
73533
|
+
${deadlineRotation}
|
|
73534
|
+
and process.deadline_cancellation_requested_at < now() -
|
|
73535
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond')
|
|
73536
|
+
and process.reconcile_attempts >= 1
|
|
73537
|
+
and (
|
|
73538
|
+
(process.owner_actor_kind = 'turn' and attempt.state = 'closed'
|
|
73539
|
+
and attempt.quiesced_at is not null
|
|
73540
|
+
and greatest(attempt.quiesced_at, process.started_at) < now() -
|
|
73541
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond'))
|
|
73542
|
+
or (process.owner_actor_kind = 'direct' and process.owner_attempt_id is null
|
|
73543
|
+
and process.started_at < now() -
|
|
73544
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond'))
|
|
73545
|
+
)
|
|
73546
|
+
)
|
|
73154
73547
|
)
|
|
73155
|
-
and attempt.state = 'closed'
|
|
73156
|
-
|
|
73157
|
-
|
|
73548
|
+
and (attempt.state = 'closed' or (
|
|
73549
|
+
${deadlineRotation}
|
|
73550
|
+
and process.owner_actor_kind = 'direct' and process.owner_attempt_id is null))
|
|
73551
|
+
and (
|
|
73552
|
+
(attempt.state = 'closed' and
|
|
73553
|
+
greatest(coalesce(attempt.quiesced_at, attempt.closed_at, attempt.updated_at), process.started_at) < now() -
|
|
73554
|
+
(${input.idleGraceMs}::bigint * interval '1 millisecond'))
|
|
73555
|
+
or (
|
|
73556
|
+
${deadlineRotation}
|
|
73557
|
+
and process.deadline_cancellation_requested_at < now() -
|
|
73558
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond')
|
|
73559
|
+
and (
|
|
73560
|
+
(process.owner_actor_kind = 'turn' and attempt.quiesced_at is not null
|
|
73561
|
+
and greatest(attempt.quiesced_at, process.started_at) < now() -
|
|
73562
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond'))
|
|
73563
|
+
or (process.owner_actor_kind = 'direct' and process.owner_attempt_id is null
|
|
73564
|
+
and process.started_at < now() -
|
|
73565
|
+
(${deadlineStopGraceMs}::bigint * interval '1 millisecond'))
|
|
73566
|
+
)
|
|
73567
|
+
)
|
|
73568
|
+
)) as eligible
|
|
73158
73569
|
from sandbox_retained_processes process
|
|
73159
73570
|
left join session_turn_attempts attempt on attempt.id = process.owner_attempt_id
|
|
73160
73571
|
and attempt.workspace_id = process.workspace_id and attempt.session_id = process.session_id
|
|
@@ -73192,7 +73603,10 @@ async function enrollUnobservableCommandIdleDrain(db, input) {
|
|
|
73192
73603
|
))
|
|
73193
73604
|
return null;
|
|
73194
73605
|
if (!enrolled) {
|
|
73195
|
-
if (lease.archive_capture_id !== null || await hasSandboxGroupAttemptActivityTx(tx,
|
|
73606
|
+
if (lease.archive_capture_id !== null || await hasSandboxGroupAttemptActivityTx(tx, {
|
|
73607
|
+
...input,
|
|
73608
|
+
idleGraceMs: deadlineRotation ? deadlineStopGraceMs : input.idleGraceMs
|
|
73609
|
+
}))
|
|
73196
73610
|
return null;
|
|
73197
73611
|
await tx.execute(sql87`
|
|
73198
73612
|
update sandbox_leases set unobservable_command_drain_ids = ${`{${ids.join(",")}}`}::uuid[],
|
|
@@ -73385,10 +73799,26 @@ async function reArmDrainingLease(db, input) {
|
|
|
73385
73799
|
return await withRlsContext(
|
|
73386
73800
|
db,
|
|
73387
73801
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
73388
|
-
async (scopedDb) => {
|
|
73389
|
-
const
|
|
73802
|
+
async (scopedDb) => await scopedDb.transaction(async (txRaw) => {
|
|
73803
|
+
const tx = txRaw;
|
|
73804
|
+
const [lease] = await tx.execute(sql87`
|
|
73805
|
+
select * from sandbox_leases
|
|
73806
|
+
where workspace_id = ${input.workspaceId} and sandbox_group_id = ${input.sandboxGroupId}
|
|
73807
|
+
for update
|
|
73808
|
+
`);
|
|
73809
|
+
if (!lease || lease.liveness !== "draining") return { rearmed: false };
|
|
73810
|
+
const snapshot = warmBillingSnapshot(lease);
|
|
73811
|
+
if (lease.resume_state?.opengeniWarmBilling && !snapshot) {
|
|
73812
|
+
throw new Error("sandbox warm billing snapshot is invalid");
|
|
73813
|
+
}
|
|
73814
|
+
const activeMode = input.warmBilling?.mode ?? snapshot?.mode ?? "usage_only";
|
|
73815
|
+
if (activeMode === "credits" && (snapshot ? snapshot.mode === "credits" && snapshot.rateMicrosPerSecond > 0 : input.warmBilling?.rateMicrosPerSecond !== void 0 && input.warmBilling.rateMicrosPerSecond > 0) && (await getBillingBalance(tx, input.accountId)).balanceMicros <= 0) {
|
|
73816
|
+
throw new SandboxPaidComputeAdmissionError(input.workspaceId, input.sandboxGroupId);
|
|
73817
|
+
}
|
|
73818
|
+
const rows = await tx.execute(sql87`
|
|
73390
73819
|
update sandbox_leases set
|
|
73391
73820
|
liveness = 'warm',
|
|
73821
|
+
resume_state = resume_state #- '{opengeniWarmBilling,stopChargeAt}',
|
|
73392
73822
|
expires_at = now() + (${String(input.leaseTtlMs)} || ' milliseconds')::interval,
|
|
73393
73823
|
updated_at = now()
|
|
73394
73824
|
where workspace_id = ${input.workspaceId} and sandbox_group_id = ${input.sandboxGroupId}
|
|
@@ -73415,7 +73845,7 @@ async function reArmDrainingLease(db, input) {
|
|
|
73415
73845
|
returning id
|
|
73416
73846
|
`);
|
|
73417
73847
|
return { rearmed: rows.length > 0 };
|
|
73418
|
-
}
|
|
73848
|
+
})
|
|
73419
73849
|
);
|
|
73420
73850
|
}
|
|
73421
73851
|
async function confirmDrainCold(db, input) {
|
|
@@ -73706,6 +74136,9 @@ function mapRetainedProcess(row) {
|
|
|
73706
74136
|
reconcileClaimedAt: row.reconcileClaimedAt?.toISOString() ?? null,
|
|
73707
74137
|
reconcileAttempts: row.reconcileAttempts,
|
|
73708
74138
|
lastReconcileOutcome: row.lastReconcileOutcome ?? null,
|
|
74139
|
+
cancellationRequestedAt: row.cancellationRequestedAt?.toISOString() ?? null,
|
|
74140
|
+
cancellationReason: row.cancellationReason ?? null,
|
|
74141
|
+
deadlineCancellationRequestedAt: row.deadlineCancellationRequestedAt?.toISOString() ?? null,
|
|
73709
74142
|
reconcileProofOutcome: row.reconcileProofOutcome ?? null,
|
|
73710
74143
|
reconcileProofExitCode: row.reconcileProofExitCode ?? null,
|
|
73711
74144
|
reconcileProofReason: row.reconcileProofReason ?? null,
|
|
@@ -79312,45 +79745,71 @@ async function accrueWarmSeconds(db, input) {
|
|
|
79312
79745
|
async (scopedDb) => await scopedDb.transaction(async (txRaw) => {
|
|
79313
79746
|
const tx = txRaw;
|
|
79314
79747
|
const rows = await tx.execute(sql87`
|
|
79315
|
-
select
|
|
79316
|
-
case when last_meter_at is null then null
|
|
79317
|
-
else floor(extract(epoch from (now() - last_meter_at)))::int end as meter_elapsed_s
|
|
79748
|
+
select *
|
|
79318
79749
|
from sandbox_leases
|
|
79319
79750
|
where workspace_id = ${input.workspaceId} and sandbox_group_id = ${input.sandboxGroupId}
|
|
79320
79751
|
for update
|
|
79321
79752
|
`);
|
|
79322
79753
|
const row = rows[0];
|
|
79323
79754
|
if (!row) return none;
|
|
79324
|
-
if (Number(row.lease_epoch) !== input.expectedEpoch || row.liveness !== "warm") {
|
|
79755
|
+
if (Number(row.lease_epoch) !== input.expectedEpoch || row.liveness !== "warm" && !(input.finalDrain && row.liveness === "draining")) {
|
|
79325
79756
|
return none;
|
|
79326
79757
|
}
|
|
79327
|
-
|
|
79758
|
+
const recovery = row.resume_state?.opengeniRecovery;
|
|
79759
|
+
const warmStartedAt = recovery?.restore?.completedAt;
|
|
79760
|
+
const warmStartMs = typeof warmStartedAt === "string" ? Date.parse(warmStartedAt) : NaN;
|
|
79761
|
+
const snapshot = warmBillingSnapshot(row);
|
|
79762
|
+
if (row.resume_state?.opengeniWarmBilling && !snapshot) {
|
|
79763
|
+
throw new Error("sandbox warm billing snapshot is invalid");
|
|
79764
|
+
}
|
|
79765
|
+
const mode = input.billingMode === "credits" ? snapshot?.mode ?? "usage_only" : input.billingMode ?? snapshot?.mode ?? "usage_only";
|
|
79766
|
+
const rate = snapshot?.rateMicrosPerSecond ?? input.warmRateMicrosPerSecond;
|
|
79767
|
+
if (!Number.isFinite(warmStartMs) && mode === "credits" && rate > 0) {
|
|
79768
|
+
throw new Error("paid warm lease has no valid warm-transition timestamp");
|
|
79769
|
+
}
|
|
79770
|
+
if (!Number.isFinite(warmStartMs) && row.last_meter_at == null) {
|
|
79328
79771
|
await tx.execute(sql87`
|
|
79329
|
-
|
|
79330
|
-
|
|
79331
|
-
|
|
79772
|
+
update sandbox_leases set last_meter_at = now(), updated_at = now()
|
|
79773
|
+
where id = ${row.id}
|
|
79774
|
+
`);
|
|
79332
79775
|
return none;
|
|
79333
79776
|
}
|
|
79334
|
-
const
|
|
79335
|
-
|
|
79777
|
+
const cursorMs = row.last_meter_at ? new Date(row.last_meter_at).getTime() : 0;
|
|
79778
|
+
const startMs = Number.isFinite(warmStartMs) ? Math.max(cursorMs, warmStartMs) : cursorMs;
|
|
79779
|
+
if (input.finalDrain && !snapshot?.stopChargeAt) {
|
|
79780
|
+
throw new Error("sandbox final tick has no durable stop cutoff");
|
|
79781
|
+
}
|
|
79782
|
+
const upperBound = snapshot?.stopChargeAt && input.finalDrain ? sql87`${snapshot.stopChargeAt}::timestamptz` : sql87`clock_timestamp()`;
|
|
79783
|
+
const elapsedRows = await tx.execute(sql87`
|
|
79784
|
+
select greatest(0, floor(extract(epoch from (${upperBound} -
|
|
79785
|
+
${new Date(startMs).toISOString()}::timestamptz)) * 1000))::bigint as elapsed_ms
|
|
79786
|
+
`);
|
|
79787
|
+
const elapsedMs = Number(elapsedRows[0]?.elapsed_ms ?? 0);
|
|
79788
|
+
const elapsedS = Math.floor(elapsedMs / 1e3);
|
|
79789
|
+
if (elapsedS <= 0 && !(input.finalDrain && elapsedMs > 0)) {
|
|
79336
79790
|
return none;
|
|
79337
79791
|
}
|
|
79338
79792
|
const tick = Number(row.last_meter_tick) + 1;
|
|
79339
|
-
const costMicros = Math.round(elapsedS *
|
|
79793
|
+
const costMicros = mode === "usage_only" ? 0 : Math.round((input.finalDrain ? elapsedMs / 1e3 : elapsedS) * rate);
|
|
79794
|
+
if (!Number.isSafeInteger(costMicros) || costMicros < 0) {
|
|
79795
|
+
throw new Error("warm-cost estimate exceeds safe integer micros");
|
|
79796
|
+
}
|
|
79340
79797
|
await tx.execute(sql87`
|
|
79341
79798
|
insert into usage_events
|
|
79342
79799
|
(account_id, workspace_id, subject_id, event_type, quantity, unit,
|
|
79343
|
-
source_resource_type, source_resource_id, idempotency_key, occurred_at)
|
|
79800
|
+
source_resource_type, source_resource_id, idempotency_key, initiator_context, occurred_at)
|
|
79344
79801
|
values
|
|
79345
79802
|
(${input.accountId}, ${input.workspaceId}, ${input.subjectId ?? null},
|
|
79346
79803
|
'sandbox.warm_seconds', ${elapsedS}, 'seconds',
|
|
79347
79804
|
'sandbox_lease', ${`${input.sandboxGroupId}:${input.expectedEpoch}`},
|
|
79348
79805
|
${`usage:sandbox.warm_seconds:${input.sandboxGroupId}:${input.expectedEpoch}:${tick}`},
|
|
79349
|
-
now())
|
|
79806
|
+
${JSON.stringify(mode === "shadow" ? { warmCostShadowMicros: costMicros } : {})}::jsonb, now())
|
|
79350
79807
|
on conflict (idempotency_key) do nothing
|
|
79351
79808
|
`);
|
|
79352
|
-
|
|
79353
|
-
|
|
79809
|
+
let settledCostMicros = costMicros;
|
|
79810
|
+
if (costMicros > 0 && mode === "credits") {
|
|
79811
|
+
const costKey = `usage:sandbox.warm_cost:${input.sandboxGroupId}:${input.expectedEpoch}:${tick}`;
|
|
79812
|
+
const inserted = await tx.execute(sql87`
|
|
79354
79813
|
insert into usage_events
|
|
79355
79814
|
(account_id, workspace_id, subject_id, event_type, quantity, unit,
|
|
79356
79815
|
source_resource_type, source_resource_id, idempotency_key, occurred_at)
|
|
@@ -79358,30 +79817,43 @@ async function accrueWarmSeconds(db, input) {
|
|
|
79358
79817
|
(${input.accountId}, ${input.workspaceId}, ${input.subjectId ?? null},
|
|
79359
79818
|
'sandbox.warm_cost', ${costMicros}, 'usd_micros',
|
|
79360
79819
|
'sandbox_lease', ${`${input.sandboxGroupId}:${input.expectedEpoch}`},
|
|
79361
|
-
${
|
|
79820
|
+
${costKey},
|
|
79362
79821
|
now())
|
|
79363
79822
|
on conflict (idempotency_key) do nothing
|
|
79823
|
+
returning quantity
|
|
79364
79824
|
`);
|
|
79825
|
+
const previous = inserted[0] ? null : await tx.execute(sql87`
|
|
79826
|
+
select quantity from usage_events
|
|
79827
|
+
where idempotency_key = ${costKey}
|
|
79828
|
+
and account_id = ${input.accountId}
|
|
79829
|
+
and workspace_id = ${input.workspaceId}
|
|
79830
|
+
and event_type = 'sandbox.warm_cost'
|
|
79831
|
+
`);
|
|
79832
|
+
settledCostMicros = Number((inserted[0] ?? previous?.[0])?.quantity ?? 0);
|
|
79833
|
+
if (!Number.isSafeInteger(settledCostMicros) || settledCostMicros <= 0) {
|
|
79834
|
+
throw new Error("warm-cost tick idempotency key conflicts with another usage event");
|
|
79835
|
+
}
|
|
79836
|
+
}
|
|
79837
|
+
if (costMicros > 0 && mode === "credits") {
|
|
79838
|
+
await applyCreditDebitAfterUse(tx, {
|
|
79839
|
+
accountId: input.accountId,
|
|
79840
|
+
workspaceId: input.workspaceId,
|
|
79841
|
+
type: "sandbox.warm_cost",
|
|
79842
|
+
amountMicros: settledCostMicros,
|
|
79843
|
+
sourceType: "sandbox_lease",
|
|
79844
|
+
sourceId: `${input.sandboxGroupId}:${input.expectedEpoch}`,
|
|
79845
|
+
idempotencyKey: `debit:sandbox.warm_cost:${input.sandboxGroupId}:${input.expectedEpoch}:${tick}`
|
|
79846
|
+
});
|
|
79365
79847
|
}
|
|
79366
79848
|
await tx.execute(sql87`
|
|
79367
79849
|
update sandbox_leases set
|
|
79368
|
-
last_meter_at =
|
|
79850
|
+
last_meter_at = ${new Date(startMs).toISOString()}::timestamptz + (${String(input.finalDrain ? elapsedMs : elapsedS * 1e3)} || ' milliseconds')::interval,
|
|
79851
|
+
last_meter_tick = ${tick}, updated_at = now()
|
|
79369
79852
|
where id = ${row.id}
|
|
79370
79853
|
`);
|
|
79371
|
-
return { accrued: true, seconds: elapsedS, tick, costMicros };
|
|
79854
|
+
return { accrued: true, seconds: elapsedS, tick, costMicros: settledCostMicros };
|
|
79372
79855
|
})
|
|
79373
79856
|
);
|
|
79374
|
-
if (result.accrued && result.costMicros > 0 && (input.debitCredits ?? true)) {
|
|
79375
|
-
await applyCreditDebitUpToBalance(db, {
|
|
79376
|
-
accountId: input.accountId,
|
|
79377
|
-
workspaceId: input.workspaceId,
|
|
79378
|
-
type: "sandbox.warm_cost",
|
|
79379
|
-
requestedAmountMicros: result.costMicros,
|
|
79380
|
-
sourceType: "sandbox_lease",
|
|
79381
|
-
sourceId: `${input.sandboxGroupId}:${input.expectedEpoch}`,
|
|
79382
|
-
idempotencyKey: `debit:sandbox.warm_cost:${input.sandboxGroupId}:${input.expectedEpoch}:${result.tick}`
|
|
79383
|
-
}).catch(() => void 0);
|
|
79384
|
-
}
|
|
79385
79857
|
return result;
|
|
79386
79858
|
}
|
|
79387
79859
|
function startOfUtcMonthDefault(date = /* @__PURE__ */ new Date()) {
|
|
@@ -79390,9 +79862,7 @@ function startOfUtcMonthDefault(date = /* @__PURE__ */ new Date()) {
|
|
|
79390
79862
|
async function forceDrainOverLimitViewerOnlyBoxes(db, input) {
|
|
79391
79863
|
return await withWorkspaceUsageLock(db, input.workspaceId, async (scopedDb) => {
|
|
79392
79864
|
let reason = null;
|
|
79393
|
-
if (input.
|
|
79394
|
-
reason = "balance";
|
|
79395
|
-
} else if (input.maxWarmSecondsPerWorkspace > 0) {
|
|
79865
|
+
if (input.maxWarmSecondsPerWorkspace > 0) {
|
|
79396
79866
|
const since = input.capWindowStart ?? startOfUtcMonthDefault();
|
|
79397
79867
|
const [{ total } = { total: 0 }] = await scopedDb.select({
|
|
79398
79868
|
total: sql87`coalesce(sum(${usageEvents.quantity}), 0)`
|
|
@@ -79435,76 +79905,25 @@ async function forceDrainOverLimitViewerOnlyBoxes(db, input) {
|
|
|
79435
79905
|
)
|
|
79436
79906
|
`);
|
|
79437
79907
|
await scopedDb.execute(sql87`
|
|
79438
|
-
select id from sandbox_leases
|
|
79439
|
-
where workspace_id = ${input.workspaceId}
|
|
79440
|
-
and liveness = 'warm' and
|
|
79908
|
+
select lease.id from sandbox_leases lease
|
|
79909
|
+
where lease.workspace_id = ${input.workspaceId}
|
|
79910
|
+
and lease.liveness = 'warm' and lease.refcount = 0
|
|
79911
|
+
and not exists (select 1 from sandbox_lease_holders h where h.lease_id = lease.id)
|
|
79441
79912
|
order by id
|
|
79442
79913
|
for update
|
|
79443
79914
|
`);
|
|
79444
|
-
const lostBrowsers = await scopedDb.execute(sql87`
|
|
79445
|
-
update browser_sessions browser set
|
|
79446
|
-
lifecycle = 'lost',
|
|
79447
|
-
controller_id = null,
|
|
79448
|
-
controller_generation = null,
|
|
79449
|
-
placement_instance_id = null,
|
|
79450
|
-
controller_heartbeat_at = null,
|
|
79451
|
-
failure_code = 'workspace_force_drained',
|
|
79452
|
-
updated_at = now()
|
|
79453
|
-
where browser.workspace_id = ${input.workspaceId}
|
|
79454
|
-
and browser.lifecycle in ('starting', 'active', 'suspending', 'restoring', 'ending')
|
|
79455
|
-
and browser.controller_host_sandbox_group_id in (
|
|
79456
|
-
select sandbox_group_id from sandbox_leases
|
|
79457
|
-
where workspace_id = ${input.workspaceId}
|
|
79458
|
-
and liveness = 'warm' and turn_holders = 0
|
|
79459
|
-
)
|
|
79460
|
-
returning browser.id
|
|
79461
|
-
`);
|
|
79462
|
-
const lostComputers = await scopedDb.execute(sql87`
|
|
79463
|
-
update computer_sessions computer set
|
|
79464
|
-
lifecycle = 'lost',
|
|
79465
|
-
controller_id = null,
|
|
79466
|
-
controller_generation = null,
|
|
79467
|
-
placement_instance_id = null,
|
|
79468
|
-
controller_heartbeat_at = null,
|
|
79469
|
-
failure_code = 'workspace_force_drained',
|
|
79470
|
-
updated_at = now()
|
|
79471
|
-
where computer.workspace_id = ${input.workspaceId}
|
|
79472
|
-
and computer.lifecycle in ('starting', 'active', 'suspending', 'restoring', 'ending')
|
|
79473
|
-
and computer.sandbox_group_id in (
|
|
79474
|
-
select sandbox_group_id from sandbox_leases
|
|
79475
|
-
where workspace_id = ${input.workspaceId}
|
|
79476
|
-
and liveness = 'warm' and turn_holders = 0
|
|
79477
|
-
)
|
|
79478
|
-
returning computer.id
|
|
79479
|
-
`);
|
|
79480
|
-
if (lostBrowsers.length > 0 || lostComputers.length > 0) {
|
|
79481
|
-
await scopedDb.execute(sql87`
|
|
79482
|
-
update workspace_interaction_revisions set
|
|
79483
|
-
revision = revision + 1,
|
|
79484
|
-
updated_at = now()
|
|
79485
|
-
where workspace_id = ${input.workspaceId}
|
|
79486
|
-
`);
|
|
79487
|
-
}
|
|
79488
|
-
await scopedDb.execute(sql87`
|
|
79489
|
-
delete from sandbox_lease_holders h
|
|
79490
|
-
where h.kind in ('viewer', 'interaction')
|
|
79491
|
-
and h.lease_id in (
|
|
79492
|
-
select id from sandbox_leases
|
|
79493
|
-
where workspace_id = ${input.workspaceId}
|
|
79494
|
-
and liveness = 'warm' and turn_holders = 0
|
|
79495
|
-
)
|
|
79496
|
-
`);
|
|
79497
79915
|
const drained = await rawRows(
|
|
79498
79916
|
scopedDb,
|
|
79499
79917
|
sql87`
|
|
79500
|
-
update sandbox_leases set
|
|
79918
|
+
update sandbox_leases lease set
|
|
79501
79919
|
liveness = 'draining',
|
|
79502
79920
|
refcount = 0, turn_holders = 0, viewer_holders = 0,
|
|
79503
79921
|
expires_at = now() + (${String(input.idleGraceMs)} || ' milliseconds')::interval,
|
|
79504
79922
|
updated_at = now()
|
|
79505
|
-
where workspace_id = ${input.workspaceId}
|
|
79506
|
-
and liveness = 'warm' and
|
|
79507
|
-
|
|
79923
|
+
where lease.workspace_id = ${input.workspaceId}
|
|
79924
|
+
and lease.liveness = 'warm' and lease.refcount = 0
|
|
79925
|
+
and not exists (select 1 from sandbox_lease_holders h where h.lease_id = lease.id)
|
|
79926
|
+
returning lease.sandbox_group_id
|
|
79508
79927
|
`
|
|
79509
79928
|
);
|
|
79510
79929
|
return {
|
|
@@ -93800,6 +94219,7 @@ export {
|
|
|
93800
94219
|
SandboxLeaseRecoveryBlockedError,
|
|
93801
94220
|
SandboxLeaseSupersededError,
|
|
93802
94221
|
SandboxLeaseTransitionError,
|
|
94222
|
+
SandboxPaidComputeAdmissionError,
|
|
93803
94223
|
SandboxProviderReadLockUnavailableError,
|
|
93804
94224
|
SandboxRecoveryConflictError,
|
|
93805
94225
|
SandboxRetainedProcessPromotionFencedError,
|
|
@@ -93960,6 +94380,7 @@ export {
|
|
|
93960
94380
|
appendSessionRealtimeOutboundInTransaction,
|
|
93961
94381
|
applyCanonicalHumanIdentityOperation,
|
|
93962
94382
|
applyContextCompaction,
|
|
94383
|
+
applyCreditDebitAfterUse,
|
|
93963
94384
|
applyCreditDebitUpToBalance,
|
|
93964
94385
|
applyCreditLedgerEntry,
|
|
93965
94386
|
applyKnowledgeMemoryOperation,
|
|
@@ -94273,6 +94694,7 @@ export {
|
|
|
94273
94694
|
createWorkspaceOpenRouterCustomModel,
|
|
94274
94695
|
createXaiSubscriptionCredential,
|
|
94275
94696
|
currentMemorySlackConfigurationMatches,
|
|
94697
|
+
currentSessionRlsActorIdentityKey,
|
|
94276
94698
|
databaseFailureCode,
|
|
94277
94699
|
sql88 as dbSql,
|
|
94278
94700
|
deactivatePreferenceRegistry,
|
|
@@ -94397,6 +94819,7 @@ export {
|
|
|
94397
94819
|
failHostExportBatch,
|
|
94398
94820
|
failMemorySlackPublication,
|
|
94399
94821
|
failOrganizationRetentionDeletion,
|
|
94822
|
+
failPreparedBrowserSessionSuspend,
|
|
94400
94823
|
failSandboxRematerialization,
|
|
94401
94824
|
failScheduledGeneratedSessionRoute,
|
|
94402
94825
|
failSessionRealtimeConnectionInTransaction,
|
|
@@ -94424,6 +94847,7 @@ export {
|
|
|
94424
94847
|
forceDrainOverLimitViewerOnlyBoxes,
|
|
94425
94848
|
forkSessionContent,
|
|
94426
94849
|
freezeAgentLearningPolicy,
|
|
94850
|
+
freezeKnowledgeIndexBillingMode,
|
|
94427
94851
|
frozenInitiatorForCommandActor,
|
|
94428
94852
|
getActiveSessionHistoryItems,
|
|
94429
94853
|
getActiveSessionHistoryItemsPaged,
|
|
@@ -94560,6 +94984,7 @@ export {
|
|
|
94560
94984
|
getRetainedFileArtifact,
|
|
94561
94985
|
getRetainedProcess,
|
|
94562
94986
|
getRetainedScreenshotArtifact,
|
|
94987
|
+
getRetainedScreenshotArtifactForToolCall,
|
|
94563
94988
|
getRig,
|
|
94564
94989
|
getRigByName,
|
|
94565
94990
|
getRigChange,
|
|
@@ -94682,6 +95107,7 @@ export {
|
|
|
94682
95107
|
getXaiSubscriptionAccountAuthoritySnapshot,
|
|
94683
95108
|
getXaiSubscriptionAccountMetadata,
|
|
94684
95109
|
grantWorkspaceAccess,
|
|
95110
|
+
guardPaidKnowledgeIndexPublication,
|
|
94685
95111
|
hasAuditableGitHubInstallationAuthority,
|
|
94686
95112
|
hasCreditLedgerEntry,
|
|
94687
95113
|
hasPendingSteerAfterContextCompaction,
|
|
@@ -94731,6 +95157,7 @@ export {
|
|
|
94731
95157
|
isStripeWebhookProcessed,
|
|
94732
95158
|
issueSelfUserResourceGrant,
|
|
94733
95159
|
issueToolGatewayApproval,
|
|
95160
|
+
knowledgeIndexBillingActivationTime,
|
|
94734
95161
|
knowledgeOriginalFileAsset,
|
|
94735
95162
|
latestWorkspaceCapture,
|
|
94736
95163
|
linkKnowledgeClaims,
|
|
@@ -94946,6 +95373,7 @@ export {
|
|
|
94946
95373
|
markVideoGenerationSubmissionUncertain,
|
|
94947
95374
|
markVideoGenerationTerminalUpdate,
|
|
94948
95375
|
markVideoGenerationTerminalUpdateInTransaction,
|
|
95376
|
+
markWarmBillingStopCutoff,
|
|
94949
95377
|
markWarmLeaseInstanceLost,
|
|
94950
95378
|
materializeConfirmedRememberKnowledgeMemory,
|
|
94951
95379
|
materializeGoalContinuation,
|
|
@@ -95064,6 +95492,7 @@ export {
|
|
|
95064
95492
|
readScheduledKnowledgeSource,
|
|
95065
95493
|
readSessionFileAttachments,
|
|
95066
95494
|
readSkillRemovalScope,
|
|
95495
|
+
readStoredBrowserCapabilities,
|
|
95067
95496
|
readVariableSetSecretAtomically,
|
|
95068
95497
|
readWorkspaceArchiveCapturePreflight,
|
|
95069
95498
|
readWorkspaceInsightsModelBundle,
|
|
@@ -95528,6 +95957,7 @@ export {
|
|
|
95528
95957
|
verifyRetainedProcessMutationSettlement,
|
|
95529
95958
|
verifyWorkspaceMutationSettlement,
|
|
95530
95959
|
waitForSessionInputWithEvent,
|
|
95960
|
+
waitKnowledgeIndexForFunding,
|
|
95531
95961
|
wakeXaiCapacityWaiters,
|
|
95532
95962
|
withAccountRls,
|
|
95533
95963
|
withCodexAppsRequestAuthorization,
|
|
@@ -95536,6 +95966,7 @@ export {
|
|
|
95536
95966
|
withCodexTokenDeadline,
|
|
95537
95967
|
withDatabaseStatementTimeout,
|
|
95538
95968
|
withDatabaseTimingObserver,
|
|
95969
|
+
withKnowledgeQueryAccountLock,
|
|
95539
95970
|
withOrganizationModelProviderCustomModelReadLock,
|
|
95540
95971
|
withOrganizationXaiCapacityMutation,
|
|
95541
95972
|
withRlsContext,
|