@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
248 lines • 8.69 kB
JavaScript
import { cloneManifest, Manifest } from "../sandbox/manifest.mjs";
import { snapshotTestingValue } from "../utils/testingSnapshot.mjs";
export class InvalidScriptedSandboxStepError extends Error {
reason;
/** The zero-based position of the invalid factory input. */
inputIndex;
method;
constructor(options) {
super(options.message);
this.name = 'InvalidScriptedSandboxStepError';
this.reason = options.reason;
this.inputIndex = options.inputIndex;
this.method = options.method;
}
}
export class UnexpectedSandboxCallError extends Error {
/** The zero-based position of the call in `calls`. */
callIndex;
actualMethod;
expectedMethod;
remainingSteps;
constructor(options) {
const expectation = options.expectedMethod
? `; expected ${options.expectedMethod}`
: '; no scripted steps remain';
super(`Scripted sandbox session received unexpected ${options.actualMethod} call #${displayIndex(options.callIndex)}${expectation}.`);
this.name = 'UnexpectedSandboxCallError';
this.callIndex = options.callIndex;
this.actualMethod = options.actualMethod;
this.expectedMethod = options.expectedMethod;
this.remainingSteps = options.remainingSteps;
}
}
export class SandboxCallMatcherError extends Error {
/** The zero-based position of the call in `calls`. */
callIndex;
method;
constructor(call) {
super(`Scripted sandbox session matcher rejected ${call.method} call #${displayIndex(call.index)}.`);
this.name = 'SandboxCallMatcherError';
this.callIndex = call.index;
this.method = call.method;
}
}
export class UnconsumedSandboxStepsError extends Error {
remainingSteps;
pendingMethods;
constructor(pendingMethods) {
super(`Scripted sandbox session has ${pendingMethods.length} unconsumed step${pendingMethods.length === 1 ? '' : 's'}: ${pendingMethods.join(', ')}.`);
this.name = 'UnconsumedSandboxStepsError';
this.remainingSteps = pendingMethods.length;
this.pendingMethods = Object.freeze([...pendingMethods]);
}
}
const sandboxMethodModes = {
start: 'async',
running: 'async',
registerPreStopHook: 'sync',
runPreStopHooks: 'async',
preStop: 'async',
stop: 'async',
shutdown: 'async',
delete: 'async',
createEditor: 'sync',
exec: 'async',
execCommand: 'async',
writeStdin: 'async',
viewImage: 'async',
readFile: 'async',
listDir: 'async',
pathExists: 'async',
directoryExists: 'async',
materializeEntry: 'async',
applyManifest: 'async',
persistWorkspace: 'async',
hydrateWorkspace: 'async',
setArchiveLimits: 'sync',
resolveExposedPort: 'async',
supportsPty: 'sync',
close: 'async',
};
export function scriptedSandboxSession(inputs = []) {
const steps = Array.from(inputs, (input, index) => normalizeInput(input, index));
const calls = [];
const configuredMethods = new Set(steps.map((step) => step.method));
const state = { manifest: new Manifest() };
const session = { state };
const invoke = (method, args) => {
const call = Object.freeze({
index: calls.length,
method,
args: Object.freeze(snapshotSandboxCallArgs(method, args)),
});
calls.push(call);
const step = steps.shift();
if (!step || step.method !== method) {
throw new UnexpectedSandboxCallError({
callIndex: call.index,
actualMethod: method,
expectedMethod: step?.method,
remainingSteps: steps.length,
});
}
if (step.match) {
const matchArgs = snapshotSandboxCallArgs(method, call.args);
if (step.match(...matchArgs) === false) {
throw new SandboxCallMatcherError(call);
}
}
if (step.type === 'error') {
throw step.error;
}
if (step.type === 'responder') {
return step.respond(snapshotRecordedCall(call));
}
return step.result;
};
for (const method of configuredMethods) {
const scriptedMethod = (...args) => {
if (sandboxMethodModes[method] === 'sync') {
return invoke(method, args);
}
try {
return Promise.resolve(invoke(method, args));
}
catch (error) {
return Promise.reject(error);
}
};
Object.defineProperty(session, method, {
configurable: false,
enumerable: true,
value: scriptedMethod,
writable: true,
});
}
Object.defineProperties(session, {
calls: {
configurable: false,
enumerable: true,
get: () => Object.freeze(calls.map(snapshotRecordedCall)),
},
remainingSteps: {
configurable: false,
enumerable: true,
get: () => steps.length,
},
assertComplete: {
configurable: false,
enumerable: true,
value: () => {
if (steps.length > 0) {
throw new UnconsumedSandboxStepsError(steps.map((step) => step.method));
}
},
writable: false,
},
});
return session;
}
function normalizeInput(input, inputIndex) {
if (!isRecord(input)) {
throw new InvalidScriptedSandboxStepError({
reason: 'invalid_input',
inputIndex,
message: `Scripted sandbox step #${displayIndex(inputIndex)} must be an object.`,
});
}
const method = input.method;
if (typeof method !== 'string' ||
!Object.prototype.hasOwnProperty.call(sandboxMethodModes, method)) {
throw new InvalidScriptedSandboxStepError({
reason: 'unknown_method',
inputIndex,
method: typeof method === 'string' ? method : undefined,
message: `Scripted sandbox step #${displayIndex(inputIndex)} has an unknown method.`,
});
}
if (typeof input.match !== 'undefined' && typeof input.match !== 'function') {
throw new InvalidScriptedSandboxStepError({
reason: 'invalid_matcher',
inputIndex,
method,
message: `Scripted sandbox step #${displayIndex(inputIndex)} for ${method} must use a function matcher.`,
});
}
const hasResult = Object.prototype.hasOwnProperty.call(input, 'result');
const hasResponder = Object.prototype.hasOwnProperty.call(input, 'respond');
const hasError = Object.prototype.hasOwnProperty.call(input, 'error');
if (Number(hasResult) + Number(hasResponder) + Number(hasError) !== 1) {
throw new InvalidScriptedSandboxStepError({
reason: 'invalid_outcome',
inputIndex,
method,
message: `Scripted sandbox step #${displayIndex(inputIndex)} for ${method} must define exactly one of result, respond, or error.`,
});
}
const base = {
method: method,
...(typeof input.match === 'function'
? { match: input.match }
: {}),
};
if (hasResponder) {
if (typeof input.respond !== 'function') {
throw new InvalidScriptedSandboxStepError({
reason: 'invalid_outcome',
inputIndex,
method,
message: `Scripted sandbox step #${displayIndex(inputIndex)} for ${method} must use a function responder.`,
});
}
return {
...base,
type: 'responder',
respond: input.respond,
};
}
if (hasError) {
return { ...base, type: 'error', error: input.error };
}
return {
...base,
type: 'result',
result: snapshotTestingValue(input.result),
};
}
function displayIndex(index) {
return index + 1;
}
function snapshotRecordedCall(call) {
return Object.freeze({
index: call.index,
method: call.method,
args: Object.freeze(snapshotSandboxCallArgs(call.method, call.args)),
});
}
function snapshotSandboxCallArgs(method, args) {
const snapshot = snapshotTestingValue([...args]);
if (method === 'applyManifest' && snapshot[0] instanceof Manifest) {
snapshot[0] = cloneManifest(snapshot[0]);
}
return snapshot;
}
function isRecord(value) {
return typeof value === 'object' && value !== null;
}
//# sourceMappingURL=scriptedSandboxSession.mjs.map