@opengeni/config 0.3.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(),
@@ -289,6 +274,15 @@ var SettingsSchema = z.object({
289
274
  dockerNetwork: z.string().optional(),
290
275
  modalAppName: z.string().default("opengeni-sandbox"),
291
276
  modalImageRef: z.string().optional(),
277
+ // Name of a Modal Secret (containing REGISTRY_USERNAME + REGISTRY_PASSWORD) used
278
+ // to authenticate the pull of `modalImageRef` from a PRIVATE registry. When UNSET
279
+ // (the default), the sandbox image is pulled UNAUTHENTICATED — i.e. it must be a
280
+ // PUBLIC registry tag, which is the only shape the Agents-extension Modal backend
281
+ // supports out of the box (`Image.fromRegistry(tag)` with no secret). Set this to
282
+ // run a private image (e.g. a cloud-hosted ACR/ECR/GCR digest): the runtime resolves
283
+ // the named Secret and builds the image via `fromRegistry(tag, secret)` before the
284
+ // first sandbox is created. Knob: OPENGENI_MODAL_IMAGE_REGISTRY_SECRET.
285
+ modalImageRegistrySecret: z.string().optional(),
292
286
  // Modal's hard sandbox lifetime (timeoutMs = this * 1000), counted from each
293
287
  // create/resume — it is the BACKSTOP that reclaims a box if the reaper/worker is
294
288
  // down, NOT the warm-window controller (that's sandboxIdleGraceMs). It must
@@ -370,6 +364,13 @@ var SettingsSchema = z.object({
370
364
  // recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
371
365
  recordingEnabled: EnvBoolean.default(true),
372
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),
373
374
  recordingFramerate: z.coerce.number().int().positive().default(15),
374
375
  recordingMaxSeconds: z.coerce.number().int().positive().default(600),
375
376
  recordingMaxBytes: z.coerce.number().int().positive().default(268435456),
@@ -426,6 +427,19 @@ var SettingsSchema = z.object({
426
427
  // EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
427
428
  // turn the flag ON the moment anyone set the env var to disable it).
428
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),
429
443
  // --- bring-your-own-compute (selfhosted 11th backend) rollout flag, default OFF ---
430
444
  // The keystone flag for the whole selfhosted feature (the enrollment device-flow,
431
445
  // the NATS control plane, the relay stream tier). When FALSE the enrollment routes
@@ -434,6 +448,11 @@ var SettingsSchema = z.object({
434
448
  // z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
435
449
  // the deploy-staging IaC secret/configmap pattern (dossier §17/§25.1).
436
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),
437
456
  // The HMAC secret the control plane signs the enrollment bearer credential with
438
457
  // (the `oge_` envelope the agent presents back to the control plane). Optional:
439
458
  // when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
@@ -490,6 +509,25 @@ var SettingsSchema = z.object({
490
509
  // bus connects anonymously (local dev / a NATS with no auth_callout).
491
510
  selfhostedNatsControlUser: z.string().optional(),
492
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),
493
531
  // --- sandbox lease cadences (cadence invariant validated at boot below) ---
494
532
  // reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
495
533
  // box idle timeout (effectiveModalIdleTimeoutSeconds, which defaults to the hard
@@ -508,6 +546,22 @@ var SettingsSchema = z.object({
508
546
  // fresh EMPTY box; lower it to trade warm cost for a snappier reclaim. Knob:
509
547
  // OPENGENI_SANDBOX_IDLE_GRACE_MS.
510
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),
511
565
  // expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
512
566
  // single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
513
567
  // window a cold->warming spawner has to commit warm before a reaper resets it.
@@ -517,6 +571,11 @@ var SettingsSchema = z.object({
517
571
  // (a liveness/reaper cadence), this bounds how long one turn waits for capacity
518
572
  // or provider creation before surfacing a clear turn.failed error.
519
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),
520
579
  // --- sandbox warm-time billing (P2.1) ---
521
580
  // Per-backend warm rate (usd_micros/sec), like modelPricingJson: an empty {}
522
581
  // means warm-cost is not debited (warm-seconds are still metered for audit).
@@ -577,30 +636,32 @@ var SettingsSchema = z.object({
577
636
  stripePublishableKey: z.string().optional(),
578
637
  stripeWebhookSecret: z.string().optional(),
579
638
  stripeCreditsProductId: z.string().optional(),
580
- mcpServers: z.array(z.object({
581
- id: z.string().min(1).regex(registryId),
582
- name: z.string().min(1).optional(),
583
- url: z.string().url(),
584
- allowedTools: z.array(z.string().min(1)).optional(),
585
- timeoutMs: z.number().int().positive().optional(),
586
- cacheToolsList: z.boolean().default(false),
587
- /**
588
- * Human-approval policy for this server's tools, overlaid per-run from a
589
- * session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
590
- * tools require approval; a string[] = only the listed UNPREFIXED tool
591
- * names do; absent = auto-run (the historical default). Enforced in the
592
- * runtime by attaching `needsApproval` to the matching MCP tools.
593
- */
594
- requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
595
- /**
596
- * Extra request headers sent to this MCP server (credential injection
597
- * for workspace-enabled capability MCPs). Populated at runtime from
598
- * encrypted capability-installation credentials; do not put secrets in
599
- * OPENGENI_MCP_SERVERS.
600
- */
601
- headers: z.record(z.string(), z.string()).optional(),
602
- connectionRef: McpServerConnectionRefSchema.optional()
603
- })).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([])
604
665
  });
605
666
  var ModelPricingSchema = z.object({
606
667
  inputMicrosPerMillionTokens: z.number().int().nonnegative(),
@@ -616,6 +677,8 @@ var RegistryModelSchema = z.object({
616
677
  label: z.string().min(1).optional(),
617
678
  // display name; defaults to id
618
679
  contextWindowTokens: z.number().int().positive().optional(),
680
+ effectiveContextWindowTokens: z.number().int().positive().optional(),
681
+ autoCompactTokenLimit: z.number().int().positive().optional(),
619
682
  reasoningEffort: z.boolean().optional(),
620
683
  // model accepts a reasoning-effort control
621
684
  hostedWebSearch: z.boolean().optional(),
@@ -644,12 +707,24 @@ var IntegrationOAuthClientConfigSchema = z.object({
644
707
  tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
645
708
  });
646
709
  var defaultModelPricing = {
647
- "gpt-5.5": {
710
+ "gpt-5.6-sol": {
648
711
  inputMicrosPerMillionTokens: 5e6,
649
712
  cachedInputMicrosPerMillionTokens: 5e5,
650
713
  outputMicrosPerMillionTokens: 3e7,
651
714
  marginBps: 2500
652
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
+ },
653
728
  "gpt-5.4": {
654
729
  inputMicrosPerMillionTokens: 25e5,
655
730
  cachedInputMicrosPerMillionTokens: 25e4,
@@ -725,21 +800,11 @@ var SANDBOX_REQUIRED_ENV = {
725
800
  { field: "modalTokenId", env: "OPENGENI_MODAL_TOKEN_ID" },
726
801
  { field: "modalTokenSecret", env: "OPENGENI_MODAL_TOKEN_SECRET" }
727
802
  ],
728
- daytona: [
729
- { field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }
730
- ],
731
- runloop: [
732
- { field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }
733
- ],
734
- e2b: [
735
- { field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }
736
- ],
737
- blaxel: [
738
- { field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }
739
- ],
740
- cloudflare: [
741
- { field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }
742
- ],
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" }],
743
808
  vercel: [
744
809
  { field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
745
810
  { field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
@@ -771,7 +836,9 @@ function getSettings() {
771
836
  temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
772
837
  temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
773
838
  startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
774
- startupDependencyRetryInitialDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"),
839
+ startupDependencyRetryInitialDelayMs: optional(
840
+ "OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
841
+ ),
775
842
  startupDependencyRetryMaxDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_MAX_DELAY_MS"),
776
843
  observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
777
844
  observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
@@ -793,21 +860,19 @@ function getSettings() {
793
860
  environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
794
861
  integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
795
862
  integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
796
- integrationsAllowPrivateNetworkTargets: optional("OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"),
863
+ integrationsAllowPrivateNetworkTargets: optional(
864
+ "OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
865
+ ),
797
866
  integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
798
867
  goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
799
868
  goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
869
+ childCompletionParentWakeEnabled: optional("OPENGENI_CHILD_COMPLETION_PARENT_WAKE_ENABLED"),
800
870
  agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
801
- sessionHistorySource: optional("OPENGENI_SESSION_HISTORY_SOURCE"),
802
- contextCompactionMode: optional("OPENGENI_CONTEXT_COMPACTION_MODE"),
803
871
  contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
872
+ contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
804
873
  contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
805
874
  contextReservedOutputTokens: optional("OPENGENI_CONTEXT_RESERVED_OUTPUT_TOKENS"),
806
- contextServerCompactThresholdTokens: optional("OPENGENI_CONTEXT_SERVER_COMPACT_THRESHOLD_TOKENS"),
807
- contextCompactSoftFraction: optional("OPENGENI_CONTEXT_COMPACT_SOFT_FRACTION"),
808
- contextCompactHardFraction: optional("OPENGENI_CONTEXT_COMPACT_HARD_FRACTION"),
809
- contextKeepRecentTokens: optional("OPENGENI_CONTEXT_KEEP_RECENT_TOKENS"),
810
- contextSummaryMaxTokens: optional("OPENGENI_CONTEXT_SUMMARY_MAX_TOKENS"),
875
+ contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
811
876
  authRequired: optional("OPENGENI_AUTH_REQUIRED"),
812
877
  accessKey: optional("OPENGENI_ACCESS_KEY"),
813
878
  authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
@@ -826,6 +891,7 @@ function getSettings() {
826
891
  modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
827
892
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
828
893
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
894
+ codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
829
895
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
830
896
  codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
831
897
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
@@ -849,6 +915,7 @@ function getSettings() {
849
915
  dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
850
916
  modalAppName: optional("OPENGENI_MODAL_APP_NAME"),
851
917
  modalImageRef: optional("OPENGENI_MODAL_IMAGE_REF"),
918
+ modalImageRegistrySecret: optional("OPENGENI_MODAL_IMAGE_REGISTRY_SECRET"),
852
919
  modalTimeoutSeconds: optional("OPENGENI_MODAL_TIMEOUT_SECONDS"),
853
920
  modalTokenId: optional("OPENGENI_MODAL_TOKEN_ID"),
854
921
  modalTokenSecret: optional("OPENGENI_MODAL_TOKEN_SECRET"),
@@ -864,6 +931,7 @@ function getSettings() {
864
931
  computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
865
932
  computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
866
933
  recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
934
+ workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
867
935
  recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
868
936
  recordingFramerate: optional("OPENGENI_RECORDING_FRAMERATE"),
869
937
  recordingMaxSeconds: optional("OPENGENI_RECORDING_MAX_SECONDS"),
@@ -903,7 +971,9 @@ function getSettings() {
903
971
  vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
904
972
  vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
905
973
  sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
974
+ sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
906
975
  sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
976
+ agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
907
977
  enrollmentSigningSecret: optional("OPENGENI_ENROLLMENT_SIGNING_SECRET"),
908
978
  selfhostedNatsUrl: optional("OPENGENI_SELFHOSTED_NATS_URL"),
909
979
  selfhostedRelayUrl: optional("OPENGENI_SELFHOSTED_RELAY_URL"),
@@ -915,13 +985,20 @@ function getSettings() {
915
985
  selfhostedNatsCalloutPassword: optional("OPENGENI_SELFHOSTED_NATS_CALLOUT_PASSWORD"),
916
986
  selfhostedNatsControlUser: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_USER"),
917
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"),
918
990
  sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
919
991
  sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
920
992
  sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
993
+ sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
994
+ sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
921
995
  sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
922
996
  sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
923
997
  sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
924
- 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
+ ),
925
1002
  sandboxMaxWarmSecondsPerWorkspace: optional("OPENGENI_SANDBOX_MAX_WARM_SECONDS_PER_WORKSPACE"),
926
1003
  sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
927
1004
  sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
@@ -1017,8 +1094,7 @@ function configuredProviders(settings) {
1017
1094
  label: builtinProviderLabel(settings),
1018
1095
  kind: "api-key",
1019
1096
  api: "responses",
1020
- builtin: true,
1021
- compactionMode: resolveContextCompactionMode(settings)
1097
+ builtin: true
1022
1098
  };
1023
1099
  if (settings.openaiProvider === "azure") {
1024
1100
  builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
@@ -1027,28 +1103,41 @@ function configuredProviders(settings) {
1027
1103
  builtin.baseUrl = settings.openaiBaseUrl;
1028
1104
  builtin.apiKey = settings.openaiApiKey;
1029
1105
  }
1030
- const registry = parseModelProvidersJson(settings.modelProvidersJson).map((provider) => ({
1031
- id: provider.id,
1032
- label: provider.label ?? provider.id,
1033
- kind: provider.kind,
1034
- api: provider.api,
1035
- builtin: false,
1036
- baseUrl: provider.baseUrl,
1037
- apiKey: resolveProviderApiKey(provider),
1038
- defaultQuery: provider.defaultQuery,
1039
- defaultHeaders: provider.defaultHeaders,
1040
- compactionMode: "client"
1041
- }));
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
+ );
1042
1119
  return [builtin, ...registry];
1043
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
+ }
1044
1128
  function configuredModels(settings) {
1045
1129
  const builtinId = builtinProviderId(settings);
1046
1130
  const builtinLabel = builtinProviderLabel(settings);
1047
1131
  const registryOwnedIds = new Set(
1048
- 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
+ )
1049
1135
  );
1050
1136
  const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
1051
- 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) => ({
1052
1141
  id,
1053
1142
  label: id,
1054
1143
  providerId: builtinId,
@@ -1068,6 +1157,8 @@ function configuredModels(settings) {
1068
1157
  providerLabel,
1069
1158
  api: provider.api,
1070
1159
  ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1160
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1161
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1071
1162
  reasoningEffort: model.reasoningEffort ?? false,
1072
1163
  hostedWebSearch: model.hostedWebSearch ?? false
1073
1164
  });
@@ -1090,7 +1181,9 @@ function resolveModelProvider(settings, modelId) {
1090
1181
  if (!model) {
1091
1182
  return void 0;
1092
1183
  }
1093
- const provider = configuredProviders(settings).find((candidate) => candidate.id === model.providerId);
1184
+ const provider = configuredProviders(settings).find(
1185
+ (candidate) => candidate.id === model.providerId
1186
+ );
1094
1187
  if (!provider) {
1095
1188
  return void 0;
1096
1189
  }
@@ -1112,27 +1205,25 @@ function configuredModelPricing(settings) {
1112
1205
  ...configured
1113
1206
  };
1114
1207
  }
1115
- function resolveContextCompactionMode(settings) {
1116
- switch (settings.contextCompactionMode) {
1117
- case "server":
1118
- return "server";
1119
- case "client":
1120
- return "client";
1121
- case "off":
1122
- return "off";
1123
- case "auto":
1124
- default:
1125
- return settings.openaiProvider === "openai" ? "server" : "client";
1126
- }
1127
- }
1128
1208
  function contextInputBudgetTokens(settings) {
1209
+ if (settings.contextEffectiveWindowTokens !== void 0) {
1210
+ return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
1211
+ }
1129
1212
  return Math.max(0, settings.contextWindowTokens - settings.contextReservedOutputTokens);
1130
1213
  }
1131
- function contextServerCompactThreshold(settings) {
1132
- if (settings.contextServerCompactThresholdTokens) {
1133
- return settings.contextServerCompactThresholdTokens;
1134
- }
1135
- 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
+ };
1136
1227
  }
1137
1228
  function configuredStaticUsageLimits(settings) {
1138
1229
  return parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1165,7 +1256,10 @@ function calculateModelUsageCostMicros(settings, model, usage) {
1165
1256
  return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1166
1257
  }
1167
1258
  function configuredAllowedReasoningEfforts(settings) {
1168
- 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));
1169
1263
  }
1170
1264
  function environmentsEncryptionKeyBytes(settings) {
1171
1265
  if (!settings.environmentsEncryptionKey) {
@@ -1173,7 +1267,9 @@ function environmentsEncryptionKeyBytes(settings) {
1173
1267
  }
1174
1268
  const decoded = Buffer.from(settings.environmentsEncryptionKey, "base64");
1175
1269
  if (decoded.length !== 32) {
1176
- 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
+ );
1177
1273
  }
1178
1274
  return new Uint8Array(decoded);
1179
1275
  }
@@ -1188,12 +1284,21 @@ function dbSearchPath(settings) {
1188
1284
  return `${schema},opengeni_private,public`;
1189
1285
  }
1190
1286
  function collectGitIdentityEnvironment(settings) {
1191
- return Object.fromEntries(Object.entries({
1192
- GIT_AUTHOR_NAME: settings.gitAuthorName,
1193
- GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
1194
- GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
1195
- GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
1196
- }).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(":");
1197
1302
  }
1198
1303
  function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, options = {}) {
1199
1304
  const environment = {
@@ -1205,18 +1310,35 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
1205
1310
  if (settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local") {
1206
1311
  environment.HOME ??= descriptor.workspaceRoot;
1207
1312
  }
1208
- 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
+ }
1209
1321
  if (settings.toolspaceEnabled) {
1210
1322
  environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
1211
1323
  if (options.workspaceId) {
1212
- environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(settings, options.workspaceId);
1324
+ environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
1325
+ settings,
1326
+ options.workspaceId
1327
+ );
1213
1328
  }
1214
1329
  }
1215
1330
  return environment;
1216
1331
  }
1217
1332
  function hasGitHubRepositorySelection(resources) {
1218
1333
  const positive = (value) => typeof value === "number" && Number.isInteger(value) && value > 0 || typeof value === "string" && /^\d+$/.test(value) && Number(value) > 0;
1219
- 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
+ );
1220
1342
  }
1221
1343
  function applyGitAuthPointerEnvironment(environment, identity) {
1222
1344
  environment.GIT_ASKPASS = `${environment.HOME ?? "/workspace"}/.opengeni/askpass`;
@@ -1271,10 +1393,14 @@ function sandboxLifecycleHookIds(settings) {
1271
1393
  return uniqueValues(ids);
1272
1394
  }
1273
1395
  function sandboxPreparationProfileNames(settings) {
1274
- const profiles = splitCsv(settings.sandboxPreparationProfiles).map((value) => value.toLowerCase());
1396
+ const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
1397
+ (value) => value.toLowerCase()
1398
+ );
1275
1399
  if (profiles.includes("none")) {
1276
1400
  if (profiles.length > 1) {
1277
- 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
+ );
1278
1404
  }
1279
1405
  return ["none"];
1280
1406
  }
@@ -1306,7 +1432,7 @@ function parseMcpServers(raw) {
1306
1432
  return parsed;
1307
1433
  } catch (error) {
1308
1434
  const message = error instanceof Error ? error.message : String(error);
1309
- 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 });
1310
1436
  }
1311
1437
  }
1312
1438
  function parseModelPricingJson(raw) {
@@ -1318,7 +1444,7 @@ function parseModelPricingJson(raw) {
1318
1444
  parsed = JSON.parse(raw);
1319
1445
  } catch (error) {
1320
1446
  const message = error instanceof Error ? error.message : String(error);
1321
- 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 });
1322
1448
  }
1323
1449
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1324
1450
  throw new Error("OPENGENI_MODEL_PRICING_JSON must be a JSON object keyed by model name");
@@ -1341,19 +1467,28 @@ function parseSandboxWarmRateJson(raw) {
1341
1467
  parsed = JSON.parse(raw);
1342
1468
  } catch (error) {
1343
1469
  const message = error instanceof Error ? error.message : String(error);
1344
- 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
+ );
1345
1474
  }
1346
1475
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1347
- 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
+ );
1348
1479
  }
1349
1480
  const out = {};
1350
1481
  for (const [backend, value] of Object.entries(parsed)) {
1351
1482
  if (!backend.trim()) {
1352
- 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
+ );
1353
1486
  }
1354
1487
  const rate = typeof value === "number" ? value : Number(value);
1355
1488
  if (!Number.isFinite(rate) || rate < 0) {
1356
- 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
+ );
1357
1492
  }
1358
1493
  out[backend] = rate;
1359
1494
  }
@@ -1372,7 +1507,9 @@ function parseModelProvidersJson(raw) {
1372
1507
  parsed = JSON.parse(raw);
1373
1508
  } catch (error) {
1374
1509
  const message = error instanceof Error ? error.message : String(error);
1375
- 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
+ });
1376
1513
  }
1377
1514
  if (!Array.isArray(parsed)) {
1378
1515
  throw new Error("OPENGENI_MODEL_PROVIDERS_JSON must be a JSON array of providers");
@@ -1380,7 +1517,9 @@ function parseModelProvidersJson(raw) {
1380
1517
  return parsed.map((entry, index) => {
1381
1518
  const result = RegistryProviderSchema.safeParse(entry);
1382
1519
  if (!result.success) {
1383
- 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
+ );
1384
1523
  }
1385
1524
  return result.data;
1386
1525
  });
@@ -1394,10 +1533,14 @@ function parseIntegrationsOauthClientsJson(raw) {
1394
1533
  parsed = JSON.parse(raw);
1395
1534
  } catch (error) {
1396
1535
  const message = error instanceof Error ? error.message : String(error);
1397
- 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
+ });
1398
1539
  }
1399
1540
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1400
- 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
+ );
1401
1544
  }
1402
1545
  const out = {};
1403
1546
  for (const [key, value] of Object.entries(parsed)) {
@@ -1406,7 +1549,9 @@ function parseIntegrationsOauthClientsJson(raw) {
1406
1549
  }
1407
1550
  const result = IntegrationOAuthClientConfigSchema.safeParse(value);
1408
1551
  if (!result.success) {
1409
- 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
+ );
1410
1555
  }
1411
1556
  out[key] = result.data;
1412
1557
  }
@@ -1421,7 +1566,9 @@ function parseStaticUsageLimitsJson(raw) {
1421
1566
  parsed = JSON.parse(raw);
1422
1567
  } catch (error) {
1423
1568
  const message = error instanceof Error ? error.message : String(error);
1424
- 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
+ });
1425
1572
  }
1426
1573
  return StaticUsageLimits.parse(parsed);
1427
1574
  }
@@ -1434,7 +1581,9 @@ function parseStaticEntitlementsJson(raw) {
1434
1581
  parsed = JSON.parse(raw);
1435
1582
  } catch (error) {
1436
1583
  const message = error instanceof Error ? error.message : String(error);
1437
- 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
+ });
1438
1587
  }
