@opengeni/runtime 0.14.3 → 0.14.16
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-4YJ5Z52Z.js → chunk-7E2ZSCZK.js} +6 -6
- package/dist/{chunk-4YJ5Z52Z.js.map → chunk-7E2ZSCZK.js.map} +1 -1
- package/dist/{chunk-GQIFSLAC.js → chunk-GNEGANUF.js} +892 -297
- package/dist/chunk-GNEGANUF.js.map +1 -0
- package/dist/codex-tool-search.d.ts +100 -0
- package/dist/context-compaction.d.ts +236 -0
- package/dist/history-sanitizer.d.ts +320 -0
- package/dist/index.d.ts +186 -718
- package/dist/index.js +612 -126
- package/dist/index.js.map +1 -1
- package/dist/{localSources-T3HZ6KY4.js → localSources-BXHBKGF4.js} +2 -2
- package/dist/mcp-network.d.ts +32 -35
- package/dist/metrics.d.ts +33 -0
- package/dist/sandbox/capabilities.d.ts +11 -0
- package/dist/sandbox/channel-a.d.ts +226 -0
- package/dist/sandbox/display-stack.d.ts +99 -0
- package/dist/sandbox/errors.d.ts +16 -0
- package/dist/sandbox/exec-banner.d.ts +19 -0
- package/dist/sandbox/index.d.ts +190 -6
- package/dist/sandbox/index.js +20 -4
- package/dist/sandbox/op-correlation.d.ts +24 -0
- package/dist/sandbox/provider-errors.d.ts +32 -0
- package/dist/sandbox/provider-operation-gate.d.ts +12 -0
- package/dist/sandbox/providers/blaxel.d.ts +2 -0
- package/dist/sandbox/providers/cloudflare.d.ts +2 -0
- package/dist/sandbox/providers/daytona.d.ts +2 -0
- package/dist/sandbox/providers/docker.d.ts +2 -0
- package/dist/sandbox/providers/e2b.d.ts +2 -0
- package/dist/sandbox/providers/index.d.ts +11 -0
- package/dist/sandbox/providers/local.d.ts +2 -0
- package/dist/sandbox/providers/modal.d.ts +132 -0
- package/dist/sandbox/providers/none.d.ts +2 -0
- package/dist/sandbox/providers/runloop.d.ts +2 -0
- package/dist/sandbox/providers/selfhosted.d.ts +2 -0
- package/dist/sandbox/providers/types.d.ts +30 -0
- package/dist/sandbox/providers/vercel.d.ts +2 -0
- package/dist/sandbox/recording.d.ts +80 -0
- package/dist/sandbox/routing/backend-resolver.d.ts +173 -0
- package/dist/sandbox/routing/routing-session.d.ts +401 -0
- package/dist/sandbox/run-credentials.d.ts +65 -0
- package/dist/sandbox/select.d.ts +120 -0
- package/dist/sandbox/selfhosted/capabilities.d.ts +91 -0
- package/dist/sandbox/selfhosted/control-rpc.d.ts +175 -0
- package/dist/sandbox/selfhosted/fault-rendering.d.ts +40 -0
- package/dist/sandbox/selfhosted/op-observer.d.ts +40 -0
- package/dist/sandbox/selfhosted/op-stream.d.ts +104 -0
- package/dist/sandbox/selfhosted/op-testing.d.ts +108 -0
- package/dist/sandbox/selfhosted/op-transport.d.ts +60 -0
- package/dist/sandbox/selfhosted/retry-policy.d.ts +106 -0
- package/dist/sandbox/selfhosted/session.d.ts +552 -0
- package/dist/sandbox/selfhosted/testing.d.ts +48 -0
- package/dist/sandbox/stream-port.d.ts +80 -0
- package/dist/sandbox/stream-token.d.ts +44 -0
- package/dist/sandbox/terminal-server.d.ts +67 -0
- package/dist/sandbox/toolspace-token.d.ts +23 -0
- package/dist/sandbox/turn-tool-cancellation.d.ts +72 -0
- package/dist/sandbox/workspace-archive.d.ts +39 -0
- package/dist/sandbox-computer.d.ts +227 -0
- package/dist/screenshot-error-card.d.ts +1 -0
- package/dist/skill-library.d.ts +10 -12
- package/dist/usage-telemetry.d.ts +45 -0
- package/dist/workspace-skills.d.ts +31 -0
- package/package.json +7 -7
- package/src/context-compaction.ts +600 -6
- package/src/history-sanitizer.ts +10 -92
- package/src/index.ts +301 -62
- package/src/sandbox/channel-a.ts +267 -81
- package/src/sandbox/index.ts +56 -94
- package/src/sandbox/provider-operation-gate.ts +110 -0
- package/src/sandbox/providers/modal.ts +433 -3
- package/src/sandbox/routing/routing-session.ts +20 -7
- package/src/sandbox/selfhosted/session.ts +7 -6
- package/src/sandbox/turn-tool-cancellation.ts +47 -7
- package/src/sandbox/workspace-archive.ts +176 -73
- package/src/sandbox-computer.ts +25 -75
- package/dist/chunk-GQIFSLAC.js.map +0 -1
- package/dist/index-BraazrAO.d.ts +0 -2931
- /package/dist/{localSources-T3HZ6KY4.js.map → localSources-BXHBKGF4.js.map} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { type Tool } from "@openai/agents";
|
|
2
|
+
/** The prefix OpenGeni's PrefixedMcpServer stamps on codex_apps connector tools. */
|
|
3
|
+
export declare const CODEX_APPS_TOOL_PREFIX = "codex_apps__";
|
|
4
|
+
/** True for a materialized codex_apps connector function tool. */
|
|
5
|
+
export declare function isCodexAppsFunctionTool(tool: unknown): tool is Tool & {
|
|
6
|
+
name: string;
|
|
7
|
+
deferLoading?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* True for non-mandatory MCP function tools on the effective agent surface.
|
|
11
|
+
* The prefix is the same runtime namespace used by PrefixedMcpServer; this
|
|
12
|
+
* intentionally does not inspect the global registry, connection metadata, or
|
|
13
|
+
* credentials, so search cannot widen the already-authorized tool set.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isSearchableMcpFunctionTool(tool: unknown, mcpServerIds?: ReadonlySet<string>): tool is Tool & {
|
|
16
|
+
name: string;
|
|
17
|
+
deferLoading?: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Rank connector tools against a plain-language query with BM25 (Okapi, k1=1.5,
|
|
21
|
+
* b=0.75) over tokenized name+description+params. Returns the top `limit` tools
|
|
22
|
+
* with a positive score, most-relevant first. An empty or no-match query returns
|
|
23
|
+
* [] — matching codex-rs, whose search returns empty rather than arbitrary tools;
|
|
24
|
+
* disclosing unrelated (and thereby CALLABLE) tools on a miss feeds the model
|
|
25
|
+
* noise. The SDK normalizes an empty executor result into an empty
|
|
26
|
+
* tool_search_output, which the model reads as "nothing matched — rephrase".
|
|
27
|
+
*/
|
|
28
|
+
export declare function bm25RankTools(tools: Tool[], query: string, limit: number): Tool[];
|
|
29
|
+
/**
|
|
30
|
+
* Render the search tool's description from the account's ACTUALLY-connected
|
|
31
|
+
* sources — codex-rs parity (its create_tool_search_tool builds "You have access
|
|
32
|
+
* to tools from the following sources:\n- <name>" from the live enabled-source
|
|
33
|
+
* list). With defer_loading stripping every connector schema (and name) from
|
|
34
|
+
* model context, this description is the model's ONLY signal of which apps
|
|
35
|
+
* exist, so a hardcoded list would both advertise absent connectors and hide
|
|
36
|
+
* present ones.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderSearchToolDescription(connectorNamespaces: ReadonlySet<string>): string;
|
|
39
|
+
/**
|
|
40
|
+
* A per-TURN freeze cell for the tool_search description (AM-8). The connector Set
|
|
41
|
+
* `installCodexToolSearch` reads is LIVE and by-reference — it fills as the turn's
|
|
42
|
+
* codex_apps tools/list resolves and can change between a turn's model calls.
|
|
43
|
+
* Re-rendering the description per call from a changed Set flips the tools block,
|
|
44
|
+
* and because tools precede the history in the request prefix, ANY change misses the
|
|
45
|
+
* ENTIRE conversation prefix from that point on — a proven prompt-cache breaker.
|
|
46
|
+
*
|
|
47
|
+
* We therefore freeze the description ONCE, at the first model call whose Set has
|
|
48
|
+
* been populated (connectors discovered), and reuse that frozen string for the rest
|
|
49
|
+
* of the turn — trading a slightly staler connector list for a byte-stable prefix.
|
|
50
|
+
* The freeze is created per {@link installCodexToolSearch} (once per turn) and shared
|
|
51
|
+
* by reference into every clone re-install, so all of a turn's clones agree.
|
|
52
|
+
*/
|
|
53
|
+
export type CodexToolSearchDescriptionFreeze = {
|
|
54
|
+
value: string | null;
|
|
55
|
+
};
|
|
56
|
+
/** Build the client-executed tool_search tool that discloses deferred MCP tools on demand. */
|
|
57
|
+
export declare function buildCodexToolSearchTool(connectorNamespaces?: ReadonlySet<string>, mcpServerIds?: ReadonlySet<string>): Tool;
|
|
58
|
+
/**
|
|
59
|
+
* The transform applied to a turn's resolved tool list: tag every searchable
|
|
60
|
+
* non-mandatory MCP function tool `deferLoading = true`, and — unless one is already
|
|
61
|
+
* present — append the client tool_search tool. Pure (mutates the passed tools +
|
|
62
|
+
* returns the possibly-extended array); the SDK's `getTools` gate requires a
|
|
63
|
+
* tool_search whenever a deferred tool is present, which appending here satisfies
|
|
64
|
+
* in the same request.
|
|
65
|
+
*
|
|
66
|
+
* The search tool is appended UNCONDITIONALLY (even when the turn has no
|
|
67
|
+
* searchable MCP tools, e.g. a best-effort connector was dropped this
|
|
68
|
+
* turn): a prior turn's history may carry tool_search_call/output items, and
|
|
69
|
+
* replaying those without a tool_search tool in the request risks a backend
|
|
70
|
+
* reject; a search over an empty pool simply discloses nothing. The description
|
|
71
|
+
* reflects the LIVE connector namespaces, so an empty turn reads
|
|
72
|
+
* "none currently available".
|
|
73
|
+
*/
|
|
74
|
+
export declare function applyCodexToolSearch(tools: Tool[], connectorNamespaces?: ReadonlySet<string>, descriptionFreeze?: CodexToolSearchDescriptionFreeze, mcpServerIds?: ReadonlySet<string>): Tool[];
|
|
75
|
+
type CloneCapableAgent = {
|
|
76
|
+
getAllTools: (runContext: unknown) => Promise<Tool[]>;
|
|
77
|
+
clone?: (config: unknown) => CloneCapableAgent;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Install progressive MCP disclosure on a Codex-path agent by wrapping
|
|
81
|
+
* `getAllTools` so every per-model-call tool resolution runs
|
|
82
|
+
* {@link applyCodexToolSearch}. Idempotent-per-call (re-tags each
|
|
83
|
+
* freshly-materialized MCP tool under cacheToolsList:false). Gated by the caller
|
|
84
|
+
* (flag + Codex path).
|
|
85
|
+
*
|
|
86
|
+
* CLONE SURVIVAL (the part that makes this work on the REAL sandbox path): the
|
|
87
|
+
* SDK's sandbox runtime routes EVERY model call through
|
|
88
|
+
* `prepareSandboxAgent → agent.clone(...)` (agentPreparation.js), and
|
|
89
|
+
* `SandboxAgent.clone` constructs a FRESH agent from a fixed field list — an
|
|
90
|
+
* instance-own `getAllTools` override is NOT copied, so patching only this
|
|
91
|
+
* instance would silently no-op the whole feature on sandbox turns (the run
|
|
92
|
+
* loop resolves tools on the CLONE). We therefore also wrap `clone` to
|
|
93
|
+
* RE-INSTALL onto every clone, recursively — covering clone-of-clone and the
|
|
94
|
+
* RunState resume paths. `connectorNamespaces` is the LIVE, by-reference Set the
|
|
95
|
+
* codex_apps sanitizing transport fills during each turn's tools/list
|
|
96
|
+
* (prepareAgentTools), so by the time the wrapper post-processes getAllTools the
|
|
97
|
+
* current turn's connector sources are known.
|
|
98
|
+
*/
|
|
99
|
+
export declare function installCodexToolSearch(agent: CloneCapableAgent, connectorNamespaces?: ReadonlySet<string>, mcpServerIds?: ReadonlySet<string>): void;
|
|
100
|
+
export {};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable conversation context compaction, following Codex CLI's local path.
|
|
3
|
+
*
|
|
4
|
+
* The checkpoint model sees the current active history plus one fixed
|
|
5
|
+
* checkpoint prompt, then the active history is rebuilt from the newest real
|
|
6
|
+
* user messages within one cumulative 20k-token budget plus one summary.
|
|
7
|
+
* Assistant messages, tool calls/results, reasoning, and images are removed
|
|
8
|
+
* from the active model-facing history; the database audit rows remain.
|
|
9
|
+
*/
|
|
10
|
+
export type CompactionItem = Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Marker stored on the synthetic summary item so the UI can render it and the
|
|
13
|
+
* next rebuild can exclude old summaries from the retained user-message set.
|
|
14
|
+
*/
|
|
15
|
+
export declare const COMPACTION_SUMMARY_MARKER = "opengeni_context_summary";
|
|
16
|
+
export declare const SUMMARY_BUFFER_TOKENS = 20000;
|
|
17
|
+
export declare const COMPACT_USER_MESSAGE_MAX_TOKENS = 20000;
|
|
18
|
+
/** Codex CLI remote compaction v2 retained-message budget (codex-rs). */
|
|
19
|
+
export declare const REMOTE_V2_RETAINED_MESSAGE_TOKEN_BUDGET = 64000;
|
|
20
|
+
export declare const REMOTE_COMPACTION_V2_IMPLEMENTATION: "responses_compaction_v2";
|
|
21
|
+
export declare const REMOTE_COMPACTION_V2_BETA_FEATURE: "remote_compaction_v2";
|
|
22
|
+
export declare const DEFAULT_COMPACTION_THRESHOLD_RATIO = 0.9;
|
|
23
|
+
export declare const MIN_COMPACTION_THRESHOLD_RATIO = 0.3;
|
|
24
|
+
export declare const MAX_COMPACTION_THRESHOLD_RATIO = 0.9;
|
|
25
|
+
export declare const COMPACTION_PROMPT: string;
|
|
26
|
+
export declare const 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:";
|
|
27
|
+
export declare const USER_MESSAGE_TRUNCATION_MARKER = "\n[... middle truncated for context compaction ...]\n";
|
|
28
|
+
/** A user-authored `message` item is the only legal turn boundary. */
|
|
29
|
+
export declare function isUserMessage(item: unknown): boolean;
|
|
30
|
+
/** True for our synthetic compaction summary item. */
|
|
31
|
+
export declare function isCompactionSummary(item: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Conservative tokenizer-independent text estimate used for every local input
|
|
34
|
+
* budget. Preserve the stable ASCII `chars / 4` heuristic, but never discount
|
|
35
|
+
* Unicode as UTF-16 length/4: each non-ASCII code unit costs at least one token
|
|
36
|
+
* (CJK ≈ 1/code point; an astral emoji's surrogate pair ≈ 2). This intentionally
|
|
37
|
+
* errs toward compaction rather than letting multilingual current input exceed
|
|
38
|
+
* a provider context window before an authoritative usage anchor exists.
|
|
39
|
+
*/
|
|
40
|
+
export declare function estimateTextTokens(text: string): number;
|
|
41
|
+
export declare const UNKNOWN_IMAGE_TOKENS = 4096;
|
|
42
|
+
export declare const MAX_NATIVE_IMAGE_TOKENS = 8192;
|
|
43
|
+
export type NativeImageEstimateReason = "dimensions" | "bounded_fallback";
|
|
44
|
+
export type NativeImageTokenEstimate = {
|
|
45
|
+
tokens: number;
|
|
46
|
+
width: number | null;
|
|
47
|
+
height: number | null;
|
|
48
|
+
detail: string;
|
|
49
|
+
reason: NativeImageEstimateReason;
|
|
50
|
+
};
|
|
51
|
+
export type ModelInputTokenBreakdown = {
|
|
52
|
+
totalTokens: number;
|
|
53
|
+
textTokens: number;
|
|
54
|
+
imageTokens: number;
|
|
55
|
+
imageCount: number;
|
|
56
|
+
imageFallbackCount: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Provider-neutral local estimate for one native image. Inline bytes are used
|
|
60
|
+
* only to recover dimensions; their base64 length never becomes text tokens.
|
|
61
|
+
*/
|
|
62
|
+
export declare function estimateNativeImageTokens(input: {
|
|
63
|
+
source?: unknown;
|
|
64
|
+
width?: number | null;
|
|
65
|
+
height?: number | null;
|
|
66
|
+
detail?: unknown;
|
|
67
|
+
}): NativeImageTokenEstimate;
|
|
68
|
+
/**
|
|
69
|
+
* Codex CLI `estimate_reasoning_length`: opaque encrypted payloads are not
|
|
70
|
+
* model-visible as raw JSON/base64. Visible bytes ≈ `len * 3/4 - 650`.
|
|
71
|
+
*/
|
|
72
|
+
export declare function estimateOpaqueEncryptedModelVisibleBytes(encodedLen: number): number;
|
|
73
|
+
/** Codex CLI bytes→tokens for opaque encrypted content (`ceil(bytes / 4)`). */
|
|
74
|
+
export declare function estimateOpaqueEncryptedTokens(encodedLen: number): number;
|
|
75
|
+
/**
|
|
76
|
+
* Length of opaque encrypted content that must use the Codex encrypted
|
|
77
|
+
* estimator instead of JSON.stringify (compaction blobs and reasoning).
|
|
78
|
+
*/
|
|
79
|
+
export declare function opaqueEncryptedContentLength(item: unknown): number | null;
|
|
80
|
+
/** Native-image-aware item estimate for pre-call accounting and retained budgets. */
|
|
81
|
+
export declare function estimateItemTokenBreakdown(item: CompactionItem): ModelInputTokenBreakdown;
|
|
82
|
+
export declare function estimateItemTokens(item: CompactionItem): number;
|
|
83
|
+
export declare function estimateTokens(items: readonly CompactionItem[]): number;
|
|
84
|
+
export declare function estimateTokensBreakdown(items: readonly CompactionItem[]): ModelInputTokenBreakdown;
|
|
85
|
+
export declare function estimateSerializedValueTokens(value: unknown): number;
|
|
86
|
+
export type CompleteModelInputFootprint = {
|
|
87
|
+
input: readonly CompactionItem[];
|
|
88
|
+
instructionsTokens: number;
|
|
89
|
+
toolSchemaTokens: number;
|
|
90
|
+
};
|
|
91
|
+
export type ProviderContextTokenSignal = {
|
|
92
|
+
/** Monotonic within one sampled run; advances after every model response. */
|
|
93
|
+
revision: number;
|
|
94
|
+
/** Provider total tokens after that response (input + generated output). */
|
|
95
|
+
totalTokens: number;
|
|
96
|
+
};
|
|
97
|
+
export type CompleteModelInputEstimate = {
|
|
98
|
+
tokens: number;
|
|
99
|
+
source: "complete_estimate" | "provider_plus_local";
|
|
100
|
+
inputTokens: number;
|
|
101
|
+
inputTextTokens: number;
|
|
102
|
+
inputImageTokens: number;
|
|
103
|
+
inputImageCount: number;
|
|
104
|
+
inputImageFallbackCount: number;
|
|
105
|
+
instructionsTokens: number;
|
|
106
|
+
toolSchemaTokens: number;
|
|
107
|
+
appendedAfterModelTokens: number;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Match Codex history accounting: after one provider response, start from its
|
|
111
|
+
* authoritative TOTAL token count and add only local items placed after the
|
|
112
|
+
* newest model-generated item. System instructions and tool schemas are
|
|
113
|
+
* compared with the exact request footprint that produced the provider count;
|
|
114
|
+
* positive growth is added. Without a bound anchor, estimate the entire
|
|
115
|
+
* outgoing request rather than trusting stale usage from an earlier turn.
|
|
116
|
+
*/
|
|
117
|
+
export declare function estimateCompleteModelInput(input: {
|
|
118
|
+
current: CompleteModelInputFootprint;
|
|
119
|
+
provider?: ProviderContextTokenSignal | null;
|
|
120
|
+
providerRequestFootprint?: CompleteModelInputFootprint | null;
|
|
121
|
+
}): CompleteModelInputEstimate;
|
|
122
|
+
export declare function itemsAfterLastModelGeneratedItem(items: readonly CompactionItem[]): CompactionItem[];
|
|
123
|
+
export declare function hasModelGeneratedItem(items: readonly CompactionItem[]): boolean;
|
|
124
|
+
export declare function clampCompactionThresholdRatio(value: number | undefined | null): number;
|
|
125
|
+
export declare function compactionThresholdTokens(input: {
|
|
126
|
+
contextWindowTokens: number;
|
|
127
|
+
contextReservedOutputTokens: number;
|
|
128
|
+
contextAutoCompactThresholdTokens?: number | null | undefined;
|
|
129
|
+
contextCompactionThresholdRatio?: number | null | undefined;
|
|
130
|
+
}): number;
|
|
131
|
+
export type CompactionDecision = {
|
|
132
|
+
shouldCompact: boolean;
|
|
133
|
+
reason: "force" | "above_threshold" | "below_threshold" | "no_history";
|
|
134
|
+
signalTokens: number;
|
|
135
|
+
thresholdTokens: number;
|
|
136
|
+
};
|
|
137
|
+
export declare function decideCompaction(input: {
|
|
138
|
+
items: readonly CompactionItem[];
|
|
139
|
+
lastInputTokens?: number | null;
|
|
140
|
+
contextWindowTokens: number;
|
|
141
|
+
contextReservedOutputTokens: number;
|
|
142
|
+
contextAutoCompactThresholdTokens?: number | null | undefined;
|
|
143
|
+
contextCompactionThresholdRatio?: number | null | undefined;
|
|
144
|
+
force?: boolean;
|
|
145
|
+
}): CompactionDecision;
|
|
146
|
+
export declare class CompactionNeededError extends Error {
|
|
147
|
+
readonly signalTokens: number;
|
|
148
|
+
readonly thresholdTokens: number;
|
|
149
|
+
readonly signalSource: "provider" | "estimate";
|
|
150
|
+
readonly trigger: "threshold" | "operator";
|
|
151
|
+
constructor(input: {
|
|
152
|
+
signalTokens: number;
|
|
153
|
+
thresholdTokens: number;
|
|
154
|
+
signalSource: "provider" | "estimate";
|
|
155
|
+
trigger?: "threshold" | "operator";
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
export declare class EmptyCompactionSummaryError extends Error {
|
|
159
|
+
readonly diagnostics: Record<string, unknown>;
|
|
160
|
+
constructor(diagnostics?: Record<string, unknown>);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The checkpoint model request ended in a provider/transport failure rather
|
|
164
|
+
* than a successful response with an empty assistant message. Diagnostics are
|
|
165
|
+
* deliberately bounded and content-free so this error can be persisted on a
|
|
166
|
+
* turn without copying provider messages or conversation input into events.
|
|
167
|
+
*/
|
|
168
|
+
export declare class CompactionProviderResponseError extends Error {
|
|
169
|
+
readonly diagnostics: Record<string, unknown>;
|
|
170
|
+
readonly status?: number;
|
|
171
|
+
readonly code?: string;
|
|
172
|
+
readonly type?: string;
|
|
173
|
+
readonly cause?: unknown;
|
|
174
|
+
constructor(diagnostics?: Record<string, unknown>, cause?: unknown);
|
|
175
|
+
}
|
|
176
|
+
export declare function findCompactionNeededError(error: unknown, seen?: WeakSet<object>): CompactionNeededError | null;
|
|
177
|
+
/**
|
|
178
|
+
* The exact checkpoint input shape: current active history followed by Codex's
|
|
179
|
+
* checkpoint prompt as a synthesized user message.
|
|
180
|
+
*/
|
|
181
|
+
export declare function buildCompactionPromptInput(items: readonly CompactionItem[]): CompactionItem[];
|
|
182
|
+
export type PreparedCompactionPromptInput = {
|
|
183
|
+
input: CompactionItem[];
|
|
184
|
+
estimatedInputTokens: number;
|
|
185
|
+
rewrittenToolOutputs: number;
|
|
186
|
+
droppedHistoryItems: number;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Fit the explicit checkpoint request without mutating canonical history.
|
|
190
|
+
*
|
|
191
|
+
* Codex first replaces oversized tool outputs in its temporary remote-
|
|
192
|
+
* compaction input. OpenGeni does the same oldest-first, preserving the most
|
|
193
|
+
* recent tool detail for the plaintext summary. If that is still insufficient,
|
|
194
|
+
* whole oldest user-delimited work units are removed and the remaining suffix
|
|
195
|
+
* is protocol-sanitized so no call/result/reasoning fragment is orphaned.
|
|
196
|
+
* The raw active history is still the source for the eventual replacement and
|
|
197
|
+
* remains unchanged if the provider call fails.
|
|
198
|
+
*/
|
|
199
|
+
export declare function prepareCompactionPromptInput(items: readonly CompactionItem[], maxInputTokens: number): PreparedCompactionPromptInput;
|
|
200
|
+
/**
|
|
201
|
+
* Build the active history after compaction:
|
|
202
|
+
* the newest real user messages that fit one cumulative 20k-token budget
|
|
203
|
+
* (prior summaries excluded, images removed) plus one marked summary item.
|
|
204
|
+
*/
|
|
205
|
+
export declare function buildCompactionReplacementHistory(items: readonly CompactionItem[], summaryBody: string): CompactionItem[];
|
|
206
|
+
/** True for a Codex remote-compaction output item with opaque encrypted content. */
|
|
207
|
+
export declare function isRemoteCompactionItem(item: unknown): item is CompactionItem;
|
|
208
|
+
/** Messages retained beside a remote v2 compaction blob (user + developer). */
|
|
209
|
+
export declare function isRetainedRemoteV2Message(item: unknown): boolean;
|
|
210
|
+
/**
|
|
211
|
+
* Build the active history after Codex remote compaction v2:
|
|
212
|
+
* newest retained user/developer messages within the CLI 64k budget plus the
|
|
213
|
+
* opaque `{ type: "compaction", encrypted_content }` item.
|
|
214
|
+
*/
|
|
215
|
+
export declare function buildRemoteV2ReplacementHistory(items: readonly CompactionItem[], compactionItem: CompactionItem): CompactionItem[];
|
|
216
|
+
/**
|
|
217
|
+
* Append the transient compaction_trigger used only for the remote v2 request.
|
|
218
|
+
*
|
|
219
|
+
* The OpenAI Agents SDK's Responses converter rejects a bare
|
|
220
|
+
* `{ type: "compaction_trigger" }` (`UserError: Unsupported item`). It does
|
|
221
|
+
* accept `type: "unknown"` and forwards `providerData` onto the wire, which is
|
|
222
|
+
* how we deliver the Codex-only trigger through CompactionResponsesModel into
|
|
223
|
+
* the Codex fetch normalizer (which allowlists top-level `compaction_trigger`).
|
|
224
|
+
*/
|
|
225
|
+
export declare function buildRemoteCompactionV2PromptInput(items: readonly CompactionItem[]): CompactionItem[];
|
|
226
|
+
/** Extract exactly one compaction output item from a Responses payload. */
|
|
227
|
+
export declare function extractRemoteCompactionV2OutputItem(response: unknown): CompactionItem;
|
|
228
|
+
export declare function compactionReplacementFingerprint(items: readonly CompactionItem[]): string;
|
|
229
|
+
/** Fingerprint the latest durable replacement prefix in active history. */
|
|
230
|
+
export declare function latestCompactionReplacementFingerprint(items: readonly CompactionItem[]): string | null;
|
|
231
|
+
/**
|
|
232
|
+
* Build the synthetic summary item (a plain user message) appended to the
|
|
233
|
+
* rebuilt active history.
|
|
234
|
+
*/
|
|
235
|
+
export declare function buildSummaryItem(summaryBody: string): CompactionItem;
|
|
236
|
+
export declare function renderCompactionPromptInputForChat(input: readonly CompactionItem[]): string;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-path sanitizer for replayed conversation history (issue: orphaned
|
|
3
|
+
* tool outputs brick a session).
|
|
4
|
+
*
|
|
5
|
+
* Conversation truth is persisted as a flat list of SDK history items in
|
|
6
|
+
* `session_history_items` and replayed verbatim into the model on every turn.
|
|
7
|
+
* The OpenAI Responses API rejects the whole request (HTTP 400) when that list
|
|
8
|
+
* violates its tool-call pairing rules — most destructively:
|
|
9
|
+
*
|
|
10
|
+
* `400 No tool call found for function call output with call_id <X>`
|
|
11
|
+
*
|
|
12
|
+
* when a `function_call_result` (a.k.a. function_call_output) has no matching
|
|
13
|
+
* `function_call` earlier in the list. Because the corrupt item is replayed on
|
|
14
|
+
* every subsequent turn, one orphaned output permanently bricks the session
|
|
15
|
+
* across revival — it stays dead until the row is hand-deleted.
|
|
16
|
+
*
|
|
17
|
+
* This module is the reliability net: before history items are sent to the
|
|
18
|
+
* model they pass through `sanitizeHistoryItemsForModel`, which removes any
|
|
19
|
+
* item that would make the request invalid. It mirrors the SDK's own
|
|
20
|
+
* `dropOrphanToolCalls` continuation logic (which only runs over the SDK's
|
|
21
|
+
* in-memory `state.history`, not over rows we reload from the database) so a
|
|
22
|
+
* reloaded history is shaped exactly like a freshly-generated one.
|
|
23
|
+
*
|
|
24
|
+
* It is a pure function over plain JSON item shapes (no SDK import, no I/O) so
|
|
25
|
+
* it is cheap to unit-test exhaustively. It NEVER mutates its input items and
|
|
26
|
+
* NEVER touches the stored rows — only the in-memory copy sent to the model is
|
|
27
|
+
* filtered, keeping the persisted audit trail intact.
|
|
28
|
+
*/
|
|
29
|
+
/** A history item is any JSON object; we only inspect a few discriminator fields. */
|
|
30
|
+
export type HistoryItem = Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* @deprecated Model-visible history is append-only outside the explicit
|
|
33
|
+
* durable compaction transition. This compatibility identity function performs
|
|
34
|
+
* no elision and returns the exact input array with every item untouched and in
|
|
35
|
+
* its original order.
|
|
36
|
+
*/
|
|
37
|
+
export declare function elideSupersededViewImagePairs<T extends HistoryItem>(items: readonly T[]): T[];
|
|
38
|
+
/**
|
|
39
|
+
* Tool-call item types and the result-item type that settles them. Kept in
|
|
40
|
+
* sync with the SDK's `TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE`; `function_call` is
|
|
41
|
+
* the one observed live, the rest are included so the same pairing logic holds
|
|
42
|
+
* for every tool-call kind the SDK can emit.
|
|
43
|
+
*/
|
|
44
|
+
export declare const TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE: Record<string, string>;
|
|
45
|
+
/**
|
|
46
|
+
* Sanitize a replayed history item list into a sequence the Responses API
|
|
47
|
+
* accepts. Pure: returns a new array of the same item references in order,
|
|
48
|
+
* with invalid items omitted. Valid histories come back byte-identical
|
|
49
|
+
* (same references, same order).
|
|
50
|
+
*
|
|
51
|
+
* Rules, each motivated by a concrete 400 the API raises:
|
|
52
|
+
*
|
|
53
|
+
* 1. Drop every tool-call RESULT whose matching tool CALL does not appear
|
|
54
|
+
* earlier in the list. This is the session-bricking orphan: a
|
|
55
|
+
* `function_call_result` with no preceding `function_call` of the same
|
|
56
|
+
* `call_id`. ("No tool call found for function call output…")
|
|
57
|
+
*
|
|
58
|
+
* 2. Drop every tool CALL that has no matching RESULT anywhere after it.
|
|
59
|
+
* The Responses API requires each tool call to be settled by its output
|
|
60
|
+
* before the conversation can continue; a dangling call left in replayed
|
|
61
|
+
* history 400s with "No tool output found for function call…". Dropping
|
|
62
|
+
* the dangling call (rather than synthesizing a fake output) is what the
|
|
63
|
+
* SDK itself does for in-memory continuation, so a reloaded history is
|
|
64
|
+
* shaped identically. The matching result, if it later exists, is kept;
|
|
65
|
+
* only genuinely unpaired calls are removed.
|
|
66
|
+
*
|
|
67
|
+
* 3. Drop any `reasoning` item that immediately precedes (across a run of
|
|
68
|
+
* reasoning items) a dropped tool call. The Responses API ties an
|
|
69
|
+
* encrypted reasoning item to the tool call it produced; a reasoning item
|
|
70
|
+
* orphaned by rule 2 trips "Item 'rs_…' of type 'reasoning' was provided
|
|
71
|
+
* without its required following item". Mirrors the SDK's
|
|
72
|
+
* `dropReasoningItemsPrecedingDroppedCalls`.
|
|
73
|
+
*
|
|
74
|
+
* A `call_id` is paired only when BOTH a call and a result of the matching
|
|
75
|
+
* types exist with that id, the call appearing before the result. Calls and
|
|
76
|
+
* results that satisfy that survive untouched.
|
|
77
|
+
*/
|
|
78
|
+
export declare function sanitizeHistoryItemsForModel<T extends HistoryItem>(items: readonly T[], toolOutputTruncationTokens?: number): T[];
|
|
79
|
+
/**
|
|
80
|
+
* Internal marker stamping machine-generated resume/continuation messages
|
|
81
|
+
* (turn-resumed notices, goal continuations) so compaction housekeeping can
|
|
82
|
+
* recognize them in STORED history. Lives here (not context-compaction, which
|
|
83
|
+
* imports from this module) so the wire sanitizer below can strip it without
|
|
84
|
+
* an import cycle; context-compaction re-exports it.
|
|
85
|
+
*/
|
|
86
|
+
export declare const INTERNAL_RESUME_MESSAGE_MARKER = "opengeni_internal_resume";
|
|
87
|
+
/**
|
|
88
|
+
* Remove the {@link INTERNAL_RESUME_MESSAGE_MARKER} providerData key from a
|
|
89
|
+
* single item before it reaches ANY model wire. Pure + non-mutating; returns
|
|
90
|
+
* the SAME reference when there is nothing to strip (keeping the common path
|
|
91
|
+
* byte-identical, mirroring {@link stripReasoningEncryptedContent}).
|
|
92
|
+
*
|
|
93
|
+
* WHY. The @openai/agents SDK serializes providerData keys verbatim into the
|
|
94
|
+
* request item, and strict Responses backends reject unknown per-item fields —
|
|
95
|
+
* observed in production as `400 Unknown parameter:
|
|
96
|
+
* 'input[N].opengeni_internal_resume'`, which deterministically failed every
|
|
97
|
+
* turn whose replayed history contained a marked resume message (the marker is
|
|
98
|
+
* durable in conversation truth, so retries could never succeed). Detection
|
|
99
|
+
* (isInternalResumeMessage) reads STORED items and also keeps a text-prefix
|
|
100
|
+
* fallback, so stripping at the wire degrades housekeeping gracefully and
|
|
101
|
+
* never the turn.
|
|
102
|
+
*/
|
|
103
|
+
export declare function stripInternalResumeMarker<T extends HistoryItem>(item: T): T;
|
|
104
|
+
/**
|
|
105
|
+
* Drop the account/org-bound `reasoning.encrypted_content` blob from a single
|
|
106
|
+
* history item, preserving everything else (the visible chain-of-thought text in
|
|
107
|
+
* `summary`/`content`, and every non-reasoning field). Pure + non-mutating: when
|
|
108
|
+
* there is nothing to strip the SAME reference is returned (so the common,
|
|
109
|
+
* same-account path stays byte-identical); otherwise a shallow clone is returned.
|
|
110
|
+
*
|
|
111
|
+
* WHY. A codex-subscription turn round-trips `reasoning.encrypted_content` — an
|
|
112
|
+
* opaque blob minted by the ChatGPT/Codex backend that is bound to the account
|
|
113
|
+
* (org) that produced it. After a manual switch from codex account A to B, the
|
|
114
|
+
* carried history items still hold A-minted blobs; replaying them into a turn
|
|
115
|
+
* running on B is rejected (400). The blob is purely a chain-of-thought
|
|
116
|
+
* continuity optimization — dropping it costs at most one turn of lost CoT
|
|
117
|
+
* continuity and never any message content.
|
|
118
|
+
*
|
|
119
|
+
* USED FOR `compaction` items only on the history-items read path: a foreign
|
|
120
|
+
* `compaction` summary carries account-bound `encrypted_content` but its summary
|
|
121
|
+
* is real conversation content that must be preserved, so we strip only the blob
|
|
122
|
+
* (we do NOT drop the whole item). Foreign `reasoning` items are instead dropped
|
|
123
|
+
* WHOLESALE by the caller (id + blob), because the Responses backend validates
|
|
124
|
+
* the foreign `rs_…` id and rejects a reasoning item that has a foreign id and no
|
|
125
|
+
* encrypted_content (so blanking the blob alone is not enough — see
|
|
126
|
+
* {@link applyCodexHistoryStrip}).
|
|
127
|
+
*
|
|
128
|
+
* The SDK's Responses converter reads the blob via `providerData.encryptedContent`
|
|
129
|
+
* (camel) or `providerData.encrypted_content` (snake); persisted rows use the
|
|
130
|
+
* snake form, but we delete both casings defensively. We also clear a top-level
|
|
131
|
+
* `encrypted_content` (the `compaction`-item shape) belt-and-braces — that blob
|
|
132
|
+
* is likewise source-bound. Only `reasoning` and `compaction` items are touched;
|
|
133
|
+
* messages, tool calls, and tool outputs pass through untouched by reference.
|
|
134
|
+
*/
|
|
135
|
+
export declare function stripReasoningEncryptedContent<T extends HistoryItem>(item: T): T;
|
|
136
|
+
/**
|
|
137
|
+
* Neutralize the account/org-bound identity of EVERY `reasoning` item embedded
|
|
138
|
+
* in a serialized RunState JSON string, returning the re-serialized string. Pure:
|
|
139
|
+
* a parse failure or a no-op returns the SAME string reference (so an unchanged
|
|
140
|
+
* or non-codex run-state replays byte-for-byte).
|
|
141
|
+
*
|
|
142
|
+
* WHY (HOLE C — approval replay). An approval decision resumes the serialized
|
|
143
|
+
* RunState blob. That blob round-trips `reasoning.encrypted_content` minted by the ChatGPT/Codex
|
|
144
|
+
* backend (bound to the freezing account/org — a foreign account 400s it) AND the
|
|
145
|
+
* foreign `rs_…` reasoning ids the Responses backend validates (rejected once the
|
|
146
|
+
* blob is gone). Unlike `session_history_items`, the blob carries NO per-item
|
|
147
|
+
* producer tag, so foreign-ness cannot be decided per item; the worker instead
|
|
148
|
+
* records the FREEZING codex account on the run-state row and calls this only when
|
|
149
|
+
* the resuming turn's codex account DIFFERS from it. When the accounts differ we
|
|
150
|
+
* conservatively neutralize every reasoning item: delete its provider id and its
|
|
151
|
+
* `encrypted_content` (both casings, in `providerData`). The visible reasoning
|
|
152
|
+
* `content`/`summary` and every message / tool-call / tool-output item are left
|
|
153
|
+
* intact (message and tool content are never account-bound).
|
|
154
|
+
*
|
|
155
|
+
* A reasoning item with no id and no encrypted_content is exactly the shape the
|
|
156
|
+
* production Azure path already sends (see `stripProviderItemIdsFilter`), so it
|
|
157
|
+
* deserializes and replays cleanly. Reasoning items live in several places in the
|
|
158
|
+
* blob — `originalInput` (when an array), each `modelResponses[].output`,
|
|
159
|
+
* `lastModelResponse.output`, and the `generatedItems` wrappers (`reasoning_item`
|
|
160
|
+
* → `rawItem`) — and we scrub all of them. `compaction` items are deliberately
|
|
161
|
+
* left untouched: their `encrypted_content` is a protocol-REQUIRED field whose
|
|
162
|
+
* removal would fail the SDK's run-state schema validation on deserialize.
|
|
163
|
+
*/
|
|
164
|
+
export declare function stripReasoningIdentityFromSerializedRunState(serialized: string): string;
|
|
165
|
+
/**
|
|
166
|
+
* Neutralize tool_search items IN PLACE in a serialized RunState blob for a
|
|
167
|
+
* cross-account codex resume — the run-state sibling of
|
|
168
|
+
* `applyCodexHistoryStrip`'s tool_search rule, but COUNT-PRESERVING (HOLE E: the
|
|
169
|
+
* blob path's reconcile watermark counts the blob's history length, so items
|
|
170
|
+
* must never be removed — only mutated, exactly like the reasoning
|
|
171
|
+
* neutralization above).
|
|
172
|
+
*
|
|
173
|
+
* The hazard: on deserialize, the SDK re-runs the registered CLIENT tool_search
|
|
174
|
+
* execute callback per frozen pair (`rehydrateToolSearchRuntimeTools`) and
|
|
175
|
+
* THROWS a UserError when the re-run's runtime-tool keys mismatch the serialized
|
|
176
|
+
* expectation — which is exactly what happens when the RESUMING account's
|
|
177
|
+
* connector pool differs from the FREEZING account's. The SDK skips that
|
|
178
|
+
* rehydration entirely for `execution === 'server'` calls, so flipping the
|
|
179
|
+
* frozen pairs' `execution` to `"server"` in place defuses the throw without
|
|
180
|
+
* touching counts, ids, pairing, or content. The flipped shape is wire-safe:
|
|
181
|
+
* LIVE-VERIFIED against /codex/responses — a replayed server-execution pair is
|
|
182
|
+
* accepted (200) and its disclosure still holds. The account-bound `tsc_…` id is
|
|
183
|
+
* separately stripped by the codex transport normalizer (all input item ids).
|
|
184
|
+
*
|
|
185
|
+
* Walks the same blob locations as {@link stripReasoningIdentityFromSerializedRunState}:
|
|
186
|
+
* `originalInput` (array form), `generatedItems` (SDK run-item wrappers — the
|
|
187
|
+
* raw shape under `rawItem`), every `modelResponses[].output`, and
|
|
188
|
+
* `lastModelResponse.output`. Returns the input string unchanged when nothing
|
|
189
|
+
* matched.
|
|
190
|
+
*/
|
|
191
|
+
export declare function neutralizeToolSearchItemsInSerializedRunState(serialized: string): string;
|
|
192
|
+
/**
|
|
193
|
+
* Normalize `computer_call` items so each carries EXACTLY ONE of the two
|
|
194
|
+
* mutually-exclusive action fields the provider accepts.
|
|
195
|
+
*
|
|
196
|
+
* The OpenAI Agents SDK 0.11.6 `computer_call` schema (protocol.mjs) carries
|
|
197
|
+
* BOTH the legacy singular `action` and the GA batched `actions`, each
|
|
198
|
+
* `.optional()`, and only requires "at least one" (its superRefine errors only
|
|
199
|
+
* when both are absent). The Azure computer-use endpoint is stricter: it
|
|
200
|
+
* requires EXACTLY one and rejects the whole request with
|
|
201
|
+
*
|
|
202
|
+
* `400 Computer call input must include exactly one of `action` or `actions`.`
|
|
203
|
+
*
|
|
204
|
+
* when an emitted `computer_call` carries both (observed live: a screenshot
|
|
205
|
+
* call carrying `action:{type:"screenshot"}` AND `actions:[{type:"screenshot"}]`).
|
|
206
|
+
*
|
|
207
|
+
* Which singular do we keep? LIVE-PROVEN against the deployed Azure deployment
|
|
208
|
+
* (gpt-5.6-sol-2026-04-24): for gpt-5.6-sol the SDK serializes the GA computer tool as
|
|
209
|
+
* `{type:"computer"}` (not the legacy `computer_use_preview`), and that GA tool
|
|
210
|
+
* accepts ONLY the batched plural `actions`. Probing all three shapes:
|
|
211
|
+
* - `action`-only -> 400 "exactly one of action or actions" (STILL rejected)
|
|
212
|
+
* - `actions`-only -> passes the action/actions structural validation
|
|
213
|
+
* - both -> 400 "exactly one …"
|
|
214
|
+
* The "exactly one" wording is misleading: only the `actions`-only form is
|
|
215
|
+
* accepted by the GA tool. So when both are present we KEEP `actions` (the GA
|
|
216
|
+
* batched plural) and DROP `action`. Calls that already carry exactly one field
|
|
217
|
+
* — or the legacy `action`-only form — pass through untouched (this transform's
|
|
218
|
+
* sole job is to resolve the both-present conflict, not to rewrite singulars).
|
|
219
|
+
*
|
|
220
|
+
* Pure and non-mutating: only the conflicting item(s) are cloned; every other
|
|
221
|
+
* item passes through by reference (byte-identical). Unlike
|
|
222
|
+
* {@link sanitizeHistoryItemsForModel} (which only *filters* items), this is a
|
|
223
|
+
* read-path *transform* of a single item's shape.
|
|
224
|
+
*/
|
|
225
|
+
export declare function normalizeComputerCallActions<T extends HistoryItem>(items: readonly T[]): T[];
|
|
226
|
+
/**
|
|
227
|
+
* Rewrite EVERY `computer_call` item in a serialized Responses request body to
|
|
228
|
+
* the ACTIONS-ONLY shape the GA Azure computer tool accepts, mutating the parsed
|
|
229
|
+
* JSON object in place and returning whether anything changed.
|
|
230
|
+
*
|
|
231
|
+
* WHY THIS LIVES AT THE WIRE LEVEL (not the input-item filter). The input-item
|
|
232
|
+
* normalizer above ({@link normalizeComputerCallActions}, wired as a
|
|
233
|
+
* callModelInputFilter) runs BEFORE the SDK's responses converter
|
|
234
|
+
* (`convertAgentItemToResponsesInput`). That converter then re-derives the wire
|
|
235
|
+
* payload from the item: when `actions` is present it emits BOTH
|
|
236
|
+
* `{action: ..., actions: [...]}`, and when only `action` is present it emits
|
|
237
|
+
* `action`-only. It can NEVER emit actions-only. Probed live against the
|
|
238
|
+
* deployed Azure gpt-5.6-sol-2026-04-24 GA computer tool (`{type:"computer"}`):
|
|
239
|
+
* - `action`-only -> 400 "Computer call input must include exactly one of
|
|
240
|
+
* `action` or `actions`." (rejected)
|
|
241
|
+
* - both -> 400 same message (rejected)
|
|
242
|
+
* - `actions`-only -> passes the action/actions structural validation
|
|
243
|
+
* So neither the input-filter nor the converter can produce an accepted body.
|
|
244
|
+
* The ONLY seam that sees — and can rewrite — the final serialized JSON is a
|
|
245
|
+
* custom `fetch` on the OpenAI client (it runs after the converter and after
|
|
246
|
+
* `responses.create` serialization). This function is that rewriter's core.
|
|
247
|
+
*
|
|
248
|
+
* It collapses each computer_call to actions-only: it prefers an existing
|
|
249
|
+
* non-empty `actions` array, else wraps the singular `action` into
|
|
250
|
+
* `actions:[action]`, then deletes `action`. A computer_call with neither field
|
|
251
|
+
* is left untouched (nothing to derive; let the provider report it).
|
|
252
|
+
*
|
|
253
|
+
* Mutates `body` in place (the caller has already JSON.parsed a private copy of
|
|
254
|
+
* the request body). Returns `true` iff at least one computer_call was changed.
|
|
255
|
+
*/
|
|
256
|
+
export declare function rewriteComputerCallsToActionsOnly(body: unknown): boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Backstop for the empty `computer_call_output` image_url: walk the `input` array of
|
|
259
|
+
* a serialized Responses request body and replace any `computer_call_output` item
|
|
260
|
+
* whose `output.image_url` is empty/missing with a LEGIBLE "screen capture failed"
|
|
261
|
+
* error card ({@link SCREENSHOT_FAILURE_CARD_IMAGE_URL}).
|
|
262
|
+
*
|
|
263
|
+
* WHY A CARD, NOT A BLANK. An empty `image_url` reaches this seam ONLY when the
|
|
264
|
+
* computer op genuinely FAILED to produce a screen: agents-core's `toolExecution.mjs`
|
|
265
|
+
* catch sets `output = ''` when the action OR the follow-up `computer.screenshot()`
|
|
266
|
+
* throws, building `{type:"computer_call_output",output:{type:"computer_screenshot",
|
|
267
|
+
* image_url:""}}`. Azure then rejects the whole request with
|
|
268
|
+
* `400 Invalid 'input[N].output.image_url'`. The previous fix substituted a 1×1
|
|
269
|
+
* TRANSPARENT placeholder to dodge the 400 — but that reaches the model as a
|
|
270
|
+
* plausible BLANK DESKTOP it confidently reports ("the screen appears blank/empty"),
|
|
271
|
+
* turning a hard capture FAILURE into a silent, wrong observation. That is the worst
|
|
272
|
+
* failure mode for computer use, and it is exactly what the 0.1.3 TCC-denied incident
|
|
273
|
+
* produced. Substituting a legible error card instead makes the failure REACH THE
|
|
274
|
+
* MODEL as an error (the only channel the hosted `computer_use_preview` protocol has
|
|
275
|
+
* is the image), so the model stops and tells the user rather than hallucinating.
|
|
276
|
+
*
|
|
277
|
+
* WHY THIS IS SAFE (empty = failure, never an intentional blank). Post-af289e3 the
|
|
278
|
+
* intentional-blank cases carry a NON-empty data URI already: agents-core's
|
|
279
|
+
* tool-approval-rejection screenshot is its own non-empty 1×1 placeholder, and the
|
|
280
|
+
* SandboxComputer action-timeout now warn+returns to a REAL screenshot rather than an
|
|
281
|
+
* empty output. So an EMPTY image_url at this seam is unambiguously a capture/interact
|
|
282
|
+
* FAILURE — the error card is the correct substitution for every empty case, and this
|
|
283
|
+
* function never touches a non-empty (real screenshot OR intentional blank) output.
|
|
284
|
+
*
|
|
285
|
+
* The failure REASON (permission denied / null image / timeout / display down) is not
|
|
286
|
+
* on the card; it is logged worker-side by `NativeDesktopComputer.screenshot()`.
|
|
287
|
+
*
|
|
288
|
+
* Mutates `body` in place (the caller has already JSON.parsed a private copy).
|
|
289
|
+
* Returns `true` iff at least one image_url was replaced.
|
|
290
|
+
*/
|
|
291
|
+
export declare function rewriteEmptyComputerCallOutputImageUrls(body: unknown): boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Wrap a `fetch` so every outbound OpenAI Responses request body that contains a
|
|
294
|
+
* `computer_call` is rewritten to the ACTIONS-ONLY shape (see
|
|
295
|
+
* {@link rewriteComputerCallsToActionsOnly}) before it reaches the network, AND
|
|
296
|
+
* any `computer_call_output` item with an empty/missing `output.image_url` is
|
|
297
|
+
* patched with the 1×1 transparent PNG placeholder (see
|
|
298
|
+
* {@link rewriteEmptyComputerCallOutputImageUrls}).
|
|
299
|
+
*
|
|
300
|
+
* Installed as the `fetch:` option on the Azure OpenAI client, this is the
|
|
301
|
+
* lowest reachable seam — below the agents-core input filter and below the SDK's
|
|
302
|
+
* responses converter — so it neutralizes the converter's both-fields synthesis
|
|
303
|
+
* regardless of what the input item carried, and backstops the action-timeout
|
|
304
|
+
* empty image_url regardless of how it was produced.
|
|
305
|
+
*
|
|
306
|
+
* Surgical and cheap: it only parses the body when it is a string that contains
|
|
307
|
+
* the prefix `"computer_call` (matching both `"computer_call"` action items and
|
|
308
|
+
* `"computer_call_output"` result items). Every other request — non-computer-use
|
|
309
|
+
* turns, streaming SSE responses, non-string bodies — forwards untouched, the
|
|
310
|
+
* SAME `init` reference, so streaming and other providers are unaffected. A JSON
|
|
311
|
+
* parse failure or a no-op rewrite also forwards the original `init` unchanged.
|
|
312
|
+
*
|
|
313
|
+
* Typed structurally (the `(input, init) => Promise<Response>` call signature)
|
|
314
|
+
* rather than as the DOM `typeof fetch` so it omits the `preconnect` static the
|
|
315
|
+
* global type carries; this matches the OpenAI SDK's `Fetch` option, which only
|
|
316
|
+
* needs the call signature. The wiring site passes it as the client `fetch:`.
|
|
317
|
+
*/
|
|
318
|
+
type FetchLike = (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => Promise<Response>;
|
|
319
|
+
export declare function computerCallNormalizingFetch(base: FetchLike): FetchLike;
|
|
320
|
+
export {};
|