@opengeni/runtime 4.0.1 → 4.0.3-canary.36199476632001

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 (61) hide show
  1. package/dist/{agent-RJQLKFIW.js → agent-ME6HT4DH.js} +2 -2
  2. package/dist/assets/codemode-client.json +1 -1
  3. package/dist/browser-action-json-schema.d.ts +4 -0
  4. package/dist/{chunk-J5AR5XAJ.js → chunk-FAFZCSN7.js} +695 -210
  5. package/dist/chunk-FAFZCSN7.js.map +1 -0
  6. package/dist/{chunk-ZG2YH25H.js → chunk-JAYHA6BF.js} +2 -2
  7. package/dist/{chunk-UNICCRW5.js → chunk-LZIXCZUV.js} +6 -3
  8. package/dist/{chunk-UNICCRW5.js.map → chunk-LZIXCZUV.js.map} +1 -1
  9. package/dist/{chunk-J6J3VYFW.js → chunk-P4V7JGQ2.js} +1037 -105
  10. package/dist/chunk-P4V7JGQ2.js.map +1 -0
  11. package/dist/{chunk-SPT3PLMX.js → chunk-PRHMSM6C.js} +2 -2
  12. package/dist/codex-tool-search.d.ts +12 -0
  13. package/dist/context-compaction.d.ts +4 -2
  14. package/dist/index.d.ts +27 -8
  15. package/dist/index.js +23 -7
  16. package/dist/interaction-tools.d.ts +1 -1
  17. package/dist/{lazy-manifest-OEYEHNVY.js → lazy-manifest-GCGUFIWP.js} +3 -3
  18. package/dist/{localSources-TWHTEKGO.js → localSources-FDH65AFI.js} +4 -4
  19. package/dist/metrics.d.ts +17 -0
  20. package/dist/model-provider-routing.d.ts +14 -0
  21. package/dist/operational-instructions.d.ts +1 -1
  22. package/dist/provider-quota.d.ts +76 -0
  23. package/dist/sandbox/browser-control-client.d.ts +28 -1
  24. package/dist/sandbox/channel-a.d.ts +43 -0
  25. package/dist/sandbox/index.d.ts +3 -3
  26. package/dist/sandbox/index.js +10 -6
  27. package/dist/sandbox/providers/modal-command-router-wire.d.ts +8 -0
  28. package/dist/sandbox/providers/modal.d.ts +5 -6
  29. package/dist/workspace-tool-gateway.js +5 -5
  30. package/package.json +11 -11
  31. package/src/browser-action-json-schema.ts +10 -0
  32. package/src/bundled_artifact_skills/opengeni-documents/SKILL.md +4 -1
  33. package/src/bundled_artifact_skills/opengeni-documents/references/api.md +20 -0
  34. package/src/bundled_default_skills/opengeni-client/SKILL.md +4 -1
  35. package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +31 -0
  36. package/src/bundled_default_skills/opengeni-client/references/runtime-profile-and-verification.md +32 -0
  37. package/src/codex-tool-search.ts +54 -6
  38. package/src/context-compaction.ts +19 -3
  39. package/src/index.ts +285 -56
  40. package/src/interaction-tools.ts +656 -48
  41. package/src/lazy-tool-transport.ts +4 -0
  42. package/src/metrics.ts +26 -0
  43. package/src/model-context-inspector.ts +1 -0
  44. package/src/model-provider-client.ts +37 -16
  45. package/src/model-provider-routing.ts +32 -6
  46. package/src/operational-instructions.ts +1 -1
  47. package/src/provider-quota.ts +412 -0
  48. package/src/sandbox/browser-control-client.ts +241 -26
  49. package/src/sandbox/channel-a.ts +370 -0
  50. package/src/sandbox/index.ts +6 -1
  51. package/src/sandbox/providers/modal-command-control.ts +7 -1
  52. package/src/sandbox/providers/modal-command-router-wire.ts +47 -6
  53. package/src/sandbox/providers/modal.ts +16 -24
  54. package/src/sandbox/routing/routing-session.ts +29 -6
  55. package/dist/chunk-J5AR5XAJ.js.map +0 -1
  56. package/dist/chunk-J6J3VYFW.js.map +0 -1
  57. /package/dist/{agent-RJQLKFIW.js.map → agent-ME6HT4DH.js.map} +0 -0
  58. /package/dist/{chunk-ZG2YH25H.js.map → chunk-JAYHA6BF.js.map} +0 -0
  59. /package/dist/{chunk-SPT3PLMX.js.map → chunk-PRHMSM6C.js.map} +0 -0
  60. /package/dist/{lazy-manifest-OEYEHNVY.js.map → lazy-manifest-GCGUFIWP.js.map} +0 -0
  61. /package/dist/{localSources-TWHTEKGO.js.map → localSources-FDH65AFI.js.map} +0 -0
