@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
307 lines • 14.1 kB
JavaScript
;
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