@opengeni/config 0.22.0 → 0.22.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/config",
3
- "version": "0.22.0",
3
+ "version": "0.22.5",
4
4
  "description": "OpenGeni runtime configuration: settings resolution, deployment knobs, and config validation shared across the server packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -33,8 +33,8 @@
33
33
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
34
34
  },
35
35
  "dependencies": {
36
- "@opengeni/codex": "^0.2.19",
37
- "@opengeni/contracts": "^2.7.0",
36
+ "@opengeni/codex": "^0.2.20",
37
+ "@opengeni/contracts": "^2.9.2",
38
38
  "@opengeni/xai-subscription": "^0.1.2",
39
39
  "zod": "^4.2.1"
40
40
  },
package/src/index.ts CHANGED
@@ -834,6 +834,13 @@ const SettingsSchema = z.object({
834
834
  // larger timeout—is what lets a session outlive one finite provider box.
835
835
  // Knob: OPENGENI_MODAL_TIMEOUT_SECONDS.
836
836
  modalTimeoutSeconds: z.coerce.number().int().positive().max(86_400).default(86_400),
837
+ // Optional provider reservations for every new Modal sandbox. CPU is measured
838
+ // in physical cores and may be fractional; memory is measured in MiB. Leave
839
+ // both unset to preserve Modal's provider defaults. The Agents adapter stores
840
+ // the resolved values in its session state so snapshot replacement and exact
841
+ // resume cannot silently change the reservation.
842
+ modalSandboxCpu: z.coerce.number().positive().optional(),
843
+ modalSandboxMemoryMiB: z.coerce.number().int().positive().optional(),
837
844
  modalTokenId: z.string().optional(),
838
845
  modalTokenSecret: z.string().optional(),
839
846
  modalEnvironment: z.string().optional(),
@@ -873,8 +880,8 @@ const SettingsSchema = z.object({
873
880
  // cell advertises mode "interactive" — the noVNC viewer can drive mouse+keyboard
874
881
  // into :0 (x11vnc runs without -viewonly). Turn it OFF for a genuinely read-only
875
882
  // deployment: the cell reports mode "read-only" and the client disables the
876
- // "Take control" affordance. Independent of computerUseReadOnly (the AGENT
877
- // driver); this gates the HUMAN viewer plane.
883
+ // "Take control" affordance. This gates the HUMAN viewer plane; agent
884
+ // interaction is authorized through managed ComputerSession tools.
878
885
  sandboxDesktopInteractive: EnvBoolean.default(true),
879
886
  // REAL PTY terminal toggle (P5.t): gates the ttyd pty-ws plane (7681) the API
880
887
  // mints over the SAME tunnel as the desktop. Defaults ON — the interactive
@@ -888,13 +895,7 @@ const SettingsSchema = z.object({
888
895
  // down→up restart. Defaults match the proven spike geometry (1280x800).
889
896
  streamResolutionWidth: z.coerce.number().int().positive().default(1280),
890
897
  streamResolutionHeight: z.coerce.number().int().positive().default(800),
891
- // P4.3 computer-use: the agent drives the SAME :0 humans watch (xdotool/XTEST +
892
- // scrot). Gated by sandboxDesktopEnabled + a desktop-capable backend in
893
- // buildAgentCapabilities; computerUseReadOnly:false is the agent-driver default
894
- // (it must click/type — the human viewer plane is the read-only one).
895
- computerUseEnabled: EnvBoolean.default(true),
896
- computerUseReadOnly: EnvBoolean.default(false),
897
- // P4.3 recording loop: ffmpeg x11grab of :0 → mp4/webm → @opengeni/storage.
898
+ // Recording loop: ffmpeg x11grab of :0 mp4/webm → @opengeni/storage.
898
899
  // recordingMaxBytes caps the in-memory finalize buffer (≤ storage single-PUT);
899
900
  // recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
900
901
  recordingEnabled: EnvBoolean.default(true),
@@ -1266,6 +1267,10 @@ const SettingsSchema = z.object({
1266
1267
  betterAuthAllowedHosts: z.string().default(""),
1267
1268
  betterAuthCookieDomain: z.string().optional(),
1268
1269
  betterAuthTrustedOrigins: z.string().default(""),
1270
+ managedAuthGoogleClientId: z.string().optional(),
1271
+ managedAuthGoogleClientSecret: z.string().optional(),
1272
+ managedAuthGithubClientId: z.string().optional(),
1273
+ managedAuthGithubClientSecret: z.string().optional(),
1269
1274
  // Rolling browser login-slot compatibility. Repository/deployment default is
1270
1275
  // deliberately legacy; changing to broker is an operator-authorized rollout.
1271
1276
  managedAuthSessionSetMode: z.enum(["legacy", "dual", "broker"]).default("legacy"),
@@ -2497,6 +2502,8 @@ export function getSettings(): Settings {
2497
2502
  modalImageId: optional("OPENGENI_MODAL_IMAGE_ID"),
2498
2503
  modalImageRegistrySecret: optional("OPENGENI_MODAL_IMAGE_REGISTRY_SECRET"),
2499
2504
  modalTimeoutSeconds: optional("OPENGENI_MODAL_TIMEOUT_SECONDS"),
2505
+ modalSandboxCpu: optional("OPENGENI_MODAL_SANDBOX_CPU"),
2506
+ modalSandboxMemoryMiB: optional("OPENGENI_MODAL_SANDBOX_MEMORY_MIB"),
2500
2507
  modalTokenId: optional("OPENGENI_MODAL_TOKEN_ID"),
2501
2508
  modalTokenSecret: optional("OPENGENI_MODAL_TOKEN_SECRET"),
2502
2509
  modalEnvironment: optional("OPENGENI_MODAL_ENVIRONMENT"),
@@ -2507,8 +2514,6 @@ export function getSettings(): Settings {
2507
2514
  sandboxTerminalEnabled: optional("OPENGENI_SANDBOX_TERMINAL_ENABLED"),
2508
2515
  streamResolutionWidth: optional("OPENGENI_STREAM_RESOLUTION_WIDTH"),
2509
2516
  streamResolutionHeight: optional("OPENGENI_STREAM_RESOLUTION_HEIGHT"),
2510
- computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
2511
- computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
2512
2517
  recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
2513
2518
  workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
2514
2519
  recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
@@ -2664,6 +2669,10 @@ export function getSettings(): Settings {
2664
2669
  betterAuthAllowedHosts: optional("OPENGENI_BETTER_AUTH_ALLOWED_HOSTS"),
2665
2670
  betterAuthCookieDomain: optional("OPENGENI_BETTER_AUTH_COOKIE_DOMAIN"),
2666
2671
  betterAuthTrustedOrigins: optional("OPENGENI_BETTER_AUTH_TRUSTED_ORIGINS"),
2672
+ managedAuthGoogleClientId: optional("OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_ID"),
2673
+ managedAuthGoogleClientSecret: optional("OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_SECRET"),
2674
+ managedAuthGithubClientId: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_ID"),
2675
+ managedAuthGithubClientSecret: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_SECRET"),
2667
2676
  managedAuthSessionSetMode: optional("OPENGENI_MANAGED_AUTH_SESSION_SET_MODE"),
2668
2677
  resendApiKey: optional("OPENGENI_RESEND_API_KEY"),
2669
2678
  emailFrom: optional("OPENGENI_EMAIL_FROM"),
@@ -3548,8 +3557,10 @@ function canonicalJson(value: unknown): string {
3548
3557
  function definitionVersionFor(
3549
3558
  model: Omit<ConfiguredModel, "definitionVersion">,
3550
3559
  provider: ResolvedModelProvider,
3560
+ options: { includeWireProfile?: boolean } = {},
3551
3561
  ): string {
3552
3562
  const requestMetadata = staticRequestMetadataForDigest(provider);
3563
+ const includeWireProfile = options.includeWireProfile ?? true;
3553
3564
  const digestInput = canonicalJson({
3554
3565
  schemaVersion: model.schemaVersion,
3555
3566
  id: model.id,
@@ -3558,7 +3569,7 @@ function definitionVersionFor(
3558
3569
  provider: {
3559
3570
  adapterKind: provider.kind,
3560
3571
  wireApi: provider.api,
3561
- wireProfile: provider.wireProfile,
3572
+ ...(includeWireProfile ? { wireProfile: provider.wireProfile } : {}),
3562
3573
  baseUrl: provider.baseUrl ?? null,
3563
3574
  defaultHeaders: requestMetadata.headers,
3564
3575
  defaultQuery: requestMetadata.query,
@@ -3576,6 +3587,15 @@ function definitionVersionFor(
3576
3587
  .digest("hex")}`;
3577
3588
  }
3578
3589
 
3590
+ function legacyImplicitOpenAiDefinitionVersionFor(
3591
+ model: ConfiguredModel,
3592
+ provider: ResolvedModelProvider,
3593
+ ): string | null {
3594
+ if (provider.wireProfile !== "openai") return null;
3595
+ const { definitionVersion: _definitionVersion, ...modelWithoutVersion } = model;
3596
+ return definitionVersionFor(modelWithoutVersion, provider, { includeWireProfile: false });
3597
+ }
3598
+
3579
3599
  /**
3580
3600
  * The built-in provider's stable id: "openai" on the OpenAI platform, "azure"
3581
3601
  * on Azure. Exported because the workspace model-policy gate must attribute
@@ -4157,11 +4177,22 @@ export function assertTurnExecutionPolicyMatchesConfigV1(
4157
4177
  if (!resolved) {
4158
4178
  throw new Error("Turn execution policy model is no longer configured");
4159
4179
  }
4180
+ // wireProfile was added to the definition digest after policies already
4181
+ // existed in durable in-flight turns. An omitted profile meant exactly
4182
+ // "openai", so accept that one legacy digest only; Azure and every other
4183
+ // executable-definition change remain fail-closed.
4184
+ const legacyImplicitOpenAiDefinitionVersion = legacyImplicitOpenAiDefinitionVersionFor(
4185
+ resolved.model,
4186
+ resolved.provider,
4187
+ );
4188
+ const definitionVersionMatches =
4189
+ parsed.definitionVersion === resolved.model.definitionVersion ||
4190
+ parsed.definitionVersion === legacyImplicitOpenAiDefinitionVersion;
4160
4191
  const mismatched =
4161
4192
  parsed.providerId !== resolved.provider.id ||
4162
4193
  parsed.upstreamModelId !== resolved.model.upstreamModelId ||
4163
4194
  parsed.wireApi !== resolved.model.api ||
4164
- parsed.definitionVersion !== resolved.model.definitionVersion ||
4195
+ !definitionVersionMatches ||
4165
4196
  canonicalJson(parsed.credentialSource) !== canonicalJson(resolved.model.credentialSource) ||
4166
4197
  canonicalJson(parsed.billing) !== canonicalJson(resolved.model.billing);
4167
4198
  if (mismatched) {
@@ -5335,6 +5366,41 @@ function validateSettings(settings: Settings): void {
5335
5366
  );
5336
5367
  }
5337
5368
  }
5369
+ if (
5370
+ Boolean(settings.managedAuthGoogleClientId) !== Boolean(settings.managedAuthGoogleClientSecret)
5371
+ ) {
5372
+ throw new Error(
5373
+ "OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_ID and OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_SECRET must be configured together",
5374
+ );
5375
+ }
5376
+ if (
5377
+ Boolean(settings.managedAuthGithubClientId) !== Boolean(settings.managedAuthGithubClientSecret)
5378
+ ) {
5379
+ throw new Error(
5380
+ "OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_ID and OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_SECRET must be configured together",
5381
+ );
5382
+ }
5383
+ const managedSocialAuthConfigured = Boolean(
5384
+ settings.managedAuthGoogleClientId || settings.managedAuthGithubClientId,
5385
+ );
5386
+ if (managedSocialAuthConfigured) {
5387
+ if (settings.productAccessMode !== "managed") {
5388
+ throw new Error(
5389
+ "Managed Google/GitHub authentication requires OPENGENI_PRODUCT_ACCESS_MODE=managed",
5390
+ );
5391
+ }
5392
+ const publicOrigin = canonicalPublicOrigin(settings.publicBaseUrl);
5393
+ if (!publicOrigin) {
5394
+ throw new Error(
5395
+ "OPENGENI_PUBLIC_BASE_URL must be a credential-free HTTP(S) origin when managed social authentication is configured",
5396
+ );
5397
+ }
5398
+ if (!publicOrigin.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
5399
+ throw new Error(
5400
+ "OPENGENI_PUBLIC_BASE_URL must use https when managed social authentication is configured outside local/test",
5401
+ );
5402
+ }
5403
+ }
5338
5404
  environmentsEncryptionKeyBytes(settings);
5339
5405
  if (settings.integrationsEnabled) {
5340
5406
  if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
@@ -5401,11 +5467,6 @@ function validateSettings(settings: Settings): void {
5401
5467
  "OPENGENI_INTEGRATIONS_ENABLED=true is required when personal GitHub OAuth is enabled",
5402
5468
  );
5403
5469
  }
5404
- if (settings.productAccessMode !== "managed") {
5405
- throw new Error(
5406
- "OPENGENI_GITHUB_PERSONAL_OAUTH_ENABLED=true requires OPENGENI_PRODUCT_ACCESS_MODE=managed",
5407
- );
5408
- }
5409
5470
  if (!settings.githubPersonalOauthClientId || !settings.githubPersonalOauthClientSecret) {
5410
5471
  throw new Error(
5411
5472
  "personal GitHub OAuth requires OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_ID and OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_SECRET",