@@ -1,4 +1,5 @@
1
- import { BROWSER_PROFILE_ARTIFACT_FORMAT, type BrowserActionCommand as BrowserActionCommandValue, type BrowserActionReceipt as BrowserActionReceiptValue, type BrowserClipboard as BrowserClipboardValue, type BrowserDiagnosticBatch as BrowserDiagnosticBatchValue, type BrowserDownload as BrowserDownloadValue, type BrowserDownloadExportReceipt as BrowserDownloadExportReceiptValue, type BrowserDownloadExportRequest as BrowserDownloadExportRequestValue, type BrowserDownloadListResponse as BrowserDownloadListResponseValue, type BrowserExternalAuthCommand as BrowserExternalAuthCommandValue, type BrowserExternalAuthResult as BrowserExternalAuthResultValue, type BrowserDiagnosticKind, type BrowserObservation as BrowserObservationValue, type BrowserProtectedAuthFillCommand as BrowserProtectedAuthFillCommandValue, type BrowserProtectedAuthFillReceipt as BrowserProtectedAuthFillReceiptValue, type BrowserRevisionMaterialization as BrowserRevisionMaterializationValue, type BrowserTarget as BrowserTargetValue, type BrowserWorkspaceFileStageRequest as BrowserWorkspaceFileStageRequestValue, type BrowserWorkspaceFileStageResponse as BrowserWorkspaceFileStageResponseValue, type ComputerActionCommand as ComputerActionCommandValue, type ComputerActionReceipt as ComputerActionReceiptValue, type ComputerClipboard as ComputerClipboardValue, type ComputerObservation as ComputerObservationValue, type ComputerSessionCapabilities as ComputerSessionCapabilitiesValue, type ComputerTarget as ComputerTargetValue, type InteractionError as InteractionErrorValue, type NetworkRouteConsistency as NetworkRouteConsistencyValue } from "@opengeni/contracts";
1
+ import { type BrowserFrameMetadata } from "@opengeni/sdk";
2
+ import { BROWSER_PROFILE_ARTIFACT_FORMAT, type BrowserActionCommand as BrowserActionCommandValue, type BrowserActionReceipt as BrowserActionReceiptValue, type BrowserClipboard as BrowserClipboardValue, type BrowserDiagnosticBatch as BrowserDiagnosticBatchValue, type BrowserDownload as BrowserDownloadValue, type BrowserDownloadExportReceipt as BrowserDownloadExportReceiptValue, type BrowserDownloadExportRequest as BrowserDownloadExportRequestValue, type BrowserDownloadListResponse as BrowserDownloadListResponseValue, type BrowserDomReadRequest as BrowserDomReadRequestValue, type BrowserDomReadResponse as BrowserDomReadResponseValue, type BrowserExternalAuthCommand as BrowserExternalAuthCommandValue, type BrowserExternalAuthResult as BrowserExternalAuthResultValue, type BrowserDiagnosticKind, type BrowserObservation as BrowserObservationValue, type BrowserProtectedAuthFillCommand as BrowserProtectedAuthFillCommandValue, type BrowserProtectedAuthFillReceipt as BrowserProtectedAuthFillReceiptValue, type BrowserRevisionMaterialization as BrowserRevisionMaterializationValue, type BrowserTarget as BrowserTargetValue, type BrowserTargetState as BrowserTargetStateValue, type BrowserWorkspaceFileStageRequest as BrowserWorkspaceFileStageRequestValue, type BrowserWorkspaceFileStageResponse as BrowserWorkspaceFileStageResponseValue, type ComputerActionCommand as ComputerActionCommandValue, type ComputerActionReceipt as ComputerActionReceiptValue, type ComputerClipboard as ComputerClipboardValue, type ComputerObservation as ComputerObservationValue, type ComputerSessionCapabilities as ComputerSessionCapabilitiesValue, type ComputerTarget as ComputerTargetValue, type InteractionError as InteractionErrorValue, type NetworkRouteConsistency as NetworkRouteConsistencyValue } from "@opengeni/contracts";
2
3
  import type { BrowserControlEnsureRequest, BrowserControlEnsureResponse, BrowserFramesOpenRequest, ComputerFramesOpenRequest, StreamChannel } from "@opengeni/agent-proto";
3
4
  import { type EnsureBrowserControlServerResult } from "./browser-control-server.js";
4
5
  import { type ExposedPortEndpoint } from "./stream-port.js";
@@ -70,6 +71,22 @@ export type ComputerControlFrame = {
70
71
  metadataHeader: string;
71
72
  metadata: ComputerControlFrameMetadata;
72
73
  };
74
+ export type BrowserControlFrame = {
75
+ data: Uint8Array;
76
+ mediaType: "image/jpeg" | "image/png";
77
+ metadataHeader: string;
78
+ metadata: BrowserFrameMetadata;
79
+ };
80
+ export type BrowserControlScreenshotOptions = {
81
+ fullPage?: boolean;
82
+ format?: "jpeg" | "png";
83
+ quality?: number;
84
+ };
85
+ type ExpectedBrowserFrameEvidence = {
86
+ browserSessionId: string;
87
+ controllerGeneration: string;
88
+ targetId: string;
89
+ };
73
90
  export type ComputerControlFrameMetadata = {
74
91
  frameId: string;
75
92
  computerSessionId: string;
@@ -378,6 +395,13 @@ export declare class BrowserControlClient {
378
395
  expectedFrameEvidence: ExpectedComputerFrameEvidence;
379
396
  timeoutMs?: number;
380
397
  }): Promise<ComputerControlFrame>;
398
+ requestBrowserBytesForSession(input: {
399
+ method: "GET";
400
+ path: string;
401
+ token: string;
402
+ expectedFrameEvidence: ExpectedBrowserFrameEvidence;
403
+ timeoutMs?: number;
404
+ }): Promise<BrowserControlFrame>;
381
405
  private requestJson;
382
406
  private requestBytes;
383
407
  private controllerPort;
