@opengeni/runtime 0.13.0 → 0.13.4

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/index.js CHANGED
@@ -29,6 +29,9 @@ import {
29
29
  REPOSITORY_DISCOVERY_LIMIT,
30
30
  RecordingError,
31
31
  RecordingUnavailableError,
32
+ RoutingBackendRecoveryRequiredError,
33
+ RoutingMutationOutcomeUnknownError,
34
+ RoutingRetainedProcessNotFoundError,
32
35
  RoutingSandboxSession,
33
36
  RoutingUnsupportedError,
34
37
  RunCredentialValidationError,
@@ -48,7 +51,9 @@ import {
48
51
  STREAM_TOKEN_DEFAULT_TTL_SECONDS,
49
52
  SandboxChannelAService,
50
53
  SandboxConfigError,
54
+ SandboxExecReadinessError,
51
55
  SandboxProviderUnavailableError,
56
+ SandboxReplacementProviderStateError,
52
57
  SandboxResumeStateUnavailableError,
53
58
  SelfhostedControlError,
54
59
  SelfhostedSandboxClient,
@@ -60,6 +65,9 @@ import {
60
65
  TerminalServerError,
61
66
  TerminalServerUnsupportedError,
62
67
  ToolspaceTokenPathError,
68
+ TurnSandboxCommandCancelledError,
69
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
70
+ WorkspaceArchiveIntegrityError,
63
71
  agentErrorToControlError,
64
72
  assertDescriptorRegistryInvariants,
65
73
  assertProviderRegistryInvariants,
@@ -69,12 +77,15 @@ import {
69
77
  buildSelfhostedBackendSession,
70
78
  buildStreamUrl,
71
79
  buildTerminalServerScript,
80
+ captureVerifiedWorkspaceArchive,
81
+ classifyProviderSandboxFailure,
72
82
  clearRunCredentials,
73
83
  clearRunCredentialsForAttempt,
74
84
  collectSandboxEnvironment,
75
85
  contentTypeForCodec,
76
86
  createSandboxClient,
77
87
  createSandboxClientForBackend,
88
+ createTurnToolCancellationController,
78
89
  decideSelfhostedRetry,
79
90
  decodeModalSnapshotId,
80
91
  defaultSelfhostedRetryClock,
@@ -91,9 +102,13 @@ import {
91
102
  execDeadlineHint,
92
103
  exposeStreamPort,
93
104
  extForCodec,
105
+ fingerprintSandboxWorkspace,
106
+ hasPersistableSandboxProviderIdentity,
94
107
  inspectRecordingArtifact,
95
108
  isExecSessionLostBanner,
109
+ isProviderSandboxGoneDuringRoutedOperation,
96
110
  isProviderSandboxNotFoundError,
111
+ isProviderSandboxTransientError,
97
112
  isSelfhostedProviderNotFoundError,
98
113
  localDirLazySkillSource,
99
114
  makeActiveBackendResolver,
@@ -115,10 +130,13 @@ import {
115
130
  parseNumstatZ,
116
131
  parsePorcelainV2,
117
132
  parseUnifiedPatch,
133
+ parseWorkspaceArchiveDescriptor,
118
134
  payloadTooLargeMessage,
135
+ readVerifiedWorkspaceArchive,
119
136
  readWorkspaceArchiveFromEnvelopeSessionState,
120
137
  recordingStorageKey,
121
138
  renderSelfhostedFault,
139
+ requirePersistableReplacementSandboxEnvelope,
122
140
  restoredSandboxSessionStateFromEntry,
123
141
  runCredentialPointerFile,
124
142
  runCredentialRoot,
@@ -130,6 +148,7 @@ import {
130
148
  selfhostedLiveness,
131
149
  selfhostedRetryBackoffMs,
132
150
  serializeEstablishedSandboxEnvelope,
151
+ serializeReplacementSandboxEnvelope,
133
152
  setSelfhostedApplyDiff,
134
153
  startRecording,
135
154
  stopRecording,
@@ -146,14 +165,17 @@ import {
146
165
  toolspaceTokenFileForSession,
147
166
  toolspaceTokenFileFromEnvironment,
148
167
  uploadRecordingArtifact,
168
+ verifyRestoredWorkspace,
169
+ verifySandboxExecReadiness,
149
170
  verifyStreamToken,
150
171
  withRunCredentialEnvironment,
151
172
  withRunCredentialsClient,
152
173
  withRunCredentialsSession,
153
174
  withToolspaceTokenClient,
154
175
  withToolspaceTokenEnvironment,
155
- withToolspaceTokenSession
156
- } from "./chunk-65UDUFUO.js";
176
+ withToolspaceTokenSession,
177
+ wrapCapabilityToolsForTurnCancellation
178
+ } from "./chunk-JGUQSAG5.js";
157
179
  import {
158
180
  getSkillLibraryEntry,
159
181
  isSkillLibraryEntryId,
@@ -172,9 +194,10 @@ import {
172
194
  boundedParallelMap,
173
195
  cancelMcpResponseBody,
174
196
  guardedMcpFetch,
197
+ mcpOuterConnectTimeoutMs,
175
198
  mcpSerializedSizeBytes,
176
199
  undiciFetch
177
- } from "./chunk-HAN5D27J.js";
200
+ } from "./chunk-QYXOVIUK.js";
178
201
  import {
179
202
  MemorySession,
180
203
  connectMcpServers
@@ -5110,7 +5133,7 @@ var OpenAITracingExporter = class {
5110
5133
  const payloadItems = groupedItems.map((entry) => entry.toJSON()).filter((item) => !!item).map((item) => isRecord5(item) ? sanitizePayloadItemForTracesIngest(item) : item);
5111
5134
  const payload = { data: payloadItems };
5112
5135
  let attempts = 0;
5113
- let delay2 = this.#options.baseDelay;
5136
+ let delay = this.#options.baseDelay;
5114
5137
  while (attempts < this.#options.maxRetries) {
5115
5138
  try {
5116
5139
  const response = await fetch(this.#options.endpoint, {
@@ -5144,13 +5167,13 @@ var OpenAITracingExporter = class {
5144
5167
  if (attempts >= this.#options.maxRetries) {
5145
5168
  break;
5146
5169
  }
5147
- const sleepTime = delay2 + Math.random() * 0.1 * delay2;
5170
+ const sleepTime = delay + Math.random() * 0.1 * delay;
5148
5171
  const shouldContinue = await sleepWithAbort(sleepTime, signal);
5149
5172
  if (!shouldContinue) {
5150
5173
  logger_default.error("Tracing: request aborted");
5151
5174
  break;
5152
5175
  }
5153
- delay2 = Math.min(delay2 * 2, this.#options.maxDelay);
5176
+ delay = Math.min(delay * 2, this.#options.maxDelay);
5154
5177
  }
5155
5178
  if (attempts >= this.#options.maxRetries) {
5156
5179
  logger_default.error(`Tracing: failed to export traces after ${this.#options.maxRetries} attempts`);
@@ -7601,668 +7624,6 @@ function safeStringify(value) {
7601
7624
  }
7602
7625
  }
7603
7626
 
7604
- // src/sandbox/turn-tool-cancellation.ts
7605
- var TURN_EXEC_YIELD_MS = 250;
7606
- var TURN_WRITE_YIELD_MS = 250;
7607
- var SHELL_HELPER_YIELD_MS = 1e3;
7608
- var SHELL_GRACEFUL_POLLS = 2;
7609
- var SHELL_POLL_MS = 100;
7610
- var SHELL_MARKER_DIR = "/tmp/opengeni-turn-shell";
7611
- var delay = async (milliseconds) => await new Promise((resolve) => setTimeout(resolve, milliseconds));
7612
- var TurnSandboxCommandCancelledError = class extends Error {
7613
- name = "TurnSandboxCommandCancelledError";
7614
- constructor(reason) {
7615
- super(
7616
- reason instanceof Error ? reason.message : String(reason ?? "Turn sandbox tools were cancelled"),
7617
- {
7618
- ...reason instanceof Error ? { cause: reason } : {}
7619
- }
7620
- );
7621
- }
7622
- };
7623
- function cancellationError(reason) {
7624
- return new TurnSandboxCommandCancelledError(reason);
7625
- }
7626
- function parsedObject(input) {
7627
- try {
7628
- const parsed = JSON.parse(input);
7629
- return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
7630
- } catch {
7631
- return null;
7632
- }
7633
- }
7634
- function cappedYield(value, cap) {
7635
- return typeof value === "number" && Number.isInteger(value) && value >= 0 ? Math.min(value, cap) : cap;
7636
- }
7637
- function execOutput(raw) {
7638
- const marker = "\nOutput:\n";
7639
- const index = raw.indexOf(marker);
7640
- return index >= 0 ? raw.slice(index + marker.length) : "";
7641
- }
7642
- function nativeCommandBanner(result) {
7643
- if (typeof result === "string") return result;
7644
- if (!result || typeof result !== "object") {
7645
- throw new Error("Sandbox command returned an invalid result");
7646
- }
7647
- const value = result;
7648
- const sessionId = typeof value.sessionId === "number" ? value.sessionId : typeof value.session_id === "number" ? value.session_id : null;
7649
- const exitCode = typeof value.exitCode === "number" ? value.exitCode : typeof value.exit_code === "number" ? value.exit_code : null;
7650
- const output = [value.output, value.stderr, value.stdout].filter(
7651
- (candidate) => typeof candidate === "string" && candidate.length > 0
7652
- ).join("\n");
7653
- if (sessionId !== null) {
7654
- return `Process running with session ID ${sessionId}
7655
-
7656
- Output:
7657
- ${output}`;
7658
- }
7659
- if (exitCode !== null) {
7660
- return `Process exited with code ${exitCode}
7661
-
7662
- Output:
7663
- ${output}`;
7664
- }
7665
- throw new Error("Sandbox command did not report a session id or exit code");
7666
- }
7667
- function completedCommandBanner(exitCode, output) {
7668
- return `Process exited with code ${exitCode}
7669
-
7670
- Output:
7671
- ${output}`;
7672
- }
7673
- function appendBoundedOutput(current, chunk, maxOutputTokens) {
7674
- const maxChars = Math.max(512, maxOutputTokens * 4);
7675
- const combined = current + chunk;
7676
- return combined.length <= maxChars ? combined : combined.slice(-maxChars);
7677
- }
7678
- function singleQuote(value) {
7679
- return `'${value.replaceAll("'", `'"'"'`)}'`;
7680
- }
7681
- function shellMarkerPath(token) {
7682
- return `${SHELL_MARKER_DIR}/${token}`;
7683
- }
7684
- function cancellableGroupLeaderCommand(command, markerPath) {
7685
- const marker = singleQuote(markerPath);
7686
- const markerDir = singleQuote(SHELL_MARKER_DIR);
7687
- return [
7688
- `__opengeni_marker=${marker}`,
7689
- "umask 077",
7690
- `command mkdir -p ${markerDir} || exit 125`,
7691
- '__opengeni_pid="$$"',
7692
- `__opengeni_pgid="$(command ps -o pgid= -p "$__opengeni_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7693
- 'case "$__opengeni_pid:$__opengeni_pgid" in *[!0-9:]*|*:|:*) exit 125 ;; esac',
7694
- '[ "$__opengeni_pid" -gt 1 ] && [ "$__opengeni_pgid" -gt 1 ] || exit 125',
7695
- // Never signal a provider/container-wide process group. A cancellable PTY
7696
- // command must be its group's leader; fail the command closed otherwise.
7697
- '[ "$__opengeni_pid" = "$__opengeni_pgid" ] || exit 125',
7698
- `command printf '%s %s\\n' "$__opengeni_pid" "$__opengeni_pgid" > "$__opengeni_marker" || exit 125`,
7699
- `trap 'command rm -f "$__opengeni_marker"' EXIT`,
7700
- "(",
7701
- command,
7702
- ")",
7703
- "__opengeni_status=$?",
7704
- 'exit "$__opengeni_status"'
7705
- ].join("\n");
7706
- }
7707
- function cancellableShellCommand(command, markerPath) {
7708
- const groupLeaderCommand = cancellableGroupLeaderCommand(command, markerPath);
7709
- return [
7710
- '__opengeni_outer_pid="$$"',
7711
- `__opengeni_outer_pgid="$(command ps -o pgid= -p "$__opengeni_outer_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7712
- 'case "$__opengeni_outer_pid:$__opengeni_outer_pgid" in *[!0-9:]*|*:|:*) exit 125 ;; esac',
7713
- 'if [ "$__opengeni_outer_pid" != "$__opengeni_outer_pgid" ]; then',
7714
- ' __opengeni_setsid="$(command -v setsid 2>/dev/null)"',
7715
- ' [ -n "$__opengeni_setsid" ] || exit 125',
7716
- ` exec "$__opengeni_setsid" /bin/sh -c ${singleQuote(groupLeaderCommand)}`,
7717
- "fi",
7718
- groupLeaderCommand
7719
- ].join("\n");
7720
- }
7721
- function shellHelperInput(command) {
7722
- return JSON.stringify({
7723
- cmd: command,
7724
- login: false,
7725
- tty: false,
7726
- yield_time_ms: SHELL_HELPER_YIELD_MS,
7727
- max_output_tokens: 128
7728
- });
7729
- }
7730
- function safeProcessIdentity(raw) {
7731
- if (parseExecBannerExitCode(raw) !== 0) return null;
7732
- const match = execOutput(raw).trim().match(/^(\d+)\s+(\d+)$/);
7733
- if (!match) return null;
7734
- const pid = Number.parseInt(match[1], 10);
7735
- const processGroupId = Number.parseInt(match[2], 10);
7736
- return Number.isSafeInteger(pid) && Number.isSafeInteger(processGroupId) && pid > 1 && processGroupId > 1 ? { pid, processGroupId } : null;
7737
- }
7738
- function identityGuardScript(state, body, missingIdentityExitCode) {
7739
- const identity = state.identity;
7740
- if (!identity || !state.token) return `exit ${missingIdentityExitCode}`;
7741
- const token = singleQuote(state.token);
7742
- return [
7743
- `__opengeni_pid=${identity.pid}`,
7744
- `__opengeni_pgid=${identity.processGroupId}`,
7745
- `__opengeni_token=${token}`,
7746
- // The randomized token lands late in the wrapped command line. Procps
7747
- // otherwise truncates `args` to the terminal width, which can make a live
7748
- // group look stale and open the quiescence fence without signalling it.
7749
- '__opengeni_args="$(command ps -ww -o args= -p "$__opengeni_pid" 2>/dev/null)"',
7750
- 'case "$__opengeni_args" in *"$__opengeni_token"*) ;; *) exit 0 ;; esac',
7751
- `__opengeni_live_pgid="$(command ps -o pgid= -p "$__opengeni_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7752
- '[ "$__opengeni_live_pgid" = "$__opengeni_pgid" ] || exit 0',
7753
- body
7754
- ].join("\n");
7755
- }
7756
- function wrapComputer(computer, track) {
7757
- const wrapped = Object.create(Object.getPrototypeOf(computer));
7758
- Object.assign(wrapped, computer);
7759
- for (const method of [
7760
- "initRun",
7761
- "screenshot",
7762
- "click",
7763
- "doubleClick",
7764
- "scroll",
7765
- "type",
7766
- "wait",
7767
- "move",
7768
- "keypress",
7769
- "drag"
7770
- ]) {
7771
- const original = computer[method];
7772
- if (typeof original !== "function") continue;
7773
- wrapped[method] = (...args) => track(async () => await original.apply(computer, args));
7774
- }
7775
- return wrapped;
7776
- }
7777
- var TurnToolCancellationControllerImpl = class {
7778
- cancelled = false;
7779
- reason;
7780
- inFlight = /* @__PURE__ */ new Set();
7781
- shellSessions = /* @__PURE__ */ new Map();
7782
- remoteExecs = /* @__PURE__ */ new Set();
7783
- rawExecInvoke = null;
7784
- rawWriteInvoke = null;
7785
- drainPromise = null;
7786
- signal;
7787
- onAbort = () => this.cancel(this.signal?.reason);
7788
- constructor(signal) {
7789
- this.signal = signal;
7790
- if (signal?.aborted) {
7791
- this.cancel(signal.reason);
7792
- } else {
7793
- signal?.addEventListener("abort", this.onAbort, { once: true });
7794
- }
7795
- }
7796
- cancel(reason) {
7797
- if (!this.cancelled) {
7798
- this.cancelled = true;
7799
- this.reason = reason;
7800
- this.signal?.removeEventListener("abort", this.onAbort);
7801
- }
7802
- void this.ensureDrain().catch(() => void 0);
7803
- }
7804
- async waitForQuiescence() {
7805
- this.cancel(this.reason);
7806
- await this.ensureDrain();
7807
- }
7808
- runSandboxCommand(session, args) {
7809
- return this.track(async () => {
7810
- if (!session.exec && !session.execCommand) {
7811
- throw new Error("Sandbox session does not support command execution");
7812
- }
7813
- const lifecycleRunContext = {};
7814
- const invokeExec = async (_runContext, input) => {
7815
- const parsed = parsedObject(input);
7816
- if (!parsed || typeof parsed.cmd !== "string") {
7817
- throw new Error("Sandbox lifecycle command input was invalid");
7818
- }
7819
- const commandArgs = {
7820
- cmd: parsed.cmd,
7821
- ...typeof parsed.workdir === "string" ? { workdir: parsed.workdir } : {},
7822
- ...typeof parsed.shell === "string" ? { shell: parsed.shell } : {},
7823
- ...typeof parsed.login === "boolean" ? { login: parsed.login } : {},
7824
- ...typeof parsed.tty === "boolean" ? { tty: parsed.tty } : {},
7825
- ...typeof parsed.yield_time_ms === "number" ? { yieldTimeMs: parsed.yield_time_ms } : {},
7826
- ...typeof parsed.max_output_tokens === "number" ? { maxOutputTokens: parsed.max_output_tokens } : {},
7827
- ...typeof parsed.run_as === "string" ? { runAs: parsed.run_as } : {}
7828
- };
7829
- const result = session.supportsPty?.() !== false && session.execCommand ? await session.execCommand(commandArgs) : session.exec ? await session.exec(commandArgs) : await session.execCommand(commandArgs);
7830
- return nativeCommandBanner(result);
7831
- };
7832
- const invokeWrite = session.writeStdin ? async (_runContext, input) => {
7833
- const parsed = parsedObject(input);
7834
- if (!parsed || typeof parsed.session_id !== "number") {
7835
- throw new Error("Sandbox lifecycle stdin input was invalid");
7836
- }
7837
- return await session.writeStdin({
7838
- sessionId: parsed.session_id,
7839
- ...typeof parsed.chars === "string" ? { chars: parsed.chars } : {},
7840
- ...typeof parsed.yield_time_ms === "number" ? { yieldTimeMs: parsed.yield_time_ms } : {},
7841
- ...typeof parsed.max_output_tokens === "number" ? { maxOutputTokens: parsed.max_output_tokens } : {}
7842
- });
7843
- } : null;
7844
- const correlationId = `turn_lifecycle_${crypto.randomUUID()}`;
7845
- const useRemoteOpCancellation = Boolean(session.cancelExecCommand) && session.supportsPty?.() === false;
7846
- const remoteExec = session.cancelExecCommand && useRemoteOpCancellation ? this.registerRemoteExec(
7847
- { cancelExecCommand: session.cancelExecCommand.bind(session) },
7848
- `${sanitizeOpIdToken(correlationId)}:0`
7849
- ) : null;
7850
- const markerPath = shellMarkerPath(crypto.randomUUID());
7851
- const commandInput = JSON.stringify({
7852
- cmd: useRemoteOpCancellation ? args.cmd : cancellableShellCommand(args.cmd, markerPath),
7853
- ...args.workdir ? { workdir: args.workdir } : {},
7854
- ...args.shell ? { shell: args.shell } : {},
7855
- ...args.login !== void 0 ? { login: args.login } : {},
7856
- ...args.runAs ? { run_as: args.runAs } : {},
7857
- tty: useRemoteOpCancellation ? args.tty : true,
7858
- yield_time_ms: useRemoteOpCancellation ? args.yieldTimeMs : cappedYield(args.yieldTimeMs, TURN_EXEC_YIELD_MS),
7859
- ...args.maxOutputTokens !== void 0 ? { max_output_tokens: args.maxOutputTokens } : {}
7860
- });
7861
- let initial;
7862
- try {
7863
- initial = await runWithToolCallCorrelation(
7864
- correlationId,
7865
- async () => await invokeExec(lifecycleRunContext, commandInput, void 0)
7866
- );
7867
- } finally {
7868
- remoteExec?.settle();
7869
- }
7870
- if (typeof initial !== "string" || useRemoteOpCancellation) return initial;
7871
- const sessionId = parseExecBannerSessionId(initial);
7872
- if (sessionId === null) return initial;
7873
- if (!invokeWrite) {
7874
- throw new Error("Sandbox lifecycle command yielded without stdin support");
7875
- }
7876
- const token = markerPath.slice(markerPath.lastIndexOf("/") + 1);
7877
- const state = {
7878
- sessionId,
7879
- markerPath,
7880
- token,
7881
- runContext: lifecycleRunContext,
7882
- execInvoke: invokeExec,
7883
- writeInvoke: invokeWrite,
7884
- identity: null,
7885
- identityValidated: false,
7886
- cancellation: null
7887
- };
7888
- this.shellSessions.set(sessionId, state);
7889
- const maxOutputTokens = args.maxOutputTokens ?? 2e4;
7890
- const initialOutput = execOutput(initial);
7891
- let output = initialOutput ? appendBoundedOutput("", initialOutput, maxOutputTokens) : "";
7892
- while (true) {
7893
- if (this.cancelled) throw cancellationError(this.reason);
7894
- const next = await invokeWrite(
7895
- lifecycleRunContext,
7896
- JSON.stringify({
7897
- session_id: sessionId,
7898
- chars: "",
7899
- yield_time_ms: TURN_WRITE_YIELD_MS,
7900
- max_output_tokens: maxOutputTokens
7901
- }),
7902
- void 0
7903
- );
7904
- if (typeof next !== "string") {
7905
- throw new Error("Sandbox lifecycle stdin returned an invalid result");
7906
- }
7907
- const nextOutput = execOutput(next);
7908
- if (nextOutput) output = appendBoundedOutput(output, nextOutput, maxOutputTokens);
7909
- const exitCode = parseExecBannerExitCode(next);
7910
- if (exitCode !== null) {
7911
- this.shellSessions.delete(sessionId);
7912
- return completedCommandBanner(exitCode, output);
7913
- }
7914
- await delay(SHELL_POLL_MS);
7915
- }
7916
- });
7917
- }
7918
- wrapTools(tools, session) {
7919
- for (const tool2 of tools) {
7920
- if (tool2.type !== "function") continue;
7921
- if (tool2.name === "exec_command") this.rawExecInvoke = tool2.invoke;
7922
- if (tool2.name === "write_stdin") this.rawWriteInvoke = tool2.invoke;
7923
- }
7924
- return tools.map((tool2) => {
7925
- if (tool2.type === "function") return this.wrapFunctionTool(tool2, session);
7926
- if (tool2.type === "apply_patch") return this.wrapApplyPatchTool(tool2);
7927
- if (tool2.type === "computer") return this.wrapComputerTool(tool2);
7928
- return tool2;
7929
- });
7930
- }
7931
- wrapFunctionTool(tool2, cancellationSession) {
7932
- if (tool2.name === "exec_command") {
7933
- return {
7934
- ...tool2,
7935
- invoke: (runContext, input, details) => this.track(async () => {
7936
- const parsed = parsedObject(input);
7937
- if (!parsed || typeof parsed.cmd !== "string") {
7938
- return await this.correlatedInvoke(tool2.invoke, runContext, input, details);
7939
- }
7940
- const token = crypto.randomUUID();
7941
- const markerPath = shellMarkerPath(token);
7942
- const correlationId = details?.toolCall?.callId ?? `turn_exec_${token}`;
7943
- const cancelExecCommand = cancellationSession?.cancelExecCommand;
7944
- const useRemoteOpCancellation = Boolean(cancelExecCommand) && cancellationSession?.supportsPty?.() === false;
7945
- const remoteExec = cancelExecCommand && useRemoteOpCancellation ? this.registerRemoteExec(
7946
- { cancelExecCommand: cancelExecCommand.bind(cancellationSession) },
7947
- `${sanitizeOpIdToken(correlationId)}:0`
7948
- ) : null;
7949
- const cancellableInput = useRemoteOpCancellation ? input : JSON.stringify({
7950
- ...parsed,
7951
- cmd: cancellableShellCommand(parsed.cmd, markerPath),
7952
- // The SDK's only provider-neutral live-process interrupt is a PTY.
7953
- // The process marker provides TERM/KILL escalation when Ctrl-C is
7954
- // ignored; the short yield exposes the provider session promptly.
7955
- tty: true,
7956
- yield_time_ms: cappedYield(parsed.yield_time_ms, TURN_EXEC_YIELD_MS)
7957
- });
7958
- let output;
7959
- try {
7960
- output = await runWithToolCallCorrelation(
7961
- correlationId,
7962
- async () => await tool2.invoke(runContext, cancellableInput, details)
7963
- );
7964
- } finally {
7965
- remoteExec?.settle();
7966
- }
7967
- if (typeof output !== "string") return output;
7968
- const sessionId = useRemoteOpCancellation ? null : parseExecBannerSessionId(output);
7969
- if (sessionId !== null) {
7970
- this.shellSessions.set(sessionId, {
7971
- sessionId,
7972
- markerPath,
7973
- token,
7974
- runContext,
7975
- execInvoke: tool2.invoke,
7976
- writeInvoke: this.rawWriteInvoke,
7977
- identity: null,
7978
- identityValidated: false,
7979
- cancellation: null
7980
- });
7981
- }
7982
- return output;
7983
- })
7984
- };
7985
- }
7986
- if (tool2.name === "write_stdin") {
7987
- return {
7988
- ...tool2,
7989
- invoke: (runContext, input, details) => this.track(async () => {
7990
- const parsed = parsedObject(input);
7991
- const sessionId = parsed && typeof parsed.session_id === "number" && Number.isSafeInteger(parsed.session_id) ? parsed.session_id : null;
7992
- const cappedInput = parsed ? JSON.stringify({
7993
- ...parsed,
7994
- yield_time_ms: cappedYield(parsed.yield_time_ms, TURN_WRITE_YIELD_MS)
7995
- }) : input;
7996
- const output = await tool2.invoke(runContext, cappedInput, details);
7997
- if (sessionId !== null && typeof output === "string") {
7998
- if (isExecSessionLostBanner(output, sessionId)) {
7999
- this.shellSessions.delete(sessionId);
8000
- } else if (parseExecBannerSessionId(output) === sessionId) {
8001
- if (!this.shellSessions.has(sessionId)) {
8002
- this.shellSessions.set(sessionId, {
8003
- sessionId,
8004
- markerPath: null,
8005
- token: null,
8006
- runContext,
8007
- execInvoke: this.rawExecInvoke ?? tool2.invoke,
8008
- writeInvoke: tool2.invoke,
8009
- identity: null,
8010
- identityValidated: false,
8011
- cancellation: null
8012
- });
8013
- }
8014
- } else if (parseExecBannerExitCode(output) !== null) {
8015
- this.shellSessions.delete(sessionId);
8016
- }
8017
- }
8018
- return output;
8019
- })
8020
- };
8021
- }
8022
- return {
8023
- ...tool2,
8024
- invoke: (runContext, input, details) => this.track(async () => await tool2.invoke(runContext, input, details))
8025
- };
8026
- }
8027
- wrapApplyPatchTool(tool2) {
8028
- const editor = tool2.editor;
8029
- const wrappedEditor = {
8030
- createFile: (operation, context) => this.track(async () => await editor.createFile(operation, context)),
8031
- updateFile: (operation, context) => this.track(async () => await editor.updateFile(operation, context)),
8032
- deleteFile: (operation, context) => this.track(async () => await editor.deleteFile(operation, context))
8033
- };
8034
- return { ...tool2, editor: wrappedEditor };
8035
- }
8036
- wrapComputerTool(tool2) {
8037
- const computer = tool2.computer;
8038
- if (typeof computer === "function") {
8039
- return {
8040
- ...tool2,
8041
- computer: async (...args) => wrapComputer(
8042
- await this.track(async () => await computer(...args)),
8043
- (operation) => this.track(operation)
8044
- )
8045
- };
8046
- }
8047
- if ("create" in computer && typeof computer.create === "function") {
8048
- return {
8049
- ...tool2,
8050
- computer: {
8051
- ...computer,
8052
- create: async (...args) => wrapComputer(
8053
- await this.track(async () => await computer.create(...args)),
8054
- (operation) => this.track(operation)
8055
- )
8056
- }
8057
- };
8058
- }
8059
- return {
8060
- ...tool2,
8061
- computer: wrapComputer(computer, (operation) => this.track(operation))
8062
- };
8063
- }
8064
- async correlatedInvoke(invoke, runContext, input, details) {
8065
- const callId = details?.toolCall?.callId;
8066
- return callId ? await runWithToolCallCorrelation(
8067
- callId,
8068
- async () => await invoke(runContext, input, details)
8069
- ) : await invoke(runContext, input, details);
8070
- }
8071
- track(operation) {
8072
- if (this.cancelled) return Promise.reject(cancellationError(this.reason));
8073
- const promise = Promise.resolve().then(async () => {
8074
- if (this.cancelled) throw cancellationError(this.reason);
8075
- return await operation();
8076
- });
8077
- this.inFlight.add(promise);
8078
- void promise.finally(() => {
8079
- this.inFlight.delete(promise);
8080
- }).catch(() => void 0);
8081
- return promise;
8082
- }
8083
- ensureDrain() {
8084
- this.drainPromise ??= this.drain();
8085
- return this.drainPromise;
8086
- }
8087
- async drain() {
8088
- while (true) {
8089
- const remoteCancellations = [...this.remoteExecs].map(async (exec) => await exec.cancel());
8090
- const inFlight = [...this.inFlight];
8091
- if (inFlight.length > 0 || remoteCancellations.length > 0) {
8092
- await Promise.allSettled([...inFlight, ...remoteCancellations]);
8093
- }
8094
- const sessions = [...this.shellSessions.values()];
8095
- if (sessions.length > 0) {
8096
- await Promise.all(sessions.map(async (session) => await this.cancelShellSession(session)));
8097
- }
8098
- if (this.inFlight.size === 0 && this.shellSessions.size === 0 && this.remoteExecs.size === 0)
8099
- return;
8100
- }
8101
- }
8102
- registerRemoteExec(session, opId) {
8103
- let resolveSettled;
8104
- const settledPromise = new Promise((resolve) => {
8105
- resolveSettled = resolve;
8106
- });
8107
- let cancellation = null;
8108
- const entry = {
8109
- settled: false,
8110
- settledPromise,
8111
- settle: () => {
8112
- if (entry.settled) return;
8113
- entry.settled = true;
8114
- resolveSettled();
8115
- this.remoteExecs.delete(entry);
8116
- },
8117
- cancel: () => {
8118
- cancellation ??= (async () => {
8119
- while (!entry.settled) {
8120
- const attempt = session.cancelExecCommand(opId).catch(() => false);
8121
- await Promise.race([attempt, entry.settledPromise]);
8122
- await Promise.race([entry.settledPromise, delay(SHELL_POLL_MS)]);
8123
- }
8124
- })();
8125
- return cancellation;
8126
- }
8127
- };
8128
- this.remoteExecs.add(entry);
8129
- return entry;
8130
- }
8131
- cancelShellSession(state) {
8132
- state.cancellation ??= this.cancelShellSessionOnce(state).finally(() => {
8133
- state.cancellation = null;
8134
- });
8135
- return state.cancellation;
8136
- }
8137
- async cancelShellSessionOnce(state) {
8138
- if (!state.markerPath) {
8139
- while (true) {
8140
- if (state.writeInvoke && await this.rawWrite(state, "", SHELL_POLL_MS)) {
8141
- this.shellSessions.delete(state.sessionId);
8142
- return;
8143
- }
8144
- await delay(SHELL_POLL_MS);
8145
- }
8146
- }
8147
- while (!state.identity) {
8148
- state.identity = await this.readIdentity(state);
8149
- if (state.identity) break;
8150
- if (state.writeInvoke) {
8151
- const completed = await this.rawWrite(state, "", SHELL_POLL_MS);
8152
- if (completed) {
8153
- this.shellSessions.delete(state.sessionId);
8154
- return;
8155
- }
8156
- }
8157
- await delay(SHELL_POLL_MS);
8158
- }
8159
- if (!await this.identityAlive(state)) {
8160
- this.shellSessions.delete(state.sessionId);
8161
- return;
8162
- }
8163
- if (state.writeInvoke) {
8164
- await this.rawWrite(state, "", SHELL_POLL_MS);
8165
- if (!await this.identityAlive(state)) {
8166
- this.shellSessions.delete(state.sessionId);
8167
- return;
8168
- }
8169
- }
8170
- await this.signalIdentity(state, "TERM");
8171
- for (let attempt = 0; attempt < SHELL_GRACEFUL_POLLS; attempt++) {
8172
- if (state.writeInvoke) await this.rawWrite(state, "", SHELL_POLL_MS);
8173
- if (!await this.identityAlive(state)) {
8174
- this.shellSessions.delete(state.sessionId);
8175
- return;
8176
- }
8177
- }
8178
- await this.signalIdentity(state, "KILL");
8179
- while (await this.identityAlive(state)) {
8180
- if (state.writeInvoke) await this.rawWrite(state, "", SHELL_POLL_MS);
8181
- await this.signalIdentity(state, "KILL");
8182
- await delay(SHELL_POLL_MS);
8183
- }
8184
- this.shellSessions.delete(state.sessionId);
8185
- }
8186
- async rawWrite(state, chars, yieldTimeMs) {
8187
- if (!state.writeInvoke) return false;
8188
- try {
8189
- const output = await state.writeInvoke(
8190
- state.runContext,
8191
- JSON.stringify({
8192
- session_id: state.sessionId,
8193
- chars,
8194
- yield_time_ms: yieldTimeMs,
8195
- max_output_tokens: 128
8196
- }),
8197
- void 0
8198
- );
8199
- if (typeof output !== "string") return false;
8200
- if (isExecSessionLostBanner(output, state.sessionId)) return true;
8201
- if (parseExecBannerSessionId(output) === state.sessionId) return false;
8202
- if (parseExecBannerExitCode(output) !== null) return true;
8203
- } catch {
8204
- }
8205
- return false;
8206
- }
8207
- async readIdentity(state) {
8208
- if (!state.markerPath) return null;
8209
- try {
8210
- const output = await state.execInvoke(
8211
- state.runContext,
8212
- shellHelperInput(`command cat ${singleQuote(state.markerPath)} 2>/dev/null`),
8213
- void 0
8214
- );
8215
- return typeof output === "string" ? safeProcessIdentity(output) : null;
8216
- } catch {
8217
- return null;
8218
- }
8219
- }
8220
- async identityAlive(state) {
8221
- const script = state.identityValidated ? [
8222
- `__opengeni_pgid=${state.identity?.processGroupId ?? 0}`,
8223
- 'command kill -0 "-$__opengeni_pgid" 2>/dev/null && exit 75',
8224
- "exit 0"
8225
- ].join("\n") : identityGuardScript(
8226
- state,
8227
- 'command kill -0 "-$__opengeni_pgid" 2>/dev/null && exit 75\nexit 0',
8228
- 76
8229
- );
8230
- try {
8231
- const output = await state.execInvoke(state.runContext, shellHelperInput(script), void 0);
8232
- if (typeof output !== "string") return true;
8233
- const exitCode = parseExecBannerExitCode(output);
8234
- if (exitCode === 75) {
8235
- state.identityValidated = true;
8236
- return true;
8237
- }
8238
- return exitCode !== 0;
8239
- } catch {
8240
- return true;
8241
- }
8242
- }
8243
- async signalIdentity(state, signal) {
8244
- const script = state.identityValidated ? `command kill -${signal} "-${state.identity?.processGroupId ?? 0}" 2>/dev/null || exit 76` : identityGuardScript(
8245
- state,
8246
- `command kill -${signal} "-$__opengeni_pgid" 2>/dev/null || exit 76
8247
- exit 0`,
8248
- 76
8249
- );
8250
- try {
8251
- await state.execInvoke(state.runContext, shellHelperInput(script), void 0);
8252
- } catch {
8253
- }
8254
- }
8255
- };
8256
- function createTurnToolCancellationController(signal) {
8257
- return new TurnToolCancellationControllerImpl(signal);
8258
- }
8259
- function wrapCapabilityToolsForTurnCancellation(capability, controller) {
8260
- const originalTools = capability.tools;
8261
- capability.tools = function() {
8262
- return controller.wrapTools(originalTools.call(this), this._session);
8263
- };
8264
- }
8265
-
8266
7627
  // src/sandbox-computer.ts
8267
7628
  import {
8268
7629
  KeyAction,
@@ -10320,6 +9681,7 @@ async function connectMcpServersInBatches(servers, options) {
10320
9681
  await connectMcpServers(
10321
9682
  servers.slice(offset, offset + MCP_MAX_CONCURRENT_SERVER_OPERATIONS),
10322
9683
  {
9684
+ ...options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs },
10323
9685
  connectInParallel: true,
10324
9686
  strict: options.strict
10325
9687
  }
@@ -10363,7 +9725,8 @@ async function prepareAgentTools(settings, tools, options = {}) {
10363
9725
  }
10364
9726
  const registry = new Map(settings.mcpServers.map((server) => [server.id, server]));
10365
9727
  const aggregateToolBudget = new McpAggregateToolListBudget();
10366
- const mcpFetchImpl = options.mcpFetchImpl ?? undiciFetch;
9728
+ const useBunNativeFetch = options.mcpFetchImpl === void 0 && !!process.versions.bun;
9729
+ const mcpFetchImpl = options.mcpFetchImpl ?? (useBunNativeFetch ? globalThis.fetch.bind(globalThis) : undiciFetch);
10367
9730
  const servers = await boundedParallelMap(
10368
9731
  tools,
10369
9732
  MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
@@ -10378,7 +9741,10 @@ async function prepareAgentTools(settings, tools, options = {}) {
10378
9741
  const guardedFetch = guardedMcpFetch(
10379
9742
  firstParty ? { ...settings, integrationsAllowPrivateNetworkTargets: true } : settings,
10380
9743
  baseFetch,
10381
- firstParty ? { requireHttpsOutsideLocalTest: false } : {}
9744
+ {
9745
+ ...firstParty ? { requireHttpsOutsideLocalTest: false } : {},
9746
+ ...useBunNativeFetch ? { pinResolvedDestination: false } : {}
9747
+ }
10382
9748
  );
10383
9749
  const fetchImpl = config.connectionRef ? connectionBrokerFetch(guardedFetch, config, options) : firstParty ? firstPartyAuthFetch(guardedFetch, settings, options) : guardedFetch;
10384
9750
  const optional = tool2.optional === true;
@@ -10408,19 +9774,30 @@ async function prepareAgentTools(settings, tools, options = {}) {
10408
9774
  return {
10409
9775
  server,
10410
9776
  bestEffort,
10411
- optional
9777
+ optional,
9778
+ timeoutMs: config.timeoutMs
10412
9779
  };
10413
9780
  }
10414
9781
  );
10415
- const requiredServers = servers.filter((entry) => !entry.bestEffort).map((entry) => entry.server);
10416
- const bestEffortServers = servers.filter((entry) => entry.bestEffort).map((entry) => entry.server);
9782
+ const requiredEntries = servers.filter((entry) => !entry.bestEffort);
9783
+ const bestEffortEntries = servers.filter((entry) => entry.bestEffort);
9784
+ const requiredServers = requiredEntries.map((entry) => entry.server);
9785
+ const bestEffortServers = bestEffortEntries.map((entry) => entry.server);
10417
9786
  const optionalServerNames = new Set(
10418
9787
  servers.filter((entry) => entry.optional).map((entry) => entry.server.name)
10419
9788
  );
10420
- const connectedRequired = await connectMcpServersInBatches(requiredServers, { strict: true });
9789
+ const connectedRequired = await connectMcpServersInBatches(requiredServers, {
9790
+ strict: true,
9791
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(requiredEntries.map((entry) => entry.timeoutMs))
9792
+ });
10421
9793
  let connectedBestEffort = null;
10422
9794
  try {
10423
- connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, { strict: false }) : null;
9795
+ connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, {
9796
+ strict: false,
9797
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(
9798
+ bestEffortEntries.map((entry) => entry.timeoutMs)
9799
+ )
9800
+ }) : null;
10424
9801
  } catch (error) {
10425
9802
  await connectedRequired.close().catch(() => void 0);
10426
9803
  throw error;
@@ -13810,6 +13187,9 @@ export {
13810
13187
  REPOSITORY_DISCOVERY_LIMIT,
13811
13188
  RecordingError,
13812
13189
  RecordingUnavailableError,
13190
+ RoutingBackendRecoveryRequiredError,
13191
+ RoutingMutationOutcomeUnknownError,
13192
+ RoutingRetainedProcessNotFoundError,
13813
13193
  RoutingSandboxSession,
13814
13194
  RoutingUnsupportedError,
13815
13195
  RunCredentialValidationError,
@@ -13832,7 +13212,9 @@ export {
13832
13212
  SandboxChannelAService,
13833
13213
  SandboxComputer,
13834
13214
  SandboxConfigError,
13215
+ SandboxExecReadinessError,
13835
13216
  SandboxProviderUnavailableError,
13217
+ SandboxReplacementProviderStateError,
13836
13218
  SandboxResumeStateUnavailableError,
13837
13219
  SelfhostedControlError,
13838
13220
  SelfhostedSandboxClient,
@@ -13847,6 +13229,8 @@ export {
13847
13229
  ToolspaceTokenPathError,
13848
13230
  TurnSandboxCommandCancelledError,
13849
13231
  USER_MESSAGE_TRUNCATION_MARKER,
13232
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
13233
+ WorkspaceArchiveIntegrityError,
13850
13234
  WorkspaceModelPolicyBlockedError,
13851
13235
  agentErrorToControlError,
13852
13236
  agentsErrorRunState,
@@ -13878,7 +13262,9 @@ export {
13878
13262
  buildSummaryItem,
13879
13263
  buildTerminalServerScript,
13880
13264
  callModelInputFilterForSettings,
13265
+ captureVerifiedWorkspaceArchive,
13881
13266
  clampCompactionThresholdRatio,
13267
+ classifyProviderSandboxFailure,
13882
13268
  clearRunCredentials,
13883
13269
  clearRunCredentialsForAttempt,
13884
13270
  collectSandboxEnvironment,
@@ -13925,16 +13311,20 @@ export {
13925
13311
  extForCodec,
13926
13312
  extractResponseOutputText,
13927
13313
  findCompactionNeededError,
13314
+ fingerprintSandboxWorkspace,
13928
13315
  getSkillLibraryEntry,
13929
13316
  gitCredentialBindingHash,
13930
13317
  gitCredentialBindingTokenRefreshCommand,
13931
13318
  gitProviderTokenRefreshCommand,
13932
13319
  hasModelGeneratedItem,
13320
+ hasPersistableSandboxProviderIdentity,
13933
13321
  inspectRecordingArtifact,
13934
13322
  isCompactionSummary,
13935
13323
  isEphemeralInternalContext,
13936
13324
  isExecSessionLostBanner,
13325
+ isProviderSandboxGoneDuringRoutedOperation,
13937
13326
  isProviderSandboxNotFoundError,
13327
+ isProviderSandboxTransientError,
13938
13328
  isSelfhostedProviderNotFoundError,
13939
13329
  isSkillLibraryEntryId,
13940
13330
  isUserMessage,
@@ -13975,12 +13365,14 @@ export {
13975
13365
  parseNumstatZ,
13976
13366
  parsePorcelainV2,
13977
13367
  parseUnifiedPatch,
13368
+ parseWorkspaceArchiveDescriptor,
13978
13369
  payloadTooLargeMessage,
13979
13370
  pinProvidedSessionManifestEnvironment,
13980
13371
  prefixedMcpToolName,
13981
13372
  prepareAgentTools,
13982
13373
  prepareCompactionPromptInput,
13983
13374
  prepareRunInput,
13375
+ readVerifiedWorkspaceArchive,
13984
13376
  readWorkspaceArchiveFromEnvelopeSessionState,
13985
13377
  recordingStorageKey,
13986
13378
  refreshGitCredentialBindingTokenFiles,
@@ -13990,6 +13382,7 @@ export {
13990
13382
  renderSelfhostedFault,
13991
13383
  repositoryCloneCommand,
13992
13384
  repositoryUsesSandboxClone,
13385
+ requirePersistableReplacementSandboxEnvelope,
13993
13386
  resolveTurnModel,
13994
13387
  restoredSandboxSessionStateFromEntry,
13995
13388
  rigInstructions,
@@ -14021,6 +13414,7 @@ export {
14021
13414
  serializeApprovals,
14022
13415
  serializeEstablishedSandboxEnvelope,
14023
13416
  serializeHumanInputRequests,
13417
+ serializeReplacementSandboxEnvelope,
14024
13418
  setSelfhostedApplyDiff,
14025
13419
  startRecording,
14026
13420
  stopRecording,
@@ -14042,6 +13436,8 @@ export {
14042
13436
  toolspaceTokenFileFromEnvironment,
14043
13437
  toolspaceTokenSeedCommand,
14044
13438
  uploadRecordingArtifact,
13439
+ verifyRestoredWorkspace,
13440
+ verifySandboxExecReadiness,
14045
13441
  verifyStreamToken,
14046
13442
  withManifestRefreshOnResume,
14047
13443
  withRunCredentialEnvironment,