@opengeni/runtime 0.3.0 → 0.4.0
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/LICENSE +190 -0
- package/dist/{chunk-D5KU3QUC.js → chunk-HGQ252FL.js} +22 -3
- package/dist/chunk-HGQ252FL.js.map +1 -0
- package/dist/index-CSGkld-v.d.ts +1801 -0
- package/dist/index.d.ts +93 -9
- package/dist/index.js +618 -66
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +4 -1784
- package/dist/sandbox/index.js +1 -1
- package/package.json +6 -8
- package/src/context-compaction.ts +164 -16
- package/src/history-sanitizer.ts +37 -39
- package/src/index.ts +586 -19
- package/src/metrics.ts +5 -0
- package/src/sandbox/display-stack.ts +25 -4
- package/src/sandbox/index.ts +28 -1
- package/src/sandbox-computer.ts +167 -36
- package/src/screenshot-error-card.ts +25 -0
- package/dist/chunk-D5KU3QUC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -95,11 +95,11 @@ import {
|
|
|
95
95
|
timeoutAgentError,
|
|
96
96
|
timeoutControlResponse,
|
|
97
97
|
verifyStreamToken
|
|
98
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-HGQ252FL.js";
|
|
99
99
|
|
|
100
100
|
// src/index.ts
|
|
101
101
|
import { AGENT_INSTRUCTIONS_CORE_PLACEHOLDER, collectSandboxEnvironment as collectSandboxEnvironment2, contextInputBudgetTokens, contextServerCompactThreshold, firstPartyMcpBaseUrl, resolveContextCompactionMode, resolveModelProvider, sandboxLifecycleHookIds } from "@opengeni/config";
|
|
102
|
-
import { CAPABILITY_DESCRIPTORS as CAPABILITY_DESCRIPTORS2, isClearedRunStateBlob, signDelegatedAccessToken } from "@opengeni/contracts";
|
|
102
|
+
import { CAPABILITY_DESCRIPTORS as CAPABILITY_DESCRIPTORS2, isClearedRunStateBlob, prefixedMcpToolName as sharedPrefixedMcpToolName, signDelegatedAccessToken } from "@opengeni/contracts";
|
|
103
103
|
import {
|
|
104
104
|
Agent,
|
|
105
105
|
AgentsError,
|
|
@@ -116,6 +116,7 @@ import {
|
|
|
116
116
|
setDefaultOpenAIClient,
|
|
117
117
|
setDefaultOpenAIKey,
|
|
118
118
|
setOpenAIResponsesTransport,
|
|
119
|
+
setTracingDisabled,
|
|
119
120
|
webSearchTool,
|
|
120
121
|
applyDiff
|
|
121
122
|
} from "@openai/agents";
|
|
@@ -145,8 +146,11 @@ import { cpSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync } from "
|
|
|
145
146
|
import { dirname, isAbsolute, join, posix as posixPath, relative } from "path";
|
|
146
147
|
import { fileURLToPath } from "url";
|
|
147
148
|
|
|
149
|
+
// src/screenshot-error-card.ts
|
|
150
|
+
var SCREENSHOT_FAILURE_CARD_IMAGE_URL = "data:image/png;base64,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";
|
|
151
|
+
|
|
148
152
|
// src/history-sanitizer.ts
|
|
149
|
-
var
|
|
153
|
+
var TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE = {
|
|
150
154
|
function_call: "function_call_result",
|
|
151
155
|
computer_call: "computer_call_result",
|
|
152
156
|
shell_call: "shell_call_output",
|
|
@@ -159,6 +163,7 @@ var RESULT_TYPE_BY_CALL_TYPE = {
|
|
|
159
163
|
// additionally ride providerData (see callIdOf).
|
|
160
164
|
tool_search_call: "tool_search_output"
|
|
161
165
|
};
|
|
166
|
+
var RESULT_TYPE_BY_CALL_TYPE = TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE;
|
|
162
167
|
var RESULT_TYPES = new Set(Object.values(RESULT_TYPE_BY_CALL_TYPE));
|
|
163
168
|
function itemType(item) {
|
|
164
169
|
if (!item || typeof item !== "object") {
|
|
@@ -466,7 +471,6 @@ function rewriteComputerCallsToActionsOnly(body) {
|
|
|
466
471
|
}
|
|
467
472
|
return changed;
|
|
468
473
|
}
|
|
469
|
-
var EMPTY_IMAGE_URL_PLACEHOLDER = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==";
|
|
470
474
|
function rewriteEmptyComputerCallOutputImageUrls(body) {
|
|
471
475
|
if (!body || typeof body !== "object") {
|
|
472
476
|
return false;
|
|
@@ -491,7 +495,7 @@ function rewriteEmptyComputerCallOutputImageUrls(body) {
|
|
|
491
495
|
const out = output;
|
|
492
496
|
const imageUrl = out.image_url;
|
|
493
497
|
if (typeof imageUrl !== "string" || imageUrl.length === 0) {
|
|
494
|
-
out.image_url =
|
|
498
|
+
out.image_url = SCREENSHOT_FAILURE_CARD_IMAGE_URL;
|
|
495
499
|
changed = true;
|
|
496
500
|
}
|
|
497
501
|
}
|
|
@@ -792,7 +796,10 @@ function installCodexToolSearch(agent, connectorNamespaces = NO_NAMESPACES) {
|
|
|
792
796
|
var COMPACTION_SUMMARY_MARKER = "opengeni_context_summary";
|
|
793
797
|
var SUMMARY_BUFFER_TOKENS = 2e4;
|
|
794
798
|
var COMPACT_USER_MESSAGE_MAX_TOKENS = 2e4;
|
|
795
|
-
var
|
|
799
|
+
var DEFAULT_COMPACTION_THRESHOLD_RATIO = 0.6;
|
|
800
|
+
var MIN_COMPACTION_THRESHOLD_RATIO = 0.3;
|
|
801
|
+
var MAX_COMPACTION_THRESHOLD_RATIO = 0.9;
|
|
802
|
+
var COMPACTION_FALLBACK_TARGET_RATIO = 0.5;
|
|
796
803
|
var COMPACTION_PROMPT = [
|
|
797
804
|
"You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.",
|
|
798
805
|
"",
|
|
@@ -806,12 +813,7 @@ var COMPACTION_PROMPT = [
|
|
|
806
813
|
].join("\n");
|
|
807
814
|
var SUMMARY_PREFIX = "Another language model started to solve this problem and produced a summary of its thinking process. You also have access to the state of the tools that were used by that language model. Use this to build on the work that has already been done and avoid duplicating work. Here is the summary produced by the other language model, use the information in this summary to assist with your own analysis:";
|
|
808
815
|
var USER_MESSAGE_TRUNCATION_MARKER = "\n[... middle truncated for context compaction ...]\n";
|
|
809
|
-
var RESULT_TYPE_BY_CALL_TYPE2 =
|
|
810
|
-
function_call: "function_call_result",
|
|
811
|
-
computer_call: "computer_call_result",
|
|
812
|
-
shell_call: "shell_call_output",
|
|
813
|
-
apply_patch_call: "apply_patch_call_output"
|
|
814
|
-
};
|
|
816
|
+
var RESULT_TYPE_BY_CALL_TYPE2 = TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE;
|
|
815
817
|
var RESULT_TYPES2 = new Set(Object.values(RESULT_TYPE_BY_CALL_TYPE2));
|
|
816
818
|
function itemType2(item) {
|
|
817
819
|
if (!item || typeof item !== "object") {
|
|
@@ -849,12 +851,12 @@ function estimateTokens(items) {
|
|
|
849
851
|
}
|
|
850
852
|
return total;
|
|
851
853
|
}
|
|
854
|
+
function clampCompactionThresholdRatio(value) {
|
|
855
|
+
const numeric = typeof value === "number" && Number.isFinite(value) ? value : DEFAULT_COMPACTION_THRESHOLD_RATIO;
|
|
856
|
+
return Math.min(MAX_COMPACTION_THRESHOLD_RATIO, Math.max(MIN_COMPACTION_THRESHOLD_RATIO, numeric));
|
|
857
|
+
}
|
|
852
858
|
function clientCompactionThresholdTokens(input) {
|
|
853
|
-
|
|
854
|
-
0,
|
|
855
|
-
input.contextWindowTokens - input.contextReservedOutputTokens - SUMMARY_BUFFER_TOKENS
|
|
856
|
-
);
|
|
857
|
-
return Math.floor(available * CLIENT_COMPACTION_TRIGGER_FRACTION);
|
|
859
|
+
return Math.floor(Math.max(0, input.contextWindowTokens) * clampCompactionThresholdRatio(input.contextCompactionThresholdRatio));
|
|
858
860
|
}
|
|
859
861
|
function decideClientCompaction(input) {
|
|
860
862
|
const thresholdTokens = clientCompactionThresholdTokens(input);
|
|
@@ -956,6 +958,58 @@ function buildCompactionReplacementHistory(items, summaryBody) {
|
|
|
956
958
|
history.push(buildSummaryItem(summaryBody));
|
|
957
959
|
return history;
|
|
958
960
|
}
|
|
961
|
+
function buildDeterministicFallbackCompactionHistory(input) {
|
|
962
|
+
const sourceTokens = Math.max(1, estimateTokens(input.items));
|
|
963
|
+
const requestedTarget = typeof input.targetTokens === "number" && input.targetTokens > 0 ? Math.floor(input.targetTokens) : Math.floor(sourceTokens * COMPACTION_FALLBACK_TARGET_RATIO);
|
|
964
|
+
const targetTokens = Math.max(1, Math.min(requestedTarget, Math.max(1, sourceTokens - 1)));
|
|
965
|
+
const summaryItem = buildSummaryItem([
|
|
966
|
+
"Non-LLM context compaction fallback.",
|
|
967
|
+
input.cause ? `Summarizer failure: ${input.cause}` : "Summarizer failure: unavailable.",
|
|
968
|
+
"Older assistant, tool, reasoning, and image history was dropped deterministically.",
|
|
969
|
+
"Retained context above is limited to initial instructions and the most recent user messages that fit the fallback budget; oversized retained messages may be middle-truncated."
|
|
970
|
+
].join("\n"));
|
|
971
|
+
const summaryTokens = estimateItemTokens(summaryItem);
|
|
972
|
+
let remaining = Math.max(0, targetTokens - summaryTokens);
|
|
973
|
+
const retainedInstructions = [];
|
|
974
|
+
for (const item of input.items) {
|
|
975
|
+
if (!isInitialInstructionMessage(item)) {
|
|
976
|
+
if (isUserMessage(item)) {
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (remaining <= 0) {
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
const capped = compactMessageToEstimatedItemBudget(item, Math.min(2e3, remaining));
|
|
985
|
+
if (!capped) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
const tokens = estimateItemTokens(capped);
|
|
989
|
+
if (tokens <= remaining) {
|
|
990
|
+
retainedInstructions.push(capped);
|
|
991
|
+
remaining -= tokens;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
const retainedUsers = [];
|
|
995
|
+
for (let index = input.items.length - 1; index >= 0 && remaining > 0; index -= 1) {
|
|
996
|
+
const item = input.items[index];
|
|
997
|
+
if (!isUserMessage(item) || isCompactionSummary(item)) {
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
const capped = compactMessageToEstimatedItemBudget(item, Math.min(COMPACT_USER_MESSAGE_MAX_TOKENS, remaining));
|
|
1001
|
+
if (!capped) {
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const tokens = estimateItemTokens(capped);
|
|
1005
|
+
if (tokens <= remaining) {
|
|
1006
|
+
retainedUsers.push(capped);
|
|
1007
|
+
remaining -= tokens;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
retainedUsers.reverse();
|
|
1011
|
+
return [...retainedInstructions, ...retainedUsers, summaryItem];
|
|
1012
|
+
}
|
|
959
1013
|
function buildSummaryItem(summaryBody) {
|
|
960
1014
|
const trimmed = summaryBody.trim();
|
|
961
1015
|
return {
|
|
@@ -967,15 +1021,38 @@ ${trimmed}`,
|
|
|
967
1021
|
};
|
|
968
1022
|
}
|
|
969
1023
|
function compactUserMessage(item) {
|
|
1024
|
+
return compactMessageToTokenBudget(item, COMPACT_USER_MESSAGE_MAX_TOKENS);
|
|
1025
|
+
}
|
|
1026
|
+
function compactMessageToTokenBudget(item, maxTokens) {
|
|
970
1027
|
const text = messageText(item);
|
|
971
1028
|
const next = { ...item };
|
|
972
|
-
if (estimatedTextTokens(text) >
|
|
973
|
-
next.content = truncateMiddleByEstimatedTokens(text,
|
|
1029
|
+
if (estimatedTextTokens(text) > maxTokens) {
|
|
1030
|
+
next.content = truncateMiddleByEstimatedTokens(text, maxTokens);
|
|
974
1031
|
return next;
|
|
975
1032
|
}
|
|
976
1033
|
next.content = contentWithoutImages(item);
|
|
977
1034
|
return next;
|
|
978
1035
|
}
|
|
1036
|
+
function compactMessageToEstimatedItemBudget(item, maxItemTokens) {
|
|
1037
|
+
if (maxItemTokens <= 0) {
|
|
1038
|
+
return null;
|
|
1039
|
+
}
|
|
1040
|
+
const overheadTokens = estimateItemTokens({ ...item, content: "" });
|
|
1041
|
+
let contentBudget = Math.max(0, maxItemTokens - overheadTokens);
|
|
1042
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
1043
|
+
const capped = compactMessageToTokenBudget(item, contentBudget);
|
|
1044
|
+
const estimated = estimateItemTokens(capped);
|
|
1045
|
+
if (estimated <= maxItemTokens) {
|
|
1046
|
+
return capped;
|
|
1047
|
+
}
|
|
1048
|
+
const overage = estimated - maxItemTokens;
|
|
1049
|
+
if (contentBudget <= 0) {
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
contentBudget = Math.max(0, contentBudget - Math.max(1, overage));
|
|
1053
|
+
}
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
979
1056
|
function estimatedTextTokens(text) {
|
|
980
1057
|
return Math.ceil(text.length / 4);
|
|
981
1058
|
}
|
|
@@ -1026,8 +1103,29 @@ function messageText(item) {
|
|
|
1026
1103
|
}
|
|
1027
1104
|
return "";
|
|
1028
1105
|
}
|
|
1029
|
-
function
|
|
1030
|
-
|
|
1106
|
+
function isInitialInstructionMessage(item) {
|
|
1107
|
+
const type = itemType2(item);
|
|
1108
|
+
const role = itemRole(item);
|
|
1109
|
+
return type === "message" && (role === "system" || role === "developer");
|
|
1110
|
+
}
|
|
1111
|
+
function renderCompactionPromptInputForChat(input, options = {}) {
|
|
1112
|
+
const rendered = input.map(renderItem);
|
|
1113
|
+
if (typeof options.maxEstimatedTokens !== "number" || options.maxEstimatedTokens <= 0) {
|
|
1114
|
+
return rendered.join("\n");
|
|
1115
|
+
}
|
|
1116
|
+
const maxChars = Math.max(1, Math.floor(options.maxEstimatedTokens * 4));
|
|
1117
|
+
const last = rendered.at(-1);
|
|
1118
|
+
const prefix = rendered.slice(0, -1);
|
|
1119
|
+
let kept = prefix.slice();
|
|
1120
|
+
while (kept.length > 0 && transcriptLength([...kept, last].filter((line) => line !== void 0)) > maxChars) {
|
|
1121
|
+
kept.shift();
|
|
1122
|
+
}
|
|
1123
|
+
const lines = [...kept, last].filter((line) => line !== void 0);
|
|
1124
|
+
const joined = lines.join("\n");
|
|
1125
|
+
if (joined.length <= maxChars) {
|
|
1126
|
+
return joined;
|
|
1127
|
+
}
|
|
1128
|
+
return truncateMiddleByChars(joined, maxChars);
|
|
1031
1129
|
}
|
|
1032
1130
|
function renderItem(item) {
|
|
1033
1131
|
const type = itemType2(item) ?? "unknown";
|
|
@@ -1073,6 +1171,24 @@ function truncateForTranscript(text, max) {
|
|
|
1073
1171
|
}
|
|
1074
1172
|
return `${text.slice(0, max)}... (${text.length - max} more chars)`;
|
|
1075
1173
|
}
|
|
1174
|
+
function transcriptLength(lines) {
|
|
1175
|
+
if (lines.length === 0) {
|
|
1176
|
+
return 0;
|
|
1177
|
+
}
|
|
1178
|
+
return lines.reduce((sum, line) => sum + line.length, 0) + Math.max(0, lines.length - 1);
|
|
1179
|
+
}
|
|
1180
|
+
function truncateMiddleByChars(text, maxChars) {
|
|
1181
|
+
if (text.length <= maxChars) {
|
|
1182
|
+
return text;
|
|
1183
|
+
}
|
|
1184
|
+
if (maxChars <= USER_MESSAGE_TRUNCATION_MARKER.length) {
|
|
1185
|
+
return USER_MESSAGE_TRUNCATION_MARKER.slice(0, maxChars);
|
|
1186
|
+
}
|
|
1187
|
+
const keepChars = maxChars - USER_MESSAGE_TRUNCATION_MARKER.length;
|
|
1188
|
+
const headChars = Math.ceil(keepChars / 2);
|
|
1189
|
+
const tailChars = Math.floor(keepChars / 2);
|
|
1190
|
+
return `${text.slice(0, headChars)}${USER_MESSAGE_TRUNCATION_MARKER}${text.slice(text.length - tailChars)}`;
|
|
1191
|
+
}
|
|
1076
1192
|
|
|
1077
1193
|
// src/sandbox-computer.ts
|
|
1078
1194
|
import { computerTool, tool } from "@openai/agents";
|
|
@@ -1091,6 +1207,7 @@ var SCROLL_NOTCH_PIXELS = 100;
|
|
|
1091
1207
|
var SCROLL_MAX_CLICKS = 15;
|
|
1092
1208
|
var SCREENSHOT_WARMUP_BUDGET_MS = 3e4;
|
|
1093
1209
|
var SCREENSHOT_RETRY_DELAY_MS = 750;
|
|
1210
|
+
var SCREENSHOT_READ_CHUNK_BYTES = 98304;
|
|
1094
1211
|
var KEYSYM = {
|
|
1095
1212
|
ctrl: "ctrl",
|
|
1096
1213
|
control: "ctrl",
|
|
@@ -1131,6 +1248,12 @@ var ComputerUnavailableError = class extends Error {
|
|
|
1131
1248
|
this.name = "ComputerUnavailableError";
|
|
1132
1249
|
}
|
|
1133
1250
|
};
|
|
1251
|
+
var ScreenshotReadError = class extends Error {
|
|
1252
|
+
constructor(message) {
|
|
1253
|
+
super(message);
|
|
1254
|
+
this.name = "ScreenshotReadError";
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1134
1257
|
var ComputerReadOnlyError = class extends Error {
|
|
1135
1258
|
constructor() {
|
|
1136
1259
|
super("computer-use is read-only \u2014 write actions are disabled");
|
|
@@ -1234,6 +1357,9 @@ var SandboxComputer = class {
|
|
|
1234
1357
|
return Buffer.from(bytes).toString("base64");
|
|
1235
1358
|
} catch (error) {
|
|
1236
1359
|
lastError = error;
|
|
1360
|
+
if (error instanceof ScreenshotReadError) {
|
|
1361
|
+
throw error;
|
|
1362
|
+
}
|
|
1237
1363
|
} finally {
|
|
1238
1364
|
await this.x(`rm -f ${f}`).catch(() => void 0);
|
|
1239
1365
|
}
|
|
@@ -1246,34 +1372,66 @@ var SandboxComputer = class {
|
|
|
1246
1372
|
}
|
|
1247
1373
|
throw new ComputerUnavailableError("scrot produced an empty screenshot (display not up?)");
|
|
1248
1374
|
}
|
|
1249
|
-
//
|
|
1250
|
-
//
|
|
1375
|
+
// Run a read-only command over the SAME command primitive computer actions use
|
|
1376
|
+
// (exec ?? execCommand) and return its RAW stdout body — NOT `session.readFile` (Modal
|
|
1251
1377
|
// path-validates against /workspace and rejects /tmp) and NOT `this.x()` (its
|
|
1252
|
-
// `sandboxCommandOutput` parser drops the execCommand STRING body
|
|
1253
|
-
//
|
|
1254
|
-
//
|
|
1255
|
-
//
|
|
1256
|
-
//
|
|
1257
|
-
|
|
1378
|
+
// `sandboxCommandOutput` parser drops the execCommand STRING body). exec exposes a
|
|
1379
|
+
// structured stdout; execCommand returns the formatted STRING, so we strip its banner
|
|
1380
|
+
// ("…Output:\n<body>") to recover the body.
|
|
1381
|
+
//
|
|
1382
|
+
// ROUTING-PROXY SEAM: when the selfhosted feature is on, the turn's box is wrapped in a
|
|
1383
|
+
// `RoutingSandboxSession`, which ALWAYS exposes an `exec` method — but for a Modal-backed
|
|
1384
|
+
// box (no native `exec`) that method internally falls back to `execCommand` and returns
|
|
1385
|
+
// the formatted STRING, not a `{output}` object. `sandboxCommandOutput` returns "" for a
|
|
1386
|
+
// string, so a naive `sandboxCommandOutput(await session.exec())` silently dropped the
|
|
1387
|
+
// whole screenshot body → empty read → "display not up" error card on EVERY Modal
|
|
1388
|
+
// computer-use turn once routing was enabled. So a STRING exec result is banner-stripped
|
|
1389
|
+
// exactly like the direct execCommand path; only a structured object goes to
|
|
1390
|
+
// sandboxCommandOutput.
|
|
1391
|
+
async readCmdRaw(cmd) {
|
|
1258
1392
|
const args = {
|
|
1259
|
-
cmd
|
|
1393
|
+
cmd,
|
|
1260
1394
|
...this.runAs ? { runAs: this.runAs } : {},
|
|
1261
1395
|
yieldTimeMs: ACTION_YIELD_MS,
|
|
1262
|
-
// null disables the provider's
|
|
1263
|
-
//
|
|
1396
|
+
// null disables the provider's TOKEN truncation; the byte cap this method chunks
|
|
1397
|
+
// around is a separate, lower-level exec-stdout buffer limit.
|
|
1264
1398
|
maxOutputTokens: null
|
|
1265
1399
|
};
|
|
1266
|
-
let raw;
|
|
1267
1400
|
if (typeof this.session.exec === "function") {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1401
|
+
const result = await this.session.exec(args);
|
|
1402
|
+
return typeof result === "string" ? stripExecBanner(result) : sandboxCommandOutput(result);
|
|
1403
|
+
}
|
|
1404
|
+
if (typeof this.session.execCommand === "function") {
|
|
1405
|
+
return stripExecBanner(await this.session.execCommand(args));
|
|
1406
|
+
}
|
|
1407
|
+
throw new ComputerUnavailableError("session cannot run commands (no exec/execCommand) \u2014 screenshots unavailable");
|
|
1408
|
+
}
|
|
1409
|
+
// Read the screenshot PNG bytes off the box. A SINGLE `base64 <file>` over Modal's
|
|
1410
|
+
// exec silently returns "" once the base64 exceeds the exec-stdout buffer cap (a full
|
|
1411
|
+
// desktop ~296 KB base64 trips it — the blank-frame incident). So we size the file
|
|
1412
|
+
// first, then base64 it in SCREENSHOT_READ_CHUNK_BYTES-sized, 3-byte-aligned chunks
|
|
1413
|
+
// (each read's base64 stays well under the cap) and reconstruct — validating the total
|
|
1414
|
+
// against the on-box size and failing LOUD on any short read rather than handing back a
|
|
1415
|
+
// truncated/blank frame. Binary-safe: base64 is plain ASCII over stdout.
|
|
1416
|
+
async readScreenshotBytes(path) {
|
|
1417
|
+
const sizeRaw = (await this.readCmdRaw(`wc -c < ${path} 2>/dev/null`)).replace(/[^0-9]/g, "");
|
|
1418
|
+
const fileSize = Number.parseInt(sizeRaw, 10);
|
|
1419
|
+
if (!Number.isFinite(fileSize) || fileSize <= 0) return new Uint8Array();
|
|
1420
|
+
const chunks = [];
|
|
1421
|
+
const nChunks = Math.ceil(fileSize / SCREENSHOT_READ_CHUNK_BYTES);
|
|
1422
|
+
for (let i = 0; i < nChunks; i++) {
|
|
1423
|
+
const raw = await this.readCmdRaw(
|
|
1424
|
+
`dd if=${path} bs=${SCREENSHOT_READ_CHUNK_BYTES} skip=${i} count=1 2>/dev/null | base64`
|
|
1425
|
+
);
|
|
1426
|
+
chunks.push(Buffer.from(raw.replace(/\s+/g, ""), "base64"));
|
|
1427
|
+
}
|
|
1428
|
+
const bytes = Buffer.concat(chunks);
|
|
1429
|
+
if (bytes.length !== fileSize) {
|
|
1430
|
+
throw new ScreenshotReadError(
|
|
1431
|
+
`screenshot read reconstructed ${bytes.length}B of ${fileSize}B (${nChunks} chunk(s), path=${path}) \u2014 a chunk was truncated by the exec-output cap; frame incomplete`
|
|
1432
|
+
);
|
|
1273
1433
|
}
|
|
1274
|
-
|
|
1275
|
-
if (b64.length === 0) return new Uint8Array();
|
|
1276
|
-
return Uint8Array.from(Buffer.from(b64, "base64"));
|
|
1434
|
+
return new Uint8Array(bytes);
|
|
1277
1435
|
}
|
|
1278
1436
|
async click(xp, yp, button) {
|
|
1279
1437
|
this.guardWrite();
|
|
@@ -1328,11 +1486,19 @@ var POINTER_BUTTON = {
|
|
|
1328
1486
|
back: PointerButton.POINTER_BUTTON_UNSPECIFIED,
|
|
1329
1487
|
forward: PointerButton.POINTER_BUTTON_UNSPECIFIED
|
|
1330
1488
|
};
|
|
1489
|
+
var NATIVE_SCREENSHOT_WARMUP_BUDGET_MS = 6e3;
|
|
1490
|
+
var NATIVE_SCREENSHOT_RETRY_DELAY_MS = 400;
|
|
1491
|
+
function isTerminalCaptureDenial(error) {
|
|
1492
|
+
const msg = (error instanceof Error ? error.message : String(error ?? "")).toLowerCase();
|
|
1493
|
+
return msg.includes("screen recording") || msg.includes("permission is not granted") || msg.includes("consent");
|
|
1494
|
+
}
|
|
1331
1495
|
var NativeDesktopComputer = class {
|
|
1332
1496
|
environment;
|
|
1333
1497
|
dimensions;
|
|
1334
1498
|
session;
|
|
1335
1499
|
readOnly;
|
|
1500
|
+
screenshotWarmupBudgetMs;
|
|
1501
|
+
screenshotRetryDelayMs;
|
|
1336
1502
|
// The ENCODED vs NATIVE geometry of the MOST RECENT screenshot the model saw. The
|
|
1337
1503
|
// model computes click coordinates in the encoded-pixel space of that screenshot;
|
|
1338
1504
|
// when the agent downscaled the PNG to fit the transport budget, encoded < native,
|
|
@@ -1347,6 +1513,8 @@ var NativeDesktopComputer = class {
|
|
|
1347
1513
|
this.dimensions = opts.dimensions ?? DEFAULT_DIMENSIONS;
|
|
1348
1514
|
this.environment = opts.environment ?? "ubuntu";
|
|
1349
1515
|
this.readOnly = opts.readOnly ?? false;
|
|
1516
|
+
this.screenshotWarmupBudgetMs = opts.screenshotWarmupBudgetMs ?? NATIVE_SCREENSHOT_WARMUP_BUDGET_MS;
|
|
1517
|
+
this.screenshotRetryDelayMs = opts.screenshotRetryDelayMs ?? NATIVE_SCREENSHOT_RETRY_DELAY_MS;
|
|
1350
1518
|
}
|
|
1351
1519
|
/** Rebind to a freshly resumed-by-id session after a box rollover / re-establish. */
|
|
1352
1520
|
rebind(session) {
|
|
@@ -1376,13 +1544,36 @@ var NativeDesktopComputer = class {
|
|
|
1376
1544
|
await this.session.desktopInput({ $case: "pointer", pointer: { x: n.x, y: n.y, action, button } });
|
|
1377
1545
|
}
|
|
1378
1546
|
async screenshot() {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1547
|
+
let lastError;
|
|
1548
|
+
const deadline = Date.now() + this.screenshotWarmupBudgetMs;
|
|
1549
|
+
let attempt = 0;
|
|
1550
|
+
while (true) {
|
|
1551
|
+
if (attempt > 0) {
|
|
1552
|
+
await new Promise((r) => setTimeout(r, this.screenshotRetryDelayMs));
|
|
1553
|
+
}
|
|
1554
|
+
attempt++;
|
|
1555
|
+
try {
|
|
1556
|
+
const { png, width, height, nativeWidth, nativeHeight } = await this.session.screenshot();
|
|
1557
|
+
if (png.length === 0) {
|
|
1558
|
+
throw new ComputerUnavailableError("native desktop screenshot returned an empty frame (display not up?)");
|
|
1559
|
+
}
|
|
1560
|
+
this.lastEncoded = [width, height];
|
|
1561
|
+
this.lastNative = [nativeWidth || width, nativeHeight || height];
|
|
1562
|
+
return Buffer.from(png).toString("base64");
|
|
1563
|
+
} catch (error) {
|
|
1564
|
+
lastError = error;
|
|
1565
|
+
if (isTerminalCaptureDenial(error)) break;
|
|
1566
|
+
}
|
|
1567
|
+
if (Date.now() + this.screenshotRetryDelayMs >= deadline) {
|
|
1568
|
+
break;
|
|
1569
|
+
}
|
|
1382
1570
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1571
|
+
const reason = lastError instanceof Error ? lastError.message : String(lastError);
|
|
1572
|
+
console.warn(`[NativeDesktopComputer] screenshot failed after ${attempt} attempt(s): ${reason}`);
|
|
1573
|
+
if (lastError instanceof Error) {
|
|
1574
|
+
throw lastError;
|
|
1575
|
+
}
|
|
1576
|
+
throw new ComputerUnavailableError("native desktop screenshot returned an empty frame (display not up?)");
|
|
1386
1577
|
}
|
|
1387
1578
|
async click(x, y, button) {
|
|
1388
1579
|
this.guardWrite();
|
|
@@ -1668,9 +1859,16 @@ function ensureReadableStreamFrom() {
|
|
|
1668
1859
|
}
|
|
1669
1860
|
});
|
|
1670
1861
|
}
|
|
1862
|
+
var runtimeMetricsHooks = null;
|
|
1863
|
+
function configureRuntimeMetricsHooks(hooks) {
|
|
1864
|
+
runtimeMetricsHooks = hooks ?? null;
|
|
1865
|
+
}
|
|
1671
1866
|
function createProductionAgentRuntime(overrides = {}) {
|
|
1672
1867
|
return {
|
|
1673
|
-
configure:
|
|
1868
|
+
configure: (settings) => {
|
|
1869
|
+
configureRuntimeMetricsHooks(overrides.metrics);
|
|
1870
|
+
configureOpenAI(settings);
|
|
1871
|
+
},
|
|
1674
1872
|
// A test/override model shadows the registry routing entirely (the scripted
|
|
1675
1873
|
// model used in worker tests is not in any provider's allow-list), so when
|
|
1676
1874
|
// one is supplied resolveTurnModel reports "no resolution" and the caller
|
|
@@ -1689,7 +1887,7 @@ function createProductionAgentRuntime(overrides = {}) {
|
|
|
1689
1887
|
serializeApprovals
|
|
1690
1888
|
};
|
|
1691
1889
|
}
|
|
1692
|
-
function buildOpenAIClientFromSettings(settings) {
|
|
1890
|
+
function buildOpenAIClientFromSettings(settings, providerId = settings.openaiProvider) {
|
|
1693
1891
|
if (settings.openaiProvider === "azure") {
|
|
1694
1892
|
const baseURL = settings.azureOpenaiBaseUrl ?? azureDeploymentBaseUrl(settings);
|
|
1695
1893
|
const apiKey = settings.azureOpenaiApiKey ?? settings.azureOpenaiAdToken ?? "azure-ad-token";
|
|
@@ -1704,13 +1902,14 @@ function buildOpenAIClientFromSettings(settings) {
|
|
|
1704
1902
|
// seam — below the SDK responses converter, which always re-synthesizes BOTH
|
|
1705
1903
|
// `action` and `actions` (rejected 400 "exactly one of action or actions").
|
|
1706
1904
|
// See computerCallNormalizingFetch / rewriteComputerCallsToActionsOnly.
|
|
1707
|
-
fetch: computerCallNormalizingFetch(globalThis.fetch)
|
|
1905
|
+
fetch: computerCallNormalizingFetch(instrumentedModelFetch(providerId, globalThis.fetch))
|
|
1708
1906
|
});
|
|
1709
1907
|
}
|
|
1710
1908
|
return new OpenAI({
|
|
1711
1909
|
apiKey: settings.openaiApiKey ?? process.env.OPENAI_API_KEY,
|
|
1712
1910
|
...settings.openaiBaseUrl ? { baseURL: settings.openaiBaseUrl } : {},
|
|
1713
|
-
maxRetries: settings.openaiMaxRetries
|
|
1911
|
+
maxRetries: settings.openaiMaxRetries,
|
|
1912
|
+
fetch: instrumentedModelFetch(providerId, globalThis.fetch)
|
|
1714
1913
|
});
|
|
1715
1914
|
}
|
|
1716
1915
|
var providerClientCache = /* @__PURE__ */ new Map();
|
|
@@ -1719,17 +1918,18 @@ function buildProviderClient(provider, settings) {
|
|
|
1719
1918
|
if (cached) {
|
|
1720
1919
|
return cached;
|
|
1721
1920
|
}
|
|
1722
|
-
const client = provider.builtin ? buildOpenAIClientFromSettings(settings) : provider.kind === "codex-subscription" ? new OpenAI({
|
|
1921
|
+
const client = provider.builtin ? buildOpenAIClientFromSettings(settings, provider.id) : provider.kind === "codex-subscription" ? new OpenAI({
|
|
1723
1922
|
apiKey: provider.apiKey ?? "codex-subscription",
|
|
1724
1923
|
...provider.baseUrl ? { baseURL: provider.baseUrl } : {},
|
|
1725
1924
|
maxRetries: settings.openaiMaxRetries,
|
|
1726
|
-
fetch: codexSubscriptionFetch(globalThis.fetch)
|
|
1925
|
+
fetch: codexSubscriptionFetch(instrumentedModelFetch(provider.id, globalThis.fetch))
|
|
1727
1926
|
}) : new OpenAI({
|
|
1728
1927
|
...provider.apiKey ? { apiKey: provider.apiKey } : {},
|
|
1729
1928
|
...provider.baseUrl ? { baseURL: provider.baseUrl } : {},
|
|
1730
1929
|
maxRetries: settings.openaiMaxRetries,
|
|
1731
1930
|
...provider.defaultQuery ? { defaultQuery: provider.defaultQuery } : {},
|
|
1732
|
-
...provider.defaultHeaders ? { defaultHeaders: provider.defaultHeaders } : {}
|
|
1931
|
+
...provider.defaultHeaders ? { defaultHeaders: provider.defaultHeaders } : {},
|
|
1932
|
+
fetch: instrumentedModelFetch(provider.id, globalThis.fetch)
|
|
1733
1933
|
});
|
|
1734
1934
|
providerClientCache.set(provider.id, client);
|
|
1735
1935
|
return client;
|
|
@@ -1797,6 +1997,7 @@ var CodexSubscriptionUnavailableError = class extends Error {
|
|
|
1797
1997
|
};
|
|
1798
1998
|
function configureOpenAI(settings) {
|
|
1799
1999
|
setOpenAIResponsesTransport(settings.openaiResponsesTransport);
|
|
2000
|
+
setTracingDisabled(settings.disableOpenaiTracing || !settings.observabilityOtlpEndpoint);
|
|
1800
2001
|
const router = new MultiProviderModelProvider(settings);
|
|
1801
2002
|
if (settings.openaiProvider === "azure") {
|
|
1802
2003
|
setDefaultOpenAIClient(buildOpenAIClientFromSettings(settings));
|
|
@@ -1811,17 +2012,56 @@ function configureOpenAI(settings) {
|
|
|
1811
2012
|
}
|
|
1812
2013
|
setDefaultModelProvider(router);
|
|
1813
2014
|
}
|
|
2015
|
+
function instrumentedModelFetch(provider, inner) {
|
|
2016
|
+
return (async (input, init) => {
|
|
2017
|
+
if (!isModelCallFetch(input)) {
|
|
2018
|
+
return await inner(input, init);
|
|
2019
|
+
}
|
|
2020
|
+
const started = performance.now();
|
|
2021
|
+
try {
|
|
2022
|
+
const response = await inner(input, init);
|
|
2023
|
+
recordModelCallMetric(provider, response.ok ? "completed" : "failed", started);
|
|
2024
|
+
return response;
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
recordModelCallMetric(provider, "failed", started);
|
|
2027
|
+
throw error;
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
function isModelCallFetch(input) {
|
|
2032
|
+
const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
2033
|
+
if (typeof rawUrl !== "string" || rawUrl.length === 0) {
|
|
2034
|
+
return false;
|
|
2035
|
+
}
|
|
2036
|
+
try {
|
|
2037
|
+
const pathname = new URL(rawUrl, "http://opengeni.local").pathname;
|
|
2038
|
+
return pathname.endsWith("/responses") || pathname.endsWith("/chat/completions") || pathname.endsWith("/codex/responses");
|
|
2039
|
+
} catch {
|
|
2040
|
+
return /\/(?:codex\/)?responses(?:\?|$)|\/chat\/completions(?:\?|$)/.test(rawUrl);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
function recordModelCallMetric(provider, outcome, started) {
|
|
2044
|
+
const durationSeconds = Math.max(0, (performance.now() - started) / 1e3);
|
|
2045
|
+
try {
|
|
2046
|
+
runtimeMetricsHooks?.onModelCall?.({ provider, outcome, durationSeconds });
|
|
2047
|
+
} catch {
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
1814
2050
|
async function summarizeForCompaction(settings, input, options = {}) {
|
|
1815
2051
|
const client = options.client ?? buildOpenAIClientFromSettings(settings);
|
|
1816
2052
|
const api = options.api ?? "responses";
|
|
1817
2053
|
const model = options.model ?? settings.openaiModel;
|
|
1818
2054
|
const maxTokens = options.maxOutputTokens ?? SUMMARY_BUFFER_TOKENS;
|
|
2055
|
+
const transcript = renderCompactionPromptInputForChat(
|
|
2056
|
+
input,
|
|
2057
|
+
typeof options.maxTranscriptTokens === "number" && options.maxTranscriptTokens > 0 ? { maxEstimatedTokens: options.maxTranscriptTokens } : {}
|
|
2058
|
+
);
|
|
1819
2059
|
try {
|
|
1820
2060
|
if (api === "chat") {
|
|
1821
2061
|
const completion = await client.chat.completions.create({
|
|
1822
2062
|
model,
|
|
1823
2063
|
max_tokens: maxTokens,
|
|
1824
|
-
messages: [{ role: "user", content:
|
|
2064
|
+
messages: [{ role: "user", content: transcript }]
|
|
1825
2065
|
});
|
|
1826
2066
|
const text2 = completion.choices?.[0]?.message?.content;
|
|
1827
2067
|
const trimmed2 = typeof text2 === "string" ? text2.trim() : "";
|
|
@@ -1834,13 +2074,13 @@ async function summarizeForCompaction(settings, input, options = {}) {
|
|
|
1834
2074
|
// built-in path (api "responses"), so gate it on the built-in provider.
|
|
1835
2075
|
...settings.openaiProvider === "azure" ? {} : { store: false },
|
|
1836
2076
|
max_output_tokens: maxTokens,
|
|
1837
|
-
input
|
|
2077
|
+
input: transcript
|
|
1838
2078
|
});
|
|
1839
2079
|
const text = extractResponseOutputText(response);
|
|
1840
2080
|
const trimmed = text.trim();
|
|
1841
2081
|
return trimmed.length > 0 ? trimmed : null;
|
|
1842
2082
|
} catch (error) {
|
|
1843
|
-
console.error("context compaction summarize failed
|
|
2083
|
+
console.error("context compaction summarize failed", error);
|
|
1844
2084
|
return null;
|
|
1845
2085
|
}
|
|
1846
2086
|
}
|
|
@@ -1916,7 +2156,13 @@ function appendGenesisTitleDirective(instructions, genesisTitleHint) {
|
|
|
1916
2156
|
var agentFileDownloads = /* @__PURE__ */ new WeakMap();
|
|
1917
2157
|
var agentRepositoryCloneHooks = /* @__PURE__ */ new WeakMap();
|
|
1918
2158
|
var agentGitTokenSeed = /* @__PURE__ */ new WeakMap();
|
|
2159
|
+
var agentToolspaceTokenSeed = /* @__PURE__ */ new WeakMap();
|
|
1919
2160
|
var agentActiveSandboxBackend = /* @__PURE__ */ new WeakMap();
|
|
2161
|
+
function mcpToolErrorOutput(error) {
|
|
2162
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
2163
|
+
return { isError: true, content: [{ type: "text", text: `An error occurred while running the tool. Please try again. Error: ${details}` }] };
|
|
2164
|
+
}
|
|
2165
|
+
var mcpToolErrorFunction = ({ error }) => mcpToolErrorOutput(error);
|
|
1920
2166
|
function buildOpenGeniAgent(settings, resources, options = {}) {
|
|
1921
2167
|
const compactionMode = options.compactionMode ?? resolveContextCompactionMode(settings);
|
|
1922
2168
|
const hostedWebSearch = options.hostedWebSearch ?? settings.webSearchEnabled;
|
|
@@ -1975,11 +2221,19 @@ function buildOpenGeniAgent(settings, resources, options = {}) {
|
|
|
1975
2221
|
// `new SandboxAgent({ ...baseConfig, ... })` path via the shared baseConfig
|
|
1976
2222
|
// spread; the SDK concatenates these with MCP and sandbox capability tools.
|
|
1977
2223
|
...hostedTools.length ? { tools: hostedTools } : {},
|
|
1978
|
-
...options.mcpServers?.length ? { mcpServers: options.mcpServers } : {}
|
|
2224
|
+
...options.mcpServers?.length ? { mcpServers: options.mcpServers } : {},
|
|
2225
|
+
// Surface FAILED MCP tool calls as `{ isError: true }` tool output (see
|
|
2226
|
+
// mcpToolErrorFunction / mcpToolErrorOutput) instead of the SDK's default
|
|
2227
|
+
// flat error string, so a thrown MCP failure (protocol error, auth, timeout,
|
|
2228
|
+
// tool-not-found) settles the timeline tool to "failed" rather than rendering
|
|
2229
|
+
// as success. Applies to every MCP server on this agent (session, first-party,
|
|
2230
|
+
// capability, codex_apps) since none set a server-level errorFunction.
|
|
2231
|
+
mcpConfig: { errorFunction: mcpToolErrorFunction }
|
|
1979
2232
|
};
|
|
1980
2233
|
if (settings.sandboxBackend === "none") {
|
|
1981
2234
|
const agent2 = new Agent(baseConfig);
|
|
1982
2235
|
maybeInstallCodexToolSearch(agent2, settings, options);
|
|
2236
|
+
applyMcpApprovalPolicy(agent2, settings);
|
|
1983
2237
|
return agent2;
|
|
1984
2238
|
}
|
|
1985
2239
|
const runAs = sandboxRunAs(settings);
|
|
@@ -2002,7 +2256,11 @@ function buildOpenGeniAgent(settings, resources, options = {}) {
|
|
|
2002
2256
|
if (options.gitTokenSeed) {
|
|
2003
2257
|
agentGitTokenSeed.set(agent, options.gitTokenSeed);
|
|
2004
2258
|
}
|
|
2259
|
+
if (options.toolspaceTokenSeed) {
|
|
2260
|
+
agentToolspaceTokenSeed.set(agent, options.toolspaceTokenSeed);
|
|
2261
|
+
}
|
|
2005
2262
|
maybeInstallCodexToolSearch(agent, settings, options);
|
|
2263
|
+
applyMcpApprovalPolicy(agent, settings);
|
|
2006
2264
|
return agent;
|
|
2007
2265
|
}
|
|
2008
2266
|
function maybeInstallCodexToolSearch(agent, settings, options) {
|
|
@@ -2013,6 +2271,41 @@ function maybeInstallCodexToolSearch(agent, settings, options) {
|
|
|
2013
2271
|
);
|
|
2014
2272
|
}
|
|
2015
2273
|
}
|
|
2274
|
+
function mcpToolRequiresApproval(policy, unprefixedName) {
|
|
2275
|
+
return policy === true || Array.isArray(policy) && policy.includes(unprefixedName);
|
|
2276
|
+
}
|
|
2277
|
+
function installMcpApprovalPolicy(agent, policies) {
|
|
2278
|
+
const listMcpTools = agent.getMcpTools.bind(agent);
|
|
2279
|
+
agent.getMcpTools = async (runContext) => {
|
|
2280
|
+
const tools = await listMcpTools(runContext);
|
|
2281
|
+
return tools.map((tool2) => {
|
|
2282
|
+
if (tool2.type !== "function") {
|
|
2283
|
+
return tool2;
|
|
2284
|
+
}
|
|
2285
|
+
const policy = policies.find((entry) => tool2.name.startsWith(entry.prefix));
|
|
2286
|
+
if (!policy) {
|
|
2287
|
+
return tool2;
|
|
2288
|
+
}
|
|
2289
|
+
const unprefixed = tool2.name.slice(policy.prefix.length);
|
|
2290
|
+
return mcpToolRequiresApproval(policy.requireApproval, unprefixed) ? { ...tool2, needsApproval: async () => true } : tool2;
|
|
2291
|
+
});
|
|
2292
|
+
};
|
|
2293
|
+
const originalClone = agent.clone?.bind(agent);
|
|
2294
|
+
if (originalClone) {
|
|
2295
|
+
agent.clone = (config) => {
|
|
2296
|
+
const cloned = originalClone(config);
|
|
2297
|
+
installMcpApprovalPolicy(cloned, policies);
|
|
2298
|
+
return cloned;
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
function applyMcpApprovalPolicy(agent, settings) {
|
|
2303
|
+
const policies = settings.mcpServers.filter((server) => server.requireApproval === true || Array.isArray(server.requireApproval) && server.requireApproval.length > 0).map((server) => ({ prefix: prefixedMcpToolName(server.id, ""), requireApproval: server.requireApproval })).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
2304
|
+
if (policies.length === 0) {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
installMcpApprovalPolicy(agent, policies);
|
|
2308
|
+
}
|
|
2016
2309
|
function neutralizeStructuredToolTransport(capability) {
|
|
2017
2310
|
const forceFunctionTransport = function() {
|
|
2018
2311
|
this._modelInstance = void 0;
|
|
@@ -2062,6 +2355,8 @@ async function prepareAgentTools(settings, tools, options = {}) {
|
|
|
2062
2355
|
throw new Error(`Unknown MCP server id: ${tool2.id}`);
|
|
2063
2356
|
}
|
|
2064
2357
|
const url = firstPartyMcpServerUrlForRun(settings, config, options.workspaceId) ?? config.url;
|
|
2358
|
+
const baseFetch = isCodexAppsMcpServer(config) ? codexAppsSanitizingFetch(globalThis.fetch, codexConnectorNamespaces) : globalThis.fetch;
|
|
2359
|
+
const fetchImpl = config.connectionRef ? connectionBrokerFetch(baseFetch, config, options) : baseFetch;
|
|
2065
2360
|
const server = new PrefixedMcpServer(new MCPServerStreamableHttp({
|
|
2066
2361
|
url,
|
|
2067
2362
|
name: config.name ?? config.id,
|
|
@@ -2070,7 +2365,7 @@ async function prepareAgentTools(settings, tools, options = {}) {
|
|
|
2070
2365
|
// MCP SDK's strict Tool schema rejects (fails the turn during tools/list);
|
|
2071
2366
|
// sanitize the response on the wire before validation. The namespace Set
|
|
2072
2367
|
// also captures each tool's original connector namespace (P4 Part B.1).
|
|
2073
|
-
...
|
|
2368
|
+
...fetchImpl !== globalThis.fetch ? { fetch: fetchImpl } : {},
|
|
2074
2369
|
...await mcpServerRequestInit(settings, config, options),
|
|
2075
2370
|
...config.timeoutMs ? {
|
|
2076
2371
|
timeout: config.timeoutMs,
|
|
@@ -2078,7 +2373,7 @@ async function prepareAgentTools(settings, tools, options = {}) {
|
|
|
2078
2373
|
} : {}
|
|
2079
2374
|
}), config.id, config.allowedTools);
|
|
2080
2375
|
const optional = tool2.optional === true;
|
|
2081
|
-
return { server, bestEffort: isCodexAppsMcpServer(config) || optional, optional };
|
|
2376
|
+
return { server, bestEffort: isCodexAppsMcpServer(config) || optional || !!config.connectionRef, optional };
|
|
2082
2377
|
}));
|
|
2083
2378
|
const requiredServers = servers.filter((entry) => !entry.bestEffort).map((entry) => entry.server);
|
|
2084
2379
|
const bestEffortServers = servers.filter((entry) => entry.bestEffort).map((entry) => entry.server);
|
|
@@ -2116,6 +2411,190 @@ async function prepareAgentTools(settings, tools, options = {}) {
|
|
|
2116
2411
|
codexConnectorNamespaces
|
|
2117
2412
|
};
|
|
2118
2413
|
}
|
|
2414
|
+
function connectionBrokerFetch(baseFetch, config, options) {
|
|
2415
|
+
const connectionRef = config.connectionRef;
|
|
2416
|
+
if (!connectionRef) {
|
|
2417
|
+
return baseFetch;
|
|
2418
|
+
}
|
|
2419
|
+
return async (input, init) => {
|
|
2420
|
+
const request = await mcpRequestInfo(input, init);
|
|
2421
|
+
const first = await resolveConnectionForRequest(options, config.id, connectionRef, request.toolName, false);
|
|
2422
|
+
if (first.status === "auth_needed") {
|
|
2423
|
+
return await authNeededFetchResponse(options, config.id, request, first, connectionRef);
|
|
2424
|
+
}
|
|
2425
|
+
const response = await baseFetch(fetchInputForAttempt(input), withConnectionHeaders(input, init, first.headers));
|
|
2426
|
+
if (response.status === 401) {
|
|
2427
|
+
const refreshed = await resolveConnectionForRequest(options, config.id, connectionRef, request.toolName, true);
|
|
2428
|
+
if (refreshed.status === "auth_needed") {
|
|
2429
|
+
return await authNeededFetchResponse(options, config.id, request, refreshed, connectionRef);
|
|
2430
|
+
}
|
|
2431
|
+
const retry = await baseFetch(fetchInputForAttempt(input), withConnectionHeaders(input, init, refreshed.headers));
|
|
2432
|
+
if (retry.status === 403) {
|
|
2433
|
+
const auth = insufficientScopeAuth(retry.headers, connectionRef, refreshed.connectionId);
|
|
2434
|
+
return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : retry;
|
|
2435
|
+
}
|
|
2436
|
+
if (retry.status === 401) {
|
|
2437
|
+
return await authNeededFetchResponse(options, config.id, request, {
|
|
2438
|
+
status: "auth_needed",
|
|
2439
|
+
reason: "expired",
|
|
2440
|
+
providerDomain: connectionRef.providerDomain,
|
|
2441
|
+
connectionId: refreshed.connectionId,
|
|
2442
|
+
...connectionRef.scopes ? { scopes: connectionRef.scopes } : {},
|
|
2443
|
+
...connectionRef.resource ? { resource: connectionRef.resource } : {}
|
|
2444
|
+
}, connectionRef);
|
|
2445
|
+
}
|
|
2446
|
+
return retry;
|
|
2447
|
+
}
|
|
2448
|
+
if (response.status === 403) {
|
|
2449
|
+
const auth = insufficientScopeAuth(response.headers, connectionRef, first.connectionId);
|
|
2450
|
+
return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : response;
|
|
2451
|
+
}
|
|
2452
|
+
return response;
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
async function resolveConnectionForRequest(options, serverId, connectionRef, toolName, forceRefresh) {
|
|
2456
|
+
if (!options.workspaceId || !options.resolveCredential) {
|
|
2457
|
+
return {
|
|
2458
|
+
status: "auth_needed",
|
|
2459
|
+
reason: "missing_connection",
|
|
2460
|
+
providerDomain: connectionRef.providerDomain,
|
|
2461
|
+
...connectionRef.connectionId ? { connectionId: connectionRef.connectionId } : {},
|
|
2462
|
+
...connectionRef.scopes ? { scopes: connectionRef.scopes } : {},
|
|
2463
|
+
...connectionRef.resource ? { resource: connectionRef.resource } : {}
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
const request = {
|
|
2467
|
+
workspaceId: options.workspaceId,
|
|
2468
|
+
serverId,
|
|
2469
|
+
connectionRef,
|
|
2470
|
+
forceRefresh,
|
|
2471
|
+
...toolName ? { toolName } : {},
|
|
2472
|
+
...options.subjectId ? { subjectId: options.subjectId } : {}
|
|
2473
|
+
};
|
|
2474
|
+
try {
|
|
2475
|
+
return await options.resolveCredential(request);
|
|
2476
|
+
} catch {
|
|
2477
|
+
return {
|
|
2478
|
+
status: "auth_needed",
|
|
2479
|
+
reason: "refresh_failed",
|
|
2480
|
+
providerDomain: connectionRef.providerDomain,
|
|
2481
|
+
...connectionRef.connectionId ? { connectionId: connectionRef.connectionId } : {},
|
|
2482
|
+
...connectionRef.scopes ? { scopes: connectionRef.scopes } : {},
|
|
2483
|
+
...connectionRef.resource ? { resource: connectionRef.resource } : {}
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function insufficientScopeAuth(headers, connectionRef, connectionId) {
|
|
2488
|
+
const challenge = parseWwwAuthenticate(headers.get("www-authenticate"));
|
|
2489
|
+
if (challenge.error !== "insufficient_scope") {
|
|
2490
|
+
return null;
|
|
2491
|
+
}
|
|
2492
|
+
return {
|
|
2493
|
+
status: "auth_needed",
|
|
2494
|
+
reason: "insufficient_scope",
|
|
2495
|
+
providerDomain: connectionRef.providerDomain,
|
|
2496
|
+
connectionId,
|
|
2497
|
+
...challenge.scope?.length ? { scopes: challenge.scope } : connectionRef.scopes ? { scopes: connectionRef.scopes } : {},
|
|
2498
|
+
...challenge.resource ? { resource: challenge.resource } : connectionRef.resource ? { resource: connectionRef.resource } : {}
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
async function authNeededFetchResponse(options, serverId, request, auth, connectionRef) {
|
|
2502
|
+
const connectionId = auth.connectionId ?? connectionRef.connectionId;
|
|
2503
|
+
await publishAuthNeeded(options, {
|
|
2504
|
+
serverId,
|
|
2505
|
+
toolName: request.toolName ?? null,
|
|
2506
|
+
providerDomain: auth.providerDomain,
|
|
2507
|
+
reason: auth.reason,
|
|
2508
|
+
...connectionId ? { connectionId } : {},
|
|
2509
|
+
...auth.scopes ? { scopes: auth.scopes } : connectionRef.scopes ? { scopes: connectionRef.scopes } : {},
|
|
2510
|
+
...auth.resource ? { resource: auth.resource } : connectionRef.resource ? { resource: connectionRef.resource } : {},
|
|
2511
|
+
...auth.authorizationUrl ? { authorizationUrl: auth.authorizationUrl } : {},
|
|
2512
|
+
...options.subjectId ? { subjectId: options.subjectId } : {}
|
|
2513
|
+
});
|
|
2514
|
+
if (request.method === "tools/call") {
|
|
2515
|
+
return mcpToolAuthNeededResponse(request.id);
|
|
2516
|
+
}
|
|
2517
|
+
return new Response("Authentication required for MCP server connection", { status: 401 });
|
|
2518
|
+
}
|
|
2519
|
+
async function publishAuthNeeded(options, payload) {
|
|
2520
|
+
try {
|
|
2521
|
+
await options.onAuthNeeded?.(payload);
|
|
2522
|
+
} catch {
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
async function mcpRequestInfo(input, init) {
|
|
2526
|
+
const body = typeof init?.body === "string" ? init.body : input instanceof Request && (init?.method ?? input.method).toUpperCase() === "POST" ? await input.clone().text().catch(() => "") : "";
|
|
2527
|
+
if (!body) {
|
|
2528
|
+
return {};
|
|
2529
|
+
}
|
|
2530
|
+
try {
|
|
2531
|
+
const parsed = JSON.parse(body);
|
|
2532
|
+
const method = typeof parsed.method === "string" ? parsed.method : void 0;
|
|
2533
|
+
const id = typeof parsed.id === "string" || typeof parsed.id === "number" || parsed.id === null ? parsed.id : void 0;
|
|
2534
|
+
const toolName = method === "tools/call" && typeof parsed.params?.name === "string" ? parsed.params.name : void 0;
|
|
2535
|
+
return {
|
|
2536
|
+
...method ? { method } : {},
|
|
2537
|
+
...id !== void 0 ? { id } : {},
|
|
2538
|
+
...toolName ? { toolName } : {}
|
|
2539
|
+
};
|
|
2540
|
+
} catch {
|
|
2541
|
+
return {};
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
function withConnectionHeaders(input, init, authHeaders) {
|
|
2545
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
2546
|
+
for (const [name, value] of Object.entries(authHeaders)) {
|
|
2547
|
+
headers.set(name, value);
|
|
2548
|
+
}
|
|
2549
|
+
return { ...init, headers };
|
|
2550
|
+
}
|
|
2551
|
+
function fetchInputForAttempt(input) {
|
|
2552
|
+
return input instanceof Request ? input.clone() : input;
|
|
2553
|
+
}
|
|
2554
|
+
function parseWwwAuthenticate(header) {
|
|
2555
|
+
if (!header) {
|
|
2556
|
+
return {};
|
|
2557
|
+
}
|
|
2558
|
+
const bearerIndex = header.toLowerCase().indexOf("bearer");
|
|
2559
|
+
if (bearerIndex < 0) {
|
|
2560
|
+
return {};
|
|
2561
|
+
}
|
|
2562
|
+
const paramsText = header.slice(bearerIndex + "bearer".length);
|
|
2563
|
+
const params = {};
|
|
2564
|
+
const re = /([a-zA-Z_][a-zA-Z0-9_-]*)\s*=\s*("(?:[^"\\]|\\.)*"|[^,\s]+)/g;
|
|
2565
|
+
let match;
|
|
2566
|
+
while ((match = re.exec(paramsText)) !== null) {
|
|
2567
|
+
const raw = match[2];
|
|
2568
|
+
params[match[1].toLowerCase()] = raw.startsWith('"') ? raw.slice(1, -1).replace(/\\"/g, '"') : raw;
|
|
2569
|
+
}
|
|
2570
|
+
return {
|
|
2571
|
+
...params.error ? { error: params.error } : {},
|
|
2572
|
+
...params.scope ? { scope: params.scope.split(/\s+/).filter(Boolean) } : {},
|
|
2573
|
+
...params.resource ? { resource: params.resource } : {}
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
var MCP_AUTH_NEEDED_ERROR_CODE = -32001;
|
|
2577
|
+
var MCP_AUTH_NEEDED_MESSAGE = "Authentication required - a connection link was posted to the session.";
|
|
2578
|
+
function mcpToolAuthNeededResponse(id) {
|
|
2579
|
+
return new Response(JSON.stringify({
|
|
2580
|
+
jsonrpc: "2.0",
|
|
2581
|
+
id: id ?? null,
|
|
2582
|
+
error: {
|
|
2583
|
+
code: MCP_AUTH_NEEDED_ERROR_CODE,
|
|
2584
|
+
message: MCP_AUTH_NEEDED_MESSAGE
|
|
2585
|
+
}
|
|
2586
|
+
}), {
|
|
2587
|
+
status: 200,
|
|
2588
|
+
headers: { "content-type": "application/json" }
|
|
2589
|
+
});
|
|
2590
|
+
}
|
|
2591
|
+
function isAuthNeededMcpError(error) {
|
|
2592
|
+
if (!(error instanceof Error)) {
|
|
2593
|
+
return false;
|
|
2594
|
+
}
|
|
2595
|
+
const code = error.code;
|
|
2596
|
+
return code === MCP_AUTH_NEEDED_ERROR_CODE || error.message.includes(MCP_AUTH_NEEDED_MESSAGE);
|
|
2597
|
+
}
|
|
2119
2598
|
async function mcpServerRequestInit(settings, config, options) {
|
|
2120
2599
|
if (isCodexAppsMcpServer(config)) {
|
|
2121
2600
|
return await codexAppsMcpRequestInit(settings);
|
|
@@ -2259,7 +2738,7 @@ function normalizeUrl(raw) {
|
|
|
2259
2738
|
}
|
|
2260
2739
|
}
|
|
2261
2740
|
function prefixedMcpToolName(registryId, toolName) {
|
|
2262
|
-
return
|
|
2741
|
+
return sharedPrefixedMcpToolName(registryId, toolName);
|
|
2263
2742
|
}
|
|
2264
2743
|
var PrefixedMcpServer = class {
|
|
2265
2744
|
constructor(inner, registryId, allowedTools) {
|
|
@@ -2289,7 +2768,14 @@ var PrefixedMcpServer = class {
|
|
|
2289
2768
|
if (!this.isAllowed(unprefixed)) {
|
|
2290
2769
|
throw new Error(`MCP tool ${unprefixed} is not allowed for server ${this.name}`);
|
|
2291
2770
|
}
|
|
2292
|
-
|
|
2771
|
+
try {
|
|
2772
|
+
return await this.inner.callTool(unprefixed, args, meta);
|
|
2773
|
+
} catch (error) {
|
|
2774
|
+
if (isAuthNeededMcpError(error)) {
|
|
2775
|
+
return { isError: true, content: [{ type: "text", text: MCP_AUTH_NEEDED_MESSAGE }] };
|
|
2776
|
+
}
|
|
2777
|
+
throw error;
|
|
2778
|
+
}
|
|
2293
2779
|
}
|
|
2294
2780
|
invalidateToolsCache() {
|
|
2295
2781
|
return this.inner.invalidateToolsCache();
|
|
@@ -2500,15 +2986,18 @@ async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
|
2500
2986
|
...runAs2 ? { runAs: runAs2 } : {}
|
|
2501
2987
|
}) : ownedClient;
|
|
2502
2988
|
const ownedGitTokenSeed = gitTokenSeedForAgent(agent);
|
|
2989
|
+
const ownedToolspaceTokenSeed = toolspaceTokenSeedForAgent(agent);
|
|
2503
2990
|
const ownedHooks = [
|
|
2504
2991
|
...sandboxLifecycleHooksForIds(sandboxLifecycleHookIds(settings)),
|
|
2992
|
+
...sandboxToolspaceTokenHooksForAgent(agent),
|
|
2505
2993
|
...sandboxRepositoryCloneHooksForAgent(agent)
|
|
2506
2994
|
];
|
|
2507
2995
|
const ownedHookContext = {
|
|
2508
2996
|
environment,
|
|
2509
2997
|
...overrides.onRuntimeEvent ? { onRuntimeEvent: overrides.onRuntimeEvent } : {},
|
|
2510
2998
|
...runAs2 ? { runAs: runAs2 } : {},
|
|
2511
|
-
...ownedGitTokenSeed ? { gitTokenSeed: ownedGitTokenSeed } : {}
|
|
2999
|
+
...ownedGitTokenSeed ? { gitTokenSeed: ownedGitTokenSeed } : {},
|
|
3000
|
+
...ownedToolspaceTokenSeed ? { toolspaceTokenSeed: ownedToolspaceTokenSeed } : {}
|
|
2512
3001
|
};
|
|
2513
3002
|
if (agentActiveSandboxBackend.get(agent) !== "selfhosted") {
|
|
2514
3003
|
await runBeforeAgentStartHooks(setupSession, ownedHooks, ownedHookContext);
|
|
@@ -2552,14 +3041,17 @@ async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
|
2552
3041
|
...runAs ? { runAs } : {}
|
|
2553
3042
|
}) : refreshedClient;
|
|
2554
3043
|
const gitTokenSeed = gitTokenSeedForAgent(agent);
|
|
3044
|
+
const toolspaceTokenSeed = toolspaceTokenSeedForAgent(agent);
|
|
2555
3045
|
const client = resourceClient ? withSandboxLifecycleHooks(resourceClient, [
|
|
2556
3046
|
...sandboxLifecycleHooksForIds(sandboxLifecycleHookIds(settings)),
|
|
3047
|
+
...sandboxToolspaceTokenHooksForAgent(agent),
|
|
2557
3048
|
...sandboxRepositoryCloneHooksForAgent(agent)
|
|
2558
3049
|
], {
|
|
2559
3050
|
environment,
|
|
2560
3051
|
...overrides.onRuntimeEvent ? { onRuntimeEvent: overrides.onRuntimeEvent } : {},
|
|
2561
3052
|
...runAs ? { runAs } : {},
|
|
2562
|
-
...gitTokenSeed ? { gitTokenSeed } : {}
|
|
3053
|
+
...gitTokenSeed ? { gitTokenSeed } : {},
|
|
3054
|
+
...toolspaceTokenSeed ? { toolspaceTokenSeed } : {}
|
|
2563
3055
|
}) : void 0;
|
|
2564
3056
|
const sandboxSessionState = prepared.sandboxSessionState ?? (prepared.serializedRunStateForSandbox && client ? await restoredSandboxSessionState(await RunState.fromString(agent, prepared.serializedRunStateForSandbox), client) : void 0);
|
|
2565
3057
|
const callModelInputFilter = composeCallModelInputFilters(
|
|
@@ -2582,7 +3074,6 @@ async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
|
2582
3074
|
// every mid-turn follow-up.
|
|
2583
3075
|
callModelInputFilter
|
|
2584
3076
|
};
|
|
2585
|
-
void settings.disableOpenaiTracing;
|
|
2586
3077
|
if (client) {
|
|
2587
3078
|
runOptions.sandbox = {
|
|
2588
3079
|
client,
|
|
@@ -3234,6 +3725,16 @@ function sandboxRepositoryCloneHooksForAgent(agent) {
|
|
|
3234
3725
|
function gitTokenSeedForAgent(agent) {
|
|
3235
3726
|
return agentGitTokenSeed.get(agent);
|
|
3236
3727
|
}
|
|
3728
|
+
function toolspaceTokenSeedForAgent(agent) {
|
|
3729
|
+
return agentToolspaceTokenSeed.get(agent);
|
|
3730
|
+
}
|
|
3731
|
+
function sandboxToolspaceTokenHooksForAgent(agent) {
|
|
3732
|
+
return toolspaceTokenSeedForAgent(agent) ? [{
|
|
3733
|
+
id: "toolspace-token",
|
|
3734
|
+
phase: "beforeAgentStart",
|
|
3735
|
+
run: runToolspaceTokenSeedHook
|
|
3736
|
+
}] : [];
|
|
3737
|
+
}
|
|
3237
3738
|
function sandboxRepositoryCloneHooks(settings, resources, activeSandboxBackend = settings.sandboxBackend) {
|
|
3238
3739
|
const repositories = resources.filter((resource) => resource.kind === "repository" && repositoryUsesSandboxClone(settings, resource, activeSandboxBackend));
|
|
3239
3740
|
if (repositories.length === 0) {
|
|
@@ -3404,6 +3905,49 @@ function repositoryCloneCommand(resources) {
|
|
|
3404
3905
|
}
|
|
3405
3906
|
return commands.join("\n");
|
|
3406
3907
|
}
|
|
3908
|
+
function toolspaceTokenSeedCommand() {
|
|
3909
|
+
return [
|
|
3910
|
+
"set -eu",
|
|
3911
|
+
'export HOME="${HOME:-/workspace}"',
|
|
3912
|
+
'if [ -n "${OPENGENI_TOOLSPACE_TOKEN_SEED:-}" ]; then',
|
|
3913
|
+
' seed_umask="$(umask)"',
|
|
3914
|
+
" umask 077",
|
|
3915
|
+
' token_file="${OPENGENI_TOOLSPACE_TOKEN_FILE:-$HOME/.opengeni/toolspace-token}"',
|
|
3916
|
+
' mkdir -p "$(dirname "$token_file")"',
|
|
3917
|
+
` printf '%s' "$OPENGENI_TOOLSPACE_TOKEN_SEED" > "$token_file.tmp.$$"`,
|
|
3918
|
+
' mv -f "$token_file.tmp.$$" "$token_file"',
|
|
3919
|
+
' umask "$seed_umask"',
|
|
3920
|
+
"fi"
|
|
3921
|
+
].join("\n");
|
|
3922
|
+
}
|
|
3923
|
+
async function runToolspaceTokenSeedHook(session, context) {
|
|
3924
|
+
if (!context.toolspaceTokenSeed) {
|
|
3925
|
+
return;
|
|
3926
|
+
}
|
|
3927
|
+
const command = `export OPENGENI_TOOLSPACE_TOKEN_SEED=${shellQuote(context.toolspaceTokenSeed)}
|
|
3928
|
+
${toolspaceTokenSeedCommand()}`;
|
|
3929
|
+
if (session.exec) {
|
|
3930
|
+
const result = await session.exec({
|
|
3931
|
+
cmd: command,
|
|
3932
|
+
workdir: "/workspace",
|
|
3933
|
+
...context.runAs ? { runAs: context.runAs } : {},
|
|
3934
|
+
yieldTimeMs: SANDBOX_LIFECYCLE_COMMAND_TIMEOUT_MS,
|
|
3935
|
+
maxOutputTokens: 4e3
|
|
3936
|
+
});
|
|
3937
|
+
assertSandboxCommandSucceeded(result, "Toolspace token seed hook");
|
|
3938
|
+
} else if (session.execCommand) {
|
|
3939
|
+
const result = await session.execCommand({
|
|
3940
|
+
cmd: command,
|
|
3941
|
+
workdir: "/workspace",
|
|
3942
|
+
...context.runAs ? { runAs: context.runAs } : {},
|
|
3943
|
+
yieldTimeMs: SANDBOX_LIFECYCLE_COMMAND_TIMEOUT_MS,
|
|
3944
|
+
maxOutputTokens: 4e3
|
|
3945
|
+
});
|
|
3946
|
+
assertSandboxCommandSucceeded(result, "Toolspace token seed hook");
|
|
3947
|
+
} else {
|
|
3948
|
+
throw new Error("Sandbox session does not support command execution");
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3407
3951
|
async function runRepositoryCloneHook(session, resources, context = { environment: {} }) {
|
|
3408
3952
|
const payload = { name: "repository-clone", repositoryCount: resources.length };
|
|
3409
3953
|
await context.onRuntimeEvent?.({ type: "sandbox.operation.started", payload });
|
|
@@ -3752,7 +4296,6 @@ function approvalIdentifier(item) {
|
|
|
3752
4296
|
export {
|
|
3753
4297
|
ActiveBackendUnresolvableError,
|
|
3754
4298
|
CAPABILITY_DESCRIPTORS,
|
|
3755
|
-
CLIENT_COMPACTION_TRIGGER_FRACTION,
|
|
3756
4299
|
COMPACTION_PROMPT,
|
|
3757
4300
|
COMPACTION_SUMMARY_MARKER,
|
|
3758
4301
|
COMPACT_USER_MESSAGE_MAX_TOKENS,
|
|
@@ -3766,12 +4309,15 @@ export {
|
|
|
3766
4309
|
ComputerReadOnlyError,
|
|
3767
4310
|
ComputerUnavailableError,
|
|
3768
4311
|
ComputerUseCapability,
|
|
4312
|
+
DEFAULT_COMPACTION_THRESHOLD_RATIO,
|
|
3769
4313
|
DEFAULT_DESKTOP_GEOMETRY,
|
|
3770
4314
|
DESKTOP_STREAM_PORT,
|
|
3771
4315
|
DISPLAY_STACK_TIMEOUT_MS,
|
|
3772
4316
|
DisplayStackError,
|
|
3773
4317
|
DisplayStackUnsupportedError,
|
|
3774
4318
|
GENESIS_TITLE_DIRECTIVE,
|
|
4319
|
+
MAX_COMPACTION_THRESHOLD_RATIO,
|
|
4320
|
+
MIN_COMPACTION_THRESHOLD_RATIO,
|
|
3775
4321
|
MaxTurnsExceededError2 as MaxTurnsExceededError,
|
|
3776
4322
|
MockAgentResponder,
|
|
3777
4323
|
MultiProviderModelProvider,
|
|
@@ -3819,6 +4365,7 @@ export {
|
|
|
3819
4365
|
buildAgentCapabilities,
|
|
3820
4366
|
buildCompactionPromptInput,
|
|
3821
4367
|
buildCompactionReplacementHistory,
|
|
4368
|
+
buildDeterministicFallbackCompactionHistory,
|
|
3822
4369
|
buildDisplayStackScript,
|
|
3823
4370
|
buildManifest,
|
|
3824
4371
|
buildModelInstance,
|
|
@@ -3830,11 +4377,13 @@ export {
|
|
|
3830
4377
|
buildSummaryItem,
|
|
3831
4378
|
buildTerminalServerScript,
|
|
3832
4379
|
callModelInputFilterForSettings,
|
|
4380
|
+
clampCompactionThresholdRatio,
|
|
3833
4381
|
clientCompactionThresholdTokens,
|
|
3834
4382
|
collectSandboxEnvironment,
|
|
3835
4383
|
composeAgentInstructions,
|
|
3836
4384
|
computerUse,
|
|
3837
4385
|
configureOpenAI,
|
|
4386
|
+
configureRuntimeMetricsHooks,
|
|
3838
4387
|
contentTypeForCodec,
|
|
3839
4388
|
contextRobustnessFilterForSettings,
|
|
3840
4389
|
coreInstructions,
|
|
@@ -3870,6 +4419,7 @@ export {
|
|
|
3870
4419
|
makeActiveBackendResolver,
|
|
3871
4420
|
materializeSandboxFileDownloads,
|
|
3872
4421
|
maxTurnsExceededRunState,
|
|
4422
|
+
mcpToolErrorOutput,
|
|
3873
4423
|
mintStreamToken,
|
|
3874
4424
|
modalSandboxAttributionEnvironment,
|
|
3875
4425
|
modalSandboxAttributionTags,
|
|
@@ -3902,6 +4452,7 @@ export {
|
|
|
3902
4452
|
runAzureCliLoginHook,
|
|
3903
4453
|
runBeforeAgentStartHooks,
|
|
3904
4454
|
runRepositoryCloneHook,
|
|
4455
|
+
runToolspaceTokenSeedHook,
|
|
3905
4456
|
sandboxCommandExitCode,
|
|
3906
4457
|
sandboxCommandOutput,
|
|
3907
4458
|
sandboxCommandStillRunning,
|
|
@@ -3930,6 +4481,7 @@ export {
|
|
|
3930
4481
|
terminateModalSandboxById,
|
|
3931
4482
|
timeoutAgentError,
|
|
3932
4483
|
timeoutControlResponse,
|
|
4484
|
+
toolspaceTokenSeedCommand,
|
|
3933
4485
|
verifyStreamToken,
|
|
3934
4486
|
withManifestRefreshOnResume,
|
|
3935
4487
|
withSandboxFileDownloads,
|