@opengeni/core 0.28.0 → 1.0.0
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/README.md +1 -1
- package/dist/access/external-actor-authority.d.ts +47 -0
- package/dist/access/index.d.ts +182 -3
- package/dist/access/service-initiator.d.ts +3 -0
- package/dist/application/api-integration-servers.d.ts +20 -0
- package/dist/application/archived-session-imports.d.ts +6 -0
- package/dist/application/composer-submit.d.ts +24 -0
- package/dist/application/connect-authority.d.ts +13 -0
- package/dist/application/connect-operation.d.ts +31 -0
- package/dist/application/external-continuation.d.ts +15 -0
- package/dist/application/external-identity-lifecycle.d.ts +20 -0
- package/dist/application/external-link-work-admission.d.ts +12 -0
- package/dist/application/external-workspace-members.d.ts +34 -0
- package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
- package/dist/application/new-session-drafts.d.ts +39 -2
- package/dist/application/sandbox-recovery.d.ts +58 -0
- package/dist/application/session-commands.d.ts +26 -13
- package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
- package/dist/application/session-tenancy.d.ts +19 -0
- package/dist/application/user-resource-grants.d.ts +20 -0
- package/dist/billing/limits.d.ts +11 -0
- package/dist/canonical-human-identities.d.ts +7 -2
- package/dist/canonical-human-identities.js +17 -4
- package/dist/canonical-human-identities.js.map +1 -1
- package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
- package/dist/chunk-2MKMRGSY.js.map +1 -0
- package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
- package/dist/chunk-GVO3N37G.js.map +1 -0
- package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
- package/dist/chunk-LCQXNR62.js.map +1 -0
- package/dist/chunk-MM6N7CTO.js +387 -0
- package/dist/chunk-MM6N7CTO.js.map +1 -0
- package/dist/chunk-QO5GVFFO.js +235 -0
- package/dist/chunk-QO5GVFFO.js.map +1 -0
- package/dist/codex-model-availability.d.ts +14 -0
- package/dist/default-session-model.d.ts +139 -0
- package/dist/dependencies.d.ts +87 -4
- package/dist/developer-setup-credential-ceiling.d.ts +10 -0
- package/dist/domain/agent-config-resolution.d.ts +49 -0
- package/dist/domain/automations.d.ts +58 -0
- package/dist/domain/capabilities.d.ts +22 -4
- package/dist/domain/company-profile-agent-admin.d.ts +25 -0
- package/dist/domain/connection-account-selection-error.d.ts +8 -0
- package/dist/domain/connector-tool-permissions.d.ts +36 -0
- package/dist/domain/direct-model-provider.d.ts +3 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
- package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
- package/dist/domain/editable-artifacts/ports.d.ts +10 -0
- package/dist/domain/editable-artifacts/service.d.ts +4 -3
- package/dist/domain/environments.d.ts +5 -9
- package/dist/domain/external-creation-attribution.d.ts +6 -0
- package/dist/domain/file-owner.d.ts +8 -0
- package/dist/domain/github-action-policies.d.ts +36 -0
- package/dist/domain/github-repository-bindings.d.ts +117 -0
- package/dist/domain/github-skill-source.d.ts +5 -0
- package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights-usage.d.ts +27 -0
- package/dist/domain/insights.d.ts +28 -1
- package/dist/domain/knowledge-files.d.ts +20 -0
- package/dist/domain/knowledge-messages.d.ts +39 -0
- package/dist/domain/knowledge-messages.test.d.ts +1 -0
- package/dist/domain/knowledge-preparation.d.ts +10 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/knowledge-search.d.ts +15 -0
- package/dist/domain/knowledge-search.test.d.ts +1 -0
- package/dist/domain/knowledge.d.ts +9 -0
- package/dist/domain/mcp-account-bindings.d.ts +19 -0
- package/dist/domain/mcp-account-routes.d.ts +21 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
- package/dist/domain/personal-connection-delegations.d.ts +87 -4
- package/dist/domain/pr-review.d.ts +249 -0
- package/dist/domain/resources.d.ts +24 -3
- package/dist/domain/scheduled-task-access.d.ts +247 -0
- package/dist/domain/scheduled-tasks.d.ts +103 -6
- package/dist/domain/session-connection-accounts.d.ts +16 -0
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/session-native-connection-replacement.d.ts +14 -0
- package/dist/domain/session-start-timing.d.ts +7 -0
- package/dist/domain/session-tool-policy.d.ts +93 -4
- package/dist/domain/sessions.d.ts +275 -28
- package/dist/domain/skill-imports.d.ts +1 -0
- package/dist/domain/skill-search.d.ts +30 -0
- package/dist/domain/skills.d.ts +117 -0
- package/dist/domain/slack-bot.d.ts +46 -1
- package/dist/domain/workspace-members.d.ts +8 -1
- package/dist/editable-artifact-live/application.d.ts +12 -3
- package/dist/editable-artifact-live/ports.d.ts +0 -1
- package/dist/editable-artifact-live/server.d.ts +5 -2
- package/dist/editable-artifact-live/ticket.d.ts +2 -2
- package/dist/editable-artifact-live/types.d.ts +14 -2
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +10 -4
- package/dist/index.d.ts +49 -3
- package/dist/index.js +18826 -6847
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.d.ts +131 -0
- package/dist/managed-auth-session-sets.js +51 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/managed-session.d.ts +98 -11
- package/dist/model-catalog.d.ts +117 -0
- package/dist/product-usage-metrics.d.ts +30 -0
- package/dist/rigs/index.d.ts +22 -5
- package/dist/rigs/provider-images.d.ts +1 -1
- package/dist/sandbox/fleet.d.ts +59 -10
- package/dist/sandbox/routing.d.ts +22 -2
- package/dist/sandbox/runtime-settings.d.ts +56 -0
- package/dist/session-authorization.d.ts +38 -1
- package/dist/site-session-origin.d.ts +9 -0
- package/dist/transcription.d.ts +12 -1
- package/dist/turn-surface.d.ts +18 -0
- package/dist/user-presence.d.ts +32 -0
- package/package.json +18 -11
- package/src/access/external-actor-authority.ts +94 -0
- package/src/access/index.ts +1386 -44
- package/src/access/service-initiator.ts +50 -0
- package/src/application/api-integration-servers.ts +215 -0
- package/src/application/archived-session-imports.ts +161 -0
- package/src/application/composer-submit.ts +77 -0
- package/src/application/connect-authority.ts +77 -0
- package/src/application/connect-operation.ts +80 -0
- package/src/application/external-continuation.ts +129 -0
- package/src/application/external-identity-lifecycle.ts +48 -0
- package/src/application/external-link-work-admission.ts +90 -0
- package/src/application/external-workspace-members.ts +232 -0
- package/src/application/modal-native-worker-host-transport.ts +263 -0
- package/src/application/new-session-drafts.ts +222 -29
- package/src/application/sandbox-recovery.ts +78 -0
- package/src/application/session-commands.ts +430 -73
- package/src/application/session-mcp-credential-rotation.ts +217 -0
- package/src/application/session-tenancy.ts +445 -0
- package/src/application/user-resource-grants.ts +154 -0
- package/src/billing/limits.ts +103 -24
- package/src/canonical-human-identities.ts +22 -3
- package/src/codex-model-availability.ts +136 -0
- package/src/default-session-model.ts +656 -0
- package/src/dependencies.ts +97 -5
- package/src/developer-setup-credential-ceiling.ts +60 -0
- package/src/domain/agent-config-resolution.ts +148 -0
- package/src/domain/automations.ts +270 -0
- package/src/domain/capabilities.ts +244 -151
- package/src/domain/company-profile-agent-admin.ts +56 -0
- package/src/domain/connection-account-selection-error.ts +19 -0
- package/src/domain/connector-tool-permissions.ts +371 -0
- package/src/domain/direct-model-provider.ts +62 -0
- package/src/domain/editable-artifacts/agent-application.ts +61 -15
- package/src/domain/editable-artifacts/durable-export.ts +100 -5
- package/src/domain/editable-artifacts/in-memory.ts +6 -0
- package/src/domain/editable-artifacts/ports.ts +11 -0
- package/src/domain/editable-artifacts/service.ts +32 -1
- package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
- package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
- package/src/domain/environments.ts +25 -45
- package/src/domain/external-creation-attribution.ts +22 -0
- package/src/domain/file-owner.ts +67 -0
- package/src/domain/github-action-policies.ts +169 -0
- package/src/domain/github-repository-bindings.ts +295 -0
- package/src/domain/github-skill-source.ts +233 -0
- package/src/domain/governed-learning-slack-publication.ts +157 -0
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights-usage.ts +67 -0
- package/src/domain/insights.ts +270 -169
- package/src/domain/knowledge-files.ts +46 -0
- package/src/domain/knowledge-messages.test.ts +91 -0
- package/src/domain/knowledge-messages.ts +106 -0
- package/src/domain/knowledge-preparation.test.ts +276 -0
- package/src/domain/knowledge-preparation.ts +147 -0
- package/src/domain/knowledge-search.test.ts +561 -0
- package/src/domain/knowledge-search.ts +256 -0
- package/src/domain/knowledge.ts +133 -0
- package/src/domain/mcp-account-bindings.ts +171 -0
- package/src/domain/mcp-account-routes.ts +121 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +88 -0
- package/src/domain/organization-membership-lifecycle.ts +10 -0
- package/src/domain/personal-connection-delegations.ts +758 -115
- package/src/domain/pr-review.ts +687 -0
- package/src/domain/resources.ts +182 -25
- package/src/domain/scheduled-task-access.ts +1052 -0
- package/src/domain/scheduled-tasks.ts +1952 -215
- package/src/domain/session-connection-accounts.ts +67 -0
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-native-connection-replacement.ts +64 -0
- package/src/domain/session-start-timing.ts +55 -0
- package/src/domain/session-tool-policy.ts +443 -19
- package/src/domain/sessions.ts +3417 -619
- package/src/domain/skill-imports.ts +137 -17
- package/src/domain/skill-search.ts +221 -0
- package/src/domain/skills.ts +104 -0
- package/src/domain/slack-bot.ts +128 -3
- package/src/domain/workspace-members.ts +34 -2
- package/src/editable-artifact-live/application.ts +29 -4
- package/src/editable-artifact-live/ports.ts +0 -1
- package/src/editable-artifact-live/server.ts +167 -13
- package/src/editable-artifact-live/ticket.ts +87 -6
- package/src/editable-artifact-live/types.ts +16 -2
- package/src/editable-artifact-live/wire.ts +0 -10
- package/src/index.ts +69 -4
- package/src/managed-auth-session-sets.ts +371 -0
- package/src/managed-session.ts +520 -2
- package/src/model-catalog.ts +873 -0
- package/src/product-usage-metrics.ts +101 -0
- package/src/rigs/index.ts +179 -63
- package/src/rigs/provider-images.ts +2 -3
- package/src/sandbox/fleet.ts +471 -59
- package/src/sandbox/routing.ts +160 -33
- package/src/sandbox/runtime-settings.ts +281 -0
- package/src/session-authorization.ts +262 -78
- package/src/site-session-origin.ts +22 -0
- package/src/transcription.ts +13 -1
- package/src/turn-surface.ts +45 -0
- package/src/user-presence.ts +134 -0
- package/dist/chunk-6WKPWE5S.js.map +0 -1
- package/dist/chunk-IBOEYG6N.js +0 -34
- package/dist/chunk-IBOEYG6N.js.map +0 -1
- package/dist/chunk-JJU6I5XD.js.map +0 -1
- package/dist/chunk-NYPMQ7NO.js.map +0 -1
- package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
- package/dist/domain/packs.d.ts +0 -55
- package/src/domain/durable-learning-slack-publication.ts +0 -202
- package/src/domain/packs.ts +0 -556
package/src/sandbox/routing.ts
CHANGED
|
@@ -12,20 +12,28 @@
|
|
|
12
12
|
// over the events bus) lives here, not in the leaf (which stays db-free).
|
|
13
13
|
|
|
14
14
|
import { sandboxLifecycleTransitionWaitMs, type Settings } from "@opengeni/config";
|
|
15
|
+
import { appendSessionCommandOutput } from "@opengeni/db/session-command-output";
|
|
16
|
+
import {
|
|
17
|
+
createProviderCommandRetainer,
|
|
18
|
+
supervisedCommandProtocolReady,
|
|
19
|
+
} from "@opengeni/db/retained-provider-commands";
|
|
15
20
|
import {
|
|
16
21
|
advanceWorkspaceGenerationForDirectRequest,
|
|
22
|
+
retainedProviderCommandPersistence,
|
|
17
23
|
advanceWorkspaceGenerationForRetainedProcess,
|
|
18
|
-
|
|
24
|
+
getLiveEnrollmentConnection,
|
|
19
25
|
getRetainedProcess,
|
|
20
26
|
getSandbox,
|
|
21
27
|
markWarmLeaseInstanceLost,
|
|
22
28
|
readActiveSandbox,
|
|
23
29
|
retainWorkspaceMutationProcess,
|
|
30
|
+
SandboxRetainedProcessPromotionFencedError,
|
|
24
31
|
retainedProcessSettlementIdentity,
|
|
25
32
|
settleRetainedProcess,
|
|
26
33
|
verifyDirectWorkspaceMutationSettlement,
|
|
27
34
|
verifyRetainedProcessMutationSettlement,
|
|
28
35
|
type Database,
|
|
36
|
+
type SandboxRetainedProcess,
|
|
29
37
|
type SandboxWorkspaceMutationAdmission,
|
|
30
38
|
} from "@opengeni/db";
|
|
31
39
|
import { appendAndPublishEvents, type EventBus } from "@opengeni/events";
|
|
@@ -45,7 +53,9 @@ import {
|
|
|
45
53
|
type RoutingRetainedProcess,
|
|
46
54
|
type RoutingRetainedProcessTerminalProof,
|
|
47
55
|
type RoutingSandboxOperationObserver,
|
|
56
|
+
type RoutingSandboxCaptureWaitObserver,
|
|
48
57
|
type SelfhostedRelayConfig,
|
|
58
|
+
type SelfhostedConnectionBinding,
|
|
49
59
|
type SelfhostedOpStreamDeps,
|
|
50
60
|
} from "@opengeni/runtime/sandbox";
|
|
51
61
|
|
|
@@ -56,11 +66,59 @@ type PersistableMutationAdmission = {
|
|
|
56
66
|
> | null;
|
|
57
67
|
};
|
|
58
68
|
|
|
69
|
+
const retainWorkspaceProviderCommand = createProviderCommandRetainer(
|
|
70
|
+
retainWorkspaceMutationProcess,
|
|
71
|
+
(error) => (error instanceof SandboxRetainedProcessPromotionFencedError ? error.process : null),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
type DirectRetainedProcessRoute = {
|
|
75
|
+
providerSessionId: number;
|
|
76
|
+
providerBackend: string;
|
|
77
|
+
providerInstanceId: string;
|
|
78
|
+
leaseEpoch: number;
|
|
79
|
+
routeKind: "home" | "active";
|
|
80
|
+
routeTargetId: string | null;
|
|
81
|
+
routeEpoch: number;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** API-direct requests always use active-route authority. The default active
|
|
85
|
+
* pointer is represented by a null target id; it is not a home-route write. */
|
|
86
|
+
export function directRetainedProcessMatchesBackend(
|
|
87
|
+
durable: DirectRetainedProcessRoute,
|
|
88
|
+
process: RoutingRetainedProcess,
|
|
89
|
+
backend: ResolvedActiveBackend,
|
|
90
|
+
): boolean {
|
|
91
|
+
return (
|
|
92
|
+
durable.providerSessionId === process.providerSessionId &&
|
|
93
|
+
durable.providerBackend === backend.kind &&
|
|
94
|
+
durable.providerInstanceId === backend.providerInstanceId &&
|
|
95
|
+
durable.leaseEpoch === backend.leaseEpoch &&
|
|
96
|
+
durable.routeKind === "active" &&
|
|
97
|
+
durable.routeTargetId === backend.sandboxId &&
|
|
98
|
+
durable.routeEpoch === backend.activeEpoch
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function retainedProcessBackgroundSettlement(
|
|
103
|
+
process: Pick<SandboxRetainedProcess, "state" | "exitCode" | "settlementReason">,
|
|
104
|
+
fallback: RoutingRetainedProcessTerminalProof,
|
|
105
|
+
): { outcome: "exited" | "lost"; exitCode: number | null; reason: string } {
|
|
106
|
+
if (process.state === "active") {
|
|
107
|
+
throw new Error("Retained-process settlement returned an active durable process");
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
outcome: process.state,
|
|
111
|
+
exitCode: process.exitCode,
|
|
112
|
+
reason: process.settlementReason ?? fallback.reason,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
59
116
|
export type ChannelARoutingServices = {
|
|
60
117
|
db: Database;
|
|
61
118
|
settings: Settings;
|
|
62
119
|
bus?: EventBus;
|
|
63
120
|
onSandboxOperation?: RoutingSandboxOperationObserver;
|
|
121
|
+
onSandboxCaptureWait?: RoutingSandboxCaptureWaitObserver;
|
|
64
122
|
waitSignal?: AbortSignal;
|
|
65
123
|
};
|
|
66
124
|
|
|
@@ -118,24 +176,31 @@ function controlRpcFactory(bus: EventBus | undefined): () => ControlRpc {
|
|
|
118
176
|
});
|
|
119
177
|
}
|
|
120
178
|
|
|
121
|
-
async function
|
|
179
|
+
async function resolveSelfhostedConnection(
|
|
122
180
|
services: ChannelARoutingServices,
|
|
123
|
-
workspaceId: string,
|
|
181
|
+
access: string | { accountId: string; workspaceId: string; subjectId: string },
|
|
124
182
|
sandbox: RoutableSandbox,
|
|
125
|
-
): Promise<
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
183
|
+
): Promise<SelfhostedConnectionBinding | null> {
|
|
184
|
+
if (!sandbox.enrollmentId) return null;
|
|
185
|
+
const enrollment = await getLiveEnrollmentConnection(services.db, access, sandbox.enrollmentId);
|
|
186
|
+
if (!enrollment?.connectionInstanceId) return null;
|
|
187
|
+
const opStream: SelfhostedOpStreamDeps | undefined =
|
|
188
|
+
services.settings.agentOpStreamEnabled === true &&
|
|
189
|
+
services.bus?.getOpStreamConnection &&
|
|
190
|
+
enrollment.opStream === true
|
|
191
|
+
? {
|
|
192
|
+
transport: new NatsOpStreamTransport(
|
|
193
|
+
async () => services.bus?.getOpStreamConnection?.() ?? null,
|
|
194
|
+
),
|
|
195
|
+
}
|
|
196
|
+
: undefined;
|
|
135
197
|
return {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
),
|
|
198
|
+
connectionInstanceId: enrollment.connectionInstanceId,
|
|
199
|
+
workspaceRoot: enrollment.workspaceRoot,
|
|
200
|
+
...(opStream ? { opStream } : {}),
|
|
201
|
+
operationResourcePolicy: enrollment.operationPolicy,
|
|
202
|
+
operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
|
|
203
|
+
operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
|
|
139
204
|
};
|
|
140
205
|
}
|
|
141
206
|
|
|
@@ -157,6 +222,7 @@ export function wrapChannelABoxWithRouting(
|
|
|
157
222
|
accountId: string;
|
|
158
223
|
workspaceId: string;
|
|
159
224
|
sessionId: string;
|
|
225
|
+
resourceSubjectId: string;
|
|
160
226
|
homeLease?: {
|
|
161
227
|
sandboxGroupId: string;
|
|
162
228
|
leaseEpoch: number;
|
|
@@ -209,6 +275,7 @@ export function wrapChannelABoxWithRouting(
|
|
|
209
275
|
sessionId: ids.sessionId,
|
|
210
276
|
requestId: ids.directRequest.requestId,
|
|
211
277
|
holderId: ids.directRequest.holderId,
|
|
278
|
+
initiatorSubjectId: ids.resourceSubjectId,
|
|
212
279
|
sandboxGroupId: homeLease.sandboxGroupId,
|
|
213
280
|
expectedEpoch: backend.leaseEpoch,
|
|
214
281
|
expectedInstanceId: backend.providerInstanceId,
|
|
@@ -232,7 +299,7 @@ export function wrapChannelABoxWithRouting(
|
|
|
232
299
|
op: string;
|
|
233
300
|
backend: ResolvedActiveBackend;
|
|
234
301
|
admission: unknown;
|
|
235
|
-
outcome: "resolved" | "rejected";
|
|
302
|
+
outcome: "resolved" | "rejected" | "outcome_unknown";
|
|
236
303
|
result?: unknown;
|
|
237
304
|
retainedProcess?: RoutingRetainedProcess;
|
|
238
305
|
}): Promise<void> => {
|
|
@@ -256,21 +323,29 @@ export function wrapChannelABoxWithRouting(
|
|
|
256
323
|
) {
|
|
257
324
|
throw new Error("API-direct workspace mutation settlement lacked its bound admission");
|
|
258
325
|
}
|
|
259
|
-
if (outcome === "resolved" && retainedProcess) {
|
|
260
|
-
await
|
|
326
|
+
if ((outcome === "resolved" || outcome === "outcome_unknown") && retainedProcess) {
|
|
327
|
+
await retainWorkspaceProviderCommand(db, {
|
|
261
328
|
accountId: ids.accountId,
|
|
262
329
|
workspaceId: ids.workspaceId,
|
|
263
330
|
sessionId: ids.sessionId,
|
|
264
331
|
processId: retainedProcess.id,
|
|
265
332
|
providerSessionId: retainedProcess.providerSessionId,
|
|
333
|
+
...(retainedProcess.providerCommand
|
|
334
|
+
? { providerCommand: retainedProcess.providerCommand }
|
|
335
|
+
: {}),
|
|
266
336
|
admissionId: exactAdmission.id,
|
|
267
337
|
admittedWorkspaceGeneration: exactAdmission.workspaceGeneration,
|
|
268
338
|
operation: op,
|
|
269
339
|
providerBinding: boundAdmission.providerBinding ?? null,
|
|
340
|
+
backgroundCommand: {
|
|
341
|
+
commandId: retainedProcess.id,
|
|
342
|
+
command: op,
|
|
343
|
+
},
|
|
270
344
|
owner: {
|
|
271
345
|
kind: "direct",
|
|
272
346
|
requestId: ids.directRequest.requestId,
|
|
273
347
|
holderId: ids.directRequest.holderId,
|
|
348
|
+
initiatorSubjectId: ids.resourceSubjectId,
|
|
274
349
|
sandboxGroupId: homeLease.sandboxGroupId,
|
|
275
350
|
expectedEpoch: backend.leaseEpoch,
|
|
276
351
|
expectedInstanceId: backend.providerInstanceId,
|
|
@@ -280,12 +355,17 @@ export function wrapChannelABoxWithRouting(
|
|
|
280
355
|
});
|
|
281
356
|
return;
|
|
282
357
|
}
|
|
358
|
+
if (outcome === "outcome_unknown")
|
|
359
|
+
throw new Error(
|
|
360
|
+
"Outcome-unknown command settlement requires its exact retained invocation",
|
|
361
|
+
);
|
|
283
362
|
await verifyDirectWorkspaceMutationSettlement(db, {
|
|
284
363
|
accountId: ids.accountId,
|
|
285
364
|
workspaceId: ids.workspaceId,
|
|
286
365
|
sessionId: ids.sessionId,
|
|
287
366
|
requestId: ids.directRequest.requestId,
|
|
288
367
|
holderId: ids.directRequest.holderId,
|
|
368
|
+
initiatorSubjectId: ids.resourceSubjectId,
|
|
289
369
|
sandboxGroupId: homeLease.sandboxGroupId,
|
|
290
370
|
expectedEpoch: backend.leaseEpoch,
|
|
291
371
|
expectedInstanceId: backend.providerInstanceId,
|
|
@@ -373,21 +453,12 @@ export function wrapChannelABoxWithRouting(
|
|
|
373
453
|
sessionId: ids.sessionId,
|
|
374
454
|
processId: process.id,
|
|
375
455
|
});
|
|
376
|
-
if (
|
|
377
|
-
!durable ||
|
|
378
|
-
durable.providerSessionId !== process.providerSessionId ||
|
|
379
|
-
durable.providerBackend !== backend.kind ||
|
|
380
|
-
durable.providerInstanceId !== backend.providerInstanceId ||
|
|
381
|
-
durable.leaseEpoch !== backend.leaseEpoch ||
|
|
382
|
-
durable.routeKind !== (backend.sandboxId === null ? "home" : "active") ||
|
|
383
|
-
durable.routeTargetId !== backend.sandboxId ||
|
|
384
|
-
durable.routeEpoch !== backend.activeEpoch
|
|
385
|
-
) {
|
|
456
|
+
if (!durable || !directRetainedProcessMatchesBackend(durable, process, backend)) {
|
|
386
457
|
throw new Error(
|
|
387
458
|
"API retained-process settlement lost its exact durable backend identity",
|
|
388
459
|
);
|
|
389
460
|
}
|
|
390
|
-
await settleRetainedProcess(db, {
|
|
461
|
+
const settlement = await settleRetainedProcess(db, {
|
|
391
462
|
accountId: ids.accountId,
|
|
392
463
|
workspaceId: ids.workspaceId,
|
|
393
464
|
sessionId: ids.sessionId,
|
|
@@ -398,6 +469,11 @@ export function wrapChannelABoxWithRouting(
|
|
|
398
469
|
reason: proof.reason,
|
|
399
470
|
idleGraceMs: settings.sandboxIdleGraceMs,
|
|
400
471
|
});
|
|
472
|
+
if (settlement.backgroundCommandEvents.length > 0 && bus) {
|
|
473
|
+
await bus
|
|
474
|
+
.publish(ids.workspaceId, ids.sessionId, settlement.backgroundCommandEvents)
|
|
475
|
+
.catch(() => undefined);
|
|
476
|
+
}
|
|
401
477
|
}
|
|
402
478
|
: undefined;
|
|
403
479
|
const resolver = makeActiveBackendResolver({
|
|
@@ -405,10 +481,19 @@ export function wrapChannelABoxWithRouting(
|
|
|
405
481
|
defaultBackend: established.session as RoutableBackendSession,
|
|
406
482
|
defaultKind: established.backendId,
|
|
407
483
|
getSandbox: async (sandboxId): Promise<RoutableSandbox | null> => {
|
|
408
|
-
const sandbox = await getSandbox(
|
|
484
|
+
const sandbox = await getSandbox(
|
|
485
|
+
db,
|
|
486
|
+
{
|
|
487
|
+
accountId: ids.accountId,
|
|
488
|
+
workspaceId: ids.workspaceId,
|
|
489
|
+
subjectId: ids.resourceSubjectId,
|
|
490
|
+
},
|
|
491
|
+
sandboxId,
|
|
492
|
+
);
|
|
409
493
|
return sandbox
|
|
410
494
|
? {
|
|
411
495
|
id: sandbox.id,
|
|
496
|
+
workspaceId: sandbox.workspaceId,
|
|
412
497
|
kind: sandbox.kind,
|
|
413
498
|
name: sandbox.name,
|
|
414
499
|
enrollmentId: sandbox.enrollmentId,
|
|
@@ -416,8 +501,16 @@ export function wrapChannelABoxWithRouting(
|
|
|
416
501
|
: null;
|
|
417
502
|
},
|
|
418
503
|
controlRpcFactory: controlRpcFactory(bus),
|
|
419
|
-
|
|
420
|
-
|
|
504
|
+
resolveSelfhostedConnection: (sandbox) =>
|
|
505
|
+
resolveSelfhostedConnection(
|
|
506
|
+
services,
|
|
507
|
+
{
|
|
508
|
+
accountId: ids.accountId,
|
|
509
|
+
workspaceId: ids.workspaceId,
|
|
510
|
+
subjectId: ids.resourceSubjectId,
|
|
511
|
+
},
|
|
512
|
+
sandbox,
|
|
513
|
+
),
|
|
421
514
|
relay: relayConfigFromSettings(settings),
|
|
422
515
|
selfhostedTimeoutMs: settings.sandboxSelfhostedControlTimeoutMs,
|
|
423
516
|
selfhostedExecTimeoutMs: settings.sandboxSelfhostedExecTimeoutMs,
|
|
@@ -444,6 +537,38 @@ export function wrapChannelABoxWithRouting(
|
|
|
444
537
|
});
|
|
445
538
|
|
|
446
539
|
const proxy = new RoutingSandboxSession({
|
|
540
|
+
providerSupervisionReady: async () =>
|
|
541
|
+
settings.modalCommandSupervisionEnabled && (await supervisedCommandProtocolReady(db)),
|
|
542
|
+
providerCommandHandle: (value) =>
|
|
543
|
+
value && typeof value === "object"
|
|
544
|
+
? (value as PersistableMutationAdmission).admission?.workspaceGeneration
|
|
545
|
+
: undefined,
|
|
546
|
+
providerCommandPersistence: (process) =>
|
|
547
|
+
retainedProviderCommandPersistence(
|
|
548
|
+
db,
|
|
549
|
+
{
|
|
550
|
+
accountId: ids.accountId,
|
|
551
|
+
workspaceId: ids.workspaceId,
|
|
552
|
+
sessionId: ids.sessionId,
|
|
553
|
+
processId: process.id,
|
|
554
|
+
},
|
|
555
|
+
bus ? (events) => bus.publish(ids.workspaceId, ids.sessionId, events) : undefined,
|
|
556
|
+
),
|
|
557
|
+
captureProcessOutput: async ({ process, chunkId, chunk, stream, streamFidelity }) => {
|
|
558
|
+
const events = await appendSessionCommandOutput(db, {
|
|
559
|
+
accountId: ids.accountId,
|
|
560
|
+
workspaceId: ids.workspaceId,
|
|
561
|
+
sessionId: ids.sessionId,
|
|
562
|
+
commandId: process.id,
|
|
563
|
+
chunkId,
|
|
564
|
+
chunk,
|
|
565
|
+
stream,
|
|
566
|
+
streamFidelity,
|
|
567
|
+
});
|
|
568
|
+
if (events.length && bus)
|
|
569
|
+
await bus.publish(ids.workspaceId, ids.sessionId, events).catch(() => undefined);
|
|
570
|
+
},
|
|
571
|
+
bindActiveRouteOnFirstResolve: true,
|
|
447
572
|
defaultResolved: {
|
|
448
573
|
session: established.session as RoutableBackendSession,
|
|
449
574
|
sandboxId: null,
|
|
@@ -464,6 +589,7 @@ export function wrapChannelABoxWithRouting(
|
|
|
464
589
|
},
|
|
465
590
|
resolveActiveBackend: resolver,
|
|
466
591
|
...(services.onSandboxOperation ? { onOperation: services.onSandboxOperation } : {}),
|
|
592
|
+
...(services.onSandboxCaptureWait ? { onCaptureWait: services.onSandboxCaptureWait } : {}),
|
|
467
593
|
...(beforeMutation ? { beforeMutation } : {}),
|
|
468
594
|
...(afterMutation ? { afterMutation } : {}),
|
|
469
595
|
...(beforeProcessMutation ? { beforeProcessMutation } : {}),
|
|
@@ -479,6 +605,7 @@ export function wrapChannelABoxWithRouting(
|
|
|
479
605
|
sandboxGroupId: homeLease.sandboxGroupId,
|
|
480
606
|
expectedEpoch: homeLease.leaseEpoch,
|
|
481
607
|
expectedInstanceId: homeLease.instanceId,
|
|
608
|
+
expectedBackend: homeLease.backend,
|
|
482
609
|
diagnostic: "provider_not_found_during_routed_operation",
|
|
483
610
|
});
|
|
484
611
|
if (marked.status === "marked" && bus) {
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { sandboxImageAllowlist, type Settings } from "@opengeni/config";
|
|
2
|
+
import {
|
|
3
|
+
resolveWorkspaceDefaultSandboxImage,
|
|
4
|
+
type RigVersion,
|
|
5
|
+
type Session,
|
|
6
|
+
type SandboxBackend,
|
|
7
|
+
type SandboxOs,
|
|
8
|
+
} from "@opengeni/contracts";
|
|
9
|
+
import { getRigVersion, getWorkspace, type Database } from "@opengeni/db";
|
|
10
|
+
import { resolveModalCheckpointProviderBinding } from "@opengeni/runtime/sandbox";
|
|
11
|
+
import {
|
|
12
|
+
rigProviderImageContentHash,
|
|
13
|
+
rigProviderImageMatchesDefinition,
|
|
14
|
+
rigProviderImageProviderBindingKeyHash,
|
|
15
|
+
} from "../rigs/provider-images";
|
|
16
|
+
|
|
17
|
+
export type ManagedSessionGroupBackend = Exclude<SandboxBackend, "none" | "selfhosted">;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the provider backend behind a session's synthetic managed group.
|
|
21
|
+
*
|
|
22
|
+
* A machine-home session keeps `selfhosted` as its durable policy while a
|
|
23
|
+
* non-null active pointer selects the enrolled machine. Clearing that pointer
|
|
24
|
+
* is an explicit switch to the deployment's managed group, so API readiness,
|
|
25
|
+
* viewer attachment, Channel-A, and worker turns must all select the same
|
|
26
|
+
* provider. Deployments configured with `none` or `selfhosted` have no managed
|
|
27
|
+
* fallback to expose.
|
|
28
|
+
*/
|
|
29
|
+
export function managedSessionGroupBackend(
|
|
30
|
+
deploymentBackend: SandboxBackend,
|
|
31
|
+
sessionBackend: SandboxBackend,
|
|
32
|
+
): ManagedSessionGroupBackend | null {
|
|
33
|
+
if (sessionBackend === "none") return null;
|
|
34
|
+
const backend = sessionBackend === "selfhosted" ? deploymentBackend : sessionBackend;
|
|
35
|
+
return backend === "none" || backend === "selfhosted" ? null : backend;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SESSION_GROUP_MACHINE_NAMES: Record<ManagedSessionGroupBackend, string> = {
|
|
39
|
+
local: "this computer",
|
|
40
|
+
docker: "Docker",
|
|
41
|
+
modal: "Modal",
|
|
42
|
+
daytona: "Daytona",
|
|
43
|
+
runloop: "Runloop",
|
|
44
|
+
e2b: "E2B",
|
|
45
|
+
blaxel: "Blaxel",
|
|
46
|
+
cloudflare: "Cloudflare",
|
|
47
|
+
vercel: "Vercel",
|
|
48
|
+
opensandbox: "OpenSandbox",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Display name and kind for the session's own managed box. The kind is the
|
|
52
|
+
* real provider. Callers must not collapse local, Docker, or other providers
|
|
53
|
+
* into Modal. */
|
|
54
|
+
export function sessionGroupMachinePresentation(backend: ManagedSessionGroupBackend): {
|
|
55
|
+
name: string;
|
|
56
|
+
kind: ManagedSessionGroupBackend;
|
|
57
|
+
} {
|
|
58
|
+
return { name: SESSION_GROUP_MACHINE_NAMES[backend], kind: backend };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A machine-home row carries the machine's host OS; its managed group uses the
|
|
62
|
+
* platform's canonical managed-sandbox OS instead. Ordinary sessions preserve
|
|
63
|
+
* their explicitly selected OS. */
|
|
64
|
+
export function managedSessionGroupOs(
|
|
65
|
+
sessionBackend: SandboxBackend,
|
|
66
|
+
sessionOs: SandboxOs,
|
|
67
|
+
): SandboxOs {
|
|
68
|
+
return sessionBackend === "selfhosted" ? "linux" : sessionOs;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Apply a workspace's selected sandbox image. The deployment keeps authority:
|
|
73
|
+
* only an image that is on its allowlist right now is used, so removing an
|
|
74
|
+
* entry returns every workspace that chose it to the deployment image. The
|
|
75
|
+
* lease treats the image as shared state, so a change rotates a live box
|
|
76
|
+
* through the normal capture-and-restore path instead of reusing it.
|
|
77
|
+
*/
|
|
78
|
+
export function settingsWithWorkspaceSandboxImage(
|
|
79
|
+
settings: Settings,
|
|
80
|
+
workspaceSettings: unknown,
|
|
81
|
+
backend: SandboxBackend,
|
|
82
|
+
): Settings {
|
|
83
|
+
const image = resolveWorkspaceDefaultSandboxImage(workspaceSettings);
|
|
84
|
+
if (!image || !sandboxImageAllowlist(settings).includes(image)) return settings;
|
|
85
|
+
if (backend === "docker") return { ...settings, dockerImage: image };
|
|
86
|
+
if (backend === "modal") {
|
|
87
|
+
return { ...settings, modalImageRef: image, modalImageId: undefined };
|
|
88
|
+
}
|
|
89
|
+
return settings;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function settingsWithRigImage(settings: Settings, rigImage: string | null): Settings {
|
|
93
|
+
// Explicit Rig image overrides are intentionally inert. Preserve this helper
|
|
94
|
+
// as a compatibility seam for callers and historical rows while ensuring the
|
|
95
|
+
// deployment-owned platform image remains authoritative.
|
|
96
|
+
void rigImage;
|
|
97
|
+
return settings;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function rigProviderImageSourceImage(
|
|
101
|
+
settings: Settings,
|
|
102
|
+
backend: SandboxBackend,
|
|
103
|
+
): string | null {
|
|
104
|
+
if (backend === "modal") return settings.modalImageId ?? settings.modalImageRef ?? null;
|
|
105
|
+
if (backend === "docker") return settings.dockerImage ?? null;
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type RigProviderImageSelectionReason =
|
|
110
|
+
| "selected"
|
|
111
|
+
| "missing"
|
|
112
|
+
| "provider_unsupported"
|
|
113
|
+
| "not_ready"
|
|
114
|
+
| "not_cold_boot_validated"
|
|
115
|
+
| "content_mismatch"
|
|
116
|
+
| "provider_binding_unavailable"
|
|
117
|
+
| "provider_binding_mismatch";
|
|
118
|
+
|
|
119
|
+
export function resolveRigProviderImageSelection(
|
|
120
|
+
settings: Settings,
|
|
121
|
+
version: RigVersion | null,
|
|
122
|
+
backend: SandboxBackend,
|
|
123
|
+
currentProviderBindingKeyHash: string | null,
|
|
124
|
+
): {
|
|
125
|
+
settings: Settings;
|
|
126
|
+
reason: RigProviderImageSelectionReason;
|
|
127
|
+
contentHash: string | null;
|
|
128
|
+
imageId: string | null;
|
|
129
|
+
} {
|
|
130
|
+
if (!version) return { settings, reason: "missing", contentHash: null, imageId: null };
|
|
131
|
+
if (backend !== "modal") {
|
|
132
|
+
return { settings, reason: "provider_unsupported", contentHash: null, imageId: null };
|
|
133
|
+
}
|
|
134
|
+
const image = version.providerImages[backend];
|
|
135
|
+
if (!image) return { settings, reason: "missing", contentHash: null, imageId: null };
|
|
136
|
+
if (image.status !== "ready" || !image.imageId) {
|
|
137
|
+
return {
|
|
138
|
+
settings,
|
|
139
|
+
reason: "not_ready",
|
|
140
|
+
contentHash: image.contentHash,
|
|
141
|
+
imageId: image.imageId,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (image.coldBootValidation?.version !== 1) {
|
|
145
|
+
return {
|
|
146
|
+
settings,
|
|
147
|
+
reason: "not_cold_boot_validated",
|
|
148
|
+
contentHash: image.contentHash,
|
|
149
|
+
imageId: null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const sourceImage = rigProviderImageSourceImage(settings, backend);
|
|
153
|
+
const expectedContentHash = rigProviderImageContentHash({
|
|
154
|
+
backend,
|
|
155
|
+
sourceImage,
|
|
156
|
+
definition: version,
|
|
157
|
+
});
|
|
158
|
+
if (
|
|
159
|
+
image.sourceImage !== sourceImage ||
|
|
160
|
+
image.contentHash !== expectedContentHash ||
|
|
161
|
+
!rigProviderImageMatchesDefinition(image, version)
|
|
162
|
+
) {
|
|
163
|
+
return {
|
|
164
|
+
settings,
|
|
165
|
+
reason: "content_mismatch",
|
|
166
|
+
contentHash: expectedContentHash,
|
|
167
|
+
imageId: null,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (!image.providerBindingKeyHash || !currentProviderBindingKeyHash) {
|
|
171
|
+
return {
|
|
172
|
+
settings,
|
|
173
|
+
reason: "provider_binding_unavailable",
|
|
174
|
+
contentHash: expectedContentHash,
|
|
175
|
+
imageId: null,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (image.providerBindingKeyHash !== currentProviderBindingKeyHash) {
|
|
179
|
+
return {
|
|
180
|
+
settings,
|
|
181
|
+
reason: "provider_binding_mismatch",
|
|
182
|
+
contentHash: expectedContentHash,
|
|
183
|
+
imageId: null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
settings: { ...settings, modalImageId: image.imageId },
|
|
188
|
+
reason: "selected",
|
|
189
|
+
contentHash: expectedContentHash,
|
|
190
|
+
imageId: image.imageId,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function resolveRigProviderImageForRun(
|
|
195
|
+
settings: Settings,
|
|
196
|
+
version: RigVersion | null,
|
|
197
|
+
backend: SandboxBackend,
|
|
198
|
+
resolveBinding: typeof resolveModalCheckpointProviderBinding = resolveModalCheckpointProviderBinding,
|
|
199
|
+
): Promise<ReturnType<typeof resolveRigProviderImageSelection>> {
|
|
200
|
+
const image = backend === "modal" ? version?.providerImages.modal : null;
|
|
201
|
+
if (image?.status !== "ready" || !image.providerBindingKeyHash) {
|
|
202
|
+
return resolveRigProviderImageSelection(settings, version, backend, null);
|
|
203
|
+
}
|
|
204
|
+
const structural = resolveRigProviderImageSelection(
|
|
205
|
+
settings,
|
|
206
|
+
version,
|
|
207
|
+
backend,
|
|
208
|
+
image.providerBindingKeyHash,
|
|
209
|
+
);
|
|
210
|
+
if (structural.reason !== "selected") return structural;
|
|
211
|
+
let currentProviderBindingKeyHash: string | null = null;
|
|
212
|
+
try {
|
|
213
|
+
const binding = await resolveBinding(settings);
|
|
214
|
+
currentProviderBindingKeyHash = rigProviderImageProviderBindingKeyHash(binding.key);
|
|
215
|
+
} catch {
|
|
216
|
+
// A provider-native image is an optimization. The exact logical image is
|
|
217
|
+
// still the correct cold-create fallback when provider identity is absent.
|
|
218
|
+
}
|
|
219
|
+
return resolveRigProviderImageSelection(
|
|
220
|
+
settings,
|
|
221
|
+
version,
|
|
222
|
+
backend,
|
|
223
|
+
currentProviderBindingKeyHash,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export async function settingsWithRigProviderImage(
|
|
228
|
+
settings: Settings,
|
|
229
|
+
version: RigVersion | null,
|
|
230
|
+
backend: SandboxBackend,
|
|
231
|
+
resolveBinding: typeof resolveModalCheckpointProviderBinding = resolveModalCheckpointProviderBinding,
|
|
232
|
+
): Promise<Settings> {
|
|
233
|
+
return (await resolveRigProviderImageForRun(settings, version, backend, resolveBinding)).settings;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export type SessionSandboxRuntime = {
|
|
237
|
+
/** Exact logical image/rig settings used to fence the durable group lease. */
|
|
238
|
+
settings: Settings;
|
|
239
|
+
image: string | null;
|
|
240
|
+
rigVersion: RigVersion | null;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export async function resolveSessionSandboxRuntime(
|
|
244
|
+
db: Database,
|
|
245
|
+
settings: Settings,
|
|
246
|
+
session: Pick<Session, "workspaceId" | "sandboxBackend" | "rigId" | "rigVersionId">,
|
|
247
|
+
): Promise<SessionSandboxRuntime> {
|
|
248
|
+
const rigVersion =
|
|
249
|
+
session.rigId && session.rigVersionId
|
|
250
|
+
? await getRigVersion(db, session.workspaceId, session.rigId, session.rigVersionId)
|
|
251
|
+
: null;
|
|
252
|
+
if (session.rigVersionId && !rigVersion) {
|
|
253
|
+
throw new Error(`Frozen sandbox environment version ${session.rigVersionId} is unavailable`);
|
|
254
|
+
}
|
|
255
|
+
// Setup and checks layer on the deployment image, or on the workspace's
|
|
256
|
+
// allowlisted selection of one.
|
|
257
|
+
const logicalSettings = sandboxImageAllowlist(settings).length
|
|
258
|
+
? settingsWithWorkspaceSandboxImage(
|
|
259
|
+
settings,
|
|
260
|
+
(await getWorkspace(db, session.workspaceId))?.settings,
|
|
261
|
+
session.sandboxBackend,
|
|
262
|
+
)
|
|
263
|
+
: settings;
|
|
264
|
+
return {
|
|
265
|
+
settings: {
|
|
266
|
+
...logicalSettings,
|
|
267
|
+
sandboxBackend: session.sandboxBackend,
|
|
268
|
+
},
|
|
269
|
+
image: rigProviderImageSourceImage(logicalSettings, session.sandboxBackend),
|
|
270
|
+
rigVersion,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Resolve provider-native cold-create optimization only for the single CAS
|
|
275
|
+
* winner. Ordinary API calls never perform provider identity I/O. */
|
|
276
|
+
export async function providerSettingsForSessionSandboxRuntime(
|
|
277
|
+
runtime: SessionSandboxRuntime,
|
|
278
|
+
backend: SandboxBackend,
|
|
279
|
+
): Promise<Settings> {
|
|
280
|
+
return await settingsWithRigProviderImage(runtime.settings, runtime.rigVersion, backend);
|
|
281
|
+
}
|