1439
1588
  return Entitlements.parse(parsed);
1440
1589
  }
@@ -1481,20 +1630,32 @@ function ensureBuiltInMcpServers(settings) {
1481
1630
  // safe to cache / leave as-is.)
1482
1631
  cacheToolsList: false
1483
1632
  },
1484
- ...hasFiles ? [] : [{
1485
- id: "files",
1486
- name: "Files",
1487
- url: firstPartyMcpUrl,
1488
- allowedTools: ["files_get_download_url"],
1489
- cacheToolsList: true
1490
- }],
1491
- ...hasDocs ? [] : [{
1492
- id: "docs",
1493
- name: "Document Search",
1494
- url: firstPartyDocsMcpUrl,
1495
- allowedTools: ["search_documents", "fetch_document_chunk", "list_document_bases", "knowledge_search", "knowledge_fetch", "memory_search", "memory_propose"],
1496
- cacheToolsList: false
1497
- }],
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
+ ],
1498
1659
  ...existing
1499
1660
  ];
1500
1661
  }
@@ -1524,40 +1685,58 @@ function validateSettings(settings) {
1524
1685
  }
1525
1686
  if (settings.productAccessMode === "managed") {
1526
1687
  if (!settings.publicBaseUrl) {
1527
- 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
+ );
1528
1691
  }