@@ -400,6 +424,9 @@ export declare class BrowserControlSessionClient {
400
424
  selectTarget(targetId: string): Promise<BrowserObservationValue>;
401
425
  closeTarget(targetId: string): Promise<BrowserTargetValue[]>;
402
426
  observe(targetId: string): Promise<BrowserObservationValue>;
427
+ targetState(targetId: string): Promise<BrowserTargetStateValue>;
428
+ readDom(targetId: string, requestInput: BrowserDomReadRequestValue): Promise<BrowserDomReadResponseValue>;
429
+ capture(targetId: string, options?: BrowserControlScreenshotOptions): Promise<BrowserControlFrame>;
403
430
  readClipboard(): Promise<BrowserClipboardValue>;
404
431
  action(command: BrowserActionCommandValue): Promise<BrowserActionReceiptValue>;
405
432
  /** API-broker-only file authority path. Signed read URLs are materialized by
@@ -187,6 +187,25 @@ export type RepositoryDiscoveryResult = {
187
187
  complete: boolean;
188
188
  degradedReason: RepositoryDiscoveryDegradedReason | null;
189
189
  };
190
+ /** Raw ripgrep stdout kept on the box before encoding. */
191
+ export declare const CODE_SEARCH_RG_MAX_BYTES: number;
192
+ export type CodeSearchRipgrepOutcome = {
193
+ /** False when ripgrep is not installed on the box. */
194
+ available: boolean;
195
+ stdout: string;
196
+ /** rg exit status (0 matches, 1 none, 2 error); null after a timeout. */
197
+ exitCode: number | null;
198
+ /** Output hit the byte cap and was cut at a line boundary. */
199
+ truncated: boolean;
200
+ timedOut: boolean;
201
+ };
202
+ /**
203
+ * Accept only the ripgrep arguments `code_search` needs. Flags that run a
204
+ * program (`--pre`, `-z`) or read other files, and paths that leave the
205
+ * workspace, are rejected before a command is built. `--no-config` stops a
206
+ * `RIPGREP_CONFIG_PATH` on the box from adding flags.
207
+ */
208
+ export declare function validateCodeSearchRipgrepArgs(args: readonly string[], workspaceRoot?: string): string[];
190
209
  export declare class SandboxChannelAService {
191
210
  private readonly session;
192
211
  private readonly workspaceRoot;
@@ -287,6 +306,30 @@ export declare class SandboxChannelAService {
287
306
  detectReposDetailed(): Promise<RepositoryDiscoveryResult>;
288
307
  /** Detect repo roots within the workspace (for the Git.repos capability). */
289
308
  detectRepos(): Promise<string[]>;
309
+ /**
310
+ * Run ripgrep read-only in the workspace root for the Jev `code_search` tool.
311
+ * Arguments are checked against a closed allowlist (no `--pre`, `-z` or other
312
+ * flags that run programs), so the command is provably read-only and skips
313
+ * durable mutation admission. stdout is capped at `maxBytes`, gzip+base64
314
+ * framed so providers that drop newlines or retain limited output cannot
315
+ * corrupt it, and bounded by a wall-clock watchdog (not GNU `timeout`, which
316
+ * stock macOS lacks). Resolves `available: false` when `rg` is not installed,
317
+ * and throws when the box could not store the output whole (a full
318
+ * temporary directory) rather than returning a prefix as complete.
319
+ */
320
+ codeSearchRipgrep(args: readonly string[], options: {
321
+ timeoutMs: number;
322
+ maxBytes: number;
323
+ maxTransferBytes?: number;
324
+ }): Promise<CodeSearchRipgrepOutcome>;
325
+ /**
326
+ * Fetch one chunk of compressed ripgrep output kept on the box by a previous
327
+ * codeSearchRipgrep call. The last fetch deletes the file. Null when the
328
+ * file is gone.
329
+ */
330
+ private codeSearchRipgrepChunk;
331
+ /** Classify workspace-relative paths for `code_search` path filters. */
332
+ codeSearchPathKinds(paths: readonly string[]): Promise<Record<string, "file" | "directory" | "missing">>;
290
333
  /** Run a bounded command to physical completion and return buffered output.
291
334
  * A deadline cancels the exact provider operation, proves its process group
292
335
  * absent, and settles any retained-process admission before returning 124. */
@@ -18,7 +18,7 @@ export { SandboxConfigError, SandboxExactResumeInstanceUnavailableError, Sandbox
18
18
  export { PROVIDER_REGISTRY, assertProviderRegistryInvariants, buildImmutableProviderImage, isProviderApiThrottleError, prepareProviderForTeardownAfterCapture, providerSupportsImmutableImageBuild, renewSandboxProviderExpiration, providerWorkspaceCapturePolicy, type ProviderRegistration, type ProviderConstructionContext, type ProviderExactResumeMode, type ProviderImmutableImageBuildInput, type ProviderImmutableImageBuildResult, type ProviderExpirationRenewalInput, type ProviderWorkspaceCapturePolicy, type ProviderWorkspaceCaptureTakeover, } from "./providers/index.js";
19
19
  export { classifyProviderSandboxFailure, isProviderSandboxGoneDuringRoutedOperation, isProviderSandboxNotFoundError, isProviderSandboxTransientError, type ProviderSandboxFailure, type ProviderSandboxFailureKind, } from "./provider-errors.js";
20
20
  export { WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION, WorkspaceArchiveIntegrityError, captureVerifiedWorkspaceArchive, decodeNativeSnapshotRef, encodeNativeSnapshotRef, describeLegacyNativeSnapshotArchive, describeNativeSnapshotArchive, fingerprintSandboxWorkspace, inlineWorkspaceArchiveForRestore, parseWorkspaceArchiveDescriptor, readVerifiedWorkspaceArchive, verifyRestoredWorkspace, type NativeSnapshotDescriptor, type NativeSnapshotProvider, type NativeSnapshotRef, type TarWorkspaceArchiveDescriptor, type VerifiedWorkspaceArchive, type WorkspaceArchiveDescriptor, type WorkspaceArchiveIntegrityCode, type WorkspaceTreeFingerprint, } from "./workspace-archive.js";
21
- export { ensureModalRegistryImage, deleteModalCheckpointSnapshot, inspectModalSandboxLifecycle, isModalTaskExecStartDnsResolutionError, modalSessionMatchesCheckpointProviderBinding, modalSandboxAttributionEnvironment, modalSandboxAttributionTags, resolveModalCheckpointProviderBinding, resolveModalCheckpointProviderBindingForLiveSandbox, resolveModalCheckpointProviderBindingForSession, sweepModalOrphanSandboxes, tagModalSandbox, terminateModalSandboxById, type LiveModalSandboxLeaseAttribution, type ModalModuleLoader, type ModalCheckpointProviderBinding, type ModalOrphanSweepResult, type ModalOrphanSweepTermination, type ModalSandboxAttribution, type RevalidateModalOrphanTermination, } from "./providers/modal.js";
21
+ export { ensureModalRegistryImage, deleteModalCheckpointSnapshot, inspectModalSandboxLifecycle, isModalTaskExecStartPreDispatchUnavailableError, modalSessionMatchesCheckpointProviderBinding, modalSandboxAttributionEnvironment, modalSandboxAttributionTags, resolveModalCheckpointProviderBinding, resolveModalCheckpointProviderBindingForLiveSandbox, resolveModalCheckpointProviderBindingForSession, sweepModalOrphanSandboxes, tagModalSandbox, terminateModalSandboxById, type LiveModalSandboxLeaseAttribution, type ModalModuleLoader, type ModalCheckpointProviderBinding, type ModalOrphanSweepResult, type ModalOrphanSweepTermination, type ModalSandboxAttribution, type RevalidateModalOrphanTermination, } from "./providers/modal.js";
22
22
  export { OpenSandboxClient, OpenSandboxSession, setOpenSandboxApplyDiff, type OpenSandboxApplyDiff, type OpenSandboxClientOptions, type OpenSandboxEditor, type OpenSandboxSessionState, } from "./providers/opensandbox-adapter.js";
23
23
  export { selectBackend, sdkBackendIdForSandboxBackend, sandboxBackendForSdkBackendId, backendSupportsOs, desktopCapableBackend, negotiateCapabilities, type NegotiationContext, } from "./select.js";
24
24
  export { STREAM_TOKEN_DEFAULT_TTL_SECONDS, mintStreamToken, verifyStreamToken, StreamTokenPayload, type MintStreamTokenInput, type StreamTokenPayloadType, } from "./stream-token.js";
@@ -28,13 +28,13 @@ export { createTurnToolCancellationController, TurnSandboxCommandCancelledError,
28
28
  export type { TurnSandboxCommandArgs, TurnSandboxCommandSession, TurnToolCancellationController, TurnToolCancellationFence, } from "./turn-tool-cancellation.js";
29
29
  export { TERMINAL_STREAM_PORT, TERMINAL_SERVER_TIMEOUT_MS, TerminalServerError, TerminalServerUnsupportedError, buildTerminalServerScript, ensureTerminalServer, tearDownTerminalServer, type EnsureTerminalServerOptions, type EnsureTerminalServerResult, } from "./terminal-server.js";
30
30
  export { BROWSER_CONTROL_PORT, BROWSER_CONTROL_SERVER_TIMEOUT_MS, BrowserControlServerError, BrowserControlServerUnsupportedError, buildBrowserControlServerScript, ensureBrowserControlServer, tearDownBrowserControlServer, type EnsureBrowserControlServerOptions, type EnsureBrowserControlServerResult, } from "./browser-control-server.js";
31
- export { BROWSER_CONTROL_ADMIN_TOKEN_FILE, BrowserControlClient, BrowserControlProtocolError, BrowserControlRequestError, BrowserControlSessionClient, BrowserControlTransportError, BrowserControlUnsupportedError, ComputerFrameEvidenceMismatchError, decodeComputerControlFrameMetadataHeader, provisionBrowserControlClient, validateComputerControlFrameEvidence, type BrowserControlPlacementSession, type BrowserStateDownloadGrant, type BrowserStateUploadGrant, type BrowserViewGrant, type CapturePlacementBrowserStateInput, type CreatePlacementBrowserSessionInput, type PlacementBrowserSession, type PlacementBrowserSessionReference, type PlacementBrowserNetworkRoute, type PlacementBrowserTransport, type PlacementBrowserStateCaptureReceipt, type ComputerControlFrame, type ComputerControlFrameMetadata, type ComputerFrameEvidenceMismatchReason, type ExpectedComputerFrameEvidence, type ProvisionBrowserControlClientInput, type ProvisionBrowserControlClientResult, type RestorePlacementBrowserStateInput, } from "./browser-control-client.js";
31
+ export { BROWSER_CONTROL_ADMIN_TOKEN_FILE, BrowserControlClient, BrowserControlProtocolError, BrowserControlRequestError, BrowserControlSessionClient, BrowserControlTransportError, BrowserControlUnsupportedError, ComputerFrameEvidenceMismatchError, decodeComputerControlFrameMetadataHeader, provisionBrowserControlClient, validateComputerControlFrameEvidence, type BrowserControlPlacementSession, type BrowserControlScreenshotOptions, type BrowserControlFrame, type BrowserStateDownloadGrant, type BrowserStateUploadGrant, type BrowserViewGrant, type CapturePlacementBrowserStateInput, type CreatePlacementBrowserSessionInput, type PlacementBrowserSession, type PlacementBrowserSessionReference, type PlacementBrowserNetworkRoute, type PlacementBrowserTransport, type PlacementBrowserStateCaptureReceipt, type ComputerControlFrame, type ComputerControlFrameMetadata, type ComputerFrameEvidenceMismatchReason, type ExpectedComputerFrameEvidence, type ProvisionBrowserControlClientInput, type ProvisionBrowserControlClientResult, type RestorePlacementBrowserStateInput, } from "./browser-control-client.js";
32
32
  export { RunCredentialValidationError, normalizeRunCredentialsResolution, runCredentialRoot, runCredentialPointerFile, materializeRunCredentials, clearRunCredentials, clearRunCredentialsForAttempt, withRunCredentialEnvironment, withRunCredentialsSession, withRunCredentialsClient, type RunCredentialExpectedScope, type NormalizedRunCredentialMaterial, type RunCredentialCommandSession, type RunCredentialCommandRunner, type RunCredentialSessionReady, type MaterializeRunCredentialsOptions, } from "./run-credentials.js";
33
33
  export { CodemodeTokenPathError, codemodeTokenFileForSession, codemodeTokenFileFromEnvironment, withCodemodeTokenEnvironment, withCodemodeTokenSession, withCodemodeTokenClient, } from "./codemode-token.js";
34
34
  export { CODEMODE_TOKEN_TTL_SECONDS, mintSandboxCodemodeToken, type CodemodeAuthorityScope, type SandboxCodemodeAuthority, type MintedSandboxCodemodeToken, } from "./codemode-authority.js";
35
35
  export { exposeStreamPort, buildStreamUrl, exposedPortAllowsHostFetch, exposedPortEndpointFromUrl, isOpenSandboxLifecycleProxyPath, joinExposedPortPath, parseOpenSandboxSignedUriPath, redactOpenSandboxSignedUriPath, signedEndpointExpiresAtMs, signedEndpointNeedsRefresh, StreamPortUnavailableError, type ExposedPortEndpoint, type ExposeStreamPortInput, type ExposeStreamPortResult, type OpenSandboxSignedUriPath, } from "./stream-port.js";
36
36
  export { startRecording, stopRecording, inspectRecordingArtifact, uploadRecordingArtifact, deleteRecordingArtifacts, recordingStorageKey, contentTypeForCodec, extForCodec, RecordingUnavailableError, RecordingError, type RecordingCodec, type RecordingContentType, type StartRecordingInput, type RecordingProcess, type RecordingArtifactMetadata, } from "./recording.js";
37
- export { SandboxChannelAService, ChannelAPartialMutationError, ChannelAValidationError, ChannelAUnavailableError, ChannelAConflictError, ChannelAFileSystemRouteChangedError, ChannelANotFoundError, ChannelAUnsupportedError, stripExecBanner, parseExecBannerSessionId, parseExecBannerExitCode, isExecSessionLostBanner, assertSafeRelPath, parsePorcelainV2, parseNumstatZ, parseUnifiedPatch, REPOSITORY_DISCOVERY_LIMIT, type ChannelASession, type ChannelAExecArgs, type ChannelAExecResult, type ChannelAEmitter, type SandboxChannelAServiceOptions, type RepositoryDiscoveryDegradedReason, type RepositoryDiscoveryResult, type NumstatEntry, } from "./channel-a.js";
37
+ export { SandboxChannelAService, ChannelAPartialMutationError, ChannelAValidationError, ChannelAUnavailableError, ChannelAConflictError, ChannelAFileSystemRouteChangedError, ChannelANotFoundError, ChannelAUnsupportedError, stripExecBanner, parseExecBannerSessionId, parseExecBannerExitCode, isExecSessionLostBanner, assertSafeRelPath, parsePorcelainV2, parseNumstatZ, parseUnifiedPatch, REPOSITORY_DISCOVERY_LIMIT, CODE_SEARCH_RG_MAX_BYTES, validateCodeSearchRipgrepArgs, type CodeSearchRipgrepOutcome, type ChannelASession, type ChannelAExecArgs, type ChannelAExecResult, type ChannelAEmitter, type SandboxChannelAServiceOptions, type RepositoryDiscoveryDegradedReason, type RepositoryDiscoveryResult, type NumstatEntry, } from "./channel-a.js";
38
38
  export { type ControlRpc, NatsControlRpc, SelfhostedControlError, agentErrorToControlError, subjectFor, offlineControlResponse, timeoutControlResponse, offlineAgentError, timeoutAgentError, drainingExhaustedError, payloadTooLargeMessage, drainingMessage, execDeadlineHint, NEVER_SENT_DETAIL_KEY, type NatsRequestConnection, type SelfhostedUnavailableReason, } from "./selfhosted/control-rpc.js";
39
39
  export type { SelfhostedOpObserver, SelfhostedOpObservation } from "./selfhosted/op-observer.js";
40
40
  export { selfhostedFaultClass, SELFHOSTED_INFRASTRUCTURE_FAULT_CLASSES, renderSelfhostedFault, FAULT_FIELD_WHAT_HAPPENED, FAULT_FIELD_WHICH_LAYER, FAULT_FIELD_WHAT_PRESERVED, FAULT_FIELD_WHAT_TO_TRY, } from "./selfhosted/fault-rendering.js";
@@ -13,6 +13,7 @@ import {
13
13
  BrowserControlUnsupportedError,
14
14
  CAPABILITY_DESCRIPTORS,
15
15
  CODEMODE_TOKEN_TTL_SECONDS,
16
+ CODE_SEARCH_RG_MAX_BYTES,
16
17
  ChannelAConflictError,
17
18
  ChannelAFileSystemRouteChangedError,
18
19
  ChannelANotFoundError,
@@ -158,7 +159,7 @@ import {
158
159
  inspectRecordingArtifact,
159
160
  isConnectedMachineAbsolutePath,
160
161
  isExecSessionLostBanner,
161
- isModalTaskExecStartDnsResolutionError,
162
+ isModalTaskExecStartPreDispatchUnavailableError,
162
163
  isOpenSandboxLifecycleProxyPath,
163
164
  isProviderApiThrottleError,
164
165
  isProviderSandboxGoneDuringRoutedOperation,
@@ -258,6 +259,7 @@ import {
258
259
  timeoutAgentError,
259
260
  timeoutControlResponse,
260
261
  uploadRecordingArtifact,
262
+ validateCodeSearchRipgrepArgs,
261
263
  validateComputerControlFrameEvidence,
262
264
  verifyRestoredWorkspace,
263
265
  verifySandboxExecReadiness,
@@ -269,11 +271,11 @@ import {
269
271
  withRunCredentialsClient,
270
272
  withRunCredentialsSession,
271
273
  withoutSandboxProviderIdentity
272
- } from "../chunk-J5AR5XAJ.js";
273
- import "../chunk-SPT3PLMX.js";
274
+ } from "../chunk-FAFZCSN7.js";
275
+ import "../chunk-PRHMSM6C.js";
274
276
  import "../chunk-4A4XTKKX.js";
275
- import "../chunk-ZG2YH25H.js";
276
- import "../chunk-UNICCRW5.js";
277
+ import "../chunk-JAYHA6BF.js";
278
+ import "../chunk-LZIXCZUV.js";
277
279
  import "../chunk-PZ5AY32C.js";
278
280
  export {
279
281
  ActiveBackendUnresolvableError,
@@ -290,6 +292,7 @@ export {
290
292
  BrowserControlUnsupportedError,
291
293
  CAPABILITY_DESCRIPTORS,
292
294
  CODEMODE_TOKEN_TTL_SECONDS,
295
+ CODE_SEARCH_RG_MAX_BYTES,
293
296
  ChannelAConflictError,
294
297
  ChannelAFileSystemRouteChangedError,
295
298
  ChannelANotFoundError,
@@ -435,7 +438,7 @@ export {
435
438
  inspectRecordingArtifact,
436
439
  isConnectedMachineAbsolutePath,
437
440
  isExecSessionLostBanner,
438
- isModalTaskExecStartDnsResolutionError,
441
+ isModalTaskExecStartPreDispatchUnavailableError,
439
442
  isOpenSandboxLifecycleProxyPath,
440
443
  isProviderApiThrottleError,
441
444
  isProviderSandboxGoneDuringRoutedOperation,
@@ -535,6 +538,7 @@ export {
535
538
  timeoutAgentError,
536
539
  timeoutControlResponse,
537
540
  uploadRecordingArtifact,
541
+ validateCodeSearchRipgrepArgs,
538
542
  validateComputerControlFrameEvidence,
539
543
  verifyRestoredWorkspace,
540
544
  verifySandboxExecReadiness,
@@ -1,3 +1,4 @@
1
+ import { Client } from "@grpc/grpc-js";
1
2
  import protobuf from "protobufjs";
2
3
  import { ProviderCommandStartRejectedError } from "../provider-command-session.js";
3
4
  export declare const modalRouterWire: protobuf.Root;
@@ -7,6 +8,13 @@ export declare class ModalCommandStartRejectedError extends ProviderCommandStart
7
8
  readonly code: number;
8
9
  constructor(code: number, cause: unknown);
9
10
  }
11
+ /** Only a local channel-readiness failure BEFORE TaskExecStart is issued can
12
+ * prove that start was never dispatched. RPC status/details, even if they look
13
+ * like a DNS resolver error, can originate from an accepting server. */
14
+ export declare class ModalCommandStartPreDispatchUnavailableError extends Error {
15
+ private constructor();
16
+ static ensureReady(client: Client, signal?: AbortSignal): Promise<void>;
17
+ }
10
18
  export type ModalRouterIdentity = {
11
19
  taskId: string;
12
20
  execId: string;
@@ -27,13 +27,12 @@ export type RevalidateModalOrphanTermination = (candidate: ModalOrphanSweepTermi
27
27
  export declare function modalSandboxAttributionEnvironment(input: ModalSandboxAttribution): Record<string, string>;
28
28
  export declare function modalSandboxAttributionTags(input: ModalSandboxAttribution): Record<string, string>;
29
29
  /**
30
- * Modal exhausted its own retries before failing to resolve the exact command
31
- * router DNS name, so TaskExecStart never connected and replay is safe. Keep
32
- * this fail-closed across Agents SDK wrappers: every reachable structural leaf
33
- * must be that exact ClientError with no HTTP status metadata, and any incomplete
34
- * or mixed graph is rejected.
30
+ * Only the native client's pre-dispatch readiness gate proves Start was not
31
+ * sent. Server-controlled gRPC/SDK status and DNS-looking details prove nothing.
32
+ * Any retained or outcome-unknown routing boundary vetoes recovery even when
33
+ * it contains a genuine pre-dispatch error among its causes.
35
34
  */
36
- export declare function isModalTaskExecStartDnsResolutionError(error: unknown): boolean;
35
+ export declare function isModalTaskExecStartPreDispatchUnavailableError(error: unknown): boolean;
37
36
  /**
38
37
  * Modal proves that the exact exec has already terminated with a typed
39
38
  * FAILED_PRECONDITION from its stdin-write RPC. Keep this deliberately
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  prepareAgentTools
3
- } from "./chunk-J6J3VYFW.js";
4
- import "./chunk-J5AR5XAJ.js";
5
- import "./chunk-SPT3PLMX.js";
3
+ } from "./chunk-P4V7JGQ2.js";
4
+ import "./chunk-FAFZCSN7.js";
5
+ import "./chunk-PRHMSM6C.js";
6
6
  import "./chunk-4A4XTKKX.js";
7
- import "./chunk-ZG2YH25H.js";
7
+ import "./chunk-JAYHA6BF.js";
8
8
  import "./chunk-TE27J65Q.js";
9
9
  import "./chunk-UZ66CGC2.js";
10
10
  import "./chunk-YU4VRTR4.js";
11
- import "./chunk-UNICCRW5.js";
11
+ import "./chunk-LZIXCZUV.js";
12
12
  import "./chunk-PZ5AY32C.js";
13
13
  export {
14
14
  prepareAgentTools as prepareWorkspaceToolGatewayTools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "4.0.1",
3
+ "version": "4.0.3-canary.36199476632001",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -63,16 +63,16 @@
63
63
  "@openai/agents": "0.14.3",
64
64
  "@openai/agents-core": "0.14.3",
65
65
  "@openai/agents-extensions": "0.14.3",
66
- "@opengeni/agent-proto": "^0.6.0",
67
- "@opengeni/capabilities": "^0.3.4",
68
- "@opengeni/codemode": "^0.6.1",
69
- "@opengeni/codex": "^0.2.25",
70
- "@opengeni/config": "^2.1.0",
71
- "@opengeni/contracts": "^5.1.0",
72
- "@opengeni/network": "^0.3.1",
73
- "@opengeni/sdk": "^7.1.0",
74
- "@opengeni/tool-gateway": "^0.1.12",
75
- "@opengeni/xai-subscription": "^0.1.4",
66
+ "@opengeni/agent-proto": "^0.6.0-canary.36199476632001",
67
+ "@opengeni/capabilities": "^0.3.4-canary.36199476632001",
68
+ "@opengeni/codemode": "^0.6.2-canary.36199476632001",
69
+ "@opengeni/codex": "^0.2.26-canary.36199476632001",
70
+ "@opengeni/config": "^2.1.1-canary.36199476632001",
71
+ "@opengeni/contracts": "^5.1.1-canary.36199476632001",
72
+ "@opengeni/network": "^0.3.1-canary.36199476632001",
73
+ "@opengeni/sdk": "^7.1.1-canary.36199476632001",
74
+ "@opengeni/tool-gateway": "^0.1.13-canary.36199476632001",
75
+ "@opengeni/xai-subscription": "^0.1.4-canary.36199476632001",
76
76
  "debug": "4.4.3",
77
77
  "modal": "0.9.0",
78
78
  "openai": "6.47.0",
@@ -0,0 +1,10 @@
1
+ import type { AttemptToolJsonSchema } from "@opengeni/contracts";
2
+ import { z } from "zod";
3
+
4
+ /** Keep repeated browser action and locator variants once in the model schema. */
5
+ export function browserActionInputJsonSchema(schema: z.ZodType): AttemptToolJsonSchema {
6
+ return z.toJSONSchema(schema, {
7
+ target: "draft-2020-12",
8
+ reused: "ref",
9
+ }) as AttemptToolJsonSchema;
10
+ }
@@ -44,7 +44,10 @@ Skill was installed or explicitly checked out to the filesystem.
44
44
  2. Make the smallest coherent edit. One `opengeni__editable_artifact_apply`
45
45
  call is one atomic command batch. Use stable ids from inspection for existing
46
46
  objects and `openGeni.artifacts.ids.document(...)` for new objects in
47
- CodeMode. A direct call must pass the inspected `headSequence` and
47
+ CodeMode. On a Connected Machine without the JS client, use the offline native
48
+ `codemode document-id <kind> <namespace>` helper described in `references/api.md`;
49
+ copy the exact namespace from the inspected summary, never invent it.
50
+ A direct call must pass the inspected `headSequence` and
48
51
  `stateHash`; CodeMode carries its last read head automatically.
49
52
  3. For one simple edit, call the artifact tools directly. For loops, several
50
53
  inspections, generated ids, or a multi-part batch, write auditable Bun code
@@ -37,6 +37,26 @@ artifactId: document.id, inspectionReceiptId }]` with the completion evidence to
37
37
  If the document changes, inspect it again and use the new receipt. Include the
38
38
  returned artifact reference in the user handoff.
39
39
 
40
+ ## Native Connected Machine ID helper
41
+
42
+ When the JavaScript client is unavailable, the native client can generate IDs
43
+ offline. Inspect the document summary with the artifact tools first. Copy its
44
+ exact decimal `idNamespace` string into `DOCUMENT_NAMESPACE`, then run:
45
+
46
+ ```sh
47
+ "$OPENGENI_CODEMODE_NATIVE_CLIENT" codemode document-id paragraph "$DOCUMENT_NAMESPACE"
48
+ ```
49
+
50
+ The result is JSON with an `id` field. Kinds are `paragraph`, `table`, `page-break`,
51
+ `section`, `header`, `footer`, `comment`, and `tracked-change`. The helper uses the
52
+ same ID format as `openGeni.artifacts.ids.document`; it needs no credentials or
53
+ network and does not mutate an artifact. Keep uint64 namespaces as decimal strings,
54
+ not JavaScript numbers. Direct apply calls still require the inspected
55
+ `headSequence` and `stateHash`. Reuse inspected IDs for existing objects.
56
+ If the installed native client lacks `document-id`, use the deployment-selected
57
+ JavaScript helper when available; otherwise report the missing client capability
58
+ rather than inventing IDs or claiming an edit succeeded.
59
+
40
60
  ## Authoring example
41
61
 
42
62
  ```js
@@ -55,7 +55,10 @@ Embedded products may narrow bundled guidance with `bundledSkillIds`.
55
55
  missing access is configured. Read
56
56
  [Discovery and autonomy](references/discovery-and-autonomy.md) for that workflow.
57
57
  - Ask only for consequential product choices or external authority that cannot
58
- be inferred. Do not ask the customer to restate facts the system proves.
58
+ be inferred. Offer a recommended setup and use the existing structured question
59
+ UI, when available, for the few unresolved choices about chat sharing, learning
60
+ across chats, or data access. Skip choices already settled; do not impose an
61
+ onboarding questionnaire. See [Discovery and autonomy](references/discovery-and-autonomy.md).
59
62
  - Use a reversible, clearly stated default when an unresolved choice is
60
63
  low-risk. Resolve privacy, tenant authority, data writes, cost exposure, and
61
64
  ambiguous external mutations before crossing those boundaries.
@@ -52,6 +52,37 @@ Group tightly related unresolved decisions when that makes them easier to answer
52
52
 
53
53
  For a missing privacy answer, default provisionally to the smaller sharing boundary and explain the operational cost. Do not silently weaken isolation to reduce workspace count.
54
54
 
55
+ ### Keep product choices lightweight
56
+
57
+ When meaningful choices remain, group them into one short question interaction
58
+ using the host's existing structured human-input UI when available, or a concise
59
+ chat question otherwise. Recommend the setup that fits the product and let the
60
+ user accept it or adjust individual choices. Do not build a new questionnaire or
61
+ ask every integration the same questions. A suggested answer is not consent to
62
+ send data, share private content, or perform an external action.
63
+
64
+ Use product language for the relevant unresolved choices:
65
+
66
+ - **Learning across chats:** no new lasting learning, remember for each person,
67
+ or shared team knowledge. Explain briefly that chat history/retention is separate
68
+ and disabling learning does not delete history or existing authorized Knowledge.
69
+ - **Who can open chats:** only their owner, the team, or a choice on each chat.
70
+ Keep human visibility separate from agent access and Knowledge scope. Choose the
71
+ workspace mapping from the actual sharing boundary; private chats alone do not
72
+ require a workspace per person.
73
+ - **How the agent gets data:** current-page snapshots, read-only tools to fetch
74
+ more reports, or controlled queries for deeper analysis. State the meaningful
75
+ limitation of the recommendation. Confirm broader access or writes separately
76
+ only when they are part of the requested product.
77
+
78
+ For example, if all three choices are unresolved for a simple dashboard, propose
79
+ “Private chats, no learning between chats, and current-page data only” with a short
80
+ explanation that the agent cannot fetch another report on its own. Do not reuse
81
+ that default for a team assistant whose requirements already imply shared work.
82
+ Continue independent discovery while awaiting an answer; ask again only when new
83
+ information introduces a material decision. Summarize any provisional choices in
84
+ the handoff so they do not become invisible product decisions.
85
+
55
86
  ## Follow the wanted autonomy
56
87
 
57
88
  Infer the delivery mode from explicit user language first, then repository guidance and established team workflow:
@@ -154,4 +154,36 @@ silently linking identities or widening membership. Complete discoverable setup
154
154
  before handoff; identify operator-only blockers separately and do not depend on
155
155
  internal source, cluster access, prior chat memory, or undocumented local helpers.
156
156
 
157
+ ### Make the next step easy
158
+
159
+ Keep the user-facing handoff short: what works, what was verified, and the next
160
+ action needed to make it usable. Put detailed commands and configuration in the
161
+ repository setup guide and link it. When setup is blocked, show a compact checklist
162
+ in the conversation using existing text/link UI; no new setup-card renderer is
163
+ required.
164
+
165
+ For missing OpenGeni credentials, point to the selected deployment's Organization
166
+ settings: the overview shows **Organization ID** with a copy control, and
167
+ **Developer** contains **Create Organization API Key** plus another ID copy
168
+ control. Prefer a verified settings link or returned setup action over an invented
169
+ URL. Verify older/self-hosted UI availability before promising these controls.
170
+ Explain the key type and required access for the implemented calls: workspace
171
+ provisioning and chat writes need a write-capable organization key, not a read-only
172
+ key. State its actual scope without inventing granular permission switches.
173
+
174
+ Name the exact server configuration variables and secret-store destination. Fill
175
+ verified non-secret IDs yourself when authorized and unambiguous. Give a secure
176
+ interactive command or secret-manager action for the key; never ask the user to
177
+ paste a secret into chat or put it in a browser variable. Follow with the actual
178
+ deployment command/target and a first-question smoke test, including model/billing
179
+ readiness when unverified. Complete authorized steps yourself; clearly identify
180
+ any remaining owner login, secret entry, or deployment approval.
181
+
182
+ Resolve ordinary missing tools using the project's supported installation or
183
+ package-runner path where possible. Investigate dependency warnings enough to
184
+ state their effect and available fix. Surface only material unresolved risks or
185
+ user actions in plain language; keep routine audit counts and logs in the setup
186
+ or verification notes. Do not claim a warning is harmless without evidence, or
187
+ make unrelated upgrades merely to silence the report.
188
+
157
189
  If a durable customer integration Skill would reduce future rediscovery, generate one beside the integration code containing only stable, non-secret project facts and smoke probes. Do not turn the generic implementation Skill into the customer's analytics prompt, and do not make generated runtime behavior depend on the implementation workspace retaining this implementation Skill.
@@ -185,6 +185,41 @@ export function bm25RankTools(tools: Tool[], query: string, limit: number): Tool
185
185
  return hits.slice(0, limit).map((s) => s.tool); // no hits ⇒ [] (codex-rs parity; see doc)
186
186
  }
187
187
 
188
+ /**
189
+ * Tools a free-text query names exactly, in query order. A query token that
190
+ * contains "_" names a tool when it equals the tool's full model name or the
191
+ * part after its last "__" server separator (`goal_complete` and
192
+ * `opengeni__goal_complete` both name `opengeni__goal_complete`). Single words
193
+ * such as "read" are never treated as names. Agents often search by exact name,
194
+ * and BM25 alone ranked a neighbouring tool first for about a fifth of those
195
+ * queries (`goal_complete` returned `goal_set` first).
196
+ */
197
+ export function toolsNamedInQuery<T extends Tool & { name: string }>(
198
+ tools: T[],
199
+ query: string,
200
+ ): T[] {
201
+ const byName = new Map<string, T[]>();
202
+ for (const tool of tools) {
203
+ const full = tool.name.toLowerCase();
204
+ const separator = full.lastIndexOf(MCP_TOOL_NAME_SEPARATOR);
205
+ const short = separator >= 0 ? full.slice(separator + MCP_TOOL_NAME_SEPARATOR.length) : full;
206
+ for (const key of new Set([full, short])) {
207
+ const named = byName.get(key) ?? [];
208
+ named.push(tool);
209
+ byName.set(key, named);
210
+ }
211
+ }
212
+ const named: T[] = [];
213
+ for (const raw of query.toLowerCase().split(/[^a-z0-9_]+/)) {
214
+ const token = raw.replace(/^_+|_+$/g, "");
215
+ if (!token.includes("_")) continue;
216
+ for (const tool of byName.get(token) ?? []) {
217
+ if (!named.includes(tool)) named.push(tool);
218
+ }
219
+ }
220
+ return named;
221
+ }
222
+
188
223
  /** Parse ranked or exact-name discovery arguments (string or object). */
189
224
  function parseSearchArgs(raw: unknown): { query: string; limit: number; names?: string[] } {
190
225
  let obj: Record<string, unknown> = {};
@@ -231,12 +266,25 @@ export function searchToolPool(availableTools: Tool[], rawArguments: unknown): T
231
266
  tool.type === "function" && typeof (tool as { name?: unknown }).name === "string",
232
267
  );
233
268
  if (searchable.length === 0) return [];
234
- const { query, limit, names } = parseSearchArgs(rawArguments);
235
- // Exact disclosure is a lookup in this already-authorized pool, not a fuzzy
236
- // search or another executor registry. Keep the SDK's original references.
237
- const ranked = names
238
- ? searchable.filter((tool) => names.includes(tool.name))
239
- : bm25RankTools(searchable, query, searchable.length);
269
+ const { query, limit: requestedLimit, names } = parseSearchArgs(rawArguments);
270
+ let ranked: Tool[];
271
+ let limit = requestedLimit;
272
+ if (names) {
273
+ // Exact disclosure is a lookup in this already-authorized pool, not a fuzzy
274
+ // search or another executor registry. Keep the SDK's original references.
275
+ ranked = searchable.filter((tool) => names.includes(tool.name));
276
+ } else {
277
+ // Every tool the query names comes first, even beyond the requested limit;
278
+ // BM25 fills any remaining places.
279
+ const named = toolsNamedInQuery(searchable, query);
280
+ ranked = [
281
+ ...named,
282
+ ...bm25RankTools(searchable, query, searchable.length).filter(
283
+ (tool) => !named.includes(tool as Tool & { name: string }),
284
+ ),
285
+ ];
286
+ limit = Math.min(MAX_SEARCH_LIMIT, Math.max(requestedLimit, named.length));
287
+ }
240
288
  const bounded: Tool[] = [];
241
289
  let disclosedBytes = 0;
242
290
  for (const tool of ranked) {
@@ -43,6 +43,10 @@ export type CompactionItem = Record<string, unknown>;
43
43
  export const COMPACTION_SUMMARY_MARKER = "opengeni_context_summary";
44
44
 
45
45
  export const SUMMARY_BUFFER_TOKENS = 20_000;
46
+ /** Leave input room on models whose entire context is smaller than the normal reserve. */
47
+ export function compactionSummaryOutputTokens(contextWindowTokens: number): number {
48
+ return Math.min(SUMMARY_BUFFER_TOKENS, Math.max(1, Math.floor(contextWindowTokens / 4)));
49
+ }
46
50
  // A single cumulative budget for all retained real user messages, matching
47
51
  // Codex core's build_compacted_history_with_limit (not a per-message allowance).
48
52
  export const COMPACT_USER_MESSAGE_MAX_TOKENS = 20_000;
@@ -1243,7 +1247,7 @@ export class EmptyCompactionSummaryError extends Error {
1243
1247
  constructor(diagnostics: Record<string, unknown> = {}) {
1244
1248
  const compact = JSON.stringify(diagnostics).slice(0, 2_000);
1245
1249
  super(
1246
- `Compaction summarizer returned no assistant text; active history was preserved${compact ? ` (${compact})` : ""}`,
1250
+ `Compaction could not produce a usable checkpoint; active history was preserved${compact ? ` (${compact})` : ""}`,
1247
1251
  );
1248
1252
  this.name = "EmptyCompactionSummaryError";
1249
1253
  this.diagnostics = diagnostics;
@@ -1520,10 +1524,18 @@ export function omitOpaqueArtifactsFromPortableCompactionHistory(
1520
1524
  }
1521
1525
  if (type === "reasoning" && hasOpaqueProviderArtifact(item)) {
1522
1526
  const projected = projectRejectedReasoningArtifact(item);
1527
+ if (Object.keys(projected).length === 1) {
1528
+ changed = true;
1529
+ continue;
1530
+ }
1523
1531
  changed ||= projected !== item;
1524
1532
  out.push(projected as CompactionItem);
1525
1533
  continue;
1526
1534
  }
1535
+ if (type === "reasoning" && Object.keys(item).length === 1) {
1536
+ changed = true;
1537
+ continue;
1538
+ }
1527
1539
  out.push(item);
1528
1540
  }
1529
1541
  return changed ? out : (items as CompactionItem[]);
@@ -1605,16 +1617,20 @@ function oldestLogicalUnitCuts(items: readonly CompactionItem[]): number[] {
1605
1617
 
1606
1618
  /**
1607
1619
  * Build the active history after compaction:
1608
- * the newest real user messages that fit one cumulative 20k-token budget
1620
+ * the newest real user messages that fit one cumulative, model-bounded budget
1609
1621
  * (prior summaries excluded, retained images preserved) plus one marked summary item.
1610
1622
  */
1611
1623
  export function buildCompactionReplacementHistory(
1612
1624
  items: readonly CompactionItem[],
1613
1625
  summaryBody: string,
1614
1626
  retainedItemTokens: (item: CompactionItem) => number = (item) => estimateTokens([item]),
1627
+ retainedMessageBudgetTokens = COMPACT_USER_MESSAGE_MAX_TOKENS,
1615
1628
  ): CompactionItem[] {
1616
1629
  const retainedReversed: CompactionItem[] = [];
1617
- let remaining = COMPACT_USER_MESSAGE_MAX_TOKENS;
1630
+ let remaining = Math.max(
1631
+ 0,
1632
+ Math.min(COMPACT_USER_MESSAGE_MAX_TOKENS, retainedMessageBudgetTokens),
1633
+ );
1618
1634
  for (let index = items.length - 1; index >= 0 && remaining > 0; index -= 1) {
1619
1635
  const item = items[index]!;
1620
1636
  if (!isUserMessage(item) || isCompactionSummary(item) || isAttachmentCatalog(item)) {