@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
240 lines • 12.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyManifestToProvidedSession = applyManifestToProvidedSession;
exports.withExclusiveProvidedSessionManifestMutation = withExclusiveProvidedSessionManifestMutation;
const errors_1 = require("../../errors.js");
const entries_1 = require("../entries/index.js");
const manifest_1 = require("../manifest.js");
const compare_1 = require("../shared/compare.js");
const environment_1 = require("../shared/environment.js");
const manifestCollections_1 = require("../shared/manifestCollections.js");
const remoteMountCommandAllowlist_1 = require("../shared/remoteMountCommandAllowlist.js");
const manifestPersistence_1 = require("../sandboxes/shared/manifestPersistence.js");
const mountSecurity_1 = require("../mountSecurity.js");
const providedSessionManifestMutationTails = new WeakMap();
async function applyManifestToProvidedSession(session, manifest, runAs) {
return await withExclusiveProvidedSessionManifestMutation(session.state, async () => applyManifestToProvidedSessionExclusive(session, manifest, runAs));
}
async function applyManifestToProvidedSessionExclusive(session, manifest, runAs) {
(0, mountSecurity_1.assertSandboxSessionStateUsable)(session.state);
const currentManifest = session.state.manifest;
const currentManifestSnapshot = (0, manifest_1.cloneManifest)(currentManifest);
const currentEnvironment = {
...(0, environment_1.materializeStaticEnvironment)(currentManifest),
...(session.state.environment ?? {}),
};
(0, mountSecurity_1.validateMountCredentialBoundaries)(currentManifest);
(0, mountSecurity_1.validateMountEnvironmentCredentialBoundaries)(currentManifest, currentEnvironment);
// A provided session may already have long-lived processes, mounts, or provider
// state, so only additive file materialization is allowed after validation.
validateProvidedSessionManifestUpdate(currentManifest, manifest);
const manifestDelta = buildProvidedSessionManifestDelta(currentManifest, manifest);
const nextManifest = (0, manifestPersistence_1.mergeManifestDelta)(currentManifest, manifestDelta);
if (currentManifest.mountTargets().length > 0) {
const nextTrustedManifest = mergeProvidedMountAuthority(nextManifest, currentManifest, manifest);
(0, mountSecurity_1.assertLiveMountCredentialAuthorityMatches)(currentManifest, nextTrustedManifest);
const trustedEnvironment = await nextTrustedManifest.resolveEnvironment();
(0, mountSecurity_1.validateMountEnvironmentCredentialBoundaries)(nextTrustedManifest, trustedEnvironment);
(0, mountSecurity_1.assertLiveMountEnvironmentAuthorityMatches)(currentManifest, nextTrustedManifest, trustedEnvironment);
}
if (isManifestDeltaEmpty(manifestDelta)) {
return;
}
const nextEnvironment = {
...(0, environment_1.materializeStaticEnvironment)(nextManifest),
...(session.state.environment ?? {}),
};
(0, mountSecurity_1.validateMountCredentialBoundaries)(nextManifest);
(0, mountSecurity_1.validateMountEnvironmentCredentialBoundaries)(nextManifest, nextEnvironment);
if (session.applyManifest) {
await session.applyManifest(manifestDelta, runAs);
}
else {
if (Object.keys(manifestDelta.entries).length > 0) {
if (!session.materializeEntry) {
throw new errors_1.UserError('Provided sandbox sessions must support applyManifest() or materializeEntry() when the agent declares manifest entries.');
}
for (const [path, entry] of Object.entries(manifestDelta.entries)) {
await session.materializeEntry({
path,
entry,
runAs,
});
}
}
}
// The helper owns the logical state commit after provider effects succeed.
// Merge through the latest provider-visible state so released side-effect-only
// implementations and concurrent additive provider updates remain supported,
// while entries present before the callback cannot be dropped by a stale or
// destructive provider commit.
const committedManifest = (0, manifestPersistence_1.mergeManifestDelta)((0, manifestPersistence_1.mergeManifestDelta)(currentManifestSnapshot, session.state.manifest), manifestDelta);
(0, mountSecurity_1.recordLiveMountCredentialAuthority)(committedManifest, session.state.manifest);
session.state.manifest = committedManifest;
}
function mergeProvidedMountAuthority(nextManifest, currentManifest, targetManifest) {
const currentMountPaths = new Set(currentManifest.mountTargets().map(({ logicalPath }) => logicalPath));
let trustedManifest = nextManifest;
for (const { logicalPath, entry } of targetManifest.iterEntries()) {
if ((0, entries_1.isMount)(entry) && currentMountPaths.has(logicalPath)) {
trustedManifest = (0, manifestPersistence_1.mergeManifestEntryDelta)(trustedManifest, logicalPath, entry);
}
}
(0, manifest_1.copyManifestMountCredentialExposurePolicy)(trustedManifest, nextManifest, targetManifest);
return trustedManifest;
}
async function withExclusiveProvidedSessionManifestMutation(state, operation) {
const previous = providedSessionManifestMutationTails.get(state) ?? Promise.resolve();
let release;
const turn = new Promise((resolve) => {
release = resolve;
});
const tail = previous.catch(() => { }).then(() => turn);
providedSessionManifestMutationTails.set(state, tail);
await previous.catch(() => { });
try {
return await operation();
}
finally {
release();
if (providedSessionManifestMutationTails.get(state) === tail) {
providedSessionManifestMutationTails.delete(state);
}
}
}
function validateProvidedSessionManifestUpdate(current, target) {
if (current.root !== target.root) {
throw new errors_1.UserError('Live sandbox sessions cannot change manifest.root. Create or resume a session with the desired root instead.');
}
validateNoEnvironmentDelta(current, target);
validateNoAddedOrChangedNamedObjects('users', current.users, target.users);
validateNoAddedOrChangedNamedObjects('groups', current.groups, target.groups);
validateProvidedEntryUpdates(current, target);
}
function validateNoEnvironmentDelta(current, target) {
const currentEnvironment = (0, environment_1.serializeManifestEnvironment)(current);
const targetEnvironment = (0, environment_1.serializeManifestEnvironment)(target);
const hasDelta = Object.entries(targetEnvironment).some(([key, value]) => !(0, compare_1.jsonEqual)(currentEnvironment[key], value));
if (hasDelta) {
throw new errors_1.UserError('Live sandbox sessions cannot change manifest environment variables. Create or resume a session with the desired environment instead.');
}
}
function validateNoAddedOrChangedNamedObjects(label, current, target) {
if ((0, manifestCollections_1.addedOrChangedNamedObjects)(current, target).length > 0) {
throw new errors_1.UserError(`Live sandbox sessions cannot change manifest ${label}. Create or resume a session with the desired ${label} instead.`);
}
}
function validateProvidedEntryUpdates(current, target) {
const activeMountAuthorityMatches = (0, mountSecurity_1.liveMountCredentialAuthorityMatches)(current, target);
const currentEntriesByPath = new Map([...current.iterEntries()].map(({ logicalPath, entry }) => [
logicalPath,
entry,
]));
for (const { logicalPath, entry } of target.iterEntries()) {
const existingEntry = currentEntriesByPath.get(logicalPath);
if (!existingEntry) {
if ((0, entries_1.isMount)(entry)) {
throw new errors_1.UserError(`Live sandbox sessions cannot add mount entries: ${logicalPath || '.'}`);
}
continue;
}
if (existingEntry.type !== entry.type) {
throw new errors_1.UserError(`Live sandbox sessions cannot replace manifest entry types: ${logicalPath || '.'}`);
}
if ((0, entries_1.isMount)(existingEntry) || (0, entries_1.isMount)(entry)) {
if (!(0, compare_1.jsonEqual)(existingEntry, entry) && !activeMountAuthorityMatches) {
throw new errors_1.UserError(`Live sandbox sessions cannot change mount entries: ${logicalPath || '.'}`);
}
continue;
}
if ((0, entries_1.isDir)(existingEntry) && (0, entries_1.isDir)(entry)) {
if (!(0, compare_1.jsonEqual)(manifestEntryWithoutDirChildren(existingEntry), manifestEntryWithoutDirChildren(entry))) {
throw new errors_1.UserError(`Live sandbox sessions cannot change manifest entries: ${logicalPath || '.'}`);
}
continue;
}
if (!(0, compare_1.jsonEqual)(existingEntry, entry)) {
throw new errors_1.UserError(`Live sandbox sessions cannot change manifest entries: ${logicalPath || '.'}`);
}
}
}
function manifestEntryWithoutDirChildren(entry) {
if (!(0, entries_1.isDir)(entry)) {
return entry;
}
const { children: _children, ...rest } = entry;
return rest;
}
function buildProvidedSessionManifestDelta(current, target) {
const currentEntriesByPath = new Map([...current.iterEntries()].map(({ logicalPath, entry }) => [
logicalPath,
entry,
]));
const currentExistingPaths = new Set();
for (const path of currentEntriesByPath.keys()) {
addManifestPathAndParents(currentExistingPaths, path);
}
const entries = {};
for (const [path, entry] of Object.entries(target.entries)) {
visitProvidedSessionTargetEntry(entries, currentEntriesByPath, currentExistingPaths, (0, manifest_1.normalizeRelativePath)(path), entry);
}
const users = (0, manifestCollections_1.addedOrChangedNamedObjects)(current.users, target.users);
const groups = (0, manifestCollections_1.addedOrChangedNamedObjects)(current.groups, target.groups);
const extraPathGrants = (0, manifestCollections_1.addedOrChangedPathKeyedObjects)(current.extraPathGrants, target.extraPathGrants);
const hasRemoteMountCommandAllowlistDelta = !(0, remoteMountCommandAllowlist_1.isDefaultRemoteMountCommandAllowlist)(target.remoteMountCommandAllowlist) &&
!(0, compare_1.arraysEqual)(current.remoteMountCommandAllowlist, target.remoteMountCommandAllowlist);
// Avoid sending the default allowlist as a delta because doing so would overwrite a
// provider's existing customized allowlist on an already-running session.
return new manifest_1.Manifest({
root: current.root,
entries,
users,
groups,
extraPathGrants,
...(hasRemoteMountCommandAllowlistDelta
? {
remoteMountCommandAllowlist: [...target.remoteMountCommandAllowlist],
}
: {}),
});
}
function visitProvidedSessionTargetEntry(entries, currentEntriesByPath, currentExistingPaths, logicalPath, entry) {
if (!currentExistingPaths.has(logicalPath)) {
entries[logicalPath] = structuredClone(entry);
return;
}
// Existing directories are containers for additive children; their own metadata was
// already checked by validateProvidedEntryUpdates().
if (!(0, entries_1.isDir)(entry) || !entry.children) {
return;
}
const currentEntry = currentEntriesByPath.get(logicalPath);
if (currentEntry && !(0, entries_1.isDir)(currentEntry)) {
return;
}
for (const [childPath, childEntry] of Object.entries(entry.children)) {
const normalizedChildPath = (0, manifest_1.normalizeRelativePath)(childPath);
const childLogicalPath = logicalPath
? `${logicalPath}/${normalizedChildPath}`
: normalizedChildPath;
visitProvidedSessionTargetEntry(entries, currentEntriesByPath, currentExistingPaths, childLogicalPath, childEntry);
}
}
function addManifestPathAndParents(paths, logicalPath) {
if (!logicalPath) {
return;
}
const segments = logicalPath.split('/');
for (let index = 1; index <= segments.length; index += 1) {
paths.add(segments.slice(0, index).join('/'));
}
}
function isManifestDeltaEmpty(manifest) {
return (Object.keys(manifest.entries).length === 0 &&
Object.keys(manifest.environment).length === 0 &&
manifest.users.length === 0 &&
manifest.groups.length === 0 &&
manifest.extraPathGrants.length === 0 &&
(0, remoteMountCommandAllowlist_1.isDefaultRemoteMountCommandAllowlist)(manifest.remoteMountCommandAllowlist));
}
//# sourceMappingURL=providedSessionManifest.js.map