1529
1692
  if (!settings.betterAuthSecret) {
1530
- 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
+ );
1531
1696
  }
1532
1697
  if (!settings.delegationSecret) {
1533
- 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
+ );
1534
1701
  }
1535
1702
  if (!["local", "test"].includes(settings.environment) && !settings.resendApiKey) {
1536
1703
  throw new Error("OPENGENI_RESEND_API_KEY is required for managed mode outside local/test");
1537
1704
  }
1538
1705
  if (!["local", "test"].includes(settings.environment) && !settings.environmentsEncryptionKey) {
1539
- 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
+ );
1540
1709
  }
1541
1710
  }
1542
1711
  environmentsEncryptionKeyBytes(settings);
1543
1712
  if (settings.integrationsEnabled) {
1544
1713
  if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
1545
- 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
+ );
1546
1717
  }
1547
1718
  if (settings.publicBaseUrl && !settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
1548
- 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
+ );
1549
1722
  }
1550
1723
  if (!settings.integrationsStateSecret && !["local", "test"].includes(settings.environment)) {
1551
- 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
+ );
1552
1727
  }
1553
1728
  }
1554
1729
  parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
1555
1730
  if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
1556
- 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
+ );
1557
1734
  }
1558
1735
  if (settings.billingMode === "stripe") {
1559
1736
  if (!settings.stripeSecretKey || !settings.stripeWebhookSecret) {
1560
- 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
+ );
1561
1740
  }
