@opengeni/runtime 0.13.0 → 0.13.3
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-SSB7VOLE.js} +10 -1
- package/dist/chunk-SSB7VOLE.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 +65 -673
- package/dist/index.js.map +1 -1
- package/dist/mcp-network.d.ts +3 -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 +20 -7
- package/src/mcp-network.ts +13 -0
- 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-SSB7VOLE.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
|
}
|
|
@@ -10408,19 +9770,30 @@ async function prepareAgentTools(settings, tools, options = {}) {
|
|
|
10408
9770
|
return {
|
|
10409
9771
|
server,
|
|
10410
9772
|
bestEffort,
|
|
10411
|
-
optional
|
|
9773
|
+
optional,
|
|
9774
|
+
timeoutMs: config.timeoutMs
|
|
10412
9775
|
};
|
|
10413
9776
|
}
|
|
10414
9777
|
);
|
|
10415
|
-
const
|
|
10416
|
-
const
|
|
9778
|
+
const requiredEntries = servers.filter((entry) => !entry.bestEffort);
|
|
9779
|
+
const bestEffortEntries = servers.filter((entry) => entry.bestEffort);
|
|
9780
|
+
const requiredServers = requiredEntries.map((entry) => entry.server);
|
|
9781
|
+
const bestEffortServers = bestEffortEntries.map((entry) => entry.server);
|
|
10417
9782
|
const optionalServerNames = new Set(
|
|
10418
9783
|
servers.filter((entry) => entry.optional).map((entry) => entry.server.name)
|
|
10419
9784
|
);
|
|
10420
|
-
const connectedRequired = await connectMcpServersInBatches(requiredServers, {
|
|
9785
|
+
const connectedRequired = await connectMcpServersInBatches(requiredServers, {
|
|
9786
|
+
strict: true,
|
|
9787
|
+
connectTimeoutMs: mcpOuterConnectTimeoutMs(requiredEntries.map((entry) => entry.timeoutMs))
|
|
9788
|
+
});
|
|
10421
9789
|
let connectedBestEffort = null;
|
|
10422
9790
|
try {
|
|
10423
|
-
connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, {
|
|
9791
|
+
connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, {
|
|
9792
|
+
strict: false,
|
|
9793
|
+
connectTimeoutMs: mcpOuterConnectTimeoutMs(
|
|
9794
|
+
bestEffortEntries.map((entry) => entry.timeoutMs)
|
|
9795
|
+
)
|
|
9796
|
+
}) : null;
|
|
10424
9797
|
} catch (error) {
|
|
10425
9798
|
await connectedRequired.close().catch(() => void 0);
|
|
10426
9799
|
throw error;
|
|
@@ -13810,6 +13183,9 @@ export {
|
|
|
13810
13183
|
REPOSITORY_DISCOVERY_LIMIT,
|
|
13811
13184
|
RecordingError,
|
|
13812
13185
|
RecordingUnavailableError,
|
|
13186
|
+
RoutingBackendRecoveryRequiredError,
|
|
13187
|
+
RoutingMutationOutcomeUnknownError,
|
|
13188
|
+
RoutingRetainedProcessNotFoundError,
|
|
13813
13189
|
RoutingSandboxSession,
|
|
13814
13190
|
RoutingUnsupportedError,
|
|
13815
13191
|
RunCredentialValidationError,
|
|
@@ -13832,7 +13208,9 @@ export {
|
|
|
13832
13208
|
SandboxChannelAService,
|
|
13833
13209
|
SandboxComputer,
|
|
13834
13210
|
SandboxConfigError,
|
|
13211
|
+
SandboxExecReadinessError,
|
|
13835
13212
|
SandboxProviderUnavailableError,
|
|
13213
|
+
SandboxReplacementProviderStateError,
|
|
13836
13214
|
SandboxResumeStateUnavailableError,
|
|
13837
13215
|
SelfhostedControlError,
|
|
13838
13216
|
SelfhostedSandboxClient,
|
|
@@ -13847,6 +13225,8 @@ export {
|
|
|
13847
13225
|
ToolspaceTokenPathError,
|
|
13848
13226
|
TurnSandboxCommandCancelledError,
|
|
13849
13227
|
USER_MESSAGE_TRUNCATION_MARKER,
|
|
13228
|
+
WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
|
|
13229
|
+
WorkspaceArchiveIntegrityError,
|
|
13850
13230
|
WorkspaceModelPolicyBlockedError,
|
|
13851
13231
|
agentErrorToControlError,
|
|
13852
13232
|
agentsErrorRunState,
|
|
@@ -13878,7 +13258,9 @@ export {
|
|
|
13878
13258
|
buildSummaryItem,
|
|
13879
13259
|
buildTerminalServerScript,
|
|
13880
13260
|
callModelInputFilterForSettings,
|
|
13261
|
+
captureVerifiedWorkspaceArchive,
|
|
13881
13262
|
clampCompactionThresholdRatio,
|
|
13263
|
+
classifyProviderSandboxFailure,
|
|
13882
13264
|
clearRunCredentials,
|
|
13883
13265
|
clearRunCredentialsForAttempt,
|
|
13884
13266
|
collectSandboxEnvironment,
|
|
@@ -13925,16 +13307,20 @@ export {
|
|
|
13925
13307
|
extForCodec,
|
|
13926
13308
|
extractResponseOutputText,
|
|
13927
13309
|
findCompactionNeededError,
|
|
13310
|
+
fingerprintSandboxWorkspace,
|
|
13928
13311
|
getSkillLibraryEntry,
|
|
13929
13312
|
gitCredentialBindingHash,
|
|
13930
13313
|
gitCredentialBindingTokenRefreshCommand,
|
|
13931
13314
|
gitProviderTokenRefreshCommand,
|
|
13932
13315
|
hasModelGeneratedItem,
|
|
13316
|
+
hasPersistableSandboxProviderIdentity,
|
|
13933
13317
|
inspectRecordingArtifact,
|
|
13934
13318
|
isCompactionSummary,
|
|
13935
13319
|
isEphemeralInternalContext,
|
|
13936
13320
|
isExecSessionLostBanner,
|
|
13321
|
+
isProviderSandboxGoneDuringRoutedOperation,
|
|
13937
13322
|
isProviderSandboxNotFoundError,
|
|
13323
|
+
isProviderSandboxTransientError,
|
|
13938
13324
|
isSelfhostedProviderNotFoundError,
|
|
13939
13325
|
isSkillLibraryEntryId,
|
|
13940
13326
|
isUserMessage,
|
|
@@ -13975,12 +13361,14 @@ export {
|
|
|
13975
13361
|
parseNumstatZ,
|
|
13976
13362
|
parsePorcelainV2,
|
|
13977
13363
|
parseUnifiedPatch,
|
|
13364
|
+
parseWorkspaceArchiveDescriptor,
|
|
13978
13365
|
payloadTooLargeMessage,
|
|
13979
13366
|
pinProvidedSessionManifestEnvironment,
|
|
13980
13367
|
prefixedMcpToolName,
|
|
13981
13368
|
prepareAgentTools,
|
|
13982
13369
|
prepareCompactionPromptInput,
|
|
13983
13370
|
prepareRunInput,
|
|
13371
|
+
readVerifiedWorkspaceArchive,
|
|
13984
13372
|
readWorkspaceArchiveFromEnvelopeSessionState,
|
|
13985
13373
|
recordingStorageKey,
|
|
13986
13374
|
refreshGitCredentialBindingTokenFiles,
|
|
@@ -13990,6 +13378,7 @@ export {
|
|
|
13990
13378
|
renderSelfhostedFault,
|
|
13991
13379
|
repositoryCloneCommand,
|
|
13992
13380
|
repositoryUsesSandboxClone,
|
|
13381
|
+
requirePersistableReplacementSandboxEnvelope,
|
|
13993
13382
|
resolveTurnModel,
|
|
13994
13383
|
restoredSandboxSessionStateFromEntry,
|
|
13995
13384
|
rigInstructions,
|
|
@@ -14021,6 +13410,7 @@ export {
|
|
|
14021
13410
|
serializeApprovals,
|
|
14022
13411
|
serializeEstablishedSandboxEnvelope,
|
|
14023
13412
|
serializeHumanInputRequests,
|
|
13413
|
+
serializeReplacementSandboxEnvelope,
|
|
14024
13414
|
setSelfhostedApplyDiff,
|
|
14025
13415
|
startRecording,
|
|
14026
13416
|
stopRecording,
|
|
@@ -14042,6 +13432,8 @@ export {
|
|
|
14042
13432
|
toolspaceTokenFileFromEnvironment,
|
|
14043
13433
|
toolspaceTokenSeedCommand,
|
|
14044
13434
|
uploadRecordingArtifact,
|
|
13435
|
+
verifyRestoredWorkspace,
|
|
13436
|
+
verifySandboxExecReadiness,
|
|
14045
13437
|
verifyStreamToken,
|
|
14046
13438
|
withManifestRefreshOnResume,
|
|
14047
13439
|
withRunCredentialEnvironment,
|