@opengeni/config 0.11.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +70 -3
- package/dist/index.js +237 -69
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +351 -77
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ReasoningEffort,
|
|
11
11
|
SandboxBackend,
|
|
12
12
|
SessionMcpApprovalPolicy,
|
|
13
|
+
SEEDANCE_2_5_MODEL_ID,
|
|
13
14
|
StaticUsageLimits,
|
|
14
15
|
TurnExecutionPolicyV1,
|
|
15
16
|
UsageLimitsMode
|
|
@@ -28,6 +29,11 @@ import { createHash } from "crypto";
|
|
|
28
29
|
import { z } from "zod";
|
|
29
30
|
var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
30
31
|
var registryId = /^[A-Za-z0-9_-]+$/;
|
|
32
|
+
var SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS = 60 * 6e4;
|
|
33
|
+
var SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS = 1e4;
|
|
34
|
+
var SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS = SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS - SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS;
|
|
35
|
+
var SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS = 60 * 6e4;
|
|
36
|
+
var SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS = 1e4;
|
|
31
37
|
var EnvBoolean = z.preprocess((value) => {
|
|
32
38
|
if (typeof value !== "string") {
|
|
33
39
|
return value;
|
|
@@ -171,6 +177,12 @@ var SettingsSchema = z.object({
|
|
|
171
177
|
turnWorkerMaxConcurrentTurns: z.coerce.number().int().positive().max(2e3).default(16),
|
|
172
178
|
turnWorkerTargetCpuUsage: z.coerce.number().positive().max(1).default(0.8),
|
|
173
179
|
turnWorkerTargetMemoryUsage: z.coerce.number().positive().max(0.8).default(0.75),
|
|
180
|
+
// Admission and emergency recovery are deliberately separate control loops.
|
|
181
|
+
// The Temporal tuner stops polling at the lower target; only genuine danger
|
|
182
|
+
// may invoke the disruptive graceful-drain fallback.
|
|
183
|
+
turnWorkerEmergencyMemoryUsage: z.coerce.number().min(0.85).max(0.95).default(0.9),
|
|
184
|
+
turnWorkerMemoryGuardIntervalMs: z.coerce.number().int().min(1e3).max(6e4).default(5e3),
|
|
185
|
+
turnWorkerMemoryGuardSustainMs: z.coerce.number().int().min(5e3).max(3e5).default(3e4),
|
|
174
186
|
observabilityStructuredLogs: EnvBoolean.default(false),
|
|
175
187
|
observabilityMetricsEnabled: EnvBoolean.default(true),
|
|
176
188
|
observabilityOtlpEndpoint: z.string().url().optional(),
|
|
@@ -192,7 +204,10 @@ var SettingsSchema = z.object({
|
|
|
192
204
|
// Explicit operator-controlled promotion pointer for `/agent/latest/*`.
|
|
193
205
|
// Versioned agent releases are immutable; changing this setting promotes or
|
|
194
206
|
// rolls back the stable channel without moving or deleting a provider tag.
|
|
195
|
-
agentStableVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).default("0.1.
|
|
207
|
+
agentStableVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).default("0.1.14"),
|
|
208
|
+
// Optional independent beta-channel pointer. When unset, the beta update
|
|
209
|
+
// manifest route is unavailable rather than silently serving stable.
|
|
210
|
+
agentBetaVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).optional(),
|
|
196
211
|
productAccessMode: ProductAccessMode.default("local"),
|
|
197
212
|
billingMode: BillingMode.default("disabled"),
|
|
198
213
|
entitlementsMode: EntitlementsMode.default("none"),
|
|
@@ -210,8 +225,7 @@ var SettingsSchema = z.object({
|
|
|
210
225
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
211
226
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
212
227
|
streamControlEnabled: EnvBoolean.default(false),
|
|
213
|
-
|
|
214
|
-
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
228
|
+
codemodeMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
215
229
|
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
216
230
|
// that do not carry the stock-image ogtool binary. Exact stable versions only:
|
|
217
231
|
// the agent must never guess a tag or silently install `latest`.
|
|
@@ -221,11 +235,15 @@ var SettingsSchema = z.object({
|
|
|
221
235
|
integrationsStateSecret: z.string().optional(),
|
|
222
236
|
integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
|
|
223
237
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
238
|
+
gmailRestAdapterEnabled: EnvBoolean.default(false),
|
|
224
239
|
slackClientId: z.string().optional(),
|
|
225
240
|
slackClientSecret: z.string().optional(),
|
|
226
241
|
slackSigningSecret: z.string().optional(),
|
|
227
242
|
googleDriveClientId: z.string().optional(),
|
|
228
243
|
googleDriveClientSecret: z.string().optional(),
|
|
244
|
+
googleDriveWorkspaceEventsEnabled: EnvBoolean.optional(),
|
|
245
|
+
atlassianClientId: z.string().optional(),
|
|
246
|
+
atlassianClientSecret: z.string().optional(),
|
|
229
247
|
// Undefined is meaningful: the migration boundary persists the product
|
|
230
248
|
// default of 3 when no deployment override is supplied.
|
|
231
249
|
maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
|
|
@@ -296,6 +314,20 @@ var SettingsSchema = z.object({
|
|
|
296
314
|
// Gateway models below are added to the managed-credit catalog. Workspace
|
|
297
315
|
// Gateway keys use the encrypted connection broker and never this secret.
|
|
298
316
|
vercelAiGatewayApiKey: z.string().optional(),
|
|
317
|
+
/** Image adapter route; native hosted providers ignore this model. */
|
|
318
|
+
imageGenerationModel: z.string().trim().min(1).max(256).default("openai/gpt-image-2"),
|
|
319
|
+
/** Durable video generation uses the workspace-owned Gateway credential. */
|
|
320
|
+
videoGenerationPollIntervalMs: z.coerce.number().int().min(1e3).max(6e4).default(5e3),
|
|
321
|
+
videoGenerationRecoveryDeadlineMs: z.coerce.number().int().min(6e4).max(24 * 60 * 6e4).default(2 * 60 * 6e4),
|
|
322
|
+
videoGenerationReferenceUrlTtlSeconds: z.coerce.number().int().min(300).max(6 * 60 * 60).default(60 * 60),
|
|
323
|
+
videoGenerationMaxConcurrentPerWorkspace: z.coerce.number().int().min(1).max(16).default(2),
|
|
324
|
+
videoGenerationWorkspaceQuotaBytes: z.coerce.number().int().positive().max(Number.MAX_SAFE_INTEGER).default(20 * 1024 * 1024 * 1024),
|
|
325
|
+
videoGenerationTempDirectory: z.string().trim().min(1).max(1024).default("/tmp/opengeni-video"),
|
|
326
|
+
videoGenerationFfprobePath: z.string().trim().min(1).max(1024).default("ffprobe"),
|
|
327
|
+
// OpenGeni's customer price, not a claim about the provider's delayed cost report.
|
|
328
|
+
// The durable operation freezes the exact resulting price before provider submit.
|
|
329
|
+
videoGenerationCredit480pMicrosPerSecond: z.coerce.number().int().positive().max(1e7).default(155e3),
|
|
330
|
+
videoGenerationCredit720pMicrosPerSecond: z.coerce.number().int().positive().max(1e7).default(35e4),
|
|
299
331
|
// Native composer voice input (browser MediaRecorder → API transcription).
|
|
300
332
|
// Provider credentials stay server-side; ClientConfig only projects availability
|
|
301
333
|
// and hard ceilings. Selection happens once before audio is sent — never retry
|
|
@@ -364,6 +396,11 @@ var SettingsSchema = z.object({
|
|
|
364
396
|
// compatibility diagnosis.
|
|
365
397
|
// OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
366
398
|
codexToolSearchEnabled: EnvBoolean.default(true),
|
|
399
|
+
// Provider-neutral progressive disclosure for direct OpenAI/Azure native
|
|
400
|
+
// client search and ordinary-function generic dispatch. Kept separate from
|
|
401
|
+
// the Codex rollout so an emergency Codex opt-out cannot disable every model.
|
|
402
|
+
// OPENGENI_LAZY_TOOL_SEARCH_ENABLED
|
|
403
|
+
lazyToolSearchEnabled: EnvBoolean.default(true),
|
|
367
404
|
// credential allocator atomic, workspace-local credential allocation. Default OFF is a
|
|
368
405
|
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
369
406
|
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
@@ -421,6 +458,13 @@ var SettingsSchema = z.object({
|
|
|
421
458
|
disableOpenaiTracing: EnvBoolean.default(false),
|
|
422
459
|
sandboxBackend: SandboxBackend.default("docker"),
|
|
423
460
|
dockerImage: z.string().default("opengeni-sandbox:local"),
|
|
461
|
+
// Explicit deployment contract: the configured base sandbox image contains
|
|
462
|
+
// the verified, self-contained native artifact runtime at its fixed image
|
|
463
|
+
// paths. Disabled by default so arbitrary/custom provider images never make
|
|
464
|
+
// document/spreadsheet/presentation skills appear when their runtime is
|
|
465
|
+
// absent. Per-pack/per-rig image overrides fail closed in the worker even
|
|
466
|
+
// when this base-image contract is enabled.
|
|
467
|
+
sandboxArtifactRuntimeEnabled: EnvBoolean.default(false),
|
|
424
468
|
dockerExposedPorts: z.string().default(""),
|
|
425
469
|
dockerNetwork: z.string().optional(),
|
|
426
470
|
// When the worker itself runs in a container and talks to a host Docker daemon,
|
|
@@ -477,15 +521,14 @@ var SettingsSchema = z.object({
|
|
|
477
521
|
// SOONER than the hard lifetime; the boot invariant forbids a value that would
|
|
478
522
|
// reap before reaperPeriod + idleGrace elapses.
|
|
479
523
|
modalIdleTimeoutSeconds: z.coerce.number().int().positive().optional(),
|
|
480
|
-
// /workspace FILE PERSISTENCE across warm/cold cycles.
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
|
|
488
|
-
modalWorkspacePersistence: z.enum(["tar", "snapshot_filesystem", "snapshot_directory"]).default("snapshot_filesystem"),
|
|
524
|
+
// /workspace FILE PERSISTENCE across warm/cold cycles. Directory snapshots
|
|
525
|
+
// preserve only the durable user workspace, so provider recovery does not
|
|
526
|
+
// restore an entire machine image or replace the selected rig/base image.
|
|
527
|
+
// Existing serialized sessions retain their original persistence mode and
|
|
528
|
+
// remain recoverable; this default governs newly created Modal sandboxes.
|
|
529
|
+
// `snapshot_filesystem` remains available for explicit compatibility and
|
|
530
|
+
// immutable rig-image materialization. `tar` is the portable fallback.
|
|
531
|
+
modalWorkspacePersistence: z.enum(["tar", "snapshot_filesystem", "snapshot_directory"]).default("snapshot_directory"),
|
|
489
532
|
// Shared desktop toggle: this module reads it for the 6080 port-merge; the
|
|
490
533
|
// owner module (P4.x) acts on it to launch the display stack.
|
|
491
534
|
sandboxDesktopEnabled: EnvBoolean.default(false),
|
|
@@ -613,10 +656,13 @@ var SettingsSchema = z.object({
|
|
|
613
656
|
// the deploy-staging IaC secret/configmap pattern.
|
|
614
657
|
sandboxSelfhostedEnabled: EnvBoolean.default(false),
|
|
615
658
|
// Gates the op-stream (streaming exec) transport to Connected Machines. The
|
|
616
|
-
// runner must ALSO advertise Capabilities.op_stream
|
|
617
|
-
//
|
|
659
|
+
// runner must ALSO advertise Capabilities.op_stream. Streaming is the default
|
|
660
|
+
// because it is the only transport that can keep a command alive without an
|
|
661
|
+
// arbitrary request/reply wall while still supporting replay and cancellation.
|
|
662
|
+
// Older runners remain usable when an explicit positive exec timeout is set.
|
|
663
|
+
// EnvBoolean (NOT
|
|
618
664
|
// z.coerce.boolean(), which coerces "false" -> true).
|
|
619
|
-
agentOpStreamEnabled: EnvBoolean.default(
|
|
665
|
+
agentOpStreamEnabled: EnvBoolean.default(true),
|
|
620
666
|
// The HMAC secret the control plane signs the enrollment bearer credential with
|
|
621
667
|
// (the `oge_` envelope the agent presents back to the control plane). Optional:
|
|
622
668
|
// when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
|
|
@@ -674,23 +720,15 @@ var SettingsSchema = z.object({
|
|
|
674
720
|
selfhostedNatsControlUser: z.string().optional(),
|
|
675
721
|
selfhostedNatsControlPassword: z.string().optional(),
|
|
676
722
|
// --- selfhosted (Connected Machine) control/exec op deadlines ---------------
|
|
677
|
-
//
|
|
678
|
-
//
|
|
679
|
-
//
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
// The exec default is a DELIBERATELY MODEST 2min (not 5): the agent-side admission
|
|
686
|
-
// pool is (until a later agent release) a FLAT 8 permits with no per-class split,
|
|
687
|
-
// so 8 slow execs holding a permit for 5 minutes would blanket-DRAIN every fs/git
|
|
688
|
-
// op — shipping the amplifier before the class-aware-admission fix. 2min still
|
|
689
|
-
// clears the large majority of the observed >30s exec tail; genuinely long jobs run
|
|
690
|
-
// in the background (see the exec-deadline hint) or raise the knob per deployment.
|
|
691
|
-
// Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 2min) and
|
|
723
|
+
// CONTROL ops (ping / fs / git / desktop / pty) keep a short request timeout so
|
|
724
|
+
// liveness failures surface promptly. EXEC duration is a different concern: by
|
|
725
|
+
// default it is unbounded (0), exactly like a command launched by an unrestricted
|
|
726
|
+
// local agent. The op-stream transport keeps control liveness, replay, and explicit
|
|
727
|
+
// cancellation independent of command duration. A deployment may opt into a hard
|
|
728
|
+
// process deadline by setting a positive value; 0 never schedules a process kill.
|
|
729
|
+
// Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 0 = none) and
|
|
692
730
|
// OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS (default 30s).
|
|
693
|
-
sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().
|
|
731
|
+
sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().nonnegative().default(0),
|
|
694
732
|
sandboxSelfhostedControlTimeoutMs: z.coerce.number().int().positive().default(3e4),
|
|
695
733
|
// --- sandbox lease cadences (cadence invariant validated at boot below) ---
|
|
696
734
|
// reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
|
|
@@ -701,6 +739,10 @@ var SettingsSchema = z.object({
|
|
|
701
739
|
// snapshotted before the box dies (sandbox-file-persistence).
|
|
702
740
|
sandboxLeaseReaperPeriodMs: z.coerce.number().int().positive().default(3e4),
|
|
703
741
|
sandboxViewerHolderTtlMs: z.coerce.number().int().positive().default(9e4),
|
|
742
|
+
// A BrowserSession controller refreshes its durable resource and exact
|
|
743
|
+
// interaction lease holder together. This longer crash horizon tolerates API
|
|
744
|
+
// replacement while still releasing a placement whose controller died.
|
|
745
|
+
sandboxInteractionHolderTtlMs: z.coerce.number().int().positive().default(18e4),
|
|
704
746
|
// The DRAIN grace: how long a refcount-0 (draining) lease stays WARM before the
|
|
705
747
|
// reaper resume-by-ids the box and terminates it. This is the cost-vs-snappiness
|
|
706
748
|
// dial — when the user navigates away the box keeps refcount 0, but it survives
|
|
@@ -726,7 +768,7 @@ var SettingsSchema = z.object({
|
|
|
726
768
|
// graceful shutdown, or become permission to GC an older archive. Timeout is
|
|
727
769
|
// treated exactly like a failed best-effort snapshot. Knob:
|
|
728
770
|
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
729
|
-
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
|
|
771
|
+
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().max(SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS).default(6e4),
|
|
730
772
|
// Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
|
|
731
773
|
// ahead of a finite provider deadline. Modal's 24h creation clock cannot be
|
|
732
774
|
// extended; the logical sandbox outlives it by moving to one successor box.
|
|
@@ -736,13 +778,12 @@ var SettingsSchema = z.object({
|
|
|
736
778
|
// an operator deliberately wants more rotation headroom; the boot invariant
|
|
737
779
|
// still requires it to remain below the provider lifetime.
|
|
738
780
|
sandboxRotationLeadMs: z.coerce.number().int().positive().default(36e5),
|
|
739
|
-
// Bound
|
|
740
|
-
//
|
|
741
|
-
//
|
|
742
|
-
//
|
|
743
|
-
//
|
|
744
|
-
|
|
745
|
-
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(1),
|
|
781
|
+
// Bound provider-deadline rotation admission independently of execution.
|
|
782
|
+
// Every admitted box receives its own durable drain child; the control worker
|
|
783
|
+
// limits provider I/O to 32 concurrent activities. Matching that bound avoids
|
|
784
|
+
// both the old one-box-per-tick deadline backlog and an unbounded provider/API
|
|
785
|
+
// burst. Operators may tune this for a differently-sized worker pool.
|
|
786
|
+
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(32),
|
|
746
787
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
747
788
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
748
789
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -971,7 +1012,11 @@ var ModelCapabilitiesV1Schema = z.object({
|
|
|
971
1012
|
hostedTools: z.object({
|
|
972
1013
|
webSearch: CapabilityStateV1Schema,
|
|
973
1014
|
xSearch: CapabilityStateV1Schema,
|
|
974
|
-
codeExecution: CapabilityStateV1Schema
|
|
1015
|
+
codeExecution: CapabilityStateV1Schema,
|
|
1016
|
+
imageGeneration: CapabilityStateV1Schema.default({
|
|
1017
|
+
upstream: "unknown",
|
|
1018
|
+
runnable: false
|
|
1019
|
+
})
|
|
975
1020
|
}),
|
|
976
1021
|
inputModalities: z.array(ModelModalityV1).min(1),
|
|
977
1022
|
/** Exact MIME types accepted as typed `input_file`; `text/*` is allowed. */
|
|
@@ -1340,6 +1385,9 @@ function getSettings() {
|
|
|
1340
1385
|
turnWorkerMaxConcurrentTurns: optional("OPENGENI_TURN_WORKER_MAX_CONCURRENT_TURNS"),
|
|
1341
1386
|
turnWorkerTargetCpuUsage: optional("OPENGENI_TURN_WORKER_TARGET_CPU_USAGE"),
|
|
1342
1387
|
turnWorkerTargetMemoryUsage: optional("OPENGENI_TURN_WORKER_TARGET_MEMORY_USAGE"),
|
|
1388
|
+
turnWorkerEmergencyMemoryUsage: optional("OPENGENI_TURN_WORKER_EMERGENCY_MEMORY_USAGE"),
|
|
1389
|
+
turnWorkerMemoryGuardIntervalMs: optional("OPENGENI_TURN_WORKER_MEMORY_GUARD_INTERVAL_MS"),
|
|
1390
|
+
turnWorkerMemoryGuardSustainMs: optional("OPENGENI_TURN_WORKER_MEMORY_GUARD_SUSTAIN_MS"),
|
|
1343
1391
|
observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
|
|
1344
1392
|
observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
|
|
1345
1393
|
observabilityOtlpEndpoint: optional("OPENGENI_OTEL_EXPORTER_OTLP_ENDPOINT") ?? optional("OTEL_EXPORTER_OTLP_ENDPOINT"),
|
|
@@ -1354,6 +1402,7 @@ function getSettings() {
|
|
|
1354
1402
|
webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
|
|
1355
1403
|
agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
|
|
1356
1404
|
agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
|
|
1405
|
+
agentBetaVersion: optional("OPENGENI_AGENT_BETA_VERSION"),
|
|
1357
1406
|
productAccessMode: optional("OPENGENI_PRODUCT_ACCESS_MODE"),
|
|
1358
1407
|
billingMode: optional("OPENGENI_BILLING_MODE"),
|
|
1359
1408
|
entitlementsMode: optional("OPENGENI_ENTITLEMENTS_MODE"),
|
|
@@ -1363,8 +1412,7 @@ function getSettings() {
|
|
|
1363
1412
|
delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
|
|
1364
1413
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
1365
1414
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1366
|
-
|
|
1367
|
-
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
1415
|
+
codemodeMaxCallsPerTurn: optional("OPENGENI_CODEMODE_MAX_CALLS_PER_TURN"),
|
|
1368
1416
|
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
1369
1417
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
1370
1418
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
@@ -1373,11 +1421,15 @@ function getSettings() {
|
|
|
1373
1421
|
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
|
|
1374
1422
|
),
|
|
1375
1423
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1424
|
+
gmailRestAdapterEnabled: optional("OPENGENI_GMAIL_REST_ADAPTER_ENABLED"),
|
|
1376
1425
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1377
1426
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1378
1427
|
slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
|
|
1379
1428
|
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1380
1429
|
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1430
|
+
googleDriveWorkspaceEventsEnabled: optional("OPENGENI_GOOGLE_DRIVE_WORKSPACE_EVENTS_ENABLED"),
|
|
1431
|
+
atlassianClientId: optional("OPENGENI_ATLASSIAN_CLIENT_ID"),
|
|
1432
|
+
atlassianClientSecret: optional("OPENGENI_ATLASSIAN_CLIENT_SECRET"),
|
|
1381
1433
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1382
1434
|
socialOauthClientsJson: optional("OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON"),
|
|
1383
1435
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
@@ -1404,6 +1456,24 @@ function getSettings() {
|
|
|
1404
1456
|
openaiModel: optional("OPENGENI_OPENAI_MODEL"),
|
|
1405
1457
|
openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
|
|
1406
1458
|
vercelAiGatewayApiKey: optional("OPENGENI_VERCEL_AI_GATEWAY_API_KEY"),
|
|
1459
|
+
imageGenerationModel: optional("OPENGENI_IMAGE_GENERATION_MODEL"),
|
|
1460
|
+
videoGenerationPollIntervalMs: optional("OPENGENI_VIDEO_GENERATION_POLL_INTERVAL_MS"),
|
|
1461
|
+
videoGenerationRecoveryDeadlineMs: optional("OPENGENI_VIDEO_GENERATION_RECOVERY_DEADLINE_MS"),
|
|
1462
|
+
videoGenerationReferenceUrlTtlSeconds: optional(
|
|
1463
|
+
"OPENGENI_VIDEO_GENERATION_REFERENCE_URL_TTL_SECONDS"
|
|
1464
|
+
),
|
|
1465
|
+
videoGenerationMaxConcurrentPerWorkspace: optional(
|
|
1466
|
+
"OPENGENI_VIDEO_GENERATION_MAX_CONCURRENT_PER_WORKSPACE"
|
|
1467
|
+
),
|
|
1468
|
+
videoGenerationWorkspaceQuotaBytes: optional("OPENGENI_VIDEO_GENERATION_WORKSPACE_QUOTA_BYTES"),
|
|
1469
|
+
videoGenerationTempDirectory: optional("OPENGENI_VIDEO_GENERATION_TEMP_DIRECTORY"),
|
|
1470
|
+
videoGenerationFfprobePath: optional("OPENGENI_VIDEO_GENERATION_FFPROBE_PATH"),
|
|
1471
|
+
videoGenerationCredit480pMicrosPerSecond: optional(
|
|
1472
|
+
"OPENGENI_VIDEO_GENERATION_CREDIT_480P_MICROS_PER_SECOND"
|
|
1473
|
+
),
|
|
1474
|
+
videoGenerationCredit720pMicrosPerSecond: optional(
|
|
1475
|
+
"OPENGENI_VIDEO_GENERATION_CREDIT_720P_MICROS_PER_SECOND"
|
|
1476
|
+
),
|
|
1407
1477
|
voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
|
|
1408
1478
|
voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
|
|
1409
1479
|
voiceInputResumableEnabled: optional("OPENGENI_VOICE_INPUT_RESUMABLE_ENABLED"),
|
|
@@ -1435,6 +1505,7 @@ function getSettings() {
|
|
|
1435
1505
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
1436
1506
|
codexConnectedAppsEnabled: optional("OPENGENI_CODEX_CONNECTED_APPS_ENABLED"),
|
|
1437
1507
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
1508
|
+
lazyToolSearchEnabled: optional("OPENGENI_LAZY_TOOL_SEARCH_ENABLED"),
|
|
1438
1509
|
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
1439
1510
|
codexFleetPolicyShadowEnabled: optional("OPENGENI_CODEX_FLEET_POLICY_SHADOW_ENABLED"),
|
|
1440
1511
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
@@ -1455,6 +1526,7 @@ function getSettings() {
|
|
|
1455
1526
|
disableOpenaiTracing: optional("OPENGENI_DISABLE_OPENAI_TRACING"),
|
|
1456
1527
|
sandboxBackend: optional("OPENGENI_SANDBOX_BACKEND"),
|
|
1457
1528
|
dockerImage: optional("OPENGENI_DOCKER_IMAGE"),
|
|
1529
|
+
sandboxArtifactRuntimeEnabled: optional("OPENGENI_SANDBOX_ARTIFACT_RUNTIME_ENABLED"),
|
|
1458
1530
|
dockerExposedPorts: optional("OPENGENI_DOCKER_EXPOSED_PORTS"),
|
|
1459
1531
|
dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
|
|
1460
1532
|
dockerWorkspaceBaseDir: optional("OPENGENI_DOCKER_WORKSPACE_BASE_DIR"),
|
|
@@ -1537,6 +1609,7 @@ function getSettings() {
|
|
|
1537
1609
|
sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
|
|
1538
1610
|
sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
|
|
1539
1611
|
sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
|
|
1612
|
+
sandboxInteractionHolderTtlMs: optional("OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS"),
|
|
1540
1613
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
1541
1614
|
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
1542
1615
|
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
@@ -1627,8 +1700,15 @@ function effectiveModalIdleTimeoutSeconds(settings) {
|
|
|
1627
1700
|
}
|
|
1628
1701
|
function sandboxArchiveCaptureTimeoutMs(settings) {
|
|
1629
1702
|
return Math.min(
|
|
1630
|
-
|
|
1631
|
-
|
|
1703
|
+
SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
|
|
1704
|
+
settings.sandboxSnapshotTimeoutMs + SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
function sandboxLifecycleTransitionWaitMs(settings) {
|
|
1708
|
+
const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
|
|
1709
|
+
return Math.min(
|
|
1710
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
|
|
1711
|
+
settings.sandboxLeaseReaperPeriodMs + captureTimeoutMs + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS
|
|
1632
1712
|
);
|
|
1633
1713
|
}
|
|
1634
1714
|
function collectSandboxEnvironment(settings, source = process.env) {
|
|
@@ -1856,7 +1936,11 @@ function legacyModelCapabilities(settings, input) {
|
|
|
1856
1936
|
runnable: input.hostedWebSearch
|
|
1857
1937
|
},
|
|
1858
1938
|
xSearch: { upstream: "unknown", runnable: false },
|
|
1859
|
-
codeExecution: { upstream: "unknown", runnable: false }
|
|
1939
|
+
codeExecution: { upstream: "unknown", runnable: false },
|
|
1940
|
+
imageGeneration: {
|
|
1941
|
+
upstream: input.hostedImageGeneration ? "supported" : "unknown",
|
|
1942
|
+
runnable: input.hostedImageGeneration ?? false
|
|
1943
|
+
}
|
|
1860
1944
|
},
|
|
1861
1945
|
inputModalities: input.vision ? ["text", "image"] : ["text"],
|
|
1862
1946
|
inputFileMediaTypes: [
|
|
@@ -2029,6 +2113,18 @@ function builtinPromptCachingForModel(modelId) {
|
|
|
2029
2113
|
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
|
|
2030
2114
|
return slug.startsWith("gpt-5.6-") ? { upstream: "supported", runnable: true, mode: "implicit" } : void 0;
|
|
2031
2115
|
}
|
|
2116
|
+
function builtinHostedImageGenerationForModel(settings, modelId) {
|
|
2117
|
+
return settings.openaiProvider === "openai" && isDirectOpenAiApiBaseUrl(settings.openaiBaseUrl) && ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"].includes(modelId);
|
|
2118
|
+
}
|
|
2119
|
+
function isDirectOpenAiApiBaseUrl(baseUrl) {
|
|
2120
|
+
if (baseUrl === void 0) return true;
|
|
2121
|
+
try {
|
|
2122
|
+
const parsed = new URL(baseUrl);
|
|
2123
|
+
return parsed.protocol === "https:" && parsed.hostname === "api.openai.com" && parsed.port === "" && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && parsed.pathname.replace(/\/+$/, "") === "/v1";
|
|
2124
|
+
} catch {
|
|
2125
|
+
return false;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2032
2128
|
function serviceTierForLatencyMode(providerId, latencyMode) {
|
|
2033
2129
|
if (latencyMode === "standard") {
|
|
2034
2130
|
return void 0;
|
|
@@ -2320,6 +2416,7 @@ function configuredModels(settings) {
|
|
|
2320
2416
|
...legacyModelCapabilities(settings, {
|
|
2321
2417
|
reasoningEffort: true,
|
|
2322
2418
|
hostedWebSearch: settings.webSearchEnabled,
|
|
2419
|
+
hostedImageGeneration: builtinHostedImageGenerationForModel(settings, id),
|
|
2323
2420
|
vision: id.startsWith("gpt-5.6-")
|
|
2324
2421
|
}),
|
|
2325
2422
|
...builtinPromptCachingForModel(id) ? { promptCaching: builtinPromptCachingForModel(id) } : {},
|
|
@@ -2563,6 +2660,9 @@ function configuredEntitlements(settings) {
|
|
|
2563
2660
|
};
|
|
2564
2661
|
}
|
|
2565
2662
|
function calculateModelUsageCostMicros(settings, model, usage, options) {
|
|
2663
|
+
return calculateModelUsageCostBreakdown(settings, model, usage, options).creditCostMicros;
|
|
2664
|
+
}
|
|
2665
|
+
function calculateModelUsageCostBreakdown(settings, model, usage, options) {
|
|
2566
2666
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
2567
2667
|
if (!schedule) {
|
|
2568
2668
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -2576,10 +2676,12 @@ function calculateModelUsageCostMicros(settings, model, usage, options) {
|
|
|
2576
2676
|
(rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry)
|
|
2577
2677
|
);
|
|
2578
2678
|
}
|
|
2579
|
-
let
|
|
2679
|
+
let providerCostMicros = 0;
|
|
2680
|
+
let creditCostMicros = 0;
|
|
2580
2681
|
for (const [pricing, rawCost] of rawCostByPricing) {
|
|
2581
2682
|
const marginBps = pricing.marginBps ?? 0;
|
|
2582
|
-
|
|
2683
|
+
providerCostMicros += rawCost;
|
|
2684
|
+
creditCostMicros += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
2583
2685
|
}
|
|
2584
2686
|
const latencyMode = options?.latencyMode ?? "standard";
|
|
2585
2687
|
if (latencyMode !== "standard") {
|
|
@@ -2592,12 +2694,16 @@ function calculateModelUsageCostMicros(settings, model, usage, options) {
|
|
|
2592
2694
|
(mode) => mode.id === latencyMode && mode.runnable
|
|
2593
2695
|
)?.billingMultiplierBps;
|
|
2594
2696
|
if (multiplierBps && multiplierBps > 0) {
|
|
2595
|
-
|
|
2697
|
+
providerCostMicros = Math.ceil(providerCostMicros * multiplierBps / 1e4);
|
|
2698
|
+
creditCostMicros = Math.ceil(creditCostMicros * multiplierBps / 1e4);
|
|
2596
2699
|
}
|
|
2597
2700
|
}
|
|
2598
|
-
return
|
|
2701
|
+
return { providerCostMicros, creditCostMicros };
|
|
2599
2702
|
}
|
|
2600
2703
|
function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, options) {
|
|
2704
|
+
return calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options).creditCostMicros;
|
|
2705
|
+
}
|
|
2706
|
+
function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
|
|
2601
2707
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
2602
2708
|
if (!schedule) {
|
|
2603
2709
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -2610,14 +2716,33 @@ function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, o
|
|
|
2610
2716
|
const fraction = match[2] ?? "";
|
|
2611
2717
|
const decimalDigits = BigInt(`${match[1]}${fraction}`);
|
|
2612
2718
|
const decimalScale = 10n ** BigInt(fraction.length);
|
|
2719
|
+
const providerNumerator = decimalDigits * 1000000n;
|
|
2720
|
+
const providerMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
|
|
2613
2721
|
const marginBps = BigInt(1e4 + (pricing.marginBps ?? 0));
|
|
2614
|
-
const numerator =
|
|
2722
|
+
const numerator = providerNumerator * marginBps;
|
|
2615
2723
|
const denominator = decimalScale * 10000n;
|
|
2616
|
-
const
|
|
2617
|
-
if (
|
|
2724
|
+
const creditMicros = (numerator + denominator - 1n) / denominator;
|
|
2725
|
+
if (providerMicros > BigInt(Number.MAX_SAFE_INTEGER) || creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
2618
2726
|
throw new Error("AI Gateway inference cost exceeds the supported billing range");
|
|
2619
2727
|
}
|
|
2620
|
-
return
|
|
2728
|
+
return {
|
|
2729
|
+
providerCostMicros: Number(providerMicros),
|
|
2730
|
+
creditCostMicros: Number(creditMicros)
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
function calculateVideoGenerationCreditCostMicros(settings, input) {
|
|
2734
|
+
if (input.modelId !== SEEDANCE_2_5_MODEL_ID) {
|
|
2735
|
+
throw new Error(`Missing video generation credit pricing for ${input.modelId}`);
|
|
2736
|
+
}
|
|
2737
|
+
if (!Number.isSafeInteger(input.durationSeconds) || input.durationSeconds < 1) {
|
|
2738
|
+
throw new Error("Video generation duration is invalid for credit pricing");
|
|
2739
|
+
}
|
|
2740
|
+
const rate = input.resolution === "480p" ? settings.videoGenerationCredit480pMicrosPerSecond : settings.videoGenerationCredit720pMicrosPerSecond;
|
|
2741
|
+
const cost = rate * input.durationSeconds;
|
|
2742
|
+
if (!Number.isSafeInteger(cost) || cost <= 0 || cost > 1e9) {
|
|
2743
|
+
throw new Error("Video generation credit price exceeds the supported range");
|
|
2744
|
+
}
|
|
2745
|
+
return cost;
|
|
2621
2746
|
}
|
|
2622
2747
|
function configuredAllowedReasoningEfforts(settings) {
|
|
2623
2748
|
return uniqueValues([
|
|
@@ -2735,16 +2860,13 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
|
|
|
2735
2860
|
environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
|
|
2736
2861
|
environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
|
|
2737
2862
|
}
|
|
2738
|
-
if (settings.
|
|
2739
|
-
environment.
|
|
2863
|
+
if (settings.sandboxBackend !== "selfhosted" && resolveFirstPartyDelegationSecret(settings)) {
|
|
2864
|
+
environment.OPENGENI_CODEMODE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/codemode-token`;
|
|
2740
2865
|
if (settings.ogtoolPackageSpec) {
|
|
2741
2866
|
environment.OPENGENI_OGTOOL_PACKAGE_SPEC ??= settings.ogtoolPackageSpec;
|
|
2742
2867
|
}
|
|
2743
2868
|
if (options.workspaceId) {
|
|
2744
|
-
environment.
|
|
2745
|
-
settings,
|
|
2746
|
-
options.workspaceId
|
|
2747
|
-
);
|
|
2869
|
+
environment.OPENGENI_CODEMODE_URL ??= codemodeWorkspaceUrl(settings, options.workspaceId);
|
|
2748
2870
|
}
|
|
2749
2871
|
}
|
|
2750
2872
|
return environment;
|
|
@@ -3118,6 +3240,7 @@ function ensureBuiltInMcpServers(settings) {
|
|
|
3118
3240
|
"search_documents",
|
|
3119
3241
|
"fetch_document_chunk",
|
|
3120
3242
|
"list_document_bases",
|
|
3243
|
+
"list_indexed_documents",
|
|
3121
3244
|
"knowledge_search",
|
|
3122
3245
|
"knowledge_fetch",
|
|
3123
3246
|
"memory_search",
|
|
@@ -3143,6 +3266,14 @@ function firstPartyMcpWorkspaceUrl(settings, workspaceId) {
|
|
|
3143
3266
|
url.hash = "";
|
|
3144
3267
|
return url.toString();
|
|
3145
3268
|
}
|
|
3269
|
+
function codemodeWorkspaceUrl(settings, workspaceId) {
|
|
3270
|
+
const url = new URL(firstPartyMcpWorkspaceUrl(settings, workspaceId));
|
|
3271
|
+
if (!url.pathname.endsWith("/mcp")) {
|
|
3272
|
+
throw new Error("First-party MCP URL cannot be projected to the Codemode endpoint");
|
|
3273
|
+
}
|
|
3274
|
+
url.pathname = `${url.pathname.slice(0, -4)}/codemode`;
|
|
3275
|
+
return url.toString();
|
|
3276
|
+
}
|
|
3146
3277
|
function firstPartyMcpServerUrl(settings) {
|
|
3147
3278
|
return firstPartyMcpBaseUrl(settings);
|
|
3148
3279
|
}
|
|
@@ -3154,9 +3285,6 @@ function firstPartyFilesMcpServerUrl(mcpUrl) {
|
|
|
3154
3285
|
}
|
|
3155
3286
|
function validateSettings(settings) {
|
|
3156
3287
|
temporalConnectionOptions(settings);
|
|
3157
|
-
if (settings.toolspaceEnabled && !settings.delegationSecret) {
|
|
3158
|
-
throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_TOOLSPACE_ENABLED=true");
|
|
3159
|
-
}
|
|
3160
3288
|
if (settings.productAccessMode === "managed") {
|
|
3161
3289
|
if (!settings.publicBaseUrl) {
|
|
3162
3290
|
throw new Error(
|
|
@@ -3206,11 +3334,6 @@ function validateSettings(settings) {
|
|
|
3206
3334
|
);
|
|
3207
3335
|
}
|
|
3208
3336
|
if (settings.slackClientId) {
|
|
3209
|
-
if (!settings.slackSigningSecret) {
|
|
3210
|
-
throw new Error(
|
|
3211
|
-
"OPENGENI_SLACK_SIGNING_SECRET is required when the OpenGeni Slack app is configured"
|
|
3212
|
-
);
|
|
3213
|
-
}
|
|
3214
3337
|
if (!settings.publicBaseUrl) {
|
|
3215
3338
|
throw new Error(
|
|
3216
3339
|
"OPENGENI_PUBLIC_BASE_URL is required when the OpenGeni Slack app is configured"
|
|
@@ -3249,6 +3372,28 @@ function validateSettings(settings) {
|
|
|
3249
3372
|
);
|
|
3250
3373
|
}
|
|
3251
3374
|
}
|
|
3375
|
+
if (Boolean(settings.atlassianClientId) !== Boolean(settings.atlassianClientSecret)) {
|
|
3376
|
+
throw new Error(
|
|
3377
|
+
"OPENGENI_ATLASSIAN_CLIENT_ID and OPENGENI_ATLASSIAN_CLIENT_SECRET must be configured together"
|
|
3378
|
+
);
|
|
3379
|
+
}
|
|
3380
|
+
if (settings.atlassianClientId) {
|
|
3381
|
+
if (!settings.publicBaseUrl) {
|
|
3382
|
+
throw new Error(
|
|
3383
|
+
"OPENGENI_PUBLIC_BASE_URL is required when the Atlassian integration is configured"
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
3386
|
+
if (!settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
3387
|
+
throw new Error(
|
|
3388
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when the Atlassian integration is configured outside local/test"
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3391
|
+
if (!settings.integrationsStateSecret) {
|
|
3392
|
+
throw new Error(
|
|
3393
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when the Atlassian integration is configured"
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3252
3397
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
3253
3398
|
parseSocialOauthClientsJson(settings.socialOauthClientsJson);
|
|
3254
3399
|
if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
|
|
@@ -3400,6 +3545,7 @@ function validateSettings(settings) {
|
|
|
3400
3545
|
{
|
|
3401
3546
|
const reaperPeriod = settings.sandboxLeaseReaperPeriodMs;
|
|
3402
3547
|
const viewerTtl = settings.sandboxViewerHolderTtlMs;
|
|
3548
|
+
const interactionTtl = settings.sandboxInteractionHolderTtlMs;
|
|
3403
3549
|
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
3404
3550
|
const providerLifetimeMs = settings.modalTimeoutSeconds * 1e3;
|
|
3405
3551
|
const rotationLeadMs = settings.sandboxRotationLeadMs;
|
|
@@ -3409,6 +3555,11 @@ function validateSettings(settings) {
|
|
|
3409
3555
|
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}): the reaper must run more often than the TTL it polices, or stale viewer holders outlive a full reaper period.`
|
|
3410
3556
|
);
|
|
3411
3557
|
}
|
|
3558
|
+
if (!(reaperPeriod < interactionTtl)) {
|
|
3559
|
+
throw new Error(
|
|
3560
|
+
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}): the reaper must run more often than the controller-heartbeat horizon.`
|
|
3561
|
+
);
|
|
3562
|
+
}
|
|
3412
3563
|
if (!(idleTimeoutMs <= providerLifetimeMs)) {
|
|
3413
3564
|
throw new Error(
|
|
3414
3565
|
`OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS*1000 (${idleTimeoutMs}) must not exceed the hard provider lifetime (OPENGENI_MODAL_TIMEOUT_SECONDS*1000 = ${providerLifetimeMs}): the idle timeout is a floor under the hard lifetime, not above it.`
|
|
@@ -3419,9 +3570,10 @@ function validateSettings(settings) {
|
|
|
3419
3570
|
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
|
|
3420
3571
|
);
|
|
3421
3572
|
}
|
|
3422
|
-
|
|
3573
|
+
const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
|
|
3574
|
+
if (!(rotationLeadMs > captureTimeoutMs + reaperPeriod)) {
|
|
3423
3575
|
throw new Error(
|
|
3424
|
-
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the
|
|
3576
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the durable capture timeout plus one reaper period (${captureTimeoutMs + reaperPeriod}).`
|
|
3425
3577
|
);
|
|
3426
3578
|
}
|
|
3427
3579
|
if (!(viewerTtl < idleTimeoutMs)) {
|
|
@@ -3429,6 +3581,11 @@ function validateSettings(settings) {
|
|
|
3429
3581
|
`OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a viewer holder must be reapable before the box idles out from under it (the provider idle-timeout is the backstop).`
|
|
3430
3582
|
);
|
|
3431
3583
|
}
|
|
3584
|
+
if (!(interactionTtl < idleTimeoutMs)) {
|
|
3585
|
+
throw new Error(
|
|
3586
|
+
`OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a dead browser controller must be reapable before the provider reclaims its placement.`
|
|
3587
|
+
);
|
|
3588
|
+
}
|
|
3432
3589
|
if (!(reaperPeriod + idleGraceMs < idleTimeoutMs)) {
|
|
3433
3590
|
throw new Error(
|
|
3434
3591
|
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS + OPENGENI_SANDBOX_IDLE_GRACE_MS (${reaperPeriod} + ${idleGraceMs} = ${reaperPeriod + idleGraceMs}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a drained box must SURVIVE its full warm window so the reaper can resume + snapshot /workspace + terminate it on the sweep AFTER the drain grace elapses \u2014 Modal's idle-reap must NOT fire first (or /workspace is lost). Raise OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS (defaults to OPENGENI_MODAL_TIMEOUT_SECONDS) or lower OPENGENI_SANDBOX_IDLE_GRACE_MS.`
|
|
@@ -3565,7 +3722,12 @@ export {
|
|
|
3565
3722
|
OPENGENI_GATEWAY_PROVIDER_ID,
|
|
3566
3723
|
OPENGENI_REALTIME_MODEL_ID_PREFIX,
|
|
3567
3724
|
RegistryProviderKind,
|
|
3725
|
+
SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
|
|
3726
|
+
SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS,
|
|
3727
|
+
SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS,
|
|
3728
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
|
|
3568
3729
|
SANDBOX_REQUIRED_ENV,
|
|
3730
|
+
SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS,
|
|
3569
3731
|
SocialOAuthClientConfigSchema,
|
|
3570
3732
|
VERCEL_AI_GATEWAY_AI_SDK_BASE_URL,
|
|
3571
3733
|
VERCEL_AI_GATEWAY_BASE_URL,
|
|
@@ -3577,9 +3739,13 @@ export {
|
|
|
3577
3739
|
applyGitAuthPointerEnvironment,
|
|
3578
3740
|
assertTurnExecutionPolicyMatchesConfigV1,
|
|
3579
3741
|
builtinProviderId,
|
|
3742
|
+
calculateGatewayReportedCostBreakdown,
|
|
3580
3743
|
calculateGatewayReportedCostMicros,
|
|
3744
|
+
calculateModelUsageCostBreakdown,
|
|
3581
3745
|
calculateModelUsageCostMicros,
|
|
3746
|
+
calculateVideoGenerationCreditCostMicros,
|
|
3582
3747
|
canonicalizeConfiguredModelId,
|
|
3748
|
+
codemodeWorkspaceUrl,
|
|
3583
3749
|
collectGitIdentityEnvironment,
|
|
3584
3750
|
collectSandboxEnvironment,
|
|
3585
3751
|
configuredAllowedModels,
|
|
@@ -3601,6 +3767,7 @@ export {
|
|
|
3601
3767
|
getSettings,
|
|
3602
3768
|
hasGitCredentialRepositorySelection,
|
|
3603
3769
|
hasGitHubRepositorySelection,
|
|
3770
|
+
isDirectOpenAiApiBaseUrl,
|
|
3604
3771
|
isUsableVoiceInputSecret,
|
|
3605
3772
|
parseExposedPorts,
|
|
3606
3773
|
parseIntegrationsOauthClientsJson,
|
|
@@ -3632,6 +3799,7 @@ export {
|
|
|
3632
3799
|
sandboxArchiveCaptureTimeoutMs,
|
|
3633
3800
|
sandboxEnvironmentVariableNames,
|
|
3634
3801
|
sandboxLifecycleHookIds,
|
|
3802
|
+
sandboxLifecycleTransitionWaitMs,
|
|
3635
3803
|
sandboxPreparationProfiles,
|
|
3636
3804
|
sandboxWarmRateMicrosPerSecond,
|
|
3637
3805
|
selectModelPricing,
|