1562
1741
  }
1563
1742
  if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
@@ -1567,13 +1746,17 @@ function validateSettings(settings) {
1567
1746
  const pricing = configuredModelPricing(settings);
1568
1747
  const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
1569
1748
  if (missing.length > 0) {
1570
- 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
+ );
1571
1752
  }
1572
1753
  }
1573
1754
  if (settings.usageLimitsMode === "static") {
1574
1755
  const limits = configuredStaticUsageLimits(settings);
1575
1756
  if (Object.keys(limits).length === 0) {
1576
- 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
+ );
1577
1760
  }
1578
1761
  } else {
1579
1762
  parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
@@ -1581,7 +1764,9 @@ function validateSettings(settings) {
1581
1764
  if (settings.entitlementsMode === "static") {
1582
1765
  const entitlements = parseStaticEntitlementsJson(settings.staticEntitlementsJson);
1583
1766
  if (Object.keys(entitlements).length === 0) {
1584
- 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
+ );
1585
1770
  }
1586
1771
  } else {
1587
1772
  parseStaticEntitlementsJson(settings.staticEntitlementsJson);
@@ -1591,7 +1776,9 @@ function validateSettings(settings) {
1591
1776
  }
1592
1777
  if (settings.openaiProvider === "azure") {
1593
1778
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiEndpoint) {
1594
- 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
+ );
1595
1782
  }
1596
1783
  if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiDeployment) {
1597
1784
  throw new Error("Azure OpenAI endpoint mode requires OPENGENI_AZURE_OPENAI_DEPLOYMENT");
@@ -1604,52 +1791,76 @@ function validateSettings(settings) {
1604
1791
  }
1605
1792
  }
