@opengeni/core 4.0.0 → 4.0.1
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/application/new-session-drafts.d.ts +96 -0
- package/dist/domain/knowledge-messages.d.ts +2 -2
- package/dist/domain/pr-review.d.ts +2 -2
- package/dist/domain/session-tool-policy.d.ts +9 -0
- package/dist/domain/sessions.d.ts +22 -0
- package/dist/index.js +257 -179
- package/dist/index.js.map +1 -1
- package/dist/sandbox/fleet.d.ts +1 -1
- package/dist/sandbox/runtime-settings.d.ts +7 -0
- package/package.json +10 -10
- package/src/application/new-session-drafts.ts +42 -0
- package/src/domain/github-skill-source.ts +3 -1
- package/src/domain/session-tool-policy.ts +18 -2
- package/src/domain/sessions.ts +70 -11
- package/src/model-catalog.ts +35 -31
- package/src/sandbox/fleet.ts +5 -4
- package/src/sandbox/runtime-settings.ts +23 -0
package/dist/sandbox/fleet.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export type FleetSandboxEntry = {
|
|
|
68
68
|
/** The sandbox id used as the attach/swap/run_on `target`. For the session's
|
|
69
69
|
* own group box this is the group id (a null active pointer == this box). */
|
|
70
70
|
id: string;
|
|
71
|
-
kind:
|
|
71
|
+
kind: Exclude<SandboxBackend, "none">;
|
|
72
72
|
name: string;
|
|
73
73
|
liveness: FleetLiveness;
|
|
74
74
|
/** True for the session's currently-active sandbox (the routing target). */
|
|
@@ -14,6 +14,13 @@ export type ManagedSessionGroupBackend = Exclude<SandboxBackend, "none" | "selfh
|
|
|
14
14
|
* fallback to expose.
|
|
15
15
|
*/
|
|
16
16
|
export declare function managedSessionGroupBackend(deploymentBackend: SandboxBackend, sessionBackend: SandboxBackend): ManagedSessionGroupBackend | null;
|
|
17
|
+
/** Display name and kind for the session's own managed box. The kind is the
|
|
18
|
+
* real provider. Callers must not collapse local, Docker, or other providers
|
|
19
|
+
* into Modal. */
|
|
20
|
+
export declare function sessionGroupMachinePresentation(backend: ManagedSessionGroupBackend): {
|
|
21
|
+
name: string;
|
|
22
|
+
kind: ManagedSessionGroupBackend;
|
|
23
|
+
};
|
|
17
24
|
/** A machine-home row carries the machine's host OS; its managed group uses the
|
|
18
25
|
* platform's canonical managed-sandbox OS instead. Ordinary sessions preserve
|
|
19
26
|
* their explicitly selected OS. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/core",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "OpenGeni framework-agnostic core: the domain, access, and billing layers (neutral access, off-HTTP V2 surface). Behavior-preserving extraction from apps/api — keeps Hono's HTTPException for error throwing (typed-errors cleanup deferred).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -51,16 +51,16 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
53
53
|
"@opengeni/capabilities": "^0.3.4",
|
|
54
|
-
"@opengeni/codex": "^0.2.
|
|
55
|
-
"@opengeni/config": "^2.
|
|
56
|
-
"@opengeni/contracts": "^5.
|
|
57
|
-
"@opengeni/db": "^6.0.
|
|
58
|
-
"@opengeni/documents": "^0.8.
|
|
59
|
-
"@opengeni/events": "^0.4.
|
|
54
|
+
"@opengeni/codex": "^0.2.25",
|
|
55
|
+
"@opengeni/config": "^2.1.0",
|
|
56
|
+
"@opengeni/contracts": "^5.1.0",
|
|
57
|
+
"@opengeni/db": "^6.0.1",
|
|
58
|
+
"@opengeni/documents": "^0.8.32",
|
|
59
|
+
"@opengeni/events": "^0.4.30",
|
|
60
60
|
"@opengeni/network": "^0.3.1",
|
|
61
|
-
"@opengeni/observability": "^0.8.
|
|
62
|
-
"@opengeni/runtime": "^4.0.
|
|
63
|
-
"@opengeni/storage": "^0.2.
|
|
61
|
+
"@opengeni/observability": "^0.8.31",
|
|
62
|
+
"@opengeni/runtime": "^4.0.1",
|
|
63
|
+
"@opengeni/storage": "^0.2.132",
|
|
64
64
|
"hono": "^4.12.18",
|
|
65
65
|
"zod": "^4.2.1"
|
|
66
66
|
},
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
publicNewSessionDraftOptions,
|
|
20
20
|
requireFileForSubject,
|
|
21
21
|
saveNewSessionDraftInTransaction,
|
|
22
|
+
reusableNewSessionRepositoryResource,
|
|
22
23
|
withWorkspaceSubjectRls,
|
|
23
24
|
} from "@opengeni/db";
|
|
24
25
|
import { HTTPException } from "hono/http-exception";
|
|
@@ -333,3 +334,44 @@ export async function saveActorNewSessionDraft(
|
|
|
333
334
|
: { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
|
|
334
335
|
return withSessionRlsActorContext(actor, () => saveActorNewSessionDraftInFileScope(...args));
|
|
335
336
|
}
|
|
337
|
+
|
|
338
|
+
/** Reuse the website's actor/workspace selections without consuming its draft. */
|
|
339
|
+
export async function getActorNewSessionDefaults(
|
|
340
|
+
deps: Parameters<typeof getActorNewSessionDraft>[0],
|
|
341
|
+
grant: AccessGrant,
|
|
342
|
+
workspaceId: string,
|
|
343
|
+
) {
|
|
344
|
+
const draft = await getActorNewSessionDraft(deps, grant, workspaceId);
|
|
345
|
+
const options = draft.options;
|
|
346
|
+
return {
|
|
347
|
+
// Revision zero is a synthetic empty form, not a user's model preference.
|
|
348
|
+
...(draft.revision > 0
|
|
349
|
+
? {
|
|
350
|
+
model: draft.model,
|
|
351
|
+
reasoningEffort: draft.reasoningEffort,
|
|
352
|
+
latencyMode: draft.latencyMode,
|
|
353
|
+
}
|
|
354
|
+
: {}),
|
|
355
|
+
resources: draft.resources.flatMap((resource) =>
|
|
356
|
+
resource.kind === "repository" ? [reusableNewSessionRepositoryResource(resource)] : [],
|
|
357
|
+
),
|
|
358
|
+
...(draft.toolsProvided
|
|
359
|
+
? { tools: draft.tools }
|
|
360
|
+
: options.excludedMcpServerIds
|
|
361
|
+
? { excludedMcpServerIds: options.excludedMcpServerIds }
|
|
362
|
+
: {}),
|
|
363
|
+
...(options.firstPartyMcpTools ? { firstPartyMcpTools: options.firstPartyMcpTools } : {}),
|
|
364
|
+
...(options.firstPartyMcpPermissions
|
|
365
|
+
? { firstPartyMcpPermissions: options.firstPartyMcpPermissions }
|
|
366
|
+
: {}),
|
|
367
|
+
...(options.variableSetIds ? { variableSetIds: options.variableSetIds } : {}),
|
|
368
|
+
...(options.rigId ? { rigId: options.rigId } : {}),
|
|
369
|
+
...(options.sandboxBackend ? { sandboxBackend: options.sandboxBackend } : {}),
|
|
370
|
+
...(options.targetSandboxId
|
|
371
|
+
? {
|
|
372
|
+
targetSandboxId: options.targetSandboxId,
|
|
373
|
+
...(options.workingDir ? { workingDir: options.workingDir } : {}),
|
|
374
|
+
}
|
|
375
|
+
: {}),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { GitHubSkillSourceClient, GitHubSkillTreeEntry } from "./skill-impo
|
|
|
4
4
|
import { pinnedFetch, readResponseJsonBounded, readResponseTextBounded } from "@opengeni/network";
|
|
5
5
|
import {
|
|
6
6
|
PORTABLE_SKILL_MAX_FILES,
|
|
7
|
+
PORTABLE_SKILL_MAX_FILE_BYTES,
|
|
7
8
|
PORTABLE_SKILL_MAX_TOTAL_BYTES,
|
|
8
9
|
type SkillLibraryFile,
|
|
9
10
|
} from "@opengeni/runtime/skill-library";
|
|
@@ -11,7 +12,8 @@ import {
|
|
|
11
12
|
const githubApiBase = "https://api.github.com";
|
|
12
13
|
const githubRequestTimeoutMs = 15_000;
|
|
13
14
|
const githubMetadataMaxBytes = 4 * 1024 * 1024;
|
|
14
|
-
|
|
15
|
+
// GitHub blobs are base64-encoded JSON; allow framing and line wrapping.
|
|
16
|
+
const githubBlobResponseMaxBytes = PORTABLE_SKILL_MAX_FILE_BYTES * 2;
|
|
15
17
|
|
|
16
18
|
type GitHubJsonRequest = (path: string, maxBytes: number, label: string) => Promise<unknown>;
|
|
17
19
|
|
|
@@ -188,9 +188,25 @@ export async function workspaceSessionToolPolicyDefaultServerIds(
|
|
|
188
188
|
const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
|
|
189
189
|
...(subjectId ? { subjectId } : {}),
|
|
190
190
|
});
|
|
191
|
-
const availableDefaults = defaultSessionMcpServerIds(runtimeSettings.mcpServers);
|
|
192
191
|
const workspace = await requireWorkspace(db, workspaceId);
|
|
193
|
-
|
|
192
|
+
return workspaceSessionToolPolicyDefaultServerIdsFor(
|
|
193
|
+
runtimeSettings.mcpServers,
|
|
194
|
+
workspace.settings,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The omitted-tools default for one resolved runtime registry and one
|
|
200
|
+
* workspace settings bag. Pure so a caller that already holds both never has
|
|
201
|
+
* to re-query the capability registry to agree with the worker and the
|
|
202
|
+
* composer on which connectors a `workspace_default` session executes with.
|
|
203
|
+
*/
|
|
204
|
+
export function workspaceSessionToolPolicyDefaultServerIdsFor(
|
|
205
|
+
runtimeMcpServers: Iterable<{ id: string }>,
|
|
206
|
+
workspaceSettings: unknown,
|
|
207
|
+
): string[] {
|
|
208
|
+
const availableDefaults = defaultSessionMcpServerIds(runtimeMcpServers);
|
|
209
|
+
const configured = resolveWorkspaceSessionToolDefaults(workspaceSettings);
|
|
194
210
|
if (!configured?.mcpServerIds) return availableDefaults;
|
|
195
211
|
const available = new Set(availableDefaults);
|
|
196
212
|
return sortedIds([
|
package/src/domain/sessions.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { sessionCreationMetadata } from "../site-session-origin";
|
|
|
16
16
|
import {
|
|
17
17
|
canonicalizeConfiguredModelId,
|
|
18
18
|
configuredAllowedModels,
|
|
19
|
+
withCodexCatalogProvider,
|
|
19
20
|
ORGANIZATION_GATEWAY_MODEL_ID_PREFIX,
|
|
20
21
|
ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX,
|
|
21
22
|
resolveFirstPartyMcpToolPolicy,
|
|
@@ -181,6 +182,10 @@ import {
|
|
|
181
182
|
workspaceCustomModelReference,
|
|
182
183
|
} from "../model-catalog";
|
|
183
184
|
import { settingsWithEnabledCapabilityMcpServers } from "./capabilities";
|
|
185
|
+
import {
|
|
186
|
+
resolveSessionToolPolicy,
|
|
187
|
+
workspaceSessionToolPolicyDefaultServerIdsFor,
|
|
188
|
+
} from "./session-tool-policy";
|
|
184
189
|
import { validateSubmittedTimelineAnnotations } from "./timeline-annotations";
|
|
185
190
|
import { requireVariableSetEncryption, validateVariableSetAttachment } from "./environments";
|
|
186
191
|
import {
|
|
@@ -1420,6 +1425,18 @@ export function canonicalConfiguredModel(
|
|
|
1420
1425
|
return model;
|
|
1421
1426
|
}
|
|
1422
1427
|
const canonicalModel = canonicalizeConfiguredModelId(settings, model);
|
|
1428
|
+
if (
|
|
1429
|
+
canonicalModel.startsWith(CODEX_MODEL_ID_PREFIX) &&
|
|
1430
|
+
settings.resolvedCodexModelsJson !== undefined
|
|
1431
|
+
) {
|
|
1432
|
+
if (
|
|
1433
|
+
settings.codexSubscriptionEnabled &&
|
|
1434
|
+
configuredAllowedModels(withCodexCatalogProvider(settings)).includes(canonicalModel)
|
|
1435
|
+
) {
|
|
1436
|
+
return canonicalModel;
|
|
1437
|
+
}
|
|
1438
|
+
throw new HTTPException(422, { message: `model is not available: ${model}` });
|
|
1439
|
+
}
|
|
1423
1440
|
if (configuredAllowedModels(settings).includes(canonicalModel)) {
|
|
1424
1441
|
return canonicalModel;
|
|
1425
1442
|
}
|
|
@@ -3724,25 +3741,39 @@ async function acceptSessionUserMessageInFileScope(
|
|
|
3724
3741
|
connectionDelegationSource.turnId,
|
|
3725
3742
|
)
|
|
3726
3743
|
: null;
|
|
3744
|
+
const capabilityRuntimeSettings = await settingsWithEnabledCapabilityMcpServers(
|
|
3745
|
+
db,
|
|
3746
|
+
workspaceId,
|
|
3747
|
+
settings,
|
|
3748
|
+
inheritedPersonalConnectionDelegations
|
|
3749
|
+
? {
|
|
3750
|
+
personalConnectionDelegations: inheritedPersonalConnectionDelegations,
|
|
3751
|
+
}
|
|
3752
|
+
: { subjectId: grant.subjectId },
|
|
3753
|
+
);
|
|
3727
3754
|
const runtimeSettings = settingsWithSessionMcpServerMetadata(
|
|
3728
|
-
|
|
3729
|
-
db,
|
|
3730
|
-
workspaceId,
|
|
3731
|
-
settings,
|
|
3732
|
-
inheritedPersonalConnectionDelegations
|
|
3733
|
-
? {
|
|
3734
|
-
personalConnectionDelegations: inheritedPersonalConnectionDelegations,
|
|
3735
|
-
}
|
|
3736
|
-
: { subjectId: grant.subjectId },
|
|
3737
|
-
),
|
|
3755
|
+
capabilityRuntimeSettings,
|
|
3738
3756
|
existingSession.mcpServers,
|
|
3739
3757
|
);
|
|
3758
|
+
// A `workspace_default` session stores only its creation-time tool
|
|
3759
|
+
// snapshot and resolves the current workspace defaults at run time, so the
|
|
3760
|
+
// raw column is not the connector allow-list this follow-up executes with.
|
|
3761
|
+
// Freeze accounts against the same resolved list the composer and the
|
|
3762
|
+
// worker see.
|
|
3763
|
+
const connectionAccountTools = sessionToolsForConnectionAccounts({
|
|
3764
|
+
session: existingSession,
|
|
3765
|
+
runtimeMcpServers: runtimeSettings.mcpServers,
|
|
3766
|
+
defaultMcpServerIds: workspaceSessionToolPolicyDefaultServerIdsFor(
|
|
3767
|
+
capabilityRuntimeSettings.mcpServers,
|
|
3768
|
+
(await requireWorkspace(db, workspaceId)).settings,
|
|
3769
|
+
),
|
|
3770
|
+
});
|
|
3740
3771
|
const { personalConnectionDelegations, mcpAccountBindings } = await freezeConnectionAccounts({
|
|
3741
3772
|
db,
|
|
3742
3773
|
accountId: grant.accountId,
|
|
3743
3774
|
workspaceId,
|
|
3744
3775
|
settings: runtimeSettings,
|
|
3745
|
-
tools:
|
|
3776
|
+
tools: connectionAccountTools,
|
|
3746
3777
|
resources: [...existingSession.resources, ...requestedResources],
|
|
3747
3778
|
source: connectionDelegationSource,
|
|
3748
3779
|
targetSessionId: sessionId,
|
|
@@ -4411,6 +4442,34 @@ function withFirstPartyTools(
|
|
|
4411
4442
|
return mergeToolRefs(tools, [{ kind: "mcp", id: "opengeni" }]);
|
|
4412
4443
|
}
|
|
4413
4444
|
|
|
4445
|
+
/**
|
|
4446
|
+
* The executable MCP tool list a follow-up on an existing session freezes
|
|
4447
|
+
* connector accounts against.
|
|
4448
|
+
*
|
|
4449
|
+
* A `workspace_default` session stores only the tool snapshot taken when it
|
|
4450
|
+
* was created and expands the current workspace defaults at run time, so a
|
|
4451
|
+
* connector enabled after creation never enters the stored column. The
|
|
4452
|
+
* composer projects that same expansion and submits account selections for
|
|
4453
|
+
* it, so freezing against the stored column rejected every such selection as
|
|
4454
|
+
* unmatched and never froze its personal delegation. Resolve through the one
|
|
4455
|
+
* ID-only policy resolver the API projection and the worker use, with the
|
|
4456
|
+
* same omitted-tools default, so all three agree on the executable set.
|
|
4457
|
+
*/
|
|
4458
|
+
export function sessionToolsForConnectionAccounts(input: {
|
|
4459
|
+
session: Pick<Session, "tools" | "toolPolicy">;
|
|
4460
|
+
/** The resolved runtime registry, including session-local servers. */
|
|
4461
|
+
runtimeMcpServers: Iterable<{ id: string }>;
|
|
4462
|
+
/** The current omitted-tools default for this workspace. */
|
|
4463
|
+
defaultMcpServerIds: Iterable<string>;
|
|
4464
|
+
}): ToolRef[] {
|
|
4465
|
+
return resolveSessionToolPolicy({
|
|
4466
|
+
toolPolicy: input.session.toolPolicy,
|
|
4467
|
+
sessionTools: input.session.tools,
|
|
4468
|
+
availableMcpServerIds: [...input.runtimeMcpServers].map((server) => server.id),
|
|
4469
|
+
defaultMcpServerIds: input.defaultMcpServerIds,
|
|
4470
|
+
}).toolRefs;
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4414
4473
|
function hasOwnProperty(value: unknown, key: string): boolean {
|
|
4415
4474
|
return Boolean(
|
|
4416
4475
|
value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, key),
|
package/src/model-catalog.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
configuredGatewayWorkspaceProductModelIds,
|
|
5
5
|
configuredGatewayOrganizationProductModelIds,
|
|
6
6
|
configuredModels,
|
|
7
|
+
isModelAvailableForNewSelection,
|
|
7
8
|
configuredModelNotes,
|
|
8
9
|
configuredOpenRouterWorkspaceProductModelIds,
|
|
9
10
|
configuredOpenRouterOrganizationProductModelIds,
|
|
@@ -708,38 +709,41 @@ export function resolveWorkspaceModelSelection(
|
|
|
708
709
|
? input.credentialReadinessMaxAgeMs
|
|
709
710
|
: MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS;
|
|
710
711
|
|
|
711
|
-
return configuredModels(catalogSettings)
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
codexSubscriptionActive: input.codexSubscriptionActive,
|
|
722
|
-
xaiSubscriptionActive: input.xaiSubscriptionActive === true,
|
|
723
|
-
workspaceGatewayConnectionActive: input.workspaceGatewayConnectionActive === true,
|
|
724
|
-
workspaceOpenRouterConnectionActive: input.workspaceOpenRouterConnectionActive === true,
|
|
725
|
-
organizationGatewayConnectionActive: input.organizationGatewayConnectionActive === true,
|
|
726
|
-
organizationOpenRouterConnectionActive: input.organizationOpenRouterConnectionActive === true,
|
|
727
|
-
observation: input.credentialReadinessObservations?.[model.definitionVersion],
|
|
728
|
-
nowMs,
|
|
729
|
-
maxAgeMs,
|
|
730
|
-
});
|
|
731
|
-
return {
|
|
732
|
-
model,
|
|
733
|
-
credentialReadiness,
|
|
734
|
-
policyAllowed,
|
|
735
|
-
availability: availabilityFor({
|
|
712
|
+
return configuredModels(catalogSettings)
|
|
713
|
+
.filter((model) => isModelAvailableForNewSelection(catalogSettings, model.id))
|
|
714
|
+
.map((model) => {
|
|
715
|
+
const provider = providers.get(model.providerId);
|
|
716
|
+
const policyAllowed =
|
|
717
|
+
evaluateWorkspaceModelPolicy(input.policy, {
|
|
718
|
+
providerId: model.providerId,
|
|
719
|
+
modelId: model.id,
|
|
720
|
+
}).allowed && modelAllowedByConnections(input.connectionModelRestrictions ?? {}, model.id);
|
|
721
|
+
const credentialReadiness = credentialReadinessFor({
|
|
736
722
|
model,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
723
|
+
provider,
|
|
724
|
+
codexSubscriptionActive: input.codexSubscriptionActive,
|
|
725
|
+
xaiSubscriptionActive: input.xaiSubscriptionActive === true,
|
|
726
|
+
workspaceGatewayConnectionActive: input.workspaceGatewayConnectionActive === true,
|
|
727
|
+
workspaceOpenRouterConnectionActive: input.workspaceOpenRouterConnectionActive === true,
|
|
728
|
+
organizationGatewayConnectionActive: input.organizationGatewayConnectionActive === true,
|
|
729
|
+
organizationOpenRouterConnectionActive:
|
|
730
|
+
input.organizationOpenRouterConnectionActive === true,
|
|
731
|
+
observation: input.credentialReadinessObservations?.[model.definitionVersion],
|
|
740
732
|
nowMs,
|
|
741
733
|
maxAgeMs,
|
|
742
|
-
})
|
|
743
|
-
|
|
744
|
-
|
|
734
|
+
});
|
|
735
|
+
return {
|
|
736
|
+
model,
|
|
737
|
+
credentialReadiness,
|
|
738
|
+
policyAllowed,
|
|
739
|
+
availability: availabilityFor({
|
|
740
|
+
model,
|
|
741
|
+
credentialReadiness,
|
|
742
|
+
policyAllowed,
|
|
743
|
+
observation: input.observations?.[model.definitionVersion],
|
|
744
|
+
nowMs,
|
|
745
|
+
maxAgeMs,
|
|
746
|
+
}),
|
|
747
|
+
};
|
|
748
|
+
});
|
|
745
749
|
}
|
package/src/sandbox/fleet.ts
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
type SelfhostedOperationResourcePolicy,
|
|
47
47
|
} from "@opengeni/runtime/sandbox";
|
|
48
48
|
import { relayConfigFromSettings } from "./routing";
|
|
49
|
-
import { managedSessionGroupBackend } from "./runtime-settings";
|
|
49
|
+
import { managedSessionGroupBackend, sessionGroupMachinePresentation } from "./runtime-settings";
|
|
50
50
|
|
|
51
51
|
export type FleetServices = {
|
|
52
52
|
db: Database;
|
|
@@ -130,7 +130,7 @@ export type FleetSandboxEntry = {
|
|
|
130
130
|
/** The sandbox id used as the attach/swap/run_on `target`. For the session's
|
|
131
131
|
* own group box this is the group id (a null active pointer == this box). */
|
|
132
132
|
id: string;
|
|
133
|
-
kind:
|
|
133
|
+
kind: Exclude<SandboxBackend, "none">;
|
|
134
134
|
name: string;
|
|
135
135
|
liveness: FleetLiveness;
|
|
136
136
|
/** True for the session's currently-active sandbox (the routing target). */
|
|
@@ -325,10 +325,11 @@ export async function listFleet(
|
|
|
325
325
|
: groupRecovering
|
|
326
326
|
? "recovering"
|
|
327
327
|
: "wakeable";
|
|
328
|
+
const presentation = sessionGroupMachinePresentation(groupBackend);
|
|
328
329
|
entries.push({
|
|
329
330
|
id: ctx.sessionGroupId,
|
|
330
|
-
kind:
|
|
331
|
-
name:
|
|
331
|
+
kind: presentation.kind,
|
|
332
|
+
name: presentation.name,
|
|
332
333
|
liveness: groupOnline ? "online" : groupRecovering ? "reconnecting" : "offline",
|
|
333
334
|
active: groupActive,
|
|
334
335
|
isSessionGroup: true,
|
|
@@ -34,6 +34,29 @@ export function managedSessionGroupBackend(
|
|
|
34
34
|
return backend === "none" || backend === "selfhosted" ? null : backend;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const SESSION_GROUP_MACHINE_NAMES: Record<ManagedSessionGroupBackend, string> = {
|
|
38
|
+
local: "this computer",
|
|
39
|
+
docker: "Docker",
|
|
40
|
+
modal: "Modal",
|
|
41
|
+
daytona: "Daytona",
|
|
42
|
+
runloop: "Runloop",
|
|
43
|
+
e2b: "E2B",
|
|
44
|
+
blaxel: "Blaxel",
|
|
45
|
+
cloudflare: "Cloudflare",
|
|
46
|
+
vercel: "Vercel",
|
|
47
|
+
opensandbox: "OpenSandbox",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Display name and kind for the session's own managed box. The kind is the
|
|
51
|
+
* real provider. Callers must not collapse local, Docker, or other providers
|
|
52
|
+
* into Modal. */
|
|
53
|
+
export function sessionGroupMachinePresentation(backend: ManagedSessionGroupBackend): {
|
|
54
|
+
name: string;
|
|
55
|
+
kind: ManagedSessionGroupBackend;
|
|
56
|
+
} {
|
|
57
|
+
return { name: SESSION_GROUP_MACHINE_NAMES[backend], kind: backend };
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
/** A machine-home row carries the machine's host OS; its managed group uses the
|
|
38
61
|
* platform's canonical managed-sandbox OS instead. Ordinary sessions preserve
|
|
39
62
|
* their explicitly selected OS. */
|