@opengeni/runtime 0.14.3 → 0.14.15

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.
Files changed (78) hide show
  1. package/dist/{chunk-4YJ5Z52Z.js → chunk-7E2ZSCZK.js} +6 -6
  2. package/dist/{chunk-4YJ5Z52Z.js.map → chunk-7E2ZSCZK.js.map} +1 -1
  3. package/dist/{chunk-GQIFSLAC.js → chunk-GNEGANUF.js} +892 -297
  4. package/dist/chunk-GNEGANUF.js.map +1 -0
  5. package/dist/codex-tool-search.d.ts +100 -0
  6. package/dist/context-compaction.d.ts +236 -0
  7. package/dist/history-sanitizer.d.ts +320 -0
  8. package/dist/index.d.ts +186 -718
  9. package/dist/index.js +612 -126
  10. package/dist/index.js.map +1 -1
  11. package/dist/{localSources-T3HZ6KY4.js → localSources-BXHBKGF4.js} +2 -2
  12. package/dist/mcp-network.d.ts +32 -35
  13. package/dist/metrics.d.ts +33 -0
  14. package/dist/sandbox/capabilities.d.ts +11 -0
  15. package/dist/sandbox/channel-a.d.ts +226 -0
  16. package/dist/sandbox/display-stack.d.ts +99 -0
  17. package/dist/sandbox/errors.d.ts +16 -0
  18. package/dist/sandbox/exec-banner.d.ts +19 -0
  19. package/dist/sandbox/index.d.ts +190 -6
  20. package/dist/sandbox/index.js +20 -4
  21. package/dist/sandbox/op-correlation.d.ts +24 -0
  22. package/dist/sandbox/provider-errors.d.ts +32 -0
  23. package/dist/sandbox/provider-operation-gate.d.ts +12 -0
  24. package/dist/sandbox/providers/blaxel.d.ts +2 -0
  25. package/dist/sandbox/providers/cloudflare.d.ts +2 -0
  26. package/dist/sandbox/providers/daytona.d.ts +2 -0
  27. package/dist/sandbox/providers/docker.d.ts +2 -0
  28. package/dist/sandbox/providers/e2b.d.ts +2 -0
  29. package/dist/sandbox/providers/index.d.ts +11 -0
  30. package/dist/sandbox/providers/local.d.ts +2 -0
  31. package/dist/sandbox/providers/modal.d.ts +132 -0
  32. package/dist/sandbox/providers/none.d.ts +2 -0
  33. package/dist/sandbox/providers/runloop.d.ts +2 -0
  34. package/dist/sandbox/providers/selfhosted.d.ts +2 -0
  35. package/dist/sandbox/providers/types.d.ts +30 -0
  36. package/dist/sandbox/providers/vercel.d.ts +2 -0
  37. package/dist/sandbox/recording.d.ts +80 -0
  38. package/dist/sandbox/routing/backend-resolver.d.ts +173 -0
  39. package/dist/sandbox/routing/routing-session.d.ts +401 -0
  40. package/dist/sandbox/run-credentials.d.ts +65 -0
  41. package/dist/sandbox/select.d.ts +120 -0
  42. package/dist/sandbox/selfhosted/capabilities.d.ts +91 -0
  43. package/dist/sandbox/selfhosted/control-rpc.d.ts +175 -0
  44. package/dist/sandbox/selfhosted/fault-rendering.d.ts +40 -0
  45. package/dist/sandbox/selfhosted/op-observer.d.ts +40 -0
  46. package/dist/sandbox/selfhosted/op-stream.d.ts +104 -0
  47. package/dist/sandbox/selfhosted/op-testing.d.ts +108 -0
  48. package/dist/sandbox/selfhosted/op-transport.d.ts +60 -0
  49. package/dist/sandbox/selfhosted/retry-policy.d.ts +106 -0
  50. package/dist/sandbox/selfhosted/session.d.ts +552 -0
  51. package/dist/sandbox/selfhosted/testing.d.ts +48 -0
  52. package/dist/sandbox/stream-port.d.ts +80 -0
  53. package/dist/sandbox/stream-token.d.ts +44 -0
  54. package/dist/sandbox/terminal-server.d.ts +67 -0
  55. package/dist/sandbox/toolspace-token.d.ts +23 -0
  56. package/dist/sandbox/turn-tool-cancellation.d.ts +72 -0
  57. package/dist/sandbox/workspace-archive.d.ts +39 -0
  58. package/dist/sandbox-computer.d.ts +227 -0
  59. package/dist/screenshot-error-card.d.ts +1 -0
  60. package/dist/skill-library.d.ts +10 -12
  61. package/dist/usage-telemetry.d.ts +45 -0
  62. package/dist/workspace-skills.d.ts +31 -0
  63. package/package.json +7 -7
  64. package/src/context-compaction.ts +600 -6
  65. package/src/history-sanitizer.ts +10 -92
  66. package/src/index.ts +301 -62
  67. package/src/sandbox/channel-a.ts +267 -81
  68. package/src/sandbox/index.ts +56 -94
  69. package/src/sandbox/provider-operation-gate.ts +110 -0
  70. package/src/sandbox/providers/modal.ts +433 -3
  71. package/src/sandbox/routing/routing-session.ts +20 -7
  72. package/src/sandbox/selfhosted/session.ts +7 -6
  73. package/src/sandbox/turn-tool-cancellation.ts +47 -7
  74. package/src/sandbox/workspace-archive.ts +176 -73
  75. package/src/sandbox-computer.ts +25 -75
  76. package/dist/chunk-GQIFSLAC.js.map +0 -1
  77. package/dist/index-BraazrAO.d.ts +0 -2931
  78. /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 {};