@oh-my-pi/pi-agent-core 16.3.14 → 16.4.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.4.0] - 2026-07-10
6
+
7
+ ### Added
8
+
9
+ - Added the `ThinkingLevel.Max` ("max") configuration option, mapping to the `Effort.Max` tier for supported models.
10
+
11
+ ### Fixed
12
+
13
+ - 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.
14
+ - Fixed an issue where aborted tool-result hooks could trigger subsequent provider calls before the abort signal fully settled.
15
+
5
16
  ## [16.3.12] - 2026-07-08
6
17
 
7
18
  ### 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
  /**
@@ -70,6 +70,9 @@ export declare function buildOpenAiNativeHistory(messages: Message[], model: Mod
70
70
  export declare function requestOpenAiRemoteCompaction(model: Model, apiKey: string, compactInput: Array<Record<string, unknown>>, instructions: string, signal?: AbortSignal, opts?: {
71
71
  fetch?: FetchImpl;
72
72
  timeoutMs?: number;
73
+ sessionId?: string;
74
+ providerSessionState?: Map<string, ProviderSessionState>;
75
+ codexCompaction?: CodexCompactionContext;
73
76
  }): Promise<OpenAiRemoteCompactionResponse>;
74
77
  /**
75
78
  * 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.14",
4
+ "version": "16.4.0",
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.14",
39
- "@oh-my-pi/pi-catalog": "16.3.14",
40
- "@oh-my-pi/pi-natives": "16.3.14",
41
- "@oh-my-pi/pi-utils": "16.3.14",
42
- "@oh-my-pi/pi-wire": "16.3.14",
43
- "@oh-my-pi/snapcompact": "16.3.14",
38
+ "@oh-my-pi/pi-ai": "16.4.0",
39
+ "@oh-my-pi/pi-catalog": "16.4.0",
40
+ "@oh-my-pi/pi-natives": "16.4.0",
41
+ "@oh-my-pi/pi-utils": "16.4.0",
42
+ "@oh-my-pi/pi-wire": "16.4.0",
43
+ "@oh-my-pi/snapcompact": "16.4.0",
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,10 +7,16 @@
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";
12
13
  import {
13
- getOpenAIResponsesPromptCacheKey,
14
+ createOpenAICodexCompactionRequestContext,
15
+ createOpenAICodexCompatibilityMetadata,
16
+ type OpenAICodexCompatibilityMetadata,
17
+ } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
18
+ import {
19
+ getOpenAIPromptCacheKey,
14
20
  getOpenAIResponsesRoutingSessionId,
15
21
  parseAzureDeploymentNameMap,
16
22
  resolveOpenAIRequestSetup,
@@ -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,25 +292,41 @@ 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`
270
299
  // stays false — compaction must never persist a server-side response object.
271
300
  const cacheOptions = { sessionId: request.sessionId, promptCacheKey: request.promptCacheKey };
272
- const promptCacheKey = getOpenAIResponsesPromptCacheKey(cacheOptions);
301
+ const promptCacheKey = getOpenAIPromptCacheKey(cacheOptions);
273
302
  const body: Record<string, unknown> = {
274
303
  model: request.model,
275
304
  input: [...request.input, COMPACTION_TRIGGER_ITEM],
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
309
+ ? {
310
+ // Lite implies gpt-5.4+, where codex-rs sends `all_turns` replay.
311
+ reasoning: model.useResponsesLite ? { ...request.reasoning, context: "all_turns" } : request.reasoning,
312
+ include: ["reasoning.encrypted_content"],
313
+ }
314
+ : {}),
280
315
  ...(promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}),
281
316
  ...(request.tools && request.tools.length > 0 ? { tools: request.tools, tool_choice: "auto" } : {}),
282
317
  };
318
+ if (codexMetadata) {
319
+ body.client_metadata = codexMetadata.clientMetadata;
320
+ }
321
+ // Responses Lite models take the same rewrite on the compaction stream:
322
+ // instructions/tools ride as input items (codex-rs `compact_remote_v2`
323
+ // builds through `build_responses_request`).
324
+ if (model.useResponsesLite) {
325
+ applyCodexResponsesLiteShape(body);
326
+ }
283
327
  const response = await fetchImpl(endpoint, {
284
328
  method: "POST",
285
- headers: buildCompactionV2Headers(model, apiKey, request),
329
+ headers: buildCompactionV2Headers(model, apiKey, request, codexMetadata),
286
330
  body: JSON.stringify(body),
287
331
  signal,
288
332
  });
@@ -307,11 +351,16 @@ async function attemptCompactionV2Streaming(
307
351
  return collectCompactionV2Output(response, request);
308
352
  }
309
353
 
310
- function buildCompactionV2Headers(model: Model, apiKey: string, request: CompactionV2Request): Record<string, string> {
354
+ function buildCompactionV2Headers(
355
+ model: Model,
356
+ apiKey: string,
357
+ request: CompactionV2Request,
358
+ codexMetadata?: OpenAICodexCompatibilityMetadata,
359
+ ): Record<string, string> {
311
360
  const api = compactionV2Api(model);
312
361
  const cacheOptions = { sessionId: request.sessionId, promptCacheKey: request.promptCacheKey };
313
362
  const routingSessionId = getOpenAIResponsesRoutingSessionId(cacheOptions);
314
- const promptCacheSessionId = getOpenAIResponsesPromptCacheKey(cacheOptions);
363
+ const promptCacheSessionId = getOpenAIPromptCacheKey(cacheOptions);
315
364
  const headers: Record<string, string> =
316
365
  api === "azure-openai-responses"
317
366
  ? {
@@ -338,7 +387,11 @@ function buildCompactionV2Headers(model: Model, apiKey: string, request: Compact
338
387
  }
339
388
  headers[OPENAI_HEADERS.BETA] = OPENAI_HEADER_VALUES.BETA_RESPONSES;
340
389
  headers[OPENAI_HEADERS.ORIGINATOR] = OPENAI_HEADER_VALUES.ORIGINATOR_CODEX;
390
+ if (model.useResponsesLite) {
391
+ headers[OPENAI_HEADERS.RESPONSES_LITE] = "true";
392
+ }
341
393
  }
394
+ if (codexMetadata) Object.assign(headers, codexMetadata.headers);
342
395
 
343
396
  return headers;
344
397
  }
@@ -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,
@@ -460,7 +473,13 @@ export async function requestOpenAiRemoteCompaction(
460
473
  compactInput: Array<Record<string, unknown>>,
461
474
  instructions: string,
462
475
  signal?: AbortSignal,
463
- opts?: { fetch?: FetchImpl; timeoutMs?: number },
476
+ opts?: {
477
+ fetch?: FetchImpl;
478
+ timeoutMs?: number;
479
+ sessionId?: string;
480
+ providerSessionState?: Map<string, ProviderSessionState>;
481
+ codexCompaction?: CodexCompactionContext;
482
+ },
464
483
  ): Promise<OpenAiRemoteCompactionResponse> {
465
484
  const endpoint = resolveOpenAiCompactEndpoint(model);
466
485
  const requestModel = resolveOpenAiCompactModel(model);
@@ -473,6 +492,8 @@ export async function requestOpenAiRemoteCompaction(
473
492
  instructions,
474
493
  };
475
494
  const isAzureOpenAiResponses = (model.remoteCompaction?.api ?? model.api) === "azure-openai-responses";
495
+ const isCodexResponses =
496
+ model.provider === "openai-codex" || (model.remoteCompaction?.api ?? model.api) === "openai-codex-responses";
476
497
  const headers: Record<string, string> = isAzureOpenAiResponses
477
498
  ? {
478
499
  "content-type": "application/json",
@@ -486,13 +507,33 @@ export async function requestOpenAiRemoteCompaction(
486
507
  };
487
508
 
488
509
  // Codex endpoints require additional auth headers
489
- if (model.provider === "openai-codex") {
510
+ if (isCodexResponses) {
490
511
  const accountId = getCodexAccountId(apiKey);
491
512
  if (accountId) {
492
513
  headers[OPENAI_HEADERS.ACCOUNT_ID] = accountId;
493
514
  }
494
515
  headers[OPENAI_HEADERS.BETA] = OPENAI_HEADER_VALUES.BETA_RESPONSES;
495
516
  headers[OPENAI_HEADERS.ORIGINATOR] = OPENAI_HEADER_VALUES.ORIGINATOR_CODEX;
517
+ Object.assign(
518
+ headers,
519
+ createOpenAICodexCompatibilityMetadata({
520
+ sessionId: opts?.sessionId,
521
+ providerSessionState: opts?.providerSessionState,
522
+ requestKind: "compaction",
523
+ compaction: createOpenAICodexCompactionRequestContext({
524
+ context: opts?.codexCompaction,
525
+ implementation: "responses_compact",
526
+ }),
527
+ includeInstallationHeader: true,
528
+ }).headers,
529
+ );
530
+ // Responses Lite models take the same rewrite on `/responses/compact`:
531
+ // instructions ride as an input item and the lite marker header is set
532
+ // (codex-rs routes compaction through `build_responses_request`).
533
+ if (model.useResponsesLite) {
534
+ applyCodexResponsesLiteShape(request);
535
+ headers[OPENAI_HEADERS.RESPONSES_LITE] = "true";
536
+ }
496
537
  }
497
538
 
498
539
  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];