@opengeni/runtime 0.14.1 → 0.14.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-4YJ5Z52Z.js → chunk-7E2ZSCZK.js} +6 -6
- package/dist/{chunk-4YJ5Z52Z.js.map → chunk-7E2ZSCZK.js.map} +1 -1
- package/dist/{chunk-HHWIZHQM.js → chunk-GNEGANUF.js} +942 -300
- package/dist/chunk-GNEGANUF.js.map +1 -0
- package/dist/codex-tool-search.d.ts +100 -0
- package/dist/context-compaction.d.ts +236 -0
- package/dist/history-sanitizer.d.ts +320 -0
- package/dist/index.d.ts +186 -718
- package/dist/index.js +612 -126
- package/dist/index.js.map +1 -1
- package/dist/{localSources-T3HZ6KY4.js → localSources-BXHBKGF4.js} +2 -2
- package/dist/mcp-network.d.ts +32 -35
- package/dist/metrics.d.ts +33 -0
- package/dist/sandbox/capabilities.d.ts +11 -0
- package/dist/sandbox/channel-a.d.ts +226 -0
- package/dist/sandbox/display-stack.d.ts +99 -0
- package/dist/sandbox/errors.d.ts +16 -0
- package/dist/sandbox/exec-banner.d.ts +19 -0
- package/dist/sandbox/index.d.ts +190 -6
- package/dist/sandbox/index.js +20 -4
- package/dist/sandbox/op-correlation.d.ts +24 -0
- package/dist/sandbox/provider-errors.d.ts +32 -0
- package/dist/sandbox/provider-operation-gate.d.ts +12 -0
- package/dist/sandbox/providers/blaxel.d.ts +2 -0
- package/dist/sandbox/providers/cloudflare.d.ts +2 -0
- package/dist/sandbox/providers/daytona.d.ts +2 -0
- package/dist/sandbox/providers/docker.d.ts +2 -0
- package/dist/sandbox/providers/e2b.d.ts +2 -0
- package/dist/sandbox/providers/index.d.ts +11 -0
- package/dist/sandbox/providers/local.d.ts +2 -0
- package/dist/sandbox/providers/modal.d.ts +132 -0
- package/dist/sandbox/providers/none.d.ts +2 -0
- package/dist/sandbox/providers/runloop.d.ts +2 -0
- package/dist/sandbox/providers/selfhosted.d.ts +2 -0
- package/dist/sandbox/providers/types.d.ts +30 -0
- package/dist/sandbox/providers/vercel.d.ts +2 -0
- package/dist/sandbox/recording.d.ts +80 -0
- package/dist/sandbox/routing/backend-resolver.d.ts +173 -0
- package/dist/sandbox/routing/routing-session.d.ts +401 -0
- package/dist/sandbox/run-credentials.d.ts +65 -0
- package/dist/sandbox/select.d.ts +120 -0
- package/dist/sandbox/selfhosted/capabilities.d.ts +91 -0
- package/dist/sandbox/selfhosted/control-rpc.d.ts +175 -0
- package/dist/sandbox/selfhosted/fault-rendering.d.ts +40 -0
- package/dist/sandbox/selfhosted/op-observer.d.ts +40 -0
- package/dist/sandbox/selfhosted/op-stream.d.ts +104 -0
- package/dist/sandbox/selfhosted/op-testing.d.ts +108 -0
- package/dist/sandbox/selfhosted/op-transport.d.ts +60 -0
- package/dist/sandbox/selfhosted/retry-policy.d.ts +106 -0
- package/dist/sandbox/selfhosted/session.d.ts +552 -0
- package/dist/sandbox/selfhosted/testing.d.ts +48 -0
- package/dist/sandbox/stream-port.d.ts +80 -0
- package/dist/sandbox/stream-token.d.ts +44 -0
- package/dist/sandbox/terminal-server.d.ts +67 -0
- package/dist/sandbox/toolspace-token.d.ts +23 -0
- package/dist/sandbox/turn-tool-cancellation.d.ts +72 -0
- package/dist/sandbox/workspace-archive.d.ts +39 -0
- package/dist/sandbox-computer.d.ts +227 -0
- package/dist/screenshot-error-card.d.ts +1 -0
- package/dist/skill-library.d.ts +10 -12
- package/dist/usage-telemetry.d.ts +45 -0
- package/dist/workspace-skills.d.ts +31 -0
- package/package.json +7 -7
- package/src/context-compaction.ts +600 -6
- package/src/history-sanitizer.ts +10 -92
- package/src/index.ts +301 -62
- package/src/sandbox/channel-a.ts +267 -81
- package/src/sandbox/index.ts +56 -94
- package/src/sandbox/provider-operation-gate.ts +110 -0
- package/src/sandbox/providers/docker.ts +13 -1
- package/src/sandbox/providers/modal.ts +433 -3
- package/src/sandbox/routing/routing-session.ts +84 -8
- package/src/sandbox/selfhosted/session.ts +7 -6
- package/src/sandbox/turn-tool-cancellation.ts +47 -7
- package/src/sandbox/workspace-archive.ts +176 -73
- package/src/sandbox-computer.ts +25 -75
- package/dist/chunk-HHWIZHQM.js.map +0 -1
- package/dist/index-BraazrAO.d.ts +0 -2931
- /package/dist/{localSources-T3HZ6KY4.js.map → localSources-BXHBKGF4.js.map} +0 -0
package/src/sandbox/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { PROVIDER_REGISTRY } from "./providers";
|
|
|
34
34
|
import { SandboxConfigError, SandboxResumeStateUnavailableError } from "./errors";
|
|
35
35
|
import { isProviderSandboxNotFoundError } from "./provider-errors";
|
|
36
36
|
import {
|
|
37
|
+
decodeNativeSnapshotRef,
|
|
37
38
|
readVerifiedWorkspaceArchive,
|
|
38
39
|
verifyRestoredWorkspace,
|
|
39
40
|
WorkspaceArchiveIntegrityError,
|
|
@@ -81,10 +82,17 @@ export {
|
|
|
81
82
|
WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
|
|
82
83
|
WorkspaceArchiveIntegrityError,
|
|
83
84
|
captureVerifiedWorkspaceArchive,
|
|
85
|
+
decodeNativeSnapshotRef,
|
|
86
|
+
describeLegacyNativeSnapshotArchive,
|
|
87
|
+
describeNativeSnapshotArchive,
|
|
84
88
|
fingerprintSandboxWorkspace,
|
|
85
89
|
parseWorkspaceArchiveDescriptor,
|
|
86
90
|
readVerifiedWorkspaceArchive,
|
|
87
91
|
verifyRestoredWorkspace,
|
|
92
|
+
type NativeSnapshotDescriptor,
|
|
93
|
+
type NativeSnapshotProvider,
|
|
94
|
+
type NativeSnapshotRef,
|
|
95
|
+
type TarWorkspaceArchiveDescriptor,
|
|
88
96
|
type VerifiedWorkspaceArchive,
|
|
89
97
|
type WorkspaceArchiveDescriptor,
|
|
90
98
|
type WorkspaceArchiveIntegrityCode,
|
|
@@ -92,13 +100,20 @@ export {
|
|
|
92
100
|
} from "./workspace-archive";
|
|
93
101
|
export {
|
|
94
102
|
ensureModalRegistryImage,
|
|
103
|
+
deleteModalCheckpointSnapshot,
|
|
104
|
+
inspectModalSandboxLifecycle,
|
|
105
|
+
modalSessionMatchesCheckpointProviderBinding,
|
|
95
106
|
modalSandboxAttributionEnvironment,
|
|
96
107
|
modalSandboxAttributionTags,
|
|
108
|
+
resolveModalCheckpointProviderBinding,
|
|
109
|
+
resolveModalCheckpointProviderBindingForLiveSandbox,
|
|
110
|
+
resolveModalCheckpointProviderBindingForSession,
|
|
97
111
|
sweepModalOrphanSandboxes,
|
|
98
112
|
tagModalSandbox,
|
|
99
113
|
terminateModalSandboxById,
|
|
100
114
|
type LiveModalSandboxLeaseAttribution,
|
|
101
115
|
type ModalModuleLoader,
|
|
116
|
+
type ModalCheckpointProviderBinding,
|
|
102
117
|
type ModalOrphanSweepResult,
|
|
103
118
|
type ModalOrphanSweepTermination,
|
|
104
119
|
type ModalSandboxAttribution,
|
|
@@ -506,7 +521,9 @@ function withDockerNetwork(client: SandboxClient, network: string | undefined):
|
|
|
506
521
|
}
|
|
507
522
|
: {}),
|
|
508
523
|
...(client.delete
|
|
509
|
-
? {
|
|
524
|
+
? {
|
|
525
|
+
delete: async (state: SandboxSessionState) => await client.delete!(state),
|
|
526
|
+
}
|
|
510
527
|
: {}),
|
|
511
528
|
...(client.serializeSessionState
|
|
512
529
|
? {
|
|
@@ -628,84 +645,14 @@ export function readWorkspaceArchiveFromEnvelopeSessionState(
|
|
|
628
645
|
}
|
|
629
646
|
}
|
|
630
647
|
|
|
631
|
-
// The native snapshot-ref prefixes the @openai/agents-extensions modal client
|
|
632
|
-
// encodes (snapshots.mjs `NATIVE_SNAPSHOT_PREFIXES`). The ref is
|
|
633
|
-
// `<PREFIX>\n{"snapshot_id":"...",...}`. We re-implement the decode here because
|
|
634
|
-
// `@openai/agents-extensions/sandbox/shared` is NOT an exported subpath (the
|
|
635
|
-
// package `exports` map only exposes `./sandbox/<provider>`), so decodeNativeSnapshotRef
|
|
636
|
-
// is unreachable — same reasoning as isProviderSandboxNotFoundError below.
|
|
637
|
-
const MODAL_SNAPSHOT_REF_PREFIXES = [
|
|
638
|
-
"MODAL_SANDBOX_FS_SNAPSHOT_V1\n",
|
|
639
|
-
"MODAL_SANDBOX_DIR_SNAPSHOT_V1\n",
|
|
640
|
-
];
|
|
641
|
-
|
|
642
648
|
/** Decode the Modal snapshot id out of a persisted base64 archive ref, or
|
|
643
|
-
* undefined when the archive is a tar payload
|
|
644
|
-
* is unparseable. Used only for keep-latest-per-lease snapshot GC. */
|
|
649
|
+
* undefined when the archive is a tar payload or is unparseable. */
|
|
645
650
|
export function decodeModalSnapshotId(archive: Uint8Array): string | undefined {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
for (const prefix of MODAL_SNAPSHOT_REF_PREFIXES) {
|
|
653
|
-
if (!text.startsWith(prefix)) {
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
try {
|
|
657
|
-
const payload = JSON.parse(text.slice(prefix.length)) as { snapshot_id?: unknown };
|
|
658
|
-
return typeof payload.snapshot_id === "string" && payload.snapshot_id.length > 0
|
|
659
|
-
? payload.snapshot_id
|
|
660
|
-
: undefined;
|
|
661
|
-
} catch {
|
|
662
|
-
return undefined;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return undefined;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Best-effort GC of a SUPERSEDED Modal filesystem/directory snapshot
|
|
670
|
-
* (sandbox-file-persistence). restoreSnapshotFilesystem terminates the previous
|
|
671
|
-
* SANDBOX but never deletes the prior SNAPSHOT image, so snapshots accumulate
|
|
672
|
-
* unbounded across warm/cold cycles. The reaper keeps only the latest per lease:
|
|
673
|
-
* when it writes a NEW archive it passes the PRIOR archive here to delete its
|
|
674
|
-
* image via the live session's Modal client (`session.modal.images.delete(id)` —
|
|
675
|
-
* the same API the SDK uses for directory images). Never throws (GC is a
|
|
676
|
-
* best-effort backstop; a leaked snapshot is a cost issue, not a correctness one).
|
|
677
|
-
* A tar archive (no snapshot id) is a no-op. Returns the deleted snapshot id (or
|
|
678
|
-
* undefined when nothing was deleted) for observability.
|
|
679
|
-
*/
|
|
680
|
-
export async function deletePriorPersistedSnapshot(
|
|
681
|
-
session: unknown,
|
|
682
|
-
priorArchiveBase64: string | null | undefined,
|
|
683
|
-
): Promise<string | undefined> {
|
|
684
|
-
if (!priorArchiveBase64) {
|
|
685
|
-
return undefined;
|
|
686
|
-
}
|
|
687
|
-
let bytes: Uint8Array;
|
|
688
|
-
try {
|
|
689
|
-
bytes = Uint8Array.from(Buffer.from(priorArchiveBase64, "base64"));
|
|
690
|
-
} catch {
|
|
691
|
-
return undefined;
|
|
692
|
-
}
|
|
693
|
-
const snapshotId = decodeModalSnapshotId(bytes);
|
|
694
|
-
if (!snapshotId) {
|
|
695
|
-
return undefined;
|
|
696
|
-
}
|
|
697
|
-
const modal = (session as { modal?: { images?: { delete?: (id: string) => Promise<unknown> } } })
|
|
698
|
-
.modal;
|
|
699
|
-
const del = modal?.images?.delete;
|
|
700
|
-
if (typeof del !== "function") {
|
|
701
|
-
return undefined;
|
|
702
|
-
}
|
|
703
|
-
try {
|
|
704
|
-
await del.call(modal!.images, snapshotId);
|
|
705
|
-
return snapshotId;
|
|
706
|
-
} catch {
|
|
707
|
-
return undefined;
|
|
708
|
-
}
|
|
651
|
+
const ref = decodeNativeSnapshotRef(archive);
|
|
652
|
+
return ref?.provider === "modal_snapshot_filesystem" ||
|
|
653
|
+
ref?.provider === "modal_snapshot_directory"
|
|
654
|
+
? ref.snapshotId
|
|
655
|
+
: undefined;
|
|
709
656
|
}
|
|
710
657
|
|
|
711
658
|
export async function deserializeSandboxSessionStateEnvelope(
|
|
@@ -932,7 +879,9 @@ async function terminateCreatedSandbox(
|
|
|
932
879
|
session: unknown,
|
|
933
880
|
sessionState: unknown,
|
|
934
881
|
): Promise<void> {
|
|
935
|
-
const clientWithDelete = client as {
|
|
882
|
+
const clientWithDelete = client as {
|
|
883
|
+
delete?: (state: unknown) => Promise<unknown>;
|
|
884
|
+
};
|
|
936
885
|
if (typeof clientWithDelete.delete === "function" && sessionState !== undefined) {
|
|
937
886
|
try {
|
|
938
887
|
await clientWithDelete.delete(sessionState);
|
|
@@ -980,6 +929,13 @@ export async function establishSandboxSessionFromEnvelope(
|
|
|
980
929
|
* is never observable as ready while verification is in flight. */
|
|
981
930
|
onWorkspaceRestoreVerifying?: (descriptor: WorkspaceArchiveDescriptor) => Promise<void>;
|
|
982
931
|
metrics?: RuntimeMetricsHooks;
|
|
932
|
+
/** Isolated conformance-test/embedding seam. Ordinary runtime callers use
|
|
933
|
+
* the validated provider registry. */
|
|
934
|
+
clientFactory?: (
|
|
935
|
+
backend: SandboxBackend,
|
|
936
|
+
settings: Settings,
|
|
937
|
+
environment: Record<string, string>,
|
|
938
|
+
) => unknown;
|
|
983
939
|
},
|
|
984
940
|
): Promise<EstablishedSandboxSession> {
|
|
985
941
|
const envelopeBackend =
|
|
@@ -987,9 +943,11 @@ export async function establishSandboxSessionFromEnvelope(
|
|
|
987
943
|
const backend =
|
|
988
944
|
opts.backendOverride ?? envelopeBackend ?? (settings.sandboxBackend as SandboxBackend);
|
|
989
945
|
const environment = opts.environment ?? collectSandboxEnvironment(settings);
|
|
990
|
-
const client =
|
|
991
|
-
|
|
992
|
-
|
|
946
|
+
const client = (opts.clientFactory ?? createSandboxClientForBackend)(
|
|
947
|
+
backend,
|
|
948
|
+
settings,
|
|
949
|
+
environment,
|
|
950
|
+
) as ResumeCapableClient | undefined;
|
|
993
951
|
if (!client) {
|
|
994
952
|
throw new SandboxConfigError(
|
|
995
953
|
backend,
|
|
@@ -1137,22 +1095,26 @@ export async function establishSandboxSessionFromEnvelope(
|
|
|
1137
1095
|
throw error;
|
|
1138
1096
|
}
|
|
1139
1097
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1098
|
+
// Native provider snapshots are restored by their exact opaque receipt.
|
|
1099
|
+
// OpenGeni verifies receipt identity/hash before hydration, but it must not
|
|
1100
|
+
// impose a tar/inode equivalence contract on the provider's filesystem
|
|
1101
|
+
// image. Tar archives remain content-verified after hydration.
|
|
1102
|
+
if (workspaceArchive.kind === "tar") {
|
|
1103
|
+
try {
|
|
1104
|
+
await verifyRestoredWorkspace(restored, workspaceArchive.descriptor);
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
await terminateCreatedSandbox(client, restored, (restored as { state?: unknown }).state);
|
|
1107
|
+
if (error instanceof WorkspaceArchiveIntegrityError) throw error;
|
|
1108
|
+
throw new WorkspaceArchiveIntegrityError(
|
|
1109
|
+
"workspace_fingerprint_unavailable",
|
|
1110
|
+
`failed to verify selected workspace archive revision ${workspaceArchive.descriptor.revision}`,
|
|
1111
|
+
{ retryable: true },
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1152
1114
|
}
|
|
1153
1115
|
hydrationApplied = true;
|
|
1154
1116
|
console.info(
|
|
1155
|
-
`[sandbox] cold-restore verified
|
|
1117
|
+
`[sandbox] cold-restore applied ${workspaceArchive.kind === "provider_snapshot" ? "native snapshot receipt" : "verified tar archive"} revision ${workspaceArchive.descriptor.revision}`,
|
|
1156
1118
|
);
|
|
1157
1119
|
}
|
|
1158
1120
|
restoredState = (restored as { state?: unknown }).state;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
type ProviderOperationGate = {
|
|
2
|
+
activeOperations: number;
|
|
3
|
+
captureActive: boolean;
|
|
4
|
+
operationWaiters: Array<() => void>;
|
|
5
|
+
captureWaiters: Array<() => void>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const providerOperationGates = new WeakMap<object, ProviderOperationGate>();
|
|
9
|
+
|
|
10
|
+
function sessionIdentity(session: unknown): object {
|
|
11
|
+
const identityType = typeof session;
|
|
12
|
+
if (!["object", "function"].includes(identityType) || session === null) {
|
|
13
|
+
throw new Error("Sandbox provider operation has no session identity");
|
|
14
|
+
}
|
|
15
|
+
return session as object;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function gateFor(session: object): ProviderOperationGate {
|
|
19
|
+
const existing = providerOperationGates.get(session);
|
|
20
|
+
if (existing) return existing;
|
|
21
|
+
const created: ProviderOperationGate = {
|
|
22
|
+
activeOperations: 0,
|
|
23
|
+
captureActive: false,
|
|
24
|
+
operationWaiters: [],
|
|
25
|
+
captureWaiters: [],
|
|
26
|
+
};
|
|
27
|
+
providerOperationGates.set(session, created);
|
|
28
|
+
return created;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function enterOperation(gate: ProviderOperationGate): Promise<void> {
|
|
32
|
+
if (!gate.captureActive && gate.captureWaiters.length === 0) {
|
|
33
|
+
gate.activeOperations += 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
await new Promise<void>((resolve) => gate.operationWaiters.push(resolve));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function leaveOperation(gate: ProviderOperationGate): void {
|
|
40
|
+
gate.activeOperations -= 1;
|
|
41
|
+
if (gate.activeOperations < 0) {
|
|
42
|
+
throw new Error("Sandbox provider operation gate underflow");
|
|
43
|
+
}
|
|
44
|
+
if (gate.activeOperations === 0) {
|
|
45
|
+
const nextCapture = gate.captureWaiters.shift();
|
|
46
|
+
if (nextCapture) {
|
|
47
|
+
gate.captureActive = true;
|
|
48
|
+
nextCapture();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function enterCapture(gate: ProviderOperationGate): Promise<void> {
|
|
54
|
+
if (!gate.captureActive && gate.activeOperations === 0) {
|
|
55
|
+
gate.captureActive = true;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
await new Promise<void>((resolve) => gate.captureWaiters.push(resolve));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function leaveCapture(gate: ProviderOperationGate): void {
|
|
62
|
+
if (!gate.captureActive) {
|
|
63
|
+
throw new Error("Sandbox provider capture gate was not held");
|
|
64
|
+
}
|
|
65
|
+
const nextCapture = gate.captureWaiters.shift();
|
|
66
|
+
if (nextCapture) {
|
|
67
|
+
nextCapture();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
gate.captureActive = false;
|
|
71
|
+
const operations = gate.operationWaiters.splice(0);
|
|
72
|
+
gate.activeOperations += operations.length;
|
|
73
|
+
for (const operation of operations) operation();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Coordinate every command/filesystem/control call against one in-process
|
|
78
|
+
* provider session. Ordinary operations may overlap; once a capture queues,
|
|
79
|
+
* later operations wait until its exclusive provider pause completes.
|
|
80
|
+
*/
|
|
81
|
+
export async function withSandboxProviderOperation<T>(
|
|
82
|
+
session: unknown,
|
|
83
|
+
operation: () => Promise<T>,
|
|
84
|
+
): Promise<T> {
|
|
85
|
+
const gate = gateFor(sessionIdentity(session));
|
|
86
|
+
await enterOperation(gate);
|
|
87
|
+
try {
|
|
88
|
+
return await operation();
|
|
89
|
+
} finally {
|
|
90
|
+
leaveOperation(gate);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Hold the provider session exclusively for the complete verified archive
|
|
96
|
+
* capture. The durable lease claim handles other workers; this local gate
|
|
97
|
+
* closes the same-holder race between native reads and the heartbeat capture.
|
|
98
|
+
*/
|
|
99
|
+
export async function withSandboxProviderCapture<T>(
|
|
100
|
+
session: unknown,
|
|
101
|
+
capture: () => Promise<T>,
|
|
102
|
+
): Promise<T> {
|
|
103
|
+
const gate = gateFor(sessionIdentity(session));
|
|
104
|
+
await enterCapture(gate);
|
|
105
|
+
try {
|
|
106
|
+
return await capture();
|
|
107
|
+
} finally {
|
|
108
|
+
leaveCapture(gate);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DockerSandboxClient } from "@openai/agents/sandbox/local";
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
2
3
|
import { CAPABILITY_DESCRIPTORS } from "../capabilities";
|
|
4
|
+
import { SandboxConfigError } from "../errors";
|
|
3
5
|
import type { ProviderRegistration } from "./types";
|
|
4
6
|
|
|
5
7
|
export const dockerProvider: ProviderRegistration = {
|
|
@@ -7,11 +9,21 @@ export const dockerProvider: ProviderRegistration = {
|
|
|
7
9
|
descriptor: CAPABILITY_DESCRIPTORS.docker,
|
|
8
10
|
// Local dev container — no credentials. (The dockerNetwork decoration is
|
|
9
11
|
// applied by the factory, not here: it wraps the constructed client.)
|
|
10
|
-
validateCredentials() {
|
|
12
|
+
validateCredentials(settings) {
|
|
13
|
+
const workspaceBaseDir = settings.dockerWorkspaceBaseDir?.trim();
|
|
14
|
+
if (workspaceBaseDir && !isAbsolute(workspaceBaseDir)) {
|
|
15
|
+
throw new SandboxConfigError(
|
|
16
|
+
"docker",
|
|
17
|
+
"OPENGENI_DOCKER_WORKSPACE_BASE_DIR must be an absolute path",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
11
21
|
build({ settings, exposedPorts }) {
|
|
22
|
+
const workspaceBaseDir = settings.dockerWorkspaceBaseDir?.trim();
|
|
12
23
|
return new DockerSandboxClient({
|
|
13
24
|
image: settings.dockerImage,
|
|
14
25
|
exposedPorts,
|
|
26
|
+
...(workspaceBaseDir ? { workspaceBaseDir } : {}),
|
|
15
27
|
});
|
|
16
28
|
},
|
|
17
29
|
};
|