@opengeni/runtime 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  export { collectSandboxEnvironment, parseExposedPorts } from '@opengeni/config';
2
2
  export { CAPABILITY_DESCRIPTORS, CapabilityDescriptor, DESKTOP_STREAM_PORT, StreamTokenPayload, StreamTokenPayload as StreamTokenPayloadType, TERMINAL_STREAM_PORT } from '@opengeni/contracts';
3
3
  import '@openai/agents/sandbox';
4
- export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalOrphanSweepResult, z as ModalSandboxAttribution, N as NatsControlRpc, B as NatsRequestConnection, G as NegotiationContext, H as NumstatEntry, P as PROVIDER_REGISTRY, I as ProviderConstructionContext, J as ProviderRegistration, K as RecordingCodec, O as RecordingContentType, Q as RecordingError, S as RecordingProcess, T as RecordingUnavailableError, U as ResolvedActiveBackend, V as RoutableBackendSession, W as RoutableSandbox, X as RoutingSandboxSession, Y as RoutingSandboxSessionDeps, Z as RoutingTransitionEvent, _ as RoutingUnsupportedError, $ as SELFHOSTED_DEFAULT_TIMEOUT_MS, a0 as SELFHOSTED_RECONNECT_WINDOW_MS, a1 as SELFHOSTED_RELAY_STREAM_PATH, a2 as STREAM_PORT, a3 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a4 as SandboxChannelAService, a5 as SandboxChannelAServiceOptions, a6 as SandboxConfigError, a7 as SandboxCreatedCallback, a8 as SandboxProviderUnavailableError, a9 as SelfhostedApplyDiff, aa as SelfhostedControlError, ab as SelfhostedEditor, ac as SelfhostedEnrollment, ad as SelfhostedExecArgs, ae as SelfhostedExecResult, af as SelfhostedImageOutput, ag as SelfhostedLivenessState, ah as SelfhostedNegotiationInput, ai as SelfhostedRelayConfig, aj as SelfhostedSandboxClient, ak as SelfhostedSession, al as SelfhostedSessionBuild, am as SelfhostedSessionDeps, an as SelfhostedSessionState, ao as SelfhostedUnavailableReason, ap as StartRecordingInput, aq as StreamPortUnavailableError, ar as TERMINAL_SERVER_TIMEOUT_MS, as as TerminalServerError, at as TerminalServerUnsupportedError, au as agentErrorToControlError, av as assertDescriptorRegistryInvariants, aw as assertProviderRegistryInvariants, ax as assertSafeRelPath, ay as backendSupportsOs, az as buildDisplayStackScript, aA as buildSelfhostedBackendSession, aB as buildStreamUrl, aC as buildTerminalServerScript, aD as contentTypeForCodec, aE as createSandboxClient, aF as createSandboxClientForBackend, aG as decodeModalSnapshotId, aH as deletePriorPersistedSnapshot, aI as deleteRecordingArtifacts, aJ as deserializeSandboxSessionStateEnvelope, aK as desktopCapableBackend, aL as ensureDisplayStack, aM as ensureTerminalServer, aN as establishSandboxSessionFromEnvelope, aO as exposeStreamPort, aP as extForCodec, aQ as isExecSessionLostBanner, aR as isProviderSandboxNotFoundError, aS as isSelfhostedProviderNotFoundError, aT as isWorkspaceEscapeError, aU as makeActiveBackendResolver, aV as mintStreamToken, aW as modalSandboxAttributionEnvironment, aX as modalSandboxAttributionTags, aY as negotiateCapabilities, aZ as negotiateSelfhostedCapabilities, a_ as offlineAgentError, a$ as offlineControlResponse, b0 as parseExecBannerSessionId, b1 as parseNumstatZ, b2 as parsePorcelainV2, b3 as parseUnifiedPatch, b4 as readRecordingBytes, b5 as readWorkspaceArchiveFromEnvelopeSessionState, b6 as recordingStorageKey, b7 as restoredSandboxSessionStateFromEntry, b8 as sandboxStateEntryFromRunState, b9 as selectBackend, ba as selfhostedLiveness, bb as serializeEstablishedSandboxEnvelope, bc as setSelfhostedApplyDiff, bd as startRecording, be as stopRecording, bf as stripExecBanner, bg as subjectFor, bh as sweepModalOrphanSandboxes, bi as tagModalSandbox, bj as tearDownDisplayStack, bk as tearDownTerminalServer, bl as terminateModalSandboxById, bm as timeoutAgentError, bn as timeoutControlResponse, bo as verifyStreamToken } from '../index-CSGkld-v.js';
4
+ export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalOrphanSweepResult, z as ModalSandboxAttribution, N as NatsControlRpc, B as NatsRequestConnection, G as NegotiationContext, H as NumstatEntry, P as PROVIDER_REGISTRY, I as ProviderConstructionContext, J as ProviderRegistration, K as RecordingCodec, O as RecordingContentType, Q as RecordingError, S as RecordingProcess, T as RecordingUnavailableError, U as ResolvedActiveBackend, V as RoutableBackendSession, W as RoutableSandbox, X as RoutingSandboxSession, Y as RoutingSandboxSessionDeps, Z as RoutingTransitionEvent, _ as RoutingUnsupportedError, $ as SELFHOSTED_DEFAULT_TIMEOUT_MS, a0 as SELFHOSTED_RECONNECT_WINDOW_MS, a1 as SELFHOSTED_RELAY_STREAM_PATH, a2 as STREAM_PORT, a3 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a4 as SandboxChannelAService, a5 as SandboxChannelAServiceOptions, a6 as SandboxConfigError, a7 as SandboxCreatedCallback, a8 as SandboxProviderUnavailableError, a9 as SelfhostedApplyDiff, aa as SelfhostedControlError, ab as SelfhostedEditor, ac as SelfhostedEnrollment, ad as SelfhostedExecArgs, ae as SelfhostedExecResult, af as SelfhostedImageOutput, ag as SelfhostedLivenessState, ah as SelfhostedNegotiationInput, ai as SelfhostedRelayConfig, aj as SelfhostedSandboxClient, ak as SelfhostedSession, al as SelfhostedSessionBuild, am as SelfhostedSessionDeps, an as SelfhostedSessionState, ao as SelfhostedUnavailableReason, ap as StartRecordingInput, aq as StreamPortUnavailableError, ar as TERMINAL_SERVER_TIMEOUT_MS, as as TerminalServerError, at as TerminalServerUnsupportedError, au as agentErrorToControlError, av as assertDescriptorRegistryInvariants, aw as assertProviderRegistryInvariants, ax as assertSafeRelPath, ay as backendSupportsOs, az as buildDisplayStackScript, aA as buildSelfhostedBackendSession, aB as buildStreamUrl, aC as buildTerminalServerScript, aD as contentTypeForCodec, aE as createSandboxClient, aF as createSandboxClientForBackend, aG as decodeModalSnapshotId, aH as deletePriorPersistedSnapshot, aI as deleteRecordingArtifacts, aJ as deserializeSandboxSessionStateEnvelope, aK as desktopCapableBackend, aL as ensureDisplayStack, aM as ensureTerminalServer, aN as establishSandboxSessionFromEnvelope, aO as exposeStreamPort, aP as extForCodec, aQ as isExecSessionLostBanner, aR as isProviderSandboxNotFoundError, aS as isSelfhostedProviderNotFoundError, aT as isWorkspaceEscapeError, aU as makeActiveBackendResolver, aV as mintStreamToken, aW as modalSandboxAttributionEnvironment, aX as modalSandboxAttributionTags, aY as negotiateCapabilities, aZ as negotiateSelfhostedCapabilities, a_ as offlineAgentError, a$ as offlineControlResponse, b0 as parseExecBannerSessionId, b1 as parseNumstatZ, b2 as parsePorcelainV2, b3 as parseUnifiedPatch, b4 as readRecordingBytes, b5 as readWorkspaceArchiveFromEnvelopeSessionState, b6 as recordingStorageKey, b7 as restoredSandboxSessionStateFromEntry, b8 as sandboxStateEntryFromRunState, b9 as selectBackend, ba as selfhostedLiveness, bb as serializeEstablishedSandboxEnvelope, bc as setSelfhostedApplyDiff, bd as startRecording, be as stopRecording, bf as stripExecBanner, bg as subjectFor, bh as sweepModalOrphanSandboxes, bi as tagModalSandbox, bj as tearDownDisplayStack, bk as tearDownTerminalServer, bl as terminateModalSandboxById, bm as timeoutAgentError, bn as timeoutControlResponse, bo as verifyStreamToken } from '../index-BrdnxaAW.js';
5
5
  import 'modal';
