@oh-my-pi/pi-agent-core 16.3.15 → 16.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.4.1] - 2026-07-10
6
+
7
+ ### Fixed
8
+
9
+ - Enabled reasoning encryption content for all Responses Lite compaction requests
10
+
11
+ ## [16.4.0] - 2026-07-10
12
+
13
+ ### Added
14
+
15
+ - Added the `ThinkingLevel.Max` ("max") configuration option, mapping to the `Effort.Max` tier for supported models.
16
+
17
+ ### Fixed
18
+
19
+ - Fixed remote compaction behavior for Codex Responses Lite (GPT-5.6 family) models across both V1 and V2 endpoints to ensure correct formatting and routing.
20
+ - Fixed an issue where aborted tool-result hooks could trigger subsequent provider calls before the abort signal fully settled.
21
+
5
22
  ## [16.3.12] - 2026-07-08
6
23
 
7
24
  ### Added
package/README.md CHANGED
@@ -134,7 +134,7 @@ const agent = new Agent({
134
134
  initialState: {
135
135
  systemPrompt: string[],
136
136
  model: Model,
137
- thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh",
137
+ thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max",
138
138
  tools: AgentTool<any>[],
139
139
  messages: AgentMessage[],
140
140
  },
@@ -26,6 +26,12 @@ export interface ToolScopedAbortReason {
26
26
  }
27
27
  /** Creates an abort reason that labels matching tool calls separately from siblings. */
28
28
  export declare function createToolScopedAbortReason(message: string, toolCallMessages: Record<string, string>, defaultToolCallMessage: string): ToolScopedAbortReason;
29
+ /**
30
+ * Marks an abort raised by a completed post-tool hook as terminal for the
31
+ * current run. External/user aborts still synthesize an aborted assistant
32
+ * boundary; this reason stops after persisting the completed tool batch.
33
+ */
34
+ export declare const TERMINAL_TOOL_RESULT_ABORT_REASON: unique symbol;
29
35
  export declare function resolveOwnedDialectFromEnv(value: string | undefined): Dialect | undefined;
30
36
  /**
31
37
  * Start an agent loop with a new prompt message.
@@ -6,7 +6,7 @@
6
6
  * compaction output item, then install retained real user messages plus that
7
7
  * compaction item as replacement history.
8
8
  */
9
- import type { FetchImpl, Model } from "@oh-my-pi/pi-ai";
9
+ import type { CodexCompactionContext, FetchImpl, Model, ProviderSessionState } from "@oh-my-pi/pi-ai";
10
10
  /** Retained-message budget Codex uses after streamed V2 compaction. */
11
11
  export declare const V2_RETAINED_MESSAGE_TOKEN_BUDGET = 64000;
12
12
  /** Max retries for V2 streaming compaction on transient stream errors. */
@@ -66,6 +66,8 @@ export declare function requestCompactionV2Streaming(model: Model, apiKey: strin
66
66
  fetch?: FetchImpl;
67
67
  timeoutMs?: number;
68
68
  retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
69
+ providerSessionState?: Map<string, ProviderSessionState>;
70
+ codexCompaction?: CodexCompactionContext;
69
71
  }): Promise<CompactionV2Response>;
70
72
  /** Build Codex-style V2 replacement history from prompt input plus compaction output. */
71
73
  export declare function buildCompactionV2ReplacementHistory(input: unknown[], compactionItem: Record<string, unknown>, retainedMessageBudget?: number): {
@@ -4,7 +4,7 @@
4
4
  * Pure functions for compaction logic. The session manager handles I/O,
5
5
  * and after compaction the session is reloaded.
6
6
  */
7
- import { type Api, type ApiKey, type AssistantMessage, type Context, type FetchImpl, type MessageAttribution, type Model, type SimpleStreamOptions, type Tool, type Usage } from "@oh-my-pi/pi-ai";
7
+ import { type Api, type ApiKey, type AssistantMessage, type CodexCompactionContext, type Context, type FetchImpl, type MessageAttribution, type Model, type ProviderSessionState, type SimpleStreamOptions, type Tool, type Usage } from "@oh-my-pi/pi-ai";
8
8
  import { type AgentTelemetry } from "../telemetry.js";
9
9
  import { ThinkingLevel } from "../thinking.js";
10
10
  import type { AgentMessage } from "../types.js";
@@ -184,6 +184,10 @@ export interface SummaryOptions {
184
184
  sessionId?: string;
185
185
  /** Prompt-cache key for remote compaction transports that support provider prefix caching. */
186
186
  promptCacheKey?: string;
187
+ /** Mutable provider state used to keep Codex compaction on the live session identity. */
188
+ providerSessionState?: Map<string, ProviderSessionState>;
189
+ /** Classification shared by every provider request in this logical compaction. */
190
+ codexCompaction?: CodexCompactionContext;
187
191
  /** Provider-visible tools for remote compaction transports that replay native tool history. */
188
192
  tools?: Tool[];
189
193
  /** Optional fetch implementation threaded into remote compaction calls. */
@@ -14,7 +14,7 @@
14
14
  * summarization endpoints that accept `{ systemPrompt, prompt }` and reply
15
15
  * with `{ summary, shortSummary? }`.
16
16
  */
17
- import type { FetchImpl, Message, Model } from "@oh-my-pi/pi-ai/types";
17
+ import type { CodexCompactionContext, FetchImpl, Message, Model, ProviderSessionState } from "@oh-my-pi/pi-ai/types";
18
18
  export * from "./compaction-v2-streaming.js";
19
19
  export declare const OPENAI_REMOTE_COMPACTION_PRESERVE_KEY = "openaiRemoteCompaction";
20
20
  /**
@@ -40,6 +40,11 @@ export interface OpenAiRemoteCompactionRequest {
40
40
  model: string;
41
41
  input: Array<Record<string, unknown>>;
42
42
  instructions: string;
43
+ reasoning?: {
44
+ context?: string;
45
+ [key: string]: unknown;
46
+ };
47
+ include?: string[];
43
48
  }
44
49
  export interface OpenAiRemoteCompactionResponse extends OpenAiRemoteCompactionPreserveData {
45
50
  }
@@ -70,6 +75,9 @@ export declare function buildOpenAiNativeHistory(messages: Message[], model: Mod
70
75
  export declare function requestOpenAiRemoteCompaction(model: Model, apiKey: string, compactInput: Array<Record<string, unknown>>, instructions: string, signal?: AbortSignal, opts?: {
71
76
  fetch?: FetchImpl;
72
77
  timeoutMs?: number;
78
+ sessionId?: string;
79
+ providerSessionState?: Map<string, ProviderSessionState>;
80
+ codexCompaction?: CodexCompactionContext;
73
81
  }): Promise<OpenAiRemoteCompactionResponse>;
74
82
  /**
75
83
  * Generic remote-compaction POST. Two wire shapes are auto-selected by
@@ -12,6 +12,7 @@ export declare const ThinkingLevel: {
12
12
  readonly Medium: Effort.Medium;
13
13
  readonly High: Effort.High;
14
14
  readonly XHigh: Effort.XHigh;
15
+ readonly Max: Effort.Max;
15
16
  };
16
17
  export type ThinkingLevel = (typeof ThinkingLevel)[keyof typeof ThinkingLevel];
17
18
  export type ResolvedThinkingLevel = Exclude<ThinkingLevel, "inherit">;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-agent-core",
4
- "version": "16.3.15",
4
+ "version": "16.4.1",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -35,12 +35,12 @@
35
35
  "fmt": "biome format --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/pi-ai": "16.3.15",
39
- "@oh-my-pi/pi-catalog": "16.3.15",
40
- "@oh-my-pi/pi-natives": "16.3.15",
41
- "@oh-my-pi/pi-utils": "16.3.15",
42
- "@oh-my-pi/pi-wire": "16.3.15",
43
- "@oh-my-pi/snapcompact": "16.3.15",
38
+ "@oh-my-pi/pi-ai": "16.4.1",
39
+ "@oh-my-pi/pi-catalog": "16.4.1",
40
+ "@oh-my-pi/pi-natives": "16.4.1",
41
+ "@oh-my-pi/pi-utils": "16.4.1",
42
+ "@oh-my-pi/pi-wire": "16.4.1",
43
+ "@oh-my-pi/snapcompact": "16.4.1",
44
44
  "@opentelemetry/api": "^1.9.1"
45
45
  },
46
46
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -132,6 +132,13 @@ export function createToolScopedAbortReason(
132
132
  return { kind: "tool-scoped-abort", message, toolCallMessages, defaultToolCallMessage };
133
133
  }
134
134
 
135
+ /**
136
+ * Marks an abort raised by a completed post-tool hook as terminal for the
137
+ * current run. External/user aborts still synthesize an aborted assistant
138
+ * boundary; this reason stops after persisting the completed tool batch.
139
+ */
140
+ export const TERMINAL_TOOL_RESULT_ABORT_REASON = Symbol.for("pi-agent-core.terminal-tool-result");
141
+
135
142
  const STEERING_INTERRUPT_POLL_MS = 250;
136
143
 
137
144
  class HarmonyLeakInterruption extends Error {
@@ -1084,6 +1091,12 @@ async function runLoopBody(
1084
1091
  }
1085
1092
  }
1086
1093
 
1094
+ // A tool hook may mark its completed result as terminal (e.g. subagent yield).
1095
+ // Stop before the next provider call without changing external/user abort semantics.
1096
+ if (signal?.reason === TERMINAL_TOOL_RESULT_ABORT_REASON) {
1097
+ hasMoreToolCalls = false;
1098
+ }
1099
+
1087
1100
  if (toolCalls.length > 0) {
1088
1101
  pausedTurnContinuations = 0;
1089
1102
  } else if (
@@ -7,8 +7,14 @@
7
7
  * compaction item as replacement history.
8
8
  */
9
9
 
10
- import type { Api, FetchImpl, Model } from "@oh-my-pi/pi-ai";
10
+ import type { Api, CodexCompactionContext, FetchImpl, Model, ProviderSessionState } from "@oh-my-pi/pi-ai";
11
11
  import { isTransientStatus, ProviderHttpError } from "@oh-my-pi/pi-ai/error";
12
+ import { applyCodexResponsesLiteShape } from "@oh-my-pi/pi-ai/providers/openai-codex/request-transformer";
13
+ import {
14
+ createOpenAICodexCompactionRequestContext,
15
+ createOpenAICodexCompatibilityMetadata,
16
+ type OpenAICodexCompatibilityMetadata,
17
+ } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
12
18
  import {
13
19
  getOpenAIPromptCacheKey,
14
20
  getOpenAIResponsesRoutingSessionId,
@@ -219,6 +225,8 @@ export async function requestCompactionV2Streaming(
219
225
  fetch?: FetchImpl;
220
226
  timeoutMs?: number;
221
227
  retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
228
+ providerSessionState?: Map<string, ProviderSessionState>;
229
+ codexCompaction?: CodexCompactionContext;
222
230
  },
223
231
  ): Promise<CompactionV2Response> {
224
232
  const endpoint = getCompactionV2Endpoint(model);
@@ -228,12 +236,32 @@ export async function requestCompactionV2Streaming(
228
236
 
229
237
  const fetchImpl = options?.fetch ?? globalThis.fetch;
230
238
  const retryWait = options?.retryWait ?? ((delayMs: number) => Bun.sleep(delayMs));
239
+ const isCodexResponses = compactionV2Api(model) === "openai-codex-responses" || model.provider === "openai-codex";
240
+ const codexMetadata = isCodexResponses
241
+ ? createOpenAICodexCompatibilityMetadata({
242
+ sessionId: request.sessionId,
243
+ providerSessionState: options?.providerSessionState,
244
+ requestKind: "compaction",
245
+ compaction: createOpenAICodexCompactionRequestContext({
246
+ context: options?.codexCompaction,
247
+ implementation: "responses_compaction_v2",
248
+ }),
249
+ })
250
+ : undefined;
231
251
  let lastError: Error | undefined;
232
252
 
233
253
  for (let attempt = 0; attempt <= V2_COMPACTION_MAX_RETRIES; attempt++) {
234
254
  const timeoutSignal = withRequestTimeout(signal, options?.timeoutMs ?? V2_COMPACTION_TIMEOUT_MS);
235
255
  try {
236
- return await attemptCompactionV2Streaming(endpoint, apiKey, model, request, fetchImpl, timeoutSignal);
256
+ return await attemptCompactionV2Streaming(
257
+ endpoint,
258
+ apiKey,
259
+ model,
260
+ request,
261
+ fetchImpl,
262
+ timeoutSignal,
263
+ codexMetadata,
264
+ );
237
265
  } catch (err) {
238
266
  const error = err instanceof Error ? err : new Error(String(err));
239
267
  if (signal?.aborted) throw error;
@@ -264,6 +292,7 @@ async function attemptCompactionV2Streaming(
264
292
  request: CompactionV2Request,
265
293
  fetchImpl: FetchImpl,
266
294
  signal?: AbortSignal,
295
+ codexMetadata?: OpenAICodexCompatibilityMetadata,
267
296
  ): Promise<CompactionV2Response> {
268
297
  // Faithful to Codex: append the compaction trigger as the final input item
269
298
  // of an otherwise-normal Responses request, then stream the result. `store`
@@ -276,13 +305,30 @@ async function attemptCompactionV2Streaming(
276
305
  instructions: request.instructions,
277
306
  stream: true,
278
307
  store: false,
279
- ...(request.reasoning ? { reasoning: request.reasoning, include: ["reasoning.encrypted_content"] } : {}),
308
+ ...(request.reasoning || model.useResponsesLite
309
+ ? {
310
+ // Lite implies gpt-5.4+, where codex-rs sends `all_turns` replay.
311
+ reasoning: model.useResponsesLite
312
+ ? { ...(request.reasoning ?? {}), context: "all_turns" }
313
+ : request.reasoning,
314
+ include: ["reasoning.encrypted_content"],
315
+ }
316
+ : {}),
280
317
  ...(promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}),
281
318
  ...(request.tools && request.tools.length > 0 ? { tools: request.tools, tool_choice: "auto" } : {}),
282
319
  };
320
+ if (codexMetadata) {
321
+ body.client_metadata = codexMetadata.clientMetadata;
322
+ }
323
+ // Responses Lite models take the same rewrite on the compaction stream:
324
+ // instructions/tools ride as input items (codex-rs `compact_remote_v2`
325
+ // builds through `build_responses_request`).
326
+ if (model.useResponsesLite) {
327
+ applyCodexResponsesLiteShape(body);
328
+ }
283
329
  const response = await fetchImpl(endpoint, {
284
330
  method: "POST",
285
- headers: buildCompactionV2Headers(model, apiKey, request),
331
+ headers: buildCompactionV2Headers(model, apiKey, request, codexMetadata),
286
332
  body: JSON.stringify(body),
287
333
  signal,
288
334
  });
@@ -307,7 +353,12 @@ async function attemptCompactionV2Streaming(
307
353
  return collectCompactionV2Output(response, request);
308
354
  }
309
355
 
310
- function buildCompactionV2Headers(model: Model, apiKey: string, request: CompactionV2Request): Record<string, string> {
356
+ function buildCompactionV2Headers(
357
+ model: Model,
358
+ apiKey: string,
359
+ request: CompactionV2Request,
360
+ codexMetadata?: OpenAICodexCompatibilityMetadata,
361
+ ): Record<string, string> {
311
362
  const api = compactionV2Api(model);
312
363
  const cacheOptions = { sessionId: request.sessionId, promptCacheKey: request.promptCacheKey };
313
364
  const routingSessionId = getOpenAIResponsesRoutingSessionId(cacheOptions);
@@ -338,7 +389,11 @@ function buildCompactionV2Headers(model: Model, apiKey: string, request: Compact
338
389
  }
339
390
  headers[OPENAI_HEADERS.BETA] = OPENAI_HEADER_VALUES.BETA_RESPONSES;
340
391
  headers[OPENAI_HEADERS.ORIGINATOR] = OPENAI_HEADER_VALUES.ORIGINATOR_CODEX;
392
+ if (model.useResponsesLite) {
393
+ headers[OPENAI_HEADERS.RESPONSES_LITE] = "true";
394
+ }
341
395
  }
396
+ if (codexMetadata) Object.assign(headers, codexMetadata.headers);
342
397
 
343
398
  return headers;
344
399
  }
@@ -9,18 +9,21 @@ import {
9
9
  type Api,
10
10
  type ApiKey,
11
11
  type AssistantMessage,
12
+ type CodexCompactionContext,
12
13
  type Context,
13
14
  Effort,
14
15
  type FetchImpl,
15
16
  type Message,
16
17
  type MessageAttribution,
17
18
  type Model,
19
+ type ProviderSessionState,
18
20
  type SimpleStreamOptions,
19
21
  type Tool,
20
22
  type Usage,
21
23
  withAuth,
22
24
  } from "@oh-my-pi/pi-ai";
23
25
  import { ProviderHttpError } from "@oh-my-pi/pi-ai/error";
26
+ import { createOpenAICodexCompactionRequestContext } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
24
27
  import { convertTools } from "@oh-my-pi/pi-ai/providers/openai-responses";
25
28
  import { buildResponsesInput, resolveOpenAICompatPolicy } from "@oh-my-pi/pi-ai/providers/openai-shared";
26
29
  import { preferredDialect } from "@oh-my-pi/pi-catalog/identity";
@@ -656,6 +659,8 @@ function effortFromThinkingLevel(level: ThinkingLevel): Effort {
656
659
  return Effort.High;
657
660
  case ThinkingLevel.XHigh:
658
661
  return Effort.XHigh;
662
+ case ThinkingLevel.Max:
663
+ return Effort.Max;
659
664
  case ThinkingLevel.Off:
660
665
  case ThinkingLevel.Inherit:
661
666
  throw new Error(`effortFromThinkingLevel: ${level} must be handled by caller`);
@@ -736,6 +741,10 @@ export interface SummaryOptions {
736
741
  sessionId?: string;
737
742
  /** Prompt-cache key for remote compaction transports that support provider prefix caching. */
738
743
  promptCacheKey?: string;
744
+ /** Mutable provider state used to keep Codex compaction on the live session identity. */
745
+ providerSessionState?: Map<string, ProviderSessionState>;
746
+ /** Classification shared by every provider request in this logical compaction. */
747
+ codexCompaction?: CodexCompactionContext;
739
748
  /** Provider-visible tools for remote compaction transports that replay native tool history. */
740
749
  tools?: Tool[];
741
750
  /** Optional fetch implementation threaded into remote compaction calls. */
@@ -755,6 +764,13 @@ export interface SummaryOptions {
755
764
  ) => Promise<AssistantMessage>;
756
765
  }
757
766
 
767
+ function localCodexCompaction(options: SummaryOptions | undefined) {
768
+ return createOpenAICodexCompactionRequestContext({
769
+ context: options?.codexCompaction,
770
+ implementation: "responses",
771
+ });
772
+ }
773
+
758
774
  function formatPreviousSnapcompactArchive(archiveText: string): string {
759
775
  return prompt.render(snapcompactArchiveContextPrompt, { archiveText });
760
776
  }
@@ -844,6 +860,11 @@ export async function generateSummary(
844
860
  reasoning: resolveCompactionEffort(model, options?.thinkingLevel),
845
861
  initiatorOverride: options?.initiatorOverride,
846
862
  metadata: options?.metadata,
863
+ fetch: options?.fetch,
864
+ sessionId: options?.sessionId,
865
+ promptCacheKey: options?.promptCacheKey,
866
+ providerSessionState: options?.providerSessionState,
867
+ codexCompaction: localCodexCompaction(options),
847
868
  },
848
869
  { telemetry: options?.telemetry, oneshotKind: "compaction_summary", completeImpl: options?.completeImpl },
849
870
  );
@@ -1047,6 +1068,11 @@ async function generateShortSummary(
1047
1068
  reasoning: resolveCompactionEffort(model, options?.thinkingLevel),
1048
1069
  initiatorOverride: options?.initiatorOverride,
1049
1070
  metadata: options?.metadata,
1071
+ fetch: options?.fetch,
1072
+ sessionId: options?.sessionId,
1073
+ promptCacheKey: options?.promptCacheKey,
1074
+ providerSessionState: options?.providerSessionState,
1075
+ codexCompaction: localCodexCompaction(options),
1050
1076
  },
1051
1077
  { telemetry: options?.telemetry, oneshotKind: "compaction_short_summary", completeImpl: options?.completeImpl },
1052
1078
  );
@@ -1317,6 +1343,8 @@ export async function compact(
1317
1343
  thinkingLevel: options?.thinkingLevel,
1318
1344
  sessionId: options?.sessionId,
1319
1345
  promptCacheKey: options?.promptCacheKey,
1346
+ providerSessionState: options?.providerSessionState,
1347
+ codexCompaction: options?.codexCompaction,
1320
1348
  tools: options?.tools,
1321
1349
  fetch: options?.fetch,
1322
1350
  completeImpl: options?.completeImpl,
@@ -1375,7 +1403,12 @@ export async function compact(
1375
1403
  );
1376
1404
  const remote = await withAuth(
1377
1405
  apiKey,
1378
- key => requestCompactionV2Streaming(model, key, request, signal, { fetch: summaryOptions.fetch }),
1406
+ key =>
1407
+ requestCompactionV2Streaming(model, key, request, signal, {
1408
+ fetch: summaryOptions.fetch,
1409
+ providerSessionState: summaryOptions.providerSessionState,
1410
+ codexCompaction: summaryOptions.codexCompaction,
1411
+ }),
1379
1412
  { signal },
1380
1413
  );
1381
1414
  preserveData = { ...(preserveData ?? {}), ...storeCompactionV2PreserveData(remote, model) };
@@ -1419,7 +1452,12 @@ export async function compact(
1419
1452
  remoteHistory,
1420
1453
  summaryOptions.remoteInstructions ?? SUMMARIZATION_SYSTEM_PROMPT,
1421
1454
  signal,
1422
- { fetch: summaryOptions.fetch },
1455
+ {
1456
+ fetch: summaryOptions.fetch,
1457
+ sessionId: summaryOptions.sessionId,
1458
+ providerSessionState: summaryOptions.providerSessionState,
1459
+ codexCompaction: summaryOptions.codexCompaction,
1460
+ },
1423
1461
  ),
1424
1462
  { signal },
1425
1463
  );
@@ -1495,16 +1533,9 @@ export async function compact(
1495
1533
  const shortSummary = usedRemoteCompaction
1496
1534
  ? "Remote compaction"
1497
1535
  : await generateShortSummary(recentMessages, summary, model, reserveTokens, apiKey, signal, {
1536
+ ...summaryOptions,
1498
1537
  extraContext: options?.extraContext,
1499
- remoteEndpoint: summaryOptions.remoteEndpoint,
1500
- initiatorOverride: summaryOptions.initiatorOverride,
1501
- metadata: summaryOptions.metadata,
1502
- telemetry: summaryOptions.telemetry,
1503
- // Same propagation as summaryOptions above — generateShortSummary
1504
- // resolves its own reasoning via resolveCompactionEffort.
1505
1538
  thinkingLevel: options?.thinkingLevel,
1506
- fetch: summaryOptions.fetch,
1507
- completeImpl: summaryOptions.completeImpl,
1508
1539
  });
1509
1540
 
1510
1541
  // Compute file lists and append to summary
@@ -1567,6 +1598,11 @@ async function generateTurnPrefixSummary(
1567
1598
  reasoning: resolveCompactionEffort(model, options?.thinkingLevel),
1568
1599
  initiatorOverride: options?.initiatorOverride,
1569
1600
  metadata: options?.metadata,
1601
+ fetch: options?.fetch,
1602
+ sessionId: options?.sessionId,
1603
+ promptCacheKey: options?.promptCacheKey,
1604
+ providerSessionState: options?.providerSessionState,
1605
+ codexCompaction: localCodexCompaction(options),
1570
1606
  },
1571
1607
  { telemetry: options?.telemetry, oneshotKind: "compaction_turn_prefix", completeImpl: options?.completeImpl },
1572
1608
  );
@@ -16,9 +16,22 @@
16
16
  */
17
17
 
18
18
  import { ProviderHttpError } from "@oh-my-pi/pi-ai/error";
19
+ import { applyCodexResponsesLiteShape } from "@oh-my-pi/pi-ai/providers/openai-codex/request-transformer";
20
+ import {
21
+ createOpenAICodexCompactionRequestContext,
22
+ createOpenAICodexCompatibilityMetadata,
23
+ } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
19
24
  import { parseAzureDeploymentNameMap, parseTextSignature } from "@oh-my-pi/pi-ai/providers/openai-shared";
20
25
  import { transformMessages } from "@oh-my-pi/pi-ai/providers/transform-messages";
21
- import type { Api, AssistantMessage, FetchImpl, Message, Model } from "@oh-my-pi/pi-ai/types";
26
+ import type {
27
+ Api,
28
+ AssistantMessage,
29
+ CodexCompactionContext,
30
+ FetchImpl,
31
+ Message,
32
+ Model,
33
+ ProviderSessionState,
34
+ } from "@oh-my-pi/pi-ai/types";
22
35
  import {
23
36
  getOpenAIResponsesHistoryItems,
24
37
  getOpenAIResponsesHistoryPayload,
@@ -75,6 +88,11 @@ export interface OpenAiRemoteCompactionRequest {
75
88
  model: string;
76
89
  input: Array<Record<string, unknown>>;
77
90
  instructions: string;
91
+ reasoning?: {
92
+ context?: string;
93
+ [key: string]: unknown;
94
+ };
95
+ include?: string[];
78
96
  }
79
97
 
80
98
  export interface OpenAiRemoteCompactionResponse extends OpenAiRemoteCompactionPreserveData {}
@@ -460,7 +478,13 @@ export async function requestOpenAiRemoteCompaction(
460
478
  compactInput: Array<Record<string, unknown>>,
461
479
  instructions: string,
462
480
  signal?: AbortSignal,
463
- opts?: { fetch?: FetchImpl; timeoutMs?: number },
481
+ opts?: {
482
+ fetch?: FetchImpl;
483
+ timeoutMs?: number;
484
+ sessionId?: string;
485
+ providerSessionState?: Map<string, ProviderSessionState>;
486
+ codexCompaction?: CodexCompactionContext;
487
+ },
464
488
  ): Promise<OpenAiRemoteCompactionResponse> {
465
489
  const endpoint = resolveOpenAiCompactEndpoint(model);
466
490
  const requestModel = resolveOpenAiCompactModel(model);
@@ -473,6 +497,8 @@ export async function requestOpenAiRemoteCompaction(
473
497
  instructions,
474
498
  };
475
499
  const isAzureOpenAiResponses = (model.remoteCompaction?.api ?? model.api) === "azure-openai-responses";
500
+ const isCodexResponses =
501
+ model.provider === "openai-codex" || (model.remoteCompaction?.api ?? model.api) === "openai-codex-responses";
476
502
  const headers: Record<string, string> = isAzureOpenAiResponses
477
503
  ? {
478
504
  "content-type": "application/json",
@@ -486,13 +512,38 @@ export async function requestOpenAiRemoteCompaction(
486
512
  };
487
513
 
488
514
  // Codex endpoints require additional auth headers
489
- if (model.provider === "openai-codex") {
515
+ if (isCodexResponses) {
490
516
  const accountId = getCodexAccountId(apiKey);
491
517
  if (accountId) {
492
518
  headers[OPENAI_HEADERS.ACCOUNT_ID] = accountId;
493
519
  }
494
520
  headers[OPENAI_HEADERS.BETA] = OPENAI_HEADER_VALUES.BETA_RESPONSES;
495
521
  headers[OPENAI_HEADERS.ORIGINATOR] = OPENAI_HEADER_VALUES.ORIGINATOR_CODEX;
522
+ Object.assign(
523
+ headers,
524
+ createOpenAICodexCompatibilityMetadata({
525
+ sessionId: opts?.sessionId,
526
+ providerSessionState: opts?.providerSessionState,
527
+ requestKind: "compaction",
528
+ compaction: createOpenAICodexCompactionRequestContext({
529
+ context: opts?.codexCompaction,
530
+ implementation: "responses_compact",
531
+ }),
532
+ includeInstallationHeader: true,
533
+ }).headers,
534
+ );
535
+ // Responses Lite models take the same rewrite on `/responses/compact`:
536
+ // instructions ride as an input item and the lite marker header is set
537
+ // (codex-rs routes compaction through `build_responses_request`).
538
+ if (model.useResponsesLite) {
539
+ applyCodexResponsesLiteShape(request);
540
+ headers[OPENAI_HEADERS.RESPONSES_LITE] = "true";
541
+ request.reasoning = {
542
+ ...request.reasoning,
543
+ context: "all_turns",
544
+ };
545
+ request.include = Array.from(new Set([...(request.include ?? []), "reasoning.encrypted_content"]));
546
+ }
496
547
  }
497
548
 
498
549
  const response = await (opts?.fetch ?? fetch)(endpoint, {
package/src/thinking.ts CHANGED
@@ -13,6 +13,7 @@ export const ThinkingLevel = {
13
13
  Medium: Effort.Medium,
14
14
  High: Effort.High,
15
15
  XHigh: Effort.XHigh,
16
+ Max: Effort.Max,
16
17
  } as const;
17
18
 
18
19
  export type ThinkingLevel = (typeof ThinkingLevel)[keyof typeof ThinkingLevel];