@opengeni/config 0.12.1 → 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.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,7 @@ 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.12"),
207
+ agentStableVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).default("0.1.14"),
196
208
  // Optional independent beta-channel pointer. When unset, the beta update
197
209
  // manifest route is unavailable rather than silently serving stable.
198
210
  agentBetaVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).optional(),
@@ -213,8 +225,7 @@ var SettingsSchema = z.object({
213
225
  // holder of stream:control gets 403 until this flips. Keeps stream:control a
214
226
  // declared-but-inert permission so later hardening is a flag flip.
215
227
  streamControlEnabled: EnvBoolean.default(false),
216
- toolspaceEnabled: EnvBoolean.default(false),
217
- toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
228
+ codemodeMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
218
229
  // Optional release-coherent bootstrap hint for custom rigs/connected machines
219
230
  // that do not carry the stock-image ogtool binary. Exact stable versions only:
220
231
  // the agent must never guess a tag or silently install `latest`.
@@ -224,11 +235,15 @@ var SettingsSchema = z.object({
224
235
  integrationsStateSecret: z.string().optional(),
225
236
  integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
226
237
  integrationsOauthClientsJson: z.string().default("{}"),
238
+ gmailRestAdapterEnabled: EnvBoolean.default(false),
227
239
  slackClientId: z.string().optional(),
228
240
  slackClientSecret: z.string().optional(),
229
241
  slackSigningSecret: z.string().optional(),
230
242
  googleDriveClientId: z.string().optional(),
231
243
  googleDriveClientSecret: z.string().optional(),
244
+ googleDriveWorkspaceEventsEnabled: EnvBoolean.optional(),
245
+ atlassianClientId: z.string().optional(),
246
+ atlassianClientSecret: z.string().optional(),
232
247
  // Undefined is meaningful: the migration boundary persists the product
233
248
  // default of 3 when no deployment override is supplied.
234
249
  maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
@@ -299,6 +314,20 @@ var SettingsSchema = z.object({
299
314
  // Gateway models below are added to the managed-credit catalog. Workspace
300
315
  // Gateway keys use the encrypted connection broker and never this secret.
301
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),
302
331
  // Native composer voice input (browser MediaRecorder → API transcription).
303
332
  // Provider credentials stay server-side; ClientConfig only projects availability
304
333
  // and hard ceilings. Selection happens once before audio is sent — never retry
@@ -367,6 +396,11 @@ var SettingsSchema = z.object({
367
396
  // compatibility diagnosis.
368
397
  // OPENGENI_CODEX_TOOL_SEARCH_ENABLED
369
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),
370
404
  // credential allocator atomic, workspace-local credential allocation. Default OFF is a
371
405
  // deliberate rolling-deploy fence: migrate + roll every worker first, then
372
406
  // enable. Turning it off restores the legacy sticky selector without a schema
@@ -424,6 +458,13 @@ var SettingsSchema = z.object({
424
458
  disableOpenaiTracing: EnvBoolean.default(false),
425
459
  sandboxBackend: SandboxBackend.default("docker"),
426
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),
427
468
  dockerExposedPorts: z.string().default(""),
428
469
  dockerNetwork: z.string().optional(),
429
470
  // When the worker itself runs in a container and talks to a host Docker daemon,
@@ -480,15 +521,14 @@ var SettingsSchema = z.object({
480
521
  // SOONER than the hard lifetime; the boot invariant forbids a value that would
481
522
  // reap before reaperPeriod + idleGrace elapses.
482
523
  modalIdleTimeoutSeconds: z.coerce.number().int().positive().optional(),
483
- // /workspace FILE PERSISTENCE across warm/cold cycles. Defaults to
484
- // `snapshot_filesystem` so EVERY box is created persistence-capable: the reaper
485
- // snapshots the live box before it terminates a drained group, and a later
486
- // cold-restore hydrates a fresh box from that snapshot (sandbox-file-persistence).
487
- // `snapshot_filesystem` requires the manifest declare NO ephemeralPersistencePaths
488
- // (buildManifest never sets entry.ephemeral, so it never downgrades to tar). Set
489
- // OPENGENI_MODAL_WORKSPACE_PERSISTENCE=tar to opt back out (no native snapshot;
490
- // the reaper persists a tar archive — same store+hydrate plumbing, slower).
491
- 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"),
492
532
  // Shared desktop toggle: this module reads it for the 6080 port-merge; the
493
533
  // owner module (P4.x) acts on it to launch the display stack.
494
534
  sandboxDesktopEnabled: EnvBoolean.default(false),
@@ -699,6 +739,10 @@ var SettingsSchema = z.object({
699
739
  // snapshotted before the box dies (sandbox-file-persistence).
700
740
  sandboxLeaseReaperPeriodMs: z.coerce.number().int().positive().default(3e4),
701
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),
702
746
  // The DRAIN grace: how long a refcount-0 (draining) lease stays WARM before the
703
747
  // reaper resume-by-ids the box and terminates it. This is the cost-vs-snappiness
704
748
  // dial — when the user navigates away the box keeps refcount 0, but it survives
@@ -724,7 +768,7 @@ var SettingsSchema = z.object({
724
768
  // graceful shutdown, or become permission to GC an older archive. Timeout is
725
769
  // treated exactly like a failed best-effort snapshot. Knob:
726
770
  // OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
727
- sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
771
+ sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().max(SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS).default(6e4),
728
772
  // Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
729
773
  // ahead of a finite provider deadline. Modal's 24h creation clock cannot be
730
774
  // extended; the logical sandbox outlives it by moving to one successor box.
@@ -734,13 +778,12 @@ var SettingsSchema = z.object({
734
778
  // an operator deliberately wants more rotation headroom; the boot invariant
735
779
  // still requires it to remain below the provider lifetime.
736
780
  sandboxRotationLeadMs: z.coerce.number().int().positive().default(36e5),
737
- // Bound each global reaper pass so a rollout that discovers many legacy boxes
738
- // with unknown creation clocks cannot create a provider/API thundering herd.
739
- // One is the safe admission default: the reaper services provider transitions
740
- // sequentially, so claiming a wider batch would fence boxes before the same
741
- // sweep can service them. Larger fleets may raise this only as an explicit,
742
- // observed deployment choice.
743
- 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),
744
787
  // expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
745
788
  // single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
746
789
  // window a cold->warming spawner has to commit warm before a reaper resets it.
@@ -969,7 +1012,11 @@ var ModelCapabilitiesV1Schema = z.object({
969
1012
  hostedTools: z.object({
970
1013
  webSearch: CapabilityStateV1Schema,
971
1014
  xSearch: CapabilityStateV1Schema,
972
- codeExecution: CapabilityStateV1Schema
1015
+ codeExecution: CapabilityStateV1Schema,
1016
+ imageGeneration: CapabilityStateV1Schema.default({
1017
+ upstream: "unknown",
1018
+ runnable: false
1019
+ })
973
1020
  }),
974
1021
  inputModalities: z.array(ModelModalityV1).min(1),
975
1022
  /** Exact MIME types accepted as typed `input_file`; `text/*` is allowed. */
@@ -1338,6 +1385,9 @@ function getSettings() {
1338
1385
  turnWorkerMaxConcurrentTurns: optional("OPENGENI_TURN_WORKER_MAX_CONCURRENT_TURNS"),
1339
1386
  turnWorkerTargetCpuUsage: optional("OPENGENI_TURN_WORKER_TARGET_CPU_USAGE"),
1340
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"),
1341
1391
  observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
1342
1392
  observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
1343
1393
  observabilityOtlpEndpoint: optional("OPENGENI_OTEL_EXPORTER_OTLP_ENDPOINT") ?? optional("OTEL_EXPORTER_OTLP_ENDPOINT"),
@@ -1362,8 +1412,7 @@ function getSettings() {
1362
1412
  delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
1363
1413
  streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
1364
1414
  streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
1365
- toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
1366
- toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
1415
+ codemodeMaxCallsPerTurn: optional("OPENGENI_CODEMODE_MAX_CALLS_PER_TURN"),
1367
1416
  ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
1368
1417
  environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
1369
1418
  integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
@@ -1372,11 +1421,15 @@ function getSettings() {
1372
1421
  "OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
1373
1422
  ),
1374
1423
  integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
1424
+ gmailRestAdapterEnabled: optional("OPENGENI_GMAIL_REST_ADAPTER_ENABLED"),
1375
1425
  slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
1376
1426
  slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
1377
1427
  slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
1378
1428
  googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
1379
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"),
1380
1433
  maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
1381
1434
  socialOauthClientsJson: optional("OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON"),
1382
1435
  goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
@@ -1403,6 +1456,24 @@ function getSettings() {
1403
1456
  openaiModel: optional("OPENGENI_OPENAI_MODEL"),
1404
1457
  openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
1405
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
+ ),
1406
1477
  voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
1407
1478
  voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
1408
1479
  voiceInputResumableEnabled: optional("OPENGENI_VOICE_INPUT_RESUMABLE_ENABLED"),
@@ -1434,6 +1505,7 @@ function getSettings() {
1434
1505
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
1435
1506
  codexConnectedAppsEnabled: optional("OPENGENI_CODEX_CONNECTED_APPS_ENABLED"),
1436
1507
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
1508
+ lazyToolSearchEnabled: optional("OPENGENI_LAZY_TOOL_SEARCH_ENABLED"),
1437
1509
  codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
1438
1510
  codexFleetPolicyShadowEnabled: optional("OPENGENI_CODEX_FLEET_POLICY_SHADOW_ENABLED"),
1439
1511
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
@@ -1454,6 +1526,7 @@ function getSettings() {
1454
1526
  disableOpenaiTracing: optional("OPENGENI_DISABLE_OPENAI_TRACING"),
1455
1527
  sandboxBackend: optional("OPENGENI_SANDBOX_BACKEND"),
1456
1528
  dockerImage: optional("OPENGENI_DOCKER_IMAGE"),
1529
+ sandboxArtifactRuntimeEnabled: optional("OPENGENI_SANDBOX_ARTIFACT_RUNTIME_ENABLED"),
1457
1530
  dockerExposedPorts: optional("OPENGENI_DOCKER_EXPOSED_PORTS"),
1458
1531
  dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
1459
1532
  dockerWorkspaceBaseDir: optional("OPENGENI_DOCKER_WORKSPACE_BASE_DIR"),
@@ -1536,6 +1609,7 @@ function getSettings() {
1536
1609
  sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
1537
1610
  sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
1538
1611
  sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
1612
+ sandboxInteractionHolderTtlMs: optional("OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS"),
1539
1613
  sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
1540
1614
  sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
1541
1615
  sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
@@ -1626,8 +1700,15 @@ function effectiveModalIdleTimeoutSeconds(settings) {
1626
1700
  }
1627
1701
  function sandboxArchiveCaptureTimeoutMs(settings) {
1628
1702
  return Math.min(
1629
- 60 * 6e4,
1630
- Math.max(settings.sandboxSnapshotTimeoutMs + 3e4, settings.sandboxSnapshotTimeoutMs * 2)
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
1631
1712
  );
1632
1713
  }
1633
1714
  function collectSandboxEnvironment(settings, source = process.env) {
@@ -1855,7 +1936,11 @@ function legacyModelCapabilities(settings, input) {
1855
1936
  runnable: input.hostedWebSearch
1856
1937
  },
1857
1938
  xSearch: { upstream: "unknown", runnable: false },
1858
- 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
+ }
1859
1944
  },
1860
1945
  inputModalities: input.vision ? ["text", "image"] : ["text"],
1861
1946
  inputFileMediaTypes: [
@@ -2028,6 +2113,18 @@ function builtinPromptCachingForModel(modelId) {
2028
2113
  const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
2029
2114
  return slug.startsWith("gpt-5.6-") ? { upstream: "supported", runnable: true, mode: "implicit" } : void 0;
2030
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
+ }
2031
2128
  function serviceTierForLatencyMode(providerId, latencyMode) {
2032
2129
  if (latencyMode === "standard") {
2033
2130
  return void 0;
@@ -2319,6 +2416,7 @@ function configuredModels(settings) {
2319
2416
  ...legacyModelCapabilities(settings, {
2320
2417
  reasoningEffort: true,
2321
2418
  hostedWebSearch: settings.webSearchEnabled,
2419
+ hostedImageGeneration: builtinHostedImageGenerationForModel(settings, id),
2322
2420
  vision: id.startsWith("gpt-5.6-")
2323
2421
  }),
2324
2422
  ...builtinPromptCachingForModel(id) ? { promptCaching: builtinPromptCachingForModel(id) } : {},
@@ -2562,6 +2660,9 @@ function configuredEntitlements(settings) {
2562
2660
  };
2563
2661
  }
2564
2662
  function calculateModelUsageCostMicros(settings, model, usage, options) {
2663
+ return calculateModelUsageCostBreakdown(settings, model, usage, options).creditCostMicros;
2664
+ }
2665
+ function calculateModelUsageCostBreakdown(settings, model, usage, options) {
2565
2666
  const schedule = configuredModelPricingSchedules(settings)[model];
2566
2667
  if (!schedule) {
2567
2668
  throw new Error(`Missing model pricing for ${model}`);
@@ -2575,10 +2676,12 @@ function calculateModelUsageCostMicros(settings, model, usage, options) {
2575
2676
  (rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry)
2576
2677
  );
2577
2678
  }
2578
- let total = 0;
2679
+ let providerCostMicros = 0;
2680
+ let creditCostMicros = 0;
2579
2681
  for (const [pricing, rawCost] of rawCostByPricing) {
2580
2682
  const marginBps = pricing.marginBps ?? 0;
2581
- total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
2683
+ providerCostMicros += rawCost;
2684
+ creditCostMicros += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
2582
2685
  }
2583
2686
  const latencyMode = options?.latencyMode ?? "standard";
2584
2687
  if (latencyMode !== "standard") {
@@ -2591,12 +2694,16 @@ function calculateModelUsageCostMicros(settings, model, usage, options) {
2591
2694
  (mode) => mode.id === latencyMode && mode.runnable
2592
2695
  )?.billingMultiplierBps;
2593
2696
  if (multiplierBps && multiplierBps > 0) {
2594
- total = Math.ceil(total * multiplierBps / 1e4);
2697
+ providerCostMicros = Math.ceil(providerCostMicros * multiplierBps / 1e4);
2698
+ creditCostMicros = Math.ceil(creditCostMicros * multiplierBps / 1e4);
2595
2699
  }
2596
2700
  }
2597
- return total;
2701
+ return { providerCostMicros, creditCostMicros };
2598
2702
  }
2599
2703
  function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, options) {
2704
+ return calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options).creditCostMicros;
2705
+ }
2706
+ function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
2600
2707
  const schedule = configuredModelPricingSchedules(settings)[model];
2601
2708
  if (!schedule) {
2602
2709
  throw new Error(`Missing model pricing for ${model}`);
@@ -2609,14 +2716,33 @@ function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, o
2609
2716
  const fraction = match[2] ?? "";
2610
2717
  const decimalDigits = BigInt(`${match[1]}${fraction}`);
2611
2718
  const decimalScale = 10n ** BigInt(fraction.length);
2719
+ const providerNumerator = decimalDigits * 1000000n;
2720
+ const providerMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
2612
2721
  const marginBps = BigInt(1e4 + (pricing.marginBps ?? 0));
2613
- const numerator = decimalDigits * 1000000n * marginBps;
2722
+ const numerator = providerNumerator * marginBps;
2614
2723
  const denominator = decimalScale * 10000n;
2615
- const micros = (numerator + denominator - 1n) / denominator;
2616
- if (micros > BigInt(Number.MAX_SAFE_INTEGER)) {
2724
+ const creditMicros = (numerator + denominator - 1n) / denominator;
2725
+ if (providerMicros > BigInt(Number.MAX_SAFE_INTEGER) || creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
2617
2726
  throw new Error("AI Gateway inference cost exceeds the supported billing range");
2618
2727
  }
2619
- return Number(micros);
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;
2620
2746
  }
2621
2747
  function configuredAllowedReasoningEfforts(settings) {
2622
2748
  return uniqueValues([
@@ -2734,16 +2860,13 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
2734
2860
  environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
2735
2861
  environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
2736
2862
  }
2737
- if (settings.toolspaceEnabled && settings.sandboxBackend !== "selfhosted") {
2738
- environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
2863
+ if (settings.sandboxBackend !== "selfhosted" && resolveFirstPartyDelegationSecret(settings)) {
2864
+ environment.OPENGENI_CODEMODE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/codemode-token`;
2739
2865
  if (settings.ogtoolPackageSpec) {
2740
2866
  environment.OPENGENI_OGTOOL_PACKAGE_SPEC ??= settings.ogtoolPackageSpec;
2741
2867
  }
2742
2868
  if (options.workspaceId) {
2743
- environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
2744
- settings,
2745
- options.workspaceId
2746
- );
2869
+ environment.OPENGENI_CODEMODE_URL ??= codemodeWorkspaceUrl(settings, options.workspaceId);
2747
2870
  }
2748
2871
  }
2749
2872
  return environment;
@@ -3117,6 +3240,7 @@ function ensureBuiltInMcpServers(settings) {
3117
3240
  "search_documents",
3118
3241
  "fetch_document_chunk",
3119
3242
  "list_document_bases",
3243
+ "list_indexed_documents",
3120
3244
  "knowledge_search",
3121
3245
  "knowledge_fetch",
3122
3246
  "memory_search",
@@ -3142,6 +3266,14 @@ function firstPartyMcpWorkspaceUrl(settings, workspaceId) {
3142
3266
  url.hash = "";
3143
3267
  return url.toString();
3144
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
+ }
3145
3277
  function firstPartyMcpServerUrl(settings) {
3146
3278
  return firstPartyMcpBaseUrl(settings);
3147
3279
  }
@@ -3153,9 +3285,6 @@ function firstPartyFilesMcpServerUrl(mcpUrl) {
3153
3285
  }
3154
3286
  function validateSettings(settings) {
3155
3287
  temporalConnectionOptions(settings);
3156
- if (settings.toolspaceEnabled && !settings.delegationSecret) {
3157
- throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_TOOLSPACE_ENABLED=true");
3158
- }
3159
3288
  if (settings.productAccessMode === "managed") {
3160
3289
  if (!settings.publicBaseUrl) {
3161
3290
  throw new Error(
@@ -3205,11 +3334,6 @@ function validateSettings(settings) {
3205
3334
  );
3206
3335
  }
3207
3336
  if (settings.slackClientId) {
3208
- if (!settings.slackSigningSecret) {
3209
- throw new Error(
3210
- "OPENGENI_SLACK_SIGNING_SECRET is required when the OpenGeni Slack app is configured"
3211
- );
3212
- }
3213
3337
  if (!settings.publicBaseUrl) {
3214
3338
  throw new Error(
3215
3339
  "OPENGENI_PUBLIC_BASE_URL is required when the OpenGeni Slack app is configured"
@@ -3248,6 +3372,28 @@ function validateSettings(settings) {
3248
3372
  );
3249
3373
  }
3250
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
+ }
3251
3397
  parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
3252
3398
  parseSocialOauthClientsJson(settings.socialOauthClientsJson);
3253
3399
  if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
@@ -3399,6 +3545,7 @@ function validateSettings(settings) {
3399
3545
  {
3400
3546
  const reaperPeriod = settings.sandboxLeaseReaperPeriodMs;
3401
3547
  const viewerTtl = settings.sandboxViewerHolderTtlMs;
3548
+ const interactionTtl = settings.sandboxInteractionHolderTtlMs;
3402
3549
  const idleGraceMs = settings.sandboxIdleGraceMs;
3403
3550
  const providerLifetimeMs = settings.modalTimeoutSeconds * 1e3;
3404
3551
  const rotationLeadMs = settings.sandboxRotationLeadMs;
@@ -3408,6 +3555,11 @@ function validateSettings(settings) {
3408
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.`
3409
3556
  );
3410
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
+ }
3411
3563
  if (!(idleTimeoutMs <= providerLifetimeMs)) {
3412
3564
  throw new Error(
3413
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.`
@@ -3418,9 +3570,10 @@ function validateSettings(settings) {
3418
3570
  `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
3419
3571
  );
3420
3572
  }
3421
- if (!(rotationLeadMs > settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod)) {
3573
+ const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
3574
+ if (!(rotationLeadMs > captureTimeoutMs + reaperPeriod)) {
3422
3575
  throw new Error(
3423
- `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the snapshot timeout plus two reaper periods (${settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod}).`
3576
+ `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the durable capture timeout plus one reaper period (${captureTimeoutMs + reaperPeriod}).`
3424
3577
  );
3425
3578
  }
3426
3579
  if (!(viewerTtl < idleTimeoutMs)) {
@@ -3428,6 +3581,11 @@ function validateSettings(settings) {
3428
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).`
3429
3582
  );
3430
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
+ }
3431
3589
  if (!(reaperPeriod + idleGraceMs < idleTimeoutMs)) {
3432
3590
  throw new Error(
3433
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.`
@@ -3564,7 +3722,12 @@ export {
3564
3722
  OPENGENI_GATEWAY_PROVIDER_ID,
3565
3723
  OPENGENI_REALTIME_MODEL_ID_PREFIX,
3566
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,
3567
3729
  SANDBOX_REQUIRED_ENV,
3730
+ SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS,
3568
3731
  SocialOAuthClientConfigSchema,
3569
3732
  VERCEL_AI_GATEWAY_AI_SDK_BASE_URL,
3570
3733
  VERCEL_AI_GATEWAY_BASE_URL,
@@ -3576,9 +3739,13 @@ export {
3576
3739
  applyGitAuthPointerEnvironment,
3577
3740
  assertTurnExecutionPolicyMatchesConfigV1,
3578
3741
  builtinProviderId,
3742
+ calculateGatewayReportedCostBreakdown,
3579
3743
  calculateGatewayReportedCostMicros,
3744
+ calculateModelUsageCostBreakdown,
3580
3745
  calculateModelUsageCostMicros,
3746
+ calculateVideoGenerationCreditCostMicros,
3581
3747
  canonicalizeConfiguredModelId,
3748
+ codemodeWorkspaceUrl,
3582
3749
  collectGitIdentityEnvironment,
3583
3750
  collectSandboxEnvironment,
3584
3751
  configuredAllowedModels,
@@ -3600,6 +3767,7 @@ export {
3600
3767
  getSettings,
3601
3768
  hasGitCredentialRepositorySelection,
3602
3769
  hasGitHubRepositorySelection,
3770
+ isDirectOpenAiApiBaseUrl,
3603
3771
  isUsableVoiceInputSecret,
3604
3772
  parseExposedPorts,
3605
3773
  parseIntegrationsOauthClientsJson,
@@ -3631,6 +3799,7 @@ export {
3631
3799
  sandboxArchiveCaptureTimeoutMs,
3632
3800
  sandboxEnvironmentVariableNames,
3633
3801
  sandboxLifecycleHookIds,
3802
+ sandboxLifecycleTransitionWaitMs,
3634
3803
  sandboxPreparationProfiles,
3635
3804
  sandboxWarmRateMicrosPerSecond,
3636
3805
  selectModelPricing,