6
6
  import '@opengeni/agent-proto';
@@ -95,7 +95,7 @@ import {
95
95
  timeoutAgentError,
96
96
  timeoutControlResponse,
97
97
  verifyStreamToken
98
- } from "../chunk-HGQ252FL.js";
98
+ } from "../chunk-Q36E4MDM.js";
99
99
  export {
100
100
  ActiveBackendUnresolvableError,
101
101
  CAPABILITY_DESCRIPTORS,
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
+ "license": "Apache-2.0",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
@@ -25,22 +26,19 @@
25
26
  },
26
27
  "scripts": {
27
28
  "build": "tsup",
28
- "typecheck": "tsc --noEmit"
29
+ "typecheck": "tsc --noEmit",
30
+ "prepublishOnly": "bash ../../scripts/prepublish-guard"
29
31
  },
30
32
  "dependencies": {
31
33
  "@opengeni/agent-proto": "^0.2.1",
32
- "@opengeni/config": "^0.2.5",
33
- "@opengeni/contracts": "^0.7.0",
34
+ "@opengeni/config": "^0.3.0",
35
+ "@opengeni/contracts": "^0.9.0",
34
36
  "@openai/agents": "^0.11.6",
35
37
  "@openai/agents-extensions": "^0.11.6",
36
38
  "modal": "^0.7.4",
37
39
  "openai": "6.36.0",
38
40
  "@opengeni/codex": "^0.2.1"
39
41
  },