1606
1793
  if (Boolean(settings.modalTokenId) !== Boolean(settings.modalTokenSecret)) {
1607
- 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
+ );
1608
1797
  }
1609
1798
  for (const required of SANDBOX_REQUIRED_ENV[settings.sandboxBackend] ?? []) {
1610
1799
  const value = settings[required.field];
1611
1800
  if (value === void 0 || value === null || typeof value === "string" && value.trim().length === 0) {
1612
- 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
+ );
1613
1804
  }
1614
1805
  }
1615
1806
  if (settings.objectStorageBackend === "s3-compatible" || settings.objectStorageBackend === "aws-s3") {
1616
1807
  if (Boolean(settings.objectStorageAccessKeyId) !== Boolean(settings.objectStorageSecretAccessKey)) {
1617
- 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
+ );
1618
1811
  }
1619
1812
  if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
1620
- 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
+ );
1621
1816
  }
1622
1817
  if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
1623
- 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
+ );
1624
1821
  }
1625
1822
  if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
1626
- 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
+ );
1627
1826
  }
1628
1827
  } else if (settings.objectStorageBackend === "azure-blob") {
1629
1828
  if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
1630
- 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
+ );
1631
1832
  }
1632
1833
  if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
1633
- 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
+ );
1634
1837
  }
