@opengeni/config 0.16.4 → 0.19.1-canary.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.d.ts +150 -17
- package/dist/index.js +539 -69
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +736 -146
package/dist/index.js
CHANGED
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
DEFAULT_FIRST_PARTY_MCP_TOOLS,
|
|
6
6
|
Entitlements,
|
|
7
7
|
EntitlementsMode,
|
|
8
|
+
KnowledgeSourceSyncLimits,
|
|
8
9
|
LatencyMode,
|
|
9
10
|
MAX_NESTED_AGENT_DEPTH,
|
|
10
11
|
ProductAccessMode,
|
|
11
12
|
ReasoningEffort,
|
|
12
13
|
FIRST_PARTY_MCP_TOOL_NAMES,
|
|
13
14
|
FirstPartyMcpToolName,
|
|
15
|
+
OpenGeniSlackBotDisplayName,
|
|
14
16
|
SandboxBackend,
|
|
15
17
|
SessionMcpApprovalPolicy,
|
|
16
18
|
SEEDANCE_2_5_MODEL_ID,
|
|
@@ -35,7 +37,8 @@ import {
|
|
|
35
37
|
XAI_SUBSCRIPTION_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
36
38
|
XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
|
|
37
39
|
XAI_SUBSCRIPTION_PROVIDER_ID,
|
|
38
|
-
XAI_SUBSCRIPTION_PROXY_BASE_URL
|
|
40
|
+
XAI_SUBSCRIPTION_PROXY_BASE_URL,
|
|
41
|
+
XAI_RESPONSE_STREAM_IDLE_TIMEOUT_MS
|
|
39
42
|
} from "@opengeni/xai-subscription";
|
|
40
43
|
import { XAI_SUBSCRIPTION_MODEL_ID_PREFIX as XAI_SUBSCRIPTION_MODEL_ID_PREFIX2 } from "@opengeni/xai-subscription";
|
|
41
44
|
import { createHash } from "crypto";
|
|
@@ -45,6 +48,8 @@ var registryId = /^[A-Za-z0-9_-]+$/;
|
|
|
45
48
|
var SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS = 60 * 6e4;
|
|
46
49
|
var SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS = 1e4;
|
|
47
50
|
var SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS = SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS - SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS;
|
|
51
|
+
var GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MAX_MS = 6e4;
|
|
52
|
+
var GOOGLE_DRIVE_PROVIDER_RETRY_DELAY_MAX_MS = 6e4;
|
|
48
53
|
var SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS = 60 * 6e4;
|
|
49
54
|
var SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS = 1e4;
|
|
50
55
|
var EnvBoolean = z.preprocess((value) => {
|
|
@@ -60,6 +65,17 @@ var EnvBoolean = z.preprocess((value) => {
|
|
|
60
65
|
}
|
|
61
66
|
return value;
|
|
62
67
|
}, z.boolean());
|
|
68
|
+
var DEFAULT_GOAL_IDLE_BACKOFF_MS = [3e3, 3e4, 12e4, 3e5];
|
|
69
|
+
var DEFAULT_GOAL_IDLE_BACKOFF_MAX_MS = 6e5;
|
|
70
|
+
var EnvGoalIdleBackoffMs = z.preprocess((value) => {
|
|
71
|
+
if (typeof value !== "string") return value;
|
|
72
|
+
const source = value.trim();
|
|
73
|
+
if (!source) return void 0;
|
|
74
|
+
return source.split(",").map((entry) => {
|
|
75
|
+
const trimmed = entry.trim();
|
|
76
|
+
return trimmed === "" ? Number.NaN : Number(trimmed);
|
|
77
|
+
});
|
|
78
|
+
}, z.array(z.number().int().nonnegative()).min(1, "OPENGENI_GOAL_IDLE_BACKOFF_MS must list at least one delay in milliseconds").readonly());
|
|
63
79
|
var EnvFirstPartyMcpTools = z.preprocess(
|
|
64
80
|
(value) => {
|
|
65
81
|
if (typeof value !== "string") return value;
|
|
@@ -245,11 +261,31 @@ var SettingsSchema = z.object({
|
|
|
245
261
|
// Explicit operator-controlled promotion pointer for `/agent/latest/*`.
|
|
246
262
|
// Versioned agent releases are immutable; changing this setting promotes or
|
|
247
263
|
// rolls back the stable channel without moving or deleting a provider tag.
|
|
248
|
-
agentStableVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).default("0.1.
|
|
264
|
+
agentStableVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).default("0.1.16"),
|
|
249
265
|
// Optional independent beta-channel pointer. When unset, the beta update
|
|
250
266
|
// manifest route is unavailable rather than silently serving stable.
|
|
251
267
|
agentBetaVersion: z.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).optional(),
|
|
252
268
|
productAccessMode: ProductAccessMode.default("local"),
|
|
269
|
+
// --- canonical organization-tenancy authority activation, default OFF ---
|
|
270
|
+
// The named PRE-ACTIVATION opt-out for the organization-tenancy program. FALSE (the
|
|
271
|
+
// default, and the value an operator leaves in place to decline or defer) means
|
|
272
|
+
// this deployment stays on the reversible legacy workspace-owned lane: no phase-F
|
|
273
|
+
// subsystem may switch its access decision to organization/membership authority
|
|
274
|
+
// ids. TRUE is an operator's explicit statement that the activation preconditions
|
|
275
|
+
// in docs/organization-tenancy.md have been proven for this deployment and that
|
|
276
|
+
// the one-way boundary is accepted.
|
|
277
|
+
//
|
|
278
|
+
// This is NOT a kill switch and NOT a rollback: once an activation migration has
|
|
279
|
+
// committed, setting it back to false does not restore the legacy authority - only
|
|
280
|
+
// forward recovery is available. It also grants and revokes nothing by itself;
|
|
281
|
+
// every individual authorization decision keeps its own fences.
|
|
282
|
+
//
|
|
283
|
+
// No runtime path reads it yet: canonical activation (phase F) is unshipped, so
|
|
284
|
+
// the flag exists to reserve the name, pin the safe default, and give every future
|
|
285
|
+
// activation slice one gate to consult. EnvBoolean (NOT z.coerce.boolean(), which
|
|
286
|
+
// coerces "false" -> true and would activate the moment an operator wrote the
|
|
287
|
+
// variable out to disable it).
|
|
288
|
+
organizationTenancyCanonicalActivationEnabled: EnvBoolean.default(false),
|
|
253
289
|
billingMode: BillingMode.default("disabled"),
|
|
254
290
|
entitlementsMode: EntitlementsMode.default("none"),
|
|
255
291
|
usageLimitsMode: UsageLimitsMode.default("none"),
|
|
@@ -268,7 +304,6 @@ var SettingsSchema = z.object({
|
|
|
268
304
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
269
305
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
270
306
|
streamControlEnabled: EnvBoolean.default(false),
|
|
271
|
-
codemodeMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
272
307
|
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
273
308
|
// that do not carry the stock-image ogtool binary. Exact stable versions only:
|
|
274
309
|
// the agent must never guess a tag or silently install `latest`.
|
|
@@ -278,12 +313,24 @@ var SettingsSchema = z.object({
|
|
|
278
313
|
integrationsStateSecret: z.string().optional(),
|
|
279
314
|
integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
|
|
280
315
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
281
|
-
gmailRestAdapterEnabled: EnvBoolean.default(false),
|
|
282
316
|
slackClientId: z.string().optional(),
|
|
283
317
|
slackClientSecret: z.string().optional(),
|
|
284
318
|
slackSigningSecret: z.string().optional(),
|
|
319
|
+
slackBotDisplayName: OpenGeniSlackBotDisplayName.default("OpenGeni"),
|
|
320
|
+
slackCommand: z.string().trim().regex(/^\/[a-z0-9_-]{1,31}$/u).default("/opengeni"),
|
|
285
321
|
googleDriveClientId: z.string().optional(),
|
|
286
322
|
googleDriveClientSecret: z.string().optional(),
|
|
323
|
+
googleDriveSyncMaxItems: z.coerce.number().int().positive().max(1e4).default(500),
|
|
324
|
+
googleDriveSyncMaxBytes: z.coerce.number().int().positive().max(5e9).default(5e8),
|
|
325
|
+
googleDriveSyncMaxFileBytes: z.coerce.number().int().positive().max(5e9).default(1e8),
|
|
326
|
+
googleDriveSyncMaxProviderRequests: z.coerce.number().int().positive().max(1e4).default(1e3),
|
|
327
|
+
googleDriveSyncMaxElapsedSeconds: z.coerce.number().int().positive().max(3600).default(300),
|
|
328
|
+
googleDriveSyncMaxFailureDetails: z.coerce.number().int().positive().max(100).default(25),
|
|
329
|
+
googleDriveProviderRequestTimeoutMs: z.coerce.number().int().min(1e3).max(GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MAX_MS).default(3e4),
|
|
330
|
+
googleDriveProviderRetryAttempts: z.coerce.number().int().min(1).max(5).default(3),
|
|
331
|
+
googleDriveProviderRetryInitialDelayMs: z.coerce.number().int().positive().max(3e4).default(250),
|
|
332
|
+
googleDriveProviderRetryMaxDelayMs: z.coerce.number().int().positive().max(GOOGLE_DRIVE_PROVIDER_RETRY_DELAY_MAX_MS).default(5e3),
|
|
333
|
+
googleDriveProviderRetryBudgetMs: z.coerce.number().int().positive().max(12e4).default(15e3),
|
|
287
334
|
fikenClientId: z.string().optional(),
|
|
288
335
|
fikenClientSecret: z.string().optional(),
|
|
289
336
|
googleDriveWorkspaceEventsEnabled: EnvBoolean.optional(),
|
|
@@ -296,24 +343,56 @@ var SettingsSchema = z.object({
|
|
|
296
343
|
// id ("x", "reddit"): {"x":{"clientId":"...","clientSecret":"..."}}.
|
|
297
344
|
socialOauthClientsJson: z.string().default("{}"),
|
|
298
345
|
// Session goal guard rails. Goals are designed for runs that legitimately
|
|
299
|
-
// span days, so length is bounded by
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
346
|
+
// span days, so length is bounded by explicit completion/pause and budget
|
|
347
|
+
// exhaustion, never by count. goalMaxAutoContinuations is therefore UNSET
|
|
348
|
+
// by default (no cap); deployments may configure one, and it then acts as a
|
|
349
|
+
// hard ceiling that per-goal overrides can only lower.
|
|
303
350
|
goalMaxAutoContinuations: z.coerce.number().int().positive().optional(),
|
|
304
|
-
|
|
351
|
+
// Idle backoff between CONSECUTIVE no-input goal continuations. This is
|
|
352
|
+
// pacing, not a cap: the first continuation after a turn that consumed any
|
|
353
|
+
// external input is immediate, the n-th consecutive no-input continuation
|
|
354
|
+
// waits schedule[min(n - 1, last)] ms after the previous one finished, and
|
|
355
|
+
// any new input (machine input, human/API prompt, Steer) wakes the session
|
|
356
|
+
// immediately. The delay never exceeds goalIdleBackoffMaxMs.
|
|
357
|
+
goalIdleBackoffMs: EnvGoalIdleBackoffMs.default(DEFAULT_GOAL_IDLE_BACKOFF_MS),
|
|
358
|
+
goalIdleBackoffMaxMs: z.coerce.number().int().positive().default(DEFAULT_GOAL_IDLE_BACKOFF_MAX_MS),
|
|
359
|
+
// Child lifecycle notices: a child session's requires_action freeze, its
|
|
360
|
+
// resolution, a direct Pause, a provider-capacity wait, and goal progress
|
|
361
|
+
// become typed `session_system_updates` rows for the parent (in addition to
|
|
362
|
+
// `child_terminal_result`). Rolling hazard: a pre-notice worker throws on an
|
|
363
|
+
// unknown update kind, so enable only once the whole fleet runs an image
|
|
364
|
+
// that understands the new kinds. Once the flag has produced rows, a
|
|
365
|
+
// pre-notice image must never restart while any new-kind row is still
|
|
366
|
+
// pending (session_system_updates or session_system_update_outbox); turning
|
|
367
|
+
// the flag back off stops production but does not drain already committed
|
|
368
|
+
// rows. Default off. The API and both workers install the validated value
|
|
369
|
+
// into @opengeni/db once at boot.
|
|
370
|
+
// Env: OPENGENI_CHILD_LIFECYCLE_NOTICES_ENABLED.
|
|
371
|
+
childLifecycleNoticesEnabled: EnvBoolean.default(false),
|
|
372
|
+
// Per-channel and per-DM Slack workspace routing. Default ON. A channel does
|
|
373
|
+
// not count a personal workspace as a candidate, so an organization with one
|
|
374
|
+
// shared workspace resolves it as the sole candidate and never asks; the
|
|
375
|
+
// visible change is confined to organizations that genuinely have more than
|
|
376
|
+
// one - plus one case worth knowing before an upgrade: a person who has lost
|
|
377
|
+
// live authority now receives a posted refusal where the pre-routing code
|
|
378
|
+
// failed silently. Set the env var to `false` to restore the short-circuit
|
|
379
|
+
// to the installation's own workspace.
|
|
380
|
+
// Env: OPENGENI_SLACK_WORKSPACE_ROUTING_ENABLED.
|
|
381
|
+
slackWorkspaceRoutingEnabled: EnvBoolean.default(true),
|
|
305
382
|
// Per-segment ceiling on agent loop turns (model calls) within a single
|
|
306
383
|
// session turn. Effectively unbounded by default for the same reason as
|
|
307
384
|
// above; the graceful max-turns valve (idle + goal continuation, never a
|
|
308
385
|
// session failure) remains as inert safety should a deployment set a cap.
|
|
309
386
|
agentMaxModelCallsPerTurn: z.coerce.number().int().positive().default(1e6),
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
387
|
+
// Deployment fallback for models that do not declare their own window.
|
|
388
|
+
// Built-in billed GPT-5.6 Sol/Terra/Luna pin Codex's 272k catalog instead.
|
|
389
|
+
// OpenGeni always performs one durable, portable plaintext compaction
|
|
390
|
+
// transition; there is no provider/server/off mode ladder.
|
|
313
391
|
contextWindowTokens: z.coerce.number().int().positive().default(105e4),
|
|
314
|
-
// Optional model-catalog effective input ceiling. Codex
|
|
315
|
-
// raw context_window * effective_context_window_percent;
|
|
316
|
-
// the deployment-level window-minus-reserved-output
|
|
392
|
+
// Optional model-catalog effective input ceiling. Codex and billed GPT-5.6
|
|
393
|
+
// models expose this as raw context_window * effective_context_window_percent;
|
|
394
|
+
// when absent, retain the deployment-level window-minus-reserved-output
|
|
395
|
+
// behavior.
|
|
317
396
|
contextEffectiveWindowTokens: z.coerce.number().int().positive().optional(),
|
|
318
397
|
// Proactive compaction threshold as a ratio of the model context window.
|
|
319
398
|
// Defaults to 90%: compact as late as possible — retained context beats early
|
|
@@ -345,6 +424,10 @@ var SettingsSchema = z.object({
|
|
|
345
424
|
apiHost: z.string().default("0.0.0.0"),
|
|
346
425
|
apiPort: z.coerce.number().int().positive().default(8e3),
|
|
347
426
|
workerHttpPort: z.coerce.number().int().positive().default(8001),
|
|
427
|
+
// Worker-side first-party MCP traffic stays on the deployment's internal
|
|
428
|
+
// network. OPENGENI_MCP_URL remains the sandbox/external route used by
|
|
429
|
+
// Codemode and remote placements.
|
|
430
|
+
opengeniMcpInternalUrl: z.string().url().optional(),
|
|
348
431
|
opengeniMcpUrl: z.string().url().optional(),
|
|
349
432
|
// Origins allowed to send browser cookies cross-origin. Other origins may
|
|
350
433
|
// call the public API with bearer credentials, but never receive credentialed
|
|
@@ -429,6 +512,9 @@ var SettingsSchema = z.object({
|
|
|
429
512
|
// account pool and a distinct rail from the existing xai/* API-key provider.
|
|
430
513
|
supergrokSubscriptionEnabled: EnvBoolean.default(false),
|
|
431
514
|
// OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED
|
|
515
|
+
// Maximum silence between complete, valid SuperGrok SSE data events. This is
|
|
516
|
+
// not a request/run duration cap; every valid event resets the timer.
|
|
517
|
+
supergrokResponseStreamIdleTimeoutMs: z.coerce.number().int().positive().max(24 * 60 * 6e4).default(XAI_RESPONSE_STREAM_IDLE_TIMEOUT_MS),
|
|
432
518
|
// Expose the connected apps attached to a Codex subscription through the
|
|
433
519
|
// synthetic codex_apps MCP server. Independent from subscription routing so
|
|
434
520
|
// operators can use Codex models without exposing ChatGPT connectors.
|
|
@@ -437,12 +523,13 @@ var SettingsSchema = z.object({
|
|
|
437
523
|
codexProductSku: z.string().optional(),
|
|
438
524
|
// OPENGENI_CODEX_PRODUCT_SKU (X-OpenAI-Product-Sku, apps only)
|
|
439
525
|
// Progressive MCP disclosure (Codex-CLI-style tool_search): on a codex turn,
|
|
440
|
-
// flag non-
|
|
526
|
+
// flag non-eager selected MCP tools `defer_loading:true` (dropping their
|
|
441
527
|
// schemas from model context) and add one client-executed tool_search tool
|
|
442
|
-
// that BM25-discloses bounded matches.
|
|
443
|
-
// eager
|
|
444
|
-
//
|
|
445
|
-
//
|
|
528
|
+
// that BM25-discloses bounded matches. Only an exact session tool ref with
|
|
529
|
+
// `eager:true` stays on the startup path; mandatory selection alone does not
|
|
530
|
+
// imply eagerness. Default ON so selected connector catalogues do not consume
|
|
531
|
+
// every Codex turn's context. Operators may explicitly disable it for
|
|
532
|
+
// emergency compatibility diagnosis.
|
|
446
533
|
// OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
447
534
|
codexToolSearchEnabled: EnvBoolean.default(true),
|
|
448
535
|
// Provider-neutral progressive disclosure for direct OpenAI/Azure native
|
|
@@ -673,6 +760,31 @@ var SettingsSchema = z.object({
|
|
|
673
760
|
vercelProjectId: z.string().optional(),
|
|
674
761
|
vercelTeamId: z.string().optional(),
|
|
675
762
|
vercelRuntime: z.string().optional(),
|
|
763
|
+
// --- OpenSandbox (optional Kubernetes-native provisioned sandbox) ---
|
|
764
|
+
openSandboxBaseUrl: z.string().url().optional(),
|
|
765
|
+
openSandboxApiKey: z.string().min(1).optional(),
|
|
766
|
+
// Release and preview profiles must provide an immutable OCI digest. The
|
|
767
|
+
// adapter refuses tag-only references when this backend is active.
|
|
768
|
+
openSandboxImage: z.string().min(1).optional(),
|
|
769
|
+
// Renewable provider TTL is a leak/backstop clock, not OpenGeni's idle
|
|
770
|
+
// policy. The pinned server accepts a one-minute minimum; ordinary
|
|
771
|
+
// deployments default to one hour.
|
|
772
|
+
openSandboxTtlSeconds: z.coerce.number().int().min(60).max(86400).default(3600),
|
|
773
|
+
openSandboxUseServerProxy: EnvBoolean.default(true),
|
|
774
|
+
// Channel B (browserd / noVNC / ttyd) uses OSEP-0011 signed URI-mode ingress.
|
|
775
|
+
// Exec/files stay on the private lifecycle server-proxy regardless of this flag.
|
|
776
|
+
openSandboxSignedEndpoints: EnvBoolean.default(false),
|
|
777
|
+
openSandboxSignedEndpointTtlSeconds: z.coerce.number().int().min(60).max(3600).default(600),
|
|
778
|
+
openSandboxChannelBPublicBaseUrl: z.string().url().optional(),
|
|
779
|
+
// Emergency hatch only: force JPEG/RFB through the API frame-proxy even when
|
|
780
|
+
// signed endpoints are on (M2 subprotocol failure). Unset means OpenSandbox
|
|
781
|
+
// uses the frame-proxy unless signed endpoints are on.
|
|
782
|
+
openSandboxInteractionFrameProxy: EnvBoolean.optional(),
|
|
783
|
+
openSandboxPoolRef: z.string().regex(/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/).optional(),
|
|
784
|
+
// Optional same-cluster, read-only observability projection. The application
|
|
785
|
+
// chart sets this only on the control worker and mounts a dedicated projected
|
|
786
|
+
// service-account token; non-Kubernetes and remote-provider deployments omit it.
|
|
787
|
+
openSandboxKubernetesInventoryNamespace: z.string().min(1).max(63).regex(/^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/).optional(),
|
|
676
788
|
// --- sandbox ownership inversion (P1.2 rollout flag, default OFF) ---
|
|
677
789
|
// The keystone flag for the stateless resume-by-id model. When FALSE the
|
|
678
790
|
// agent-turn path is BYTE-FOR-BYTE today's build-and-discard behavior (no
|
|
@@ -717,7 +829,7 @@ var SettingsSchema = z.object({
|
|
|
717
829
|
// runner must ALSO advertise Capabilities.op_stream. Streaming is the default
|
|
718
830
|
// because it is the only transport that can keep a command alive without an
|
|
719
831
|
// arbitrary request/reply wall while still supporting replay and cancellation.
|
|
720
|
-
//
|
|
832
|
+
// Exec fails closed when the deployment or runner does not provide op-stream.
|
|
721
833
|
// EnvBoolean (NOT
|
|
722
834
|
// z.coerce.boolean(), which coerces "false" -> true).
|
|
723
835
|
agentOpStreamEnabled: EnvBoolean.default(true),
|
|
@@ -749,9 +861,10 @@ var SettingsSchema = z.object({
|
|
|
749
861
|
// nats-server is configured with AUTH CALLOUT: an external agent connects
|
|
750
862
|
// presenting its `oge_` enrollment bearer as the connect auth-token; the server
|
|
751
863
|
// issues an authorization request on $SYS.REQ.USER.AUTH to our responder, which
|
|
752
|
-
// validates the bearer and returns a SIGNED NATS
|
|
753
|
-
//
|
|
754
|
-
//
|
|
864
|
+
// validates the bearer, claims one daemon generation, and returns a SIGNED NATS
|
|
865
|
+
// user JWT scoped to that exact process subtree (+ `_INBOX.>`). The exact scope
|
|
866
|
+
// provides both workspace isolation and single-daemon routing authority. These
|
|
867
|
+
// are deployment-level secrets in the opengeni-runtime secret
|
|
755
868
|
// (Helm-clobbered configmap avoided), all OPTIONAL: when the callout plane is not
|
|
756
869
|
// configured the responder simply does not start (selfhosted agents cannot
|
|
757
870
|
// connect — graceful, never a boot-fail).
|
|
@@ -763,7 +876,7 @@ var SettingsSchema = z.object({
|
|
|
763
876
|
// The TARGET ACCOUNT NAME the minted user is placed into (the server-config-mode
|
|
764
877
|
// `auth_callout.account`, e.g. "APP"). The responder writes it as the minted user
|
|
765
878
|
// JWT `aud` so nats-server binds the agent to this account — the SAME account the
|
|
766
|
-
// privileged control plane connects into, so
|
|
879
|
+
// privileged control plane connects into, so exact process request/reply
|
|
767
880
|
// routes. Optional; resolveNatsCalloutConfig defaults it to "APP".
|
|
768
881
|
selfhostedNatsCalloutAccountName: z.string().optional(),
|
|
769
882
|
// The callout RESPONDER's own NATS login (one of the `auth_callout.auth_users`
|
|
@@ -772,7 +885,7 @@ var SettingsSchema = z.object({
|
|
|
772
885
|
selfhostedNatsCalloutUser: z.string().optional(),
|
|
773
886
|
selfhostedNatsCalloutPassword: z.string().optional(),
|
|
774
887
|
// The PRIVILEGED control-plane login (api/worker): a static account user that may
|
|
775
|
-
// request
|
|
888
|
+
// request exact process RPC subjects + receive their inbox replies. The event bus + the
|
|
776
889
|
// selfhosted control RPC ride THIS connection. Username/password; when unset the
|
|
777
890
|
// bus connects anonymously (local dev / a NATS with no auth_callout).
|
|
778
891
|
selfhostedNatsControlUser: z.string().optional(),
|
|
@@ -851,6 +964,17 @@ var SettingsSchema = z.object({
|
|
|
851
964
|
// (a liveness/reaper cadence), this bounds how long one turn waits for capacity
|
|
852
965
|
// or provider creation before surfacing a clear turn.failed error.
|
|
853
966
|
sandboxWarmingTimeoutMs: z.coerce.number().int().positive().default(6e5),
|
|
967
|
+
// Request-scoped workspace control-prefix budget: how long one HTTP-originated
|
|
968
|
+
// session/workspace mutation (Send, Steer, Pause/Resume/Cancel, queue
|
|
969
|
+
// move/edit/delete, composer draft, settings narrowing, quiescent tree
|
|
970
|
+
// deletion) may wait to enter the fair `workspace_inference_controls` prefix
|
|
971
|
+
// before failing with the retryable 503 `WORKSPACE_CONTROL_BUSY`. Worker
|
|
972
|
+
// settlement and claims never use it. The API installs the validated value
|
|
973
|
+
// into @opengeni/db once at app construction; nothing reads the env per
|
|
974
|
+
// request. Env: OPENGENI_WORKSPACE_CONTROL_LOCK_TIMEOUT_MS. Default 20 s.
|
|
975
|
+
workspaceControlLockTimeoutMs: z.coerce.number({
|
|
976
|
+
message: "OPENGENI_WORKSPACE_CONTROL_LOCK_TIMEOUT_MS must be a positive integer (ms)"
|
|
977
|
+
}).int("OPENGENI_WORKSPACE_CONTROL_LOCK_TIMEOUT_MS must be a positive integer (ms)").positive("OPENGENI_WORKSPACE_CONTROL_LOCK_TIMEOUT_MS must be a positive integer (ms)").default(2e4),
|
|
854
978
|
// Rig setup-script budget (M3): the wall-clock timeout the rig-setup lifecycle
|
|
855
979
|
// hook runs its script under, distinct from the 120s per-command lifecycle
|
|
856
980
|
// default (a rig may compile/install heavy tooling on first cold create).
|
|
@@ -908,6 +1032,11 @@ var SettingsSchema = z.object({
|
|
|
908
1032
|
githubAppId: z.string().optional(),
|
|
909
1033
|
githubClientId: z.string().optional(),
|
|
910
1034
|
githubClientSecret: z.string().optional(),
|
|
1035
|
+
/** Default-off rollout for the in-process GitHub repository API tool surface. */
|
|
1036
|
+
githubRestMcpEnabled: EnvBoolean.default(false),
|
|
1037
|
+
githubPersonalOauthEnabled: EnvBoolean.default(false),
|
|
1038
|
+
githubPersonalOauthClientId: z.string().optional(),
|
|
1039
|
+
githubPersonalOauthClientSecret: z.string().optional(),
|
|
911
1040
|
githubAppSlug: z.string().optional(),
|
|
912
1041
|
githubWebhookSecret: z.string().optional(),
|
|
913
1042
|
githubAppPrivateKey: z.string().optional(),
|
|
@@ -942,6 +1071,46 @@ var SettingsSchema = z.object({
|
|
|
942
1071
|
})
|
|
943
1072
|
).default([])
|
|
944
1073
|
});
|
|
1074
|
+
function configuredGoogleDriveSyncLimits(settings) {
|
|
1075
|
+
return KnowledgeSourceSyncLimits.parse({
|
|
1076
|
+
maxItems: settings.googleDriveSyncMaxItems,
|
|
1077
|
+
maxBytes: settings.googleDriveSyncMaxBytes,
|
|
1078
|
+
maxFileBytes: settings.googleDriveSyncMaxFileBytes,
|
|
1079
|
+
maxProviderRequests: settings.googleDriveSyncMaxProviderRequests,
|
|
1080
|
+
maxElapsedSeconds: settings.googleDriveSyncMaxElapsedSeconds,
|
|
1081
|
+
maxFailureDetails: settings.googleDriveSyncMaxFailureDetails
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
function googleDriveProviderRetryOptions(settings) {
|
|
1085
|
+
return {
|
|
1086
|
+
requestTimeoutMs: settings.googleDriveProviderRequestTimeoutMs,
|
|
1087
|
+
attempts: settings.googleDriveProviderRetryAttempts,
|
|
1088
|
+
initialDelayMs: settings.googleDriveProviderRetryInitialDelayMs,
|
|
1089
|
+
maxDelayMs: settings.googleDriveProviderRetryMaxDelayMs,
|
|
1090
|
+
budgetMs: settings.googleDriveProviderRetryBudgetMs
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function canonicalPublicOrigin(publicBaseUrl) {
|
|
1094
|
+
if (!publicBaseUrl) return null;
|
|
1095
|
+
let parsed;
|
|
1096
|
+
try {
|
|
1097
|
+
parsed = new URL(publicBaseUrl);
|
|
1098
|
+
} catch {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.pathname !== "" && parsed.pathname !== "/") {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
return parsed.origin;
|
|
1105
|
+
}
|
|
1106
|
+
function googleDriveOAuthCallbackUrl(publicBaseUrl) {
|
|
1107
|
+
const origin = canonicalPublicOrigin(publicBaseUrl);
|
|
1108
|
+
return origin ? `${origin}/v1/integrations/google-drive/callback` : null;
|
|
1109
|
+
}
|
|
1110
|
+
function personalGitHubOAuthCallbackUrl(publicBaseUrl) {
|
|
1111
|
+
const origin = canonicalPublicOrigin(publicBaseUrl);
|
|
1112
|
+
return origin ? `${origin}/v1/integrations/github-personal/oauth/callback` : null;
|
|
1113
|
+
}
|
|
945
1114
|
function isUsableVoiceInputSecret(value) {
|
|
946
1115
|
if (value == null) return false;
|
|
947
1116
|
const trimmed = value.trim();
|
|
@@ -1157,8 +1326,10 @@ var ModelCapabilitiesV1Schema = z.object({
|
|
|
1157
1326
|
}
|
|
1158
1327
|
});
|
|
1159
1328
|
var ModelProviderApi = z.enum(["responses", "chat"]);
|
|
1329
|
+
var ModelProviderWireProfile = z.enum(["openai", "azure-openai"]);
|
|
1160
1330
|
var RegistryProviderKind = z.enum([
|
|
1161
1331
|
"api-key",
|
|
1332
|
+
"anonymous",
|
|
1162
1333
|
"codex-subscription",
|
|
1163
1334
|
"xai-subscription",
|
|
1164
1335
|
"vercel-gateway-managed",
|
|
@@ -1213,6 +1384,7 @@ var RegistryProviderSchema = z.object({
|
|
|
1213
1384
|
// stable provider id, e.g. "fireworks"
|
|
1214
1385
|
label: z.string().min(1).optional(),
|
|
1215
1386
|
api: ModelProviderApi.default("chat"),
|
|
1387
|
+
wireProfile: ModelProviderWireProfile.default("openai"),
|
|
1216
1388
|
baseUrl: z.string().url(),
|
|
1217
1389
|
apiKey: z.string().optional(),
|
|
1218
1390
|
// inline key (pragmatic) ...
|
|
@@ -1227,6 +1399,52 @@ var RegistryProviderSchema = z.object({
|
|
|
1227
1399
|
credentialSource: z.never().optional(),
|
|
1228
1400
|
billing: z.never().optional(),
|
|
1229
1401
|
models: z.array(RegistryModelSchema).min(1)
|
|
1402
|
+
}).superRefine((provider, ctx) => {
|
|
1403
|
+
if (provider.kind !== "anonymous") {
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
if (provider.apiKey !== void 0) {
|
|
1407
|
+
ctx.addIssue({
|
|
1408
|
+
code: "custom",
|
|
1409
|
+
path: ["apiKey"],
|
|
1410
|
+
message: "anonymous providers must not declare apiKey"
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
if (provider.apiKeyEnv !== void 0) {
|
|
1414
|
+
ctx.addIssue({
|
|
1415
|
+
code: "custom",
|
|
1416
|
+
path: ["apiKeyEnv"],
|
|
1417
|
+
message: "anonymous providers must not declare apiKeyEnv"
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
if (provider.defaultHeaders !== void 0) {
|
|
1421
|
+
ctx.addIssue({
|
|
1422
|
+
code: "custom",
|
|
1423
|
+
path: ["defaultHeaders"],
|
|
1424
|
+
message: "anonymous providers must not declare defaultHeaders"
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
if (provider.defaultQuery !== void 0) {
|
|
1428
|
+
ctx.addIssue({
|
|
1429
|
+
code: "custom",
|
|
1430
|
+
path: ["defaultQuery"],
|
|
1431
|
+
message: "anonymous providers must not declare defaultQuery"
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
if (provider.publicDefaultHeaderNames !== void 0) {
|
|
1435
|
+
ctx.addIssue({
|
|
1436
|
+
code: "custom",
|
|
1437
|
+
path: ["publicDefaultHeaderNames"],
|
|
1438
|
+
message: "anonymous providers must not declare publicDefaultHeaderNames"
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
if (provider.publicDefaultQueryNames !== void 0) {
|
|
1442
|
+
ctx.addIssue({
|
|
1443
|
+
code: "custom",
|
|
1444
|
+
path: ["publicDefaultQueryNames"],
|
|
1445
|
+
message: "anonymous providers must not declare publicDefaultQueryNames"
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1230
1448
|
});
|
|
1231
1449
|
var IntegrationOAuthClientConfigSchema = z.object({
|
|
1232
1450
|
clientId: z.string().min(1),
|
|
@@ -1410,6 +1628,11 @@ var SANDBOX_REQUIRED_ENV = {
|
|
|
1410
1628
|
{ field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
|
|
1411
1629
|
{ field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
|
|
1412
1630
|
],
|
|
1631
|
+
opensandbox: [
|
|
1632
|
+
{ field: "openSandboxBaseUrl", env: "OPENGENI_OPENSANDBOX_BASE_URL" },
|
|
1633
|
+
{ field: "openSandboxApiKey", env: "OPENGENI_OPENSANDBOX_API_KEY" },
|
|
1634
|
+
{ field: "openSandboxImage", env: "OPENGENI_OPENSANDBOX_IMAGE" }
|
|
1635
|
+
],
|
|
1413
1636
|
// selfhosted needs NO per-box credentials: it is the user's own machine reached
|
|
1414
1637
|
// over the agent's own enrollment. The enrollment-signing + relay-token secrets
|
|
1415
1638
|
// are deployment-level (a single runtime secret, not per-active-backend creds),
|
|
@@ -1419,6 +1642,28 @@ var SANDBOX_REQUIRED_ENV = {
|
|
|
1419
1642
|
function requiredSandboxEnvForBackend(backend) {
|
|
1420
1643
|
return (SANDBOX_REQUIRED_ENV[backend] ?? []).map((entry) => entry.env);
|
|
1421
1644
|
}
|
|
1645
|
+
function objectStorageConfiguredForWorkspaceArchives(settings) {
|
|
1646
|
+
switch (settings.objectStorageBackend) {
|
|
1647
|
+
case "azure-blob":
|
|
1648
|
+
return Boolean(
|
|
1649
|
+
settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName && settings.objectStorageAzureAccountKey
|
|
1650
|
+
);
|
|
1651
|
+
case "gcs":
|
|
1652
|
+
return Boolean(
|
|
1653
|
+
settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsProjectId
|
|
1654
|
+
);
|
|
1655
|
+
case "aws-s3":
|
|
1656
|
+
return true;
|
|
1657
|
+
case "s3-compatible":
|
|
1658
|
+
return Boolean(
|
|
1659
|
+
settings.objectStorageEndpoint && settings.objectStorageAccessKeyId && settings.objectStorageSecretAccessKey
|
|
1660
|
+
);
|
|
1661
|
+
default: {
|
|
1662
|
+
const _exhaustive = settings.objectStorageBackend;
|
|
1663
|
+
return _exhaustive;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1422
1667
|
function optional(name) {
|
|
1423
1668
|
const value = process.env[name];
|
|
1424
1669
|
return value && value.trim().length > 0 ? value : void 0;
|
|
@@ -1473,6 +1718,9 @@ function getSettings() {
|
|
|
1473
1718
|
agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
|
|
1474
1719
|
agentBetaVersion: optional("OPENGENI_AGENT_BETA_VERSION"),
|
|
1475
1720
|
productAccessMode: optional("OPENGENI_PRODUCT_ACCESS_MODE"),
|
|
1721
|
+
organizationTenancyCanonicalActivationEnabled: optional(
|
|
1722
|
+
"OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED"
|
|
1723
|
+
),
|
|
1476
1724
|
billingMode: optional("OPENGENI_BILLING_MODE"),
|
|
1477
1725
|
entitlementsMode: optional("OPENGENI_ENTITLEMENTS_MODE"),
|
|
1478
1726
|
usageLimitsMode: optional("OPENGENI_USAGE_LIMITS_MODE"),
|
|
@@ -1483,7 +1731,6 @@ function getSettings() {
|
|
|
1483
1731
|
allowedFirstPartyMcpTools: optional("OPENGENI_ALLOWED_FIRST_PARTY_MCP_TOOLS"),
|
|
1484
1732
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
1485
1733
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1486
|
-
codemodeMaxCallsPerTurn: optional("OPENGENI_CODEMODE_MAX_CALLS_PER_TURN"),
|
|
1487
1734
|
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
1488
1735
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
1489
1736
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
@@ -1492,12 +1739,32 @@ function getSettings() {
|
|
|
1492
1739
|
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
|
|
1493
1740
|
),
|
|
1494
1741
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1495
|
-
gmailRestAdapterEnabled: optional("OPENGENI_GMAIL_REST_ADAPTER_ENABLED"),
|
|
1496
1742
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1497
1743
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1498
1744
|
slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
|
|
1745
|
+
slackBotDisplayName: optional("OPENGENI_SLACK_BOT_DISPLAY_NAME"),
|
|
1746
|
+
slackCommand: optional("OPENGENI_SLACK_COMMAND"),
|
|
1499
1747
|
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1500
1748
|
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1749
|
+
googleDriveSyncMaxItems: optional("OPENGENI_GOOGLE_DRIVE_SYNC_MAX_ITEMS"),
|
|
1750
|
+
googleDriveSyncMaxBytes: optional("OPENGENI_GOOGLE_DRIVE_SYNC_MAX_BYTES"),
|
|
1751
|
+
googleDriveSyncMaxFileBytes: optional("OPENGENI_GOOGLE_DRIVE_SYNC_MAX_FILE_BYTES"),
|
|
1752
|
+
googleDriveSyncMaxProviderRequests: optional(
|
|
1753
|
+
"OPENGENI_GOOGLE_DRIVE_SYNC_MAX_PROVIDER_REQUESTS"
|
|
1754
|
+
),
|
|
1755
|
+
googleDriveSyncMaxElapsedSeconds: optional("OPENGENI_GOOGLE_DRIVE_SYNC_MAX_ELAPSED_SECONDS"),
|
|
1756
|
+
googleDriveSyncMaxFailureDetails: optional("OPENGENI_GOOGLE_DRIVE_SYNC_MAX_FAILURE_DETAILS"),
|
|
1757
|
+
googleDriveProviderRequestTimeoutMs: optional(
|
|
1758
|
+
"OPENGENI_GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MS"
|
|
1759
|
+
),
|
|
1760
|
+
googleDriveProviderRetryAttempts: optional("OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_ATTEMPTS"),
|
|
1761
|
+
googleDriveProviderRetryInitialDelayMs: optional(
|
|
1762
|
+
"OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_INITIAL_DELAY_MS"
|
|
1763
|
+
),
|
|
1764
|
+
googleDriveProviderRetryMaxDelayMs: optional(
|
|
1765
|
+
"OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_MAX_DELAY_MS"
|
|
1766
|
+
),
|
|
1767
|
+
googleDriveProviderRetryBudgetMs: optional("OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_BUDGET_MS"),
|
|
1501
1768
|
fikenClientId: optional("OPENGENI_FIKEN_OAUTH_CLIENT_ID"),
|
|
1502
1769
|
fikenClientSecret: optional("OPENGENI_FIKEN_OAUTH_CLIENT_SECRET"),
|
|
1503
1770
|
googleDriveWorkspaceEventsEnabled: optional("OPENGENI_GOOGLE_DRIVE_WORKSPACE_EVENTS_ENABLED"),
|
|
@@ -1506,7 +1773,10 @@ function getSettings() {
|
|
|
1506
1773
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1507
1774
|
socialOauthClientsJson: optional("OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON"),
|
|
1508
1775
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
1509
|
-
|
|
1776
|
+
goalIdleBackoffMs: optional("OPENGENI_GOAL_IDLE_BACKOFF_MS"),
|
|
1777
|
+
goalIdleBackoffMaxMs: optional("OPENGENI_GOAL_IDLE_BACKOFF_MAX_MS"),
|
|
1778
|
+
childLifecycleNoticesEnabled: optional("OPENGENI_CHILD_LIFECYCLE_NOTICES_ENABLED"),
|
|
1779
|
+
slackWorkspaceRoutingEnabled: optional("OPENGENI_SLACK_WORKSPACE_ROUTING_ENABLED"),
|
|
1510
1780
|
agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
|
|
1511
1781
|
contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
|
|
1512
1782
|
contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
|
|
@@ -1521,6 +1791,7 @@ function getSettings() {
|
|
|
1521
1791
|
apiHost: optional("OPENGENI_API_HOST"),
|
|
1522
1792
|
apiPort: optional("OPENGENI_API_PORT"),
|
|
1523
1793
|
workerHttpPort: optional("OPENGENI_WORKER_HTTP_PORT"),
|
|
1794
|
+
opengeniMcpInternalUrl: optional("OPENGENI_MCP_INTERNAL_URL"),
|
|
1524
1795
|
opengeniMcpUrl: optional("OPENGENI_MCP_URL"),
|
|
1525
1796
|
corsAllowOriginRegex: optional("OPENGENI_CORS_ALLOW_ORIGIN_REGEX"),
|
|
1526
1797
|
openaiProvider: optional("OPENGENI_OPENAI_PROVIDER"),
|
|
@@ -1577,6 +1848,9 @@ function getSettings() {
|
|
|
1577
1848
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
1578
1849
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
1579
1850
|
supergrokSubscriptionEnabled: optional("OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED"),
|
|
1851
|
+
supergrokResponseStreamIdleTimeoutMs: optional(
|
|
1852
|
+
"OPENGENI_SUPERGROK_RESPONSE_STREAM_IDLE_TIMEOUT_MS"
|
|
1853
|
+
),
|
|
1580
1854
|
codexConnectedAppsEnabled: optional("OPENGENI_CODEX_CONNECTED_APPS_ENABLED"),
|
|
1581
1855
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
1582
1856
|
lazyToolSearchEnabled: optional("OPENGENI_LAZY_TOOL_SEARCH_ENABLED"),
|
|
@@ -1666,6 +1940,21 @@ function getSettings() {
|
|
|
1666
1940
|
vercelProjectId: optional("OPENGENI_VERCEL_PROJECT_ID"),
|
|
1667
1941
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
1668
1942
|
vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
|
|
1943
|
+
openSandboxBaseUrl: optional("OPENGENI_OPENSANDBOX_BASE_URL"),
|
|
1944
|
+
openSandboxApiKey: optional("OPENGENI_OPENSANDBOX_API_KEY"),
|
|
1945
|
+
openSandboxImage: optional("OPENGENI_OPENSANDBOX_IMAGE"),
|
|
1946
|
+
openSandboxTtlSeconds: optional("OPENGENI_OPENSANDBOX_TTL_SECONDS"),
|
|
1947
|
+
openSandboxUseServerProxy: optional("OPENGENI_OPENSANDBOX_USE_SERVER_PROXY"),
|
|
1948
|
+
openSandboxSignedEndpoints: optional("OPENGENI_OPENSANDBOX_SIGNED_ENDPOINTS"),
|
|
1949
|
+
openSandboxSignedEndpointTtlSeconds: optional(
|
|
1950
|
+
"OPENGENI_OPENSANDBOX_SIGNED_ENDPOINT_TTL_SECONDS"
|
|
1951
|
+
),
|
|
1952
|
+
openSandboxChannelBPublicBaseUrl: optional("OPENGENI_OPENSANDBOX_CHANNEL_B_PUBLIC_BASE_URL"),
|
|
1953
|
+
openSandboxInteractionFrameProxy: optional("OPENGENI_OPENSANDBOX_INTERACTION_FRAME_PROXY"),
|
|
1954
|
+
openSandboxPoolRef: optional("OPENGENI_OPENSANDBOX_POOL_REF"),
|
|
1955
|
+
openSandboxKubernetesInventoryNamespace: optional(
|
|
1956
|
+
"OPENGENI_OPENSANDBOX_KUBERNETES_INVENTORY_NAMESPACE"
|
|
1957
|
+
),
|
|
1669
1958
|
sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
|
|
1670
1959
|
rigVerificationLeaseOwnershipEnabled: optional(
|
|
1671
1960
|
"OPENGENI_RIG_VERIFICATION_LEASE_OWNERSHIP_ENABLED"
|
|
@@ -1697,6 +1986,7 @@ function getSettings() {
|
|
|
1697
1986
|
sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
|
|
1698
1987
|
sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
|
|
1699
1988
|
sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
|
|
1989
|
+
workspaceControlLockTimeoutMs: optional("OPENGENI_WORKSPACE_CONTROL_LOCK_TIMEOUT_MS"),
|
|
1700
1990
|
rigSetupTimeoutMs: optional("OPENGENI_RIG_SETUP_TIMEOUT_MS"),
|
|
1701
1991
|
sandboxWarmRateMicrosPerSecondJson: optional(
|
|
1702
1992
|
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"
|
|
@@ -1743,6 +2033,10 @@ function getSettings() {
|
|
|
1743
2033
|
githubAppId: optional("OPENGENI_GITHUB_APP_ID"),
|
|
1744
2034
|
githubClientId: optional("OPENGENI_GITHUB_CLIENT_ID"),
|
|
1745
2035
|
githubClientSecret: optional("OPENGENI_GITHUB_CLIENT_SECRET"),
|
|
2036
|
+
githubRestMcpEnabled: optional("OPENGENI_GITHUB_REST_MCP_ENABLED"),
|
|
2037
|
+
githubPersonalOauthEnabled: optional("OPENGENI_GITHUB_PERSONAL_OAUTH_ENABLED"),
|
|
2038
|
+
githubPersonalOauthClientId: optional("OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_ID"),
|
|
2039
|
+
githubPersonalOauthClientSecret: optional("OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_SECRET"),
|
|
1746
2040
|
githubAppSlug: optional("OPENGENI_GITHUB_APP_SLUG"),
|
|
1747
2041
|
githubWebhookSecret: optional("OPENGENI_GITHUB_WEBHOOK_SECRET"),
|
|
1748
2042
|
githubAppPrivateKey: optional("OPENGENI_GITHUB_APP_PRIVATE_KEY"),
|
|
@@ -1761,8 +2055,8 @@ function getSettings() {
|
|
|
1761
2055
|
const parsed = SettingsSchema.parse(raw);
|
|
1762
2056
|
const settings = {
|
|
1763
2057
|
...parsed,
|
|
1764
|
-
sandboxIdleGraceMs: raw.sandboxIdleGraceMs === void 0 ? Math.min(9e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxIdleGraceMs,
|
|
1765
|
-
sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
|
|
2058
|
+
sandboxIdleGraceMs: raw.sandboxIdleGraceMs === void 0 && parsed.sandboxBackend === "modal" ? Math.min(9e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxIdleGraceMs,
|
|
2059
|
+
sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 && parsed.sandboxBackend === "modal" ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
|
|
1766
2060
|
mcpServers: ensureBuiltInMcpServers(parsed)
|
|
1767
2061
|
};
|
|
1768
2062
|
validateSettings(settings);
|
|
@@ -1795,6 +2089,30 @@ function allowedFirstPartyMcpToolsForSession(settings, selected) {
|
|
|
1795
2089
|
function effectiveModalIdleTimeoutSeconds(settings) {
|
|
1796
2090
|
return settings.modalIdleTimeoutSeconds ?? settings.modalTimeoutSeconds;
|
|
1797
2091
|
}
|
|
2092
|
+
function effectiveSandboxLifecycle(settings, backend = settings.sandboxBackend) {
|
|
2093
|
+
if (backend === "modal") {
|
|
2094
|
+
return {
|
|
2095
|
+
hardLifetimeMs: settings.modalTimeoutSeconds * 1e3,
|
|
2096
|
+
renewableTtlSeconds: null,
|
|
2097
|
+
providerIdleTimeoutMs: effectiveModalIdleTimeoutSeconds(settings) * 1e3,
|
|
2098
|
+
rotationLeadMs: settings.sandboxRotationLeadMs
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
if (backend === "opensandbox") {
|
|
2102
|
+
return {
|
|
2103
|
+
hardLifetimeMs: null,
|
|
2104
|
+
renewableTtlSeconds: settings.openSandboxTtlSeconds,
|
|
2105
|
+
providerIdleTimeoutMs: null,
|
|
2106
|
+
rotationLeadMs: null
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
return {
|
|
2110
|
+
hardLifetimeMs: CAPABILITY_DESCRIPTORS[backend].lifetime.hardLifetimeMs ?? null,
|
|
2111
|
+
renewableTtlSeconds: null,
|
|
2112
|
+
providerIdleTimeoutMs: null,
|
|
2113
|
+
rotationLeadMs: null
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
1798
2116
|
function sandboxArchiveCaptureTimeoutMs(settings) {
|
|
1799
2117
|
return Math.min(
|
|
1800
2118
|
SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
|
|
@@ -2118,6 +2436,7 @@ function gatewayRegistryProvider(settings, input) {
|
|
|
2118
2436
|
// Model-specific compatibility stays at the reviewed request fence rather
|
|
2119
2437
|
// than downgrading the whole provider wire.
|
|
2120
2438
|
api: "responses",
|
|
2439
|
+
wireProfile: "openai",
|
|
2121
2440
|
baseUrl: VERCEL_AI_GATEWAY_BASE_URL,
|
|
2122
2441
|
...input.apiKey ? { apiKey: input.apiKey } : {},
|
|
2123
2442
|
models
|
|
@@ -2198,8 +2517,22 @@ function productShortLabelForModelId(modelId) {
|
|
|
2198
2517
|
return null;
|
|
2199
2518
|
}
|
|
2200
2519
|
}
|
|
2520
|
+
var BUILTIN_GPT56_MODEL_IDS = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"];
|
|
2521
|
+
function isBuiltinGpt56ModelId(modelId) {
|
|
2522
|
+
return BUILTIN_GPT56_MODEL_IDS.includes(modelId);
|
|
2523
|
+
}
|
|
2524
|
+
function builtinContextLimitsForModel(settings, modelId) {
|
|
2525
|
+
if (isBuiltinGpt56ModelId(modelId)) {
|
|
2526
|
+
return {
|
|
2527
|
+
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
2528
|
+
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
2529
|
+
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
return { contextWindowTokens: settings.contextWindowTokens };
|
|
2533
|
+
}
|
|
2201
2534
|
function builtinLatencyModesForModel(modelId) {
|
|
2202
|
-
if (modelId
|
|
2535
|
+
if (isBuiltinGpt56ModelId(modelId) || modelId.startsWith("codex/gpt-5.6-")) {
|
|
2203
2536
|
return [
|
|
2204
2537
|
{ id: "standard", upstream: "supported", runnable: true },
|
|
2205
2538
|
{
|
|
@@ -2217,7 +2550,7 @@ function builtinPromptCachingForModel(modelId) {
|
|
|
2217
2550
|
return slug.startsWith("gpt-5.6-") ? { upstream: "supported", runnable: true, mode: "implicit" } : void 0;
|
|
2218
2551
|
}
|
|
2219
2552
|
function builtinHostedImageGenerationForModel(settings, modelId) {
|
|
2220
|
-
return settings.openaiProvider === "openai" && isDirectOpenAiApiBaseUrl(settings.openaiBaseUrl) &&
|
|
2553
|
+
return settings.openaiProvider === "openai" && isDirectOpenAiApiBaseUrl(settings.openaiBaseUrl) && isBuiltinGpt56ModelId(modelId);
|
|
2221
2554
|
}
|
|
2222
2555
|
function isDirectOpenAiApiBaseUrl(baseUrl) {
|
|
2223
2556
|
if (baseUrl === void 0) return true;
|
|
@@ -2262,6 +2595,9 @@ function assertLatencyModeRunnable(settings, modelId, latencyMode) {
|
|
|
2262
2595
|
}
|
|
2263
2596
|
}
|
|
2264
2597
|
function registryCredentialSource(provider) {
|
|
2598
|
+
if (provider.kind === "anonymous") {
|
|
2599
|
+
return { kind: "deployment", mechanism: "none" };
|
|
2600
|
+
}
|
|
2265
2601
|
if (provider.kind === "codex-subscription") {
|
|
2266
2602
|
return { kind: "connected_subscription", provider: "codex" };
|
|
2267
2603
|
}
|
|
@@ -2274,6 +2610,9 @@ function registryCredentialSource(provider) {
|
|
|
2274
2610
|
return { kind: "deployment", mechanism: "api_key" };
|
|
2275
2611
|
}
|
|
2276
2612
|
function registryBilling(provider) {
|
|
2613
|
+
if (provider.kind === "anonymous") {
|
|
2614
|
+
return { upstreamPayer: "deployment", metering: "external" };
|
|
2615
|
+
}
|
|
2277
2616
|
if (provider.kind === "codex-subscription" || provider.kind === "xai-subscription") {
|
|
2278
2617
|
return { upstreamPayer: "connected_subscription", metering: "external" };
|
|
2279
2618
|
}
|
|
@@ -2329,6 +2668,7 @@ function definitionVersionFor(model, provider) {
|
|
|
2329
2668
|
provider: {
|
|
2330
2669
|
adapterKind: provider.kind,
|
|
2331
2670
|
wireApi: provider.api,
|
|
2671
|
+
wireProfile: provider.wireProfile,
|
|
2332
2672
|
baseUrl: provider.baseUrl ?? null,
|
|
2333
2673
|
defaultHeaders: requestMetadata.headers,
|
|
2334
2674
|
defaultQuery: requestMetadata.query
|
|
@@ -2355,6 +2695,7 @@ function configuredProviders(settings) {
|
|
|
2355
2695
|
label: builtinProviderLabel(settings),
|
|
2356
2696
|
kind: "api-key",
|
|
2357
2697
|
api: "responses",
|
|
2698
|
+
wireProfile: settings.openaiProvider === "azure" ? "azure-openai" : "openai",
|
|
2358
2699
|
builtin: true,
|
|
2359
2700
|
credentialSource,
|
|
2360
2701
|
billing: { upstreamPayer: "deployment", metering: "opengeni_credits" }
|
|
@@ -2373,6 +2714,7 @@ function configuredProviders(settings) {
|
|
|
2373
2714
|
label: provider.label ?? provider.id,
|
|
2374
2715
|
kind: provider.kind,
|
|
2375
2716
|
api: provider.api,
|
|
2717
|
+
wireProfile: provider.wireProfile,
|
|
2376
2718
|
builtin: false,
|
|
2377
2719
|
baseUrl: provider.baseUrl,
|
|
2378
2720
|
apiKey: resolveProviderApiKey(provider),
|
|
@@ -2396,6 +2738,7 @@ function withCodexCatalogProvider(settings) {
|
|
|
2396
2738
|
id: CODEX_PROVIDER_ID,
|
|
2397
2739
|
label: "Codex (ChatGPT subscription)",
|
|
2398
2740
|
api: "responses",
|
|
2741
|
+
wireProfile: "openai",
|
|
2399
2742
|
baseUrl: CODEX_PROVIDER_BASE_URL,
|
|
2400
2743
|
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => {
|
|
2401
2744
|
const capabilities = {
|
|
@@ -2443,11 +2786,13 @@ function withXaiSubscriptionCatalogProvider(settings) {
|
|
|
2443
2786
|
id: XAI_SUBSCRIPTION_PROVIDER_ID,
|
|
2444
2787
|
label: "SuperGrok (xAI subscription)",
|
|
2445
2788
|
api: "responses",
|
|
2789
|
+
wireProfile: "openai",
|
|
2446
2790
|
baseUrl: XAI_SUBSCRIPTION_PROXY_BASE_URL,
|
|
2447
2791
|
models: XAI_SUBSCRIPTION_MODEL_SLUGS.map((slug) => {
|
|
2448
2792
|
const capabilities = legacyModelCapabilities(settings, {
|
|
2449
2793
|
reasoningEffort: true,
|
|
2450
|
-
hostedWebSearch: true
|
|
2794
|
+
hostedWebSearch: true,
|
|
2795
|
+
vision: true
|
|
2451
2796
|
});
|
|
2452
2797
|
capabilities.reasoning.efforts = ["low", "medium", "high", "xhigh"];
|
|
2453
2798
|
capabilities.reasoning.defaultEffort = "high";
|
|
@@ -2586,7 +2931,7 @@ function configuredModels(settings) {
|
|
|
2586
2931
|
billing: builtinProvider.billing,
|
|
2587
2932
|
capabilities,
|
|
2588
2933
|
...pricingSchedules[id] === void 0 ? {} : { pricing: pricingSchedules[id] },
|
|
2589
|
-
|
|
2934
|
+
...builtinContextLimitsForModel(settings, id),
|
|
2590
2935
|
toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
|
|
2591
2936
|
reasoningEffort: capabilities.reasoning.runnable,
|
|
2592
2937
|
hostedWebSearch: capabilities.hostedTools.webSearch.runnable
|
|
@@ -3410,8 +3755,10 @@ function ensureBuiltInMcpServers(settings) {
|
|
|
3410
3755
|
function firstPartyMcpBaseUrl(settings) {
|
|
3411
3756
|
return settings.opengeniMcpUrl ?? `http://127.0.0.1:${settings.apiPort}/v1/workspaces/{workspaceId}/mcp`;
|
|
3412
3757
|
}
|
|
3413
|
-
function
|
|
3414
|
-
|
|
3758
|
+
function firstPartyMcpInternalBaseUrl(settings) {
|
|
3759
|
+
return settings.opengeniMcpInternalUrl ?? `http://127.0.0.1:${settings.apiPort}/v1/workspaces/{workspaceId}/mcp`;
|
|
3760
|
+
}
|
|
3761
|
+
function scopedFirstPartyMcpUrl(raw, workspaceId) {
|
|
3415
3762
|
if (raw.includes("{workspaceId}")) {
|
|
3416
3763
|
return raw.replaceAll("{workspaceId}", workspaceId);
|
|
3417
3764
|
}
|
|
@@ -3421,6 +3768,12 @@ function firstPartyMcpWorkspaceUrl(settings, workspaceId) {
|
|
|
3421
3768
|
url.hash = "";
|
|
3422
3769
|
return url.toString();
|
|
3423
3770
|
}
|
|
3771
|
+
function firstPartyMcpWorkspaceUrl(settings, workspaceId) {
|
|
3772
|
+
return scopedFirstPartyMcpUrl(firstPartyMcpBaseUrl(settings), workspaceId);
|
|
3773
|
+
}
|
|
3774
|
+
function firstPartyMcpInternalWorkspaceUrl(settings, workspaceId) {
|
|
3775
|
+
return scopedFirstPartyMcpUrl(firstPartyMcpInternalBaseUrl(settings), workspaceId);
|
|
3776
|
+
}
|
|
3424
3777
|
function codemodeWorkspaceUrl(settings, workspaceId) {
|
|
3425
3778
|
if (settings.opengeniMcpUrl) {
|
|
3426
3779
|
const url2 = new URL(firstPartyMcpWorkspaceUrl(settings, workspaceId));
|
|
@@ -3446,8 +3799,18 @@ function firstPartyDocumentsMcpServerUrl(mcpUrl) {
|
|
|
3446
3799
|
function firstPartyFilesMcpServerUrl(mcpUrl) {
|
|
3447
3800
|
return `${mcpUrl.replace(/\/+$/, "")}/files`;
|
|
3448
3801
|
}
|
|
3802
|
+
var MODAL_DESKTOP_IMAGE_DIGEST_REF = /@sha256:[0-9a-f]{64}$/i;
|
|
3803
|
+
function isDigestPinnedModalDesktopImage(settings) {
|
|
3804
|
+
if (settings.modalImageId) return true;
|
|
3805
|
+
return typeof settings.modalImageRef === "string" && MODAL_DESKTOP_IMAGE_DIGEST_REF.test(settings.modalImageRef);
|
|
3806
|
+
}
|
|
3449
3807
|
function validateSettings(settings) {
|
|
3450
3808
|
temporalConnectionOptions(settings);
|
|
3809
|
+
if (settings.goalIdleBackoffMs.some((delayMs) => delayMs > settings.goalIdleBackoffMaxMs)) {
|
|
3810
|
+
throw new Error(
|
|
3811
|
+
`OPENGENI_GOAL_IDLE_BACKOFF_MS entries must not exceed OPENGENI_GOAL_IDLE_BACKOFF_MAX_MS (${settings.goalIdleBackoffMaxMs})`
|
|
3812
|
+
);
|
|
3813
|
+
}
|
|
3451
3814
|
const allowedFirstPartyMcpTools = new Set(
|
|
3452
3815
|
settings.allowedFirstPartyMcpTools ?? FIRST_PARTY_MCP_TOOL_NAMES
|
|
3453
3816
|
);
|
|
@@ -3529,6 +3892,53 @@ function validateSettings(settings) {
|
|
|
3529
3892
|
"OPENGENI_GOOGLE_DRIVE_CLIENT_ID and OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET must be configured together"
|
|
3530
3893
|
);
|
|
3531
3894
|
}
|
|
3895
|
+
if (Boolean(settings.githubPersonalOauthClientId) !== Boolean(settings.githubPersonalOauthClientSecret)) {
|
|
3896
|
+
throw new Error(
|
|
3897
|
+
"OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_ID and OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_SECRET must be configured together"
|
|
3898
|
+
);
|
|
3899
|
+
}
|
|
3900
|
+
if (settings.githubPersonalOauthEnabled) {
|
|
3901
|
+
if (!settings.integrationsEnabled) {
|
|
3902
|
+
throw new Error(
|
|
3903
|
+
"OPENGENI_INTEGRATIONS_ENABLED=true is required when personal GitHub OAuth is enabled"
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
if (settings.productAccessMode !== "managed") {
|
|
3907
|
+
throw new Error(
|
|
3908
|
+
"OPENGENI_GITHUB_PERSONAL_OAUTH_ENABLED=true requires OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
3909
|
+
);
|
|
3910
|
+
}
|
|
3911
|
+
if (!settings.githubPersonalOauthClientId || !settings.githubPersonalOauthClientSecret) {
|
|
3912
|
+
throw new Error(
|
|
3913
|
+
"personal GitHub OAuth requires OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_ID and OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_SECRET"
|
|
3914
|
+
);
|
|
3915
|
+
}
|
|
3916
|
+
if (settings.githubPersonalOauthClientId === settings.githubClientId) {
|
|
3917
|
+
throw new Error(
|
|
3918
|
+
"personal GitHub OAuth must use a different OAuth App client from the OpenGeni GitHub App"
|
|
3919
|
+
);
|
|
3920
|
+
}
|
|
3921
|
+
if (!personalGitHubOAuthCallbackUrl(settings.publicBaseUrl)) {
|
|
3922
|
+
throw new Error(
|
|
3923
|
+
"OPENGENI_PUBLIC_BASE_URL must be a credential-free origin without a path, query, or fragment when personal GitHub OAuth is enabled"
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3926
|
+
if (!settings.publicBaseUrl?.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
3927
|
+
throw new Error(
|
|
3928
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when personal GitHub OAuth is enabled outside local/test"
|
|
3929
|
+
);
|
|
3930
|
+
}
|
|
3931
|
+
if (!settings.integrationsStateSecret) {
|
|
3932
|
+
throw new Error(
|
|
3933
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when personal GitHub OAuth is enabled"
|
|
3934
|
+
);
|
|
3935
|
+
}
|
|
3936
|
+
if (!settings.environmentsEncryptionKey) {
|
|
3937
|
+
throw new Error(
|
|
3938
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required when personal GitHub OAuth is enabled"
|
|
3939
|
+
);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3532
3942
|
if (Boolean(settings.fikenClientId) !== Boolean(settings.fikenClientSecret)) {
|
|
3533
3943
|
throw new Error(
|
|
3534
3944
|
"OPENGENI_FIKEN_OAUTH_CLIENT_ID and OPENGENI_FIKEN_OAUTH_CLIENT_SECRET must be configured together"
|
|
@@ -3552,11 +3962,21 @@ function validateSettings(settings) {
|
|
|
3552
3962
|
}
|
|
3553
3963
|
}
|
|
3554
3964
|
if (settings.googleDriveClientId) {
|
|
3965
|
+
if (!settings.integrationsEnabled) {
|
|
3966
|
+
throw new Error(
|
|
3967
|
+
"OPENGENI_INTEGRATIONS_ENABLED=true is required when the Google Drive integration is configured"
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3555
3970
|
if (!settings.publicBaseUrl) {
|
|
3556
3971
|
throw new Error(
|
|
3557
3972
|
"OPENGENI_PUBLIC_BASE_URL is required when the Google Drive integration is configured"
|
|
3558
3973
|
);
|
|
3559
3974
|
}
|
|
3975
|
+
if (!googleDriveOAuthCallbackUrl(settings.publicBaseUrl)) {
|
|
3976
|
+
throw new Error(
|
|
3977
|
+
"OPENGENI_PUBLIC_BASE_URL must be a credential-free origin without a path, query, or fragment when the Google Drive integration is configured"
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3560
3980
|
if (!settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
3561
3981
|
throw new Error(
|
|
3562
3982
|
"OPENGENI_PUBLIC_BASE_URL must use https when the Google Drive integration is configured outside local/test"
|
|
@@ -3568,6 +3988,21 @@ function validateSettings(settings) {
|
|
|
3568
3988
|
);
|
|
3569
3989
|
}
|
|
3570
3990
|
}
|
|
3991
|
+
if (settings.googleDriveSyncMaxFileBytes > settings.googleDriveSyncMaxBytes) {
|
|
3992
|
+
throw new Error(
|
|
3993
|
+
"OPENGENI_GOOGLE_DRIVE_SYNC_MAX_FILE_BYTES must not exceed OPENGENI_GOOGLE_DRIVE_SYNC_MAX_BYTES"
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
if (settings.googleDriveProviderRetryInitialDelayMs > settings.googleDriveProviderRetryMaxDelayMs) {
|
|
3997
|
+
throw new Error(
|
|
3998
|
+
"OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_INITIAL_DELAY_MS must not exceed OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_MAX_DELAY_MS"
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
if (settings.googleDriveProviderRetryInitialDelayMs > settings.googleDriveProviderRetryBudgetMs) {
|
|
4002
|
+
throw new Error(
|
|
4003
|
+
"OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_INITIAL_DELAY_MS must not exceed OPENGENI_GOOGLE_DRIVE_PROVIDER_RETRY_BUDGET_MS"
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
3571
4006
|
if (Boolean(settings.atlassianClientId) !== Boolean(settings.atlassianClientSecret)) {
|
|
3572
4007
|
throw new Error(
|
|
3573
4008
|
"OPENGENI_ATLASSIAN_CLIENT_ID and OPENGENI_ATLASSIAN_CLIENT_SECRET must be configured together"
|
|
@@ -3731,6 +4166,18 @@ function validateSettings(settings) {
|
|
|
3731
4166
|
sandboxEnvironmentVariableNames(settings);
|
|
3732
4167
|
sandboxLifecycleHookIds(settings);
|
|
3733
4168
|
parseSandboxWarmRateJson(settings.sandboxWarmRateMicrosPerSecondJson);
|
|
4169
|
+
if (settings.sandboxBackend === "opensandbox") {
|
|
4170
|
+
if (!/@sha256:[0-9a-f]{64}$/i.test(settings.openSandboxImage ?? "")) {
|
|
4171
|
+
throw new Error(
|
|
4172
|
+
"OPENGENI_OPENSANDBOX_IMAGE must be an immutable OCI reference ending in @sha256:<64 hex characters>"
|
|
4173
|
+
);
|
|
4174
|
+
}
|
|
4175
|
+
if (!objectStorageConfiguredForWorkspaceArchives(settings)) {
|
|
4176
|
+
throw new Error(
|
|
4177
|
+
"OPENGENI_SANDBOX_BACKEND=opensandbox requires configured object storage for portable /workspace archives"
|
|
4178
|
+
);
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
3734
4181
|
const serverIds = /* @__PURE__ */ new Set();
|
|
3735
4182
|
for (const server of settings.mcpServers) {
|
|
3736
4183
|
if (serverIds.has(server.id)) {
|
|
@@ -3742,10 +4189,6 @@ function validateSettings(settings) {
|
|
|
3742
4189
|
const reaperPeriod = settings.sandboxLeaseReaperPeriodMs;
|
|
3743
4190
|
const viewerTtl = settings.sandboxViewerHolderTtlMs;
|
|
3744
4191
|
const interactionTtl = settings.sandboxInteractionHolderTtlMs;
|
|
3745
|
-
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
3746
|
-
const providerLifetimeMs = settings.modalTimeoutSeconds * 1e3;
|
|
3747
|
-
const rotationLeadMs = settings.sandboxRotationLeadMs;
|
|
3748
|
-
const idleTimeoutMs = effectiveModalIdleTimeoutSeconds(settings) * 1e3;
|
|
3749
4192
|
if (!(reaperPeriod < viewerTtl)) {
|
|
3750
4193
|
throw new Error(
|
|
3751
4194
|
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}): the reaper must run more often than the TTL it polices, or stale viewer holders outlive a full reaper period.`
|
|
@@ -3756,35 +4199,49 @@ function validateSettings(settings) {
|
|
|
3756
4199
|
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}): the reaper must run more often than the controller-heartbeat horizon.`
|
|
3757
4200
|
);
|
|
3758
4201
|
}
|
|
3759
|
-
if (
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
4202
|
+
if (settings.sandboxBackend === "modal") {
|
|
4203
|
+
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
4204
|
+
const lifecycle = effectiveSandboxLifecycle(settings, "modal");
|
|
4205
|
+
const providerLifetimeMs = lifecycle.hardLifetimeMs;
|
|
4206
|
+
const rotationLeadMs = lifecycle.rotationLeadMs;
|
|
4207
|
+
const idleTimeoutMs = lifecycle.providerIdleTimeoutMs;
|
|
4208
|
+
if (!(idleTimeoutMs <= providerLifetimeMs)) {
|
|
4209
|
+
throw new Error(
|
|
4210
|
+
`OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS*1000 (${idleTimeoutMs}) must not exceed the hard provider lifetime (OPENGENI_MODAL_TIMEOUT_SECONDS*1000 = ${providerLifetimeMs}): the idle timeout is a floor under the hard lifetime, not above it.`
|
|
4211
|
+
);
|
|
4212
|
+
}
|
|
4213
|
+
if (!(rotationLeadMs < providerLifetimeMs)) {
|
|
4214
|
+
throw new Error(
|
|
4215
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
|
|
4216
|
+
);
|
|
4217
|
+
}
|
|
4218
|
+
const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
|
|
4219
|
+
if (!(rotationLeadMs > captureTimeoutMs + reaperPeriod)) {
|
|
4220
|
+
throw new Error(
|
|
4221
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the durable capture timeout plus one reaper period (${captureTimeoutMs + reaperPeriod}).`
|
|
4222
|
+
);
|
|
4223
|
+
}
|
|
4224
|
+
if (!(viewerTtl < idleTimeoutMs)) {
|
|
4225
|
+
throw new Error(
|
|
4226
|
+
`OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a viewer holder must be reapable before the box idles out from under it (the provider idle-timeout is the backstop).`
|
|
4227
|
+
);
|
|
4228
|
+
}
|
|
4229
|
+
if (!(interactionTtl < idleTimeoutMs)) {
|
|
4230
|
+
throw new Error(
|
|
4231
|
+
`OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a dead browser controller must be reapable before the provider reclaims its placement.`
|
|
4232
|
+
);
|
|
4233
|
+
}
|
|
4234
|
+
if (!(reaperPeriod + idleGraceMs < idleTimeoutMs)) {
|
|
4235
|
+
throw new Error(
|
|
4236
|
+
`OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS + OPENGENI_SANDBOX_IDLE_GRACE_MS (${reaperPeriod} + ${idleGraceMs} = ${reaperPeriod + idleGraceMs}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a drained box must SURVIVE its full warm window so the reaper can resume + snapshot /workspace + terminate it on the sweep AFTER the drain grace elapses \u2014 Modal's idle-reap must NOT fire first (or /workspace is lost). Raise OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS (defaults to OPENGENI_MODAL_TIMEOUT_SECONDS) or lower OPENGENI_SANDBOX_IDLE_GRACE_MS.`
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
3784
4239
|
}
|
|
3785
|
-
|
|
4240
|
+
}
|
|
4241
|
+
if (settings.sandboxDesktopEnabled && settings.sandboxBackend === "modal" && !["local", "test"].includes(settings.environment)) {
|
|
4242
|
+
if (!isDigestPinnedModalDesktopImage(settings)) {
|
|
3786
4243
|
throw new Error(
|
|
3787
|
-
|
|
4244
|
+
"OPENGENI_MODAL_IMAGE_REF must be digest-pinned (registry/name@sha256:\u2026) when OPENGENI_SANDBOX_BACKEND=modal and OPENGENI_SANDBOX_DESKTOP_ENABLED=true. Computer/Browser need docker/desktop.Dockerfile, not the official headless opengeni-sandbox image. Helm desktop.imageRef writes this pin."
|
|
3788
4245
|
);
|
|
3789
4246
|
}
|
|
3790
4247
|
}
|
|
@@ -3813,7 +4270,7 @@ function validateSettings(settings) {
|
|
|
3813
4270
|
);
|
|
3814
4271
|
}
|
|
3815
4272
|
providerIds.add(provider.id);
|
|
3816
|
-
if (provider.kind !== "codex-subscription" && !resolveProviderApiKey(provider)) {
|
|
4273
|
+
if (provider.kind !== "codex-subscription" && provider.kind !== "anonymous" && !resolveProviderApiKey(provider)) {
|
|
3817
4274
|
throw new Error(
|
|
3818
4275
|
`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
|
|
3819
4276
|
);
|
|
@@ -3910,10 +4367,15 @@ export {
|
|
|
3910
4367
|
CapabilityStateV1Schema,
|
|
3911
4368
|
CapabilitySupportV1,
|
|
3912
4369
|
DEFAULT_AGENT_INSTRUCTIONS,
|
|
4370
|
+
DEFAULT_GOAL_IDLE_BACKOFF_MAX_MS,
|
|
4371
|
+
DEFAULT_GOAL_IDLE_BACKOFF_MS,
|
|
4372
|
+
GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MAX_MS,
|
|
4373
|
+
GOOGLE_DRIVE_PROVIDER_RETRY_DELAY_MAX_MS,
|
|
3913
4374
|
IntegrationOAuthClientConfigSchema,
|
|
3914
4375
|
McpServerConnectionRefSchema,
|
|
3915
4376
|
ModelCapabilitiesV1Schema,
|
|
3916
4377
|
ModelProviderApi,
|
|
4378
|
+
ModelProviderWireProfile,
|
|
3917
4379
|
OPENGENI_GATEWAY_MODELS,
|
|
3918
4380
|
OPENGENI_GATEWAY_PROVIDER_ID,
|
|
3919
4381
|
OPENGENI_REALTIME_MODEL_ID_PREFIX,
|
|
@@ -3943,6 +4405,7 @@ export {
|
|
|
3943
4405
|
calculateModelUsageCostBreakdown,
|
|
3944
4406
|
calculateModelUsageCostMicros,
|
|
3945
4407
|
calculateVideoGenerationCreditCostMicros,
|
|
4408
|
+
canonicalPublicOrigin,
|
|
3946
4409
|
canonicalizeConfiguredModelId,
|
|
3947
4410
|
codemodeWorkspaceUrl,
|
|
3948
4411
|
collectGitIdentityEnvironment,
|
|
@@ -3950,6 +4413,7 @@ export {
|
|
|
3950
4413
|
configuredAllowedModels,
|
|
3951
4414
|
configuredAllowedReasoningEfforts,
|
|
3952
4415
|
configuredEntitlements,
|
|
4416
|
+
configuredGoogleDriveSyncLimits,
|
|
3953
4417
|
configuredModelPricing,
|
|
3954
4418
|
configuredModelPricingSchedules,
|
|
3955
4419
|
configuredModels,
|
|
@@ -3959,11 +4423,16 @@ export {
|
|
|
3959
4423
|
dbSearchPath,
|
|
3960
4424
|
defaultModelPricing,
|
|
3961
4425
|
effectiveModalIdleTimeoutSeconds,
|
|
4426
|
+
effectiveSandboxLifecycle,
|
|
3962
4427
|
environmentsEncryptionKeyBytes,
|
|
3963
4428
|
firstPartyMcpBaseUrl,
|
|
4429
|
+
firstPartyMcpInternalBaseUrl,
|
|
4430
|
+
firstPartyMcpInternalWorkspaceUrl,
|
|
3964
4431
|
firstPartyMcpWorkspaceUrl,
|
|
3965
4432
|
gatewayRequestPolicyForUpstreamModel,
|
|
3966
4433
|
getSettings,
|
|
4434
|
+
googleDriveOAuthCallbackUrl,
|
|
4435
|
+
googleDriveProviderRetryOptions,
|
|
3967
4436
|
hasGitCredentialRepositorySelection,
|
|
3968
4437
|
hasGitHubRepositorySelection,
|
|
3969
4438
|
isDirectOpenAiApiBaseUrl,
|
|
@@ -3977,6 +4446,7 @@ export {
|
|
|
3977
4446
|
parseSocialOauthClientsJson,
|
|
3978
4447
|
parseStaticEntitlementsJson,
|
|
3979
4448
|
parseStaticUsageLimitsJson,
|
|
4449
|
+
personalGitHubOAuthCallbackUrl,
|
|
3980
4450
|
policyProviderIdForModel,
|
|
3981
4451
|
productLabelForModelId,
|
|
3982
4452
|
productShortLabelForModelId,
|