40
- "devDependencies": {
41
- "tsup": "^8.5.0",
42
- "typescript": "^6.0.3"
43
- },
44
42
  "repository": {
45
43
  "type": "git",
46
44
  "url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
@@ -8,6 +8,8 @@
8
8
  * from the active model-facing history; the database audit rows remain.
9
9
  */
10
10
 
11
+ import { TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE } from "./history-sanitizer";
12
+
11
13
  export type CompactionItem = Record<string, unknown>;
12
14
 
13
15
  /**
@@ -18,7 +20,10 @@ export const COMPACTION_SUMMARY_MARKER = "opengeni_context_summary";
18
20
 
19
21
  export const SUMMARY_BUFFER_TOKENS = 20_000;
20
22
  export const COMPACT_USER_MESSAGE_MAX_TOKENS = 20_000;
21
- export const CLIENT_COMPACTION_TRIGGER_FRACTION = 0.9;
23
+ export const DEFAULT_COMPACTION_THRESHOLD_RATIO = 0.6;
24
+ export const MIN_COMPACTION_THRESHOLD_RATIO = 0.3;
25
+ export const MAX_COMPACTION_THRESHOLD_RATIO = 0.9;
26
+ export const COMPACTION_FALLBACK_TARGET_RATIO = 0.5;
22
27
 
23
28
  // Verbatim from Codex CLI:
24
29
  // codex-rs/prompts/templates/compact/prompt.md
@@ -42,12 +47,7 @@ export const SUMMARY_PREFIX =
42
47
  export const USER_MESSAGE_TRUNCATION_MARKER =
43
48
  "\n[... middle truncated for context compaction ...]\n";
44
49
 
45
- const RESULT_TYPE_BY_CALL_TYPE: Record<string, string> = {
46
- function_call: "function_call_result",
47
- computer_call: "computer_call_result",
48
- shell_call: "shell_call_output",
49
- apply_patch_call: "apply_patch_call_output",
50
- };
50
+ const RESULT_TYPE_BY_CALL_TYPE = TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE;
51
51
  const RESULT_TYPES = new Set(Object.values(RESULT_TYPE_BY_CALL_TYPE));
52
52
 
53
53
  function itemType(item: unknown): string | undefined {
@@ -101,15 +101,19 @@ export function estimateTokens(items: readonly CompactionItem[]): number {
101
101
  return total;
102
102
  }
103
103
 
104
+ export function clampCompactionThresholdRatio(value: number | undefined | null): number {
105
+ const numeric = typeof value === "number" && Number.isFinite(value)
106
+ ? value
107
+ : DEFAULT_COMPACTION_THRESHOLD_RATIO;
108
+ return Math.min(MAX_COMPACTION_THRESHOLD_RATIO, Math.max(MIN_COMPACTION_THRESHOLD_RATIO, numeric));
109
+ }
110
+
104
111
  export function clientCompactionThresholdTokens(input: {
105
112
  contextWindowTokens: number;
106
113
  contextReservedOutputTokens: number;
114
+ contextCompactionThresholdRatio?: number | null;
107
115
  }): number {
108
- const available = Math.max(
109
- 0,
110
- input.contextWindowTokens - input.contextReservedOutputTokens - SUMMARY_BUFFER_TOKENS,
111
- );
112
- return Math.floor(available * CLIENT_COMPACTION_TRIGGER_FRACTION);
116
+ return Math.floor(Math.max(0, input.contextWindowTokens) * clampCompactionThresholdRatio(input.contextCompactionThresholdRatio));
113
117
  }
114
118
 
115
119
  export type ClientCompactionDecision = {
@@ -124,6 +128,7 @@ export function decideClientCompaction(input: {
124
128
  lastInputTokens?: number | null;
125
129
  contextWindowTokens: number;
126
130
  contextReservedOutputTokens: number;
131
+ contextCompactionThresholdRatio?: number | null;
127
132
  force?: boolean;
128
133
  }): ClientCompactionDecision {
129
134
  const thresholdTokens = clientCompactionThresholdTokens(input);
@@ -272,6 +277,70 @@ export function buildCompactionReplacementHistory(
272
277
  return history;
273
278
  }
274
279
 
280
+ export type DeterministicFallbackCompactionInput = {
281
+ items: readonly CompactionItem[];
282
+ cause?: string;
283
+ targetTokens?: number;
284
+ };
285
+
286
+ export function buildDeterministicFallbackCompactionHistory(input: DeterministicFallbackCompactionInput): CompactionItem[] {
287
+ const sourceTokens = Math.max(1, estimateTokens(input.items));
288
+ const requestedTarget = typeof input.targetTokens === "number" && input.targetTokens > 0
289
+ ? Math.floor(input.targetTokens)
290
+ : Math.floor(sourceTokens * COMPACTION_FALLBACK_TARGET_RATIO);
291
+ const targetTokens = Math.max(1, Math.min(requestedTarget, Math.max(1, sourceTokens - 1)));
292
+ const summaryItem = buildSummaryItem([
293
+ "Non-LLM context compaction fallback.",
294
+ input.cause ? `Summarizer failure: ${input.cause}` : "Summarizer failure: unavailable.",
295
+ "Older assistant, tool, reasoning, and image history was dropped deterministically.",
296
+ "Retained context above is limited to initial instructions and the most recent user messages that fit the fallback budget; oversized retained messages may be middle-truncated.",
297
+ ].join("\n"));
298
+ const summaryTokens = estimateItemTokens(summaryItem);
299
+ let remaining = Math.max(0, targetTokens - summaryTokens);
300
+ const retainedInstructions: CompactionItem[] = [];
301
+
302
+ for (const item of input.items) {
303
+ if (!isInitialInstructionMessage(item)) {
304
+ if (isUserMessage(item)) {
305
+ break;
306
+ }
307
+ continue;
308
+ }
309
+ if (remaining <= 0) {
310
+ break;
311
+ }
312
+ const capped = compactMessageToEstimatedItemBudget(item, Math.min(2_000, remaining));
313
+ if (!capped) {
314
+ continue;
315
+ }
316
+ const tokens = estimateItemTokens(capped);
317
+ if (tokens <= remaining) {
318
+ retainedInstructions.push(capped);
319
+ remaining -= tokens;
320
+ }
321
+ }
322
+
323
+ const retainedUsers: CompactionItem[] = [];
324
+ for (let index = input.items.length - 1; index >= 0 && remaining > 0; index -= 1) {
325
+ const item = input.items[index]!;
326
+ if (!isUserMessage(item) || isCompactionSummary(item)) {
327
+ continue;
328
+ }
329
+ const capped = compactMessageToEstimatedItemBudget(item, Math.min(COMPACT_USER_MESSAGE_MAX_TOKENS, remaining));
330
+ if (!capped) {
331
+ continue;
332
+ }
333
+ const tokens = estimateItemTokens(capped);
334
+ if (tokens <= remaining) {
335
+ retainedUsers.push(capped);
336
+ remaining -= tokens;
337
+ }
338
+ }
339
+ retainedUsers.reverse();
340
+
341
+ return [...retainedInstructions, ...retainedUsers, summaryItem];
342
+ }
343
+
275
344
  /**
276
345
  * Build the synthetic summary item (a plain user message) appended to the
277
346
  * rebuilt active history.
@@ -287,16 +356,41 @@ export function buildSummaryItem(summaryBody: string): CompactionItem {
287
356
  }
288
357
 
289
358
  function compactUserMessage(item: CompactionItem): CompactionItem {
359
+ return compactMessageToTokenBudget(item, COMPACT_USER_MESSAGE_MAX_TOKENS);
360
+ }
361
+
362
+ function compactMessageToTokenBudget(item: CompactionItem, maxTokens: number): CompactionItem {
290
363
  const text = messageText(item);
291
364
  const next = { ...item };
292
- if (estimatedTextTokens(text) > COMPACT_USER_MESSAGE_MAX_TOKENS) {
293
- next.content = truncateMiddleByEstimatedTokens(text, COMPACT_USER_MESSAGE_MAX_TOKENS);
365
+ if (estimatedTextTokens(text) > maxTokens) {
366
+ next.content = truncateMiddleByEstimatedTokens(text, maxTokens);
294
367
  return next;
295
368
  }
296
369
  next.content = contentWithoutImages(item);
297
370
  return next;
298
371
  }
299
372
 
373
+ function compactMessageToEstimatedItemBudget(item: CompactionItem, maxItemTokens: number): CompactionItem | null {
374
+ if (maxItemTokens <= 0) {
375
+ return null;
376
+ }
377
+ const overheadTokens = estimateItemTokens({ ...item, content: "" });
378
+ let contentBudget = Math.max(0, maxItemTokens - overheadTokens);
379
+ for (let attempt = 0; attempt < 6; attempt += 1) {
380
+ const capped = compactMessageToTokenBudget(item, contentBudget);
381
+ const estimated = estimateItemTokens(capped);
382
+ if (estimated <= maxItemTokens) {
383
+ return capped;
384
+ }
385
+ const overage = estimated - maxItemTokens;
386
+ if (contentBudget <= 0) {
387
+ break;
388
+ }
389
+ contentBudget = Math.max(0, contentBudget - Math.max(1, overage));
390
+ }
391
+ return null;
392
+ }
393
+
300
394
  function estimatedTextTokens(text: string): number {
301
395
  return Math.ceil(text.length / 4);
302
396
  }
@@ -353,8 +447,42 @@ function messageText(item: CompactionItem): string {
353
447
  return "";
354
448
  }
355
449
 
356
- export function renderCompactionPromptInputForChat(input: readonly CompactionItem[]): string {
357
- return input.map(renderItem).join("\n");
450
+ function isInitialInstructionMessage(item: unknown): boolean {
451
+ const type = itemType(item);
452
+ const role = itemRole(item);
453
+ return type === "message" && (role === "system" || role === "developer");
454
+ }
455
+
456
+ export type RenderCompactionTranscriptOptions = {
457
+ /**
458
+ * Hard cap for a rendered transcript. When set, oldest rendered items are
459
+ * dropped first while preserving the final checkpoint prompt; if the retained
460
+ * item itself is too large, it is middle-truncated instead of looping.
461
+ */
462
+ maxEstimatedTokens?: number;
463
+ };
464
+
465
+ export function renderCompactionPromptInputForChat(
466
+ input: readonly CompactionItem[],
467
+ options: RenderCompactionTranscriptOptions = {},
468
+ ): string {
469
+ const rendered = input.map(renderItem);
470
+ if (typeof options.maxEstimatedTokens !== "number" || options.maxEstimatedTokens <= 0) {
471
+ return rendered.join("\n");
472
+ }
473
+ const maxChars = Math.max(1, Math.floor(options.maxEstimatedTokens * 4));
474
+ const last = rendered.at(-1);
475
+ const prefix = rendered.slice(0, -1);
476
+ let kept = prefix.slice();
477
+ while (kept.length > 0 && transcriptLength([...kept, last].filter((line): line is string => line !== undefined)) > maxChars) {
478
+ kept.shift();
479
+ }
480
+ const lines = [...kept, last].filter((line): line is string => line !== undefined);
481
+ const joined = lines.join("\n");
482
+ if (joined.length <= maxChars) {
483
+ return joined;
484
+ }
485
+ return truncateMiddleByChars(joined, maxChars);
358
486
  }
