@opengeni/core 2.5.3 → 2.6.4

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.
Files changed (39) hide show
  1. package/dist/access/index.d.ts +24 -0
  2. package/dist/canonical-human-identities.js +2 -2
  3. package/dist/{chunk-ZVZJTMSV.js → chunk-OF65T3PM.js} +2 -2
  4. package/dist/{chunk-YGOMUGYS.js → chunk-QO5GVFFO.js} +17 -8
  5. package/dist/{chunk-YGOMUGYS.js.map → chunk-QO5GVFFO.js.map} +1 -1
  6. package/dist/domain/company-brain-governed-writes.d.ts +15 -6
  7. package/dist/domain/company-profile-agent-admin.d.ts +3 -2
  8. package/dist/domain/environments.d.ts +1 -1
  9. package/dist/domain/memory-slack-delivery.d.ts +4 -1
  10. package/dist/domain/personal-connection-delegations.d.ts +1 -0
  11. package/dist/domain/pr-review.d.ts +1 -1
  12. package/dist/domain/scheduled-tasks.d.ts +12 -0
  13. package/dist/domain/sessions.d.ts +14 -1
  14. package/dist/domain/workspace-members.d.ts +8 -1
  15. package/dist/index.js +686 -380
  16. package/dist/index.js.map +1 -1
  17. package/dist/managed-auth-session-sets.d.ts +18 -0
  18. package/dist/managed-auth-session-sets.js +3 -1
  19. package/dist/sandbox/fleet.d.ts +6 -4
  20. package/dist/sandbox/routing.d.ts +7 -2
  21. package/dist/sandbox/runtime-settings.d.ts +17 -1
  22. package/package.json +10 -10
  23. package/src/access/index.ts +140 -5
  24. package/src/application/user-resource-grants.ts +31 -2
  25. package/src/billing/limits.ts +5 -2
  26. package/src/domain/company-brain-governed-writes.ts +29 -13
  27. package/src/domain/company-profile-agent-admin.ts +3 -2
  28. package/src/domain/environments.ts +6 -34
  29. package/src/domain/memory-slack-delivery.ts +30 -0
  30. package/src/domain/personal-connection-delegations.ts +40 -7
  31. package/src/domain/remember.ts +5 -6
  32. package/src/domain/scheduled-tasks.ts +94 -0
  33. package/src/domain/sessions.ts +117 -9
  34. package/src/domain/workspace-members.ts +34 -2
  35. package/src/managed-auth-session-sets.ts +38 -11
  36. package/src/sandbox/fleet.ts +20 -17
  37. package/src/sandbox/routing.ts +18 -4
  38. package/src/sandbox/runtime-settings.ts +32 -0
  39. /package/dist/{chunk-ZVZJTMSV.js.map → chunk-OF65T3PM.js.map} +0 -0
package/dist/index.js CHANGED
@@ -125,8 +125,8 @@ import {
125
125
  markManagedAuthRequestActorTransitionApplied,
126
126
  releaseManagedAuthRequestActorLease,
127
127
  validateManagedAuthRequestActorLease
128
- } from "./chunk-ZVZJTMSV.js";
129
- import "./chunk-YGOMUGYS.js";
128
+ } from "./chunk-OF65T3PM.js";
129
+ import "./chunk-QO5GVFFO.js";
130
130
 
131
131
  // src/workflow-wake-contract.ts
132
132
  var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
