@opengeni/config 0.4.0 → 0.5.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.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  StaticUsageLimits,
11
11
  UsageLimitsMode
12
12
  } from "@opengeni/contracts";
13
- import { CODEX_MODEL_ID_PREFIX } from "@opengeni/codex/constants";
13
+ import { CODEX_MODEL_ID_PREFIX, CODEX_PROVIDER_ID } from "@opengeni/codex/constants";
14
14
  import { z } from "zod";
15
15
  var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
16
16
  var registryId = /^[A-Za-z0-9_-]+$/;
@@ -67,9 +67,9 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
67
67
  "File resources are mounted under files/<file-id>/ unless the session specifies another mount path.",
68
68
  "Attached files are mounted read-only; copy them before modifying.",
69
69
  "Bundled skills are under .agents/ and can include infrastructure, marketing, or other role-specific guidance.",
70
- "Use Checkov, Terraform, Azure CLI, GitHub CLI, and repository tools when relevant.",
70
+ "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.",
71
71
  "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.",
72
- "Treat code-changing work as GitOps work: create a focused branch/commit/PR when GitHub credentials are available; otherwise report exact commands and blockers.",
72
+ "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.",
73
73
  "Return concise, factual summaries with files changed, commands run, and remaining blockers.",
74
74
  AGENT_INSTRUCTIONS_CORE_PLACEHOLDER
75
75
  ].join(" ");
