@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/{chunk-65UDUFUO.js → chunk-JGUQSAG5.js} +1887 -172
- package/dist/chunk-JGUQSAG5.js.map +1 -0
- package/dist/{chunk-HAN5D27J.js → chunk-QYXOVIUK.js} +24 -4
- package/dist/chunk-QYXOVIUK.js.map +1 -0
- package/dist/{index-DGUrY_yR.d.ts → index-DU-KXVlx.d.ts} +328 -25
- package/dist/index.d.ts +25 -3
- package/dist/index.js +71 -675
- package/dist/index.js.map +1 -1
- package/dist/mcp-network.d.ts +4 -1
- package/dist/mcp-network.js +5 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +39 -1
- package/package.json +3 -3
- package/src/index.ts +31 -9
- package/src/mcp-network.ts +28 -3
- package/src/sandbox/channel-a.ts +92 -46
- package/src/sandbox/exec-banner.ts +15 -0
- package/src/sandbox/index.ts +360 -145
- package/src/sandbox/provider-errors.ts +175 -0
- package/src/sandbox/routing/backend-resolver.ts +15 -0
- package/src/sandbox/routing/routing-session.ts +730 -36
- package/src/sandbox/select.ts +8 -0
- package/src/sandbox/turn-tool-cancellation.ts +366 -92
- package/src/sandbox/workspace-archive.ts +289 -0
- package/dist/chunk-65UDUFUO.js.map +0 -1
- package/dist/chunk-HAN5D27J.js.map +0 -1
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
|
-
|
|
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-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
10416
|
-
const
|
|
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, {
|
|
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, {
|
|
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,
|