UNPKG

@openai/agents-core

Version:

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

307 lines • 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.serializeMountCredentialRedactionMetadata = exports.rebindPersistedMountCredentials = exports.deserializeMountCredentialRedactionMetadata = exports.assertMountCredentialsRebound = void 0; exports.serializeManifest = serializeManifest; exports.serializeHostPathGrantRedactionMetadata = serializeHostPathGrantRedactionMetadata; exports.deserializeHostPathGrantRedactionMetadata = deserializeHostPathGrantRedactionMetadata; exports.rebindPersistedPathGrants = rebindPersistedPathGrants; exports.assertHostPathGrantsRebound = assertHostPathGrantsRebound; exports.serializeManifestRecord = serializeManifestRecord; exports.deserializeManifest = deserializeManifest; exports.sanitizeEnvironmentForPersistence = sanitizeEnvironmentForPersistence; exports.serializeEnvironmentForPersistence = serializeEnvironmentForPersistence; exports.mergeManifestDelta = mergeManifestDelta; exports.mergeManifestEntryDelta = mergeManifestEntryDelta; const entries_1 = require("../../entries/index.js"); const errors_1 = require("../../../errors.js"); const manifest_1 = require("../../manifest.js"); const manifestCollections_1 = require("../../shared/manifestCollections.js"); const remoteMountCommandAllowlist_1 = require("../../shared/remoteMountCommandAllowlist.js"); const environment_1 = require("../../shared/environment.js"); const base64_1 = require("../../../utils/base64.js"); const mountSecurity_1 = require("../../mountSecurity.js"); var mountSecurity_2 = require("../../mountSecurity.js"); Object.defineProperty(exports, "assertMountCredentialsRebound", { enumerable: true, get: function () { return mountSecurity_2.assertMountCredentialsRebound; } }); Object.defineProperty(exports, "deserializeMountCredentialRedactionMetadata", { enumerable: true, get: function () { return mountSecurity_2.deserializeMountCredentialRedactionMetadata; } }); Object.defineProperty(exports, "rebindPersistedMountCredentials", { enumerable: true, get: function () { return mountSecurity_2.rebindPersistedMountCredentials; } }); Object.defineProperty(exports, "serializeMountCredentialRedactionMetadata", { enumerable: true, get: function () { return mountSecurity_2.serializeMountCredentialRedactionMetadata; } }); const redactedHostPathGrantPathsKey = '__openaiAgentsRedactedHostPathGrantPaths'; function serializeManifest(manifest) { return deserializeManifest(serializeManifestRecord(manifest)); } function serializeHostPathGrantRedactionMetadata(state) { const paths = new Set(readRedactedHostPathGrantPaths(state)); for (const grant of state.manifest.extraPathGrants) { if (grant.hostPath !== undefined) { paths.add(grant.path); } } return paths.size > 0 ? { [redactedHostPathGrantPathsKey]: [...paths] } : {}; } function deserializeHostPathGrantRedactionMetadata(state) { const paths = readRedactedHostPathGrantPaths(state); return paths.length > 0 ? { [redactedHostPathGrantPathsKey]: paths } : {}; } function rebindPersistedPathGrants(state, trustedManifest, options = {}) { // Native host paths are runtime authority. Only the current manifest may // reintroduce them after persisted session state has been deserialized. const reboundState = { ...state }; if (options.replaceWithTrustedManifest && trustedManifest) { reboundState.manifest = (0, manifest_1.cloneManifest)(trustedManifest); (0, mountSecurity_1.copyTrustedMountCredentialRebindProvenance)(reboundState.manifest, state.manifest); delete reboundState[redactedHostPathGrantPathsKey]; return reboundState; } const trustedGrantsByPath = new Map((trustedManifest?.extraPathGrants ?? []).map((grant) => [ grant.path, grant, ])); let extraPathGrants; if (options.replaceWithTrustedGrantSet && trustedManifest) { extraPathGrants = trustedManifest.extraPathGrants.map((grant) => structuredClone(grant)); } else { const unmatchedGrantPaths = state.manifest.extraPathGrants .filter((grant) => !trustedGrantsByPath.has(grant.path)) .map((grant) => grant.path); if (unmatchedGrantPaths.length > 0) { throw new errors_1.UserError(`Sandbox session state contains path grants that are not present in the current trusted manifest: ${unmatchedGrantPaths.join(', ')}. Define each grant in the current manifest before resuming.`); } extraPathGrants = state.manifest.extraPathGrants.map((grant) => { return structuredClone(trustedGrantsByPath.get(grant.path)); }); } reboundState.manifest = new manifest_1.Manifest({ version: state.manifest.version, root: state.manifest.root, entries: structuredClone(state.manifest.entries), environment: Object.fromEntries(Object.entries(state.manifest.environment).map(([key, value]) => [ key, value.init(), ])), users: structuredClone(state.manifest.users), groups: structuredClone(state.manifest.groups), extraPathGrants, remoteMountCommandAllowlist: [ ...state.manifest.remoteMountCommandAllowlist, ], }); (0, manifest_1.copyManifestMountCredentialExposurePolicy)(reboundState.manifest, state.manifest); (0, mountSecurity_1.copyTrustedMountCredentialRebindProvenance)(reboundState.manifest, state.manifest); const reboundGrantsByPath = new Map(reboundState.manifest.extraPathGrants.map((grant) => [grant.path, grant])); const unresolvedPaths = options.replaceWithTrustedGrantSet ? [] : readRedactedHostPathGrantPaths(state).filter((path) => reboundGrantsByPath.get(path)?.hostPath === undefined); if (unresolvedPaths.length > 0) { reboundState[redactedHostPathGrantPathsKey] = unresolvedPaths; } else { delete reboundState[redactedHostPathGrantPathsKey]; } return reboundState; } function assertHostPathGrantsRebound(state) { const paths = readRedactedHostPathGrantPaths(state); if (paths.length === 0) { return; } throw new errors_1.UserError(`Sandbox session state requires trusted hostPath values for these path grants: ${paths.join(', ')}. Resume it through the Runner with a current manifest that defines each hostPath.`); } function readRedactedHostPathGrantPaths(state) { const value = state[redactedHostPathGrantPathsKey]; return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : []; } function serializeManifestRecord(manifest) { (0, mountSecurity_1.validateMountCredentialBoundaries)(manifest); return { version: manifest.version, root: manifest.root, entries: sanitizeEntriesForPersistence(manifest.entries), environment: serializePersistentManifestEnvironment(manifest), users: structuredClone(manifest.users), groups: structuredClone(manifest.groups), // Native host paths are rebound from trusted current configuration on resume. extraPathGrants: manifest.extraPathGrants.map((grant) => { const { hostPath: _hostPath, ...persistentGrant } = grant; return structuredClone(persistentGrant); }), remoteMountCommandAllowlist: [...manifest.remoteMountCommandAllowlist], }; } function deserializeManifest(value) { const { record, credentialPaths } = (0, mountSecurity_1.sanitizePersistedManifestRecord)(value ?? {}); const manifest = new manifest_1.Manifest(deserializeManifestRecord(record)); (0, mountSecurity_1.recordPersistedMountTopology)(manifest); (0, mountSecurity_1.recordPendingMountCredentialPaths)(manifest, credentialPaths); return manifest; } function sanitizeEnvironmentForPersistence(state) { const environment = serializeEnvironmentForPersistence(state); return Object.fromEntries(Object.entries(environment).map(([key, value]) => [key, value.value])); } function serializeEnvironmentForPersistence(state) { const runtimeEnvironment = state.environment ?? {}; const ephemeralKeys = new Set(); const serialized = {}; for (const [key, value] of Object.entries(state.manifest.environment)) { if (value.ephemeral || (0, manifest_1.isEnvValueReference)(value)) { ephemeralKeys.add(key); continue; } serialized[key] = { ...value.normalized(), value: runtimeEnvironment[key] ?? value.value, }; } for (const [key, value] of Object.entries(runtimeEnvironment)) { if (key in state.manifest.environment || ephemeralKeys.has(key)) { continue; } // Provider startup may add runtime env vars that are not in the manifest; keep them // unless they collide with an explicitly ephemeral manifest key. serialized[key] = { value }; } return serialized; } function mergeManifestDelta(base, update) { const merged = new manifest_1.Manifest({ version: update.version ?? base.version, root: base.root, entries: { ...structuredClone(base.entries), ...structuredClone(update.entries), }, environment: { ...cloneManifestEnvironment(base), ...cloneManifestEnvironment(update), }, users: (0, manifestCollections_1.mergeNamedObjects)(base.users, update.users), groups: (0, manifestCollections_1.mergeNamedObjects)(base.groups, update.groups), extraPathGrants: (0, manifestCollections_1.mergePathKeyedObjects)(base.extraPathGrants, update.extraPathGrants), remoteMountCommandAllowlist: shouldMergeRemoteMountCommandAllowlist(update) ? update.remoteMountCommandAllowlist : base.remoteMountCommandAllowlist, }); (0, manifest_1.copyManifestMountCredentialExposurePolicy)(merged, base, update); return merged; } function cloneManifestEnvironment(manifest) { return Object.fromEntries(Object.entries(manifest.environment).map(([key, value]) => [ key, value.init(), ])); } function mergeManifestEntryDelta(base, path, entry) { return mergeManifestDelta(base, new manifest_1.Manifest({ root: base.root, entries: { [path]: structuredClone(entry), }, })); } function serializePersistentManifestEnvironment(manifest) { const environment = (0, environment_1.serializeManifestEnvironment)(manifest); // Ephemeral envs are runtime-only and must not be persisted. // Persisting them would leak values into snapshots. // Resume would also depend on regenerated data. return Object.fromEntries(Object.entries(environment).filter(([key]) => !manifest.environment[key]?.ephemeral)); } function shouldMergeRemoteMountCommandAllowlist(update) { return (0, remoteMountCommandAllowlist_1.hasCustomRemoteMountCommandAllowlist)(update.remoteMountCommandAllowlist); } function sanitizeEntriesForPersistence(entries, ancestorEphemeral = false) { const sanitizedEntries = {}; for (const [path, entry] of Object.entries(entries)) { const sanitizedEntry = sanitizeEntryForPersistence(entry, ancestorEphemeral); if (sanitizedEntry) { sanitizedEntries[path] = sanitizedEntry; } } return sanitizedEntries; } function sanitizeEntryForPersistence(entry, ancestorEphemeral) { const effectiveEphemeral = ancestorEphemeral || Boolean(entry.ephemeral); if (entry.type === 'file') { return !effectiveEphemeral ? { ...entry, content: serializeFileContentForPersistence(entry.content), } : undefined; } if (entry.type !== 'dir' || !entry.children) { if (effectiveEphemeral && !(0, entries_1.isMount)(entry)) { return undefined; } return (0, entries_1.isMount)(entry) ? (0, mountSecurity_1.redactMountCredentialsForPersistence)(entry) : { ...entry }; } const children = sanitizeEntriesForPersistence(entry.children, effectiveEphemeral); if (effectiveEphemeral && Object.keys(children).length === 0) { return undefined; } return { ...entry, children, }; } function deserializeManifestRecord(value) { return { ...value, entries: deserializeEntriesForRuntime(value.entries), extraPathGrants: Array.isArray(value.extraPathGrants) ? value.extraPathGrants.map((grant) => { if (typeof grant !== 'object' || grant === null) { return grant; } const { hostPath: _hostPath, ...persistentGrant } = grant; return persistentGrant; }) : value.extraPathGrants, }; } function deserializeEntriesForRuntime(entries) { if (!entries) { return {}; } return Object.fromEntries(Object.entries(entries).map(([path, entry]) => [ path, deserializeEntryForRuntime(entry), ])); } function deserializeEntryForRuntime(entry) { if (entry.type === 'file') { const content = entry.content; return { ...entry, content: isSerializedFileContent(content) ? (0, base64_1.decodeBase64ToUint8Array)(content.data) : entry.content, }; } if (entry.type === 'dir' && entry.children) { return { ...entry, children: deserializeEntriesForRuntime(entry.children), }; } return { ...entry }; } function serializeFileContentForPersistence(content) { return typeof content === 'string' ? content : { type: 'base64', data: (0, base64_1.encodeUint8ArrayToBase64)(content), }; } function isSerializedFileContent(value) { return (typeof value === 'object' && value !== null && value.type === 'base64' && typeof value.data === 'string'); } //# sourceMappingURL=manifestPersistence.js.map