@opengeni/runtime 0.2.2 → 0.2.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-2PO56VAL.js → chunk-KNW7AMQB.js} +11 -4
- package/dist/chunk-KNW7AMQB.js.map +1 -0
- package/dist/index.d.ts +89 -177
- package/dist/index.js +346 -156
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +6 -4
- package/dist/sandbox/index.js +1 -1
- package/package.json +3 -3
- package/src/context-compaction.ts +217 -348
- package/src/image-history.ts +149 -0
- package/src/index.ts +129 -34
- package/src/sandbox/display-stack.ts +61 -12
- package/src/sandbox-computer.ts +36 -9
- package/dist/chunk-2PO56VAL.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -90,10 +90,10 @@ import {
|
|
|
90
90
|
timeoutAgentError,
|
|
91
91
|
timeoutControlResponse,
|
|
92
92
|
verifyStreamToken
|
|
93
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-KNW7AMQB.js";
|
|
94
94
|
|
|
95
95
|
// src/index.ts
|
|
96
|
-
import { AGENT_INSTRUCTIONS_CORE_PLACEHOLDER, collectSandboxEnvironment as collectSandboxEnvironment2, contextServerCompactThreshold, firstPartyMcpBaseUrl, resolveContextCompactionMode, resolveModelProvider, sandboxLifecycleHookIds } from "@opengeni/config";
|
|
96
|
+
import { AGENT_INSTRUCTIONS_CORE_PLACEHOLDER, collectSandboxEnvironment as collectSandboxEnvironment2, contextInputBudgetTokens, contextServerCompactThreshold, firstPartyMcpBaseUrl, resolveContextCompactionMode, resolveModelProvider, sandboxLifecycleHookIds } from "@opengeni/config";
|
|
97
97
|
import { CAPABILITY_DESCRIPTORS as CAPABILITY_DESCRIPTORS2, isClearedRunStateBlob, signDelegatedAccessToken } from "@opengeni/contracts";
|
|
98
98
|
import {
|
|
99
99
|
Agent,
|
|
@@ -509,6 +509,106 @@ function computerCallNormalizingFetch(base) {
|
|
|
509
509
|
};
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
+
// src/image-history.ts
|
|
513
|
+
var SCREENSHOT_OMITTED_PLACEHOLDER = "[screenshot omitted: an older desktop frame \u2014 the full image remains in the session event log]";
|
|
514
|
+
var DATA_IMAGE_BASE64_PATTERN = /data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=_-]+/i;
|
|
515
|
+
function elideStaleScreenshotImages(items, options = {}) {
|
|
516
|
+
const keepLast = Math.max(0, Math.floor(options.keepLast ?? 3));
|
|
517
|
+
const placeholder = options.placeholder ?? SCREENSHOT_OMITTED_PLACEHOLDER;
|
|
518
|
+
const occurrences = [];
|
|
519
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
520
|
+
collectItemImageOccurrences(items[i], [i], placeholder, occurrences);
|
|
521
|
+
}
|
|
522
|
+
const elidedCount = Math.max(0, occurrences.length - keepLast);
|
|
523
|
+
if (elidedCount === 0) {
|
|
524
|
+
return { items: items.slice(), imageCount: occurrences.length, elidedCount: 0 };
|
|
525
|
+
}
|
|
526
|
+
const cloned = structuredClone(items);
|
|
527
|
+
for (const occurrence of occurrences.slice(0, elidedCount)) {
|
|
528
|
+
setPath(cloned, occurrence.path, occurrence.replacement);
|
|
529
|
+
}
|
|
530
|
+
return { items: cloned, imageCount: occurrences.length, elidedCount };
|
|
531
|
+
}
|
|
532
|
+
function collectItemImageOccurrences(item, path, placeholder, out) {
|
|
533
|
+
if (!isRecord(item)) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (item.type === "message" && (item.role === "user" || item.role === "system")) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (item.type === "computer_call_result" || item.type === "computer_call_output") {
|
|
540
|
+
collectComputerOutputImages(item, path, placeholder, out);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (item.type === "function_call_result" || item.type === "function_call_output") {
|
|
544
|
+
collectToolResultImages(item.output, [...path, "output"], placeholder, out);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function collectComputerOutputImages(item, path, placeholder, out) {
|
|
548
|
+
const output = item.output;
|
|
549
|
+
if (!isRecord(output) || output.type !== "computer_screenshot") {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
for (const key of ["data", "image_url", "imageUrl"]) {
|
|
553
|
+
if (isImageDataUrl(output[key])) {
|
|
554
|
+
out.push({ path: [...path, "output", key], replacement: placeholder });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function collectToolResultImages(value, path, placeholder, out) {
|
|
560
|
+
if (typeof value === "string") {
|
|
561
|
+
if (isImageDataUrl(value)) {
|
|
562
|
+
out.push({ path, replacement: placeholder });
|
|
563
|
+
}
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (Array.isArray(value)) {
|
|
567
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
568
|
+
collectToolResultImages(value[i], [...path, i], placeholder, out);
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (!isRecord(value)) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (value.type === "input_image") {
|
|
576
|
+
for (const key of ["image", "imageUrl", "image_url"]) {
|
|
577
|
+
if (isImageDataUrl(value[key])) {
|
|
578
|
+
out.push({ path, replacement: { type: "input_text", text: placeholder } });
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const key of ["content", "text", "output"]) {
|
|
584
|
+
if (key in value) {
|
|
585
|
+
collectToolResultImages(value[key], [...path, key], placeholder, out);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function isImageDataUrl(value) {
|
|
590
|
+
return typeof value === "string" && DATA_IMAGE_BASE64_PATTERN.test(value);
|
|
591
|
+
}
|
|
592
|
+
function isRecord(value) {
|
|
593
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
594
|
+
}
|
|
595
|
+
function setPath(root, path, value) {
|
|
596
|
+
if (path.length === 0) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
let cursor = root;
|
|
600
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
601
|
+
const segment = path[i];
|
|
602
|
+
cursor = Array.isArray(cursor) ? cursor[segment] : cursor[segment];
|
|
603
|
+
}
|
|
604
|
+
const last = path[path.length - 1];
|
|
605
|
+
if (Array.isArray(cursor)) {
|
|
606
|
+
cursor[last] = value;
|
|
607
|
+
} else {
|
|
608
|
+
cursor[last] = value;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
512
612
|
// src/codex-tool-search.ts
|
|
513
613
|
import { toolSearchTool } from "@openai/agents";
|
|
514
614
|
var CODEX_APPS_TOOL_PREFIX = "codex_apps__";
|
|
@@ -685,13 +785,22 @@ function installCodexToolSearch(agent, connectorNamespaces = NO_NAMESPACES) {
|
|
|
685
785
|
|
|
686
786
|
// src/context-compaction.ts
|
|
687
787
|
var COMPACTION_SUMMARY_MARKER = "opengeni_context_summary";
|
|
688
|
-
var
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
788
|
+
var SUMMARY_BUFFER_TOKENS = 2e4;
|
|
789
|
+
var COMPACT_USER_MESSAGE_MAX_TOKENS = 2e4;
|
|
790
|
+
var CLIENT_COMPACTION_TRIGGER_FRACTION = 0.9;
|
|
791
|
+
var COMPACTION_PROMPT = [
|
|
792
|
+
"You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.",
|
|
793
|
+
"",
|
|
794
|
+
"Include:",
|
|
795
|
+
"- Current progress and key decisions made",
|
|
796
|
+
"- Important context, constraints, or user preferences",
|
|
797
|
+
"- What remains to be done (clear next steps)",
|
|
798
|
+
"- Any critical data, examples, or references needed to continue",
|
|
692
799
|
"",
|
|
693
|
-
"
|
|
800
|
+
"Be concise, structured, and focused on helping the next LLM seamlessly continue the work."
|
|
694
801
|
].join("\n");
|
|
802
|
+
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:";
|
|
803
|
+
var USER_MESSAGE_TRUNCATION_MARKER = "\n[... middle truncated for context compaction ...]\n";
|
|
695
804
|
var RESULT_TYPE_BY_CALL_TYPE2 = {
|
|
696
805
|
function_call: "function_call_result",
|
|
697
806
|
computer_call: "computer_call_result",
|
|
@@ -735,6 +844,56 @@ function estimateTokens(items) {
|
|
|
735
844
|
}
|
|
736
845
|
return total;
|
|
737
846
|
}
|
|
847
|
+
function clientCompactionThresholdTokens(input) {
|
|
848
|
+
const available = Math.max(
|
|
849
|
+
0,
|
|
850
|
+
input.contextWindowTokens - input.contextReservedOutputTokens - SUMMARY_BUFFER_TOKENS
|
|
851
|
+
);
|
|
852
|
+
return Math.floor(available * CLIENT_COMPACTION_TRIGGER_FRACTION);
|
|
853
|
+
}
|
|
854
|
+
function decideClientCompaction(input) {
|
|
855
|
+
const thresholdTokens = clientCompactionThresholdTokens(input);
|
|
856
|
+
const recorded = typeof input.lastInputTokens === "number" && input.lastInputTokens > 0 ? input.lastInputTokens : 0;
|
|
857
|
+
const signalTokens = recorded > 0 ? recorded : estimateTokens(input.items);
|
|
858
|
+
if (input.items.length === 0) {
|
|
859
|
+
return { shouldCompact: false, reason: "no_history", signalTokens, thresholdTokens };
|
|
860
|
+
}
|
|
861
|
+
if (input.force) {
|
|
862
|
+
return { shouldCompact: true, reason: "force", signalTokens, thresholdTokens };
|
|
863
|
+
}
|
|
864
|
+
if (signalTokens > thresholdTokens) {
|
|
865
|
+
return { shouldCompact: true, reason: "above_threshold", signalTokens, thresholdTokens };
|
|
866
|
+
}
|
|
867
|
+
return { shouldCompact: false, reason: "below_threshold", signalTokens, thresholdTokens };
|
|
868
|
+
}
|
|
869
|
+
var CompactionNeededError = class extends Error {
|
|
870
|
+
signalTokens;
|
|
871
|
+
thresholdTokens;
|
|
872
|
+
signalSource;
|
|
873
|
+
constructor(input) {
|
|
874
|
+
super(
|
|
875
|
+
`Context compaction needed: signal ${input.signalTokens} tokens exceeded threshold ${input.thresholdTokens}`
|
|
876
|
+
);
|
|
877
|
+
this.name = "CompactionNeededError";
|
|
878
|
+
this.signalTokens = input.signalTokens;
|
|
879
|
+
this.thresholdTokens = input.thresholdTokens;
|
|
880
|
+
this.signalSource = input.signalSource;
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
function findCompactionNeededError(error, seen = /* @__PURE__ */ new WeakSet()) {
|
|
884
|
+
if (error instanceof CompactionNeededError) {
|
|
885
|
+
return error;
|
|
886
|
+
}
|
|
887
|
+
if (!error || typeof error !== "object") {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
if (seen.has(error)) {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
seen.add(error);
|
|
894
|
+
const record = error;
|
|
895
|
+
return findCompactionNeededError(record.cause, seen) ?? findCompactionNeededError(record.error, seen);
|
|
896
|
+
}
|
|
738
897
|
function findKeepBoundary(items, keepRecentTokens) {
|
|
739
898
|
const boundaries = [];
|
|
740
899
|
for (let i = 0; i < items.length; i += 1) {
|
|
@@ -771,150 +930,116 @@ function enforceInputBudget(items, maxTokens, trailingTokens = 0) {
|
|
|
771
930
|
estimatedTokens: estimateTokens(kept) + Math.max(0, trailingTokens)
|
|
772
931
|
};
|
|
773
932
|
}
|
|
774
|
-
function
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
tailItems: [...input.items]
|
|
789
|
-
};
|
|
790
|
-
if (!input.force && signalTokens < softLimit) {
|
|
791
|
-
return empty;
|
|
792
|
-
}
|
|
793
|
-
const effectiveKeepRecent = hardForced ? Math.min(
|
|
794
|
-
Math.floor(input.keepRecentTokens / 2),
|
|
795
|
-
Math.floor(input.inputBudgetTokens / 4)
|
|
796
|
-
) : input.keepRecentTokens;
|
|
797
|
-
const boundaryIndex = findKeepBoundary(input.items, effectiveKeepRecent);
|
|
798
|
-
if (boundaryIndex <= 0) {
|
|
799
|
-
return { ...empty, reason: "no_boundary", boundaryIndex };
|
|
800
|
-
}
|
|
801
|
-
const prefix = input.items.slice(0, boundaryIndex);
|
|
802
|
-
const tailItems = input.items.slice(boundaryIndex);
|
|
803
|
-
let priorSummaryItem = null;
|
|
804
|
-
const prefixItems = [];
|
|
805
|
-
for (const item of prefix) {
|
|
806
|
-
if (isCompactionSummary(item)) {
|
|
807
|
-
priorSummaryItem = item;
|
|
933
|
+
function buildCompactionPromptInput(items) {
|
|
934
|
+
return [
|
|
935
|
+
...items,
|
|
936
|
+
{
|
|
937
|
+
type: "message",
|
|
938
|
+
role: "user",
|
|
939
|
+
content: COMPACTION_PROMPT
|
|
940
|
+
}
|
|
941
|
+
];
|
|
942
|
+
}
|
|
943
|
+
function buildCompactionReplacementHistory(items, summaryBody) {
|
|
944
|
+
const history = [];
|
|
945
|
+
for (const item of items) {
|
|
946
|
+
if (!isUserMessage(item) || isCompactionSummary(item)) {
|
|
808
947
|
continue;
|
|
809
948
|
}
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
if (prefixItems.length === 0) {
|
|
813
|
-
return { ...empty, reason: "nothing_to_summarize", boundaryIndex };
|
|
949
|
+
history.push(compactUserMessage(item));
|
|
814
950
|
}
|
|
951
|
+
history.push(buildSummaryItem(summaryBody));
|
|
952
|
+
return history;
|
|
953
|
+
}
|
|
954
|
+
function buildSummaryItem(summaryBody) {
|
|
955
|
+
const trimmed = summaryBody.trim();
|
|
815
956
|
return {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
prefixItems,
|
|
822
|
-
priorSummaryItem,
|
|
823
|
-
tailItems
|
|
957
|
+
type: "message",
|
|
958
|
+
role: "user",
|
|
959
|
+
content: `${SUMMARY_PREFIX}
|
|
960
|
+
${trimmed}`,
|
|
961
|
+
[COMPACTION_SUMMARY_MARKER]: true
|
|
824
962
|
};
|
|
825
963
|
}
|
|
826
|
-
function
|
|
827
|
-
|
|
828
|
-
|
|
964
|
+
function compactUserMessage(item) {
|
|
965
|
+
const text = messageText(item);
|
|
966
|
+
const next = { ...item };
|
|
967
|
+
if (estimatedTextTokens(text) > COMPACT_USER_MESSAGE_MAX_TOKENS) {
|
|
968
|
+
next.content = truncateMiddleByEstimatedTokens(text, COMPACT_USER_MESSAGE_MAX_TOKENS);
|
|
969
|
+
return next;
|
|
970
|
+
}
|
|
971
|
+
next.content = contentWithoutImages(item);
|
|
972
|
+
return next;
|
|
973
|
+
}
|
|
974
|
+
function estimatedTextTokens(text) {
|
|
975
|
+
return Math.ceil(text.length / 4);
|
|
976
|
+
}
|
|
977
|
+
function truncateMiddleByEstimatedTokens(text, maxTokens) {
|
|
978
|
+
const maxChars = Math.max(0, maxTokens * 4);
|
|
979
|
+
if (text.length <= maxChars) {
|
|
980
|
+
return text;
|
|
981
|
+
}
|
|
982
|
+
if (maxChars <= USER_MESSAGE_TRUNCATION_MARKER.length) {
|
|
983
|
+
return USER_MESSAGE_TRUNCATION_MARKER.slice(0, maxChars);
|
|
829
984
|
}
|
|
985
|
+
const keepChars = maxChars - USER_MESSAGE_TRUNCATION_MARKER.length;
|
|
986
|
+
const headChars = Math.ceil(keepChars / 2);
|
|
987
|
+
const tailChars = Math.floor(keepChars / 2);
|
|
988
|
+
return `${text.slice(0, headChars)}${USER_MESSAGE_TRUNCATION_MARKER}${text.slice(text.length - tailChars)}`;
|
|
989
|
+
}
|
|
990
|
+
function contentWithoutImages(item) {
|
|
991
|
+
const content = item.content;
|
|
992
|
+
if (!Array.isArray(content)) {
|
|
993
|
+
return content;
|
|
994
|
+
}
|
|
995
|
+
return content.filter((part) => {
|
|
996
|
+
if (!part || typeof part !== "object") {
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
999
|
+
const type = part.type;
|
|
1000
|
+
return type !== "input_image" && type !== "image_url";
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function messageText(item) {
|
|
830
1004
|
const content = item.content;
|
|
831
1005
|
if (typeof content === "string") {
|
|
832
|
-
return
|
|
1006
|
+
return content;
|
|
833
1007
|
}
|
|
834
1008
|
if (Array.isArray(content)) {
|
|
835
|
-
|
|
1009
|
+
return content.map((part) => {
|
|
836
1010
|
if (part && typeof part === "object") {
|
|
837
|
-
const
|
|
838
|
-
|
|
1011
|
+
const record = part;
|
|
1012
|
+
if (typeof record.text === "string") {
|
|
1013
|
+
return record.text;
|
|
1014
|
+
}
|
|
1015
|
+
if (typeof record.content === "string") {
|
|
1016
|
+
return record.content;
|
|
1017
|
+
}
|
|
839
1018
|
}
|
|
840
1019
|
return "";
|
|
841
1020
|
}).join("");
|
|
842
|
-
return stripSummaryPrefix(text);
|
|
843
1021
|
}
|
|
844
1022
|
return "";
|
|
845
1023
|
}
|
|
846
|
-
function
|
|
847
|
-
|
|
848
|
-
const idx = text.indexOf(marker);
|
|
849
|
-
return idx >= 0 ? text.slice(idx + marker.length) : text;
|
|
850
|
-
}
|
|
851
|
-
function buildSummaryItem(summaryBody) {
|
|
852
|
-
return {
|
|
853
|
-
type: "message",
|
|
854
|
-
role: "user",
|
|
855
|
-
content: `${SUMMARY_PREFIX}${summaryBody}`,
|
|
856
|
-
[COMPACTION_SUMMARY_MARKER]: true
|
|
857
|
-
};
|
|
858
|
-
}
|
|
859
|
-
var SUMMARY_INSTRUCTIONS = [
|
|
860
|
-
"You are compacting the earlier part of a long-running agent conversation into a compact working-memory checkpoint so the agent can continue past the model's context limit.",
|
|
861
|
-
"Durable facts already live in the workspace notebook and document bases (via MCP). Do NOT re-derive or copy those; summarize POINTERS, not contents.",
|
|
862
|
-
"Capture, concisely and factually:",
|
|
863
|
-
"- The current objective and the key decisions made so far.",
|
|
864
|
-
"- Open blockers and anything in-progress.",
|
|
865
|
-
"- Deployed / infrastructure state that has changed (what exists now).",
|
|
866
|
-
"- Environment and credential facts BY REFERENCE ONLY \u2014 name the env var keys, secret names, or notebook/document ids; NEVER copy a secret value, token, key, or password.",
|
|
867
|
-
"- Concrete next steps.",
|
|
868
|
-
"Say explicitly that durable facts are in the notebook and that this summary lists pointers, not contents.",
|
|
869
|
-
"Output only the summary body \u2014 no preamble, no markdown headers, plain prose or terse bullets."
|
|
870
|
-
].join("\n");
|
|
871
|
-
function renderPrefixTranscript(items, priorSummaryText) {
|
|
872
|
-
const lines = [];
|
|
873
|
-
if (priorSummaryText.trim().length > 0) {
|
|
874
|
-
lines.push("PRIOR CHECKPOINT SUMMARY (fold this forward; it already replaced even older history):");
|
|
875
|
-
lines.push(priorSummaryText.trim());
|
|
876
|
-
lines.push("");
|
|
877
|
-
lines.push("CONVERSATION SINCE THAT CHECKPOINT:");
|
|
878
|
-
} else {
|
|
879
|
-
lines.push("CONVERSATION TO SUMMARIZE:");
|
|
880
|
-
}
|
|
881
|
-
for (const item of items) {
|
|
882
|
-
lines.push(renderItem(item));
|
|
883
|
-
}
|
|
884
|
-
return lines.join("\n");
|
|
1024
|
+
function renderCompactionPromptInputForChat(input) {
|
|
1025
|
+
return input.map(renderItem).join("\n");
|
|
885
1026
|
}
|
|
886
1027
|
function renderItem(item) {
|
|
887
1028
|
const type = itemType2(item) ?? "unknown";
|
|
888
1029
|
if (type === "message") {
|
|
889
1030
|
const role = itemRole(item) ?? "assistant";
|
|
890
|
-
return `[${role}] ${
|
|
1031
|
+
return `[${role}] ${truncateForTranscript(messageText(item), 4e3)}`;
|
|
891
1032
|
}
|
|
892
1033
|
if (type === "reasoning") {
|
|
893
1034
|
return "[reasoning] (omitted)";
|
|
894
1035
|
}
|
|
895
1036
|
if (RESULT_TYPES2.has(type)) {
|
|
896
|
-
return `[tool_result] ${
|
|
1037
|
+
return `[tool_result] ${truncateForTranscript(resultText(item), 2e3)}`;
|
|
897
1038
|
}
|
|
898
1039
|
if (RESULT_TYPE_BY_CALL_TYPE2[type]) {
|
|
899
|
-
return `[tool_call ${type}] ${
|
|
900
|
-
}
|
|
901
|
-
return `[${type}] ${truncate(safeStringify(item), 1e3)}`;
|
|
902
|
-
}
|
|
903
|
-
function messageText(item) {
|
|
904
|
-
const content = item.content;
|
|
905
|
-
if (typeof content === "string") {
|
|
906
|
-
return content;
|
|
907
|
-
}
|
|
908
|
-
if (Array.isArray(content)) {
|
|
909
|
-
return content.map((part) => {
|
|
910
|
-
if (part && typeof part === "object") {
|
|
911
|
-
const t = part.text;
|
|
912
|
-
return typeof t === "string" ? t : "";
|
|
913
|
-
}
|
|
914
|
-
return "";
|
|
915
|
-
}).join("");
|
|
1040
|
+
return `[tool_call ${type}] ${truncateForTranscript(callText(item), 1e3)}`;
|
|
916
1041
|
}
|
|
917
|
-
return
|
|
1042
|
+
return `[${type}] ${truncateForTranscript(safeStringify(item), 1e3)}`;
|
|
918
1043
|
}
|
|
919
1044
|
function resultText(item) {
|
|
920
1045
|
const output = item.output;
|
|
@@ -937,18 +1062,11 @@ function safeStringify(value) {
|
|
|
937
1062
|
return String(value);
|
|
938
1063
|
}
|
|
939
1064
|
}
|
|
940
|
-
function
|
|
1065
|
+
function truncateForTranscript(text, max) {
|
|
941
1066
|
if (text.length <= max) {
|
|
942
1067
|
return text;
|
|
943
1068
|
}
|
|
944
|
-
return `${text.slice(0, max)}
|
|
945
|
-
}
|
|
946
|
-
function buildCompactionMessages(plan) {
|
|
947
|
-
const priorText = compactionSummaryText(plan.priorSummaryItem);
|
|
948
|
-
return {
|
|
949
|
-
system: SUMMARY_INSTRUCTIONS,
|
|
950
|
-
user: renderPrefixTranscript(plan.prefixItems, priorText)
|
|
951
|
-
};
|
|
1069
|
+
return `${text.slice(0, max)}... (${text.length - max} more chars)`;
|
|
952
1070
|
}
|
|
953
1071
|
|
|
954
1072
|
// src/sandbox-computer.ts
|
|
@@ -966,8 +1084,8 @@ var DEFAULT_DIMENSIONS = [1280, 800];
|
|
|
966
1084
|
var ACTION_YIELD_MS = 15e3;
|
|
967
1085
|
var SCROLL_NOTCH_PIXELS = 100;
|
|
968
1086
|
var SCROLL_MAX_CLICKS = 15;
|
|
969
|
-
var
|
|
970
|
-
var SCREENSHOT_RETRY_DELAY_MS =
|
|
1087
|
+
var SCREENSHOT_WARMUP_BUDGET_MS = 3e4;
|
|
1088
|
+
var SCREENSHOT_RETRY_DELAY_MS = 750;
|
|
971
1089
|
var KEYSYM = {
|
|
972
1090
|
ctrl: "ctrl",
|
|
973
1091
|
control: "ctrl",
|
|
@@ -1036,6 +1154,8 @@ var SandboxComputer = class {
|
|
|
1036
1154
|
typeDelayMs;
|
|
1037
1155
|
readOnly;
|
|
1038
1156
|
tmp;
|
|
1157
|
+
screenshotWarmupBudgetMs;
|
|
1158
|
+
screenshotRetryDelayMs;
|
|
1039
1159
|
constructor(session, opts = {}) {
|
|
1040
1160
|
this.session = session;
|
|
1041
1161
|
this.display = opts.display ?? DEFAULT_DISPLAY;
|
|
@@ -1046,6 +1166,8 @@ var SandboxComputer = class {
|
|
|
1046
1166
|
this.typeDelayMs = opts.typeDelayMs ?? 12;
|
|
1047
1167
|
this.readOnly = opts.readOnly ?? false;
|
|
1048
1168
|
this.tmp = opts.screenshotTmpDir ?? "/tmp";
|
|
1169
|
+
this.screenshotWarmupBudgetMs = opts.screenshotWarmupBudgetMs ?? SCREENSHOT_WARMUP_BUDGET_MS;
|
|
1170
|
+
this.screenshotRetryDelayMs = opts.screenshotRetryDelayMs ?? SCREENSHOT_RETRY_DELAY_MS;
|
|
1049
1171
|
}
|
|
1050
1172
|
/** Rebind to a freshly resumed-by-id session after a box rollover / re-establish. */
|
|
1051
1173
|
rebind(session) {
|
|
@@ -1090,10 +1212,13 @@ var SandboxComputer = class {
|
|
|
1090
1212
|
}
|
|
1091
1213
|
async screenshot() {
|
|
1092
1214
|
let lastError;
|
|
1093
|
-
|
|
1215
|
+
const deadline = Date.now() + this.screenshotWarmupBudgetMs;
|
|
1216
|
+
let attempt = 0;
|
|
1217
|
+
while (true) {
|
|
1094
1218
|
if (attempt > 0) {
|
|
1095
|
-
await new Promise((r) => setTimeout(r,
|
|
1219
|
+
await new Promise((r) => setTimeout(r, this.screenshotRetryDelayMs));
|
|
1096
1220
|
}
|
|
1221
|
+
attempt++;
|
|
1097
1222
|
const f = `${this.tmp}/og-shot-${Date.now()}-${Math.random().toString(36).slice(2)}.png`;
|
|
1098
1223
|
try {
|
|
1099
1224
|
await this.x(`scrot --pointer --overwrite ${f}`);
|
|
@@ -1107,6 +1232,9 @@ var SandboxComputer = class {
|
|
|
1107
1232
|
} finally {
|
|
1108
1233
|
await this.x(`rm -f ${f}`).catch(() => void 0);
|
|
1109
1234
|
}
|
|
1235
|
+
if (Date.now() + this.screenshotRetryDelayMs >= deadline) {
|
|
1236
|
+
break;
|
|
1237
|
+
}
|
|
1110
1238
|
}
|
|
1111
1239
|
if (lastError instanceof Error) {
|
|
1112
1240
|
throw lastError;
|
|
@@ -1635,20 +1763,17 @@ function configureOpenAI(settings) {
|
|
|
1635
1763
|
}
|
|
1636
1764
|
setDefaultModelProvider(router);
|
|
1637
1765
|
}
|
|
1638
|
-
async function summarizeForCompaction(settings,
|
|
1766
|
+
async function summarizeForCompaction(settings, input, options = {}) {
|
|
1639
1767
|
const client = options.client ?? buildOpenAIClientFromSettings(settings);
|
|
1640
1768
|
const api = options.api ?? "responses";
|
|
1641
1769
|
const model = options.model ?? settings.openaiModel;
|
|
1642
|
-
const maxTokens = options.maxOutputTokens ??
|
|
1770
|
+
const maxTokens = options.maxOutputTokens ?? SUMMARY_BUFFER_TOKENS;
|
|
1643
1771
|
try {
|
|
1644
1772
|
if (api === "chat") {
|
|
1645
1773
|
const completion = await client.chat.completions.create({
|
|
1646
1774
|
model,
|
|
1647
1775
|
max_tokens: maxTokens,
|
|
1648
|
-
messages: [
|
|
1649
|
-
{ role: "system", content: messages.system },
|
|
1650
|
-
{ role: "user", content: messages.user }
|
|
1651
|
-
]
|
|
1776
|
+
messages: [{ role: "user", content: renderCompactionPromptInputForChat(input) }]
|
|
1652
1777
|
});
|
|
1653
1778
|
const text2 = completion.choices?.[0]?.message?.content;
|
|
1654
1779
|
const trimmed2 = typeof text2 === "string" ? text2.trim() : "";
|
|
@@ -1661,10 +1786,7 @@ async function summarizeForCompaction(settings, messages, options = {}) {
|
|
|
1661
1786
|
// built-in path (api "responses"), so gate it on the built-in provider.
|
|
1662
1787
|
...settings.openaiProvider === "azure" ? {} : { store: false },
|
|
1663
1788
|
max_output_tokens: maxTokens,
|
|
1664
|
-
input
|
|
1665
|
-
{ role: "system", content: messages.system },
|
|
1666
|
-
{ role: "user", content: messages.user }
|
|
1667
|
-
]
|
|
1789
|
+
input
|
|
1668
1790
|
});
|
|
1669
1791
|
const text = extractResponseOutputText(response);
|
|
1670
1792
|
const trimmed = text.trim();
|
|
@@ -2233,6 +2355,52 @@ var normalizeComputerCallsFilter = ({ modelData }) => ({
|
|
|
2233
2355
|
modelData.input
|
|
2234
2356
|
)
|
|
2235
2357
|
});
|
|
2358
|
+
function contextRobustnessFilterForSettings(settings, options = {}) {
|
|
2359
|
+
const inputBudgetTokens = modelCallBudgetTokens(settings);
|
|
2360
|
+
const clientCompactionMode = resolveContextCompactionMode(settings) === "client";
|
|
2361
|
+
const compactionThresholdTokens = clientCompactionThresholdTokens(settings);
|
|
2362
|
+
return ({ modelData }) => {
|
|
2363
|
+
const images = elideStaleScreenshotImages(modelData.input);
|
|
2364
|
+
if (images.elidedCount > 0) {
|
|
2365
|
+
console.warn(
|
|
2366
|
+
`per-call image history policy elided ${images.elidedCount} older screenshot image(s), keeping the last ${Math.min(3, images.imageCount)} full image(s)`
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
let input = images.items;
|
|
2370
|
+
if (inputBudgetTokens !== void 0) {
|
|
2371
|
+
const guarded = enforceInputBudget(
|
|
2372
|
+
input,
|
|
2373
|
+
inputBudgetTokens
|
|
2374
|
+
);
|
|
2375
|
+
if (guarded.trimmed) {
|
|
2376
|
+
console.warn(
|
|
2377
|
+
`per-call budget guard trimmed ${guarded.droppedCount} oldest history item(s) to fit input budget (${inputBudgetTokens} tokens); the over-budget model call was NOT sent`
|
|
2378
|
+
);
|
|
2379
|
+
input = guarded.items;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
if (clientCompactionMode && options.throwOnCompactionNeeded) {
|
|
2383
|
+
const reported = options.contextCompactionSignalTokens?.();
|
|
2384
|
+
const hasReported = typeof reported === "number" && reported > 0;
|
|
2385
|
+
const signalTokens = hasReported ? reported : estimateTokens(input);
|
|
2386
|
+
if (signalTokens > compactionThresholdTokens) {
|
|
2387
|
+
throw new CompactionNeededError({
|
|
2388
|
+
signalTokens,
|
|
2389
|
+
thresholdTokens: compactionThresholdTokens,
|
|
2390
|
+
signalSource: hasReported ? "provider" : "estimate"
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
return { ...modelData, input };
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
function modelCallBudgetTokens(settings) {
|
|
2398
|
+
if (resolveContextCompactionMode(settings) !== "client") {
|
|
2399
|
+
return void 0;
|
|
2400
|
+
}
|
|
2401
|
+
const budget = contextInputBudgetTokens(settings);
|
|
2402
|
+
return budget > 0 ? budget : void 0;
|
|
2403
|
+
}
|
|
2236
2404
|
function composeCallModelInputFilters(filters) {
|
|
2237
2405
|
return async (args) => {
|
|
2238
2406
|
let modelData = args.modelData;
|
|
@@ -2242,11 +2410,12 @@ function composeCallModelInputFilters(filters) {
|
|
|
2242
2410
|
return modelData;
|
|
2243
2411
|
};
|
|
2244
2412
|
}
|
|
2245
|
-
function callModelInputFilterForSettings(settings) {
|
|
2413
|
+
function callModelInputFilterForSettings(settings, options = {}) {
|
|
2246
2414
|
const filters = [normalizeComputerCallsFilter];
|
|
2247
2415
|
if (settings.openaiProviderItemIds === "strip") {
|
|
2248
2416
|
filters.push(stripProviderItemIdsFilter);
|
|
2249
2417
|
}
|
|
2418
|
+
filters.push(contextRobustnessFilterForSettings(settings, options));
|
|
2250
2419
|
return composeCallModelInputFilters(filters);
|
|
2251
2420
|
}
|
|
2252
2421
|
async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
@@ -2283,7 +2452,13 @@ async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
|
2283
2452
|
}
|
|
2284
2453
|
const decoratedClient = withSandboxLifecycleHooks(resourceClient2, ownedHooks, ownedHookContext);
|
|
2285
2454
|
const ownedFilter = composeCallModelInputFilters(
|
|
2286
|
-
[
|
|
2455
|
+
[
|
|
2456
|
+
callModelInputFilterForSettings(settings, {
|
|
2457
|
+
throwOnCompactionNeeded: Boolean(overrides.contextCompactionSignalTokens),
|
|
2458
|
+
...overrides.contextCompactionSignalTokens ? { contextCompactionSignalTokens: overrides.contextCompactionSignalTokens } : {}
|
|
2459
|
+
}),
|
|
2460
|
+
overrides.callModelInputFilter
|
|
2461
|
+
].filter(
|
|
2287
2462
|
(f) => Boolean(f)
|
|
2288
2463
|
)
|
|
2289
2464
|
);
|
|
@@ -2319,18 +2494,23 @@ async function runAgentStream(agent, input, settings, overrides = {}) {
|
|
|
2319
2494
|
}) : void 0;
|
|
2320
2495
|
const sandboxSessionState = prepared.sandboxSessionState ?? (prepared.serializedRunStateForSandbox && client ? await restoredSandboxSessionState(await RunState.fromString(agent, prepared.serializedRunStateForSandbox), client) : void 0);
|
|
2321
2496
|
const callModelInputFilter = composeCallModelInputFilters(
|
|
2322
|
-
[
|
|
2497
|
+
[
|
|
2498
|
+
callModelInputFilterForSettings(settings, {
|
|
2499
|
+
throwOnCompactionNeeded: Boolean(overrides.contextCompactionSignalTokens),
|
|
2500
|
+
...overrides.contextCompactionSignalTokens ? { contextCompactionSignalTokens: overrides.contextCompactionSignalTokens } : {}
|
|
2501
|
+
}),
|
|
2502
|
+
overrides.callModelInputFilter
|
|
2503
|
+
].filter(
|
|
2323
2504
|
(f) => Boolean(f)
|
|
2324
2505
|
)
|
|
2325
2506
|
);
|
|
2326
2507
|
const runOptions = {
|
|
2327
2508
|
stream: true,
|
|
2328
2509
|
maxTurns: settings.agentMaxModelCallsPerTurn,
|
|
2329
|
-
//
|
|
2330
|
-
//
|
|
2331
|
-
//
|
|
2332
|
-
//
|
|
2333
|
-
// id 'rs_…' not found"; with the ids gone the request is self-contained.
|
|
2510
|
+
// Built-in per-call guard chain: normalize computer calls, optionally strip
|
|
2511
|
+
// provider ids, elide stale screenshots in every mode, and trim to the input
|
|
2512
|
+
// budget on the client-compaction path. This runs for turn-start replay AND
|
|
2513
|
+
// every mid-turn follow-up.
|
|
2334
2514
|
callModelInputFilter
|
|
2335
2515
|
};
|
|
2336
2516
|
void settings.disableOpenaiTracing;
|
|
@@ -3503,12 +3683,16 @@ function approvalIdentifier(item) {
|
|
|
3503
3683
|
export {
|
|
3504
3684
|
ActiveBackendUnresolvableError,
|
|
3505
3685
|
CAPABILITY_DESCRIPTORS,
|
|
3686
|
+
CLIENT_COMPACTION_TRIGGER_FRACTION,
|
|
3687
|
+
COMPACTION_PROMPT,
|
|
3506
3688
|
COMPACTION_SUMMARY_MARKER,
|
|
3689
|
+
COMPACT_USER_MESSAGE_MAX_TOKENS,
|
|
3507
3690
|
ChannelAConflictError,
|
|
3508
3691
|
ChannelANotFoundError,
|
|
3509
3692
|
ChannelAUnsupportedError,
|
|
3510
3693
|
ChannelAValidationError,
|
|
3511
3694
|
CodexSubscriptionUnavailableError,
|
|
3695
|
+
CompactionNeededError,
|
|
3512
3696
|
ComputerActionError,
|
|
3513
3697
|
ComputerReadOnlyError,
|
|
3514
3698
|
ComputerUnavailableError,
|
|
@@ -3530,12 +3714,13 @@ export {
|
|
|
3530
3714
|
RecordingUnavailableError,
|
|
3531
3715
|
RoutingSandboxSession,
|
|
3532
3716
|
RoutingUnsupportedError,
|
|
3717
|
+
SCREENSHOT_OMITTED_PLACEHOLDER,
|
|
3533
3718
|
SELFHOSTED_DEFAULT_TIMEOUT_MS,
|
|
3534
3719
|
SELFHOSTED_RECONNECT_WINDOW_MS,
|
|
3535
3720
|
SELFHOSTED_RELAY_STREAM_PATH,
|
|
3536
3721
|
STREAM_PORT,
|
|
3537
3722
|
STREAM_TOKEN_DEFAULT_TTL_SECONDS,
|
|
3538
|
-
|
|
3723
|
+
SUMMARY_BUFFER_TOKENS,
|
|
3539
3724
|
SUMMARY_PREFIX,
|
|
3540
3725
|
SandboxChannelAService,
|
|
3541
3726
|
SandboxComputer,
|
|
@@ -3550,6 +3735,7 @@ export {
|
|
|
3550
3735
|
TERMINAL_STREAM_PORT,
|
|
3551
3736
|
TerminalServerError,
|
|
3552
3737
|
TerminalServerUnsupportedError,
|
|
3738
|
+
USER_MESSAGE_TRUNCATION_MARKER,
|
|
3553
3739
|
agentErrorToControlError,
|
|
3554
3740
|
agentsErrorRunState,
|
|
3555
3741
|
applyMissingManifestEntries,
|
|
@@ -3560,7 +3746,8 @@ export {
|
|
|
3560
3746
|
azureOpenAIDefaultQuery,
|
|
3561
3747
|
backendSupportsOs,
|
|
3562
3748
|
buildAgentCapabilities,
|
|
3563
|
-
|
|
3749
|
+
buildCompactionPromptInput,
|
|
3750
|
+
buildCompactionReplacementHistory,
|
|
3564
3751
|
buildDisplayStackScript,
|
|
3565
3752
|
buildManifest,
|
|
3566
3753
|
buildModelInstance,
|
|
@@ -3572,21 +3759,24 @@ export {
|
|
|
3572
3759
|
buildSummaryItem,
|
|
3573
3760
|
buildTerminalServerScript,
|
|
3574
3761
|
callModelInputFilterForSettings,
|
|
3762
|
+
clientCompactionThresholdTokens,
|
|
3575
3763
|
collectSandboxEnvironment,
|
|
3576
|
-
compactionSummaryText,
|
|
3577
3764
|
composeAgentInstructions,
|
|
3578
3765
|
computerUse,
|
|
3579
3766
|
configureOpenAI,
|
|
3580
3767
|
contentTypeForCodec,
|
|
3768
|
+
contextRobustnessFilterForSettings,
|
|
3581
3769
|
coreInstructions,
|
|
3582
3770
|
createProductionAgentRuntime,
|
|
3583
3771
|
createSandboxClient,
|
|
3584
3772
|
createSandboxClientForBackend,
|
|
3773
|
+
decideClientCompaction,
|
|
3585
3774
|
decodeModalSnapshotId,
|
|
3586
3775
|
deletePriorPersistedSnapshot,
|
|
3587
3776
|
deleteRecordingArtifacts,
|
|
3588
3777
|
deserializeSandboxSessionStateEnvelope,
|
|
3589
3778
|
desktopCapableBackend,
|
|
3779
|
+
elideStaleScreenshotImages,
|
|
3590
3780
|
enforceInputBudget,
|
|
3591
3781
|
ensureDisplayStack,
|
|
3592
3782
|
ensureReadableStreamFrom,
|
|
@@ -3597,6 +3787,7 @@ export {
|
|
|
3597
3787
|
exposeStreamPort,
|
|
3598
3788
|
extForCodec,
|
|
3599
3789
|
extractResponseOutputText,
|
|
3790
|
+
findCompactionNeededError,
|
|
3600
3791
|
findKeepBoundary,
|
|
3601
3792
|
isCompactionSummary,
|
|
3602
3793
|
isExecSessionLostBanner,
|
|
@@ -3623,14 +3814,13 @@ export {
|
|
|
3623
3814
|
parseNumstatZ,
|
|
3624
3815
|
parsePorcelainV2,
|
|
3625
3816
|
parseUnifiedPatch,
|
|
3626
|
-
planCompaction,
|
|
3627
3817
|
prefixedMcpToolName,
|
|
3628
3818
|
prepareAgentTools,
|
|
3629
3819
|
prepareRunInput,
|
|
3630
3820
|
readRecordingBytes,
|
|
3631
3821
|
readWorkspaceArchiveFromEnvelopeSessionState,
|
|
3632
3822
|
recordingStorageKey,
|
|
3633
|
-
|
|
3823
|
+
renderCompactionPromptInputForChat,
|
|
3634
3824
|
repositoryCloneCommand,
|
|
3635
3825
|
repositoryUsesSandboxClone,
|
|
3636
3826
|
resolveTurnModel,
|