@opengeni/config 0.12.1 → 0.16.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/index.d.ts +362 -12
- package/dist/index.js +433 -63
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +616 -69
package/src/index.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BillingMode,
|
|
3
3
|
CAPABILITY_DESCRIPTORS,
|
|
4
|
+
DEFAULT_FIRST_PARTY_MCP_TOOLS,
|
|
4
5
|
Entitlements,
|
|
5
6
|
EntitlementsMode,
|
|
6
7
|
LatencyMode,
|
|
7
8
|
MAX_NESTED_AGENT_DEPTH,
|
|
8
9
|
ProductAccessMode,
|
|
9
10
|
ReasoningEffort,
|
|
11
|
+
FIRST_PARTY_MCP_TOOL_NAMES,
|
|
12
|
+
FirstPartyMcpToolName,
|
|
10
13
|
SandboxBackend,
|
|
11
14
|
SessionMcpApprovalPolicy,
|
|
15
|
+
SEEDANCE_2_5_MODEL_ID,
|
|
12
16
|
StaticUsageLimits,
|
|
13
17
|
TurnExecutionPolicyV1,
|
|
14
18
|
UsageLimitsMode,
|
|
15
19
|
type TurnExecutionLatencyModeSourceV1,
|
|
16
20
|
type TurnExecutionModelSourceV1,
|
|
17
21
|
type TurnExecutionReasoningSourceV1,
|
|
22
|
+
type VideoGenerationResolution,
|
|
23
|
+
type FirstPartyMcpToolName as FirstPartyMcpToolNameType,
|
|
18
24
|
} from "@opengeni/contracts";
|
|
19
25
|
import { CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS } from "@opengeni/codex";
|
|
20
26
|
import {
|
|
@@ -26,11 +32,39 @@ import {
|
|
|
26
32
|
CODEX_PROVIDER_BASE_URL,
|
|
27
33
|
CODEX_PROVIDER_ID,
|
|
28
34
|
} from "@opengeni/codex/constants";
|
|
35
|
+
import {
|
|
36
|
+
XAI_SUBSCRIPTION_MODEL_SLUGS,
|
|
37
|
+
XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
38
|
+
XAI_SUBSCRIPTION_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
39
|
+
XAI_SUBSCRIPTION_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
40
|
+
XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
|
|
41
|
+
XAI_SUBSCRIPTION_PROVIDER_ID,
|
|
42
|
+
XAI_SUBSCRIPTION_PROXY_BASE_URL,
|
|
43
|
+
} from "@opengeni/xai-subscription";
|
|
44
|
+
export { XAI_SUBSCRIPTION_MODEL_ID_PREFIX } from "@opengeni/xai-subscription";
|
|
29
45
|
import { createHash } from "node:crypto";
|
|
30
46
|
import { z } from "zod";
|
|
31
47
|
|
|
32
48
|
const envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
33
49
|
const registryId = /^[A-Za-z0-9_-]+$/;
|
|
50
|
+
|
|
51
|
+
// Archive capture claims are also the admission/teardown fence around a
|
|
52
|
+
// provider snapshot. Keep a real settlement window after the provider request;
|
|
53
|
+
// a configured request timeout may never consume the entire durable claim.
|
|
54
|
+
export const SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS = 60 * 60_000;
|
|
55
|
+
export const SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS = 10_000;
|
|
56
|
+
export const SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS =
|
|
57
|
+
SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS - SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS;
|
|
58
|
+
// Admission waits are observational: successful capture/teardown returns as
|
|
59
|
+
// soon as the DB fence clears. This ceiling only covers the unhealthy path. It
|
|
60
|
+
// allows one scheduled inventory and one complete successor claim without
|
|
61
|
+
// turning a recoverable lifecycle transition into a visible caller error. A
|
|
62
|
+
// dead holder's TTL is deliberately NOT included: admission cannot accelerate
|
|
63
|
+
// that proof, and the turn moves to durable recovery if holder quiescence takes
|
|
64
|
+
// longer than this observational wait. The outer cap remains an explicit
|
|
65
|
+
// request-resource boundary; successful transitions return immediately.
|
|
66
|
+
export const SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS = 60 * 60_000;
|
|
67
|
+
export const SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS = 10_000;
|
|
34
68
|
const EnvBoolean = z.preprocess((value) => {
|
|
35
69
|
if (typeof value !== "string") {
|
|
36
70
|
return value;
|
|
@@ -45,6 +79,38 @@ const EnvBoolean = z.preprocess((value) => {
|
|
|
45
79
|
return value;
|
|
46
80
|
}, z.boolean());
|
|
47
81
|
|
|
82
|
+
const EnvFirstPartyMcpTools = z.preprocess(
|
|
83
|
+
(value) => {
|
|
84
|
+
if (typeof value !== "string") return value;
|
|
85
|
+
const source = value.trim();
|
|
86
|
+
if (!source) return undefined;
|
|
87
|
+
if (source.startsWith("[")) {
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(source);
|
|
90
|
+
} catch {
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return source.split(",").map((entry) => entry.trim());
|
|
95
|
+
},
|
|
96
|
+
z
|
|
97
|
+
.array(FirstPartyMcpToolName)
|
|
98
|
+
.superRefine((tools, context) => {
|
|
99
|
+
const seen = new Set<FirstPartyMcpToolNameType>();
|
|
100
|
+
for (const [index, tool] of tools.entries()) {
|
|
101
|
+
if (seen.has(tool)) {
|
|
102
|
+
context.addIssue({
|
|
103
|
+
code: "custom",
|
|
104
|
+
message: "first-party MCP tool lists must not contain duplicates",
|
|
105
|
+
path: [index],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
seen.add(tool);
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.optional(),
|
|
112
|
+
);
|
|
113
|
+
|
|
48
114
|
export const sandboxPreparationProfiles: Record<string, { env: string[]; hooks: string[] }> = {
|
|
49
115
|
none: {
|
|
50
116
|
env: [],
|
|
@@ -110,7 +176,7 @@ export const DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
110
176
|
"Repository resources are mounted under repos/<host>/<owner>/<repo> unless the session specifies another collision-free mount path.",
|
|
111
177
|
"File resources are mounted under .opengeni/files/<file-id>/ unless the session specifies another mount path.",
|
|
112
178
|
"Attached files are mounted read-only; copy them before modifying.",
|
|
113
|
-
"
|
|
179
|
+
"Installed and selected Skills are indexed under .agents/ and may include role-specific guidance.",
|
|
114
180
|
"Use Checkov, Terraform, Azure CLI, git provider CLIs, and repository tools when relevant; gh, glab, and az repos are pre-authenticated when the host brokers matching git credentials.",
|
|
115
181
|
"When the Azure sandbox preparation profile is enabled and service-principal variables are present, the sandbox is pre-authenticated with normal Azure CLI before work starts.",
|
|
116
182
|
"Treat code-changing work as GitOps work: create a focused branch/commit/PR when git provider credentials are available; otherwise report exact commands and blockers.",
|
|
@@ -214,6 +280,12 @@ const SettingsSchema = z.object({
|
|
|
214
280
|
turnWorkerMaxConcurrentTurns: z.coerce.number().int().positive().max(2_000).default(16),
|
|
215
281
|
turnWorkerTargetCpuUsage: z.coerce.number().positive().max(1).default(0.8),
|
|
216
282
|
turnWorkerTargetMemoryUsage: z.coerce.number().positive().max(0.8).default(0.75),
|
|
283
|
+
// Admission and emergency recovery are deliberately separate control loops.
|
|
284
|
+
// The Temporal tuner stops polling at the lower target; only genuine danger
|
|
285
|
+
// may invoke the disruptive graceful-drain fallback.
|
|
286
|
+
turnWorkerEmergencyMemoryUsage: z.coerce.number().min(0.85).max(0.95).default(0.9),
|
|
287
|
+
turnWorkerMemoryGuardIntervalMs: z.coerce.number().int().min(1_000).max(60_000).default(5_000),
|
|
288
|
+
turnWorkerMemoryGuardSustainMs: z.coerce.number().int().min(5_000).max(300_000).default(30_000),
|
|
217
289
|
observabilityStructuredLogs: EnvBoolean.default(false),
|
|
218
290
|
observabilityMetricsEnabled: EnvBoolean.default(true),
|
|
219
291
|
observabilityOtlpEndpoint: z.string().url().optional(),
|
|
@@ -249,7 +321,7 @@ const SettingsSchema = z.object({
|
|
|
249
321
|
agentStableVersion: z
|
|
250
322
|
.string()
|
|
251
323
|
.regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u)
|
|
252
|
-
.default("0.1.
|
|
324
|
+
.default("0.1.14"),
|
|
253
325
|
// Optional independent beta-channel pointer. When unset, the beta update
|
|
254
326
|
// manifest route is unavailable rather than silently serving stable.
|
|
255
327
|
agentBetaVersion: z
|
|
@@ -263,6 +335,8 @@ const SettingsSchema = z.object({
|
|
|
263
335
|
staticEntitlementsJson: z.string().default("{}"),
|
|
264
336
|
staticUsageLimitsJson: z.string().default("{}"),
|
|
265
337
|
delegationSecret: z.string().optional(),
|
|
338
|
+
defaultFirstPartyMcpTools: EnvFirstPartyMcpTools,
|
|
339
|
+
allowedFirstPartyMcpTools: EnvFirstPartyMcpTools,
|
|
266
340
|
// sandbox workspace scoped stream-token HMAC secret (sandbox contract §C.3 / stream-token availability contract).
|
|
267
341
|
// When unset, the API falls back to `delegationSecret` (the same HMAC envelope
|
|
268
342
|
// family, `ogs_` vs `ogd_` prefix). REQUIRED-WHEN-DESKTOP, but the absence of
|
|
@@ -273,8 +347,7 @@ const SettingsSchema = z.object({
|
|
|
273
347
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
274
348
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
275
349
|
streamControlEnabled: EnvBoolean.default(false),
|
|
276
|
-
|
|
277
|
-
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
350
|
+
codemodeMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
278
351
|
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
279
352
|
// that do not carry the stock-image ogtool binary. Exact stable versions only:
|
|
280
353
|
// the agent must never guess a tag or silently install `latest`.
|
|
@@ -287,11 +360,17 @@ const SettingsSchema = z.object({
|
|
|
287
360
|
integrationsStateSecret: z.string().optional(),
|
|
288
361
|
integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
|
|
289
362
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
363
|
+
gmailRestAdapterEnabled: EnvBoolean.default(false),
|
|
290
364
|
slackClientId: z.string().optional(),
|
|
291
365
|
slackClientSecret: z.string().optional(),
|
|
292
366
|
slackSigningSecret: z.string().optional(),
|
|
293
367
|
googleDriveClientId: z.string().optional(),
|
|
294
368
|
googleDriveClientSecret: z.string().optional(),
|
|
369
|
+
fikenClientId: z.string().optional(),
|
|
370
|
+
fikenClientSecret: z.string().optional(),
|
|
371
|
+
googleDriveWorkspaceEventsEnabled: EnvBoolean.optional(),
|
|
372
|
+
atlassianClientId: z.string().optional(),
|
|
373
|
+
atlassianClientSecret: z.string().optional(),
|
|
295
374
|
// Undefined is meaningful: the migration boundary persists the product
|
|
296
375
|
// default of 3 when no deployment override is supplied.
|
|
297
376
|
maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
|
|
@@ -365,6 +444,45 @@ const SettingsSchema = z.object({
|
|
|
365
444
|
// Gateway models below are added to the managed-credit catalog. Workspace
|
|
366
445
|
// Gateway keys use the encrypted connection broker and never this secret.
|
|
367
446
|
vercelAiGatewayApiKey: z.string().optional(),
|
|
447
|
+
/** Image adapter route; native hosted providers ignore this model. */
|
|
448
|
+
imageGenerationModel: z.string().trim().min(1).max(256).default("openai/gpt-image-2"),
|
|
449
|
+
/** Durable video generation uses the workspace-owned Gateway credential. */
|
|
450
|
+
videoGenerationPollIntervalMs: z.coerce.number().int().min(1_000).max(60_000).default(5_000),
|
|
451
|
+
videoGenerationRecoveryDeadlineMs: z.coerce
|
|
452
|
+
.number()
|
|
453
|
+
.int()
|
|
454
|
+
.min(60_000)
|
|
455
|
+
.max(24 * 60 * 60_000)
|
|
456
|
+
.default(2 * 60 * 60_000),
|
|
457
|
+
videoGenerationReferenceUrlTtlSeconds: z.coerce
|
|
458
|
+
.number()
|
|
459
|
+
.int()
|
|
460
|
+
.min(300)
|
|
461
|
+
.max(6 * 60 * 60)
|
|
462
|
+
.default(60 * 60),
|
|
463
|
+
videoGenerationMaxConcurrentPerWorkspace: z.coerce.number().int().min(1).max(16).default(2),
|
|
464
|
+
videoGenerationWorkspaceQuotaBytes: z.coerce
|
|
465
|
+
.number()
|
|
466
|
+
.int()
|
|
467
|
+
.positive()
|
|
468
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
469
|
+
.default(20 * 1024 * 1024 * 1024),
|
|
470
|
+
videoGenerationTempDirectory: z.string().trim().min(1).max(1_024).default("/tmp/opengeni-video"),
|
|
471
|
+
videoGenerationFfprobePath: z.string().trim().min(1).max(1_024).default("ffprobe"),
|
|
472
|
+
// OpenGeni's customer price, not a claim about the provider's delayed cost report.
|
|
473
|
+
// The durable operation freezes the exact resulting price before provider submit.
|
|
474
|
+
videoGenerationCredit480pMicrosPerSecond: z.coerce
|
|
475
|
+
.number()
|
|
476
|
+
.int()
|
|
477
|
+
.positive()
|
|
478
|
+
.max(10_000_000)
|
|
479
|
+
.default(155_000),
|
|
480
|
+
videoGenerationCredit720pMicrosPerSecond: z.coerce
|
|
481
|
+
.number()
|
|
482
|
+
.int()
|
|
483
|
+
.positive()
|
|
484
|
+
.max(10_000_000)
|
|
485
|
+
.default(350_000),
|
|
368
486
|
// Native composer voice input (browser MediaRecorder → API transcription).
|
|
369
487
|
// Provider credentials stay server-side; ClientConfig only projects availability
|
|
370
488
|
// and hard ceilings. Selection happens once before audio is sent — never retry
|
|
@@ -406,10 +524,12 @@ const SettingsSchema = z.object({
|
|
|
406
524
|
.default(24 * 60 * 60),
|
|
407
525
|
voiceInputFfmpegPath: z.string().trim().min(1).max(1024).default("ffmpeg"),
|
|
408
526
|
// Preferred provider order (comma-separated ids). First configured+ready wins.
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
// Supported:
|
|
412
|
-
voiceInputProviderOrder: z
|
|
527
|
+
// Connected subscription STT is preferred by default; operators can put
|
|
528
|
+
// openai/azure-openai first explicitly.
|
|
529
|
+
// Supported: supergrok-subscription, codex-subscription, openai, azure-openai.
|
|
530
|
+
voiceInputProviderOrder: z
|
|
531
|
+
.string()
|
|
532
|
+
.default("supergrok-subscription,codex-subscription,openai,azure-openai"),
|
|
413
533
|
// OpenAI public /v1/audio/transcriptions path. Reuses OPENGENI_OPENAI_API_KEY
|
|
414
534
|
// when voiceInputOpenaiApiKey is unset. Default model is gpt-transcribe.
|
|
415
535
|
voiceInputOpenaiEnabled: EnvBoolean.default(true),
|
|
@@ -441,6 +561,9 @@ const SettingsSchema = z.object({
|
|
|
441
561
|
// subscription is injected as a synthetic "codex-subscription" registry
|
|
442
562
|
// provider whose models route through the ChatGPT backend (@opengeni/codex).
|
|
443
563
|
codexSubscriptionEnabled: EnvBoolean.default(false), // OPENGENI_CODEX_SUBSCRIPTION_ENABLED
|
|
564
|
+
// SuperGrok/xAI connected subscription. This is a workspace-scoped OAuth
|
|
565
|
+
// account pool and a distinct rail from the existing xai/* API-key provider.
|
|
566
|
+
supergrokSubscriptionEnabled: EnvBoolean.default(false), // OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED
|
|
444
567
|
// Expose the connected apps attached to a Codex subscription through the
|
|
445
568
|
// synthetic codex_apps MCP server. Independent from subscription routing so
|
|
446
569
|
// operators can use Codex models without exposing ChatGPT connectors.
|
|
@@ -455,6 +578,11 @@ const SettingsSchema = z.object({
|
|
|
455
578
|
// compatibility diagnosis.
|
|
456
579
|
// OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
457
580
|
codexToolSearchEnabled: EnvBoolean.default(true),
|
|
581
|
+
// Provider-neutral progressive disclosure for direct OpenAI/Azure native
|
|
582
|
+
// client search and ordinary-function generic dispatch. Kept separate from
|
|
583
|
+
// the Codex rollout so an emergency Codex opt-out cannot disable every model.
|
|
584
|
+
// OPENGENI_LAZY_TOOL_SEARCH_ENABLED
|
|
585
|
+
lazyToolSearchEnabled: EnvBoolean.default(true),
|
|
458
586
|
// credential allocator atomic, workspace-local credential allocation. Default OFF is a
|
|
459
587
|
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
460
588
|
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
@@ -512,6 +640,13 @@ const SettingsSchema = z.object({
|
|
|
512
640
|
disableOpenaiTracing: EnvBoolean.default(false),
|
|
513
641
|
sandboxBackend: SandboxBackend.default("docker"),
|
|
514
642
|
dockerImage: z.string().default("opengeni-sandbox:local"),
|
|
643
|
+
// Explicit deployment contract: the configured base sandbox image contains
|
|
644
|
+
// the verified, self-contained native artifact runtime at its fixed image
|
|
645
|
+
// paths. Disabled by default so arbitrary/custom provider images never make
|
|
646
|
+
// document/spreadsheet/presentation skills appear when their runtime is
|
|
647
|
+
// absent. Per-pack/per-rig image overrides fail closed in the worker even
|
|
648
|
+
// when this base-image contract is enabled.
|
|
649
|
+
sandboxArtifactRuntimeEnabled: EnvBoolean.default(false),
|
|
515
650
|
dockerExposedPorts: z.string().default(""),
|
|
516
651
|
dockerNetwork: z.string().optional(),
|
|
517
652
|
// When the worker itself runs in a container and talks to a host Docker daemon,
|
|
@@ -528,7 +663,13 @@ const SettingsSchema = z.object({
|
|
|
528
663
|
// the Modal session envelope persists the actual image ID.
|
|
529
664
|
modalImageId: z
|
|
530
665
|
.string()
|
|
531
|
-
.
|
|
666
|
+
// Modal image IDs are provider-opaque. Older builds use a 22-character
|
|
667
|
+
// random suffix while current filesystem snapshots use a 26-character
|
|
668
|
+
// ULID suffix. Validate the stable namespace/safe alphabet and let Modal
|
|
669
|
+
// remain authoritative over current/future suffix lengths.
|
|
670
|
+
.min(4)
|
|
671
|
+
.max(128)
|
|
672
|
+
.regex(/^im-[A-Za-z0-9]+$/)
|
|
532
673
|
.optional(),
|
|
533
674
|
// Name of a Modal Secret (containing REGISTRY_USERNAME + REGISTRY_PASSWORD) used
|
|
534
675
|
// to authenticate the pull of `modalImageRef` from a PRIVATE registry. When UNSET
|
|
@@ -571,17 +712,17 @@ const SettingsSchema = z.object({
|
|
|
571
712
|
// SOONER than the hard lifetime; the boot invariant forbids a value that would
|
|
572
713
|
// reap before reaperPeriod + idleGrace elapses.
|
|
573
714
|
modalIdleTimeoutSeconds: z.coerce.number().int().positive().optional(),
|
|
574
|
-
// /workspace FILE PERSISTENCE across warm/cold cycles.
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
//
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
//
|
|
715
|
+
// /workspace FILE PERSISTENCE across warm/cold cycles. Directory snapshots
|
|
716
|
+
// preserve only the durable user workspace, so provider recovery does not
|
|
717
|
+
// restore an entire machine image or replace the selected rig/base image.
|
|
718
|
+
// Cold restore derives the mode from its verified native artifact, so existing
|
|
719
|
+
// serialized sessions remain recoverable; this default governs archive-free
|
|
720
|
+
// Modal creations only.
|
|
721
|
+
// `snapshot_filesystem` remains available for explicit compatibility and
|
|
722
|
+
// immutable rig-image materialization. `tar` is the portable fallback.
|
|
582
723
|
modalWorkspacePersistence: z
|
|
583
724
|
.enum(["tar", "snapshot_filesystem", "snapshot_directory"])
|
|
584
|
-
.default("
|
|
725
|
+
.default("snapshot_directory"),
|
|
585
726
|
// Shared desktop toggle: this module reads it for the 6080 port-merge; the
|
|
586
727
|
// owner module (P4.x) acts on it to launch the display stack.
|
|
587
728
|
sandboxDesktopEnabled: EnvBoolean.default(false),
|
|
@@ -660,6 +801,14 @@ const SettingsSchema = z.object({
|
|
|
660
801
|
// --- cloudflare (headless) ---
|
|
661
802
|
cloudflareWorkerUrl: z.string().url().optional(),
|
|
662
803
|
cloudflareApiKey: z.string().optional(),
|
|
804
|
+
// --- remote browser placements ---
|
|
805
|
+
// Provider credentials are injected only into the placement-resident
|
|
806
|
+
// browserd launch. They never enter session contracts, journals, or sandboxes.
|
|
807
|
+
browserbaseApiKey: z.string().min(1).max(8192).optional(),
|
|
808
|
+
kernelApiKey: z.string().min(1).max(8192).optional(),
|
|
809
|
+
kernelEndpoint: z.string().url().optional(),
|
|
810
|
+
kernelBrowserTimeoutSeconds: z.coerce.number().int().positive().max(86_400).default(3_600),
|
|
811
|
+
kernelBrowserStealth: EnvBoolean.default(false),
|
|
663
812
|
// --- vercel (headless) ---
|
|
664
813
|
vercelToken: z.string().optional(),
|
|
665
814
|
vercelProjectId: z.string().optional(),
|
|
@@ -789,6 +938,10 @@ const SettingsSchema = z.object({
|
|
|
789
938
|
// snapshotted before the box dies (sandbox-file-persistence).
|
|
790
939
|
sandboxLeaseReaperPeriodMs: z.coerce.number().int().positive().default(30_000),
|
|
791
940
|
sandboxViewerHolderTtlMs: z.coerce.number().int().positive().default(90_000),
|
|
941
|
+
// A BrowserSession controller refreshes its durable resource and exact
|
|
942
|
+
// interaction lease holder together. This longer crash horizon tolerates API
|
|
943
|
+
// replacement while still releasing a placement whose controller died.
|
|
944
|
+
sandboxInteractionHolderTtlMs: z.coerce.number().int().positive().default(180_000),
|
|
792
945
|
// The DRAIN grace: how long a refcount-0 (draining) lease stays WARM before the
|
|
793
946
|
// reaper resume-by-ids the box and terminates it. This is the cost-vs-snappiness
|
|
794
947
|
// dial — when the user navigates away the box keeps refcount 0, but it survives
|
|
@@ -814,7 +967,12 @@ const SettingsSchema = z.object({
|
|
|
814
967
|
// graceful shutdown, or become permission to GC an older archive. Timeout is
|
|
815
968
|
// treated exactly like a failed best-effort snapshot. Knob:
|
|
816
969
|
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
817
|
-
sandboxSnapshotTimeoutMs: z.coerce
|
|
970
|
+
sandboxSnapshotTimeoutMs: z.coerce
|
|
971
|
+
.number()
|
|
972
|
+
.int()
|
|
973
|
+
.positive()
|
|
974
|
+
.max(SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS)
|
|
975
|
+
.default(60_000),
|
|
818
976
|
// Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
|
|
819
977
|
// ahead of a finite provider deadline. Modal's 24h creation clock cannot be
|
|
820
978
|
// extended; the logical sandbox outlives it by moving to one successor box.
|
|
@@ -824,13 +982,12 @@ const SettingsSchema = z.object({
|
|
|
824
982
|
// an operator deliberately wants more rotation headroom; the boot invariant
|
|
825
983
|
// still requires it to remain below the provider lifetime.
|
|
826
984
|
sandboxRotationLeadMs: z.coerce.number().int().positive().default(3_600_000),
|
|
827
|
-
// Bound
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
|
|
833
|
-
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(1),
|
|
985
|
+
// Bound provider-deadline rotation admission independently of execution.
|
|
986
|
+
// Every admitted box receives its own durable drain child; the control worker
|
|
987
|
+
// limits provider I/O to 32 concurrent activities. Matching that bound avoids
|
|
988
|
+
// both the old one-box-per-tick deadline backlog and an unbounded provider/API
|
|
989
|
+
// burst. Operators may tune this for a differently-sized worker pool.
|
|
990
|
+
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(32),
|
|
834
991
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
835
992
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
836
993
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -940,7 +1097,11 @@ export type Settings = z.infer<typeof SettingsSchema>;
|
|
|
940
1097
|
export type McpServerConfig = Settings["mcpServers"][number];
|
|
941
1098
|
|
|
942
1099
|
/** Declarative voice-input transcription provider ids. */
|
|
943
|
-
export type VoiceInputProviderId =
|
|
1100
|
+
export type VoiceInputProviderId =
|
|
1101
|
+
| "openai"
|
|
1102
|
+
| "azure-openai"
|
|
1103
|
+
| "codex-subscription"
|
|
1104
|
+
| "supergrok-subscription";
|
|
944
1105
|
|
|
945
1106
|
export type VoiceInputProviderConfig =
|
|
946
1107
|
| {
|
|
@@ -963,6 +1124,11 @@ export type VoiceInputProviderConfig =
|
|
|
963
1124
|
id: "codex-subscription";
|
|
964
1125
|
kind: "codex-subscription";
|
|
965
1126
|
experimental: true;
|
|
1127
|
+
}
|
|
1128
|
+
| {
|
|
1129
|
+
id: "supergrok-subscription";
|
|
1130
|
+
kind: "supergrok-subscription";
|
|
1131
|
+
experimental: true;
|
|
966
1132
|
};
|
|
967
1133
|
|
|
968
1134
|
/**
|
|
@@ -999,7 +1165,10 @@ export function resolveVoiceInputProviderRegistry(settings: Settings): VoiceInpu
|
|
|
999
1165
|
.map((part) => part.trim())
|
|
1000
1166
|
.filter(
|
|
1001
1167
|
(part): part is VoiceInputProviderId =>
|
|
1002
|
-
part === "openai" ||
|
|
1168
|
+
part === "openai" ||
|
|
1169
|
+
part === "azure-openai" ||
|
|
1170
|
+
part === "codex-subscription" ||
|
|
1171
|
+
part === "supergrok-subscription",
|
|
1003
1172
|
);
|
|
1004
1173
|
const seen = new Set<VoiceInputProviderId>();
|
|
1005
1174
|
const providers: VoiceInputProviderConfig[] = [];
|
|
@@ -1087,6 +1256,15 @@ export function resolveVoiceInputProviderRegistry(settings: Settings): VoiceInpu
|
|
|
1087
1256
|
kind: "codex-subscription",
|
|
1088
1257
|
experimental: true,
|
|
1089
1258
|
});
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (id === "supergrok-subscription") {
|
|
1262
|
+
if (!settings.supergrokSubscriptionEnabled) continue;
|
|
1263
|
+
providers.push({
|
|
1264
|
+
id: "supergrok-subscription",
|
|
1265
|
+
kind: "supergrok-subscription",
|
|
1266
|
+
experimental: true,
|
|
1267
|
+
});
|
|
1090
1268
|
}
|
|
1091
1269
|
}
|
|
1092
1270
|
return providers;
|
|
@@ -1095,7 +1273,8 @@ export function resolveVoiceInputProviderRegistry(settings: Settings): VoiceInpu
|
|
|
1095
1273
|
/** True when the deployment has at least one supported (non-experimental) provider. */
|
|
1096
1274
|
export function voiceInputDeploymentConfigured(settings: Settings): boolean {
|
|
1097
1275
|
return resolveVoiceInputProviderRegistry(settings).some(
|
|
1098
|
-
(provider) =>
|
|
1276
|
+
(provider) =>
|
|
1277
|
+
provider.kind !== "codex-subscription" && provider.kind !== "supergrok-subscription",
|
|
1099
1278
|
);
|
|
1100
1279
|
}
|
|
1101
1280
|
|
|
@@ -1135,6 +1314,13 @@ export type ModelUsageInput = {
|
|
|
1135
1314
|
requestUsageEntries?: ModelUsageInput[] | undefined;
|
|
1136
1315
|
};
|
|
1137
1316
|
|
|
1317
|
+
export type ModelUsageCostBreakdown = {
|
|
1318
|
+
/** Provider-rate cost basis for the exact usage, before OpenGeni margin. */
|
|
1319
|
+
providerCostMicros: number;
|
|
1320
|
+
/** OpenGeni credit price after configured margin and latency-mode multiplier. */
|
|
1321
|
+
creditCostMicros: number;
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1138
1324
|
export type StaticUsageLimitsConfig = StaticUsageLimits;
|
|
1139
1325
|
export type EntitlementsConfig = Entitlements;
|
|
1140
1326
|
|
|
@@ -1206,6 +1392,10 @@ export const ModelCapabilitiesV1Schema = z
|
|
|
1206
1392
|
webSearch: CapabilityStateV1Schema,
|
|
1207
1393
|
xSearch: CapabilityStateV1Schema,
|
|
1208
1394
|
codeExecution: CapabilityStateV1Schema,
|
|
1395
|
+
imageGeneration: CapabilityStateV1Schema.default({
|
|
1396
|
+
upstream: "unknown",
|
|
1397
|
+
runnable: false,
|
|
1398
|
+
}),
|
|
1209
1399
|
}),
|
|
1210
1400
|
inputModalities: z.array(ModelModalityV1).min(1),
|
|
1211
1401
|
/** Exact MIME types accepted as typed `input_file`; `text/*` is allowed. */
|
|
@@ -1300,7 +1490,7 @@ export type ModelExecutionLimitsV1 = {
|
|
|
1300
1490
|
|
|
1301
1491
|
export type CredentialSourceV1 =
|
|
1302
1492
|
| { kind: "deployment"; mechanism: "api_key" | "azure_ad_bearer" }
|
|
1303
|
-
| { kind: "connected_subscription"; provider: "codex" }
|
|
1493
|
+
| { kind: "connected_subscription"; provider: "codex" | "xai" }
|
|
1304
1494
|
| { kind: "workspace_connection"; mechanism: "api_key" };
|
|
1305
1495
|
|
|
1306
1496
|
export type BillingAttributionV1 = {
|
|
@@ -1320,12 +1510,13 @@ export type ModelProviderApi = z.infer<typeof ModelProviderApi>;
|
|
|
1320
1510
|
|
|
1321
1511
|
/**
|
|
1322
1512
|
* Registry provider kind. "api-key" providers carry their own static key/headers;
|
|
1323
|
-
*
|
|
1324
|
-
*
|
|
1513
|
+
* connected-subscription providers resolve a workspace account token at call
|
|
1514
|
+
* time and never carry a static key in the registry definition.
|
|
1325
1515
|
*/
|
|
1326
1516
|
export const RegistryProviderKind = z.enum([
|
|
1327
1517
|
"api-key",
|
|
1328
1518
|
"codex-subscription",
|
|
1519
|
+
"xai-subscription",
|
|
1329
1520
|
"vercel-gateway-managed",
|
|
1330
1521
|
"vercel-gateway-workspace",
|
|
1331
1522
|
]);
|
|
@@ -1475,6 +1666,7 @@ export const VERCEL_AI_GATEWAY_CONNECTION_DOMAIN = "ai-gateway.vercel.sh" as con
|
|
|
1475
1666
|
export const VERCEL_AI_GATEWAY_CONNECTION_ROLE = "vercel_ai_gateway" as const;
|
|
1476
1667
|
|
|
1477
1668
|
export const CODEX_REALTIME_MODEL_ID = "gpt-live-1-boulder-alpha" as const;
|
|
1669
|
+
export const SUPERGROK_REALTIME_MODEL_ID = "supergrok/grok-voice-think-fast-2.0" as const;
|
|
1478
1670
|
export const OPENGENI_REALTIME_MODEL_ID_PREFIX = "opengeni-gateway/" as const;
|
|
1479
1671
|
export const WORKSPACE_REALTIME_MODEL_ID_PREFIX = "workspace-gateway/" as const;
|
|
1480
1672
|
|
|
@@ -1742,6 +1934,9 @@ export function getSettings(): Settings {
|
|
|
1742
1934
|
turnWorkerMaxConcurrentTurns: optional("OPENGENI_TURN_WORKER_MAX_CONCURRENT_TURNS"),
|
|
1743
1935
|
turnWorkerTargetCpuUsage: optional("OPENGENI_TURN_WORKER_TARGET_CPU_USAGE"),
|
|
1744
1936
|
turnWorkerTargetMemoryUsage: optional("OPENGENI_TURN_WORKER_TARGET_MEMORY_USAGE"),
|
|
1937
|
+
turnWorkerEmergencyMemoryUsage: optional("OPENGENI_TURN_WORKER_EMERGENCY_MEMORY_USAGE"),
|
|
1938
|
+
turnWorkerMemoryGuardIntervalMs: optional("OPENGENI_TURN_WORKER_MEMORY_GUARD_INTERVAL_MS"),
|
|
1939
|
+
turnWorkerMemoryGuardSustainMs: optional("OPENGENI_TURN_WORKER_MEMORY_GUARD_SUSTAIN_MS"),
|
|
1745
1940
|
observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
|
|
1746
1941
|
observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
|
|
1747
1942
|
observabilityOtlpEndpoint:
|
|
@@ -1766,10 +1961,11 @@ export function getSettings(): Settings {
|
|
|
1766
1961
|
staticEntitlementsJson: optional("OPENGENI_STATIC_ENTITLEMENTS_JSON"),
|
|
1767
1962
|
staticUsageLimitsJson: optional("OPENGENI_STATIC_USAGE_LIMITS_JSON"),
|
|
1768
1963
|
delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
|
|
1964
|
+
defaultFirstPartyMcpTools: optional("OPENGENI_DEFAULT_FIRST_PARTY_MCP_TOOLS"),
|
|
1965
|
+
allowedFirstPartyMcpTools: optional("OPENGENI_ALLOWED_FIRST_PARTY_MCP_TOOLS"),
|
|
1769
1966
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
1770
1967
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1771
|
-
|
|
1772
|
-
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
1968
|
+
codemodeMaxCallsPerTurn: optional("OPENGENI_CODEMODE_MAX_CALLS_PER_TURN"),
|
|
1773
1969
|
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
1774
1970
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
1775
1971
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
@@ -1778,11 +1974,17 @@ export function getSettings(): Settings {
|
|
|
1778
1974
|
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS",
|
|
1779
1975
|
),
|
|
1780
1976
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1977
|
+
gmailRestAdapterEnabled: optional("OPENGENI_GMAIL_REST_ADAPTER_ENABLED"),
|
|
1781
1978
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1782
1979
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1783
1980
|
slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
|
|
1784
1981
|
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1785
1982
|
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1983
|
+
fikenClientId: optional("OPENGENI_FIKEN_OAUTH_CLIENT_ID"),
|
|
1984
|
+
fikenClientSecret: optional("OPENGENI_FIKEN_OAUTH_CLIENT_SECRET"),
|
|
1985
|
+
googleDriveWorkspaceEventsEnabled: optional("OPENGENI_GOOGLE_DRIVE_WORKSPACE_EVENTS_ENABLED"),
|
|
1986
|
+
atlassianClientId: optional("OPENGENI_ATLASSIAN_CLIENT_ID"),
|
|
1987
|
+
atlassianClientSecret: optional("OPENGENI_ATLASSIAN_CLIENT_SECRET"),
|
|
1786
1988
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1787
1989
|
socialOauthClientsJson: optional("OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON"),
|
|
1788
1990
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
@@ -1809,6 +2011,24 @@ export function getSettings(): Settings {
|
|
|
1809
2011
|
openaiModel: optional("OPENGENI_OPENAI_MODEL"),
|
|
1810
2012
|
openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
|
|
1811
2013
|
vercelAiGatewayApiKey: optional("OPENGENI_VERCEL_AI_GATEWAY_API_KEY"),
|
|
2014
|
+
imageGenerationModel: optional("OPENGENI_IMAGE_GENERATION_MODEL"),
|
|
2015
|
+
videoGenerationPollIntervalMs: optional("OPENGENI_VIDEO_GENERATION_POLL_INTERVAL_MS"),
|
|
2016
|
+
videoGenerationRecoveryDeadlineMs: optional("OPENGENI_VIDEO_GENERATION_RECOVERY_DEADLINE_MS"),
|
|
2017
|
+
videoGenerationReferenceUrlTtlSeconds: optional(
|
|
2018
|
+
"OPENGENI_VIDEO_GENERATION_REFERENCE_URL_TTL_SECONDS",
|
|
2019
|
+
),
|
|
2020
|
+
videoGenerationMaxConcurrentPerWorkspace: optional(
|
|
2021
|
+
"OPENGENI_VIDEO_GENERATION_MAX_CONCURRENT_PER_WORKSPACE",
|
|
2022
|
+
),
|
|
2023
|
+
videoGenerationWorkspaceQuotaBytes: optional("OPENGENI_VIDEO_GENERATION_WORKSPACE_QUOTA_BYTES"),
|
|
2024
|
+
videoGenerationTempDirectory: optional("OPENGENI_VIDEO_GENERATION_TEMP_DIRECTORY"),
|
|
2025
|
+
videoGenerationFfprobePath: optional("OPENGENI_VIDEO_GENERATION_FFPROBE_PATH"),
|
|
2026
|
+
videoGenerationCredit480pMicrosPerSecond: optional(
|
|
2027
|
+
"OPENGENI_VIDEO_GENERATION_CREDIT_480P_MICROS_PER_SECOND",
|
|
2028
|
+
),
|
|
2029
|
+
videoGenerationCredit720pMicrosPerSecond: optional(
|
|
2030
|
+
"OPENGENI_VIDEO_GENERATION_CREDIT_720P_MICROS_PER_SECOND",
|
|
2031
|
+
),
|
|
1812
2032
|
voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
|
|
1813
2033
|
voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
|
|
1814
2034
|
voiceInputResumableEnabled: optional("OPENGENI_VOICE_INPUT_RESUMABLE_ENABLED"),
|
|
@@ -1838,8 +2058,10 @@ export function getSettings(): Settings {
|
|
|
1838
2058
|
modelPricingJson: optional("OPENGENI_MODEL_PRICING_JSON"),
|
|
1839
2059
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
1840
2060
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
2061
|
+
supergrokSubscriptionEnabled: optional("OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED"),
|
|
1841
2062
|
codexConnectedAppsEnabled: optional("OPENGENI_CODEX_CONNECTED_APPS_ENABLED"),
|
|
1842
2063
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
2064
|
+
lazyToolSearchEnabled: optional("OPENGENI_LAZY_TOOL_SEARCH_ENABLED"),
|
|
1843
2065
|
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
1844
2066
|
codexFleetPolicyShadowEnabled: optional("OPENGENI_CODEX_FLEET_POLICY_SHADOW_ENABLED"),
|
|
1845
2067
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
@@ -1860,6 +2082,7 @@ export function getSettings(): Settings {
|
|
|
1860
2082
|
disableOpenaiTracing: optional("OPENGENI_DISABLE_OPENAI_TRACING"),
|
|
1861
2083
|
sandboxBackend: optional("OPENGENI_SANDBOX_BACKEND"),
|
|
1862
2084
|
dockerImage: optional("OPENGENI_DOCKER_IMAGE"),
|
|
2085
|
+
sandboxArtifactRuntimeEnabled: optional("OPENGENI_SANDBOX_ARTIFACT_RUNTIME_ENABLED"),
|
|
1863
2086
|
dockerExposedPorts: optional("OPENGENI_DOCKER_EXPOSED_PORTS"),
|
|
1864
2087
|
dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
|
|
1865
2088
|
dockerWorkspaceBaseDir: optional("OPENGENI_DOCKER_WORKSPACE_BASE_DIR"),
|
|
@@ -1916,6 +2139,11 @@ export function getSettings(): Settings {
|
|
|
1916
2139
|
blaxelTtl: optional("OPENGENI_BLAXEL_TTL"),
|
|
1917
2140
|
cloudflareWorkerUrl: optional("OPENGENI_CLOUDFLARE_WORKER_URL"),
|
|
1918
2141
|
cloudflareApiKey: optional("OPENGENI_CLOUDFLARE_API_KEY"),
|
|
2142
|
+
browserbaseApiKey: optional("OPENGENI_BROWSERBASE_API_KEY"),
|
|
2143
|
+
kernelApiKey: optional("OPENGENI_KERNEL_API_KEY"),
|
|
2144
|
+
kernelEndpoint: optional("OPENGENI_KERNEL_ENDPOINT"),
|
|
2145
|
+
kernelBrowserTimeoutSeconds: optional("OPENGENI_KERNEL_BROWSER_TIMEOUT_SECONDS"),
|
|
2146
|
+
kernelBrowserStealth: optional("OPENGENI_KERNEL_BROWSER_STEALTH"),
|
|
1919
2147
|
vercelToken: optional("OPENGENI_VERCEL_TOKEN"),
|
|
1920
2148
|
vercelProjectId: optional("OPENGENI_VERCEL_PROJECT_ID"),
|
|
1921
2149
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
@@ -1942,6 +2170,7 @@ export function getSettings(): Settings {
|
|
|
1942
2170
|
sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
|
|
1943
2171
|
sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
|
|
1944
2172
|
sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
|
|
2173
|
+
sandboxInteractionHolderTtlMs: optional("OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS"),
|
|
1945
2174
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
1946
2175
|
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
1947
2176
|
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
@@ -2039,12 +2268,44 @@ const LOCAL_FIRST_PARTY_DELEGATION_SECRET = "opengeni-local-first-party-delegati
|
|
|
2039
2268
|
export function resolveFirstPartyDelegationSecret(settings: Settings): string | undefined {
|
|
2040
2269
|
const explicit = settings.delegationSecret?.trim();
|
|
2041
2270
|
if (explicit) return explicit;
|
|
2271
|
+
const configuredAccessKey = settings.accessKey?.trim();
|
|
2272
|
+
if (settings.productAccessMode === "configured" && settings.authRequired && configuredAccessKey) {
|
|
2273
|
+
return configuredAccessKey;
|
|
2274
|
+
}
|
|
2042
2275
|
return settings.productAccessMode === "local" &&
|
|
2043
2276
|
(settings.environment === "local" || settings.environment === "test")
|
|
2044
2277
|
? LOCAL_FIRST_PARTY_DELEGATION_SECRET
|
|
2045
2278
|
: undefined;
|
|
2046
2279
|
}
|
|
2047
2280
|
|
|
2281
|
+
export type FirstPartyMcpToolPolicy = {
|
|
2282
|
+
default: FirstPartyMcpToolNameType[];
|
|
2283
|
+
allowed: FirstPartyMcpToolNameType[];
|
|
2284
|
+
};
|
|
2285
|
+
|
|
2286
|
+
/** Resolve the deployment's session-tool defaults and hard execution ceiling. */
|
|
2287
|
+
export function resolveFirstPartyMcpToolPolicy(
|
|
2288
|
+
settings: Pick<Settings, "defaultFirstPartyMcpTools" | "allowedFirstPartyMcpTools">,
|
|
2289
|
+
): FirstPartyMcpToolPolicy {
|
|
2290
|
+
const allowed = settings.allowedFirstPartyMcpTools ?? [...FIRST_PARTY_MCP_TOOL_NAMES];
|
|
2291
|
+
const allowedSet = new Set(allowed);
|
|
2292
|
+
const defaults = settings.defaultFirstPartyMcpTools ?? [...DEFAULT_FIRST_PARTY_MCP_TOOLS];
|
|
2293
|
+
return {
|
|
2294
|
+
default: defaults.filter((tool) => allowedSet.has(tool)),
|
|
2295
|
+
allowed: [...allowed],
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
/** Apply the deployment ceiling to an existing durable session selection. */
|
|
2300
|
+
export function allowedFirstPartyMcpToolsForSession(
|
|
2301
|
+
settings: Pick<Settings, "defaultFirstPartyMcpTools" | "allowedFirstPartyMcpTools">,
|
|
2302
|
+
selected: readonly FirstPartyMcpToolNameType[] | null | undefined,
|
|
2303
|
+
): FirstPartyMcpToolNameType[] {
|
|
2304
|
+
const policy = resolveFirstPartyMcpToolPolicy(settings);
|
|
2305
|
+
const allowed = new Set(policy.allowed);
|
|
2306
|
+
return [...(selected ?? policy.default)].filter((tool) => allowed.has(tool));
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2048
2309
|
/**
|
|
2049
2310
|
* The Modal sandbox idle timeout (seconds) the provider actually passes as
|
|
2050
2311
|
* idleTimeoutMs (sandbox-file-persistence). When the operator did not pin
|
|
@@ -2072,8 +2333,20 @@ export function sandboxArchiveCaptureTimeoutMs(
|
|
|
2072
2333
|
settings: Pick<Settings, "sandboxSnapshotTimeoutMs">,
|
|
2073
2334
|
): number {
|
|
2074
2335
|
return Math.min(
|
|
2075
|
-
|
|
2076
|
-
|
|
2336
|
+
SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
|
|
2337
|
+
settings.sandboxSnapshotTimeoutMs + SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS,
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
export function sandboxLifecycleTransitionWaitMs(
|
|
2342
|
+
settings: Pick<Settings, "sandboxSnapshotTimeoutMs" | "sandboxLeaseReaperPeriodMs">,
|
|
2343
|
+
): number {
|
|
2344
|
+
const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
|
|
2345
|
+
return Math.min(
|
|
2346
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
|
|
2347
|
+
settings.sandboxLeaseReaperPeriodMs +
|
|
2348
|
+
captureTimeoutMs +
|
|
2349
|
+
SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS,
|
|
2077
2350
|
);
|
|
2078
2351
|
}
|
|
2079
2352
|
|
|
@@ -2336,7 +2609,12 @@ function normalizeCapabilities(capabilities: ModelCapabilitiesV1): ModelCapabili
|
|
|
2336
2609
|
|
|
2337
2610
|
function legacyModelCapabilities(
|
|
2338
2611
|
settings: Settings,
|
|
2339
|
-
input: {
|
|
2612
|
+
input: {
|
|
2613
|
+
reasoningEffort: boolean;
|
|
2614
|
+
hostedWebSearch: boolean;
|
|
2615
|
+
hostedImageGeneration?: boolean;
|
|
2616
|
+
vision?: boolean;
|
|
2617
|
+
},
|
|
2340
2618
|
): ModelCapabilitiesV1 {
|
|
2341
2619
|
const reasoningEfforts = input.reasoningEffort ? configuredAllowedReasoningEfforts(settings) : [];
|
|
2342
2620
|
return normalizeCapabilities({
|
|
@@ -2356,6 +2634,10 @@ function legacyModelCapabilities(
|
|
|
2356
2634
|
},
|
|
2357
2635
|
xSearch: { upstream: "unknown", runnable: false },
|
|
2358
2636
|
codeExecution: { upstream: "unknown", runnable: false },
|
|
2637
|
+
imageGeneration: {
|
|
2638
|
+
upstream: input.hostedImageGeneration ? "supported" : "unknown",
|
|
2639
|
+
runnable: input.hostedImageGeneration ?? false,
|
|
2640
|
+
},
|
|
2359
2641
|
},
|
|
2360
2642
|
inputModalities: input.vision ? ["text", "image"] : ["text"],
|
|
2361
2643
|
inputFileMediaTypes: [
|
|
@@ -2509,12 +2791,17 @@ const GPT56_FAST_BILLING_MULTIPLIER_BPS = 20_000;
|
|
|
2509
2791
|
/**
|
|
2510
2792
|
* Product display label for catalog/picker UI.
|
|
2511
2793
|
* Same string for OpenAI and Codex copies of a slug (`gpt-5.6-luna` and
|
|
2512
|
-
* `codex/gpt-5.6-luna` → `GPT-5.6 Luna`).
|
|
2794
|
+
* `codex/gpt-5.6-luna` → `GPT-5.6 Luna`). Curated Grok slugs receive the same
|
|
2795
|
+
* product casing; other ids pass through unchanged.
|
|
2513
2796
|
*/
|
|
2514
2797
|
export function productLabelForModelId(modelId: string): string {
|
|
2515
2798
|
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX)
|
|
2516
2799
|
? modelId.slice(CODEX_MODEL_ID_PREFIX.length)
|
|
2517
2800
|
: modelId;
|
|
2801
|
+
const grokMatch = /^grok-(\d+(?:\.\d+)?)$/i.exec(slug);
|
|
2802
|
+
if (grokMatch) {
|
|
2803
|
+
return `Grok ${grokMatch[1]}`;
|
|
2804
|
+
}
|
|
2518
2805
|
const match = /^(gpt-\d+(?:\.\d+)?)(?:-(.+))?$/i.exec(slug);
|
|
2519
2806
|
if (!match) {
|
|
2520
2807
|
return slug;
|
|
@@ -2533,14 +2820,16 @@ export function productLabelForModelId(modelId: string): string {
|
|
|
2533
2820
|
}
|
|
2534
2821
|
|
|
2535
2822
|
/**
|
|
2536
|
-
* Curated compact product labels for dense UI.
|
|
2537
|
-
*
|
|
2823
|
+
* Curated compact product labels for dense UI. Unknown model slugs return null
|
|
2824
|
+
* so callers fall back to the full `label`.
|
|
2538
2825
|
*/
|
|
2539
2826
|
export function productShortLabelForModelId(modelId: string): string | null {
|
|
2540
2827
|
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX)
|
|
2541
2828
|
? modelId.slice(CODEX_MODEL_ID_PREFIX.length)
|
|
2542
2829
|
: modelId;
|
|
2543
2830
|
switch (slug) {
|
|
2831
|
+
case "grok-4.6":
|
|
2832
|
+
return "4.6";
|
|
2544
2833
|
case "gpt-5.6-sol":
|
|
2545
2834
|
return "5.6 Sol";
|
|
2546
2835
|
case "gpt-5.6-terra":
|
|
@@ -2588,9 +2877,38 @@ function builtinPromptCachingForModel(
|
|
|
2588
2877
|
: undefined;
|
|
2589
2878
|
}
|
|
2590
2879
|
|
|
2880
|
+
/** Reviewed direct-OpenAI text models that accept the hosted image tool. */
|
|
2881
|
+
function builtinHostedImageGenerationForModel(settings: Settings, modelId: string): boolean {
|
|
2882
|
+
return (
|
|
2883
|
+
settings.openaiProvider === "openai" &&
|
|
2884
|
+
isDirectOpenAiApiBaseUrl(settings.openaiBaseUrl) &&
|
|
2885
|
+
["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"].includes(modelId)
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
/** Undefined and the exact public OpenAI v1 endpoint are the same direct route. */
|
|
2890
|
+
export function isDirectOpenAiApiBaseUrl(baseUrl: string | undefined): boolean {
|
|
2891
|
+
if (baseUrl === undefined) return true;
|
|
2892
|
+
try {
|
|
2893
|
+
const parsed = new URL(baseUrl);
|
|
2894
|
+
return (
|
|
2895
|
+
parsed.protocol === "https:" &&
|
|
2896
|
+
parsed.hostname === "api.openai.com" &&
|
|
2897
|
+
parsed.port === "" &&
|
|
2898
|
+
parsed.username === "" &&
|
|
2899
|
+
parsed.password === "" &&
|
|
2900
|
+
parsed.search === "" &&
|
|
2901
|
+
parsed.hash === "" &&
|
|
2902
|
+
parsed.pathname.replace(/\/+$/, "") === "/v1"
|
|
2903
|
+
);
|
|
2904
|
+
} catch {
|
|
2905
|
+
return false;
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2591
2909
|
/**
|
|
2592
2910
|
* Map OpenGeni latency mode to the provider `service_tier` wire value.
|
|
2593
|
-
* Azure
|
|
2911
|
+
* Azure, Codex ChatGPT, and xAI accept `priority`; OpenAI API accepts `fast`.
|
|
2594
2912
|
* Standard omits the field.
|
|
2595
2913
|
*/
|
|
2596
2914
|
export function serviceTierForLatencyMode(
|
|
@@ -2600,7 +2918,11 @@ export function serviceTierForLatencyMode(
|
|
|
2600
2918
|
if (latencyMode === "standard") {
|
|
2601
2919
|
return undefined;
|
|
2602
2920
|
}
|
|
2603
|
-
if (
|
|
2921
|
+
if (
|
|
2922
|
+
providerId === "azure" ||
|
|
2923
|
+
providerId === CODEX_PROVIDER_ID ||
|
|
2924
|
+
providerId === XAI_SUBSCRIPTION_PROVIDER_ID
|
|
2925
|
+
) {
|
|
2604
2926
|
return "priority";
|
|
2605
2927
|
}
|
|
2606
2928
|
return "fast";
|
|
@@ -2647,6 +2969,9 @@ function registryCredentialSource(provider: RegistryProvider): CredentialSourceV
|
|
|
2647
2969
|
if (provider.kind === "codex-subscription") {
|
|
2648
2970
|
return { kind: "connected_subscription", provider: "codex" };
|
|
2649
2971
|
}
|
|
2972
|
+
if (provider.kind === "xai-subscription") {
|
|
2973
|
+
return { kind: "connected_subscription", provider: "xai" };
|
|
2974
|
+
}
|
|
2650
2975
|
if (provider.kind === "vercel-gateway-workspace") {
|
|
2651
2976
|
return { kind: "workspace_connection", mechanism: "api_key" };
|
|
2652
2977
|
}
|
|
@@ -2654,7 +2979,7 @@ function registryCredentialSource(provider: RegistryProvider): CredentialSourceV
|
|
|
2654
2979
|
}
|
|
2655
2980
|
|
|
2656
2981
|
function registryBilling(provider: RegistryProvider): BillingAttributionV1 {
|
|
2657
|
-
if (provider.kind === "codex-subscription") {
|
|
2982
|
+
if (provider.kind === "codex-subscription" || provider.kind === "xai-subscription") {
|
|
2658
2983
|
return { upstreamPayer: "connected_subscription", metering: "external" };
|
|
2659
2984
|
}
|
|
2660
2985
|
if (provider.kind === "vercel-gateway-workspace") {
|
|
@@ -2874,6 +3199,55 @@ export function withCodexCatalogProvider(settings: Settings): Settings {
|
|
|
2874
3199
|
};
|
|
2875
3200
|
}
|
|
2876
3201
|
|
|
3202
|
+
/**
|
|
3203
|
+
* Static SuperGrok product catalogue, matching the Codex subscription seam.
|
|
3204
|
+
* The overlay never contains a concrete account id or bearer; selection and
|
|
3205
|
+
* per-turn credential freeze remain worker/DB responsibilities.
|
|
3206
|
+
*/
|
|
3207
|
+
export function withXaiSubscriptionCatalogProvider(settings: Settings): Settings {
|
|
3208
|
+
const providers = parseModelProvidersJson(settings.modelProvidersJson);
|
|
3209
|
+
if (providers.some((provider) => provider.id === XAI_SUBSCRIPTION_PROVIDER_ID)) {
|
|
3210
|
+
return settings;
|
|
3211
|
+
}
|
|
3212
|
+
const provider: RegistryProvider = {
|
|
3213
|
+
kind: "xai-subscription",
|
|
3214
|
+
id: XAI_SUBSCRIPTION_PROVIDER_ID,
|
|
3215
|
+
label: "SuperGrok (xAI subscription)",
|
|
3216
|
+
api: "responses",
|
|
3217
|
+
baseUrl: XAI_SUBSCRIPTION_PROXY_BASE_URL,
|
|
3218
|
+
models: XAI_SUBSCRIPTION_MODEL_SLUGS.map((slug) => {
|
|
3219
|
+
const capabilities = legacyModelCapabilities(settings, {
|
|
3220
|
+
reasoningEffort: true,
|
|
3221
|
+
hostedWebSearch: true,
|
|
3222
|
+
});
|
|
3223
|
+
capabilities.reasoning.efforts = ["low", "medium", "high", "xhigh"];
|
|
3224
|
+
capabilities.reasoning.defaultEffort = "high";
|
|
3225
|
+
capabilities.latencyModes = [
|
|
3226
|
+
{ id: "standard", upstream: "supported", runnable: true },
|
|
3227
|
+
{ id: "fast", upstream: "supported", runnable: true },
|
|
3228
|
+
];
|
|
3229
|
+
capabilities.hostedTools.xSearch = { upstream: "supported", runnable: true };
|
|
3230
|
+
capabilities.hostedTools.imageGeneration = { upstream: "supported", runnable: true };
|
|
3231
|
+
return {
|
|
3232
|
+
id: `${XAI_SUBSCRIPTION_MODEL_ID_PREFIX}${slug}`,
|
|
3233
|
+
upstreamModelId: slug,
|
|
3234
|
+
label: productLabelForModelId(slug),
|
|
3235
|
+
...(productShortLabelForModelId(slug)
|
|
3236
|
+
? { shortLabel: productShortLabelForModelId(slug)! }
|
|
3237
|
+
: {}),
|
|
3238
|
+
reasoningEffort: true,
|
|
3239
|
+
hostedWebSearch: true,
|
|
3240
|
+
capabilities,
|
|
3241
|
+
contextWindowTokens: XAI_SUBSCRIPTION_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
3242
|
+
effectiveContextWindowTokens: XAI_SUBSCRIPTION_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
3243
|
+
autoCompactTokenLimit: XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
3244
|
+
toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
|
|
3245
|
+
};
|
|
3246
|
+
}),
|
|
3247
|
+
};
|
|
3248
|
+
return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
|
|
3249
|
+
}
|
|
3250
|
+
|
|
2877
3251
|
/**
|
|
2878
3252
|
* The provider identity a model id resolves to, for workspace model-policy
|
|
2879
3253
|
* evaluation — MUST agree with the real router (resolveTurnModel /
|
|
@@ -2892,6 +3266,9 @@ export function policyProviderIdForModel(settings: Settings, modelId: string): s
|
|
|
2892
3266
|
if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
2893
3267
|
return CODEX_PROVIDER_ID;
|
|
2894
3268
|
}
|
|
3269
|
+
if (canonicalModelId.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)) {
|
|
3270
|
+
return XAI_SUBSCRIPTION_PROVIDER_ID;
|
|
3271
|
+
}
|
|
2895
3272
|
if (canonicalModelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
2896
3273
|
return WORKSPACE_GATEWAY_PROVIDER_ID;
|
|
2897
3274
|
}
|
|
@@ -3012,6 +3389,7 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
3012
3389
|
);
|
|
3013
3390
|
const isRegistryNamespaced = (id: string): boolean =>
|
|
3014
3391
|
id.startsWith(CODEX_MODEL_ID_PREFIX) ||
|
|
3392
|
+
id.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX) ||
|
|
3015
3393
|
registryAliases.has(id) ||
|
|
3016
3394
|
(id.includes("/") && registryOwnedIds.has(id));
|
|
3017
3395
|
const builtinProvider = providerById.get(builtinId);
|
|
@@ -3028,6 +3406,7 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
3028
3406
|
...legacyModelCapabilities(settings, {
|
|
3029
3407
|
reasoningEffort: true,
|
|
3030
3408
|
hostedWebSearch: settings.webSearchEnabled,
|
|
3409
|
+
hostedImageGeneration: builtinHostedImageGenerationForModel(settings, id),
|
|
3031
3410
|
vision: id.startsWith("gpt-5.6-"),
|
|
3032
3411
|
}),
|
|
3033
3412
|
...(builtinPromptCachingForModel(id)
|
|
@@ -3177,6 +3556,12 @@ function settingsForTurnExecutionPolicy(settings: Settings, modelId: string): Se
|
|
|
3177
3556
|
if (settings.codexSubscriptionEnabled && modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
3178
3557
|
return withCodexCatalogProvider(settings);
|
|
3179
3558
|
}
|
|
3559
|
+
if (
|
|
3560
|
+
settings.supergrokSubscriptionEnabled &&
|
|
3561
|
+
modelId.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)
|
|
3562
|
+
) {
|
|
3563
|
+
return withXaiSubscriptionCatalogProvider(settings);
|
|
3564
|
+
}
|
|
3180
3565
|
if (modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
3181
3566
|
return withWorkspaceGatewayCatalogProvider(settings);
|
|
3182
3567
|
}
|
|
@@ -3424,6 +3809,15 @@ export function calculateModelUsageCostMicros(
|
|
|
3424
3809
|
usage: ModelUsageInput,
|
|
3425
3810
|
options?: { latencyMode?: LatencyMode },
|
|
3426
3811
|
): number {
|
|
3812
|
+
return calculateModelUsageCostBreakdown(settings, model, usage, options).creditCostMicros;
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
export function calculateModelUsageCostBreakdown(
|
|
3816
|
+
settings: Settings,
|
|
3817
|
+
model: string,
|
|
3818
|
+
usage: ModelUsageInput,
|
|
3819
|
+
options?: { latencyMode?: LatencyMode },
|
|
3820
|
+
): ModelUsageCostBreakdown {
|
|
3427
3821
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
3428
3822
|
if (!schedule) {
|
|
3429
3823
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -3440,10 +3834,12 @@ export function calculateModelUsageCostMicros(
|
|
|
3440
3834
|
(rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry),
|
|
3441
3835
|
);
|
|
3442
3836
|
}
|
|
3443
|
-
let
|
|
3837
|
+
let providerCostMicros = 0;
|
|
3838
|
+
let creditCostMicros = 0;
|
|
3444
3839
|
for (const [pricing, rawCost] of rawCostByPricing) {
|
|
3445
3840
|
const marginBps = pricing.marginBps ?? 0;
|
|
3446
|
-
|
|
3841
|
+
providerCostMicros += rawCost;
|
|
3842
|
+
creditCostMicros += Math.ceil((rawCost * (10_000 + marginBps)) / 10_000);
|
|
3447
3843
|
}
|
|
3448
3844
|
const latencyMode = options?.latencyMode ?? "standard";
|
|
3449
3845
|
if (latencyMode !== "standard") {
|
|
@@ -3456,10 +3852,11 @@ export function calculateModelUsageCostMicros(
|
|
|
3456
3852
|
(mode) => mode.id === latencyMode && mode.runnable,
|
|
3457
3853
|
)?.billingMultiplierBps;
|
|
3458
3854
|
if (multiplierBps && multiplierBps > 0) {
|
|
3459
|
-
|
|
3855
|
+
providerCostMicros = Math.ceil((providerCostMicros * multiplierBps) / 10_000);
|
|
3856
|
+
creditCostMicros = Math.ceil((creditCostMicros * multiplierBps) / 10_000);
|
|
3460
3857
|
}
|
|
3461
3858
|
}
|
|
3462
|
-
return
|
|
3859
|
+
return { providerCostMicros, creditCostMicros };
|
|
3463
3860
|
}
|
|
3464
3861
|
|
|
3465
3862
|
/**
|
|
@@ -3473,6 +3870,16 @@ export function calculateGatewayReportedCostMicros(
|
|
|
3473
3870
|
inferenceCostUsd: string,
|
|
3474
3871
|
options?: { inputTokens?: number },
|
|
3475
3872
|
): number {
|
|
3873
|
+
return calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options)
|
|
3874
|
+
.creditCostMicros;
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
export function calculateGatewayReportedCostBreakdown(
|
|
3878
|
+
settings: Settings,
|
|
3879
|
+
model: string,
|
|
3880
|
+
inferenceCostUsd: string,
|
|
3881
|
+
options?: { inputTokens?: number },
|
|
3882
|
+
): ModelUsageCostBreakdown {
|
|
3476
3883
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
3477
3884
|
if (!schedule) {
|
|
3478
3885
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -3485,14 +3892,52 @@ export function calculateGatewayReportedCostMicros(
|
|
|
3485
3892
|
const fraction = match[2] ?? "";
|
|
3486
3893
|
const decimalDigits = BigInt(`${match[1]}${fraction}`);
|
|
3487
3894
|
const decimalScale = 10n ** BigInt(fraction.length);
|
|
3895
|
+
const providerNumerator = decimalDigits * 1_000_000n;
|
|
3896
|
+
const providerMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
|
|
3488
3897
|
const marginBps = BigInt(10_000 + (pricing.marginBps ?? 0));
|
|
3489
|
-
const numerator =
|
|
3898
|
+
const numerator = providerNumerator * marginBps;
|
|
3490
3899
|
const denominator = decimalScale * 10_000n;
|
|
3491
|
-
const
|
|
3492
|
-
if (
|
|
3900
|
+
const creditMicros = (numerator + denominator - 1n) / denominator;
|
|
3901
|
+
if (
|
|
3902
|
+
providerMicros > BigInt(Number.MAX_SAFE_INTEGER) ||
|
|
3903
|
+
creditMicros > BigInt(Number.MAX_SAFE_INTEGER)
|
|
3904
|
+
) {
|
|
3493
3905
|
throw new Error("AI Gateway inference cost exceeds the supported billing range");
|
|
3494
3906
|
}
|
|
3495
|
-
return
|
|
3907
|
+
return {
|
|
3908
|
+
providerCostMicros: Number(providerMicros),
|
|
3909
|
+
creditCostMicros: Number(creditMicros),
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
/**
|
|
3914
|
+
* Exact OpenGeni product price frozen before a managed video request starts.
|
|
3915
|
+
* Gateway reporting is delayed for asynchronous video, so this deliberately
|
|
3916
|
+
* does not masquerade as provider-reported cost.
|
|
3917
|
+
*/
|
|
3918
|
+
export function calculateVideoGenerationCreditCostMicros(
|
|
3919
|
+
settings: Settings,
|
|
3920
|
+
input: {
|
|
3921
|
+
modelId: string;
|
|
3922
|
+
resolution: VideoGenerationResolution;
|
|
3923
|
+
durationSeconds: number;
|
|
3924
|
+
},
|
|
3925
|
+
): number {
|
|
3926
|
+
if (input.modelId !== SEEDANCE_2_5_MODEL_ID) {
|
|
3927
|
+
throw new Error(`Missing video generation credit pricing for ${input.modelId}`);
|
|
3928
|
+
}
|
|
3929
|
+
if (!Number.isSafeInteger(input.durationSeconds) || input.durationSeconds < 1) {
|
|
3930
|
+
throw new Error("Video generation duration is invalid for credit pricing");
|
|
3931
|
+
}
|
|
3932
|
+
const rate =
|
|
3933
|
+
input.resolution === "480p"
|
|
3934
|
+
? settings.videoGenerationCredit480pMicrosPerSecond
|
|
3935
|
+
: settings.videoGenerationCredit720pMicrosPerSecond;
|
|
3936
|
+
const cost = rate * input.durationSeconds;
|
|
3937
|
+
if (!Number.isSafeInteger(cost) || cost <= 0 || cost > 1_000_000_000) {
|
|
3938
|
+
throw new Error("Video generation credit price exceeds the supported range");
|
|
3939
|
+
}
|
|
3940
|
+
return cost;
|
|
3496
3941
|
}
|
|
3497
3942
|
|
|
3498
3943
|
export function configuredAllowedReasoningEfforts(
|
|
@@ -3706,16 +4151,13 @@ export function stableSandboxEnvironmentForRun(
|
|
|
3706
4151
|
environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
|
|
3707
4152
|
environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
|
|
3708
4153
|
}
|
|
3709
|
-
if (settings.
|
|
3710
|
-
environment.
|
|
4154
|
+
if (settings.sandboxBackend !== "selfhosted" && resolveFirstPartyDelegationSecret(settings)) {
|
|
4155
|
+
environment.OPENGENI_CODEMODE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/codemode-token`;
|
|
3711
4156
|
if (settings.ogtoolPackageSpec) {
|
|
3712
4157
|
environment.OPENGENI_OGTOOL_PACKAGE_SPEC ??= settings.ogtoolPackageSpec;
|
|
3713
4158
|
}
|
|
3714
4159
|
if (options.workspaceId) {
|
|
3715
|
-
environment.
|
|
3716
|
-
settings,
|
|
3717
|
-
options.workspaceId,
|
|
3718
|
-
);
|
|
4160
|
+
environment.OPENGENI_CODEMODE_URL ??= codemodeWorkspaceUrl(settings, options.workspaceId);
|
|
3719
4161
|
}
|
|
3720
4162
|
}
|
|
3721
4163
|
return environment;
|
|
@@ -4220,7 +4662,10 @@ function ensureBuiltInMcpServers(settings: Settings): Settings["mcpServers"] {
|
|
|
4220
4662
|
"search_documents",
|
|
4221
4663
|
"fetch_document_chunk",
|
|
4222
4664
|
"list_document_bases",
|
|
4665
|
+
"list_indexed_documents",
|
|
4223
4666
|
"knowledge_search",
|
|
4667
|
+
"knowledge_get",
|
|
4668
|
+
"knowledge_browse",
|
|
4224
4669
|
"knowledge_fetch",
|
|
4225
4670
|
"memory_search",
|
|
4226
4671
|
"memory_propose",
|
|
@@ -4272,6 +4717,34 @@ export function firstPartyMcpWorkspaceUrl(settings: Settings, workspaceId: strin
|
|
|
4272
4717
|
return url.toString();
|
|
4273
4718
|
}
|
|
4274
4719
|
|
|
4720
|
+
export function codemodeWorkspaceUrl(settings: Settings, workspaceId: string): string {
|
|
4721
|
+
if (settings.opengeniMcpUrl) {
|
|
4722
|
+
const url = new URL(firstPartyMcpWorkspaceUrl(settings, workspaceId));
|
|
4723
|
+
if (!url.pathname.endsWith("/mcp")) {
|
|
4724
|
+
throw new Error("First-party MCP URL cannot be projected to the Codemode endpoint");
|
|
4725
|
+
}
|
|
4726
|
+
url.pathname = `${url.pathname.slice(0, -4)}/codemode`;
|
|
4727
|
+
return url.toString();
|
|
4728
|
+
}
|
|
4729
|
+
|
|
4730
|
+
// Codemode executes inside the selected placement, not beside the worker.
|
|
4731
|
+
// Local Docker reaches the host through Docker's canonical host alias; an
|
|
4732
|
+
// in-process local sandbox uses loopback; remote managed providers use the
|
|
4733
|
+
// deployment's public origin. `OPENGENI_MCP_URL` above remains the explicit
|
|
4734
|
+
// escape hatch for mounted deployments and local remote-provider tunnels.
|
|
4735
|
+
const executionOrigin =
|
|
4736
|
+
settings.sandboxBackend === "docker"
|
|
4737
|
+
? `http://host.docker.internal:${settings.apiPort}`
|
|
4738
|
+
: settings.sandboxBackend === "local"
|
|
4739
|
+
? `http://127.0.0.1:${settings.apiPort}`
|
|
4740
|
+
: (settings.publicBaseUrl ?? `http://127.0.0.1:${settings.apiPort}`);
|
|
4741
|
+
const url = new URL(executionOrigin);
|
|
4742
|
+
url.pathname = `${url.pathname.replace(/\/+$/u, "")}/v1/workspaces/${workspaceId}/codemode`;
|
|
4743
|
+
url.search = "";
|
|
4744
|
+
url.hash = "";
|
|
4745
|
+
return url.toString();
|
|
4746
|
+
}
|
|
4747
|
+
|
|
4275
4748
|
function firstPartyMcpServerUrl(settings: Settings): string {
|
|
4276
4749
|
return firstPartyMcpBaseUrl(settings);
|
|
4277
4750
|
}
|
|
@@ -4286,8 +4759,16 @@ function firstPartyFilesMcpServerUrl(mcpUrl: string): string {
|
|
|
4286
4759
|
|
|
4287
4760
|
function validateSettings(settings: Settings): void {
|
|
4288
4761
|
temporalConnectionOptions(settings);
|
|
4289
|
-
|
|
4290
|
-
|
|
4762
|
+
const allowedFirstPartyMcpTools = new Set(
|
|
4763
|
+
settings.allowedFirstPartyMcpTools ?? FIRST_PARTY_MCP_TOOL_NAMES,
|
|
4764
|
+
);
|
|
4765
|
+
const disallowedDefaults = (settings.defaultFirstPartyMcpTools ?? []).filter(
|
|
4766
|
+
(tool) => !allowedFirstPartyMcpTools.has(tool),
|
|
4767
|
+
);
|
|
4768
|
+
if (disallowedDefaults.length > 0) {
|
|
4769
|
+
throw new Error(
|
|
4770
|
+
`OPENGENI_DEFAULT_FIRST_PARTY_MCP_TOOLS must be a subset of OPENGENI_ALLOWED_FIRST_PARTY_MCP_TOOLS: ${disallowedDefaults.join(", ")}`,
|
|
4771
|
+
);
|
|
4291
4772
|
}
|
|
4292
4773
|
if (settings.productAccessMode === "managed") {
|
|
4293
4774
|
if (!settings.publicBaseUrl) {
|
|
@@ -4342,11 +4823,6 @@ function validateSettings(settings: Settings): void {
|
|
|
4342
4823
|
);
|
|
4343
4824
|
}
|
|
4344
4825
|
if (settings.slackClientId) {
|
|
4345
|
-
if (!settings.slackSigningSecret) {
|
|
4346
|
-
throw new Error(
|
|
4347
|
-
"OPENGENI_SLACK_SIGNING_SECRET is required when the OpenGeni Slack app is configured",
|
|
4348
|
-
);
|
|
4349
|
-
}
|
|
4350
4826
|
if (!settings.publicBaseUrl) {
|
|
4351
4827
|
throw new Error(
|
|
4352
4828
|
"OPENGENI_PUBLIC_BASE_URL is required when the OpenGeni Slack app is configured",
|
|
@@ -4371,6 +4847,31 @@ function validateSettings(settings: Settings): void {
|
|
|
4371
4847
|
"OPENGENI_GOOGLE_DRIVE_CLIENT_ID and OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET must be configured together",
|
|
4372
4848
|
);
|
|
4373
4849
|
}
|
|
4850
|
+
if (Boolean(settings.fikenClientId) !== Boolean(settings.fikenClientSecret)) {
|
|
4851
|
+
throw new Error(
|
|
4852
|
+
"OPENGENI_FIKEN_OAUTH_CLIENT_ID and OPENGENI_FIKEN_OAUTH_CLIENT_SECRET must be configured together",
|
|
4853
|
+
);
|
|
4854
|
+
}
|
|
4855
|
+
if (settings.fikenClientId) {
|
|
4856
|
+
if (!settings.publicBaseUrl) {
|
|
4857
|
+
throw new Error(
|
|
4858
|
+
"OPENGENI_PUBLIC_BASE_URL is required when the Fiken OAuth integration is configured",
|
|
4859
|
+
);
|
|
4860
|
+
}
|
|
4861
|
+
if (
|
|
4862
|
+
!settings.publicBaseUrl.startsWith("https://") &&
|
|
4863
|
+
!["local", "test"].includes(settings.environment)
|
|
4864
|
+
) {
|
|
4865
|
+
throw new Error(
|
|
4866
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when the Fiken OAuth integration is configured outside local/test",
|
|
4867
|
+
);
|
|
4868
|
+
}
|
|
4869
|
+
if (!settings.integrationsStateSecret) {
|
|
4870
|
+
throw new Error(
|
|
4871
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when the Fiken OAuth integration is configured",
|
|
4872
|
+
);
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4374
4875
|
if (settings.googleDriveClientId) {
|
|
4375
4876
|
if (!settings.publicBaseUrl) {
|
|
4376
4877
|
throw new Error(
|
|
@@ -4391,6 +4892,31 @@ function validateSettings(settings: Settings): void {
|
|
|
4391
4892
|
);
|
|
4392
4893
|
}
|
|
4393
4894
|
}
|
|
4895
|
+
if (Boolean(settings.atlassianClientId) !== Boolean(settings.atlassianClientSecret)) {
|
|
4896
|
+
throw new Error(
|
|
4897
|
+
"OPENGENI_ATLASSIAN_CLIENT_ID and OPENGENI_ATLASSIAN_CLIENT_SECRET must be configured together",
|
|
4898
|
+
);
|
|
4899
|
+
}
|
|
4900
|
+
if (settings.atlassianClientId) {
|
|
4901
|
+
if (!settings.publicBaseUrl) {
|
|
4902
|
+
throw new Error(
|
|
4903
|
+
"OPENGENI_PUBLIC_BASE_URL is required when the Atlassian integration is configured",
|
|
4904
|
+
);
|
|
4905
|
+
}
|
|
4906
|
+
if (
|
|
4907
|
+
!settings.publicBaseUrl.startsWith("https://") &&
|
|
4908
|
+
!["local", "test"].includes(settings.environment)
|
|
4909
|
+
) {
|
|
4910
|
+
throw new Error(
|
|
4911
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when the Atlassian integration is configured outside local/test",
|
|
4912
|
+
);
|
|
4913
|
+
}
|
|
4914
|
+
if (!settings.integrationsStateSecret) {
|
|
4915
|
+
throw new Error(
|
|
4916
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when the Atlassian integration is configured",
|
|
4917
|
+
);
|
|
4918
|
+
}
|
|
4919
|
+
}
|
|
4394
4920
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
4395
4921
|
parseSocialOauthClientsJson(settings.socialOauthClientsJson);
|
|
4396
4922
|
if (
|
|
@@ -4614,6 +5140,7 @@ function validateSettings(settings: Settings): void {
|
|
|
4614
5140
|
{
|
|
4615
5141
|
const reaperPeriod = settings.sandboxLeaseReaperPeriodMs;
|
|
4616
5142
|
const viewerTtl = settings.sandboxViewerHolderTtlMs;
|
|
5143
|
+
const interactionTtl = settings.sandboxInteractionHolderTtlMs;
|
|
4617
5144
|
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
4618
5145
|
const providerLifetimeMs = settings.modalTimeoutSeconds * 1000;
|
|
4619
5146
|
const rotationLeadMs = settings.sandboxRotationLeadMs;
|
|
@@ -4632,6 +5159,13 @@ function validateSettings(settings: Settings): void {
|
|
|
4632
5159
|
`than the TTL it polices, or stale viewer holders outlive a full reaper period.`,
|
|
4633
5160
|
);
|
|
4634
5161
|
}
|
|
5162
|
+
if (!(reaperPeriod < interactionTtl)) {
|
|
5163
|
+
throw new Error(
|
|
5164
|
+
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than ` +
|
|
5165
|
+
`OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}): the reaper must run ` +
|
|
5166
|
+
`more often than the controller-heartbeat horizon.`,
|
|
5167
|
+
);
|
|
5168
|
+
}
|
|
4635
5169
|
if (!(idleTimeoutMs <= providerLifetimeMs)) {
|
|
4636
5170
|
throw new Error(
|
|
4637
5171
|
`OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS*1000 (${idleTimeoutMs}) must not exceed the hard provider ` +
|
|
@@ -4645,10 +5179,15 @@ function validateSettings(settings: Settings): void {
|
|
|
4645
5179
|
`OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`,
|
|
4646
5180
|
);
|
|
4647
5181
|
}
|
|
4648
|
-
|
|
5182
|
+
// This is provider-hard-deadline headroom, not a retry delay. Rotation is
|
|
5183
|
+
// admitted immediately before the same sweep's drain inventory, so only the
|
|
5184
|
+
// worst-case time until that sweep plus the complete durable capture window
|
|
5185
|
+
// is required. No second schedule period belongs in the availability path.
|
|
5186
|
+
const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
|
|
5187
|
+
if (!(rotationLeadMs > captureTimeoutMs + reaperPeriod)) {
|
|
4649
5188
|
throw new Error(
|
|
4650
|
-
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the
|
|
4651
|
-
`plus
|
|
5189
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the durable capture ` +
|
|
5190
|
+
`timeout plus one reaper period (${captureTimeoutMs + reaperPeriod}).`,
|
|
4652
5191
|
);
|
|
4653
5192
|
}
|
|
4654
5193
|
if (!(viewerTtl < idleTimeoutMs)) {
|
|
@@ -4658,6 +5197,13 @@ function validateSettings(settings: Settings): void {
|
|
|
4658
5197
|
`under it (the provider idle-timeout is the backstop).`,
|
|
4659
5198
|
);
|
|
4660
5199
|
}
|
|
5200
|
+
if (!(interactionTtl < idleTimeoutMs)) {
|
|
5201
|
+
throw new Error(
|
|
5202
|
+
`OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}) must be strictly less than ` +
|
|
5203
|
+
`the effective box idle timeout (${idleTimeoutMs}): a dead browser controller must be ` +
|
|
5204
|
+
`reapable before the provider reclaims its placement.`,
|
|
5205
|
+
);
|
|
5206
|
+
}
|
|
4661
5207
|
if (!(reaperPeriod + idleGraceMs < idleTimeoutMs)) {
|
|
4662
5208
|
throw new Error(
|
|
4663
5209
|
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS + OPENGENI_SANDBOX_IDLE_GRACE_MS ` +
|
|
@@ -4699,10 +5245,11 @@ function validateSettings(settings: Settings): void {
|
|
|
4699
5245
|
for (const provider of registryProviders) {
|
|
4700
5246
|
if (
|
|
4701
5247
|
provider.kind === "vercel-gateway-managed" ||
|
|
4702
|
-
provider.kind === "vercel-gateway-workspace"
|
|
5248
|
+
provider.kind === "vercel-gateway-workspace" ||
|
|
5249
|
+
provider.kind === "xai-subscription"
|
|
4703
5250
|
) {
|
|
4704
5251
|
throw new Error(
|
|
4705
|
-
`OPENGENI_MODEL_PROVIDERS_JSON provider kind ${provider.kind} is reserved for
|
|
5252
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider kind ${provider.kind} is reserved for a reviewed OpenGeni credential broker`,
|
|
4706
5253
|
);
|
|
4707
5254
|
}
|
|
4708
5255
|
if (provider.id === builtinId) {
|