@opengeni/runtime 0.5.0 → 0.6.1

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.
@@ -1,6 +1,6 @@
1
1
  export { collectSandboxEnvironment, parseExposedPorts } from '@opengeni/config';
2
2
  export { CAPABILITY_DESCRIPTORS, CapabilityDescriptor, DESKTOP_STREAM_PORT, StreamTokenPayload, StreamTokenPayload as StreamTokenPayloadType, TERMINAL_STREAM_PORT } from '@opengeni/contracts';
3
3
  import '@openai/agents/sandbox';
4
- export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalOrphanSweepResult, z as ModalSandboxAttribution, N as NatsControlRpc, B as NatsRequestConnection, G as NegotiationContext, H as NumstatEntry, P as PROVIDER_REGISTRY, I as ProviderConstructionContext, J as ProviderRegistration, K as RecordingCodec, O as RecordingContentType, Q as RecordingError, S as RecordingProcess, T as RecordingUnavailableError, U as ResolvedActiveBackend, V as RoutableBackendSession, W as RoutableSandbox, X as RoutingSandboxSession, Y as RoutingSandboxSessionDeps, Z as RoutingTransitionEvent, _ as RoutingUnsupportedError, $ as SELFHOSTED_DEFAULT_TIMEOUT_MS, a0 as SELFHOSTED_RECONNECT_WINDOW_MS, a1 as SELFHOSTED_RELAY_STREAM_PATH, a2 as STREAM_PORT, a3 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a4 as SandboxChannelAService, a5 as SandboxChannelAServiceOptions, a6 as SandboxConfigError, a7 as SandboxCreatedCallback, a8 as SandboxProviderUnavailableError, a9 as SelfhostedApplyDiff, aa as SelfhostedControlError, ab as SelfhostedEditor, ac as SelfhostedEnrollment, ad as SelfhostedExecArgs, ae as SelfhostedExecResult, af as SelfhostedImageOutput, ag as SelfhostedLivenessState, ah as SelfhostedNegotiationInput, ai as SelfhostedRelayConfig, aj as SelfhostedSandboxClient, ak as SelfhostedSession, al as SelfhostedSessionBuild, am as SelfhostedSessionDeps, an as SelfhostedSessionState, ao as SelfhostedUnavailableReason, ap as StartRecordingInput, aq as StreamPortUnavailableError, ar as TERMINAL_SERVER_TIMEOUT_MS, as as TerminalServerError, at as TerminalServerUnsupportedError, au as agentErrorToControlError, av as assertDescriptorRegistryInvariants, aw as assertProviderRegistryInvariants, ax as assertSafeRelPath, ay as backendSupportsOs, az as buildDisplayStackScript, aA as buildSelfhostedBackendSession, aB as buildStreamUrl, aC as buildTerminalServerScript, aD as contentTypeForCodec, aE as createSandboxClient, aF as createSandboxClientForBackend, aG as decodeModalSnapshotId, aH as deletePriorPersistedSnapshot, aI as deleteRecordingArtifacts, aJ as deserializeSandboxSessionStateEnvelope, aK as desktopCapableBackend, aL as ensureDisplayStack, aM as ensureTerminalServer, aN as establishSandboxSessionFromEnvelope, aO as exposeStreamPort, aP as extForCodec, aQ as isExecSessionLostBanner, aR as isProviderSandboxNotFoundError, aS as isSelfhostedProviderNotFoundError, aT as isWorkspaceEscapeError, aU as makeActiveBackendResolver, aV as mintStreamToken, aW as modalSandboxAttributionEnvironment, aX as modalSandboxAttributionTags, aY as negotiateCapabilities, aZ as negotiateSelfhostedCapabilities, a_ as offlineAgentError, a$ as offlineControlResponse, b0 as parseExecBannerSessionId, b1 as parseNumstatZ, b2 as parsePorcelainV2, b3 as parseUnifiedPatch, b4 as readRecordingBytes, b5 as readWorkspaceArchiveFromEnvelopeSessionState, b6 as recordingStorageKey, b7 as restoredSandboxSessionStateFromEntry, b8 as sandboxStateEntryFromRunState, b9 as selectBackend, ba as selfhostedLiveness, bb as serializeEstablishedSandboxEnvelope, bc as setSelfhostedApplyDiff, bd as startRecording, be as stopRecording, bf as stripExecBanner, bg as subjectFor, bh as sweepModalOrphanSandboxes, bi as tagModalSandbox, bj as tearDownDisplayStack, bk as tearDownTerminalServer, bl as terminateModalSandboxById, bm as timeoutAgentError, bn as timeoutControlResponse, bo as verifyStreamToken } from '../index-BrdnxaAW.js';
4
+ export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalModuleLoader, z as ModalOrphanSweepResult, B as ModalSandboxAttribution, N as NatsControlRpc, G as NatsRequestConnection, H as NegotiationContext, I as NumstatEntry, P as PROVIDER_REGISTRY, J as ProviderConstructionContext, K as ProviderRegistration, O as RecordingCodec, Q as RecordingContentType, S as RecordingError, T as RecordingProcess, U as RecordingUnavailableError, V as ResolvedActiveBackend, W as RoutableBackendSession, X as RoutableSandbox, Y as RoutingSandboxSession, Z as RoutingSandboxSessionDeps, _ as RoutingTransitionEvent, $ as RoutingUnsupportedError, a0 as SELFHOSTED_DEFAULT_TIMEOUT_MS, a1 as SELFHOSTED_RECONNECT_WINDOW_MS, a2 as SELFHOSTED_RELAY_STREAM_PATH, a3 as STREAM_PORT, a4 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a5 as SandboxChannelAService, a6 as SandboxChannelAServiceOptions, a7 as SandboxConfigError, a8 as SandboxCreatedCallback, a9 as SandboxProviderUnavailableError, aa as SelfhostedApplyDiff, ab as SelfhostedControlError, ac as SelfhostedEditor, ad as SelfhostedEnrollment, ae as SelfhostedExecArgs, af as SelfhostedExecResult, ag as SelfhostedImageOutput, ah as SelfhostedLivenessState, ai as SelfhostedNegotiationInput, aj as SelfhostedRelayConfig, ak as SelfhostedSandboxClient, al as SelfhostedSession, am as SelfhostedSessionBuild, an as SelfhostedSessionDeps, ao as SelfhostedSessionState, ap as SelfhostedUnavailableReason, aq as StartRecordingInput, ar as StreamPortUnavailableError, as as TERMINAL_SERVER_TIMEOUT_MS, at as TerminalServerError, au as TerminalServerUnsupportedError, av as agentErrorToControlError, aw as assertDescriptorRegistryInvariants, ax as assertProviderRegistryInvariants, ay as assertSafeRelPath, az as backendSupportsOs, aA as buildDisplayStackScript, aB as buildSelfhostedBackendSession, aC as buildStreamUrl, aD as buildTerminalServerScript, aE as contentTypeForCodec, aF as createSandboxClient, aG as createSandboxClientForBackend, aH as decodeModalSnapshotId, aI as deletePriorPersistedSnapshot, aJ as deleteRecordingArtifacts, aK as deserializeSandboxSessionStateEnvelope, aL as desktopCapableBackend, aM as ensureDisplayStack, aN as ensureModalRegistryImage, aO as ensureTerminalServer, aP as establishSandboxSessionFromEnvelope, aQ as exposeStreamPort, aR as extForCodec, aS as isExecSessionLostBanner, aT as isProviderSandboxNotFoundError, aU as isSelfhostedProviderNotFoundError, aV as isWorkspaceEscapeError, aW as makeActiveBackendResolver, aX as mintStreamToken, aY as modalSandboxAttributionEnvironment, aZ as modalSandboxAttributionTags, a_ as negotiateCapabilities, a$ as negotiateSelfhostedCapabilities, b0 as offlineAgentError, b1 as offlineControlResponse, b2 as parseExecBannerSessionId, b3 as parseNumstatZ, b4 as parsePorcelainV2, b5 as parseUnifiedPatch, b6 as readRecordingBytes, b7 as readWorkspaceArchiveFromEnvelopeSessionState, b8 as recordingStorageKey, b9 as restoredSandboxSessionStateFromEntry, ba as sandboxStateEntryFromRunState, bb as selectBackend, bc as selfhostedLiveness, bd as serializeEstablishedSandboxEnvelope, be as setSelfhostedApplyDiff, bf as startRecording, bg as stopRecording, bh as stripExecBanner, bi as subjectFor, bj as sweepModalOrphanSandboxes, bk as tagModalSandbox, bl as tearDownDisplayStack, bm as tearDownTerminalServer, bn as terminateModalSandboxById, bo as timeoutAgentError, bp as timeoutControlResponse, bq as verifyStreamToken } from '../index-BTo_bZvN.js';
5
5
  import 'modal';