@@ -151,57 +151,33 @@ var SettingsSchema = z.object({
151
151
  // above; the graceful max-turns valve (idle + goal continuation, never a
152
152
  // session failure) remains as inert safety should a deployment set a cap.
153
153
  agentMaxModelCallsPerTurn: z.coerce.number().int().positive().default(1e6),
154
- // Where turn-input conversation history comes from (issue #35):
155
- // "items" (default) = the session_history_items table (SDK-native,
156
- // version-stable conversation truth); "run_state" = the legacy serialized
157
- // RunState blob. Items and the sandbox envelope are dual-written
158
- // unconditionally; this flag governs the read path only, so flipping back to
159
- // "run_state" remains a safe rollback at any time.
160
- sessionHistorySource: z.enum(["run_state", "items"]).default("items"),
161
- // Provider-aware conversation context management (long-lived sessions
162
- // otherwise grow unbounded until they overflow the model context window and
163
- // hard-fail every turn). Resolution (see resolveContextCompactionMode):
164
- // "auto" (default) -> "server" when openaiProvider === "openai" (the
165
- // OpenAI platform Responses API honors server-side context_management),
166
- // else "client" (Azure rejects context_management with a 400, so we run
167
- // our own client-side compaction).
168
- // "server" / "client" -> force that path regardless of provider.
169
- // "off" -> neither path (legacy unbounded growth; escape hatch only).
170
- contextCompactionMode: z.enum(["auto", "server", "client", "off"]).default("auto"),
171
- // The model's real context window in tokens. gpt-5.5's true window is
172
- // 1,050,000; it is absent from the SDK's hardcoded compaction window map (it
173
- // knows only up to gpt-5.4), so the SDK's DynamicCompactionPolicy would fall
174
- // back to a wrong 240k. We pass an explicit StaticCompactionPolicy threshold
175
- // derived from these settings on the server path, and use the same numbers to
176
- // budget the client path.
154
+ // The model family's real context window in tokens. OpenGeni always performs
155
+ // one durable, portable plaintext compaction transition; there is no
156
+ // provider/server/off mode ladder.
177
157
  contextWindowTokens: z.coerce.number().int().positive().default(105e4),
158
+ // Optional model-catalog effective input ceiling. Codex models expose this as
159
+ // raw context_window * effective_context_window_percent; when absent, retain
160
+ // the deployment-level window-minus-reserved-output behavior.
161
+ contextEffectiveWindowTokens: z.coerce.number().int().positive().optional(),
178
162
  // Proactive compaction threshold as a ratio of the model context window.
179
- // Defaults to 60% and is clamped to [0.3, 0.9] so deployments can tune the
180
- // trigger without accidentally disabling compaction or waiting until the
181
- // provider is already at the cliff.
182
- contextCompactionThresholdRatio: z.coerce.number().default(0.6).transform((value) => {
163
+ // Defaults to 90%: compact as late as possible retained context beats early
164
+ // headroom now that per-model windows are declared honestly (input-effective,
165
+ // empirically measured), and the fail-closed reactive compact-on-reject path
166
+ // absorbs any overshoot as one retried call rather than a dead session.
167
+ // Clamped to [0.3, 0.9] so deployments can tune the trigger without
168
+ // accidentally disabling compaction.
169
+ contextCompactionThresholdRatio: z.coerce.number().default(0.9).transform((value) => {
183
170
  if (!Number.isFinite(value)) {
184
- return 0.6;
171
+ return 0.9;
185
172
  }
186
173
  return Math.min(0.9, Math.max(0.3, value));
187
174
  }),
188
175
  // Tokens reserved for model output; subtracted from the window to get the
189
176
  // usable input budget B = contextWindowTokens - contextReservedOutputTokens.
190
177
  contextReservedOutputTokens: z.coerce.number().int().nonnegative().default(128e3),
191
- // Server path only: explicit compact_threshold (tokens) handed to the SDK's
192
- // StaticCompactionPolicy. Defaults to floor(contextWindowTokens *
193
- // contextCompactionThresholdRatio) when unset.
194
- contextServerCompactThresholdTokens: z.coerce.number().int().positive().optional(),
195
- // Deprecated back-compat knobs. The threshold is now controlled by
196
- // contextCompactionThresholdRatio; these remain parsed so older deployments do
197
- // not fail boot when their env still contains them.
198
- contextCompactSoftFraction: z.coerce.number().positive().max(1).default(0.7),
199
- contextCompactHardFraction: z.coerce.number().positive().max(1).default(0.85),
200
- // Deprecated for the client path; parsed for env/back-compat only.
201
- contextKeepRecentTokens: z.coerce.number().int().positive().default(32e3),
202
- // Parsed for back-compat. Client compaction uses the fixed 20k Codex summary
203
- // buffer as its generated-summary output ceiling.
204
- contextSummaryMaxTokens: z.coerce.number().int().positive().default(2e4),
178
+ // Model-catalog auto-compact limit. When present it is clamped to
179
+ // 90% of the raw window, matching Codex core's auto_compact_token_limit().
180
+ contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
205
181
  authRequired: EnvBoolean.default(false),
206
182
  accessKey: z.string().optional(),
207
183
  authAllowHealth: EnvBoolean.default(true),
@@ -214,8 +190,8 @@ var SettingsSchema = z.object({
214
190
  openaiProvider: z.enum(["openai", "azure"]).default("openai"),
215
191
  openaiApiKey: z.string().optional(),
216
192
  openaiBaseUrl: z.string().optional(),
217
- openaiModel: z.string().default("gpt-5.5"),
218
- openaiAllowedModels: z.string().default("gpt-5.5,gpt-5.4,gpt-5.4-mini"),
193
+ openaiModel: z.string().default("gpt-5.6-sol"),
194
+ openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
219
195
  modelPricingJson: z.string().default("{}"),
220
196
  // Extra (non-built-in) model providers, declared by the host as a JSON
221
197
  // provider registry. Each entry carries its own base URL, API key, wire API
@@ -237,6 +213,11 @@ var SettingsSchema = z.object({
237
213
  // tool that BM25-discloses only the matching connectors. Default OFF — a codex
238
214
  // turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
239
215
  codexToolSearchEnabled: EnvBoolean.default(false),
216
+ // OPE-21 atomic, workspace-local credential allocation. Default OFF is a
217
+ // deliberate rolling-deploy fence: migrate + roll every worker first, then
218
+ // enable. Turning it off restores the legacy sticky selector without a schema
219
+ // rollback; the additive lease table/cursor columns become inert.
220
+ codexCredentialLeasingEnabled: EnvBoolean.default(false),
240
221
  // Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
241
222
  // rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
242
223
  // match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
@@ -274,7 +255,7 @@ var SettingsSchema = z.object({
274
255
  // the non-bypassable CORE at AGENT_INSTRUCTIONS_CORE_PLACEHOLDER (or appends
275
256
  // it when the template omits the marker), and uses the result as the agent's
276
257
  // instructions. Defaulting to DEFAULT_AGENT_INSTRUCTIONS keeps the composed
277
- // default byte-identical to the historical hardcoded preamble.
258
+ // default pinned by runtime tests.
278
259
  agentInstructionsTemplate: z.string().default(DEFAULT_AGENT_INSTRUCTIONS),
279
260
  azureOpenaiBaseUrl: z.string().optional(),
280
261
  azureOpenaiEndpoint: z.string().optional(),
@@ -379,6 +360,13 @@ var SettingsSchema = z.object({
379
360
  // recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
380
361
  recordingEnabled: EnvBoolean.default(true),
381
362
  recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
363
+ // Workbench v2 turn-end workspace capture (dossier §10.1). When on, the turn
364
+ // activity probes the box's changed files off the live box at turn end and
365
+ // persists a capture revision (blobs in @opengeni/storage) so the workbench
366
+ // paints cold/offline sessions with zero machine round-trips. Best-effort and
367
+ // fully behind this flag: off ⇒ capture is skipped and reads fall back to the
368
+ // live/wake path (status-quo behavior). Default on; explicit per environment.
369
+ workspaceCaptureEnabled: EnvBoolean.default(true),
382
370
  recordingFramerate: z.coerce.number().int().positive().default(15),
383
371
  recordingMaxSeconds: z.coerce.number().int().positive().default(600),
384
372
  recordingMaxBytes: z.coerce.number().int().positive().default(268435456),
@@ -435,6 +423,19 @@ var SettingsSchema = z.object({
435
423
  // EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
436
424
  // turn the flag ON the moment anyone set the env var to disable it).
437
425
  sandboxOwnershipEnabled: EnvBoolean.default(false),
426
+ // --- lazy sandbox provisioning rollout flag, default OFF ---
427
+ // Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
428
+ // property of the owned path — the SDK never creates/resumes an injected session,
429
+ // so we control when the box is established). When TRUE, a turn does NOT provision
430
+ // its box at turn start: the lease acquire + resume-by-id + hooks + downloads +
431
+ // heartbeat + recording are deferred to an in-process single-flight provisioner
432
+ // that runs the FIRST time a sandbox op is dispatched (via the routing proxy's
433
+ // resolveActiveBackend). A turn whose model never calls a sandbox-backed tool ends
434
+ // with NO lease row and ZERO warm-seconds. When FALSE (or ownership off) the turn
435
+ // provisions eagerly exactly as today — byte-for-byte. EnvBoolean (NOT
436
+ // z.coerce.boolean(), which coerces "false" -> true and would turn the flag ON the
437
+ // moment anyone set the env var to disable it).
438
+ sandboxLazyProvisionEnabled: EnvBoolean.default(false),
438
439
  // --- bring-your-own-compute (selfhosted 11th backend) rollout flag, default OFF ---
439
440
  // The keystone flag for the whole selfhosted feature (the enrollment device-flow,
440
441
  // the NATS control plane, the relay stream tier). When FALSE the enrollment routes
@@ -443,6 +444,11 @@ var SettingsSchema = z.object({
443
444
  // z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
444
445
  // the deploy-staging IaC secret/configmap pattern (dossier §17/§25.1).
445
446
  sandboxSelfhostedEnabled: EnvBoolean.default(false),
447
+ // Gates the op-stream (streaming exec) transport to Connected Machines. The
448
+ // runner must ALSO advertise Capabilities.op_stream; default off, and legacy
449
+ // request/reply exec is the permanent fallback. EnvBoolean (NOT
450
+ // z.coerce.boolean(), which coerces "false" -> true).
451
+ agentOpStreamEnabled: EnvBoolean.default(false),
446
452
  // The HMAC secret the control plane signs the enrollment bearer credential with
447
453
  // (the `oge_` envelope the agent presents back to the control plane). Optional:
448
454
  // when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
@@ -499,6 +505,25 @@ var SettingsSchema = z.object({
499
505
  // bus connects anonymously (local dev / a NATS with no auth_callout).
500
506
  selfhostedNatsControlUser: z.string().optional(),
501
507
  selfhostedNatsControlPassword: z.string().optional(),
508
+ // --- selfhosted (Connected Machine) control/exec op deadlines ---------------
509
+ // The control plane splits its op deadline in two. CONTROL ops (ping / fs / git /
510
+ // desktop / pty) must stay responsive so a machine's liveness is never masked by a
511
+ // slow op, so they use the short control timeout. EXEC gets its OWN, larger budget:
512
+ // a real command (compile, test run, dependency install) routinely outlives the
513
+ // control timeout, and before the split a long command was killed at the ~30s
514
+ // control wall. The agent kills the exec child at this deadline; the wire waits
515
+ // slightly longer (SELFHOSTED_EXEC_REPLY_GRACE_MS) for the typed timed-out reply.
516
+ //
517
+ // The exec default is a DELIBERATELY MODEST 2min (not 5): the agent-side admission
518
+ // pool is (until a later agent release) a FLAT 8 permits with no per-class split,
519
+ // so 8 slow execs holding a permit for 5 minutes would blanket-DRAIN every fs/git
520
+ // op — shipping the amplifier before the class-aware-admission fix. 2min still
521
+ // clears the large majority of the observed >30s exec tail; genuinely long jobs run
522
+ // in the background (see the exec-deadline hint) or raise the knob per deployment.
523
+ // Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 2min) and
524
+ // OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS (default 30s).
525
+ sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().positive().default(12e4),
526
+ sandboxSelfhostedControlTimeoutMs: z.coerce.number().int().positive().default(3e4),
502
527
  // --- sandbox lease cadences (cadence invariant validated at boot below) ---
503
528
  // reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
504
529
  // box idle timeout (effectiveModalIdleTimeoutSeconds, which defaults to the hard
@@ -517,6 +542,22 @@ var SettingsSchema = z.object({
517
542
  // fresh EMPTY box; lower it to trade warm cost for a snappier reclaim. Knob:
518
543
  // OPENGENI_SANDBOX_IDLE_GRACE_MS.
519
544
  sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
545
+ // MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
546
+ // reaper's drain-persist only protects boxes the reaper itself kills; a box
547
+ // that dies any other way (Modal's hard creation-time timeout on a session
548
+ // busy past it, provider OOM/infra death) loses everything since the last
549
+ // clean drain. While a turn holds the box, the turn heartbeat and turn-end
550
+ // both take a snapshot when at least this interval has passed since the last
551
+ // one (same epoch-fenced fold-onto-lease seam as the drain), bounding the
552
+ // worst-case loss of ANY unclean box death to this window. 0 disables.
553
+ // Knob: OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS. Default 15min.
554
+ sandboxSnapshotIntervalMs: z.coerce.number().int().min(0).default(9e5),
555
+ // Maximum time a best-effort /workspace snapshot capture may hold turn/reaper
556
+ // cleanup. A hung provider snapshot must never pin a lease holder, block
557
+ // graceful shutdown, or become permission to GC an older archive. Timeout is
558
+ // treated exactly like a failed best-effort snapshot. Knob:
559
+ // OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
560
+ sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
520
561
  // expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
521
562
  // single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
522
563
  // window a cold->warming spawner has to commit warm before a reaper resets it.
@@ -526,6 +567,11 @@ var SettingsSchema = z.object({
526
567
  // (a liveness/reaper cadence), this bounds how long one turn waits for capacity
527
568
  // or provider creation before surfacing a clear turn.failed error.
528
569
  sandboxWarmingTimeoutMs: z.coerce.number().int().positive().default(6e5),
570
+ // Rig setup-script budget (M3): the wall-clock timeout the rig-setup lifecycle
571
+ // hook runs its script under, distinct from the 120s per-command lifecycle
572
+ // default (a rig may compile/install heavy tooling on first cold create).
573
+ // Env: OPENGENI_RIG_SETUP_TIMEOUT_MS. Default 10min.
574
+ rigSetupTimeoutMs: z.coerce.number().int().positive().default(6e5),
529
575
  // --- sandbox warm-time billing (P2.1) ---
530
576
  // Per-backend warm rate (usd_micros/sec), like modelPricingJson: an empty {}
531
577
  // means warm-cost is not debited (warm-seconds are still metered for audit).
@@ -586,30 +632,32 @@ var SettingsSchema = z.object({
586
632
  stripePublishableKey: z.string().optional(),
587
633
  stripeWebhookSecret: z.string().optional(),
588
634
  stripeCreditsProductId: z.string().optional(),
589
- mcpServers: z.array(z.object({
590
- id: z.string().min(1).regex(registryId),
591
- name: z.string().min(1).optional(),
592
- url: z.string().url(),
593
- allowedTools: z.array(z.string().min(1)).optional(),
594
- timeoutMs: z.number().int().positive().optional(),
595
- cacheToolsList: z.boolean().default(false),
596
- /**
597
- * Human-approval policy for this server's tools, overlaid per-run from a
598
- * session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
599
- * tools require approval; a string[] = only the listed UNPREFIXED tool
600
- * names do; absent = auto-run (the historical default). Enforced in the
601
- * runtime by attaching `needsApproval` to the matching MCP tools.
602
- */
603
- requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
604
- /**
605
- * Extra request headers sent to this MCP server (credential injection
606
- * for workspace-enabled capability MCPs). Populated at runtime from
607
- * encrypted capability-installation credentials; do not put secrets in
608
- * OPENGENI_MCP_SERVERS.
609
- */
610
- headers: z.record(z.string(), z.string()).optional(),
611
- connectionRef: McpServerConnectionRefSchema.optional()
612
- })).default([])
635
+ mcpServers: z.array(
636
+ z.object({
637
+ id: z.string().min(1).regex(registryId),
638
+ name: z.string().min(1).optional(),
639
+ url: z.string().url(),
640
+ allowedTools: z.array(z.string().min(1)).optional(),
641
+ timeoutMs: z.number().int().positive().optional(),
642
+ cacheToolsList: z.boolean().default(false),
643
+ /**
644
+ * Human-approval policy for this server's tools, overlaid per-run from a
645
+ * session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
646
+ * tools require approval; a string[] = only the listed UNPREFIXED tool
647
+ * names do; absent = auto-run (the historical default). Enforced in the
648
+ * runtime by attaching `needsApproval` to the matching MCP tools.
649
+ */
650
+ requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
651
+ /**
652
+ * Extra request headers sent to this MCP server (credential injection
653
+ * for workspace-enabled capability MCPs). Populated at runtime from
654
+ * encrypted capability-installation credentials; do not put secrets in
655
+ * OPENGENI_MCP_SERVERS.
656
+ */
657
+ headers: z.record(z.string(), z.string()).optional(),
658
+ connectionRef: McpServerConnectionRefSchema.optional()
659
+ })
660
+ ).default([])
613
661
  });
614
662
  var ModelPricingSchema = z.object({
615
663
  inputMicrosPerMillionTokens: z.number().int().nonnegative(),
@@ -625,6 +673,8 @@ var RegistryModelSchema = z.object({
625
673
  label: z.string().min(1).optional(),
626
674
  // display name; defaults to id
627
675
  contextWindowTokens: z.number().int().positive().optional(),
676
+ effectiveContextWindowTokens: z.number().int().positive().optional(),
677
+ autoCompactTokenLimit: z.number().int().positive().optional(),
628
678
  reasoningEffort: z.boolean().optional(),
629
679
  // model accepts a reasoning-effort control
630
680
  hostedWebSearch: z.boolean().optional(),
@@ -653,12 +703,24 @@ var IntegrationOAuthClientConfigSchema = z.object({
653
703
  tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
654
704
  });
655
705
  var defaultModelPricing = {
656
- "gpt-5.5": {
706
+ "gpt-5.6-sol": {
657
707
  inputMicrosPerMillionTokens: 5e6,
658
708
  cachedInputMicrosPerMillionTokens: 5e5,
659
709
  outputMicrosPerMillionTokens: 3e7,
660
710
  marginBps: 2500
661
711
  },
712
+ "gpt-5.6-terra": {
713
+ inputMicrosPerMillionTokens: 25e5,
714
+ cachedInputMicrosPerMillionTokens: 25e4,
715
+ outputMicrosPerMillionTokens: 15e6,
716
+ marginBps: 2500
717
+ },
718
+ "gpt-5.6-luna": {
719
+ inputMicrosPerMillionTokens: 1e6,
720
+ cachedInputMicrosPerMillionTokens: 1e5,
721
+ outputMicrosPerMillionTokens: 6e6,
722
+ marginBps: 2500
723
+ },
662
724
  "gpt-5.4": {
663
725
  inputMicrosPerMillionTokens: 25e5,
664
726
  cachedInputMicrosPerMillionTokens: 25e4,
@@ -734,21 +796,11 @@ var SANDBOX_REQUIRED_ENV = {
734
796
  { field: "modalTokenId", env: "OPENGENI_MODAL_TOKEN_ID" },
735
797
  { field: "modalTokenSecret", env: "OPENGENI_MODAL_TOKEN_SECRET" }
736
798
  ],
737
- daytona: [
738
- { field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }
739
- ],
740
- runloop: [
741
- { field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }
742
- ],
743
- e2b: [
744
- { field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }
745
- ],
746
- blaxel: [
747
- { field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }
748
- ],
749
- cloudflare: [
750
- { field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }
751
- ],
799
+ daytona: [{ field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }],
800
+ runloop: [{ field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }],
801
+ e2b: [{ field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }],
802
+ blaxel: [{ field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }],
803
+ cloudflare: [{ field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }],
752
804
  vercel: [
753
805
  { field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
754
806
  { field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
@@ -780,7 +832,9 @@ function getSettings() {
780
832
  temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
781
833
  temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
782
834
  startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
783
- startupDependencyRetryInitialDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"),
835
+ startupDependencyRetryInitialDelayMs: optional(
836
+ "OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
837
+ ),
784
838
  startupDependencyRetryMaxDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_MAX_DELAY_MS"),
785
839
  observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
786
840
  observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
@@ -802,21 +856,18 @@ function getSettings() {
802
856
  environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
803
857
  integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
804
858
  integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
805
- integrationsAllowPrivateNetworkTargets: optional("OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"),
859
+ integrationsAllowPrivateNetworkTargets: optional(
860
+ "OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
861
+ ),
806
862
  integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
807
863
  goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
808
864
  goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
809
865
  agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
810
- sessionHistorySource: optional("OPENGENI_SESSION_HISTORY_SOURCE"),
811
- contextCompactionMode: optional("OPENGENI_CONTEXT_COMPACTION_MODE"),
812
866
  contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
867
+ contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
813
868
  contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
814
869
  contextReservedOutputTokens: optional("OPENGENI_CONTEXT_RESERVED_OUTPUT_TOKENS"),
815
- contextServerCompactThresholdTokens: optional("OPENGENI_CONTEXT_SERVER_COMPACT_THRESHOLD_TOKENS"),
816
- contextCompactSoftFraction: optional("OPENGENI_CONTEXT_COMPACT_SOFT_FRACTION"),
817
- contextCompactHardFraction: optional("OPENGENI_CONTEXT_COMPACT_HARD_FRACTION"),
818
- contextKeepRecentTokens: optional("OPENGENI_CONTEXT_KEEP_RECENT_TOKENS"),
819
- contextSummaryMaxTokens: optional("OPENGENI_CONTEXT_SUMMARY_MAX_TOKENS"),
870
+ contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
820
871
  authRequired: optional("OPENGENI_AUTH_REQUIRED"),
821
872
  accessKey: optional("OPENGENI_ACCESS_KEY"),
822
873
  authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
@@ -835,6 +886,7 @@ function getSettings() {
835
886
  modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
836
887
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
837
888
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
889
+ codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
838
890
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
839
891
  codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
840
892
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
@@ -874,6 +926,7 @@ function getSettings() {
874
926
  computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
875
927
  computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
876
928
  recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
929
+ workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
877
930
  recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
878
931
  recordingFramerate: optional("OPENGENI_RECORDING_FRAMERATE"),
879
932
  recordingMaxSeconds: optional("OPENGENI_RECORDING_MAX_SECONDS"),
@@ -913,7 +966,9 @@ function getSettings() {
913
966
  vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
914
967
  vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
915
968
  sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
969
+ sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
916
970
  sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
971
+ agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
917
972
  enrollmentSigningSecret: optional("OPENGENI_ENROLLMENT_SIGNING_SECRET"),
918
973
  selfhostedNatsUrl: optional("OPENGENI_SELFHOSTED_NATS_URL"),
919
974
  selfhostedRelayUrl: optional("OPENGENI_SELFHOSTED_RELAY_URL"),
@@ -925,13 +980,20 @@ function getSettings() {
925
980
  selfhostedNatsCalloutPassword: optional("OPENGENI_SELFHOSTED_NATS_CALLOUT_PASSWORD"),
926
981
  selfhostedNatsControlUser: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_USER"),
927
982
  selfhostedNatsControlPassword: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_PASSWORD"),
983
+ sandboxSelfhostedExecTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS"),
984
+ sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
928
985
  sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
929
986
  sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
930
987
  sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
988
+ sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
989
+ sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
931
990
  sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
932
991
  sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
933
992
  sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
934
- sandboxWarmRateMicrosPerSecondJson: optional("OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"),
993
+ rigSetupTimeoutMs: optional("OPENGENI_RIG_SETUP_TIMEOUT_MS"),
994
+ sandboxWarmRateMicrosPerSecondJson: optional(
995
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"
996
+ ),
935
997
  sandboxMaxWarmSecondsPerWorkspace: optional("OPENGENI_SANDBOX_MAX_WARM_SECONDS_PER_WORKSPACE"),
936
998
  sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
937
999
  sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
@@ -1027,8 +1089,7 @@ function configuredProviders(settings) {
1027
1089
  label: builtinProviderLabel(settings),
1028
1090
  kind: "api-key",
1029
1091
  api: "responses",
1030
- builtin: true,
1031
- compactionMode: resolveContextCompactionMode(settings)
1092
+ builtin: true
1032
1093
  };
1033
1094
  if (settings.openaiProvider === "azure") {
1034
1095
  builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
@@ -1037,28 +1098,41 @@ function configuredProviders(settings) {
1037
1098
  builtin.baseUrl = settings.openaiBaseUrl;
1038
1099
  builtin.apiKey = settings.openaiApiKey;
1039
1100
  }
1040
- const registry = parseModelProvidersJson(settings.modelProvidersJson).map((provider) => ({
1041
- id: provider.id,
1042
- label: provider.label ?? provider.id,
1043
- kind: provider.kind,
1044
- api: provider.api,
1045
- builtin: false,
1046
- baseUrl: provider.baseUrl,
1047
- apiKey: resolveProviderApiKey(provider),
1048
- defaultQuery: provider.defaultQuery,
1049
- defaultHeaders: provider.defaultHeaders,
1050
- compactionMode: "client"
1051
- }));
1101
+ const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
1102
+ (provider) => ({
1103
+ id: provider.id,
1104
+ label: provider.label ?? provider.id,
1105
+ kind: provider.kind,
1106
+ api: provider.api,
1107
+ builtin: false,
1108
+ baseUrl: provider.baseUrl,
1109
+ apiKey: resolveProviderApiKey(provider),
1110
+ defaultQuery: provider.defaultQuery,
1111
+ defaultHeaders: provider.defaultHeaders
1112
+ })
1113
+ );
1052
1114
  return [builtin, ...registry];
1053
1115
  }
1116
+ function policyProviderIdForModel(settings, modelId) {
1117
+ if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1118
+ return CODEX_PROVIDER_ID;
1119
+ }
1120
+ const configured = configuredModels(settings).find((model) => model.id === modelId);
1121
+ return configured?.providerId ?? builtinProviderId(settings);
1122
+ }
1054
1123
  function configuredModels(settings) {
1055
1124
  const builtinId = builtinProviderId(settings);
1056
1125
  const builtinLabel = builtinProviderLabel(settings);
1057
1126
  const registryOwnedIds = new Set(
1058
- parseModelProvidersJson(settings.modelProvidersJson).flatMap((provider) => provider.models.map((model) => model.id))
1127
+ parseModelProvidersJson(settings.modelProvidersJson).flatMap(
1128
+ (provider) => provider.models.map((model) => model.id)
1129
+ )
1059
1130
  );
1060
1131
  const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
1061
- const out = uniqueValues([settings.openaiModel, ...splitCsv(settings.openaiAllowedModels)]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
1132
+ const out = uniqueValues([
1133
+ settings.openaiModel,
1134
+ ...splitCsv(settings.openaiAllowedModels)
1135
+ ]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
1062
1136
  id,
1063
1137
  label: id,
1064
1138
  providerId: builtinId,
@@ -1078,6 +1152,8 @@ function configuredModels(settings) {
1078
1152
  providerLabel,
1079
1153
  api: provider.api,
1080
1154
  ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1155
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1156
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1081
1157
  reasoningEffort: model.reasoningEffort ?? false,
1082
1158
  hostedWebSearch: model.hostedWebSearch ?? false
1083
1159
  });
@@ -1100,7 +1176,9 @@ function resolveModelProvider(settings, modelId) {
1100
1176
  if (!model) {
1101
1177
  return void 0;
1102
1178
  }
1103
- const provider = configuredProviders(settings).find((candidate) => candidate.id === model.providerId);
1179
+ const provider = configuredProviders(settings).find(
1180
+ (candidate) => candidate.id === model.providerId
1181
+ );
1104
1182
  if (!provider) {
1105
1183
  return void 0;
1106
1184
  }
@@ -1122,27 +1200,25 @@ function configuredModelPricing(settings) {
1122
1200
  ...configured
1123
1201
  };
1124
1202
  }
1125
- function resolveContextCompactionMode(settings) {
1126
- switch (settings.contextCompactionMode) {
1127
- case "server":
1128
- return "server";
1129
- case "client":
1130
- return "client";
1131
- case "off":
1132
- return "off";
1133
- case "auto":
1134
- default:
1135
- return settings.openaiProvider === "openai" ? "server" : "client";
1136
- }
1137
- }
1138
1203
  function contextInputBudgetTokens(settings) {
1204
+ if (settings.contextEffectiveWindowTokens !== void 0) {
1205
+ return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
1206
+ }
1139
1207
  return Math.max(0, settings.contextWindowTokens - settings.contextReservedOutputTokens);
1140
1208
  }
1141
- function contextServerCompactThreshold(settings) {
1142
- if (settings.contextServerCompactThresholdTokens) {
1143
- return settings.contextServerCompactThresholdTokens;
1144
- }
1145
- return Math.floor(settings.contextWindowTokens * settings.contextCompactionThresholdRatio);
1209
+ function settingsWithResolvedModelContext(settings, model) {
1210
+ const contextWindowTokens = model.contextWindowTokens ?? settings.contextWindowTokens;
1211
+ return {
1212
+ ...settings,
1213
+ contextWindowTokens,
1214
+ ...model.effectiveContextWindowTokens === void 0 ? {} : {
1215
+ contextEffectiveWindowTokens: Math.min(
1216
+ contextWindowTokens,
1217
+ model.effectiveContextWindowTokens
1218
+ )
1219
+ },
1220
+ ...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }
1221
+ };
1146
1222
  }
1147
1223
  function configuredStaticUsageLimits(settings) {
1148
1224
  return parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1175,7 +1251,10 @@ function calculateModelUsageCostMicros(settings, model, usage) {
1175
1251
  return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1176
1252
  }
1177
1253
  function configuredAllowedReasoningEfforts(settings) {
1178
- return uniqueValues([settings.openaiReasoningEffort, ...splitCsv(settings.openaiAllowedReasoningEfforts)]).map((value) => ReasoningEffort.parse(value));
1254
+ return uniqueValues([
1255
+ settings.openaiReasoningEffort,
1256
+ ...splitCsv(settings.openaiAllowedReasoningEfforts)
1257
+ ]).map((value) => ReasoningEffort.parse(value));
1179
1258
  }
1180
1259
  function environmentsEncryptionKeyBytes(settings) {
1181
1260
  if (!settings.environmentsEncryptionKey) {
@@ -1183,7 +1262,9 @@ function environmentsEncryptionKeyBytes(settings) {
1183
1262
  }
1184
1263
  const decoded = Buffer.from(settings.environmentsEncryptionKey, "base64");
1185
1264
  if (decoded.length !== 32) {
1186
- throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY must be base64 for exactly 32 bytes (generate with: openssl rand -base64 32)");
1265
+ throw new Error(
1266
+ "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY must be base64 for exactly 32 bytes (generate with: openssl rand -base64 32)"
1267
+ );
1187
1268
  }
1188
1269
  return new Uint8Array(decoded);
1189
1270
  }
@@ -1198,12 +1279,21 @@ function dbSearchPath(settings) {
1198
1279
  return `${schema},opengeni_private,public`;
1199
1280
  }
1200
1281
  function collectGitIdentityEnvironment(settings) {
1201
- return Object.fromEntries(Object.entries({
1202
- GIT_AUTHOR_NAME: settings.gitAuthorName,
1203
- GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
1204
- GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
1205
- GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
1206
- }).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0));
1282
+ return Object.fromEntries(
1283
+ Object.entries({
1284
+ GIT_AUTHOR_NAME: settings.gitAuthorName,
1285
+ GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
1286
+ GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
1287
+ GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
1288
+ }).filter(
1289
+ (entry) => typeof entry[1] === "string" && entry[1].trim().length > 0
1290
+ )
1291
+ );
1292
+ }
1293
+ var DEFAULT_SANDBOX_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
1294
+ function prependPathEntry(pathValue, entry) {
1295
+ const parts = (pathValue ?? DEFAULT_SANDBOX_PATH).split(":").filter(Boolean);
1296
+ return [entry, ...parts.filter((part) => part !== entry)].join(":");
1207
1297
  }
1208
1298
  function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, options = {}) {
1209
1299
  const environment = {
@@ -1215,18 +1305,35 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
1215
1305
  if (settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local") {
1216
1306
  environment.HOME ??= descriptor.workspaceRoot;
1217
1307
  }
1218
- environment.OPENGENI_GIT_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/git-token`;
1308
+ const provisionedGitHelperBackend = settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local" && settings.sandboxBackend !== "selfhosted";
1309
+ if (provisionedGitHelperBackend) {
1310
+ const home = environment.HOME ?? descriptor.workspaceRoot;
1311
+ environment.OPENGENI_GIT_CREDENTIALS_DIR ??= `${home}/.opengeni/git-credentials`;
1312
+ environment.OPENGENI_GIT_TOKEN_FILE ??= `${home}/.opengeni/git-token`;
1313
+ environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
1314
+ environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
1315
+ }
1219
1316
  if (settings.toolspaceEnabled) {
1220
1317
  environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
1221
1318
  if (options.workspaceId) {
1222
- environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(settings, options.workspaceId);
1319
+ environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
1320
+ settings,
1321
+ options.workspaceId
1322
+ );
1223
1323
  }
1224
1324
  }
1225
1325
  return environment;
1226
1326
  }
1227
1327
  function hasGitHubRepositorySelection(resources) {
1228
1328
  const positive = (value) => typeof value === "number" && Number.isInteger(value) && value > 0 || typeof value === "string" && /^\d+$/.test(value) && Number(value) > 0;
1229
- return resources.some((resource) => resource.kind === "repository" && positive(resource.githubInstallationId) && positive(resource.githubRepositoryId));
1329
+ return resources.some(
1330
+ (resource) => resource.kind === "repository" && (positive(resource.githubInstallationId) && positive(resource.githubRepositoryId) || resource.provider === "github" && positive(resource.installationId) && positive(resource.repositoryId))
1331
+ );
1332
+ }
1333
+ function hasGitCredentialRepositorySelection(resources) {
1334
+ return resources.some(
1335
+ (resource) => resource.kind === "repository" && (resource.provider === "github" || resource.provider === "gitlab" || resource.provider === "azure_devops" || hasGitHubRepositorySelection([resource]))
1336
+ );
1230
1337
  }
1231
1338
  function applyGitAuthPointerEnvironment(environment, identity) {
1232
1339
  environment.GIT_ASKPASS = `${environment.HOME ?? "/workspace"}/.opengeni/askpass`;
@@ -1281,10 +1388,14 @@ function sandboxLifecycleHookIds(settings) {
1281
1388
  return uniqueValues(ids);
1282
1389
  }
1283
1390
  function sandboxPreparationProfileNames(settings) {
1284
- const profiles = splitCsv(settings.sandboxPreparationProfiles).map((value) => value.toLowerCase());
1391
+ const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
1392
+ (value) => value.toLowerCase()
1393
+ );
1285
1394
  if (profiles.includes("none")) {
1286
1395
  if (profiles.length > 1) {
1287
- throw new Error("OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles");
1396
+ throw new Error(
1397
+ "OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles"
1398
+ );
1288
1399
  }
1289
1400
  return ["none"];
1290
1401
  }
@@ -1316,7 +1427,7 @@ function parseMcpServers(raw) {
1316
1427
  return parsed;
1317
1428
  } catch (error) {
1318
1429
  const message = error instanceof Error ? error.message : String(error);
1319
- throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`);
1430
+ throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, { cause: error });
1320
1431
  }
1321
1432
  }
1322
1433
  function parseModelPricingJson(raw) {
@@ -1328,7 +1439,7 @@ function parseModelPricingJson(raw) {
1328
1439
  parsed = JSON.parse(raw);
1329
1440
  } catch (error) {
1330
1441
  const message = error instanceof Error ? error.message : String(error);
1331
- throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`);
1442
+ throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`, { cause: error });
1332
1443
  }
1333
1444
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1334
1445
  throw new Error("OPENGENI_MODEL_PRICING_JSON must be a JSON object keyed by model name");
@@ -1351,19 +1462,28 @@ function parseSandboxWarmRateJson(raw) {
1351
1462
  parsed = JSON.parse(raw);
1352
1463
  } catch (error) {
1353
1464
  const message = error instanceof Error ? error.message : String(error);
1354
- throw new Error(`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be valid JSON: ${message}`);
1465
+ throw new Error(
1466
+ `OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be valid JSON: ${message}`,
1467
+ { cause: error }
1468
+ );
1355
1469
  }
1356
1470
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1357
- throw new Error("OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be a JSON object keyed by backend name");
1471
+ throw new Error(
1472
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be a JSON object keyed by backend name"
1473
+ );
1358
1474
  }
1359
1475
  const out = {};
1360
1476
  for (const [backend, value] of Object.entries(parsed)) {
1361
1477
  if (!backend.trim()) {
1362
- throw new Error("OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name");
1478
+ throw new Error(
1479
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name"
1480
+ );
1363
1481
  }
1364
1482
  const rate = typeof value === "number" ? value : Number(value);
1365
1483
  if (!Number.isFinite(rate) || rate < 0) {
1366
- throw new Error(`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON rate for ${backend} must be a non-negative number`);
1484
+ throw new Error(
1485
+ `OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON rate for ${backend} must be a non-negative number`
1486
+ );
1367
1487
  }
1368
1488
  out[backend] = rate;
1369
1489
  }
@@ -1382,7 +1502,9 @@ function parseModelProvidersJson(raw) {
1382
1502
  parsed = JSON.parse(raw);
1383
1503
  } catch (error) {
1384
1504
  const message = error instanceof Error ? error.message : String(error);
1385
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`);
1505
+ throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`, {
1506
+ cause: error
1507
+ });
1386
1508
  }
1387
1509
  if (!Array.isArray(parsed)) {
1388
1510
  throw new Error("OPENGENI_MODEL_PROVIDERS_JSON must be a JSON array of providers");
@@ -1390,7 +1512,9 @@ function parseModelProvidersJson(raw) {
1390
1512
  return parsed.map((entry, index) => {
1391
1513
  const result = RegistryProviderSchema.safeParse(entry);
1392
1514
  if (!result.success) {
1393
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`);
1515
+ throw new Error(
1516
+ `OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
1517
+ );
1394
1518
  }
1395
1519
  return result.data;
1396
1520
  });
@@ -1404,10 +1528,14 @@ function parseIntegrationsOauthClientsJson(raw) {
1404
1528
  parsed = JSON.parse(raw);
1405
1529
  } catch (error) {
1406
1530
  const message = error instanceof Error ? error.message : String(error);
1407
- throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`);
1531
+ throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`, {
1532
+ cause: error
1533
+ });
1408
1534
  }
1409
1535
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1410
- throw new Error("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be a JSON object keyed by authorization-server issuer or URL");
1536
+ throw new Error(
1537
+ "OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be a JSON object keyed by authorization-server issuer or URL"
1538
+ );
1411
1539
  }
1412
1540
  const out = {};
1413
1541
  for (const [key, value] of Object.entries(parsed)) {
@@ -1416,7 +1544,9 @@ function parseIntegrationsOauthClientsJson(raw) {
1416
1544
  }
1417
1545
  const result = IntegrationOAuthClientConfigSchema.safeParse(value);
1418
1546
  if (!result.success) {
1419
- throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`);
1547
+ throw new Error(
1548
+ `OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`
1549
+ );
1420
1550
  }
1421
1551
  out[key] = result.data;
1422
1552
  }
@@ -1431,7 +1561,9 @@ function parseStaticUsageLimitsJson(raw) {
1431
1561
  parsed = JSON.parse(raw);
1432
1562
  } catch (error) {
1433
1563
  const message = error instanceof Error ? error.message : String(error);
1434
- throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`);
1564
+ throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`, {
1565
+ cause: error
1566
+ });
1435
1567
  }
1436
1568
  return StaticUsageLimits.parse(parsed);
1437
1569
  }
@@ -1444,7 +1576,9 @@ function parseStaticEntitlementsJson(raw) {
1444
1576
  parsed = JSON.parse(raw);
1445
1577
  } catch (error) {
1446
1578
  const message = error instanceof Error ? error.message : String(error);
1447
- throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`);
1579
+ throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`, {
1580
+ cause: error
1581
+ });
1448
1582
  }
1449
1583
  return Entitlements.parse(parsed);
1450
1584
  }
@@ -1491,20 +1625,32 @@ function ensureBuiltInMcpServers(settings) {
1491
1625
  // safe to cache / leave as-is.)
1492
1626
  cacheToolsList: false
1493
1627
  },
1494
- ...hasFiles ? [] : [{
1495
- id: "files",
1496
- name: "Files",
1497
- url: firstPartyMcpUrl,
1498
- allowedTools: ["files_get_download_url"],
1499
- cacheToolsList: true
1500
- }],
1501
- ...hasDocs ? [] : [{
1502
- id: "docs",
1503
- name: "Document Search",
1504
- url: firstPartyDocsMcpUrl,
1505
- allowedTools: ["search_documents", "fetch_document_chunk", "list_document_bases", "knowledge_search", "knowledge_fetch", "memory_search", "memory_propose"],
1506
- cacheToolsList: false
1507
- }],
1628
+ ...hasFiles ? [] : [
1629
+ {
1630
+ id: "files",
1631
+ name: "Files",
1632
+ url: firstPartyMcpUrl,
1633
+ allowedTools: ["files_get_download_url"],
1634
+ cacheToolsList: true
1635
+ }
1636
+ ],
1637
+ ...hasDocs ? [] : [
1638
+ {
1639
+ id: "docs",
1640
+ name: "Document Search",
1641
+ url: firstPartyDocsMcpUrl,
1642
+ allowedTools: [
1643
+ "search_documents",
1644
+ "fetch_document_chunk",
1645
+ "list_document_bases",
1646
+ "knowledge_search",
1647
+ "knowledge_fetch",
1648
+ "memory_search",
1649
+ "memory_propose"
1650
+ ],
1651
+ cacheToolsList: false
1652
+ }
1653
+ ],
1508
1654
  ...existing
1509
1655
  ];
1510
1656
  }
@@ -1534,40 +1680,58 @@ function validateSettings(settings) {
1534
1680
  }
1535
1681
  if (settings.productAccessMode === "managed") {
1536
1682
  if (!settings.publicBaseUrl) {
1537
- throw new Error("OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1683
+ throw new Error(
1684
+ "OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1685
+ );
1538
1686
  }
1539
1687
  if (!settings.betterAuthSecret) {
1540
- throw new Error("OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1688
+ throw new Error(
1689
+ "OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1690
+ );
1541
1691
  }
1542
1692
  if (!settings.delegationSecret) {
1543
- throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1693
+ throw new Error(
1694
+ "OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1695
+ );
1544
1696
  }
1545
1697
  if (!["local", "test"].includes(settings.environment) && !settings.resendApiKey) {
1546
1698
  throw new Error("OPENGENI_RESEND_API_KEY is required for managed mode outside local/test");
1547
1699
  }
1548
1700
  if (!["local", "test"].includes(settings.environment) && !settings.environmentsEncryptionKey) {
1549
- throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test");
1701
+ throw new Error(
1702
+ "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test"
1703
+ );
1550
1704
  }
1551
1705
  }
1552
1706
  environmentsEncryptionKeyBytes(settings);
1553
1707
  if (settings.integrationsEnabled) {
1554
1708
  if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
1555
- throw new Error("OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_INTEGRATIONS_ENABLED=true and OPENGENI_PRODUCT_ACCESS_MODE=managed");
1709
+ throw new Error(
1710
+ "OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_INTEGRATIONS_ENABLED=true and OPENGENI_PRODUCT_ACCESS_MODE=managed"
1711
+ );
1556
1712
  }
1557
1713
  if (settings.publicBaseUrl && !settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
1558
- throw new Error("OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test");
1714
+ throw new Error(
1715
+ "OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
1716
+ );
1559
1717
  }
1560
1718
  if (!settings.integrationsStateSecret && !["local", "test"].includes(settings.environment)) {
1561
- throw new Error("OPENGENI_INTEGRATIONS_STATE_SECRET is required when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test");
1719
+ throw new Error(
1720
+ "OPENGENI_INTEGRATIONS_STATE_SECRET is required when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
1721
+ );
1562
1722
  }
1563
1723
  }
1564
1724
  parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
1565
1725
  if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
1566
- throw new Error("OPENGENI_PRODUCT_ACCESS_MODE=configured requires OPENGENI_DELEGATION_SECRET or OPENGENI_AUTH_REQUIRED=true outside local/test");
1726
+ throw new Error(
1727
+ "OPENGENI_PRODUCT_ACCESS_MODE=configured requires OPENGENI_DELEGATION_SECRET or OPENGENI_AUTH_REQUIRED=true outside local/test"
1728
+ );
1567
1729
  }
1568
1730
  if (settings.billingMode === "stripe") {
1569
1731
  if (!settings.stripeSecretKey || !settings.stripeWebhookSecret) {
1570
- throw new Error("OPENGENI_STRIPE_SECRET_KEY and OPENGENI_STRIPE_WEBHOOK_SECRET are required when OPENGENI_BILLING_MODE=stripe");
1732
+ throw new Error(
1733
+ "OPENGENI_STRIPE_SECRET_KEY and OPENGENI_STRIPE_WEBHOOK_SECRET are required when OPENGENI_BILLING_MODE=stripe"
1734
+ );
1571
1735
  }
1572
1736
  }
1573
1737
  if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
@@ -1577,13 +1741,17 @@ function validateSettings(settings) {
1577
1741
  const pricing = configuredModelPricing(settings);
1578
1742
  const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
1579
1743
  if (missing.length > 0) {
1580
- throw new Error(`Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`);
1744
+ throw new Error(
1745
+ `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
1746
+ );
1581
1747
  }
1582
1748
  }
1583
1749
  if (settings.usageLimitsMode === "static") {
1584
1750
  const limits = configuredStaticUsageLimits(settings);
1585
1751
  if (Object.keys(limits).length === 0) {
1586
- throw new Error("OPENGENI_STATIC_USAGE_LIMITS_JSON must define at least one cap when OPENGENI_USAGE_LIMITS_MODE=static");
1752
+ throw new Error(
1753
+ "OPENGENI_STATIC_USAGE_LIMITS_JSON must define at least one cap when OPENGENI_USAGE_LIMITS_MODE=static"
1754
+ );
1587
1755
  }
1588
1756
  } else {
1589
1757
  parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1591,7 +1759,9 @@ function validateSettings(settings) {
1591
1759
  if (settings.entitlementsMode === "static") {
1592
1760
  const entitlements = parseStaticEntitlementsJson(settings.staticEntitlementsJson);
1593
1761
  if (Object.keys(entitlements).length === 0) {
1594
- throw new Error("OPENGENI_STATIC_ENTITLEMENTS_JSON must define at least one feature when OPENGENI_ENTITLEMENTS_MODE=static");
1762
+ throw new Error(
1763
+ "OPENGENI_STATIC_ENTITLEMENTS_JSON must define at least one feature when OPENGENI_ENTITLEMENTS_MODE=static"
1764
+ );
1595
1765
  }
1596
1766
  } else {
1597
1767
  parseStaticEntitlementsJson(settings.staticEntitlementsJson);
@@ -1601,7 +1771,9 @@ function validateSettings(settings) {
1601
1771
  }
1602
1772
  if (settings.openaiProvider === "azure") {
1603
1773
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiEndpoint) {
1604
- throw new Error("Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT");
1774
+ throw new Error(
1775
+ "Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT"
1776
+ );
1605
1777
  }
1606
1778
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiDeployment) {
1607
1779
  throw new Error("Azure OpenAI endpoint mode requires OPENGENI_AZURE_OPENAI_DEPLOYMENT");
@@ -1614,52 +1786,76 @@ function validateSettings(settings) {
1614
1786
  }
1615
1787
  }
1616
1788
  if (Boolean(settings.modalTokenId) !== Boolean(settings.modalTokenSecret)) {
1617
- throw new Error("OPENGENI_MODAL_TOKEN_ID and OPENGENI_MODAL_TOKEN_SECRET must both be set or both omitted");
1789
+ throw new Error(
1790
+ "OPENGENI_MODAL_TOKEN_ID and OPENGENI_MODAL_TOKEN_SECRET must both be set or both omitted"
1791
+ );
1618
1792
  }
1619
1793
  for (const required of SANDBOX_REQUIRED_ENV[settings.sandboxBackend] ?? []) {
1620
1794
  const value = settings[required.field];
1621
1795
  if (value === void 0 || value === null || typeof value === "string" && value.trim().length === 0) {
1622
- throw new Error(`${required.env} is required when OPENGENI_SANDBOX_BACKEND=${settings.sandboxBackend}`);
1796
+ throw new Error(
1797
+ `${required.env} is required when OPENGENI_SANDBOX_BACKEND=${settings.sandboxBackend}`
1798
+ );
1623
1799
  }
1624
1800
  }
1625
1801
  if (settings.objectStorageBackend === "s3-compatible" || settings.objectStorageBackend === "aws-s3") {
1626
1802
  if (Boolean(settings.objectStorageAccessKeyId) !== Boolean(settings.objectStorageSecretAccessKey)) {
1627
- throw new Error("OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted");
1803
+ throw new Error(
1804
+ "OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
1805
+ );
1628
1806
  }
1629
1807
  if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
1630
- throw new Error("S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY");
1808
+ throw new Error(
1809
+ "S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
1810
+ );
1631
1811
  }
1632
1812
  if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
1633
- throw new Error("S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings");
1813
+ throw new Error(
1814
+ "S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
1815
+ );
1634
1816
  }
1635
1817
  if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
1636
- throw new Error("S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings");
1818
+ throw new Error(
1819
+ "S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
1820
+ );
1637
1821
  }
1638
1822
  } else if (settings.objectStorageBackend === "azure-blob") {
1639
1823
  if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
1640
- throw new Error("Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings");
1824
+ throw new Error(
1825
+ "Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
1826
+ );
1641
1827
  }
1642
1828
  if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
1643
- throw new Error("Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings");
1829
+ throw new Error(
1830
+ "Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
1831
+ );
1644
1832
  }
1645
1833
  const hasConnectionString = Boolean(settings.objectStorageAzureConnectionString);
1646
1834
  const hasSharedKey = Boolean(settings.objectStorageAzureAccountName) && Boolean(settings.objectStorageAzureAccountKey);
1647
1835
  if (!hasConnectionString && !hasSharedKey) {
1648
- throw new Error("Azure Blob storage requires OPENGENI_OBJECT_STORAGE_AZURE_CONNECTION_STRING or OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_NAME plus OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_KEY");
1836
+ throw new Error(
1837
+ "Azure Blob storage requires OPENGENI_OBJECT_STORAGE_AZURE_CONNECTION_STRING or OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_NAME plus OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_KEY"
1838
+ );
1649
1839
  }
1650
1840
  } else {
1651
1841
  if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
1652
- throw new Error("GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings");
1842
+ throw new Error(
1843
+ "GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
1844
+ );
1653
1845
  }
1654
1846
  if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
1655
- throw new Error("GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings");
1847
+ throw new Error(
1848
+ "GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
1849
+ );
1656
1850
  }
1657
1851
  if (settings.objectStorageGcsCredentialsJson) {
1658
1852
  parseGcsCredentialsJson(settings.objectStorageGcsCredentialsJson);
1659
1853
  }
1660
1854
  }
1661
1855
  if (settings.documentChunkOverlap >= settings.documentChunkSize) {
1662
- throw new Error("OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE");
1856
+ throw new Error(
1857
+ "OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE"
1858
+ );
1663
1859
  }
1664
1860
  parseExposedPorts(settings.dockerExposedPorts);
1665
1861
  sandboxEnvironmentVariableNames(settings);
@@ -1709,14 +1905,20 @@ function validateSettings(settings) {
1709
1905
  const providerIds = /* @__PURE__ */ new Set();
1710
1906
  for (const provider of registryProviders) {
1711
1907
  if (provider.id === builtinId) {
1712
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`);
1908
+ throw new Error(
1909
+ `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
1910
+ );
1713
1911
  }
1714
1912
  if (providerIds.has(provider.id)) {
1715
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`);
1913
+ throw new Error(
1914
+ `OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`
1915
+ );
1716
1916
  }
1717
1917
  providerIds.add(provider.id);
1718
1918
  if (!resolveProviderApiKey(provider)) {
1719
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`);
1919
+ throw new Error(
1920
+ `OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
1921
+ );
1720
1922
  }
1721
1923
  }
1722
1924
  }
@@ -1794,7 +1996,9 @@ function parseGcsCredentialsJson(raw) {
1794
1996
  return JSON.parse(raw);
1795
1997
  } catch (error) {
1796
1998
  const message = error instanceof Error ? error.message : String(error);
1797
- throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`);
1999
+ throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`, {
2000
+ cause: error
2001
+ });
1798
2002
  }
1799
2003
  }
1800
2004
  function delay(ms) {
@@ -1809,6 +2013,7 @@ export {
1809
2013
  RegistryProviderKind,
1810
2014
  SANDBOX_REQUIRED_ENV,
1811
2015
  applyGitAuthPointerEnvironment,
2016
+ builtinProviderId,
1812
2017
  calculateModelUsageCostMicros,
1813
2018
  collectGitIdentityEnvironment,
1814
2019
  collectSandboxEnvironment,
@@ -1820,7 +2025,6 @@ export {
1820
2025
  configuredProviders,
1821
2026
  configuredStaticUsageLimits,
1822
2027
  contextInputBudgetTokens,
1823
- contextServerCompactThreshold,
1824
2028
  dbSearchPath,
1825
2029
  defaultModelPricing,
1826
2030
  effectiveModalIdleTimeoutSeconds,
@@ -1828,6 +2032,7 @@ export {
1828
2032
  firstPartyMcpBaseUrl,
1829
2033
  firstPartyMcpWorkspaceUrl,
1830
2034
  getSettings,
2035
+ hasGitCredentialRepositorySelection,
1831
2036
  hasGitHubRepositorySelection,
1832
2037
  parseExposedPorts,
1833
2038
  parseIntegrationsOauthClientsJson,
@@ -1837,8 +2042,8 @@ export {
1837
2042
  parseSandboxWarmRateJson,
1838
2043
  parseStaticEntitlementsJson,
1839
2044
  parseStaticUsageLimitsJson,
2045
+ policyProviderIdForModel,
1840
2046
  requiredSandboxEnvForBackend,
1841
- resolveContextCompactionMode,
1842
2047
  resolveEnrollmentSigningSecret,
1843
2048
  resolveModelProvider,
1844
2049
  resolveNatsCalloutConfig,
@@ -1851,6 +2056,7 @@ export {
1851
2056
  sandboxLifecycleHookIds,
1852
2057
  sandboxPreparationProfiles,
1853
2058
  sandboxWarmRateMicrosPerSecond,
2059
+ settingsWithResolvedModelContext,
1854
2060
  stableSandboxEnvironmentForRun,
1855
2061
  startupRetryOptions,
1856
2062
  streamTokenDegraded