@opengeni/config 0.4.0 → 0.5.0

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(" ");
@@ -146,62 +146,42 @@ var SettingsSchema = z.object({
146
146
  // it then acts as a hard ceiling that per-goal overrides can only lower.
147
147
  goalMaxAutoContinuations: z.coerce.number().int().positive().optional(),
148
148
  goalNoProgressLimit: z.coerce.number().int().positive().default(3),
149
+ // Temporary safety valve: spawned child sessions still run and retain their
150
+ // own durable events/goals, but terminal child episodes do not create parent
151
+ // system updates or inference work unless explicitly enabled.
152
+ childCompletionParentWakeEnabled: EnvBoolean.default(false),
149
153
  // Per-segment ceiling on agent loop turns (model calls) within a single
150
154
  // session turn. Effectively unbounded by default for the same reason as
151
155
  // above; the graceful max-turns valve (idle + goal continuation, never a
152
156
  // session failure) remains as inert safety should a deployment set a cap.
153
157
  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.
158
+ // The model family's real context window in tokens. OpenGeni always performs
159
+ // one durable, portable plaintext compaction transition; there is no
160
+ // provider/server/off mode ladder.
177
161
  contextWindowTokens: z.coerce.number().int().positive().default(105e4),
162
+ // Optional model-catalog effective input ceiling. Codex models expose this as
163
+ // raw context_window * effective_context_window_percent; when absent, retain
164
+ // the deployment-level window-minus-reserved-output behavior.
165
+ contextEffectiveWindowTokens: z.coerce.number().int().positive().optional(),
178
166
  // 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) => {
167
+ // Defaults to 90%: compact as late as possible retained context beats early
168
+ // headroom now that per-model windows are declared honestly (input-effective,
169
+ // empirically measured), and the fail-closed reactive compact-on-reject path
170
+ // absorbs any overshoot as one retried call rather than a dead session.
171
+ // Clamped to [0.3, 0.9] so deployments can tune the trigger without
172
+ // accidentally disabling compaction.
173
+ contextCompactionThresholdRatio: z.coerce.number().default(0.9).transform((value) => {
183
174
  if (!Number.isFinite(value)) {
184
- return 0.6;
175
+ return 0.9;
185
176
  }
186
177
  return Math.min(0.9, Math.max(0.3, value));
187
178
  }),
188
179
  // Tokens reserved for model output; subtracted from the window to get the
189
180
  // usable input budget B = contextWindowTokens - contextReservedOutputTokens.
190
181
  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),
182
+ // Model-catalog auto-compact limit. When present it is clamped to
183
+ // 90% of the raw window, matching Codex core's auto_compact_token_limit().
184
+ contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
205
185
  authRequired: EnvBoolean.default(false),
206
186
  accessKey: z.string().optional(),
207
187
  authAllowHealth: EnvBoolean.default(true),
@@ -214,8 +194,8 @@ var SettingsSchema = z.object({
214
194
  openaiProvider: z.enum(["openai", "azure"]).default("openai"),
215
195
  openaiApiKey: z.string().optional(),
216
196
  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"),
197
+ openaiModel: z.string().default("gpt-5.6-sol"),
198
+ openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
219
199
  modelPricingJson: z.string().default("{}"),
220
200
  // Extra (non-built-in) model providers, declared by the host as a JSON
221
201
  // provider registry. Each entry carries its own base URL, API key, wire API
@@ -237,6 +217,11 @@ var SettingsSchema = z.object({
237
217
  // tool that BM25-discloses only the matching connectors. Default OFF — a codex
238
218
  // turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
239
219
  codexToolSearchEnabled: EnvBoolean.default(false),
220
+ // OPE-21 atomic, workspace-local credential allocation. Default OFF is a
221
+ // deliberate rolling-deploy fence: migrate + roll every worker first, then
222
+ // enable. Turning it off restores the legacy sticky selector without a schema
223
+ // rollback; the additive lease table/cursor columns become inert.
224
+ codexCredentialLeasingEnabled: EnvBoolean.default(false),
240
225
  // Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
241
226
  // rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
242
227
  // match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
@@ -274,7 +259,7 @@ var SettingsSchema = z.object({
274
259
  // the non-bypassable CORE at AGENT_INSTRUCTIONS_CORE_PLACEHOLDER (or appends
275
260
  // it when the template omits the marker), and uses the result as the agent's
276
261
  // instructions. Defaulting to DEFAULT_AGENT_INSTRUCTIONS keeps the composed
277
- // default byte-identical to the historical hardcoded preamble.
262
+ // default pinned by runtime tests.
278
263
  agentInstructionsTemplate: z.string().default(DEFAULT_AGENT_INSTRUCTIONS),
279
264
  azureOpenaiBaseUrl: z.string().optional(),
280
265
  azureOpenaiEndpoint: z.string().optional(),
@@ -379,6 +364,13 @@ var SettingsSchema = z.object({
379
364
  // recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
380
365
  recordingEnabled: EnvBoolean.default(true),
381
366
  recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
367
+ // Workbench v2 turn-end workspace capture (dossier §10.1). When on, the turn
368
+ // activity probes the box's changed files off the live box at turn end and
369
+ // persists a capture revision (blobs in @opengeni/storage) so the workbench
370
+ // paints cold/offline sessions with zero machine round-trips. Best-effort and
371
+ // fully behind this flag: off ⇒ capture is skipped and reads fall back to the
372
+ // live/wake path (status-quo behavior). Default on; explicit per environment.
373
+ workspaceCaptureEnabled: EnvBoolean.default(true),
382
374
  recordingFramerate: z.coerce.number().int().positive().default(15),
383
375
  recordingMaxSeconds: z.coerce.number().int().positive().default(600),
384
376
  recordingMaxBytes: z.coerce.number().int().positive().default(268435456),
@@ -435,6 +427,19 @@ var SettingsSchema = z.object({
435
427
  // EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
436
428
  // turn the flag ON the moment anyone set the env var to disable it).
437
429
  sandboxOwnershipEnabled: EnvBoolean.default(false),
430
+ // --- lazy sandbox provisioning rollout flag, default OFF ---
431
+ // Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
432
+ // property of the owned path — the SDK never creates/resumes an injected session,
433
+ // so we control when the box is established). When TRUE, a turn does NOT provision
434
+ // its box at turn start: the lease acquire + resume-by-id + hooks + downloads +
435
+ // heartbeat + recording are deferred to an in-process single-flight provisioner
436
+ // that runs the FIRST time a sandbox op is dispatched (via the routing proxy's
437
+ // resolveActiveBackend). A turn whose model never calls a sandbox-backed tool ends
438
+ // with NO lease row and ZERO warm-seconds. When FALSE (or ownership off) the turn
439
+ // provisions eagerly exactly as today — byte-for-byte. EnvBoolean (NOT
440
+ // z.coerce.boolean(), which coerces "false" -> true and would turn the flag ON the
441
+ // moment anyone set the env var to disable it).
442
+ sandboxLazyProvisionEnabled: EnvBoolean.default(false),
438
443
  // --- bring-your-own-compute (selfhosted 11th backend) rollout flag, default OFF ---
439
444
  // The keystone flag for the whole selfhosted feature (the enrollment device-flow,
440
445
  // the NATS control plane, the relay stream tier). When FALSE the enrollment routes
@@ -443,6 +448,11 @@ var SettingsSchema = z.object({
443
448
  // z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
444
449
  // the deploy-staging IaC secret/configmap pattern (dossier §17/§25.1).
445
450
  sandboxSelfhostedEnabled: EnvBoolean.default(false),
451
+ // Gates the op-stream (streaming exec) transport to Connected Machines. The
452
+ // runner must ALSO advertise Capabilities.op_stream; default off, and legacy
453
+ // request/reply exec is the permanent fallback. EnvBoolean (NOT
454
+ // z.coerce.boolean(), which coerces "false" -> true).
455
+ agentOpStreamEnabled: EnvBoolean.default(false),
446
456
  // The HMAC secret the control plane signs the enrollment bearer credential with
447
457
  // (the `oge_` envelope the agent presents back to the control plane). Optional:
448
458
  // when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
@@ -499,6 +509,25 @@ var SettingsSchema = z.object({
499
509
  // bus connects anonymously (local dev / a NATS with no auth_callout).
500
510
  selfhostedNatsControlUser: z.string().optional(),
501
511
  selfhostedNatsControlPassword: z.string().optional(),
512
+ // --- selfhosted (Connected Machine) control/exec op deadlines ---------------
513
+ // The control plane splits its op deadline in two. CONTROL ops (ping / fs / git /
514
+ // desktop / pty) must stay responsive so a machine's liveness is never masked by a
515
+ // slow op, so they use the short control timeout. EXEC gets its OWN, larger budget:
516
+ // a real command (compile, test run, dependency install) routinely outlives the
517
+ // control timeout, and before the split a long command was killed at the ~30s
518
+ // control wall. The agent kills the exec child at this deadline; the wire waits
519
+ // slightly longer (SELFHOSTED_EXEC_REPLY_GRACE_MS) for the typed timed-out reply.
520
+ //
521
+ // The exec default is a DELIBERATELY MODEST 2min (not 5): the agent-side admission
522
+ // pool is (until a later agent release) a FLAT 8 permits with no per-class split,
523
+ // so 8 slow execs holding a permit for 5 minutes would blanket-DRAIN every fs/git
524
+ // op — shipping the amplifier before the class-aware-admission fix. 2min still
525
+ // clears the large majority of the observed >30s exec tail; genuinely long jobs run
526
+ // in the background (see the exec-deadline hint) or raise the knob per deployment.
527
+ // Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 2min) and
528
+ // OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS (default 30s).
529
+ sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().positive().default(12e4),
530
+ sandboxSelfhostedControlTimeoutMs: z.coerce.number().int().positive().default(3e4),
502
531
  // --- sandbox lease cadences (cadence invariant validated at boot below) ---
503
532
  // reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
504
533
  // box idle timeout (effectiveModalIdleTimeoutSeconds, which defaults to the hard
@@ -517,6 +546,22 @@ var SettingsSchema = z.object({
517
546
  // fresh EMPTY box; lower it to trade warm cost for a snappier reclaim. Knob:
518
547
  // OPENGENI_SANDBOX_IDLE_GRACE_MS.
519
548
  sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
549
+ // MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
550
+ // reaper's drain-persist only protects boxes the reaper itself kills; a box
551
+ // that dies any other way (Modal's hard creation-time timeout on a session
552
+ // busy past it, provider OOM/infra death) loses everything since the last
553
+ // clean drain. While a turn holds the box, the turn heartbeat and turn-end
554
+ // both take a snapshot when at least this interval has passed since the last
555
+ // one (same epoch-fenced fold-onto-lease seam as the drain), bounding the
556
+ // worst-case loss of ANY unclean box death to this window. 0 disables.
557
+ // Knob: OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS. Default 15min.
558
+ sandboxSnapshotIntervalMs: z.coerce.number().int().min(0).default(9e5),
559
+ // Maximum time a best-effort /workspace snapshot capture may hold turn/reaper
560
+ // cleanup. A hung provider snapshot must never pin a lease holder, block
561
+ // graceful shutdown, or become permission to GC an older archive. Timeout is
562
+ // treated exactly like a failed best-effort snapshot. Knob:
563
+ // OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
564
+ sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
520
565
  // expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
521
566
  // single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
522
567
  // window a cold->warming spawner has to commit warm before a reaper resets it.
@@ -526,6 +571,11 @@ var SettingsSchema = z.object({
526
571
  // (a liveness/reaper cadence), this bounds how long one turn waits for capacity
527
572
  // or provider creation before surfacing a clear turn.failed error.
528
573
  sandboxWarmingTimeoutMs: z.coerce.number().int().positive().default(6e5),
574
+ // Rig setup-script budget (M3): the wall-clock timeout the rig-setup lifecycle
575
+ // hook runs its script under, distinct from the 120s per-command lifecycle
576
+ // default (a rig may compile/install heavy tooling on first cold create).
577
+ // Env: OPENGENI_RIG_SETUP_TIMEOUT_MS. Default 10min.
578
+ rigSetupTimeoutMs: z.coerce.number().int().positive().default(6e5),
529
579
  // --- sandbox warm-time billing (P2.1) ---
530
580
  // Per-backend warm rate (usd_micros/sec), like modelPricingJson: an empty {}
531
581
  // means warm-cost is not debited (warm-seconds are still metered for audit).
@@ -586,30 +636,32 @@ var SettingsSchema = z.object({
586
636
  stripePublishableKey: z.string().optional(),
587
637
  stripeWebhookSecret: z.string().optional(),
588
638
  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([])
639
+ mcpServers: z.array(
640
+ z.object({
641
+ id: z.string().min(1).regex(registryId),
642
+ name: z.string().min(1).optional(),
643
+ url: z.string().url(),
644
+ allowedTools: z.array(z.string().min(1)).optional(),
645
+ timeoutMs: z.number().int().positive().optional(),
646
+ cacheToolsList: z.boolean().default(false),
647
+ /**
648
+ * Human-approval policy for this server's tools, overlaid per-run from a
649
+ * session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
650
+ * tools require approval; a string[] = only the listed UNPREFIXED tool
651
+ * names do; absent = auto-run (the historical default). Enforced in the
652
+ * runtime by attaching `needsApproval` to the matching MCP tools.
653
+ */
654
+ requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
655
+ /**
656
+ * Extra request headers sent to this MCP server (credential injection
657
+ * for workspace-enabled capability MCPs). Populated at runtime from
658
+ * encrypted capability-installation credentials; do not put secrets in
659
+ * OPENGENI_MCP_SERVERS.
660
+ */
661
+ headers: z.record(z.string(), z.string()).optional(),
662
+ connectionRef: McpServerConnectionRefSchema.optional()
663
+ })
664
+ ).default([])
613
665
  });
614
666
  var ModelPricingSchema = z.object({
615
667
  inputMicrosPerMillionTokens: z.number().int().nonnegative(),
@@ -625,6 +677,8 @@ var RegistryModelSchema = z.object({
625
677
  label: z.string().min(1).optional(),
626
678
  // display name; defaults to id
627
679
  contextWindowTokens: z.number().int().positive().optional(),
680
+ effectiveContextWindowTokens: z.number().int().positive().optional(),
681
+ autoCompactTokenLimit: z.number().int().positive().optional(),
628
682
  reasoningEffort: z.boolean().optional(),
629
683
  // model accepts a reasoning-effort control
630
684
  hostedWebSearch: z.boolean().optional(),
@@ -653,12 +707,24 @@ var IntegrationOAuthClientConfigSchema = z.object({
653
707
  tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
654
708
  });
655
709
  var defaultModelPricing = {
656
- "gpt-5.5": {
710
+ "gpt-5.6-sol": {
657
711
  inputMicrosPerMillionTokens: 5e6,
658
712
  cachedInputMicrosPerMillionTokens: 5e5,
659
713
  outputMicrosPerMillionTokens: 3e7,
660
714
  marginBps: 2500
661
715
  },
716
+ "gpt-5.6-terra": {
717
+ inputMicrosPerMillionTokens: 25e5,
718
+ cachedInputMicrosPerMillionTokens: 25e4,
719
+ outputMicrosPerMillionTokens: 15e6,
720
+ marginBps: 2500
721
+ },
722
+ "gpt-5.6-luna": {
723
+ inputMicrosPerMillionTokens: 1e6,
724
+ cachedInputMicrosPerMillionTokens: 1e5,
725
+ outputMicrosPerMillionTokens: 6e6,
726
+ marginBps: 2500
727
+ },
662
728
  "gpt-5.4": {
663
729
  inputMicrosPerMillionTokens: 25e5,
664
730
  cachedInputMicrosPerMillionTokens: 25e4,
@@ -734,21 +800,11 @@ var SANDBOX_REQUIRED_ENV = {
734
800
  { field: "modalTokenId", env: "OPENGENI_MODAL_TOKEN_ID" },
735
801
  { field: "modalTokenSecret", env: "OPENGENI_MODAL_TOKEN_SECRET" }
736
802
  ],
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
- ],
803
+ daytona: [{ field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }],
804
+ runloop: [{ field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }],
805
+ e2b: [{ field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }],
806
+ blaxel: [{ field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }],
807
+ cloudflare: [{ field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }],
752
808
  vercel: [
753
809
  { field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
754
810
  { field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
@@ -780,7 +836,9 @@ function getSettings() {
780
836
  temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
781
837
  temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
782
838
  startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
783
- startupDependencyRetryInitialDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"),
839
+ startupDependencyRetryInitialDelayMs: optional(
840
+ "OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
841
+ ),
784
842
  startupDependencyRetryMaxDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_MAX_DELAY_MS"),
785
843
  observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
786
844
  observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
@@ -802,21 +860,19 @@ function getSettings() {
802
860
  environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
803
861
  integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
804
862
  integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
805
- integrationsAllowPrivateNetworkTargets: optional("OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"),
863
+ integrationsAllowPrivateNetworkTargets: optional(
864
+ "OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
865
+ ),
806
866
  integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
807
867
  goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
808
868
  goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
869
+ childCompletionParentWakeEnabled: optional("OPENGENI_CHILD_COMPLETION_PARENT_WAKE_ENABLED"),
809
870
  agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
810
- sessionHistorySource: optional("OPENGENI_SESSION_HISTORY_SOURCE"),
811
- contextCompactionMode: optional("OPENGENI_CONTEXT_COMPACTION_MODE"),
812
871
  contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
872
+ contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
813
873
  contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
814
874
  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"),
875
+ contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
820
876
  authRequired: optional("OPENGENI_AUTH_REQUIRED"),
821
877
  accessKey: optional("OPENGENI_ACCESS_KEY"),
822
878
  authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
@@ -835,6 +891,7 @@ function getSettings() {
835
891
  modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
836
892
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
837
893
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
894
+ codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
838
895
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
839
896
  codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
840
897
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
@@ -874,6 +931,7 @@ function getSettings() {
874
931
  computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
875
932
  computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
876
933
  recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
934
+ workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
877
935
  recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
878
936
  recordingFramerate: optional("OPENGENI_RECORDING_FRAMERATE"),
879
937
  recordingMaxSeconds: optional("OPENGENI_RECORDING_MAX_SECONDS"),
@@ -913,7 +971,9 @@ function getSettings() {
913
971
  vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
914
972
  vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
915
973
  sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
974
+ sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
916
975
  sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
976
+ agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
917
977
  enrollmentSigningSecret: optional("OPENGENI_ENROLLMENT_SIGNING_SECRET"),
918
978
  selfhostedNatsUrl: optional("OPENGENI_SELFHOSTED_NATS_URL"),
919
979
  selfhostedRelayUrl: optional("OPENGENI_SELFHOSTED_RELAY_URL"),
@@ -925,13 +985,20 @@ function getSettings() {
925
985
  selfhostedNatsCalloutPassword: optional("OPENGENI_SELFHOSTED_NATS_CALLOUT_PASSWORD"),
926
986
  selfhostedNatsControlUser: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_USER"),
927
987
  selfhostedNatsControlPassword: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_PASSWORD"),
988
+ sandboxSelfhostedExecTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS"),
989
+ sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
928
990
  sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
929
991
  sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
930
992
  sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
993
+ sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
994
+ sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
931
995
  sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
932
996
  sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
933
997
  sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
934
- sandboxWarmRateMicrosPerSecondJson: optional("OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"),
998
+ rigSetupTimeoutMs: optional("OPENGENI_RIG_SETUP_TIMEOUT_MS"),
999
+ sandboxWarmRateMicrosPerSecondJson: optional(
1000
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"
1001
+ ),
935
1002
  sandboxMaxWarmSecondsPerWorkspace: optional("OPENGENI_SANDBOX_MAX_WARM_SECONDS_PER_WORKSPACE"),
936
1003
  sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
937
1004
  sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
@@ -1027,8 +1094,7 @@ function configuredProviders(settings) {
1027
1094
  label: builtinProviderLabel(settings),
1028
1095
  kind: "api-key",
1029
1096
  api: "responses",
1030
- builtin: true,
1031
- compactionMode: resolveContextCompactionMode(settings)
1097
+ builtin: true
1032
1098
  };
1033
1099
  if (settings.openaiProvider === "azure") {
1034
1100
  builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
@@ -1037,28 +1103,41 @@ function configuredProviders(settings) {
1037
1103
  builtin.baseUrl = settings.openaiBaseUrl;
1038
1104
  builtin.apiKey = settings.openaiApiKey;
1039
1105
  }
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
- }));
1106
+ const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
1107
+ (provider) => ({
1108
+ id: provider.id,
1109
+ label: provider.label ?? provider.id,
1110
+ kind: provider.kind,
1111
+ api: provider.api,
1112
+ builtin: false,
1113
+ baseUrl: provider.baseUrl,
1114
+ apiKey: resolveProviderApiKey(provider),
1115
+ defaultQuery: provider.defaultQuery,
1116
+ defaultHeaders: provider.defaultHeaders
1117
+ })
1118
+ );
1052
1119
  return [builtin, ...registry];
1053
1120
  }
1121
+ function policyProviderIdForModel(settings, modelId) {
1122
+ if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1123
+ return CODEX_PROVIDER_ID;
1124
+ }
1125
+ const configured = configuredModels(settings).find((model) => model.id === modelId);
1126
+ return configured?.providerId ?? builtinProviderId(settings);
1127
+ }
1054
1128
  function configuredModels(settings) {
1055
1129
  const builtinId = builtinProviderId(settings);
1056
1130
  const builtinLabel = builtinProviderLabel(settings);
1057
1131
  const registryOwnedIds = new Set(
1058
- parseModelProvidersJson(settings.modelProvidersJson).flatMap((provider) => provider.models.map((model) => model.id))
1132
+ parseModelProvidersJson(settings.modelProvidersJson).flatMap(
1133
+ (provider) => provider.models.map((model) => model.id)
1134
+ )
1059
1135
  );
1060
1136
  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) => ({
1137
+ const out = uniqueValues([
1138
+ settings.openaiModel,
1139
+ ...splitCsv(settings.openaiAllowedModels)
1140
+ ]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
1062
1141
  id,
1063
1142
  label: id,
1064
1143
  providerId: builtinId,
@@ -1078,6 +1157,8 @@ function configuredModels(settings) {
1078
1157
  providerLabel,
1079
1158
  api: provider.api,
1080
1159
  ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1160
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1161
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1081
1162
  reasoningEffort: model.reasoningEffort ?? false,
1082
1163
  hostedWebSearch: model.hostedWebSearch ?? false
1083
1164
  });
@@ -1100,7 +1181,9 @@ function resolveModelProvider(settings, modelId) {
1100
1181
  if (!model) {
1101
1182
  return void 0;
1102
1183
  }
1103
- const provider = configuredProviders(settings).find((candidate) => candidate.id === model.providerId);
1184
+ const provider = configuredProviders(settings).find(
1185
+ (candidate) => candidate.id === model.providerId
1186
+ );
1104
1187
  if (!provider) {
1105
1188
  return void 0;
1106
1189
  }
@@ -1122,27 +1205,25 @@ function configuredModelPricing(settings) {
1122
1205
  ...configured
1123
1206
  };
1124
1207
  }
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
1208
  function contextInputBudgetTokens(settings) {
1209
+ if (settings.contextEffectiveWindowTokens !== void 0) {
1210
+ return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
1211
+ }
1139
1212
  return Math.max(0, settings.contextWindowTokens - settings.contextReservedOutputTokens);
1140
1213
  }
1141
- function contextServerCompactThreshold(settings) {
1142
- if (settings.contextServerCompactThresholdTokens) {
1143
- return settings.contextServerCompactThresholdTokens;
1144
- }
1145
- return Math.floor(settings.contextWindowTokens * settings.contextCompactionThresholdRatio);
1214
+ function settingsWithResolvedModelContext(settings, model) {
1215
+ const contextWindowTokens = model.contextWindowTokens ?? settings.contextWindowTokens;
1216
+ return {
1217
+ ...settings,
1218
+ contextWindowTokens,
1219
+ ...model.effectiveContextWindowTokens === void 0 ? {} : {
1220
+ contextEffectiveWindowTokens: Math.min(
1221
+ contextWindowTokens,
1222
+ model.effectiveContextWindowTokens
1223
+ )
1224
+ },
1225
+ ...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }
1226
+ };
1146
1227
  }
1147
1228
  function configuredStaticUsageLimits(settings) {
1148
1229
  return parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1175,7 +1256,10 @@ function calculateModelUsageCostMicros(settings, model, usage) {
1175
1256
  return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1176
1257
  }
1177
1258
  function configuredAllowedReasoningEfforts(settings) {
1178
- return uniqueValues([settings.openaiReasoningEffort, ...splitCsv(settings.openaiAllowedReasoningEfforts)]).map((value) => ReasoningEffort.parse(value));
1259
+ return uniqueValues([
1260
+ settings.openaiReasoningEffort,
1261
+ ...splitCsv(settings.openaiAllowedReasoningEfforts)
1262
+ ]).map((value) => ReasoningEffort.parse(value));
1179
1263
  }
1180
1264
  function environmentsEncryptionKeyBytes(settings) {
1181
1265
  if (!settings.environmentsEncryptionKey) {
@@ -1183,7 +1267,9 @@ function environmentsEncryptionKeyBytes(settings) {
1183
1267
  }
1184
1268
  const decoded = Buffer.from(settings.environmentsEncryptionKey, "base64");
1185
1269
  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)");
1270
+ throw new Error(
1271
+ "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY must be base64 for exactly 32 bytes (generate with: openssl rand -base64 32)"
1272
+ );
1187
1273
  }
1188
1274
  return new Uint8Array(decoded);
1189
1275
  }
@@ -1198,12 +1284,21 @@ function dbSearchPath(settings) {
1198
1284
  return `${schema},opengeni_private,public`;
1199
1285
  }
1200
1286
  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));
1287
+ return Object.fromEntries(
1288
+ Object.entries({
1289
+ GIT_AUTHOR_NAME: settings.gitAuthorName,
1290
+ GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
1291
+ GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
1292
+ GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
1293
+ }).filter(
1294
+ (entry) => typeof entry[1] === "string" && entry[1].trim().length > 0
1295
+ )
1296
+ );
1297
+ }
1298
+ var DEFAULT_SANDBOX_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
1299
+ function prependPathEntry(pathValue, entry) {
1300
+ const parts = (pathValue ?? DEFAULT_SANDBOX_PATH).split(":").filter(Boolean);
1301
+ return [entry, ...parts.filter((part) => part !== entry)].join(":");
1207
1302
  }
1208
1303
  function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, options = {}) {
1209
1304
  const environment = {
@@ -1215,18 +1310,35 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
1215
1310
  if (settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local") {
1216
1311
  environment.HOME ??= descriptor.workspaceRoot;
1217
1312
  }
1218
- environment.OPENGENI_GIT_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/git-token`;
1313
+ const provisionedGitHelperBackend = settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local" && settings.sandboxBackend !== "selfhosted";
1314
+ if (provisionedGitHelperBackend) {
1315
+ const home = environment.HOME ?? descriptor.workspaceRoot;
1316
+ environment.OPENGENI_GIT_CREDENTIALS_DIR ??= `${home}/.opengeni/git-credentials`;
1317
+ environment.OPENGENI_GIT_TOKEN_FILE ??= `${home}/.opengeni/git-token`;
1318
+ environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
1319
+ environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
1320
+ }
1219
1321
  if (settings.toolspaceEnabled) {
1220
1322
  environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
1221
1323
  if (options.workspaceId) {
1222
- environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(settings, options.workspaceId);
1324
+ environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
1325
+ settings,
1326
+ options.workspaceId
1327
+ );
1223
1328
  }
1224
1329
  }
1225
1330
  return environment;
1226
1331
  }
1227
1332
  function hasGitHubRepositorySelection(resources) {
1228
1333
  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));
1334
+ return resources.some(
1335
+ (resource) => resource.kind === "repository" && (positive(resource.githubInstallationId) && positive(resource.githubRepositoryId) || resource.provider === "github" && positive(resource.installationId) && positive(resource.repositoryId))
1336
+ );
1337
+ }
1338
+ function hasGitCredentialRepositorySelection(resources) {
1339
+ return resources.some(
1340
+ (resource) => resource.kind === "repository" && (resource.provider === "github" || resource.provider === "gitlab" || resource.provider === "azure_devops" || hasGitHubRepositorySelection([resource]))
1341
+ );
1230
1342
  }
1231
1343
  function applyGitAuthPointerEnvironment(environment, identity) {
1232
1344
  environment.GIT_ASKPASS = `${environment.HOME ?? "/workspace"}/.opengeni/askpass`;
@@ -1281,10 +1393,14 @@ function sandboxLifecycleHookIds(settings) {
1281
1393
  return uniqueValues(ids);
1282
1394
  }
1283
1395
  function sandboxPreparationProfileNames(settings) {
1284
- const profiles = splitCsv(settings.sandboxPreparationProfiles).map((value) => value.toLowerCase());
1396
+ const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
1397
+ (value) => value.toLowerCase()
1398
+ );
1285
1399
  if (profiles.includes("none")) {
1286
1400
  if (profiles.length > 1) {
1287
- throw new Error("OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles");
1401
+ throw new Error(
1402
+ "OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles"
1403
+ );
1288
1404
  }
1289
1405
  return ["none"];
1290
1406
  }
@@ -1316,7 +1432,7 @@ function parseMcpServers(raw) {
1316
1432
  return parsed;
1317
1433
  } catch (error) {
1318
1434
  const message = error instanceof Error ? error.message : String(error);
1319
- throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`);
1435
+ throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, { cause: error });
1320
1436
  }
1321
1437
  }
1322
1438
  function parseModelPricingJson(raw) {
@@ -1328,7 +1444,7 @@ function parseModelPricingJson(raw) {
1328
1444
  parsed = JSON.parse(raw);
1329
1445
  } catch (error) {
1330
1446
  const message = error instanceof Error ? error.message : String(error);
1331
- throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`);
1447
+ throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`, { cause: error });
1332
1448
  }
1333
1449
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1334
1450
  throw new Error("OPENGENI_MODEL_PRICING_JSON must be a JSON object keyed by model name");
@@ -1351,19 +1467,28 @@ function parseSandboxWarmRateJson(raw) {
1351
1467
  parsed = JSON.parse(raw);
1352
1468
  } catch (error) {
1353
1469
  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}`);
1470
+ throw new Error(
1471
+ `OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be valid JSON: ${message}`,
1472
+ { cause: error }
1473
+ );
1355
1474
  }
1356
1475
  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");
1476
+ throw new Error(
1477
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be a JSON object keyed by backend name"
1478
+ );
1358
1479
  }
1359
1480
  const out = {};
1360
1481
  for (const [backend, value] of Object.entries(parsed)) {
1361
1482
  if (!backend.trim()) {
1362
- throw new Error("OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name");
1483
+ throw new Error(
1484
+ "OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name"
1485
+ );
1363
1486
  }
1364
1487
  const rate = typeof value === "number" ? value : Number(value);
1365
1488
  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`);
1489
+ throw new Error(
1490
+ `OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON rate for ${backend} must be a non-negative number`
1491
+ );
1367
1492
  }
1368
1493
  out[backend] = rate;
1369
1494
  }
@@ -1382,7 +1507,9 @@ function parseModelProvidersJson(raw) {
1382
1507
  parsed = JSON.parse(raw);
1383
1508
  } catch (error) {
1384
1509
  const message = error instanceof Error ? error.message : String(error);
1385
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`);
1510
+ throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`, {
1511
+ cause: error
1512
+ });
1386
1513
  }
1387
1514
  if (!Array.isArray(parsed)) {
1388
1515
  throw new Error("OPENGENI_MODEL_PROVIDERS_JSON must be a JSON array of providers");
@@ -1390,7 +1517,9 @@ function parseModelProvidersJson(raw) {
1390
1517
  return parsed.map((entry, index) => {
1391
1518
  const result = RegistryProviderSchema.safeParse(entry);
1392
1519
  if (!result.success) {
1393
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`);
1520
+ throw new Error(
1521
+ `OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
1522
+ );
1394
1523
  }
1395
1524
  return result.data;
1396
1525
  });
@@ -1404,10 +1533,14 @@ function parseIntegrationsOauthClientsJson(raw) {
1404
1533
  parsed = JSON.parse(raw);
1405
1534
  } catch (error) {
1406
1535
  const message = error instanceof Error ? error.message : String(error);
1407
- throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`);
1536
+ throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`, {
1537
+ cause: error
1538
+ });
1408
1539
  }
1409
1540
  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");
1541
+ throw new Error(
1542
+ "OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be a JSON object keyed by authorization-server issuer or URL"
1543
+ );
1411
1544
  }
1412
1545
  const out = {};
1413
1546
  for (const [key, value] of Object.entries(parsed)) {
@@ -1416,7 +1549,9 @@ function parseIntegrationsOauthClientsJson(raw) {
1416
1549
  }
1417
1550
  const result = IntegrationOAuthClientConfigSchema.safeParse(value);
1418
1551
  if (!result.success) {
1419
- throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`);
1552
+ throw new Error(
1553
+ `OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`
1554
+ );
1420
1555
  }
1421
1556
  out[key] = result.data;
1422
1557
  }
@@ -1431,7 +1566,9 @@ function parseStaticUsageLimitsJson(raw) {
1431
1566
  parsed = JSON.parse(raw);
1432
1567
  } catch (error) {
1433
1568
  const message = error instanceof Error ? error.message : String(error);
1434
- throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`);
1569
+ throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`, {
1570
+ cause: error
1571
+ });
1435
1572
  }
1436
1573
  return StaticUsageLimits.parse(parsed);
1437
1574
  }
@@ -1444,7 +1581,9 @@ function parseStaticEntitlementsJson(raw) {
1444
1581
  parsed = JSON.parse(raw);
1445
1582
  } catch (error) {
1446
1583
  const message = error instanceof Error ? error.message : String(error);
1447
- throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`);
1584
+ throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`, {
1585
+ cause: error
1586
+ });
1448
1587
  }
1449
1588
  return Entitlements.parse(parsed);
1450
1589
  }
@@ -1491,20 +1630,32 @@ function ensureBuiltInMcpServers(settings) {
1491
1630
  // safe to cache / leave as-is.)
1492
1631
  cacheToolsList: false
1493
1632
  },
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
- }],
1633
+ ...hasFiles ? [] : [
1634
+ {
1635
+ id: "files",
1636
+ name: "Files",
1637
+ url: firstPartyMcpUrl,
1638
+ allowedTools: ["files_get_download_url"],
1639
+ cacheToolsList: true
1640
+ }
1641
+ ],
1642
+ ...hasDocs ? [] : [
1643
+ {
1644
+ id: "docs",
1645
+ name: "Document Search",
1646
+ url: firstPartyDocsMcpUrl,
1647
+ allowedTools: [
1648
+ "search_documents",
1649
+ "fetch_document_chunk",
1650
+ "list_document_bases",
1651
+ "knowledge_search",
1652
+ "knowledge_fetch",
1653
+ "memory_search",
1654
+ "memory_propose"
1655
+ ],
1656
+ cacheToolsList: false
1657
+ }
1658
+ ],
1508
1659
  ...existing
1509
1660
  ];
1510
1661
  }
@@ -1534,40 +1685,58 @@ function validateSettings(settings) {
1534
1685
  }
1535
1686
  if (settings.productAccessMode === "managed") {
1536
1687
  if (!settings.publicBaseUrl) {
1537
- throw new Error("OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1688
+ throw new Error(
1689
+ "OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1690
+ );
1538
1691
  }
1539
1692
  if (!settings.betterAuthSecret) {
1540
- throw new Error("OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1693
+ throw new Error(
1694
+ "OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1695
+ );
1541
1696
  }
1542
1697
  if (!settings.delegationSecret) {
1543
- throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed");
1698
+ throw new Error(
1699
+ "OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
1700
+ );
1544
1701
  }
1545
1702
  if (!["local", "test"].includes(settings.environment) && !settings.resendApiKey) {
1546
1703
  throw new Error("OPENGENI_RESEND_API_KEY is required for managed mode outside local/test");
1547
1704
  }
1548
1705
  if (!["local", "test"].includes(settings.environment) && !settings.environmentsEncryptionKey) {
1549
- throw new Error("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test");
1706
+ throw new Error(
1707
+ "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test"
1708
+ );
1550
1709
  }
1551
1710
  }
1552
1711
  environmentsEncryptionKeyBytes(settings);
1553
1712
  if (settings.integrationsEnabled) {
1554
1713
  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");
1714
+ throw new Error(
1715
+ "OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_INTEGRATIONS_ENABLED=true and OPENGENI_PRODUCT_ACCESS_MODE=managed"
1716
+ );
1556
1717
  }
1557
1718
  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");
1719
+ throw new Error(
1720
+ "OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
1721
+ );
1559
1722
  }
1560
1723
  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");
1724
+ throw new Error(
1725
+ "OPENGENI_INTEGRATIONS_STATE_SECRET is required when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
1726
+ );
1562
1727
  }
1563
1728
  }
1564
1729
  parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
1565
1730
  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");
1731
+ throw new Error(
1732
+ "OPENGENI_PRODUCT_ACCESS_MODE=configured requires OPENGENI_DELEGATION_SECRET or OPENGENI_AUTH_REQUIRED=true outside local/test"
1733
+ );
1567
1734
  }
1568
1735
  if (settings.billingMode === "stripe") {
1569
1736
  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");
1737
+ throw new Error(
1738
+ "OPENGENI_STRIPE_SECRET_KEY and OPENGENI_STRIPE_WEBHOOK_SECRET are required when OPENGENI_BILLING_MODE=stripe"
1739
+ );
1571
1740
  }
1572
1741
  }
1573
1742
  if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
@@ -1577,13 +1746,17 @@ function validateSettings(settings) {
1577
1746
  const pricing = configuredModelPricing(settings);
1578
1747
  const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
1579
1748
  if (missing.length > 0) {
1580
- throw new Error(`Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`);
1749
+ throw new Error(
1750
+ `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
1751
+ );
1581
1752
  }
1582
1753
  }
1583
1754
  if (settings.usageLimitsMode === "static") {
1584
1755
  const limits = configuredStaticUsageLimits(settings);
1585
1756
  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");
1757
+ throw new Error(
1758
+ "OPENGENI_STATIC_USAGE_LIMITS_JSON must define at least one cap when OPENGENI_USAGE_LIMITS_MODE=static"
1759
+ );
1587
1760
  }
1588
1761
  } else {
1589
1762
  parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1591,7 +1764,9 @@ function validateSettings(settings) {
1591
1764
  if (settings.entitlementsMode === "static") {
1592
1765
  const entitlements = parseStaticEntitlementsJson(settings.staticEntitlementsJson);
1593
1766
  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");
1767
+ throw new Error(
1768
+ "OPENGENI_STATIC_ENTITLEMENTS_JSON must define at least one feature when OPENGENI_ENTITLEMENTS_MODE=static"
1769
+ );
1595
1770
  }
1596
1771
  } else {
1597
1772
  parseStaticEntitlementsJson(settings.staticEntitlementsJson);
@@ -1601,7 +1776,9 @@ function validateSettings(settings) {
1601
1776
  }
1602
1777
  if (settings.openaiProvider === "azure") {
1603
1778
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiEndpoint) {
1604
- throw new Error("Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT");
1779
+ throw new Error(
1780
+ "Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT"
1781
+ );
1605
1782
  }
1606
1783
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiDeployment) {
1607
1784
  throw new Error("Azure OpenAI endpoint mode requires OPENGENI_AZURE_OPENAI_DEPLOYMENT");
@@ -1614,52 +1791,76 @@ function validateSettings(settings) {
1614
1791
  }
1615
1792
  }
1616
1793
  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");
1794
+ throw new Error(
1795
+ "OPENGENI_MODAL_TOKEN_ID and OPENGENI_MODAL_TOKEN_SECRET must both be set or both omitted"
1796
+ );
1618
1797
  }
1619
1798
  for (const required of SANDBOX_REQUIRED_ENV[settings.sandboxBackend] ?? []) {
1620
1799
  const value = settings[required.field];
1621
1800
  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}`);
1801
+ throw new Error(
1802
+ `${required.env} is required when OPENGENI_SANDBOX_BACKEND=${settings.sandboxBackend}`
1803
+ );
1623
1804
  }
1624
1805
  }
1625
1806
  if (settings.objectStorageBackend === "s3-compatible" || settings.objectStorageBackend === "aws-s3") {
1626
1807
  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");
1808
+ throw new Error(
1809
+ "OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
1810
+ );
1628
1811
  }
1629
1812
  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");
1813
+ throw new Error(
1814
+ "S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
1815
+ );
1631
1816
  }
1632
1817
  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");
1818
+ throw new Error(
1819
+ "S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
1820
+ );
1634
1821
  }
1635
1822
  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");
1823
+ throw new Error(
1824
+ "S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
1825
+ );
1637
1826
  }
1638
1827
  } else if (settings.objectStorageBackend === "azure-blob") {
1639
1828
  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");
1829
+ throw new Error(
1830
+ "Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
1831
+ );
1641
1832
  }
1642
1833
  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");
1834
+ throw new Error(
1835
+ "Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
1836
+ );
1644
1837
  }
1645
1838
  const hasConnectionString = Boolean(settings.objectStorageAzureConnectionString);
1646
1839
  const hasSharedKey = Boolean(settings.objectStorageAzureAccountName) && Boolean(settings.objectStorageAzureAccountKey);
1647
1840
  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");
1841
+ throw new Error(
1842
+ "Azure Blob storage requires OPENGENI_OBJECT_STORAGE_AZURE_CONNECTION_STRING or OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_NAME plus OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_KEY"
1843
+ );
1649
1844
  }
1650
1845
  } else {
1651
1846
  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");
1847
+ throw new Error(
1848
+ "GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
1849
+ );
1653
1850
  }
1654
1851
  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");
1852
+ throw new Error(
1853
+ "GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
1854
+ );
1656
1855
  }
1657
1856
  if (settings.objectStorageGcsCredentialsJson) {
1658
1857
  parseGcsCredentialsJson(settings.objectStorageGcsCredentialsJson);
1659
1858
  }
1660
1859
  }
1661
1860
  if (settings.documentChunkOverlap >= settings.documentChunkSize) {
1662
- throw new Error("OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE");
1861
+ throw new Error(
1862
+ "OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE"
1863
+ );
1663
1864
  }
1664
1865
  parseExposedPorts(settings.dockerExposedPorts);
1665
1866
  sandboxEnvironmentVariableNames(settings);
@@ -1709,14 +1910,20 @@ function validateSettings(settings) {
1709
1910
  const providerIds = /* @__PURE__ */ new Set();
1710
1911
  for (const provider of registryProviders) {
1711
1912
  if (provider.id === builtinId) {
1712
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`);
1913
+ throw new Error(
1914
+ `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
1915
+ );
1713
1916
  }
1714
1917
  if (providerIds.has(provider.id)) {
1715
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`);
1918
+ throw new Error(
1919
+ `OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`
1920
+ );
1716
1921
  }
1717
1922
  providerIds.add(provider.id);
1718
1923
  if (!resolveProviderApiKey(provider)) {
1719
- throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`);
1924
+ throw new Error(
1925
+ `OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
1926
+ );
1720
1927
  }
1721
1928
  }
1722
1929
  }
@@ -1794,7 +2001,9 @@ function parseGcsCredentialsJson(raw) {
1794
2001
  return JSON.parse(raw);
1795
2002
  } catch (error) {
1796
2003
  const message = error instanceof Error ? error.message : String(error);
1797
- throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`);
2004
+ throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`, {
2005
+ cause: error
2006
+ });
1798
2007
  }
1799
2008
  }
1800
2009
  function delay(ms) {
@@ -1809,6 +2018,7 @@ export {
1809
2018
  RegistryProviderKind,
1810
2019
  SANDBOX_REQUIRED_ENV,
1811
2020
  applyGitAuthPointerEnvironment,
2021
+ builtinProviderId,
1812
2022
  calculateModelUsageCostMicros,
1813
2023
  collectGitIdentityEnvironment,
1814
2024
  collectSandboxEnvironment,
@@ -1820,7 +2030,6 @@ export {
1820
2030
  configuredProviders,
1821
2031
  configuredStaticUsageLimits,
1822
2032
  contextInputBudgetTokens,
1823
- contextServerCompactThreshold,
1824
2033
  dbSearchPath,
1825
2034
  defaultModelPricing,
1826
2035
  effectiveModalIdleTimeoutSeconds,
@@ -1828,6 +2037,7 @@ export {
1828
2037
  firstPartyMcpBaseUrl,
1829
2038
  firstPartyMcpWorkspaceUrl,
1830
2039
  getSettings,
2040
+ hasGitCredentialRepositorySelection,
1831
2041
  hasGitHubRepositorySelection,
1832
2042
  parseExposedPorts,
1833
2043
  parseIntegrationsOauthClientsJson,
@@ -1837,8 +2047,8 @@ export {
1837
2047
  parseSandboxWarmRateJson,
1838
2048
  parseStaticEntitlementsJson,
1839
2049
  parseStaticUsageLimitsJson,
2050
+ policyProviderIdForModel,
1840
2051
  requiredSandboxEnvForBackend,
1841
- resolveContextCompactionMode,
1842
2052
  resolveEnrollmentSigningSecret,
1843
2053
  resolveModelProvider,
1844
2054
  resolveNatsCalloutConfig,
@@ -1851,6 +2061,7 @@ export {
1851
2061
  sandboxLifecycleHookIds,
1852
2062
  sandboxPreparationProfiles,
1853
2063
  sandboxWarmRateMicrosPerSecond,
2064
+ settingsWithResolvedModelContext,
1854
2065
  stableSandboxEnvironmentForRun,
1855
2066
  startupRetryOptions,
1856
2067
  streamTokenDegraded