@@ -260,6 +260,16 @@ import {
260
260
  function directRetainedProcessMatchesBackend(durable, process, backend) {
261
261
  return durable.providerSessionId === process.providerSessionId && durable.providerBackend === backend.kind && durable.providerInstanceId === backend.providerInstanceId && durable.leaseEpoch === backend.leaseEpoch && durable.routeKind === "active" && durable.routeTargetId === backend.sandboxId && durable.routeEpoch === backend.activeEpoch;
262
262
  }
263
+ function retainedProcessBackgroundSettlement(process, fallback) {
264
+ if (process.state === "active") {
265
+ throw new Error("Retained-process settlement returned an active durable process");
266
+ }
267
+ return {
268
+ outcome: process.state,
269
+ exitCode: process.exitCode,
270
+ reason: process.settlementReason ?? fallback.reason
271
+ };
272
+ }
263
273
  function relayConfigFromSettings(settings) {
264
274
  const raw = settings.selfhostedRelayUrl?.trim();
265
275
  if (!raw) {
@@ -460,7 +470,7 @@ function wrapChannelABoxWithRouting(services, ids, established) {
460
470
  "API retained-process settlement lost its exact durable backend identity"
461
471
  );
462
472
  }
463
- await settleRetainedProcess(db, {
473
+ const settlement = await settleRetainedProcess(db, {
464
474
  accountId: ids.accountId,
465
475
  workspaceId: ids.workspaceId,
466
476
  sessionId: ids.sessionId,
@@ -471,14 +481,13 @@ function wrapChannelABoxWithRouting(services, ids, established) {
471
481
  reason: proof.reason,
472
482
  idleGraceMs: settings.sandboxIdleGraceMs
473
483
  });
484
+ const backgroundSettlement = retainedProcessBackgroundSettlement(settlement.process, proof);
474
485
  await settleSessionBackgroundCommandForRetainedProcess(db, {
475
486
  accountId: ids.accountId,
476
487
  workspaceId: ids.workspaceId,
477
488
  sessionId: ids.sessionId,
478
489
  retainedProcessId: process.id,
479
- outcome: proof.outcome,
480
- exitCode: proof.exitCode,
481
- reason: proof.reason
490
+ ...backgroundSettlement
482
491
  });
483
492
  } : void 0;
484
493
  const resolver = makeActiveBackendResolver({
@@ -588,6 +597,235 @@ function wrapChannelABoxWithRouting(services, ids, established) {
588
597
  return { ...established, session: proxy };
589
598
  }
590
599
 
600
+ // src/sandbox/runtime-settings.ts
601
+ import {
602
+ CapabilityPack
603
+ } from "@opengeni/contracts";
604
+ import {
605
+ getRigVersion,
606
+ getWorkspacePack,
607
+ listPackInstallations
608
+ } from "@opengeni/db";
609
+ import { resolveModalCheckpointProviderBinding } from "@opengeni/runtime/sandbox";
610
+
611
+ // src/rigs/provider-images.ts
612
+ import { createHash } from "crypto";
613
+ import {
614
+ RigProviderImage as RigProviderImageContract,
615
+ stableJson
616
+ } from "@opengeni/contracts";
617
+ function sha256(value) {
618
+ return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
619
+ }
620
+ function rigProviderImageProviderBindingKeyHash(bindingKey) {
621
+ return sha256(bindingKey);
622
+ }
623
+ function rigProviderImageSetupHash(definition) {
624
+ return sha256(definition.setupScript ?? "");
625
+ }
626
+ function rigProviderImageContentHash(input) {
627
+ return sha256(
628
+ stableJson({
629
+ version: 2,
630
+ backend: input.backend,
631
+ sourceImage: input.sourceImage,
632
+ setupScript: input.definition.setupScript,
633
+ checks: input.definition.checks,
634
+ credentialHooks: input.definition.credentialHooks,
635
+ defaultVariableSetIds: input.definition.defaultVariableSetIds
636
+ })
637
+ );
638
+ }
639
+ function rigProviderImageBuildRequestId(input) {
640
+ const bytes = createHash("sha256").update("opengeni-rig-provider-image-build-v2\0", "utf8").update(input.targetId, "utf8").update("\0", "utf8").update(input.backend, "utf8").update("\0", "utf8").update(input.contentHash, "utf8").digest().subarray(0, 16);
641
+ bytes[6] = bytes[6] & 15 | 80;
642
+ bytes[8] = bytes[8] & 63 | 128;
643
+ const hex = bytes.toString("hex");
644
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
645
+ }
646
+ function rigProviderImageMatchesDefinition(image, definition) {
647
+ return image.setupHash === rigProviderImageSetupHash(definition) && image.contentHash === rigProviderImageContentHash({
648
+ backend: image.backend,
649
+ sourceImage: image.sourceImage,
650
+ definition
651
+ });
652
+ }
653
+ function rigProviderImagesFromVerification(verification, definition) {
654
+ const parsed = RigProviderImageContract.safeParse(verification?.providerImage);
655
+ if (!parsed.success || parsed.data.status === "building") return {};
656
+ if (!rigProviderImageMatchesDefinition(parsed.data, definition)) return {};
657
+ return { [parsed.data.backend]: parsed.data };
658
+ }
659
+
660
+ // src/sandbox/runtime-settings.ts
661
+ function managedSessionGroupBackend(deploymentBackend, sessionBackend) {
662
+ if (sessionBackend === "none") return null;
663
+ const backend = sessionBackend === "selfhosted" ? deploymentBackend : sessionBackend;
664
+ return backend === "none" || backend === "selfhosted" ? null : backend;
665
+ }
666
+ function managedSessionGroupOs(sessionBackend, sessionOs) {
667
+ return sessionBackend === "selfhosted" ? "linux" : sessionOs;
668
+ }
669
+ function packInstallationUsesLegacyRuntime(input) {
670
+ return input.manifestSnapshot === null && input.manifestDigest === null;
671
+ }
672
+ async function resolveWorkspaceLegacyRuntimePacks(db, workspaceId) {
673
+ const installations = await listPackInstallations(db, workspaceId);
674
+ const packs2 = [];
675
+ for (const installation of installations) {
676
+ if (installation.status !== "active" || !packInstallationUsesLegacyRuntime(installation)) {
677
+ continue;
678
+ }
679
+ const registration = await getWorkspacePack(db, workspaceId, installation.packId);
680
+ const parsed = CapabilityPack.safeParse(registration?.pack);
681
+ if (parsed.success) packs2.push(parsed.data);
682
+ }
683
+ return packs2;
684
+ }
685
+ function legacySandboxRuntimeFromPacks(packs2) {
686
+ const imagePacks = packs2.filter(
687
+ (pack) => typeof pack.sandboxImage === "string" && pack.sandboxImage.trim().length > 0
688
+ );
689
+ if (imagePacks.length > 1) {
690
+ const ids = imagePacks.map((pack) => pack.id).sort().join(", ");
691
+ throw new Error(
692
+ `Multiple enabled packs declare a sandbox image (${ids}). Only one enabled pack per workspace may declare sandboxImage; disable the others and retry.`
693
+ );
694
+ }
695
+ return {
696
+ sandboxImage: imagePacks[0]?.sandboxImage?.trim() ?? null,
697
+ sandboxProviderImages: imagePacks[0]?.sandboxProviderImages ?? null
698
+ };
699
+ }
700
+ function settingsWithPackSandboxImage(settings, sandboxImage, sandboxProviderImages = null) {
701
+ if (!sandboxImage) return settings;
702
+ return {
703
+ ...settings,
704
+ dockerImage: sandboxImage,
705
+ modalImageRef: sandboxImage,
706
+ modalImageId: sandboxProviderImages?.modal?.imageId
707
+ };
708
+ }
709
+ function settingsWithRigImage(settings, rigImage) {
710
+ void rigImage;
711
+ return settings;
712
+ }
713
+ function rigProviderImageSourceImage(settings, backend) {
714
+ if (backend === "modal") return settings.modalImageId ?? settings.modalImageRef ?? null;
715
+ if (backend === "docker") return settings.dockerImage ?? null;
716
+ return null;
717
+ }
718
+ function resolveRigProviderImageSelection(settings, version, backend, currentProviderBindingKeyHash) {
719
+ if (!version) return { settings, reason: "missing", contentHash: null, imageId: null };
720
+ if (backend !== "modal") {
721
+ return { settings, reason: "provider_unsupported", contentHash: null, imageId: null };
722
+ }
723
+ const image = version.providerImages[backend];
724
+ if (!image) return { settings, reason: "missing", contentHash: null, imageId: null };
725
+ if (image.status !== "ready" || !image.imageId) {
726
+ return {
727
+ settings,
728
+ reason: "not_ready",
729
+ contentHash: image.contentHash,
730
+ imageId: image.imageId
731
+ };
732
+ }
733
+ if (image.coldBootValidation?.version !== 1) {
734
+ return {
735
+ settings,
736
+ reason: "not_cold_boot_validated",
737
+ contentHash: image.contentHash,
738
+ imageId: null
739
+ };
740
+ }
741
+ const sourceImage = rigProviderImageSourceImage(settings, backend);
742
+ const expectedContentHash = rigProviderImageContentHash({
743
+ backend,
744
+ sourceImage,
745
+ definition: version
746
+ });
747
+ if (image.sourceImage !== sourceImage || image.contentHash !== expectedContentHash || !rigProviderImageMatchesDefinition(image, version)) {
748
+ return {
749
+ settings,
750
+ reason: "content_mismatch",
751
+ contentHash: expectedContentHash,
752
+ imageId: null
753
+ };
754
+ }
755
+ if (!image.providerBindingKeyHash || !currentProviderBindingKeyHash) {
756
+ return {
757
+ settings,
758
+ reason: "provider_binding_unavailable",
759
+ contentHash: expectedContentHash,
760
+ imageId: null
761
+ };
762
+ }
763
+ if (image.providerBindingKeyHash !== currentProviderBindingKeyHash) {
764
+ return {
765
+ settings,
766
+ reason: "provider_binding_mismatch",
767
+ contentHash: expectedContentHash,
768
+ imageId: null
769
+ };
770
+ }
771
+ return {
772
+ settings: { ...settings, modalImageId: image.imageId },
773
+ reason: "selected",
774
+ contentHash: expectedContentHash,
775
+ imageId: image.imageId
776
+ };
777
+ }
778
+ async function resolveRigProviderImageForRun(settings, version, backend, resolveBinding = resolveModalCheckpointProviderBinding) {
779
+ const image = backend === "modal" ? version?.providerImages.modal : null;
780
+ if (image?.status !== "ready" || !image.providerBindingKeyHash) {
781
+ return resolveRigProviderImageSelection(settings, version, backend, null);
782
+ }
783
+ const structural = resolveRigProviderImageSelection(
784
+ settings,
785
+ version,
786
+ backend,
787
+ image.providerBindingKeyHash
788
+ );
789
+ if (structural.reason !== "selected") return structural;
790
+ let currentProviderBindingKeyHash = null;
791
+ try {
792
+ const binding = await resolveBinding(settings);
793
+ currentProviderBindingKeyHash = rigProviderImageProviderBindingKeyHash(binding.key);
794
+ } catch {
795
+ }
796
+ return resolveRigProviderImageSelection(
797
+ settings,
798
+ version,
799
+ backend,
800
+ currentProviderBindingKeyHash
801
+ );
802
+ }
803
+ async function settingsWithRigProviderImage(settings, version, backend, resolveBinding = resolveModalCheckpointProviderBinding) {
804
+ return (await resolveRigProviderImageForRun(settings, version, backend, resolveBinding)).settings;
805
+ }
806
+ async function resolveSessionSandboxRuntime(db, settings, session) {
807
+ const [packs2, rigVersion] = await Promise.all([
808
+ resolveWorkspaceLegacyRuntimePacks(db, session.workspaceId),
809
+ session.rigId && session.rigVersionId ? getRigVersion(db, session.workspaceId, session.rigId, session.rigVersionId) : Promise.resolve(null)
810
+ ]);
811
+ if (session.rigVersionId && !rigVersion) {
812
+ throw new Error(`Frozen rig version ${session.rigVersionId} is unavailable`);
813
+ }
814
+ const legacy = legacySandboxRuntimeFromPacks(packs2);
815
+ const logicalSettings = rigVersion ? settings : settingsWithPackSandboxImage(settings, legacy.sandboxImage, legacy.sandboxProviderImages);
816
+ return {
817
+ settings: {
818
+ ...logicalSettings,
819
+ sandboxBackend: session.sandboxBackend
820
+ },
821
+ image: rigProviderImageSourceImage(logicalSettings, session.sandboxBackend),
822
+ rigVersion
823
+ };
824
+ }
825
+ async function providerSettingsForSessionSandboxRuntime(runtime, backend) {
826
+ return await settingsWithRigProviderImage(runtime.settings, runtime.rigVersion, backend);
827
+ }
828
+
591
829
  // src/sandbox/fleet.ts
592
830
  async function buildFleetContextForSession(deps, ctx) {
593
831
  const session = await requireSession(deps.db, ctx.workspaceId, ctx.sessionId);
@@ -661,7 +899,11 @@ async function listFleet(services, ctx) {
661
899
  activeEpoch: 0
662
900
  };
663
901
  const entries = [];
664
- if (ctx.sessionBackend !== "none") {
902
+ const groupBackend = managedSessionGroupBackend(
903
+ services.settings.sandboxBackend,
904
+ ctx.sessionBackend
905
+ );
906
+ if (groupBackend) {
665
907
  const groupActive = pointer.activeSandboxId === null;
666
908
  const groupLease = await readLease(db, ctx.workspaceId, ctx.sessionGroupId);
667
909
  const groupOnline = Boolean(
@@ -673,10 +915,10 @@ async function listFleet(services, ctx) {
673
915
  const groupRecoveryUnavailable = Boolean(
674
916
  groupLease && (groupLease.recovery.restore.status === "degraded" || groupLease.recovery.restore.status === "unrecoverable" || groupLease.recovery.workspace.status === "degraded" || groupLease.recovery.workspace.status === "unrecoverable")
675
917
  );
676
- const groupOperationAvailability = groupOnline ? "ready" : groupRecoveryUnavailable ? "unavailable" : groupRecovering ? "recovering" : ctx.sessionBackend === "selfhosted" ? "unavailable" : "wakeable";
918
+ const groupOperationAvailability = groupOnline ? "ready" : groupRecoveryUnavailable ? "unavailable" : groupRecovering ? "recovering" : "wakeable";
677
919
  entries.push({
678
920
  id: ctx.sessionGroupId,
679
- kind: ctx.sessionBackend === "selfhosted" ? "selfhosted" : ctx.sessionBackend === "opensandbox" ? "opensandbox" : "modal",
921
+ kind: groupBackend === "opensandbox" ? "opensandbox" : "modal",
680
922
  name: "session sandbox",
681
923
  liveness: groupOnline ? "online" : groupRecovering ? "reconnecting" : "offline",
682
924
  active: groupActive,
@@ -780,10 +1022,10 @@ async function listFleet(services, ctx) {
780
1022
  }
781
1023
  async function resolveTarget(services, ctx, target) {
782
1024
  if (target === ctx.sessionGroupId || target === "session" || target === "default") {
783
- if (ctx.sessionBackend === "none") {
1025
+ if (!managedSessionGroupBackend(services.settings.sandboxBackend, ctx.sessionBackend)) {
784
1026
  return {
785
1027
  ok: false,
786
- reason: "this session has no home sandbox; attach a Connected Machine",
1028
+ reason: ctx.sessionBackend === "none" ? "this session has no home sandbox; attach a Connected Machine" : "this deployment has no managed session sandbox; select an enrolled machine",
787
1029
  code: "unsupported_backend_context"
788
1030
  };
789
1031
  }
@@ -1149,307 +1391,86 @@ async function runOnSandbox(services, ctx, target, op, options = {}) {
1149
1391
  const current = sandbox.scope === "user" && ctx.subjectId && ctx.attemptAuthority ? await resolvePersonalMachineConnectionForAttempt(services.db, {
1150
1392
  accountId: ctx.accountId,
1151
1393
  workspaceId: ctx.workspaceId,
1152
- subjectId: ctx.subjectId,
1153
- sessionId: ctx.sessionId,
1154
- turnId: ctx.attemptAuthority.turnId,
1155
- attemptId: ctx.attemptAuthority.attemptId,
1156
- executionGeneration: ctx.attemptAuthority.executionGeneration,
1157
- enrollmentId: sandbox.enrollmentId,
1158
- requireActiveSandbox: false
1159
- }) : await getLiveEnrollmentConnection2(
1160
- services.db,
1161
- ctx.subjectId ? {
1162
- accountId: ctx.accountId,
1163
- workspaceId: ctx.workspaceId,
1164
- subjectId: ctx.subjectId
1165
- } : sandbox.workspaceId,
1166
- sandbox.enrollmentId
1167
- );
1168
- return runOnOperationAdmission(services, current);
1169
- } catch {
1170
- return null;
1171
- }
1172
- };
1173
- const result = await executeRunOnSelfhostedMachine(
1174
- {
1175
- workspaceId: sandbox.workspaceId,
1176
- agentId: sandbox.enrollmentId,
1177
- connectionInstanceId: enrollment.connectionInstanceId,
1178
- workspaceRoot: enrollment.workspaceRoot,
1179
- controlRpc: controlRpc(services.bus),
1180
- relay: relayConfigFromSettings(services.settings),
1181
- controlTimeoutMs: services.settings.sandboxSelfhostedControlTimeoutMs,
1182
- execTimeoutMs: services.settings.sandboxSelfhostedExecTimeoutMs,
1183
- operationResourcePolicy: enrollment.operationPolicy,
1184
- operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
1185
- operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
1186
- resolveOperationAdmission,
1187
- ...options.transientExecEnvironment !== void 0 ? { transientExecEnvironment: options.transientExecEnvironment } : {},
1188
- ...services.settings.agentOpStreamEnabled === true && enrollment.opStream === true && services.bus?.getOpStreamConnection ? {
1189
- opStream: {
1190
- transport: new NatsOpStreamTransport2(
1191
- async () => services.bus?.getOpStreamConnection?.() ?? null
1192
- )
1193
- }
1194
- } : {}
1195
- },
1196
- target,
1197
- op
1198
- );
1199
- return { ...result, targetName: sandbox.name };
1200
- }
1201
- async function provisionSandbox(services, ctx, input) {
1202
- if (input.kind === "selfhosted") {
1203
- const base = (services.settings.publicBaseUrl ?? "https://app.opengeni.ai").replace(/\/+$/, "");
1204
- const unixEnvironment = `OPENGENI_API_URL=${base} OPENGENI_WORKSPACE_ID=${ctx.workspaceId}`;
1205
- const windowsEnvironment = `$env:OPENGENI_API_URL='${base}'; $env:OPENGENI_WORKSPACE_ID='${ctx.workspaceId}';`;
1206
- return {
1207
- kind: "selfhosted",
1208
- instructions: "Share one of these deployment-specific commands with a human operator. It installs the OpenGeni agent and starts `opengeni-agent connect` for this exact deployment and workspace; do not run a second bare `connect`. Complete the device-flow at the verification URL (the loud whole-machine + screen-control consent), and the machine then appears here as an attachable selfhosted sandbox. Existing connections to other OpenGeni workspaces or deployments are preserved.",
1209
- // Install from THIS control plane's origin (not a hardcoded public CDN): the
1210
- // served install script is rewritten to pull the per-SHA agent baked into
1211
- // this exact deployment (see apps/api/src/routes/install.ts), so a deployed
1212
- // env is self-contained and a private/air-gapped one works with no public DNS.
1213
- installCommandUnix: `curl -fsSL ${base}/install.sh | ${unixEnvironment} sh`,
1214
- installCommandWindows: `${windowsEnvironment} irm ${base}/install.ps1 | iex`,
1215
- verificationUri: `${base}/device`,
1216
- note: "Whole-machine access requires explicit human consent in the device-flow web page; the agent cannot self-consent."
1217
- };
1218
- }
1219
- const { createSandbox } = await import("@opengeni/db");
1220
- const sandbox = await createSandbox(services.db, {
1221
- accountId: ctx.accountId,
1222
- workspaceId: ctx.workspaceId,
1223
- kind: "modal",
1224
- name: input.name?.trim() || "modal-box"
1225
- });
1226
- return {
1227
- kind: "modal",
1228
- sandbox,
1229
- note: "A named Modal sandbox record was created, but it is NOT yet attachable as a swap target: routing a session onto a second Modal box is not supported yet, so a sandbox_swap to this id is rejected. Use the session's own box (the default) or attach a Connected Machine instead."
1230
- };
1231
- }
1232
-
1233
- // src/sandbox/runtime-settings.ts
1234
- import {
1235
- CapabilityPack
1236
- } from "@opengeni/contracts";
1237
- import {
1238
- getRigVersion,
1239
- getWorkspacePack,
1240
- listPackInstallations
1241
- } from "@opengeni/db";
1242
- import { resolveModalCheckpointProviderBinding } from "@opengeni/runtime/sandbox";
1243
-
1244
- // src/rigs/provider-images.ts
1245
- import { createHash } from "crypto";
1246
- import {
1247
- RigProviderImage as RigProviderImageContract,
1248
- stableJson
1249
- } from "@opengeni/contracts";
1250
- function sha256(value) {
1251
- return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
1252
- }
1253
- function rigProviderImageProviderBindingKeyHash(bindingKey) {
1254
- return sha256(bindingKey);
1255
- }
1256
- function rigProviderImageSetupHash(definition) {
1257
- return sha256(definition.setupScript ?? "");
1258
- }
1259
- function rigProviderImageContentHash(input) {
1260
- return sha256(
1261
- stableJson({
1262
- version: 2,
1263
- backend: input.backend,
1264
- sourceImage: input.sourceImage,
1265
- setupScript: input.definition.setupScript,
1266
- checks: input.definition.checks,
1267
- credentialHooks: input.definition.credentialHooks,
1268
- defaultVariableSetIds: input.definition.defaultVariableSetIds
1269
- })
1270
- );
1271
- }
1272
- function rigProviderImageBuildRequestId(input) {
1273
- const bytes = createHash("sha256").update("opengeni-rig-provider-image-build-v2\0", "utf8").update(input.targetId, "utf8").update("\0", "utf8").update(input.backend, "utf8").update("\0", "utf8").update(input.contentHash, "utf8").digest().subarray(0, 16);
1274
- bytes[6] = bytes[6] & 15 | 80;
1275
- bytes[8] = bytes[8] & 63 | 128;
1276
- const hex = bytes.toString("hex");
1277
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1278
- }
1279
- function rigProviderImageMatchesDefinition(image, definition) {
1280
- return image.setupHash === rigProviderImageSetupHash(definition) && image.contentHash === rigProviderImageContentHash({
1281
- backend: image.backend,
1282
- sourceImage: image.sourceImage,
1283
- definition
1284
- });
1285
- }
1286
- function rigProviderImagesFromVerification(verification, definition) {
1287
- const parsed = RigProviderImageContract.safeParse(verification?.providerImage);
1288
- if (!parsed.success || parsed.data.status === "building") return {};
1289
- if (!rigProviderImageMatchesDefinition(parsed.data, definition)) return {};
1290
- return { [parsed.data.backend]: parsed.data };
1291
- }
1292
-
1293
- // src/sandbox/runtime-settings.ts
1294
- function packInstallationUsesLegacyRuntime(input) {
1295
- return input.manifestSnapshot === null && input.manifestDigest === null;
1296
- }
1297
- async function resolveWorkspaceLegacyRuntimePacks(db, workspaceId) {
1298
- const installations = await listPackInstallations(db, workspaceId);
1299
- const packs2 = [];
1300
- for (const installation of installations) {
1301
- if (installation.status !== "active" || !packInstallationUsesLegacyRuntime(installation)) {
1302
- continue;
1303
- }
1304
- const registration = await getWorkspacePack(db, workspaceId, installation.packId);
1305
- const parsed = CapabilityPack.safeParse(registration?.pack);
1306
- if (parsed.success) packs2.push(parsed.data);
1307
- }
1308
- return packs2;
1309
- }
1310
- function legacySandboxRuntimeFromPacks(packs2) {
1311
- const imagePacks = packs2.filter(
1312
- (pack) => typeof pack.sandboxImage === "string" && pack.sandboxImage.trim().length > 0
1313
- );
1314
- if (imagePacks.length > 1) {
1315
- const ids = imagePacks.map((pack) => pack.id).sort().join(", ");
1316
- throw new Error(
1317
- `Multiple enabled packs declare a sandbox image (${ids}). Only one enabled pack per workspace may declare sandboxImage; disable the others and retry.`
1318
- );
1319
- }
1320
- return {
1321
- sandboxImage: imagePacks[0]?.sandboxImage?.trim() ?? null,
1322
- sandboxProviderImages: imagePacks[0]?.sandboxProviderImages ?? null
1323
- };
1324
- }
1325
- function settingsWithPackSandboxImage(settings, sandboxImage, sandboxProviderImages = null) {
1326
- if (!sandboxImage) return settings;
1327
- return {
1328
- ...settings,
1329
- dockerImage: sandboxImage,
1330
- modalImageRef: sandboxImage,
1331
- modalImageId: sandboxProviderImages?.modal?.imageId
1332
- };
1333
- }
1334
- function settingsWithRigImage(settings, rigImage) {
1335
- void rigImage;
1336
- return settings;
1337
- }
1338
- function rigProviderImageSourceImage(settings, backend) {
1339
- if (backend === "modal") return settings.modalImageId ?? settings.modalImageRef ?? null;
1340
- if (backend === "docker") return settings.dockerImage ?? null;
1341
- return null;
1342
- }
1343
- function resolveRigProviderImageSelection(settings, version, backend, currentProviderBindingKeyHash) {
1344
- if (!version) return { settings, reason: "missing", contentHash: null, imageId: null };
1345
- if (backend !== "modal") {
1346
- return { settings, reason: "provider_unsupported", contentHash: null, imageId: null };
1347
- }
1348
- const image = version.providerImages[backend];
1349
- if (!image) return { settings, reason: "missing", contentHash: null, imageId: null };
1350
- if (image.status !== "ready" || !image.imageId) {
1351
- return {
1352
- settings,
1353
- reason: "not_ready",
1354
- contentHash: image.contentHash,
1355
- imageId: image.imageId
1356
- };
1357
- }
1358
- if (image.coldBootValidation?.version !== 1) {
1359
- return {
1360
- settings,
1361
- reason: "not_cold_boot_validated",
1362
- contentHash: image.contentHash,
1363
- imageId: null
1364
- };
1365
- }
1366
- const sourceImage = rigProviderImageSourceImage(settings, backend);
1367
- const expectedContentHash = rigProviderImageContentHash({
1368
- backend,
1369
- sourceImage,
1370
- definition: version
1371
- });
1372
- if (image.sourceImage !== sourceImage || image.contentHash !== expectedContentHash || !rigProviderImageMatchesDefinition(image, version)) {
1373
- return {
1374
- settings,
1375
- reason: "content_mismatch",
1376
- contentHash: expectedContentHash,
1377
- imageId: null
1378
- };
1379
- }
1380
- if (!image.providerBindingKeyHash || !currentProviderBindingKeyHash) {
1381
- return {
1382
- settings,
1383
- reason: "provider_binding_unavailable",
1384
- contentHash: expectedContentHash,
1385
- imageId: null
1386
- };
1387
- }
1388
- if (image.providerBindingKeyHash !== currentProviderBindingKeyHash) {
1389
- return {
1390
- settings,
1391
- reason: "provider_binding_mismatch",
1392
- contentHash: expectedContentHash,
1393
- imageId: null
1394
- };
1395
- }
1396
- return {
1397
- settings: { ...settings, modalImageId: image.imageId },
1398
- reason: "selected",
1399
- contentHash: expectedContentHash,
1400
- imageId: image.imageId
1401
- };
1402
- }
1403
- async function resolveRigProviderImageForRun(settings, version, backend, resolveBinding = resolveModalCheckpointProviderBinding) {
1404
- const image = backend === "modal" ? version?.providerImages.modal : null;
1405
- if (image?.status !== "ready" || !image.providerBindingKeyHash) {
1406
- return resolveRigProviderImageSelection(settings, version, backend, null);
1407
- }
1408
- const structural = resolveRigProviderImageSelection(
1409
- settings,
1410
- version,
1411
- backend,
1412
- image.providerBindingKeyHash
1413
- );
1414
- if (structural.reason !== "selected") return structural;
1415
- let currentProviderBindingKeyHash = null;
1416
- try {
1417
- const binding = await resolveBinding(settings);
1418
- currentProviderBindingKeyHash = rigProviderImageProviderBindingKeyHash(binding.key);
1419
- } catch {
1420
- }
1421
- return resolveRigProviderImageSelection(
1422
- settings,
1423
- version,
1424
- backend,
1425
- currentProviderBindingKeyHash
1394
+ subjectId: ctx.subjectId,
1395
+ sessionId: ctx.sessionId,
1396
+ turnId: ctx.attemptAuthority.turnId,
1397
+ attemptId: ctx.attemptAuthority.attemptId,
1398
+ executionGeneration: ctx.attemptAuthority.executionGeneration,
1399
+ enrollmentId: sandbox.enrollmentId,
1400
+ requireActiveSandbox: false
1401
+ }) : await getLiveEnrollmentConnection2(
1402
+ services.db,
1403
+ ctx.subjectId ? {
1404
+ accountId: ctx.accountId,
1405
+ workspaceId: ctx.workspaceId,
1406
+ subjectId: ctx.subjectId
1407
+ } : sandbox.workspaceId,
1408
+ sandbox.enrollmentId
1409
+ );
1410
+ return runOnOperationAdmission(services, current);
1411
+ } catch {
1412
+ return null;
1413
+ }
1414
+ };
1415
+ const result = await executeRunOnSelfhostedMachine(
1416
+ {
1417
+ workspaceId: sandbox.workspaceId,
1418
+ agentId: sandbox.enrollmentId,
1419
+ connectionInstanceId: enrollment.connectionInstanceId,
1420
+ workspaceRoot: enrollment.workspaceRoot,
1421
+ controlRpc: controlRpc(services.bus),
1422
+ relay: relayConfigFromSettings(services.settings),
1423
+ controlTimeoutMs: services.settings.sandboxSelfhostedControlTimeoutMs,
1424
+ execTimeoutMs: services.settings.sandboxSelfhostedExecTimeoutMs,
1425
+ operationResourcePolicy: enrollment.operationPolicy,
1426
+ operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
1427
+ operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
1428
+ resolveOperationAdmission,
1429
+ ...options.transientExecEnvironment !== void 0 ? { transientExecEnvironment: options.transientExecEnvironment } : {},
1430
+ ...services.settings.agentOpStreamEnabled === true && enrollment.opStream === true && services.bus?.getOpStreamConnection ? {
1431
+ opStream: {
1432
+ transport: new NatsOpStreamTransport2(
1433
+ async () => services.bus?.getOpStreamConnection?.() ?? null
1434
+ )
1435
+ }
1436
+ } : {}
1437
+ },
1438
+ target,
1439
+ op
1426
1440
  );
1441
+ return { ...result, targetName: sandbox.name };
1427
1442
  }
1428
- async function settingsWithRigProviderImage(settings, version, backend, resolveBinding = resolveModalCheckpointProviderBinding) {
1429
- return (await resolveRigProviderImageForRun(settings, version, backend, resolveBinding)).settings;
1430
- }
1431
- async function resolveSessionSandboxRuntime(db, settings, session) {
1432
- const [packs2, rigVersion] = await Promise.all([
1433
- resolveWorkspaceLegacyRuntimePacks(db, session.workspaceId),
1434
- session.rigId && session.rigVersionId ? getRigVersion(db, session.workspaceId, session.rigId, session.rigVersionId) : Promise.resolve(null)
1435
- ]);
1436
- if (session.rigVersionId && !rigVersion) {
1437
- throw new Error(`Frozen rig version ${session.rigVersionId} is unavailable`);
1443
+ async function provisionSandbox(services, ctx, input) {
1444
+ if (input.kind === "selfhosted") {
1445
+ const base = (services.settings.publicBaseUrl ?? "https://app.opengeni.ai").replace(/\/+$/, "");
1446
+ const unixEnvironment = `OPENGENI_API_URL=${base} OPENGENI_WORKSPACE_ID=${ctx.workspaceId}`;
1447
+ const windowsEnvironment = `$env:OPENGENI_API_URL='${base}'; $env:OPENGENI_WORKSPACE_ID='${ctx.workspaceId}';`;
1448
+ return {
1449
+ kind: "selfhosted",
1450
+ instructions: "Share one of these deployment-specific commands with a human operator. It installs the OpenGeni agent and starts `opengeni-agent connect` for this exact deployment and workspace; do not run a second bare `connect`. Complete the device-flow at the verification URL (the loud whole-machine + screen-control consent), and the machine then appears here as an attachable selfhosted sandbox. Existing connections to other OpenGeni workspaces or deployments are preserved.",
1451
+ // Install from THIS control plane's origin (not a hardcoded public CDN): the
1452
+ // served install script is rewritten to pull the per-SHA agent baked into
1453
+ // this exact deployment (see apps/api/src/routes/install.ts), so a deployed
1454
+ // env is self-contained and a private/air-gapped one works with no public DNS.
1455
+ installCommandUnix: `curl -fsSL ${base}/install.sh | ${unixEnvironment} sh`,
1456
+ installCommandWindows: `${windowsEnvironment} irm ${base}/install.ps1 | iex`,
1457
+ verificationUri: `${base}/device`,
1458
+ note: "Whole-machine access requires explicit human consent in the device-flow web page; the agent cannot self-consent."
1459
+ };
1438
1460
  }
1439
- const legacy = legacySandboxRuntimeFromPacks(packs2);
1440
- const logicalSettings = rigVersion ? settings : settingsWithPackSandboxImage(settings, legacy.sandboxImage, legacy.sandboxProviderImages);
1461
+ const { createSandbox } = await import("@opengeni/db");
1462
+ const sandbox = await createSandbox(services.db, {
1463
+ accountId: ctx.accountId,
1464
+ workspaceId: ctx.workspaceId,
1465
+ kind: "modal",
1466
+ name: input.name?.trim() || "modal-box"
1467
+ });
1441
1468
  return {
1442
- settings: {
1443
- ...logicalSettings,
1444
- sandboxBackend: session.sandboxBackend
1445
- },
1446
- image: rigProviderImageSourceImage(logicalSettings, session.sandboxBackend),
1447
- rigVersion
1469
+ kind: "modal",
1470
+ sandbox,
1471
+ note: "A named Modal sandbox record was created, but it is NOT yet attachable as a swap target: routing a session onto a second Modal box is not supported yet, so a sandbox_swap to this id is rejected. Use the session's own box (the default) or attach a Connected Machine instead."
1448
1472
  };
1449
1473
  }
1450
- async function providerSettingsForSessionSandboxRuntime(runtime, backend) {
1451
- return await settingsWithRigProviderImage(runtime.settings, runtime.rigVersion, backend);
1452
- }
1453
1474
 
1454
1475
  // src/access/index.ts
1455
1476
  import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
@@ -1467,7 +1488,38 @@ import { HTTPException } from "hono/http-exception";
1467
1488
  var bearerPrefix = "Bearer ";
1468
1489
  var accessContextByRequest = /* @__PURE__ */ new WeakMap();
1469
1490
  var canonicalManagedCookieContexts = /* @__PURE__ */ new WeakSet();
1491
+ var canonicalLocalHumanContexts = /* @__PURE__ */ new WeakSet();
1470
1492
  var resolvedAccessGrantAuthorizations = /* @__PURE__ */ new WeakSet();
1493
+ var accountScopedApiKeyContexts = /* @__PURE__ */ new WeakMap();
1494
+ var accountScopedApiKeyAccountPermissions = /* @__PURE__ */ new Set([
1495
+ "account:read",
1496
+ "account:admin",
1497
+ "workspace:create",
1498
+ "billing:read",
1499
+ "billing:manage",
1500
+ "api_keys:manage"
1501
+ ]);
1502
+ var accountScopedApiKeyWorkspaceExcludedPermissions = /* @__PURE__ */ new Set([
1503
+ "account:read",
1504
+ "account:admin",
1505
+ "workspace:create",
1506
+ "billing:read",
1507
+ "billing:manage"
1508
+ ]);
1509
+ function accountScopedApiKeyWorkspaceAuthority(context) {
1510
+ const authority = accountScopedApiKeyContexts.get(context);
1511
+ if (!authority) return null;
1512
+ const matchingAccountGrants = context.accountGrants.filter(
1513
+ (grant) => grant.accountId === authority.accountId && grant.subjectId === context.subjectId
1514
+ );
1515
+ if (!context.subjectId.startsWith("api_key:") || matchingAccountGrants.length !== 1 || context.defaultAccountId !== authority.accountId || context.defaultWorkspaceId !== null) {
1516
+ return null;
1517
+ }
1518
+ return {
1519
+ accountId: authority.accountId,
1520
+ permissions: [...authority.permissions]
1521
+ };
1522
+ }
1471
1523
  async function requireAccessContext(c, deps) {
1472
1524
  let pending = accessContextByRequest.get(c.req.raw);
1473
1525
  if (!pending) {
@@ -1505,7 +1557,8 @@ function accessGrantAuthorizationFromContext(context, grant) {
1505
1557
  accountGrant: contextIntegrity ? matchingAccountGrants[0] : null,
1506
1558
  authenticatedSubjectId: context.subjectId,
1507
1559
  contextIntegrity,
1508
- canonicalManagedHumanSession: isCanonicalManagedHumanSession(context, grant)
1560
+ canonicalManagedHumanSession: isCanonicalManagedHumanSession(context, grant),
1561
+ canonicalLocalHumanSession: isCanonicalLocalHumanSession(context, grant)
1509
1562
  };
1510
1563
  resolvedAccessGrantAuthorizations.add(authorization);
1511
1564
  return authorization;
@@ -1522,13 +1575,31 @@ function requireAccountAdminAuthorizationStamp(authorization) {
1522
1575
  permission: "account:admin"
1523
1576
  });
1524
1577
  }
1578
+ async function requireCanonicalLocalAccountAdministrator(c, deps, accountId) {
1579
+ if (c.req.header("authorization")) {
1580
+ throw new HTTPException(401, { message: "organization administrator session required" });
1581
+ }
1582
+ const context = await requireAccessContext(c, deps);
1583
+ const grant = context.workspaceGrants.find((candidate) => candidate.accountId === accountId);
1584
+ if (!grant) {
1585
+ throw new HTTPException(403, {
1586
+ message: "local organization administration is not authorized"
1587
+ });
1588
+ }
1589
+ const authorization = accessGrantAuthorizationFromContext(context, grant);
1590
+ if (!authorization.canonicalLocalHumanSession) {
1591
+ throw new HTTPException(401, { message: "organization administrator session required" });
1592
+ }
1593
+ requireAccountAdminAuthorizationStamp(authorization);
1594
+ return { subjectId: context.subjectId, authorization };
1595
+ }
1525
1596
  async function requireAccessGrantAuthorization(c, deps, workspaceId, permission) {
1526
1597
  const context = await requireAccessContext(c, deps);
1527
1598
  return await accessGrantAuthorization(context, deps, workspaceId, permission);
1528
1599
  }
1529
1600
  async function accessGrantAuthorization(context, deps, workspaceId, permission) {
1530
1601
  const principalKind = hostedHumanSessionPrincipalKind(context);
1531
- const grant = context.workspaceGrants.find((candidate) => candidate.workspaceId === workspaceId) ?? await getWorkspaceGrant(
1602
+ let grant = context.workspaceGrants.find((candidate) => candidate.workspaceId === workspaceId) ?? await getWorkspaceGrant(
1532
1603
  deps.db,
1533
1604
  context.subjectId,
1534
1605
  workspaceId,
@@ -1539,7 +1610,20 @@ async function accessGrantAuthorization(context, deps, workspaceId, permission)
1539
1610
  if (!workspace) {
1540
1611
  throw new HTTPException(404, { message: "workspace not found" });
1541
1612
  }
1542
- throw new HTTPException(403, { message: "workspace access denied" });
1613
+ const authority = accountScopedApiKeyWorkspaceAuthority(context);
1614
+ const requiredWorkspacePermission = permission ?? "workspace:read";
1615
+ if (authority && workspace.accountId === authority.accountId && workspace.kind === "shared" && hasPermission(authority.permissions, requiredWorkspacePermission)) {
1616
+ grant = {
1617
+ workspaceId: workspace.id,
1618
+ accountId: workspace.accountId,
1619
+ subjectId: context.subjectId,
1620
+ ...context.subjectLabel ? { subjectLabel: context.subjectLabel } : {},
1621
+ permissions: authority.permissions,
1622
+ principalKind: "api_key"
1623
+ };
1624
+ } else {
1625
+ throw new HTTPException(403, { message: "workspace access denied" });
1626
+ }
1543
1627
  }
1544
1628
  if (permission) {
1545
1629
  requirePermission(grant, permission);
@@ -1549,6 +1633,9 @@ async function accessGrantAuthorization(context, deps, workspaceId, permission)
1549
1633
  function isCanonicalManagedHumanSession(context, grant) {
1550
1634
  return canonicalManagedCookieContexts.has(context) && grant.subjectId === context.subjectId && grant.subjectId.startsWith("user:");
1551
1635
  }
1636
+ function isCanonicalLocalHumanSession(context, grant) {
1637
+ return canonicalLocalHumanContexts.has(context) && context.mode === "local" && context.subjectId === "dev" && grant.subjectId === context.subjectId && grant.principalKind === "human_session" && grant.metadata?.delegated !== true && !grant.serviceInitiator;
1638
+ }
1552
1639
  function hostedHumanSessionPrincipalKind(context) {
1553
1640
  if (context.mode !== "managed" || context.workspaceGrants.length === 0) {
1554
1641
  return void 0;
@@ -1598,7 +1685,7 @@ async function resolveAccessContext(c, deps) {
1598
1685
  if (delegated) {
1599
1686
  return delegated;
1600
1687
  }
1601
- return await bootstrapWorkspace(deps.db, {
1688
+ const context = await bootstrapWorkspace(deps.db, {
1602
1689
  accountExternalSource: "opengeni:local",
1603
1690
  accountExternalId: "default",
1604
1691
  accountName: "Local",
@@ -1608,6 +1695,8 @@ async function resolveAccessContext(c, deps) {
1608
1695
  subjectId: "dev",
1609
1696
  subjectLabel: "Local dev"
1610
1697
  });
1698
+ canonicalLocalHumanContexts.add(context);
1699
+ return context;
1611
1700
  }
1612
1701
  if (deps.settings.productAccessMode === "configured") {
1613
1702
  const delegated = await delegatedAccessContext(c, deps, "configured");
@@ -1676,8 +1765,10 @@ async function apiKeyAccessContext(c, deps, mode) {
1676
1765
  const subjectId = `api_key:${apiKey.id}`;
1677
1766
  const accountPermissions = apiKey.workspaceId ? apiKey.permissions.filter(
1678
1767
  (permission) => permission === "billing:read" || permission === "billing:manage"
1679
- ) : apiKey.permissions;
1680
- return {
1768
+ ) : apiKey.permissions.filter(
1769
+ (permission) => accountScopedApiKeyAccountPermissions.has(permission)
1770
+ );
1771
+ const context = {
1681
1772
  mode,
1682
1773
  subjectId,
1683
1774
  subjectLabel: apiKey.name,
@@ -1702,6 +1793,20 @@ async function apiKeyAccessContext(c, deps, mode) {
1702
1793
  defaultAccountId: apiKey.accountId,
1703
1794
  defaultWorkspaceId: apiKey.workspaceId
1704
1795
  };
1796
+ if (apiKey.workspaceId === null && apiKey.credentialKind === "organization") {
1797
+ accountScopedApiKeyContexts.set(
1798
+ context,
1799
+ Object.freeze({
1800
+ accountId: apiKey.accountId,
1801
+ permissions: Object.freeze(
1802
+ apiKey.permissions.filter(
1803
+ (permission) => !accountScopedApiKeyWorkspaceExcludedPermissions.has(permission)
1804
+ )
1805
+ )
1806
+ })
1807
+ );
1808
+ }
1809
+ return context;
1705
1810
  }
1706
1811
  async function delegatedAccessContext(c, deps, mode, token = bearerToken(c)) {
1707
1812
  const delegationSecret = resolveFirstPartyDelegationSecret(deps.settings);
@@ -1972,6 +2077,7 @@ async function resolveSessionAuthorizationActor(db, grant) {
1972
2077
  import { configuredStaticUsageLimits, resolveModelProvider } from "@opengeni/config";
1973
2078
  import {
1974
2079
  countActiveApiKeysForWorkspace,
2080
+ countActiveOrganizationApiKeysForAccount,
1975
2081
  countScheduledTasksForWorkspace,
1976
2082
  countWorkspacesForAccount,
1977
2083
  getBillingBalance,
@@ -2050,10 +2156,10 @@ async function checkStaticCaps(deps, input, externallyBilled) {
2050
2156
  );
2051
2157
  }
2052
2158
  case "api_key:create": {
2053
- if (!limits.maxApiKeysPerWorkspace || !input.workspaceId) {
2159
+ if (!limits.maxApiKeysPerWorkspace) {
2054
2160
  return { allowed: true };
2055
2161
  }
2056
- const count = await countActiveApiKeysForWorkspace(deps.db, input.workspaceId);
2162
+ const count = input.workspaceId ? await countActiveApiKeysForWorkspace(deps.db, input.workspaceId) : await countActiveOrganizationApiKeysForAccount(deps.db, input.accountId);
2057
2163
  return count < limits.maxApiKeysPerWorkspace ? { allowed: true } : blocked(
2058
2164
  "max_api_keys_per_workspace",
2059
2165
  `API key limit reached (${limits.maxApiKeysPerWorkspace})`
@@ -4965,42 +5071,15 @@ function sha256Hex2(bytes) {
4965
5071
 
4966
5072
  // src/domain/environments.ts
4967
5073
  import { environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes2 } from "@opengeni/config";
5074
+ import {
5075
+ variableSetVariableNameReservation
5076
+ } from "@opengeni/contracts";
4968
5077
  import { getVariableSet as getVariableSet2, recordAuditEvent } from "@opengeni/db";
4969
5078
  import { HTTPException as HTTPException6 } from "hono/http-exception";
4970
5079
  var MAX_ENVIRONMENTS_PER_WORKSPACE = 25;
4971
5080
  var MAX_VARIABLES_PER_ENVIRONMENT = 100;
4972
- var reservedExactNames = /* @__PURE__ */ new Set([
4973
- "HOME",
4974
- "PATH",
4975
- "SHELL",
4976
- "USER",
4977
- "LOGNAME",
4978
- "TMPDIR",
4979
- "IFS",
4980
- "ENV",
4981
- "BASH_ENV",
4982
- "NODE_OPTIONS",
4983
- "PYTHONPATH",
4984
- "PYTHONSTARTUP",
4985
- "PERL5OPT",
4986
- "PERL5LIB",
4987
- "GH_TOKEN",
4988
- "GITHUB_TOKEN",
4989
- "GITLAB_TOKEN",
4990
- "AZURE_DEVOPS_EXT_PAT",
4991
- "GIT_ASKPASS",
4992
- "GIT_TERMINAL_PROMPT"
4993
- ]);
4994
- var reservedPrefixes = [
4995
- "OPENGENI_",
4996
- "GIT_CONFIG_",
4997
- "GIT_AUTHOR_",
4998
- "GIT_COMMITTER_",
4999
- "LD_",
5000
- "DYLD_"
5001
- ];
5002
5081
  function assertAllowedVariableSetVariableName(name) {
5003
- if (reservedExactNames.has(name) || reservedPrefixes.some((prefix) => name.startsWith(prefix))) {
5082
+ if (variableSetVariableNameReservation(name)) {
5004
5083
  throw new HTTPException6(422, {
5005
5084
  message: `reserved variable set variable name / reserved environment variable name: ${name}`
5006
5085
  });
@@ -6250,15 +6329,40 @@ function personalConnectionDelegationsFromParent(input) {
6250
6329
  const projected = [
6251
6330
  ...mcp,
6252
6331
  ...input.parentDelegations.filter(
6253
- (item) => childEligible(item) && (item.connectionType === "social" || item.connectionType === "atlassian" || item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID)
6332
+ (item) => childEligible(item) && (item.connectionType === "social" || item.connectionType === "atlassian" || item.connectionType === "github_personal" || item.serverId === GOOGLE_DRIVE_PUBLICATION_SERVER_ID)
6254
6333
  ).map((item) => ({ ...item }))
6255
6334
  ];
6256
- if (input.rejectActivatedConnections && projected.some((delegation) => delegation.userDelegation)) {
6335
+ const requestedGitHub = personalGitHubRepositoryResources(input.personalGitHubResources ?? []);
6336
+ const inherited = projected.flatMap((delegation) => {
6337
+ if (delegation.connectionType !== "github_personal") return [delegation];
6338
+ if (requestedGitHub.length === 0) return [];
6339
+ const snapshot = delegation.personalGitHubRepositorySelection;
6340
+ if (!snapshot) return [];
6341
+ const repositories = requestedGitHub.map((resource) => {
6342
+ const parent = snapshot.repositories.find(
6343
+ (candidate) => candidate.repositoryId === resource.repositoryId && candidate.canonicalUrl === resource.uri && candidate.ref === resource.ref
6344
+ );
6345
+ if (!parent || resource.credentialBindingId !== snapshot.credentialBindingId || resource.access === "write" && parent.access !== "write") {
6346
+ throw new Error("agent-created personal GitHub repository exceeds parent authority");
6347
+ }
6348
+ return { ...parent, access: resource.access };
6349
+ });
6350
+ return [
6351
+ {
6352
+ ...delegation,
6353
+ personalGitHubRepositorySelection: { ...snapshot, repositories }
6354
+ }
6355
+ ];
6356
+ });
6357
+ if (requestedGitHub.length > 0 && !inherited.some((delegation) => delegation.connectionType === "github_personal")) {
6358
+ throw new Error("agent-created personal GitHub repository authority is unavailable");
6359
+ }
6360
+ if (input.rejectActivatedConnections && inherited.some((delegation) => delegation.userDelegation)) {
6257
6361
  throw new Error(
6258
6362
  "scheduled connection authority is not available until task occurrence authority is activated"
6259
6363
  );
6260
6364
  }
6261
- return projected;
6365
+ return inherited;
6262
6366
  }
6263
6367
  function googleDrivePublicationDelegationFromVisibleConnections(input) {
6264
6368
  const selection = input.authoritySelection;
@@ -6471,11 +6575,6 @@ async function freezePersonalConnectionDelegations(input) {
6471
6575
  "agent-created work inherits connection authority from its exact parent turn"
6472
6576
  );
6473
6577
  }
6474
- if (personalGitHubResources.length > 0) {
6475
- throw new Error(
6476
- "agent-created personal GitHub repository authority is not activated in this delivery phase"
6477
- );
6478
- }
6479
6578
  const inherited = personalConnectionDelegationsFromParent({
6480
6579
  servers,
6481
6580
  parentDelegations: await getSessionTurnPersonalConnectionDelegations(
@@ -6484,6 +6583,7 @@ async function freezePersonalConnectionDelegations(input) {
6484
6583
  input.source.sessionId,
6485
6584
  input.source.turnId
6486
6585
  ),
6586
+ personalGitHubResources,
6487
6587
  ...input.targetSessionId ? { targetSessionId: input.targetSessionId } : {},
6488
6588
  ...input.rejectUnselectedActivatedConnections !== void 0 ? { rejectActivatedConnections: input.rejectUnselectedActivatedConnections } : {}
6489
6589
  });
@@ -6935,6 +7035,8 @@ import {
6935
7035
  createScheduledTask,
6936
7036
  deleteScheduledTask,
6937
7037
  getConnectionMetadata as getConnectionMetadata4,
7038
+ getEnrollment as getEnrollment3,
7039
+ getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
6938
7040
  getKnowledgeSourceForSyncAuthority,
6939
7041
  getNestedAgentDepthDeploymentPolicy,
6940
7042
  getRig as getRig5,
@@ -6942,6 +7044,7 @@ import {
6942
7044
  getScheduledTaskIncludingDeletedForUpdate,
6943
7045
  getScheduledTaskPersonalConnectionDelegations,
6944
7046
  getScheduledTaskXaiProviderAccountAuthoritySnapshot,
7047
+ getSandbox as getSandbox4,
6945
7048
  getSessionTurnXaiProviderAccountAuthoritySnapshot as getSessionTurnXaiProviderAccountAuthoritySnapshot2,
6946
7049
  getSession as getSession3,
6947
7050
  nestedPostgresSqlState as nestedPostgresSqlState2,
@@ -6966,6 +7069,7 @@ import {
6966
7069
  XAI_SUBSCRIPTION_MODEL_ID_PREFIX
6967
7070
  } from "@opengeni/config";
6968
7071
  import {
7072
+ AUTOMATIC_SESSION_TITLE_FALLBACK,
6969
7073
  CreateSessionRequest,
6970
7074
  DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
6971
7075
  DEFAULT_FIRST_PARTY_MCP_TOOLS,
@@ -6976,6 +7080,7 @@ import {
6976
7080
  ServiceTurnInitiator,
6977
7081
  ServiceTurnInitiatorContext,
6978
7082
  evaluateWorkspaceModelPolicy,
7083
+ normalizeAutomaticSessionTitle,
6979
7084
  resolveWorkspaceSessionToolDefaults as resolveWorkspaceSessionToolDefaults2,
6980
7085
  stableJson as stableJson4,
6981
7086
  SessionMcpApprovalPolicy
@@ -7876,6 +7981,22 @@ function validateSessionMcpCredentialUpdates(input) {
7876
7981
  });
7877
7982
  return encryptedUpdates;
7878
7983
  }
7984
+ var AGENT_CHILD_AUTOMATIC_TITLE_CONTEXT_KEY = "agentChildAutomaticTitle";
7985
+ function freezeAgentChildAutomaticTitleInCreatorContext(context, title) {
7986
+ return title ? { ...context ?? {}, [AGENT_CHILD_AUTOMATIC_TITLE_CONTEXT_KEY]: title } : context;
7987
+ }
7988
+ function initialAutomaticTitleForSessionStart(session, requestedTitle) {
7989
+ const frozenTitle = session.createdByContext[AGENT_CHILD_AUTOMATIC_TITLE_CONTEXT_KEY];
7990
+ return typeof frozenTitle === "string" ? frozenTitle : requestedTitle ?? null;
7991
+ }
7992
+ function automaticTitleForAgentChildCreate(presentation, goal, initialMessage) {
7993
+ for (const candidate of [presentation.automaticTitleCandidate, goal?.text, initialMessage]) {
7994
+ if (typeof candidate !== "string") continue;
7995
+ const normalized = normalizeAutomaticSessionTitle(candidate);
7996
+ if (normalized && normalized !== AUTOMATIC_SESSION_TITLE_FALLBACK) return normalized;
7997
+ }
7998
+ return null;
7999
+ }
7879
8000
  async function createAndStartSessionWithOutcome(input) {
7880
8001
  const sessionMetadata = {
7881
8002
  ...input.metadata,
@@ -7883,6 +8004,10 @@ async function createAndStartSessionWithOutcome(input) {
7883
8004
  reasoningEffort: input.reasoningEffort,
7884
8005
  ...input.latencyMode !== void 0 ? { latencyMode: input.latencyMode } : {}
7885
8006
  };
8007
+ const frozenCreatedByContext = freezeAgentChildAutomaticTitleInCreatorContext(
8008
+ input.createdByContext,
8009
+ input.initialAutomaticTitle
8010
+ );
7886
8011
  if (input.createIdempotencyKey) {
7887
8012
  const keyedResult = await createSessionWithIdempotencyKeyResult(input.db, {
7888
8013
  ...input.requestedSessionId ? { requestedSessionId: input.requestedSessionId } : {},
@@ -7897,7 +8022,7 @@ async function createAndStartSessionWithOutcome(input) {
7897
8022
  toolPolicy: input.toolPolicy,
7898
8023
  metadata: sessionMetadata,
7899
8024
  ...input.createdBy ? { createdBy: input.createdBy } : {},
7900
- ...input.createdByContext ? { createdByContext: input.createdByContext } : {},
8025
+ ...frozenCreatedByContext ? { createdByContext: frozenCreatedByContext } : {},
7901
8026
  createdByActor: input.createdByActor ?? null,
7902
8027
  model: input.model,
7903
8028
  reasoningEffort: input.reasoningEffort,
@@ -7966,7 +8091,7 @@ async function createAndStartSessionWithOutcome(input) {
7966
8091
  toolPolicy: input.toolPolicy,
7967
8092
  metadata: sessionMetadata,
7968
8093
  ...input.createdBy ? { createdBy: input.createdBy } : {},
7969
- ...input.createdByContext ? { createdByContext: input.createdByContext } : {},
8094
+ ...frozenCreatedByContext ? { createdByContext: frozenCreatedByContext } : {},
7970
8095
  createdByActor: input.createdByActor ?? null,
7971
8096
  model: input.model,
7972
8097
  reasoningEffort: input.reasoningEffort,
@@ -8070,6 +8195,10 @@ async function finishStartSession(input, session) {
8070
8195
  ...input.goal.maxAutoContinuations !== void 0 ? { maxAutoContinuations: input.goal.maxAutoContinuations } : {},
8071
8196
  ...input.goal.mutationPolicy !== void 0 ? { mutationPolicy: input.goal.mutationPolicy } : {}
8072
8197
  } : null,
8198
+ initialAutomaticTitle: initialAutomaticTitleForSessionStart(
8199
+ session,
8200
+ input.initialAutomaticTitle
8201
+ ),
8073
8202
  consumeNewSessionDraft: input.consumeNewSessionDraft ?? null,
8074
8203
  rememberNewSessionSelection: input.rememberNewSessionSelection ?? null,
8075
8204
  deferInitialTurn: input.deferInitialTurn === true
@@ -8372,7 +8501,7 @@ function resolveSessionCreateVisibility(input) {
8372
8501
  }
8373
8502
  return input.requestedVisibility === "private" ? "user_private" : "workspace_shared";
8374
8503
  }
8375
- async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawPayload, authorization) {
8504
+ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawPayload, authorization, agentChildPresentation) {
8376
8505
  const { settings, db, bus, workflowClient, objectStorage } = deps;
8377
8506
  const payload = CreateSessionRequest.parse(rawPayload);
8378
8507
  const visibilityProvided = hasOwnProperty(rawPayload, "visibility");
@@ -8480,6 +8609,11 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8480
8609
  });
8481
8610
  }
8482
8611
  }
8612
+ const initialAutomaticTitle = parentSession && agentChildPresentation ? automaticTitleForAgentChildCreate(
8613
+ agentChildPresentation,
8614
+ effectiveGoal,
8615
+ payload.initialMessage
8616
+ ) : null;
8483
8617
  const personalResourceSubjectId = creationInitiator.actor ? (await requireLiveAgentAttemptAuthorization(db, grant, creationInitiator.actor.sessionId)).initiatingHumanSubjectId : grant.subjectId;
8484
8618
  const xaiProviderAccountAuthoritySnapshot = parentSession && creationInitiator.actor ? await getSessionTurnXaiProviderAccountAuthoritySnapshot(
8485
8619
  db,
@@ -8733,6 +8867,8 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8733
8867
  const sandboxChoice = payload.sandbox ?? (payload.targetSandboxId ? "new" : parentSessionId ? "shared" : "new");
8734
8868
  let sandboxGroupId = null;
8735
8869
  let inheritedBackend;
8870
+ let inheritedSandboxOs;
8871
+ let inheritedActiveTarget = null;
8736
8872
  const requestedVariableSetIds = variableSets.map((variableSet) => variableSet.id);
8737
8873
  const variableSetsMatchGroup = (memberVariableSetIds) => stableJson4(memberVariableSetIds) === stableJson4(requestedVariableSetIds);
8738
8874
  const rigVersionMatchesGroup = (memberRigVersionId) => memberRigVersionId === frozenRigVersionId;
@@ -8768,6 +8904,11 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8768
8904
  } else {
8769
8905
  sandboxGroupId = parent.sandboxGroupId;
8770
8906
  inheritedBackend = parent.sandboxBackend;
8907
+ inheritedSandboxOs = parent.sandboxOs;
8908
+ inheritedActiveTarget = parent.activeSandboxId ? {
8909
+ sandboxId: parent.activeSandboxId,
8910
+ workingDir: parent.workingDir
8911
+ } : null;
8771
8912
  }
8772
8913
  } else if (typeof sandboxChoice === "object") {
8773
8914
  const member = await getAnySessionInGroup(db, workspaceId, sandboxChoice.groupId);
@@ -8804,12 +8945,18 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8804
8945
  }
8805
8946
  sandboxGroupId = sandboxChoice.groupId;
8806
8947
  inheritedBackend = member.sandboxBackend;
8948
+ inheritedSandboxOs = member.sandboxOs;
8807
8949
  }
8808
8950
  if (payload.workingDir !== void 0 && !payload.targetSandboxId) {
8809
8951
  throw new HTTPException11(422, {
8810
8952
  message: "workingDir requires targetSandboxId (it is the targeted machine's working directory)"
8811
8953
  });
8812
8954
  }
8955
+ if (inheritedBackend === void 0 && !payload.targetSandboxId && (payload.sandboxBackend ?? settings.sandboxBackend) === "selfhosted") {
8956
+ throw new HTTPException11(422, {
8957
+ message: "selfhosted sessions require targetSandboxId; select an online Connected Machine"
8958
+ });
8959
+ }
8813
8960
  let machineHomeBackend;
8814
8961
  let machineHomeOs;
8815
8962
  if (payload.targetSandboxId && inheritedBackend === void 0 && settings.sandboxOwnershipEnabled && settings.sandboxSelfhostedEnabled) {
@@ -8832,6 +8979,17 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8832
8979
  }
8833
8980
  }
8834
8981
  }
8982
+ const effectiveSandboxBackend = inheritedBackend ?? machineHomeBackend ?? payload.sandboxBackend ?? settings.sandboxBackend;
8983
+ const effectiveSandboxOs = inheritedSandboxOs ?? machineHomeOs;
8984
+ const effectiveSeedTarget = payload.targetSandboxId ? {
8985
+ sandboxId: payload.targetSandboxId,
8986
+ workingDir: payload.workingDir ?? null
8987
+ } : inheritedActiveTarget;
8988
+ if (effectiveSandboxBackend === "selfhosted" && effectiveSeedTarget === null && managedSessionGroupBackend(settings.sandboxBackend, effectiveSandboxBackend) === null) {
8989
+ throw new HTTPException11(422, {
8990
+ message: "self-hosted execution runs on a Connected Machine, but no machine was selected or inherited; connect the parent session to a machine or provide machineTarget"
8991
+ });
8992
+ }
8835
8993
  if (payload.startMode !== "realtime") {
8836
8994
  await requireLimit(deps, {
8837
8995
  accountId: grant.accountId,
@@ -8868,10 +9026,10 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8868
9026
  // machine-targeted create (top-level or own-box child) labels the home
8869
9027
  // "selfhosted" (machineHomeBackend), overriding the caller/deployment
8870
9028
  // default so the row matches where the session actually runs.
8871
- sandboxBackend: inheritedBackend ?? machineHomeBackend ?? payload.sandboxBackend ?? settings.sandboxBackend,
9029
+ sandboxBackend: effectiveSandboxBackend,
8872
9030
  // Mirror the backend relabel on the OS axis: a machine-targeted own-box
8873
- // create carries a derived OS; shared spawns keep the parent-box behavior.
8874
- ...machineHomeOs ? { sandboxOs: machineHomeOs } : {},
9031
+ // create carries a derived OS; shared spawns inherit the exact parent box.
9032
+ ...effectiveSandboxOs ? { sandboxOs: effectiveSandboxOs } : {},
8875
9033
  sandboxGroupId,
8876
9034
  metadata: payload.metadata,
8877
9035
  ...creationInitiator.initiator ? { createdBy: creationInitiator.initiator } : {},
@@ -8887,6 +9045,7 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8887
9045
  rigVersionId: frozenRigVersionId,
8888
9046
  channelId,
8889
9047
  goal: effectiveGoal ?? null,
9048
+ initialAutomaticTitle,
8890
9049
  // Per-session persona instructions (already trimmed/validated by the
8891
9050
  // contracts schema). Persisted on the row; composed system-level at turn
8892
9051
  // time. Not surfaced as an event.
@@ -8908,10 +9067,10 @@ async function createSessionForRequestWithOutcome(deps, grant, workspaceId, rawP
8908
9067
  // active-sandbox pointer is seeded race-free inside createAndStartSession
8909
9068
  // (after the row exists, before the first turn dispatches). Validation
8910
9069
  // (ownership/liveness) lives in swapActiveSandbox; an invalid target 422s.
8911
- seedTargetSandbox: payload.targetSandboxId ? {
8912
- sandboxId: payload.targetSandboxId,
9070
+ seedTargetSandbox: effectiveSeedTarget ? {
9071
+ sandboxId: effectiveSeedTarget.sandboxId,
8913
9072
  settings,
8914
- workingDir: payload.workingDir ?? null,
9073
+ workingDir: effectiveSeedTarget.workingDir,
8915
9074
  resourceSubjectId: personalResourceSubjectId
8916
9075
  } : null,
8917
9076
  consumeNewSessionDraft: payload.expectedNewSessionDraftRevision !== void 0 && payload.startMode !== "realtime" ? {
@@ -9513,6 +9672,15 @@ async function createValidatedScheduledTask(input) {
9513
9672
  rigId: input.payload.rigId,
9514
9673
  agentConfig
9515
9674
  });
9675
+ if (!knowledgeAction) {
9676
+ await validateScheduledTaskMachineTarget({
9677
+ settings: input.settings,
9678
+ db: input.db,
9679
+ grant: input.grant,
9680
+ runMode: input.payload.runMode,
9681
+ agentConfig
9682
+ });
9683
+ }
9516
9684
  if (!knowledgeAction && input.payload.variableSetId) {
9517
9685
  await validateVariableSetAttachment(
9518
9686
  { settings: input.settings, db: input.db },
@@ -9695,6 +9863,59 @@ async function validateScheduledTaskTarget(input) {
9695
9863
  }
9696
9864
  return session;
9697
9865
  }
9866
+ async function validateScheduledTaskMachineTarget(input) {
9867
+ const machineTarget = input.agentConfig.machineTarget;
9868
+ if (!machineTarget) {
9869
+ if (input.runMode !== "existing_session" && (input.agentConfig.sandboxBackend ?? input.settings.sandboxBackend) === "selfhosted") {
9870
+ throw new HTTPException12(422, {
9871
+ message: "self-hosted scheduled tasks require a Connected Machine; select a machine before saving"
9872
+ });
9873
+ }
9874
+ return null;
9875
+ }
9876
+ if (input.runMode === "existing_session") {
9877
+ throw new HTTPException12(422, {
9878
+ message: "machineTarget cannot be used with an existing-session target"
9879
+ });
9880
+ }
9881
+ if (!input.settings.sandboxOwnershipEnabled || !input.settings.sandboxSelfhostedEnabled) {
9882
+ throw new HTTPException12(422, {
9883
+ message: "Connected Machines are not enabled for scheduled tasks in this deployment"
9884
+ });
9885
+ }
9886
+ const access = {
9887
+ accountId: input.grant.accountId,
9888
+ workspaceId: input.grant.workspaceId,
9889
+ subjectId: input.grant.subjectId
9890
+ };
9891
+ const sandbox = await getSandbox4(input.db, access, machineTarget.targetSandboxId);
9892
+ if (!sandbox || sandbox.kind !== "selfhosted" || !sandbox.enrollmentId) {
9893
+ throw new HTTPException12(422, {
9894
+ message: "the selected Connected Machine is unavailable"
9895
+ });
9896
+ }
9897
+ if (sandbox.scope === "user") {
9898
+ throw new HTTPException12(422, {
9899
+ message: "personal Connected Machines cannot run unattended schedules; select a workspace or organization machine"
9900
+ });
9901
+ }
9902
+ const enrollment = input.requireOnline ? await getLiveEnrollmentConnection3(input.db, access, sandbox.enrollmentId) : await getEnrollment3(input.db, access, sandbox.enrollmentId);
9903
+ if (!enrollment || enrollment.status !== "active") {
9904
+ throw new HTTPException12(422, {
9905
+ message: input.requireOnline ? "the selected Connected Machine is offline" : "the selected Connected Machine is unavailable"
9906
+ });
9907
+ }
9908
+ if (input.requireOnline && !enrollment.workspaceRoot) {
9909
+ throw new HTTPException12(422, {
9910
+ message: "the selected Connected Machine has not reported a workspace root; reconnect it with a current agent"
9911
+ });
9912
+ }
9913
+ return {
9914
+ sandboxId: sandbox.id,
9915
+ enrollmentId: sandbox.enrollmentId,
9916
+ sandboxOs: enrollment.os
9917
+ };
9918
+ }
9698
9919
  function scheduledTaskForGrant(task, grant) {
9699
9920
  if (hasPermission(grant.permissions, "sessions:control") || task.targetSessionId === null) {
9700
9921
  return task;
@@ -9966,6 +10187,13 @@ async function validatedScheduledTaskUpdate(input) {
9966
10187
  rigId: input.payload.rigId !== void 0 ? input.payload.rigId : input.existing.rigId,
9967
10188
  agentConfig: update.agentConfig ?? input.existing.agentConfig
9968
10189
  });
10190
+ await validateScheduledTaskMachineTarget({
10191
+ settings: input.settings,
10192
+ db: input.db,
10193
+ grant: input.grant,
10194
+ runMode: nextRunMode,
10195
+ agentConfig: update.agentConfig ?? input.existing.agentConfig
10196
+ });
9969
10197
  if (input.payload.targetSessionId !== void 0 || input.existing.runMode === "existing_session" || nextRunMode === "existing_session" || input.existing.runMode === "reusable_session" && nextRunMode !== "reusable_session") {
9970
10198
  update.targetSessionId = nextTargetSessionId;
9971
10199
  }
@@ -11008,6 +11236,7 @@ function denied(reason) {
11008
11236
 
11009
11237
  // src/domain/memory-slack-delivery.ts
11010
11238
  import {
11239
+ correctWorkspaceMemory,
11011
11240
  enqueueMemorySlackPublication,
11012
11241
  getCurrentMemorySlackPublicationConfiguration,
11013
11242
  getWorkspaceMemorySlackPublicationSnapshot,
@@ -11021,6 +11250,25 @@ async function saveWorkspaceMemoryWithSlackPublication(db, input, publication, e
11021
11250
  return { ...result, slackPublication };
11022
11251
  });
11023
11252
  }
11253
+ async function correctWorkspaceMemoryWithSlackPublication(db, input, publication, embedder) {
11254
+ return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
11255
+ const result = await correctWorkspaceMemory(scopedDb, input, embedder);
11256
+ if (!publication || result.action !== "superseded" || !result.replacement) {
11257
+ return { ...result, slackPublication: { decision: null, enqueue: null } };
11258
+ }
11259
+ const replacement = await requiredSnapshot(scopedDb, input.workspaceId, result.replacement.id);
11260
+ const slackPublication = await evaluateAndEnqueue(scopedDb, {
11261
+ accountId: input.accountId,
11262
+ workspaceId: input.workspaceId,
11263
+ snapshot: replacement,
11264
+ changeKind: "corrected",
11265
+ relatedMemoryId: result.memory.id,
11266
+ occurredAt: result.replacement.updatedAt,
11267
+ publication
11268
+ });
11269
+ return { ...result, slackPublication };
11270
+ });
11271
+ }
11024
11272
  async function publishSavedMemoryMutation(db, input, result, publication) {
11025
11273
  if (result.updated || result.deduped && !result.superseded) {
11026
11274
  return { decision: null, enqueue: null };
@@ -11405,7 +11653,13 @@ import {
11405
11653
  writeCompanyBrainGovernedProposal
11406
11654
  } from "@opengeni/db";
11407
11655
  import { createHash as createHash7 } from "crypto";
11408
- var DEFAULT_AUTOMATIC_LEARNING_DESTINATIONS = ["preference"];
11656
+ var DEFAULT_AUTOMATIC_LEARNING_DESTINATIONS = ["preference", "instruction_policy"];
11657
+ function dispatchBestEffortGovernedLearningNotification(notify) {
11658
+ try {
11659
+ void notify().catch(() => void 0);
11660
+ } catch {
11661
+ }
11662
+ }
11409
11663
  function createCompanyBrainGovernedWriteRouter(options) {
11410
11664
  const authority = options.authority ?? writeCompanyBrainGovernedProposal;
11411
11665
  return {
@@ -11566,15 +11820,14 @@ function createCompanyBrainLearningPolicyRouter(options) {
11566
11820
  learningFailure: classifyLearningFailure("activation", error)
11567
11821
  });
11568
11822
  }
11569
- try {
11570
- await notifyActivation({
11823
+ dispatchBestEffortGovernedLearningNotification(
11824
+ () => notifyActivation({
11571
11825
  db: options.db,
11572
11826
  receipt: activation,
11573
11827
  sessionId: attempt.sessionId,
11574
11828
  attemptId: attempt.attemptId
11575
- });
11576
- } catch {
11577
- }
11829
+ })
11830
+ );
11578
11831
  return CompanyBrainLearningPolicyRouteReceipt.parse({
11579
11832
  operationId: request.operationId,
11580
11833
  workspaceId: attempt.workspaceId,
@@ -12020,15 +12273,14 @@ function createRememberRouter(options) {
12020
12273
  }
12021
12274
  }
12022
12275
  if (!activation) throw asRememberFailure(lastFailure);
12023
- try {
12024
- await notifyActivation({
12276
+ dispatchBestEffortGovernedLearningNotification(
12277
+ () => notifyActivation({
12025
12278
  db: options.db,
12026
12279
  receipt: activation,
12027
12280
  sessionId: attempt.sessionId,
12028
12281
  attemptId: attempt.attemptId
12029
- });
12030
- } catch {
12031
- }
12282
+ })
12283
+ );
12032
12284
  return RememberConfirmReceipt.parse({
12033
12285
  status: "activated",
12034
12286
  operationId: request.operationId,
@@ -12848,7 +13100,9 @@ function resolveMemberSubjectId(userId) {
12848
13100
  function assertWorkspaceMemberRemovable(input) {
12849
13101
  const { members, subjectId, callerSubjectId } = input;
12850
13102
  if (subjectId === callerSubjectId) {
12851
- throw new HTTPException13(409, { message: "you cannot remove your own membership" });
13103
+ throw new HTTPException13(409, {
13104
+ message: "you cannot remove your own membership"
13105
+ });
12852
13106
  }
12853
13107
  const target = members.find((member) => member.subjectId === subjectId);
12854
13108
  if (!target) {
@@ -12865,9 +13119,28 @@ function assertWorkspaceMemberRemovable(input) {
12865
13119
  }
12866
13120
  }
12867
13121
  }
13122
+ function assertWorkspaceMemberUpdateAllowed(input) {
13123
+ const { members, subjectId, callerSubjectId, nextPermissions } = input;
13124
+ if (subjectId === callerSubjectId) {
13125
+ throw new HTTPException13(409, {
13126
+ message: "you cannot change your own workspace access"
13127
+ });
13128
+ }
13129
+ const target = members.find((member) => member.subjectId === subjectId);
13130
+ if (!target) {
13131
+ throw new HTTPException13(404, { message: "member not found" });
13132
+ }
13133
+ if (memberCanAdminister(target) && !memberCanAdminister({ permissions: nextPermissions }) && !members.some((member) => member.subjectId !== subjectId && memberCanAdminister(member))) {
13134
+ throw new HTTPException13(409, {
13135
+ message: "the workspace must keep at least one administrator"
13136
+ });
13137
+ }
13138
+ }
12868
13139
  function assertWorkspaceDeletable(input) {
12869
13140
  if (input.workspaceCountForAccount <= 1) {
12870
- throw new HTTPException13(409, { message: "cannot delete the account's only workspace" });
13141
+ throw new HTTPException13(409, {
13142
+ message: "cannot delete the account's only workspace"
13143
+ });
12871
13144
  }
12872
13145
  if (input.activeSessionCount > 0) {
12873
13146
  throw new HTTPException13(409, {
@@ -13125,9 +13398,9 @@ import {
13125
13398
  } from "@opengeni/contracts";
13126
13399
  import {
13127
13400
  getNewSessionDraftInTransaction,
13128
- getEnrollment as getEnrollment3,
13401
+ getEnrollment as getEnrollment4,
13129
13402
  getRig as getRig6,
13130
- getSandbox as getSandbox4,
13403
+ getSandbox as getSandbox5,
13131
13404
  getVariableSet as getVariableSet4,
13132
13405
  NewSessionDraftAccessError,
13133
13406
  newSessionDraftToolsProvided,
@@ -13220,8 +13493,8 @@ async function hydrateNewSessionDraft(deps, grant, workspaceId, row) {
13220
13493
  if (!rig?.activeVersion) delete options.rigId;
13221
13494
  }
13222
13495
  if (options.targetSandboxId) {
13223
- const sandbox = await getSandbox4(deps.db, workspaceId, options.targetSandboxId);
13224
- const enrollment = sandbox?.enrollmentId ? await getEnrollment3(deps.db, workspaceId, sandbox.enrollmentId) : null;
13496
+ const sandbox = await getSandbox5(deps.db, workspaceId, options.targetSandboxId);
13497
+ const enrollment = sandbox?.enrollmentId ? await getEnrollment4(deps.db, workspaceId, sandbox.enrollmentId) : null;
13225
13498
  if (!sandbox || sandbox.kind !== "selfhosted" || !enrollment || enrollment.status !== "active") {
13226
13499
  delete options.targetSandboxId;
13227
13500
  delete options.workingDir;
@@ -14118,6 +14391,7 @@ async function saveHumanComposerDraft(deps, context, input) {
14118
14391
  // src/application/user-resource-grants.ts
14119
14392
  import {
14120
14393
  issueSelfUserResourceGrant,
14394
+ issueSelfLocalConnectionUseGrant,
14121
14395
  listSelfUserResourceAuthorities,
14122
14396
  revokeSelfUserResourceGrant,
14123
14397
  sessionTenancyProductActivated as sessionTenancyProductActivated3,
@@ -14144,7 +14418,11 @@ async function requireOwnerProductGate(deps, authorization, workspaceId, permiss
14144
14418
  }
14145
14419
  }
14146
14420
  function requireOwnerAuthority(authorization, workspaceId, permissions) {
14147
- requireCanonicalManagedHuman(authorization, workspaceId);
14421
+ if (!authorization.canonicalLocalHumanSession) {
14422
+ requireCanonicalManagedHuman(authorization, workspaceId);
14423
+ } else if (!authorization.contextIntegrity || authorization.authenticatedSubjectId !== authorization.grant.subjectId || authorization.grant.workspaceId !== workspaceId) {
14424
+ throw new SessionTenancyManagedHumanRequiredError();
14425
+ }
14148
14426
  for (const permission of permissions) requirePermission(authorization.grant, permission);
14149
14427
  }
14150
14428
  async function listManagedHumanUserResourceAuthorities(deps, authorization, workspaceId, input) {
@@ -14161,6 +14439,23 @@ async function listManagedHumanUserResourceAuthorities(deps, authorization, work
14161
14439
  }
14162
14440
  async function issueManagedHumanUserResourceGrant(deps, authorization, workspaceId, authorityId, request, authorizationSurface = "core") {
14163
14441
  const modePermissions = request.mode === "session" ? ["sessions:control"] : ["sessions:create"];
14442
+ if (authorization.canonicalLocalHumanSession) {
14443
+ requireOwnerAuthority(authorization, workspaceId, [
14444
+ ...ISSUE_PERMISSIONS[request.resourceKind],
14445
+ "sessions:create"
14446
+ ]);
14447
+ if (request.resourceKind !== "connection" || request.mode !== "always") {
14448
+ throw new SessionTenancyManagedHumanRequiredError();
14449
+ }
14450
+ return await issueSelfLocalConnectionUseGrant(deps.db, {
14451
+ accountId: authorization.grant.accountId,
14452
+ workspaceId,
14453
+ subjectId: authorization.grant.subjectId,
14454
+ authorityId,
14455
+ context: request.context,
14456
+ workspaceSharedAcknowledged: request.workspaceSharedAcknowledged
14457
+ });
14458
+ }
14164
14459
  await requireOwnerProductGate(deps, authorization, workspaceId, [
14165
14460
  ...ISSUE_PERMISSIONS[request.resourceKind],
14166
14461
  ...modePermissions
@@ -14317,6 +14612,7 @@ export {
14317
14612
  acceptSessionUserMessage,
14318
14613
  acceptSessionUserMessageWithOutcome,
14319
14614
  accessGrantAuthorizationFromContext,
14615
+ accountScopedApiKeyWorkspaceAuthority,
14320
14616
  activateRigVersionForApi,
14321
14617
  apiIntegrationsMatchingDelegations,
14322
14618
  appendRigSetupCommand,
@@ -14344,6 +14640,7 @@ export {
14344
14640
  assertVideoGenerationTransition,
14345
14641
  assertWorkspaceDeletable,
14346
14642
  assertWorkspaceMemberRemovable,
14643
+ assertWorkspaceMemberUpdateAllowed,
14347
14644
  assertWorkspaceModelPolicyAllows,
14348
14645
  authorizedAtlassianConnectionsForGrant,
14349
14646
  authorizedSocialConnectionsForGrant,
@@ -14374,6 +14671,7 @@ export {
14374
14671
  controlHumanSessionWorkstreamWithOutcome,
14375
14672
  controlHumanWorkspace,
14376
14673
  conversationDeliveryNextAction,
14674
+ correctWorkspaceMemoryWithSlackPublication,
14377
14675
  createAndStartSession,
14378
14676
  createAndStartSessionWithOutcome,
14379
14677
  createCatalogItem,
@@ -14405,6 +14703,7 @@ export {
14405
14703
  directRetainedProcessMatchesBackend,
14406
14704
  disableCapability,
14407
14705
  discoverMcpRegistryCapabilities,
14706
+ dispatchBestEffortGovernedLearningNotification,
14408
14707
  editHumanQueuePrompt,
14409
14708
  editableArtifactActorKey,
14410
14709
  editableArtifactAuthorizationRevisionPortFromPostgres,
@@ -14442,6 +14741,7 @@ export {
14442
14741
  fikenConnectionMetadata,
14443
14742
  filenameForMimeType,
14444
14743
  forkManagedHumanSession,
14744
+ freezeAgentChildAutomaticTitleInCreatorContext,
14445
14745
  freezePersonalConnectionDelegations,
14446
14746
  getActorNewSessionDraft,
14447
14747
  getAutomationAdapter,
@@ -14465,6 +14765,7 @@ export {
14465
14765
  hasReservedOpenGeniSlackBotSessionMetadata,
14466
14766
  hashEditableArtifactCreateRequest,
14467
14767
  hashEditableArtifactImportRequest,
14768
+ initialAutomaticTitleForSessionStart,
14468
14769
  inlinePackSkillInstall,
14469
14770
  insightsSessionLabel,
14470
14771
  inspectEditableArtifactLiveWireEnvelope,
@@ -14486,6 +14787,8 @@ export {
14486
14787
  listRigVersionsForApi,
14487
14788
  listWorkspaceCapabilityPacks,
14488
14789
  loadRigDefaultVariableSetEnvironment,
14790
+ managedSessionGroupBackend,
14791
+ managedSessionGroupOs,
14489
14792
  manualScheduledTaskTriggerUsageKey,
14490
14793
  manualScheduledTaskTriggerWorkflowId,
14491
14794
  markManagedAuthRequestActorTransitionApplied,
@@ -14557,6 +14860,7 @@ export {
14557
14860
  requireAccessGrantAuthorization,
14558
14861
  requireAccountAdminAuthorizationStamp,
14559
14862
  requireAutomationAdapter,
14863
+ requireCanonicalLocalAccountAdministrator,
14560
14864
  requireCanonicalManagedHuman,
14561
14865
  requireEnvironmentEncryption,
14562
14866
  requireFreshAccessGrant,
@@ -14589,6 +14893,7 @@ export {
14589
14893
  resolveSkillImport,
14590
14894
  resolveWorkspaceLegacyRuntimePacks,
14591
14895
  restoreScheduledTask,
14896
+ retainedProcessBackgroundSettlement,
14592
14897
  revokeManagedHumanUserResourceGrant,
14593
14898
  rigActorForGrant,
14594
14899
  rigProviderImageBuildRequestId,
@@ -14653,6 +14958,7 @@ export {
14653
14958
  validateManagedAuthRequestActorLease,
14654
14959
  validateMcpCapabilityConnection,
14655
14960
  validateOpenGeniSlackBotConnectionSelection,
14961
+ validateScheduledTaskMachineTarget,
14656
14962
  validateScheduledTaskTarget,
14657
14963
  validateToolRefs,
14658
14964
  validateToolRefsForSessionPolicy,