@opengeni/db 4.3.3-canary.0 → 4.3.3-canary.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/codex-selection-diagnostics.d.ts +12 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.js +260 -49
- package/dist/index.js.map +1 -1
- package/dist/plugin-packages.d.ts +1 -0
- package/dist/sandbox-transition-wait.d.ts +10 -0
- package/dist/session-mcp-progress.d.ts +8 -1
- package/drizzle/0460_host_export_message_attribution.sql +177 -0
- package/package.json +6 -6
- package/src/codex-selection-diagnostics.ts +25 -0
- package/src/computer-sessions.ts +5 -1
- package/src/index.ts +286 -56
- package/src/plugin-packages.ts +3 -0
- package/src/sandbox-transition-wait.ts +45 -0
- package/src/session-mcp-progress.ts +26 -5
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Observations about this session, never allocator inputs or failover policy. */
|
|
2
|
+
export declare function codexSelectionDiagnostics(input: {
|
|
3
|
+
previousCredentialId: string | null;
|
|
4
|
+
credentialId: string;
|
|
5
|
+
reusedLease: boolean;
|
|
6
|
+
pinnedCredentialId: string | null;
|
|
7
|
+
pinSource?: "manual" | "policy" | null;
|
|
8
|
+
}): {
|
|
9
|
+
transition: "assigned" | "switched" | "unchanged";
|
|
10
|
+
source: "allocator" | "manual_pin";
|
|
11
|
+
reason: "affinity_reused" | "assigned" | "lease_reused" | "switched";
|
|
12
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { codexSelectionDiagnostics } from "./codex-selection-diagnostics.js";
|
|
2
|
+
export { codexSelectionDiagnostics } from "./codex-selection-diagnostics.js";
|
|
1
3
|
import { type ModelConnectionAccess, type ModelConnectionTarget } from "./model-connection-access.js";
|
|
2
4
|
export { assertModelConnectionAllowsTurn, modelAllowedByConnections, type ConnectionModelRestrictions, } from "./workspace-model-connection-access.js";
|
|
3
5
|
export * from "./model-connection-access.js";
|
|
@@ -4884,6 +4886,27 @@ export declare function switchSessionCodexAccount(db: Database, input: {
|
|
|
4884
4886
|
}>>;
|
|
4885
4887
|
export declare function setSessionCodexPin(db: Database, workspaceId: string, sessionId: string, pinnedCredentialId: string | null, source?: CodexPinSource, options?: SetSessionCodexPinOptions): Promise<boolean>;
|
|
4886
4888
|
/** Written by the worker at the turn boundary; drives the in-session indicator. */
|
|
4889
|
+
/** Commit observed assignment and its events together under the exact live attempt.
|
|
4890
|
+
* Allocation still uses its frozen policy snapshot; this is observation only.
|
|
4891
|
+
* Replays return the same receipt and never manufacture a second switch.
|
|
4892
|
+
*/
|
|
4893
|
+
export declare function recordSessionCodexSelectionForTurnAttempt(db: Database, input: {
|
|
4894
|
+
workspaceId: string;
|
|
4895
|
+
sessionId: string;
|
|
4896
|
+
turnId: string;
|
|
4897
|
+
attemptId: string;
|
|
4898
|
+
executionGeneration: number;
|
|
4899
|
+
credentialId: string;
|
|
4900
|
+
strategy: string;
|
|
4901
|
+
reusedLease: boolean;
|
|
4902
|
+
pinnedCredentialId: string | null;
|
|
4903
|
+
pinSource: "manual" | "policy" | null;
|
|
4904
|
+
eligibleCount: number;
|
|
4905
|
+
connectedCount: number;
|
|
4906
|
+
}): Promise<{
|
|
4907
|
+
events: SessionEvent[];
|
|
4908
|
+
diagnostics: ReturnType<typeof codexSelectionDiagnostics>;
|
|
4909
|
+
}>;
|
|
4887
4910
|
export declare function recordSessionActiveCodexCredential(db: Database, workspaceId: string, sessionId: string, credentialId: string): Promise<void>;
|
|
4888
4911
|
/**
|
|
4889
4912
|
* Disconnect ONE account. DELETE WHERE id = credentialId AND workspace_id. If it
|
|
@@ -5471,7 +5494,6 @@ export declare function reapExpiredSessionListSnapshots(db: Database, limit?: nu
|
|
|
5471
5494
|
* lifecycle/title and authorization data.
|
|
5472
5495
|
*/
|
|
5473
5496
|
export declare function listSessionsForSubject(db: Database, workspaceId: string, options: ListSessionsForSubjectOptions): Promise<SessionListResponse>;
|
|
5474
|
-
/** Read a session with the caller subject's personal pin projection. */
|
|
5475
5497
|
export declare function getSessionForSubject(db: Database, workspaceId: string, sessionId: string, subjectId: string, relatedSessionAccess?: "target" | "root"): Promise<Session | null>;
|
|
5476
5498
|
/**
|
|
5477
5499
|
* Idempotently set a member's pin without mutating the session's lifecycle or
|
package/dist/index.js
CHANGED
|
@@ -494,6 +494,14 @@ import {
|
|
|
494
494
|
} from "./chunk-6TKL3GQO.js";
|
|
495
495
|
import "./chunk-PZ5AY32C.js";
|
|
496
496
|
|
|
497
|
+
// src/codex-selection-diagnostics.ts
|
|
498
|
+
function codexSelectionDiagnostics(input) {
|
|
499
|
+
const transition = input.previousCredentialId === input.credentialId ? "unchanged" : input.previousCredentialId === null ? "assigned" : "switched";
|
|
500
|
+
const source = input.pinnedCredentialId === input.credentialId && input.pinSource !== "policy" ? "manual_pin" : "allocator";
|
|
501
|
+
const reason = input.reusedLease ? "lease_reused" : transition === "unchanged" ? "affinity_reused" : transition;
|
|
502
|
+
return { transition, source, reason };
|
|
503
|
+
}
|
|
504
|
+
|
|
497
505
|
// src/session-execution-policy.ts
|
|
498
506
|
import { and, desc, eq, inArray, sql } from "drizzle-orm";
|
|
499
507
|
function latestStartedSessionTurnQuery(db, workspaceId, sessionId) {
|
|
@@ -9272,14 +9280,41 @@ import {
|
|
|
9272
9280
|
} from "@opengeni/contracts";
|
|
9273
9281
|
import {
|
|
9274
9282
|
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2,
|
|
9275
|
-
|
|
9276
|
-
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
|
|
9283
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS as SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS2,
|
|
9277
9284
|
WORKSPACE_OPENROUTER_CONNECTION_DOMAIN as WORKSPACE_OPENROUTER_CONNECTION_DOMAIN2,
|
|
9278
9285
|
WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
9279
9286
|
VERCEL_AI_GATEWAY_CONNECTION_DOMAIN as VERCEL_AI_GATEWAY_CONNECTION_DOMAIN2,
|
|
9280
9287
|
VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
9281
9288
|
} from "@opengeni/config";
|
|
9282
9289
|
|
|
9290
|
+
// src/sandbox-transition-wait.ts
|
|
9291
|
+
import {
|
|
9292
|
+
SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS,
|
|
9293
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS
|
|
9294
|
+
} from "@opengeni/config";
|
|
9295
|
+
var SandboxTransitionWaitBudget = class {
|
|
9296
|
+
constructor(waitMs, startedAt) {
|
|
9297
|
+
this.waitMs = waitMs;
|
|
9298
|
+
this.hardDeadline = startedAt + SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS;
|
|
9299
|
+
this.currentDeadline = Math.min(this.hardDeadline, startedAt + waitMs);
|
|
9300
|
+
}
|
|
9301
|
+
observedCapture = false;
|
|
9302
|
+
currentDeadline;
|
|
9303
|
+
hardDeadline;
|
|
9304
|
+
get deadline() {
|
|
9305
|
+
return this.currentDeadline;
|
|
9306
|
+
}
|
|
9307
|
+
observeCapture(remainingMs, now) {
|
|
9308
|
+
if (this.waitMs === 0 || this.observedCapture || remainingMs === null || remainingMs === void 0 || !Number.isSafeInteger(remainingMs) || remainingMs < 0 || remainingMs > SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS)
|
|
9309
|
+
return;
|
|
9310
|
+
this.observedCapture = true;
|
|
9311
|
+
this.currentDeadline = Math.min(
|
|
9312
|
+
this.hardDeadline,
|
|
9313
|
+
Math.max(this.currentDeadline, now + remainingMs + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS)
|
|
9314
|
+
);
|
|
9315
|
+
}
|
|
9316
|
+
};
|
|
9317
|
+
|
|
9283
9318
|
// src/workspace-membership-permissions.ts
|
|
9284
9319
|
import { Permission as PermissionSchema } from "@opengeni/contracts";
|
|
9285
9320
|
function normalizeWorkspaceMembershipPermissions(value) {
|
|
@@ -10205,7 +10240,12 @@ import {
|
|
|
10205
10240
|
// src/session-mcp-progress.ts
|
|
10206
10241
|
import { sql as sql22 } from "drizzle-orm";
|
|
10207
10242
|
var SESSION_MCP_PROGRESS_CHARS = 600;
|
|
10208
|
-
|
|
10243
|
+
function sessionTextStoragePrefixChars(maxChars) {
|
|
10244
|
+
return LOSSLESS_JSON_STRING_PREFIX.length + Math.ceil((maxChars + 1) * 2 / 3) * 8;
|
|
10245
|
+
}
|
|
10246
|
+
var SESSION_MCP_PROGRESS_STORAGE_CHARS = sessionTextStoragePrefixChars(
|
|
10247
|
+
SESSION_MCP_PROGRESS_CHARS
|
|
10248
|
+
);
|
|
10209
10249
|
var SESSION_MCP_PROGRESS_UTF16_BASE64_PATTERN = "([A-Za-z0-9+/]{8})*([A-Za-z0-9+/]{8}|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=|[A-Za-z0-9+/]{5}[AQgw]==)";
|
|
10210
10250
|
function sessionMcpProgressScalarIsEncodedSql(value, codecVersion) {
|
|
10211
10251
|
return sql22`coalesce(
|
|
@@ -10216,18 +10256,27 @@ function sessionMcpProgressScalarIsEncodedSql(value, codecVersion) {
|
|
|
10216
10256
|
false
|
|
10217
10257
|
)`;
|
|
10218
10258
|
}
|
|
10219
|
-
function
|
|
10259
|
+
function projectSessionTextPrefix(storedPrefix, storedChars, codecVersion, scalarIsEncoded, maxChars) {
|
|
10220
10260
|
if (storedPrefix === null) return { text: null, originalChars: null };
|
|
10221
10261
|
const decoded = scalarIsEncoded ? fromPostgresLosslessJson(storedPrefix, codecVersion) : storedPrefix;
|
|
10222
10262
|
const chars = Array.from(decoded);
|
|
10223
10263
|
const encodedPrefixWasCut = decoded !== storedPrefix && (storedChars ?? 0) > Array.from(storedPrefix).length;
|
|
10224
10264
|
const originalChars = encodedPrefixWasCut ? null : decoded === storedPrefix ? storedChars : chars.length;
|
|
10225
10265
|
return {
|
|
10226
|
-
text: chars.slice(0,
|
|
10266
|
+
text: chars.slice(0, maxChars).join(""),
|
|
10227
10267
|
originalChars,
|
|
10228
10268
|
...encodedPrefixWasCut ? { textTruncated: true } : {}
|
|
10229
10269
|
};
|
|
10230
10270
|
}
|
|
10271
|
+
function projectSessionMcpProgressText(storedPrefix, storedChars, codecVersion, scalarIsEncoded) {
|
|
10272
|
+
return projectSessionTextPrefix(
|
|
10273
|
+
storedPrefix,
|
|
10274
|
+
storedChars,
|
|
10275
|
+
codecVersion,
|
|
10276
|
+
scalarIsEncoded,
|
|
10277
|
+
SESSION_MCP_PROGRESS_CHARS
|
|
10278
|
+
);
|
|
10279
|
+
}
|
|
10231
10280
|
|
|
10232
10281
|
// src/new-session-drafts.ts
|
|
10233
10282
|
import {
|
|
@@ -34161,6 +34210,7 @@ async function listInstalledPluginPackages(db, workspaceId) {
|
|
|
34161
34210
|
return rows.map((row) => {
|
|
34162
34211
|
const manifest = objectValue3(row.manifest);
|
|
34163
34212
|
const sourceUrl = stringValue5(manifest.sourceUrl);
|
|
34213
|
+
const logoUrl = stringValue5(objectValue3(manifest.discovery).logoUrl);
|
|
34164
34214
|
const bom = pluginBom(manifest);
|
|
34165
34215
|
if (row.status !== "active" && row.status !== "needs_attention") {
|
|
34166
34216
|
throw new Error(`Unknown installed Plugin status: ${row.status}`);
|
|
@@ -34173,6 +34223,7 @@ async function listInstalledPluginPackages(db, workspaceId) {
|
|
|
34173
34223
|
category: row.category,
|
|
34174
34224
|
tags: stringArray3(manifest.tags).length > 0 ? stringArray3(manifest.tags) : stringArray3(row.tags),
|
|
34175
34225
|
sourceUrl: sourceUrl && safeHttpUrl(sourceUrl) ? sourceUrl : null,
|
|
34226
|
+
logoUrl: logoUrl && safeHttpUrl(logoUrl) ? logoUrl : null,
|
|
34176
34227
|
manifestDigest: row.manifestDigest,
|
|
34177
34228
|
installationVersion: row.installationVersion,
|
|
34178
34229
|
componentCount: bom.length,
|
|
@@ -40521,7 +40572,7 @@ async function prepareComputerSessionCreate(db, input) {
|
|
|
40521
40572
|
requestDigest: digest2,
|
|
40522
40573
|
state: "prepared",
|
|
40523
40574
|
actorSubjectId: input.actorSubjectId
|
|
40524
|
-
}).onConflictDoNothing(
|
|
40575
|
+
}).onConflictDoNothing().returning();
|
|
40525
40576
|
if (!insertedOperation) {
|
|
40526
40577
|
const existing = await loadOperation3(tx, input.workspaceId, input.operationId);
|
|
40527
40578
|
if (!existing) {
|
|
@@ -61548,6 +61599,86 @@ async function setSessionCodexPin(db, workspaceId, sessionId, pinnedCredentialId
|
|
|
61548
61599
|
)
|
|
61549
61600
|
);
|
|
61550
61601
|
}
|
|
61602
|
+
async function recordSessionCodexSelectionForTurnAttempt(db, input) {
|
|
61603
|
+
return await withWorkspaceSessionEventActivityRls(db, input.workspaceId, true, async (tx) => {
|
|
61604
|
+
const fence = await lockTurnAttemptWriteFenceTx(tx, {
|
|
61605
|
+
workspaceId: input.workspaceId,
|
|
61606
|
+
sessionId: input.sessionId,
|
|
61607
|
+
turnId: input.turnId,
|
|
61608
|
+
executionGeneration: input.executionGeneration,
|
|
61609
|
+
attemptId: input.attemptId,
|
|
61610
|
+
sessionLock: "no_key_update"
|
|
61611
|
+
});
|
|
61612
|
+
if (!fence.allowed || !fence.session) throw new CodexCredentialLeaseAttemptFencedError();
|
|
61613
|
+
const key2 = `opengeni:codex-selection:${input.attemptId}`;
|
|
61614
|
+
const prior = await tx.select().from(sessionEvents).where(
|
|
61615
|
+
and56(
|
|
61616
|
+
eq57(sessionEvents.workspaceId, input.workspaceId),
|
|
61617
|
+
eq57(sessionEvents.sessionId, input.sessionId),
|
|
61618
|
+
inArray31(sessionEvents.clientEventId, [key2, `${key2}:switch`])
|
|
61619
|
+
)
|
|
61620
|
+
).orderBy(asc22(sessionEvents.sequence));
|
|
61621
|
+
if (prior.length) {
|
|
61622
|
+
const receipt2 = prior.find((event) => event.type === "codex.credential.selected");
|
|
61623
|
+
if (!receipt2) throw new Error("Codex selection receipt missing");
|
|
61624
|
+
const payload = sessionEventPayloadRecord(receipt2.payload, receipt2.payloadCodecVersion);
|
|
61625
|
+
if (payload.credentialId !== input.credentialId)
|
|
61626
|
+
throw new Error("An attempt cannot record two different Codex selections");
|
|
61627
|
+
return {
|
|
61628
|
+
events: prior.map(mapEvent2),
|
|
61629
|
+
diagnostics: {
|
|
61630
|
+
transition: payload.transition,
|
|
61631
|
+
source: payload.source,
|
|
61632
|
+
reason: payload.reason
|
|
61633
|
+
}
|
|
61634
|
+
};
|
|
61635
|
+
}
|
|
61636
|
+
const previousCredentialId = fence.session.codexLastCredentialId;
|
|
61637
|
+
const diagnostics = codexSelectionDiagnostics({ ...input, previousCredentialId });
|
|
61638
|
+
const events = [];
|
|
61639
|
+
if (previousCredentialId !== null && previousCredentialId !== input.credentialId) {
|
|
61640
|
+
events.push({
|
|
61641
|
+
type: "codex.account.switched",
|
|
61642
|
+
clientEventId: `${key2}:switch`,
|
|
61643
|
+
payload: {
|
|
61644
|
+
fromAccountId: previousCredentialId,
|
|
61645
|
+
toAccountId: input.credentialId,
|
|
61646
|
+
reason: diagnostics.source === "manual_pin" ? "manual" : "rotation"
|
|
61647
|
+
}
|
|
61648
|
+
});
|
|
61649
|
+
}
|
|
61650
|
+
events.push({
|
|
61651
|
+
type: "codex.credential.selected",
|
|
61652
|
+
clientEventId: key2,
|
|
61653
|
+
payload: {
|
|
61654
|
+
credentialId: input.credentialId,
|
|
61655
|
+
strategy: input.strategy,
|
|
61656
|
+
...diagnostics,
|
|
61657
|
+
previousCredentialId,
|
|
61658
|
+
eligibleCount: input.eligibleCount,
|
|
61659
|
+
connectedCount: input.connectedCount,
|
|
61660
|
+
reused: input.reusedLease
|
|
61661
|
+
}
|
|
61662
|
+
});
|
|
61663
|
+
await tx.update(sessions).set({ codexLastCredentialId: input.credentialId }).where(
|
|
61664
|
+
and56(
|
|
61665
|
+
eq57(sessions.workspaceId, input.workspaceId),
|
|
61666
|
+
eq57(sessions.id, input.sessionId)
|
|
61667
|
+
)
|
|
61668
|
+
);
|
|
61669
|
+
const appended = await appendSessionEventsForTurnAttempt(
|
|
61670
|
+
tx,
|
|
61671
|
+
input.workspaceId,
|
|
61672
|
+
input.sessionId,
|
|
61673
|
+
input.turnId,
|
|
61674
|
+
input.executionGeneration,
|
|
61675
|
+
input.attemptId,
|
|
61676
|
+
events
|
|
61677
|
+
);
|
|
61678
|
+
if (!appended.accepted) throw new CodexCredentialLeaseAttemptFencedError();
|
|
61679
|
+
return { events: appended.events, diagnostics };
|
|
61680
|
+
});
|
|
61681
|
+
}
|
|
61551
61682
|
async function recordSessionActiveCodexCredential(db, workspaceId, sessionId, credentialId) {
|
|
61552
61683
|
await withWorkspaceSessionActivityRls(db, workspaceId, async (scopedDb) => {
|
|
61553
61684
|
await scopedDb.update(sessions).set({ codexLastCredentialId: credentialId }).where(
|
|
@@ -64990,9 +65121,95 @@ async function listSessionsForSubject(db, workspaceId, options) {
|
|
|
64990
65121
|
}
|
|
64991
65122
|
throw new Error("unreachable session list retry state");
|
|
64992
65123
|
}
|
|
65124
|
+
async function sessionFailureDiagnostics(db, workspaceId, session) {
|
|
65125
|
+
if (session.status !== "failed") return null;
|
|
65126
|
+
const scope = and56(
|
|
65127
|
+
eq57(sessionEvents.workspaceId, workspaceId),
|
|
65128
|
+
eq57(sessionEvents.sessionId, session.id),
|
|
65129
|
+
lte4(sessionEvents.sequence, session.lastSequence),
|
|
65130
|
+
isNull16(sessionEvents.duplicateOfEventId),
|
|
65131
|
+
or12(
|
|
65132
|
+
isNull16(sessionEvents.turnAssociation),
|
|
65133
|
+
eq57(sessionEvents.turnAssociation, "current")
|
|
65134
|
+
)
|
|
65135
|
+
);
|
|
65136
|
+
const maxChars = 1024;
|
|
65137
|
+
const storageChars = sessionTextStoragePrefixChars(maxChars);
|
|
65138
|
+
const diagnosticFields = ["error", "message", "detail", "lastRetryableError", "code", "status"];
|
|
65139
|
+
const boundedFields = diagnosticFields.flatMap((field) => {
|
|
65140
|
+
const scalar = sql81`case when jsonb_typeof(${sessionEvents.payload}->${field}::text) = 'string'
|
|
65141
|
+
then ${sessionEvents.payload}->>${field}::text else null end`;
|
|
65142
|
+
return [
|
|
65143
|
+
sql81`${field}::text`,
|
|
65144
|
+
sql81`jsonb_build_object(
|
|
65145
|
+
'prefix', left(${scalar}, ${storageChars}), 'chars', char_length(${scalar}),
|
|
65146
|
+
'encoded', ${sessionMcpProgressScalarIsEncodedSql(scalar, sessionEvents.payloadCodecVersion)})`
|
|
65147
|
+
];
|
|
65148
|
+
});
|
|
65149
|
+
const projectedPayload = sql81`jsonb_build_object(
|
|
65150
|
+
${sql81.join(boundedFields, sql81`, `)},
|
|
65151
|
+
'providerRecoveryCount', case when jsonb_typeof(${sessionEvents.payload}->'providerRecoveryCount') = 'number'
|
|
65152
|
+
and length((${sessionEvents.payload}->'providerRecoveryCount')::text) <= 16
|
|
65153
|
+
then ${sessionEvents.payload}->'providerRecoveryCount' else 'null'::jsonb end)`;
|
|
65154
|
+
const latest = async (type) => {
|
|
65155
|
+
const [row] = await db.select({
|
|
65156
|
+
id: sessionEvents.id,
|
|
65157
|
+
sequence: sessionEvents.sequence,
|
|
65158
|
+
turnId: sessionEvents.turnId,
|
|
65159
|
+
occurredAt: sessionEvents.occurredAt,
|
|
65160
|
+
codecVersion: sessionEvents.payloadCodecVersion,
|
|
65161
|
+
payload: projectedPayload
|
|
65162
|
+
}).from(sessionEvents).where(and56(scope, eq57(sessionEvents.type, type))).orderBy(desc22(sessionEvents.sequence)).limit(1);
|
|
65163
|
+
if (!row) return null;
|
|
65164
|
+
const payload = {
|
|
65165
|
+
providerRecoveryCount: row.payload.providerRecoveryCount
|
|
65166
|
+
};
|
|
65167
|
+
const truncatedFields = [];
|
|
65168
|
+
for (const field of diagnosticFields) {
|
|
65169
|
+
const stored = row.payload[field];
|
|
65170
|
+
const projected = projectSessionTextPrefix(
|
|
65171
|
+
stored.prefix,
|
|
65172
|
+
stored.chars,
|
|
65173
|
+
row.codecVersion,
|
|
65174
|
+
stored.encoded,
|
|
65175
|
+
maxChars
|
|
65176
|
+
);
|
|
65177
|
+
payload[field] = projected.text;
|
|
65178
|
+
if (projected.textTruncated || (projected.originalChars ?? 0) > maxChars)
|
|
65179
|
+
truncatedFields.push(field);
|
|
65180
|
+
}
|
|
65181
|
+
payload.projection = { fieldsOnly: true, fieldLimitChars: maxChars, truncatedFields };
|
|
65182
|
+
return { ...row, payload, occurredAt: row.occurredAt.toISOString() };
|
|
65183
|
+
};
|
|
65184
|
+
const turnFailure = await latest("turn.failed");
|
|
65185
|
+
const latestStatus = await latest("session.status.changed");
|
|
65186
|
+
const statusPayload = latestStatus?.payload;
|
|
65187
|
+
if (latestStatus && (!turnFailure || latestStatus.sequence > turnFailure.sequence) && typeof statusPayload?.status === "string" && statusPayload.status !== "failed")
|
|
65188
|
+
return null;
|
|
65189
|
+
const preClaimFailure = statusPayload?.status === "failed" && statusPayload.code === "pre_claim_failure" ? latestStatus : null;
|
|
65190
|
+
const failure = preClaimFailure && (!turnFailure || preClaimFailure.sequence > turnFailure.sequence && (!preClaimFailure.turnId || preClaimFailure.turnId !== turnFailure.turnId)) ? preClaimFailure : turnFailure;
|
|
65191
|
+
if (!failure) return null;
|
|
65192
|
+
return {
|
|
65193
|
+
eventId: failure.id,
|
|
65194
|
+
sequence: failure.sequence,
|
|
65195
|
+
turnId: failure.turnId ?? null,
|
|
65196
|
+
occurredAt: failure.occurredAt,
|
|
65197
|
+
payload: failure.payload
|
|
65198
|
+
};
|
|
65199
|
+
}
|
|
64993
65200
|
async function getSessionForSubject(db, workspaceId, sessionId, subjectId, relatedSessionAccess = "root") {
|
|
64994
65201
|
return await withWorkspaceSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
64995
|
-
const [row] = await scopedDb.select({
|
|
65202
|
+
const [row] = await scopedDb.select({
|
|
65203
|
+
session: sessions,
|
|
65204
|
+
pin: sessionPins,
|
|
65205
|
+
cursor: sessionEventCursors
|
|
65206
|
+
}).from(sessions).leftJoin(
|
|
65207
|
+
sessionEventCursors,
|
|
65208
|
+
and56(
|
|
65209
|
+
eq57(sessionEventCursors.workspaceId, sessions.workspaceId),
|
|
65210
|
+
eq57(sessionEventCursors.sessionId, sessions.id)
|
|
65211
|
+
)
|
|
65212
|
+
).leftJoin(
|
|
64996
65213
|
sessionPins,
|
|
64997
65214
|
and56(
|
|
64998
65215
|
eq57(sessionPins.workspaceId, workspaceId),
|
|
@@ -65014,25 +65231,36 @@ async function getSessionForSubject(db, workspaceId, sessionId, subjectId, relat
|
|
|
65014
65231
|
)
|
|
65015
65232
|
).limit(1);
|
|
65016
65233
|
if (!row) return null;
|
|
65017
|
-
|
|
65018
|
-
|
|
65019
|
-
|
|
65234
|
+
if (!row.cursor || row.cursor.accountId !== row.session.accountId || row.cursor.lastSequence < row.session.lastSequence) {
|
|
65235
|
+
throw new Error(`Session event cursor invariant failed for session ${sessionId}`);
|
|
65236
|
+
}
|
|
65237
|
+
const [session] = await withLatestStartedSessionPolicy(
|
|
65238
|
+
scopedDb,
|
|
65239
|
+
workspaceId,
|
|
65240
|
+
await withCurrentSessionInputWait(scopedDb, workspaceId, [
|
|
65241
|
+
{ ...row.session, lastSequence: row.cursor.lastSequence }
|
|
65242
|
+
])
|
|
65243
|
+
);
|
|
65020
65244
|
if (!session) throw new Error(`Session event cursor missing for session ${sessionId}`);
|
|
65021
65245
|
const mcpServers = await sessionMcpServerMetadataForSessions(scopedDb, workspaceId, [
|
|
65022
65246
|
sessionId
|
|
65023
65247
|
]);
|
|
65024
65248
|
const tenancyActivated = await sessionTenancyProductActivated(scopedDb, workspaceId);
|
|
65249
|
+
const failureDiagnostics = await sessionFailureDiagnostics(scopedDb, workspaceId, session);
|
|
65025
65250
|
return projectSessionForRelatedAccess(
|
|
65026
|
-
|
|
65027
|
-
|
|
65028
|
-
|
|
65029
|
-
|
|
65030
|
-
|
|
65031
|
-
|
|
65032
|
-
|
|
65033
|
-
|
|
65034
|
-
|
|
65035
|
-
|
|
65251
|
+
{
|
|
65252
|
+
...await mapSessionWithControl(
|
|
65253
|
+
scopedDb,
|
|
65254
|
+
session,
|
|
65255
|
+
mcpServers.get(sessionId) ?? [],
|
|
65256
|
+
mapSessionPin(row.pin),
|
|
65257
|
+
mapSessionAttention(session, row.pin),
|
|
65258
|
+
mapSessionArchive(row.pin),
|
|
65259
|
+
void 0,
|
|
65260
|
+
{ subjectId, activated: tenancyActivated }
|
|
65261
|
+
),
|
|
65262
|
+
failureDiagnostics
|
|
65263
|
+
},
|
|
65036
65264
|
relatedSessionAccess
|
|
65037
65265
|
);
|
|
65038
65266
|
});
|
|
@@ -69869,14 +70097,7 @@ function archiveCaptureRemainingMs(row) {
|
|
|
69869
70097
|
return null;
|
|
69870
70098
|
}
|
|
69871
70099
|
const remainingMs = Number(row.archive_capture_remaining_ms);
|
|
69872
|
-
return Number.isSafeInteger(remainingMs) && remainingMs >= 0 && remainingMs <=
|
|
69873
|
-
}
|
|
69874
|
-
function extendSandboxTransitionDeadline(currentDeadline, hardDeadline, remainingCaptureMs, now) {
|
|
69875
|
-
if (remainingCaptureMs === null || remainingCaptureMs === void 0) return currentDeadline;
|
|
69876
|
-
return Math.min(
|
|
69877
|
-
hardDeadline,
|
|
69878
|
-
Math.max(currentDeadline, now + remainingCaptureMs + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS)
|
|
69879
|
-
);
|
|
70100
|
+
return Number.isSafeInteger(remainingMs) && remainingMs >= 0 && remainingMs <= SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS2 ? remainingMs : null;
|
|
69880
70101
|
}
|
|
69881
70102
|
function providerlessLeaseBackendPredicateSql() {
|
|
69882
70103
|
const providerlessBackends = Object.entries(SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND).filter(([, fields]) => fields.length === 0).map(([backend]) => backend);
|
|
@@ -70352,25 +70573,19 @@ async function acquireLease(db, input) {
|
|
|
70352
70573
|
throw new Error("Sandbox lease archive capture wait is invalid");
|
|
70353
70574
|
}
|
|
70354
70575
|
const startedAt = performance.now();
|
|
70355
|
-
const
|
|
70356
|
-
let deadline = Math.min(hardDeadline, startedAt + captureWaitMs);
|
|
70576
|
+
const waitBudget = new SandboxTransitionWaitBudget(captureWaitMs, startedAt);
|
|
70357
70577
|
let delayMs = 25;
|
|
70358
70578
|
for (; ; ) {
|
|
70359
70579
|
const result = await acquireLeaseOnce(db, input);
|
|
70360
70580
|
const now = performance.now();
|
|
70361
70581
|
if (captureWaitMs > 0 && result.role === "fenced" && result.reason === "capture_in_progress") {
|
|
70362
|
-
|
|
70363
|
-
deadline,
|
|
70364
|
-
hardDeadline,
|
|
70365
|
-
result.captureRemainingMs,
|
|
70366
|
-
now
|
|
70367
|
-
);
|
|
70582
|
+
waitBudget.observeCapture(result.captureRemainingMs, now);
|
|
70368
70583
|
}
|
|
70369
|
-
if (result.role !== "fenced" || result.reason === "superseded" || result.reason === "rotation_in_progress" || now >= deadline) {
|
|
70584
|
+
if (result.role !== "fenced" || result.reason === "superseded" || result.reason === "rotation_in_progress" || now >= waitBudget.deadline) {
|
|
70370
70585
|
return result;
|
|
70371
70586
|
}
|
|
70372
70587
|
await waitForSandboxTransition(
|
|
70373
|
-
Math.min(delayMs, Math.max(1, deadline - now)),
|
|
70588
|
+
Math.min(delayMs, Math.max(1, waitBudget.deadline - now)),
|
|
70374
70589
|
input.waitSignal,
|
|
70375
70590
|
"Sandbox lease transition wait cancelled"
|
|
70376
70591
|
);
|
|
@@ -74146,8 +74361,7 @@ async function advanceWorkspaceGenerationForAuthority(db, authority, operationIn
|
|
|
74146
74361
|
throw new Error("Workspace archive capture wait is invalid");
|
|
74147
74362
|
}
|
|
74148
74363
|
const startedAt = performance.now();
|
|
74149
|
-
const
|
|
74150
|
-
let deadline = Math.min(hardDeadline, startedAt + captureWaitMs);
|
|
74364
|
+
const waitBudget = new SandboxTransitionWaitBudget(captureWaitMs, startedAt);
|
|
74151
74365
|
let delayMs = 25;
|
|
74152
74366
|
let captureWaitStartedAt;
|
|
74153
74367
|
let captureWaitOutcome = "failed";
|
|
@@ -74164,19 +74378,14 @@ async function advanceWorkspaceGenerationForAuthority(db, authority, operationIn
|
|
|
74164
74378
|
} catch (error) {
|
|
74165
74379
|
const now = performance.now();
|
|
74166
74380
|
if (captureWaitMs > 0 && error instanceof SandboxWorkspaceMutationFencedError && error.code === "capture_in_progress") {
|
|
74167
|
-
|
|
74168
|
-
deadline,
|
|
74169
|
-
hardDeadline,
|
|
74170
|
-
error.captureRemainingMs,
|
|
74171
|
-
now
|
|
74172
|
-
);
|
|
74381
|
+
waitBudget.observeCapture(error.captureRemainingMs, now);
|
|
74173
74382
|
}
|
|
74174
|
-
if (!(error instanceof SandboxWorkspaceMutationFencedError) || error.code !== "capture_in_progress" || now >= deadline) {
|
|
74383
|
+
if (!(error instanceof SandboxWorkspaceMutationFencedError) || error.code !== "capture_in_progress" || now >= waitBudget.deadline) {
|
|
74175
74384
|
throw error;
|
|
74176
74385
|
}
|
|
74177
74386
|
captureWaitStartedAt ??= performance.now();
|
|
74178
74387
|
await waitForSandboxTransition(
|
|
74179
|
-
Math.min(delayMs, Math.max(1, deadline - now)),
|
|
74388
|
+
Math.min(delayMs, Math.max(1, waitBudget.deadline - now)),
|
|
74180
74389
|
waitSignal,
|
|
74181
74390
|
"Sandbox workspace mutation wait cancelled"
|
|
74182
74391
|
);
|
|
@@ -75422,7 +75631,7 @@ async function claimWorkspaceArchiveCapture(db, input) {
|
|
|
75422
75631
|
throw new Error("Workspace archive capture timeout is invalid");
|
|
75423
75632
|
}
|
|
75424
75633
|
const operationId = input.operationId ?? input.captureId;
|
|
75425
|
-
const providerRequestId =
|
|
75634
|
+
const providerRequestId = randomUUID12();
|
|
75426
75635
|
const captureAttempt = input.attempt ?? 1;
|
|
75427
75636
|
const providerReplaySafe = input.providerReplaySafe === true;
|
|
75428
75637
|
const takeoverSafe = input.takeoverSafe === true;
|
|
@@ -93323,6 +93532,7 @@ export {
|
|
|
93323
93532
|
closeSessionTurnAttemptInTransaction,
|
|
93324
93533
|
closeSlackInteractionDelivery,
|
|
93325
93534
|
codexCapacityRefreshBackoffMs,
|
|
93535
|
+
codexSelectionDiagnostics,
|
|
93326
93536
|
commitBrowserRevisionPublication,
|
|
93327
93537
|
commitBrowserSessionSuspension,
|
|
93328
93538
|
commitBrowserStateUploadInTransaction,
|
|
@@ -94293,6 +94503,7 @@ export {
|
|
|
94293
94503
|
recordRetainedProcessReconciliationProof,
|
|
94294
94504
|
recordRetainedScreenshotArtifactError,
|
|
94295
94505
|
recordSessionActiveCodexCredential,
|
|
94506
|
+
recordSessionCodexSelectionForTurnAttempt,
|
|
94296
94507
|
recordSessionGoalProgressWithEvent,
|
|
94297
94508
|
recordSkippedContextCompaction,
|
|
94298
94509
|
recordSlackBotInstallCallbackFailure,
|