359
487
 
360
488
  function renderItem(item: CompactionItem): string {
@@ -405,3 +533,23 @@ function truncateForTranscript(text: string, max: number): string {
405
533
  }
406
534
  return `${text.slice(0, max)}... (${text.length - max} more chars)`;
407
535
  }
536
+
537
+ function transcriptLength(lines: readonly string[]): number {
538
+ if (lines.length === 0) {
539
+ return 0;
540
+ }
541
+ return lines.reduce((sum, line) => sum + line.length, 0) + Math.max(0, lines.length - 1);
542
+ }
543
+
544
+ function truncateMiddleByChars(text: string, maxChars: number): string {
545
+ if (text.length <= maxChars) {
546
+ return text;
547
+ }
548
+ if (maxChars <= USER_MESSAGE_TRUNCATION_MARKER.length) {
549
+ return USER_MESSAGE_TRUNCATION_MARKER.slice(0, maxChars);
550
+ }
551
+ const keepChars = maxChars - USER_MESSAGE_TRUNCATION_MARKER.length;
552
+ const headChars = Math.ceil(keepChars / 2);
553
+ const tailChars = Math.floor(keepChars / 2);
554
+ return `${text.slice(0, headChars)}${USER_MESSAGE_TRUNCATION_MARKER}${text.slice(text.length - tailChars)}`;
555
+ }
@@ -38,7 +38,7 @@ export type HistoryItem = Record<string, unknown>;
38
38
  * the one observed live, the rest are included so the same pairing logic holds
39
39
  * for every tool-call kind the SDK can emit.
40
40
  */
41
- const RESULT_TYPE_BY_CALL_TYPE: Record<string, string> = {
41
+ export const TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE: Record<string, string> = {
42
42
  function_call: "function_call_result",
43
43
  computer_call: "computer_call_result",
44
44
  shell_call: "shell_call_output",
@@ -52,6 +52,7 @@ const RESULT_TYPE_BY_CALL_TYPE: Record<string, string> = {
52
52
  tool_search_call: "tool_search_output",
53
53
  };
54
54
 
55
+ const RESULT_TYPE_BY_CALL_TYPE = TOOL_CALL_RESULT_TYPE_BY_CALL_TYPE;
55
56
  const RESULT_TYPES = new Set(Object.values(RESULT_TYPE_BY_CALL_TYPE));
56
57
 
57
58
  function itemType(item: unknown): string | undefined {