6
6
  import '@opengeni/agent-proto';
@@ -53,6 +53,7 @@ import {
53
53
  deserializeSandboxSessionStateEnvelope,
54
54
  desktopCapableBackend,
55
55
  ensureDisplayStack,
56
+ ensureModalRegistryImage,
56
57
  ensureTerminalServer,
57
58
  establishSandboxSessionFromEnvelope,
58
59
  exposeStreamPort,
@@ -95,7 +96,7 @@ import {
95
96
  timeoutAgentError,
96
97
  timeoutControlResponse,
97
98
  verifyStreamToken
98
- } from "../chunk-Q36E4MDM.js";
99
+ } from "../chunk-DYBF7ZEB.js";
99
100
  export {
100
101
  ActiveBackendUnresolvableError,
101
102
  CAPABILITY_DESCRIPTORS,
@@ -151,6 +152,7 @@ export {
151
152
  deserializeSandboxSessionStateEnvelope,
152
153
  desktopCapableBackend,
153
154
  ensureDisplayStack,
155
+ ensureModalRegistryImage,
154
156
  ensureTerminalServer,
155
157
  establishSandboxSessionFromEnvelope,
156
158
  exposeStreamPort,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@opengeni/agent-proto": "^0.2.1",
34
- "@opengeni/config": "^0.3.0",
34
+ "@opengeni/config": "^0.4.0",
35
35
  "@opengeni/contracts": "^0.9.0",
36
36
  "@openai/agents": "^0.11.6",
37
37
  "@openai/agents-extensions": "^0.11.6",
@@ -1,4 +1,5 @@
1
1
  import type { AgentInputItem } from "@openai/agents";
2
+ import { SCREENSHOT_OMITTED_IMAGE_DATA_URL } from "./screenshot-omitted-card";
2
3
 
3
4
  export const SCREENSHOT_OMITTED_PLACEHOLDER =
4
5
  "[screenshot omitted: an older desktop frame — the full image remains in the session event log]";
@@ -59,7 +60,7 @@ function collectItemImageOccurrences(
59
60
  return;
60
61
  }
61
62
  if (item.type === "computer_call_result" || item.type === "computer_call_output") {
62
- collectComputerOutputImages(item, path, placeholder, out);
63
+ collectComputerOutputImages(item, path, out);
63
64
  return;
64
65
  }
65
66
  if (item.type === "function_call_result" || item.type === "function_call_output") {
@@ -70,7 +71,6 @@ function collectItemImageOccurrences(
70
71
  function collectComputerOutputImages(
71
72
  item: Record<string, unknown>,
72
73
  path: PathSegment[],
73
- placeholder: string,
74
74
  out: ImageOccurrence[],
75
75
  ): void {
76
76
  const output = item.output;
@@ -79,7 +79,7 @@ function collectComputerOutputImages(
79
79
  }
80
80
  for (const key of ["data", "image_url", "imageUrl"]) {
81
81
  if (isImageDataUrl(output[key])) {
82
- out.push({ path: [...path, "output", key], replacement: placeholder });
82
+ out.push({ path: [...path, "output", key], replacement: SCREENSHOT_OMITTED_IMAGE_DATA_URL });
83
83
  return;
84
84
  }
85
85
  }
package/src/index.ts CHANGED
@@ -180,6 +180,7 @@ export {
180
180
  elideStaleScreenshotImages,
181
181
  SCREENSHOT_OMITTED_PLACEHOLDER,
182
182
  } from "./image-history";
183
+ export { SCREENSHOT_OMITTED_IMAGE_DATA_URL } from "./screenshot-omitted-card";
183
184
  export type { ElideStaleScreenshotsOptions, ElideStaleScreenshotsResult } from "./image-history";
184
185
 
185
186
  ensureReadableStreamFrom();
@@ -55,12 +55,14 @@ export {
55
55
  type ProviderConstructionContext,
56
56
  } from "./providers";
57
57
  export {
58
+ ensureModalRegistryImage,
58
59
  modalSandboxAttributionEnvironment,
59
60
  modalSandboxAttributionTags,
60
61
  sweepModalOrphanSandboxes,
61
62
  tagModalSandbox,
62
63
  terminateModalSandboxById,
63
64
  type LiveModalSandboxLeaseAttribution,
65
+ type ModalModuleLoader,
64
66
  type ModalOrphanSweepResult,
65
67
  type ModalSandboxAttribution,
66
68
  } from "./providers/modal";
@@ -87,8 +87,9 @@ export const modalProvider: ProviderRegistration = {
87
87
  if (settings.modalWorkspacePersistence) {
88
88
  options.workspacePersistence = settings.modalWorkspacePersistence;
89
89
  }
90
- if (settings.modalImageRef) {
91
- options.image = ModalImageSelector.fromTag(settings.modalImageRef);
90
+ const imageSelector = resolveModalImageSelector(settings);
91
+ if (imageSelector) {
92
+ options.image = imageSelector;
92
93
  }
93
94
  if (settings.modalTokenId) {
94
95
  options.tokenId = settings.modalTokenId;
@@ -106,6 +107,115 @@ export const modalProvider: ProviderRegistration = {
106
107
  type ModalModule = typeof import("modal");
107
108
  type ModalClientLike = InstanceType<ModalModule["ModalClient"]>;
108
109
 
110
+ // --- Private-registry image resolution (OPENGENI_MODAL_IMAGE_REGISTRY_SECRET) ------
111
+ //
112
+ // The Agents-extension Modal backend resolves `modalImageRef` via
113
+ // `Image.fromRegistry(tag)` with NO secret, so it can only pull PUBLIC images. To run
114
+ // a PRIVATE image we resolve the named Modal Secret and pre-build the authenticated
115
+ // `fromRegistry(tag, secret)` image ONCE per process, then hand the provider `build`
116
+ // a `ModalImageSelector.fromImage(...)`. `build` is synchronous and modal is imported
117
+ // lazily (never loaded for non-modal backends), so resolution can't happen inside
118
+ // `build`; the worker awaits `ensureModalRegistryImage` at boot for global refs and
119
+ // at turn time for pack-scoped refs, then `build` reads the settled result. Modal
120
+ // images are lazy, workspace-scoped definitions, so an image built by this module's
121
+ // client is usable by the ModalSandboxClient's own client.
122
+
123
+ /** Loader seam so unit tests can inject a fake modal module. */
124
+ export type ModalModuleLoader = () => Promise<Pick<ModalModule, "ModalClient">>;
125
+
126
+ const defaultModalLoader: ModalModuleLoader = () => import("modal");
127
+
128
+ /** Settled, synchronously-readable resolved images, keyed per config. */
129
+ const resolvedRegistryImages = new Map<string, unknown>();
130
+ /** In-flight resolutions, for cross-call de-duplication. */
131
+ const inFlightRegistryImages = new Map<string, Promise<void>>();
132
+
133
+ function registryImageCacheKey(settings: Settings): string {
134
+ return [
135
+ settings.modalImageRef ?? "",
136
+ settings.modalImageRegistrySecret ?? "",
137
+ settings.modalEnvironment ?? "",
138
+ ].join("|");
139
+ }
140
+
141
+ /**
142
+ * Resolve + cache the private-registry Modal image. No-op unless BOTH
143
+ * `modalImageRef` and `modalImageRegistrySecret` are set. Memoized per
144
+ * (imageRef, secret, environment) so it runs once per worker process. Awaited at
145
+ * worker boot for the deployment-global image and at turn time for pack-scoped
146
+ * images BEFORE the first sandbox using that ref is created; `build` then reads the
147
+ * resolved image and otherwise falls back to the public `fromTag` path.
148
+ */
149
+ export async function ensureModalRegistryImage(
150
+ settings: Settings,
151
+ loadModal: ModalModuleLoader = defaultModalLoader,
152
+ ): Promise<void> {
153
+ if (!settings.modalImageRegistrySecret || !settings.modalImageRef) {
154
+ return;
155
+ }
156
+ const key = registryImageCacheKey(settings);
157
+ if (resolvedRegistryImages.has(key)) {
158
+ return;
159
+ }
160
+ let pending = inFlightRegistryImages.get(key);
161
+ if (!pending) {
162
+ pending = (async () => {
163
+ const modal = await loadModal();
164
+ const client = new modal.ModalClient(modalClientOptions(settings));
165
+ // Resolve the Secret via the AUTHENTICATED client (client.secrets.fromName),
166
+ // NOT the static `modal.Secret.fromName`, which resolves against
167
+ // `getDefaultClient()` — i.e. the standard MODAL_TOKEN_ID/MODAL_TOKEN_SECRET env
168
+ // or ~/.modal.toml — and so would throw "Profile is missing token_id" in any host
169
+ // that supplies the token only through OpenGeni settings (OPENGENI_MODAL_TOKEN_ID).
170
+ const secret = await client.secrets.fromName(
171
+ settings.modalImageRegistrySecret!,
172
+ settings.modalEnvironment ? { environment: settings.modalEnvironment } : undefined,
173
+ );
174
+ // fromRegistry is synchronous and returns a lazy image definition (built
175
+ // server-side at sandbox create); the resolved secretId travels with it.
176
+ const image = client.images.fromRegistry(settings.modalImageRef!, secret);
177
+ resolvedRegistryImages.set(key, image);
178
+ })().finally(() => {
179
+ inFlightRegistryImages.delete(key);
180
+ });
181
+ inFlightRegistryImages.set(key, pending);
182
+ }
183
+ await pending;
184
+ }
185
+
186
+ /** The resolved private-registry image for these settings, or undefined if none. */
187
+ function cachedModalRegistryImage(settings: Settings): unknown | undefined {
188
+ if (!settings.modalImageRegistrySecret || !settings.modalImageRef) {
189
+ return undefined;
190
+ }
191
+ return resolvedRegistryImages.get(registryImageCacheKey(settings));
192
+ }
193
+
194
+ /**
195
+ * Choose the image selector for a Modal sandbox client from settings. Returns:
196
+ * - `fromImage(resolved)` when a private-registry secret is configured AND the
197
+ * image has been resolved (ensureModalRegistryImage ran before create);
198
+ * - `fromTag(modalImageRef)` for the public path (no secret, or cold cache — the
199
+ * resume/attach paths never pull an image so the tag branch is harmless there);
200
+ * - `undefined` when no image ref is set (Modal uses its default image).
201
+ * Exported for unit tests.
202
+ */
203
+ export function resolveModalImageSelector(settings: Settings): ModalImageSelector | undefined {
204
+ if (!settings.modalImageRef) {
205
+ return undefined;
206
+ }
207
+ const registryImage = cachedModalRegistryImage(settings);
208
+ return registryImage
209
+ ? ModalImageSelector.fromImage(registryImage as Parameters<typeof ModalImageSelector.fromImage>[0])
210
+ : ModalImageSelector.fromTag(settings.modalImageRef);
211
+ }
212
+
213
+ /** Test-only: clear the resolved/in-flight image caches. */
214
+ export function __resetModalRegistryImageCacheForTest(): void {
215
+ resolvedRegistryImages.clear();
216
+ inFlightRegistryImages.clear();
217
+ }
218
+
109
219
  function modalClientOptions(settings: Settings): ConstructorParameters<ModalModule["ModalClient"]>[0] {
110
220
  return {
111
221
  ...(settings.modalTokenId ? { tokenId: settings.modalTokenId } : {}),
@@ -0,0 +1,15 @@
1
+ // The legible "screenshot omitted" card that image-history substitutes for an
2
+ // ELIDED `computer_call_output` screenshot. The hosted computer tool requires
3
+ // `computer_call_output.output.image_url` to remain a syntactically valid URL;
4
+ // the text placeholder is valid for tool-result text paths but 400s this image
5
+ // channel once older screenshots are elided.
6
+ //
7
+ // This is a valid 8-bit RGBA PNG (signature + chunk CRCs emitted by the
8
+ // dependency-free generator) rendered from a fixed message:
9
+ // SCREENSHOT OMITTED
10
+ // EARLIER FRAME ELIDED
11
+ // TO SAVE CONTEXT.
12
+ //
13
+ // Regenerate with: `bun run scripts/gen-screenshot-omitted-card.mjs`
14
+ export const SCREENSHOT_OMITTED_IMAGE_DATA_URL =
15
+ "data:image/png;base64,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";