UNPKG

@openai/agents-core

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

408 lines • 21.6 kB
"use strict"; 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