@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.
- package/dist/access/index.d.ts +24 -0
- package/dist/canonical-human-identities.js +2 -2
- package/dist/{chunk-ZVZJTMSV.js → chunk-OF65T3PM.js} +2 -2
- package/dist/{chunk-YGOMUGYS.js → chunk-QO5GVFFO.js} +17 -8
- package/dist/{chunk-YGOMUGYS.js.map → chunk-QO5GVFFO.js.map} +1 -1
- package/dist/domain/company-brain-governed-writes.d.ts +15 -6
- package/dist/domain/company-profile-agent-admin.d.ts +3 -2
- package/dist/domain/environments.d.ts +1 -1
- package/dist/domain/memory-slack-delivery.d.ts +4 -1
- package/dist/domain/personal-connection-delegations.d.ts +1 -0
- package/dist/domain/pr-review.d.ts +1 -1
- package/dist/domain/scheduled-tasks.d.ts +12 -0
- package/dist/domain/sessions.d.ts +14 -1
- package/dist/domain/workspace-members.d.ts +8 -1
- package/dist/index.js +686 -380
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.d.ts +18 -0
- package/dist/managed-auth-session-sets.js +3 -1
- package/dist/sandbox/fleet.d.ts +6 -4
- package/dist/sandbox/routing.d.ts +7 -2
- package/dist/sandbox/runtime-settings.d.ts +17 -1
- package/package.json +10 -10
- package/src/access/index.ts +140 -5
- package/src/application/user-resource-grants.ts +31 -2
- package/src/billing/limits.ts +5 -2
- package/src/domain/company-brain-governed-writes.ts +29 -13
- package/src/domain/company-profile-agent-admin.ts +3 -2
- package/src/domain/environments.ts +6 -34
- package/src/domain/memory-slack-delivery.ts +30 -0
- package/src/domain/personal-connection-delegations.ts +40 -7
- package/src/domain/remember.ts +5 -6
- package/src/domain/scheduled-tasks.ts +94 -0
- package/src/domain/sessions.ts +117 -9
- package/src/domain/workspace-members.ts +34 -2
- package/src/managed-auth-session-sets.ts +38 -11
- package/src/sandbox/fleet.ts +20 -17
- package/src/sandbox/routing.ts +18 -4
- package/src/sandbox/runtime-settings.ts +32 -0
- /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-
|
|
129
|
-
import "./chunk-
|
|
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
|
-
|
|
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
|
-
|
|
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" :
|
|
918
|
+
const groupOperationAvailability = groupOnline ? "ready" : groupRecoveryUnavailable ? "unavailable" : groupRecovering ? "recovering" : "wakeable";
|
|
677
919
|
entries.push({
|
|
678
920
|
id: ctx.sessionGroupId,
|
|
679
|
-
kind:
|
|
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
|
|
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
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
|
1440
|
-
const
|
|
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
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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:
|
|
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
|
|
8874
|
-
...
|
|
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:
|
|
8912
|
-
sandboxId:
|
|
9070
|
+
seedTargetSandbox: effectiveSeedTarget ? {
|
|
9071
|
+
sandboxId: effectiveSeedTarget.sandboxId,
|
|
8913
9072
|
settings,
|
|
8914
|
-
workingDir:
|
|
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
|
-
|
|
11570
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12024
|
-
|
|
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
|
-
|
|
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, {
|
|
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, {
|
|
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
|
|
13401
|
+
getEnrollment as getEnrollment4,
|
|
13129
13402
|
getRig as getRig6,
|
|
13130
|
-
getSandbox as
|
|
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
|
|
13224
|
-
const enrollment = sandbox?.enrollmentId ? await
|
|
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
|
-
|
|
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,
|