1635
1838
  const hasConnectionString = Boolean(settings.objectStorageAzureConnectionString);
1636
1839
  const hasSharedKey = Boolean(settings.objectStorageAzureAccountName) && Boolean(settings.objectStorageAzureAccountKey);
1637
1840
  if (!hasConnectionString && !hasSharedKey) {
1638
- 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
+ );
1639
1844
  }
1640
1845
  } else {
1641
1846
  if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
1642
- 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
+ );
1643
1850
  }
1644
1851
  if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
1645
- 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
+ );
1646
1855
  }
1647
1856
  if (settings.objectStorageGcsCredentialsJson) {
1648
1857
  parseGcsCredentialsJson(settings.objectStorageGcsCredentialsJson);
1649
1858
  }
1650
1859
  }
1651
1860
  if (settings.documentChunkOverlap >= settings.documentChunkSize) {
1652
- 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
+ );
1653
1864
  }
1654
1865
  parseExposedPorts(settings.dockerExposedPorts);
1655
1866
  sandboxEnvironmentVariableNames(settings);
@@ -1699,14 +1910,20 @@ function validateSettings(settings) {
1699
1910
  const providerIds = /* @__PURE__ */ new Set();
1700
1911
  for (const provider of registryProviders) {
1701
1912
  if (provider.id === builtinId) {
1702
- 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
+ );
1703
1916
  }
1704
1917
  if (providerIds.has(provider.id)) {
1705
- 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
+ );
1706
1921
  }
