@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
408 lines • 21.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSerializedSandboxState = getSerializedSandboxState;
exports.sanitizeSerializedSandboxState = sanitizeSerializedSandboxState;
exports.toSessionStateEnvelope = toSessionStateEnvelope;
exports.mergeClientPreparedManifestAuthority = mergeClientPreparedManifestAuthority;
exports.clientPreparedManifestAuthoritiesMatch = clientPreparedManifestAuthoritiesMatch;
exports.assertSessionStateEnvelope = assertSessionStateEnvelope;
exports.deserializeSandboxSessionStateEntry = deserializeSandboxSessionStateEntry;
exports.resolveTrustedManifestForResume = resolveTrustedManifestForResume;
exports.assertTrustedManifestForDockerRunState = assertTrustedManifestForDockerRunState;
exports.getSerializedSessionEntryForAgent = getSerializedSessionEntryForAgent;
exports.serializedSessionEntryHasInContainerMounts = serializedSessionEntryHasInContainerMounts;
exports.serializedSessionEntryRequiresFreshMountAuthority = serializedSessionEntryRequiresFreshMountAuthority;
exports.getPreviousSerializedSessionsByAgent = getPreviousSerializedSessionsByAgent;
exports.hasPreservedOwnedSessions = hasPreservedOwnedSessions;
const errors_1 = require("../../errors.js");
const errors_2 = require("../errors.js");
const manifest_1 = require("../manifest.js");
const environment_1 = require("../shared/environment.js");
const stableJson_1 = require("../shared/stableJson.js");
const session_1 = require("../session.js");
const manifestPersistence_1 = require("../sandboxes/shared/manifestPersistence.js");
const mountSecurity_1 = require("../mountSecurity.js");
const sessionStateTrust_1 = require("../internal/sessionStateTrust.js");
function getSerializedSandboxState(runState) {
return runState?._sandbox;
}
function sanitizeSerializedSandboxState(state) {
const sessionsByAgent = Object.fromEntries(Object.entries(state.sessionsByAgent).map(([agentKey, entry]) => [
agentKey,
{
...entry,
sessionState: sanitizeSerializedSessionEnvelope(entry.sessionState),
},
]));
return {
...state,
sessionState: sanitizeSerializedSessionEnvelope(state.sessionState),
sessionsByAgent,
};
}
function sanitizeSerializedSessionEnvelope(envelope) {
const hasNonResumableMountAuthority = (0, mountSecurity_1.persistedManifestRecordHasNonResumableMountAuthority)(envelope.manifest);
const existingMetadata = (0, manifestPersistence_1.deserializeMountCredentialRedactionMetadata)(envelope.providerState);
const existingPaths = existingMetadata[mountSecurity_1.REDACTED_MOUNT_CREDENTIAL_PATHS_KEY] ?? [];
const { record, credentialPaths } = (0, mountSecurity_1.sanitizePersistedManifestRecord)(envelope.manifest);
const persistedManifest = (0, manifestPersistence_1.deserializeManifest)(record);
const redactedPaths = [...new Set([...existingPaths, ...credentialPaths])];
(0, mountSecurity_1.validateMountCredentialRedactionPaths)(persistedManifest, redactedPaths);
const serializedSanitizedManifest = (0, manifestPersistence_1.serializeManifestRecord)((0, mountSecurity_1.sanitizeMountCredentialEnvironmentForPersistence)({
manifest: persistedManifest,
}).manifest);
const persistentManifest = {
...record,
entries: serializedSanitizedManifest.entries,
environment: serializedSanitizedManifest.environment,
};
const providerState = providerStateWithoutSdkEnvelopeFields(envelope.providerState, envelope.backendId);
const persistedEnvironment = isStringRecord(providerState.environment)
? providerState.environment
: undefined;
delete providerState.environment;
if (persistedEnvironment) {
providerState.environment =
(0, mountSecurity_1.sanitizeMountCredentialEnvironmentForPersistence)({
manifest: persistedManifest,
environment: persistedEnvironment,
}).environment;
}
if (redactedPaths.length > 0) {
providerState[mountSecurity_1.REDACTED_MOUNT_CREDENTIAL_PATHS_KEY] = redactedPaths;
}
else {
delete providerState[mountSecurity_1.REDACTED_MOUNT_CREDENTIAL_PATHS_KEY];
}
if (hasNonResumableMountAuthority) {
providerState[mountSecurity_1.NON_RESUMABLE_MOUNT_AUTHORITY_KEY] = true;
}
return {
...envelope,
manifest: persistentManifest,
providerState,
};
}
function toSessionStateEnvelope(backendId, state, providerState, options = {}) {
const stateForEnvelope = options.environmentPreparation === 'client' && options.trustedManifest
? {
...state,
manifest: mergeClientPreparedManifestAuthority(state.manifest, options.trustedManifest),
}
: state;
(0, mountSecurity_1.validateMountEnvironmentCredentialBoundaries)(stateForEnvelope.manifest, options.environmentPreparation === 'client'
? {}
: (stateForEnvelope.environment ?? {}), options.environmentPreparation === 'client'
? { preserveDeferredValuePresence: true }
: {});
const persistentManifest = (0, manifestPersistence_1.serializeManifest)((0, mountSecurity_1.sanitizeMountCredentialEnvironmentForPersistence)(stateForEnvelope).manifest);
return {
version: session_1.SANDBOX_SESSION_STATE_VERSION,
backendId,
...(options.environmentPreparation
? { environmentPreparation: options.environmentPreparation }
: {}),
manifest: (0, manifestPersistence_1.serializeManifestRecord)(persistentManifest),
...(state.snapshot !== undefined ? { snapshot: state.snapshot } : {}),
...(state.snapshotFingerprint !== undefined
? { snapshotFingerprint: state.snapshotFingerprint }
: {}),
...(state.snapshotFingerprintVersion !== undefined
? { snapshotFingerprintVersion: state.snapshotFingerprintVersion }
: {}),
workspaceReady: state.workspaceReady ?? true,
...(state.exposedPorts
? { exposedPorts: structuredClone(state.exposedPorts) }
: {}),
// Keep provider-owned fields separate from the SDK envelope so version and backend
// checks can run before deserializing provider-specific state.
providerState: {
...providerStateWithoutSdkEnvelopeFields(providerState, backendId),
...(0, manifestPersistence_1.serializeHostPathGrantRedactionMetadata)(stateForEnvelope),
...(0, manifestPersistence_1.serializeMountCredentialRedactionMetadata)(stateForEnvelope),
},
};
}
function mergeClientPreparedManifestAuthority(providerManifest, trustedManifest) {
const merged = new manifest_1.Manifest({
version: providerManifest.version,
root: providerManifest.root,
entries: structuredClone(providerManifest.entries),
environment: cloneManifestEnvironment(trustedManifest.environment),
users: structuredClone(providerManifest.users),
groups: structuredClone(providerManifest.groups),
extraPathGrants: structuredClone(trustedManifest.extraPathGrants),
remoteMountCommandAllowlist: [
...trustedManifest.remoteMountCommandAllowlist,
],
});
(0, manifest_1.replaceManifestMountCredentialExposurePolicy)(merged, trustedManifest);
return merged;
}
function clientPreparedManifestAuthoritiesMatch(providerManifest, leftTrustedManifest, rightTrustedManifest) {
const left = mergeClientPreparedManifestAuthority(providerManifest, leftTrustedManifest);
const right = mergeClientPreparedManifestAuthority(providerManifest, rightTrustedManifest);
const environmentKeys = new Set([
...Object.keys(left.environment),
...Object.keys(right.environment),
]);
for (const key of environmentKeys) {
if (left.environment[key]?.resolver !== right.environment[key]?.resolver) {
return false;
}
}
const mountCredentialExposure = (manifest) => manifest.mountTargets().map(({ mountPath }) => ({
mountPath,
mountScoped: (0, manifest_1.manifestAcknowledgesInContainerMountCredentialExposure)(manifest, mountPath, 'mount_scoped'),
broad: (0, manifest_1.manifestAcknowledgesInContainerMountCredentialExposure)(manifest, mountPath, 'broad'),
}));
return ((0, stableJson_1.stableJsonStringify)({
environment: (0, environment_1.serializeManifestEnvironment)(left),
extraPathGrants: left.extraPathGrants,
remoteMountCommandAllowlist: left.remoteMountCommandAllowlist,
mountCredentialExposure: mountCredentialExposure(left),
}) ===
(0, stableJson_1.stableJsonStringify)({
environment: (0, environment_1.serializeManifestEnvironment)(right),
extraPathGrants: right.extraPathGrants,
remoteMountCommandAllowlist: right.remoteMountCommandAllowlist,
mountCredentialExposure: mountCredentialExposure(right),
}));
}
function providerStateWithoutSdkEnvelopeFields(providerState, backendId) {
const sanitized = { ...providerState };
delete sanitized.manifest;
if (backendId === 'docker') {
delete sanitized.sessionIdentity;
}
delete sanitized.snapshot;
delete sanitized.snapshotFingerprint;
delete sanitized.snapshotFingerprintVersion;
delete sanitized.workspaceReady;
delete sanitized.exposedPorts;
return sanitized;
}
function assertSessionStateEnvelope(client, envelope) {
if (!session_1.SUPPORTED_SANDBOX_SESSION_STATE_VERSIONS.includes(envelope.version)) {
throw new errors_1.UserError(`Sandbox session state version ${envelope.version} is not supported. Please use version ${session_1.SANDBOX_SESSION_STATE_VERSION}.`);
}
if (envelope.backendId !== client.backendId) {
throw new errors_1.UserError('RunState sandbox session backend does not match the configured sandbox client.');
}
if (envelope.environmentPreparation !== undefined &&
envelope.environmentPreparation !== 'client') {
throw new errors_1.UserError('RunState sandbox session environment preparation mode is not supported.');
}
const clientPreparesEnvironment = client.environmentPreparation === 'client';
const envelopeUsesClientPreparation = envelope.environmentPreparation === 'client';
if (clientPreparesEnvironment !== envelopeUsesClientPreparation) {
throw new errors_1.UserError('RunState sandbox session environment preparation does not match the configured sandbox client.');
}
if (envelopeUsesClientPreparation &&
envelope.version !== session_1.SANDBOX_SESSION_STATE_VERSION) {
throw new errors_1.UserError('Client-owned environment preparation requires the current sandbox session state version.');
}
}
async function deserializeSandboxSessionStateEntry(client, serializedEntry, trustedManifest, trustedConfig = {}, options = {}) {
if (!serializedEntry) {
return undefined;
}
if (serializedEntry.backendId !== client.backendId) {
throw new errors_1.UserError('RunState sandbox backend does not match the configured sandbox client.');
}
const resolvedTrustedManifest = options.trustedManifestAlreadyResolved
? trustedManifest
: resolveTrustedManifestForResume(client, trustedManifest, trustedConfig.clientOptions);
assertTrustedManifestForDockerRunState(client, resolvedTrustedManifest);
if (!client.deserializeSessionState) {
throw new errors_1.UserError('Sandbox client must implement deserializeSessionState() to resume RunState sandbox state.');
}
const envelope = serializedEntry.sessionState;
assertSessionStateEnvelope(client, envelope);
const clientPreparesEnvironment = client.environmentPreparation === 'client';
if (clientPreparesEnvironment && !resolvedTrustedManifest) {
throw new errors_1.UserError('Client-owned environment preparation requires a current trusted manifest before resuming sandbox session state.');
}
assertTrustedEnvironmentForPersistedReferences(envelope, resolvedTrustedManifest, { requireMatchingReferences: !clientPreparesEnvironment });
if ((0, mountSecurity_1.manifestHasInContainerMounts)((0, manifestPersistence_1.deserializeManifest)(envelope.manifest))) {
throw new errors_2.SandboxMountError('Sandbox session state with in-container mounts cannot be resumed safely. Create a fresh sandbox so mount helpers are recreated from current trusted configuration.', undefined, 'mount_config_invalid');
}
const prevalidatedPersistedState = (0, manifestPersistence_1.rebindPersistedMountCredentials)({
manifest: (0, manifestPersistence_1.deserializeManifest)(envelope.manifest),
...(0, manifestPersistence_1.deserializeMountCredentialRedactionMetadata)(envelope.providerState),
}, resolvedTrustedManifest);
(0, manifestPersistence_1.assertMountCredentialsRebound)(prevalidatedPersistedState);
client.validateSessionStateForResume?.({
source: 'runState',
state: providerStateWithSdkEnvelopeFields(envelope, prevalidatedPersistedState.manifest, clientPreparesEnvironment ? undefined : resolvedTrustedManifest),
}, {
clientOptions: trustedConfig.clientOptions,
...(clientPreparesEnvironment
? { trustedManifest: resolvedTrustedManifest }
: {}),
});
const materializedTrustedManifest = resolvedTrustedManifest
? clientPreparesEnvironment
? resolvedTrustedManifest
: await (0, mountSecurity_1.resolveAndValidateMountEnvironment)(resolvedTrustedManifest)
: undefined;
const state = await client.deserializeSessionState(providerStateWithSdkEnvelopeFields(envelope, prevalidatedPersistedState.manifest, clientPreparesEnvironment ? undefined : materializedTrustedManifest));
if (client.backendId === 'docker' && !clientPreparesEnvironment) {
delete state.sessionIdentity;
}
const hostPathMetadata = (0, manifestPersistence_1.deserializeHostPathGrantRedactionMetadata)(envelope.providerState);
const mountCredentialMetadata = (0, manifestPersistence_1.deserializeMountCredentialRedactionMetadata)(envelope.providerState);
const credentialReboundState = clientPreparesEnvironment
? {
...prevalidatedPersistedState,
...hostPathMetadata,
}
: (0, manifestPersistence_1.rebindPersistedMountCredentials)({
...state,
...hostPathMetadata,
...mountCredentialMetadata,
}, materializedTrustedManifest);
const reboundState = (0, manifestPersistence_1.rebindPersistedPathGrants)(credentialReboundState, materializedTrustedManifest, {
replaceWithTrustedManifest: client.backendId === 'docker' &&
materializedTrustedManifest !== undefined,
});
(0, manifestPersistence_1.assertMountCredentialsRebound)(reboundState);
(0, mountSecurity_1.validateMountCredentialBoundaries)(reboundState.manifest);
(0, mountSecurity_1.validateMountEnvironmentCredentialBoundaries)(clientPreparesEnvironment
? resolvedTrustedManifest
: reboundState.manifest, clientPreparesEnvironment ? {} : (reboundState.environment ?? {}), clientPreparesEnvironment ? { preserveDeferredValuePresence: true } : {});
(0, manifestPersistence_1.assertHostPathGrantsRebound)(reboundState);
if (clientPreparesEnvironment) {
return (0, sessionStateTrust_1.markRunStateSessionState)(state, trustedConfig);
}
return (0, sessionStateTrust_1.markRunStateSessionState)(reboundState, trustedConfig);
}
function resolveTrustedManifestForResume(client, trustedManifest, clientOptions) {
if (!trustedManifest) {
return undefined;
}
const resolved = client.resolveTrustedManifestForResume
? client.resolveTrustedManifestForResume(trustedManifest, clientOptions)
: trustedManifest;
(0, mountSecurity_1.validateMountCredentialBoundaries)(resolved);
return resolved;
}
function assertTrustedManifestForDockerRunState(client, trustedManifest) {
if (client.backendId === 'docker' && trustedManifest === undefined) {
throw new errors_1.UserError('Docker RunState resume requires a current trusted manifest for the sandbox agent.');
}
}
function providerStateWithSdkEnvelopeFields(envelope, persistedManifest, trustedManifest) {
const providerState = providerStateWithoutSdkEnvelopeFields(envelope.providerState, envelope.backendId);
const persistedEnvironment = isStringRecord(providerState.environment)
? providerState.environment
: undefined;
const sanitizedEnvironment = persistedEnvironment
? (0, mountSecurity_1.sanitizeMountCredentialEnvironmentForPersistence)({
manifest: trustedManifest ?? persistedManifest,
environment: persistedEnvironment,
}).environment
: undefined;
return {
...providerState,
...(sanitizedEnvironment ? { environment: sanitizedEnvironment } : {}),
manifest: trustedManifest
? {
...(0, manifestPersistence_1.serializeManifestRecord)(persistedManifest),
environment: cloneManifestEnvironment(trustedManifest.environment),
}
: (0, manifestPersistence_1.serializeManifestRecord)(persistedManifest),
...(envelope.snapshot !== undefined ? { snapshot: envelope.snapshot } : {}),
...(envelope.snapshotFingerprint !== undefined
? { snapshotFingerprint: envelope.snapshotFingerprint }
: {}),
...(envelope.snapshotFingerprintVersion !== undefined
? {
snapshotFingerprintVersion: envelope.snapshotFingerprintVersion,
}
: {}),
workspaceReady: envelope.workspaceReady,
...(envelope.exposedPorts
? { exposedPorts: structuredClone(envelope.exposedPorts) }
: {}),
};
}
function isStringRecord(value) {
return (typeof value === 'object' &&
value !== null &&
!Array.isArray(value) &&
Object.values(value).every((entry) => typeof entry === 'string'));
}
function assertTrustedEnvironmentForPersistedReferences(envelope, trustedManifest, options = {}) {
const persistedEnvironment = envelope.manifest.environment;
if (!persistedEnvironment ||
typeof persistedEnvironment !== 'object' ||
Array.isArray(persistedEnvironment)) {
return;
}
const referenceKeys = Object.entries(persistedEnvironment)
.filter(([, value]) => (0, manifest_1.isSerializedEnvValueReference)(value))
.map(([key]) => key);
if (referenceKeys.length === 0) {
return;
}
if (!trustedManifest) {
throw new errors_1.UserError('RunState sandbox session state with environment value references requires a current trusted manifest for the sandbox agent.');
}
if (options.requireMatchingReferences === false) {
return;
}
const missingReferenceKeys = referenceKeys.filter((key) => !trustedManifest.environment[key] ||
!(0, manifest_1.isEnvValueReference)(trustedManifest.environment[key]));
if (missingReferenceKeys.length > 0) {
throw new errors_1.UserError(`RunState sandbox session state contains environment value references that are not present in the current trusted manifest: ${missingReferenceKeys.join(', ')}.`);
}
}
function cloneManifestEnvironment(environment) {
return Object.fromEntries(Object.entries(environment).map(([key, value]) => [key, value.init()]));
}
function getSerializedSessionEntryForAgent(sandboxState, currentAgentKey) {
if (!sandboxState) {
return undefined;
}
return (sandboxState.sessionsByAgent?.[currentAgentKey] ??
(sandboxState.currentAgentKey === currentAgentKey
? {
backendId: sandboxState.backendId,
currentAgentKey: sandboxState.currentAgentKey,
currentAgentName: sandboxState.currentAgentName,
sessionState: sandboxState.sessionState,
}
: undefined));
}
function serializedSessionEntryHasInContainerMounts(entry) {
return entry
? (0, mountSecurity_1.manifestHasInContainerMounts)((0, manifestPersistence_1.deserializeManifest)(entry.sessionState.manifest))
: false;
}
function serializedSessionEntryRequiresFreshMountAuthority(entry) {
return (serializedSessionEntryHasInContainerMounts(entry) ||
entry?.sessionState.providerState[mountSecurity_1.NON_RESUMABLE_MOUNT_AUTHORITY_KEY] ===
true);
}
function getPreviousSerializedSessionsByAgent(sandboxState, client) {
if (!sandboxState || sandboxState.backendId !== client.backendId) {
return {};
}
const entries = {
...(sandboxState.sessionsByAgent ?? {}),
};
if (sandboxState.currentAgentKey && !entries[sandboxState.currentAgentKey]) {
entries[sandboxState.currentAgentKey] = {
backendId: sandboxState.backendId,
currentAgentKey: sandboxState.currentAgentKey,
currentAgentName: sandboxState.currentAgentName,
sessionState: sandboxState.sessionState,
};
}
return Object.fromEntries(Object.entries(entries).filter(([, entry]) => entry.backendId === client.backendId));
}
function hasPreservedOwnedSessions(sandboxState) {
return Object.values(sandboxState?.sessionsByAgent ?? {}).some((entry) => entry.preservedOwnedSession);
}
//# sourceMappingURL=sessionState.js.map