1707
1922
  providerIds.add(provider.id);
1708
1923
  if (!resolveProviderApiKey(provider)) {
1709
- 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
+ );
1710
1927
  }
1711
1928
  }
1712
1929
  }
@@ -1784,7 +2001,9 @@ function parseGcsCredentialsJson(raw) {
1784
2001
  return JSON.parse(raw);
1785
2002
  } catch (error) {
1786
2003
  const message = error instanceof Error ? error.message : String(error);
1787
- 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
+ });
1788
2007
  }
1789
2008
  }
1790
2009
  function delay(ms) {
@@ -1799,6 +2018,7 @@ export {
1799
2018
  RegistryProviderKind,
1800
2019
  SANDBOX_REQUIRED_ENV,
1801
2020
  applyGitAuthPointerEnvironment,
2021
+ builtinProviderId,
1802
2022
  calculateModelUsageCostMicros,
1803
2023
  collectGitIdentityEnvironment,
1804
2024
  collectSandboxEnvironment,
@@ -1810,7 +2030,6 @@ export {
1810
2030
  configuredProviders,
1811
2031
  configuredStaticUsageLimits,
1812
2032
  contextInputBudgetTokens,
1813
- contextServerCompactThreshold,
1814
2033
  dbSearchPath,
1815
2034
  defaultModelPricing,
1816
2035
  effectiveModalIdleTimeoutSeconds,
@@ -1818,6 +2037,7 @@ export {
1818
2037
  firstPartyMcpBaseUrl,
1819
2038
  firstPartyMcpWorkspaceUrl,
1820
2039
  getSettings,
2040
+ hasGitCredentialRepositorySelection,
1821
2041
  hasGitHubRepositorySelection,
1822
2042
  parseExposedPorts,
1823
2043
  parseIntegrationsOauthClientsJson,
@@ -1827,8 +2047,8 @@ export {
1827
2047
  parseSandboxWarmRateJson,
1828
2048
  parseStaticEntitlementsJson,
1829
2049
  parseStaticUsageLimitsJson,
2050
+ policyProviderIdForModel,
1830
2051
  requiredSandboxEnvForBackend,
1831
- resolveContextCompactionMode,
1832
2052
  resolveEnrollmentSigningSecret,
1833
2053
  resolveModelProvider,
1834
2054
  resolveNatsCalloutConfig,
@@ -1841,6 +2061,7 @@ export {
1841
2061
  sandboxLifecycleHookIds,
1842
2062
  sandboxPreparationProfiles,
1843
2063
  sandboxWarmRateMicrosPerSecond,
2064
+ settingsWithResolvedModelContext,
1844
2065
  stableSandboxEnvironmentForRun,
1845
2066
  startupRetryOptions,
1846
2067
  streamTokenDegraded