@oh-my-pi/pi-agent-core 16.2.2 → 16.2.4

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,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.2.4] - 2026-06-28
6
+
7
+ ### Changed
8
+
9
+ - Improved the reliability of remote compaction by introducing transient error retries, configurable timeouts, and immediate termination upon user-initiated aborts.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed an issue where assistant responses and encrypted reasoning could be lost during local history trimming prior to remote compaction.
14
+ - Fixed type compatibility for hosts with title audit entries by adding support for `title_change` session metadata.
15
+ - Fixed an issue where transient stream read failures after a completed tool call were treated as terminal errors, allowing the agent to successfully execute the tool and continue the turn.
16
+
17
+ ## [16.2.3] - 2026-06-28
18
+
19
+ ### Changed
20
+
21
+ - Enabled V2 streaming remote compaction by default for compatible AI and OpenAI-compatible models, which forwards full conversation history to the provider and supports session routing, prompt caching, provider-native tool history replay, transient error retries, and configurable timeouts.
22
+
23
+ ### Fixed
24
+
25
+ - Fixed an issue where assistant responses and encrypted reasoning could be lost during local history trimming.
26
+ - Added `title_change` session metadata to the compaction entry type union to maintain type compatibility for hosts with title audit entries.
27
+
5
28
  ## [16.2.2] - 2026-06-27
6
29
 
7
30
  ### Added
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Remote Compaction V2: streaming Responses compaction.
3
+ *
4
+ * Mirrors Codex `core/src/compact_remote_v2.rs`: append a `compaction_trigger`
5
+ * input item to the normal Responses stream, require exactly one streamed
6
+ * compaction output item, then install retained real user messages plus that
7
+ * compaction item as replacement history.
8
+ */
9
+ import type { FetchImpl, Model } from "@oh-my-pi/pi-ai";
10
+ /** Retained-message budget Codex uses after streamed V2 compaction. */
11
+ export declare const V2_RETAINED_MESSAGE_TOKEN_BUDGET = 64000;
12
+ /** Max retries for V2 streaming compaction on transient stream errors. */
13
+ export declare const V2_COMPACTION_MAX_RETRIES = 2;
14
+ /** Timeout for V2 streaming compaction (3 minutes, same as V1). */
15
+ export declare const V2_COMPACTION_TIMEOUT_MS = 180000;
16
+ /** Token usage reported by the streamed V2 Responses completion. */
17
+ export interface CompactionV2Usage {
18
+ inputTokens: number;
19
+ outputTokens: number;
20
+ totalTokens: number;
21
+ cachedInputTokens?: number;
22
+ reasoningOutputTokens?: number;
23
+ }
24
+ /** Request body fields needed for Responses-stream V2 compaction. */
25
+ export interface CompactionV2Request {
26
+ model: string;
27
+ input: unknown[];
28
+ instructions: string;
29
+ retainedMessageBudget: number;
30
+ tools?: unknown[];
31
+ /** Responses reasoning param (effort + summary), matching a normal turn; omitted for non-reasoning models. */
32
+ reasoning?: {
33
+ effort: string;
34
+ summary: string;
35
+ };
36
+ sessionId?: string;
37
+ promptCacheKey?: string;
38
+ }
39
+ /** Response collected from the V2 stream and converted into replacement history. */
40
+ export interface CompactionV2Response {
41
+ compactionItem: Record<string, unknown>;
42
+ replacementHistory: Array<Record<string, unknown>>;
43
+ usedTokens: number;
44
+ usage?: CompactionV2Usage;
45
+ retainedImageCount: number;
46
+ }
47
+ /** Resolve the streaming Responses endpoint for a V2-capable model. */
48
+ export declare function getCompactionV2Endpoint(model: Model): string | undefined;
49
+ /** Check whether a model can use streaming V2 compaction. */
50
+ export declare function shouldUseCompactionV2Streaming(model: Model): model is Model<"openai-responses" | "azure-openai-responses" | "openai-codex-responses">;
51
+ /** Clamp the retained-message budget to Codex's known-safe 64K ceiling. */
52
+ export declare function resolveCompactionV2RetainedMessageBudget(value: number | undefined): number;
53
+ /** Build a V2 streaming compaction request from Responses-native history. */
54
+ export declare function buildCompactionV2Request(model: Model, input: unknown[], instructions: string, options?: {
55
+ tools?: unknown[];
56
+ reasoning?: {
57
+ effort: string;
58
+ summary: string;
59
+ };
60
+ sessionId?: string;
61
+ promptCacheKey?: string;
62
+ retainedMessageBudget?: number;
63
+ }): CompactionV2Request;
64
+ /** Request V2 compaction over the normal OpenAI Responses streaming endpoint. */
65
+ export declare function requestCompactionV2Streaming(model: Model, apiKey: string, request: CompactionV2Request, signal?: AbortSignal, options?: {
66
+ fetch?: FetchImpl;
67
+ timeoutMs?: number;
68
+ retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
69
+ }): Promise<CompactionV2Response>;
70
+ /** Build Codex-style V2 replacement history from prompt input plus compaction output. */
71
+ export declare function buildCompactionV2ReplacementHistory(input: unknown[], compactionItem: Record<string, unknown>, retainedMessageBudget?: number): {
72
+ replacementHistory: Array<Record<string, unknown>>;
73
+ retainedImageCount: number;
74
+ };
75
+ /** Store V2 replacement history in the OpenAI remote-compaction preserve slot. */
76
+ export declare function storeCompactionV2PreserveData(response: CompactionV2Response, model: Model): Record<string, unknown>;
77
+ /** Retrieve preserved OpenAI replacement history that V2 can extend. */
78
+ export declare function getCompactionV2PreserveData(preserveData: Record<string, unknown> | undefined): {
79
+ provider: string;
80
+ replacementHistory: Array<Record<string, unknown>>;
81
+ usedTokens: number;
82
+ } | undefined;
@@ -39,6 +39,8 @@ export interface CompactionSettings {
39
39
  autoContinue?: boolean;
40
40
  remoteEnabled?: boolean;
41
41
  remoteEndpoint?: string;
42
+ remoteStreamingV2Enabled?: boolean;
43
+ v2RetainedMessageBudget?: number;
42
44
  }
43
45
  export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
44
46
  /**
@@ -151,6 +153,12 @@ export interface SummaryOptions {
151
153
  * `resolveCompactionEffort` for the conversion contract.
152
154
  */
153
155
  thinkingLevel?: ThinkingLevel;
156
+ /** Session routing key for remote compaction transports with sticky provider sessions. */
157
+ sessionId?: string;
158
+ /** Prompt-cache key for remote compaction transports that support provider prefix caching. */
159
+ promptCacheKey?: string;
160
+ /** Provider-visible tools for remote compaction transports that replay native tool history. */
161
+ tools?: Tool[];
154
162
  /** Optional fetch implementation threaded into remote compaction calls. */
155
163
  fetch?: FetchImpl;
156
164
  }
@@ -229,7 +237,7 @@ export interface CompactionPreparation {
229
237
  /** Compaction settions from settings.jsonl */
230
238
  settings: CompactionSettings;
231
239
  }
232
- export declare function prepareCompaction(pathEntries: SessionEntry[], settings: CompactionSettings): CompactionPreparation | undefined;
240
+ export declare function prepareCompaction(pathEntries: SessionEntry[], settings: CompactionSettings, compactionModels?: readonly Model[]): CompactionPreparation | undefined;
233
241
  /**
234
242
  * Generate summaries for compaction using prepared data.
235
243
  * Returns CompactionResult - SessionManager adds id/parentId when saving.
@@ -66,6 +66,13 @@ export interface LabelEntry extends SessionEntryBase {
66
66
  targetId: string;
67
67
  label: string | undefined;
68
68
  }
69
+ export interface TitleChangeEntry extends SessionEntryBase {
70
+ type: "title_change";
71
+ title: string;
72
+ previousTitle?: string;
73
+ source: "auto" | "user";
74
+ trigger?: string;
75
+ }
69
76
  export interface TtsrInjectionEntry extends SessionEntryBase {
70
77
  type: "ttsr_injection";
71
78
  /** Names of rules that were injected */
@@ -96,7 +103,7 @@ export interface ModeChangeEntry extends SessionEntryBase {
96
103
  }
97
104
  export interface CustomCompactionSessionEntries {
98
105
  }
99
- export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
106
+ export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TitleChangeEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
100
107
  export interface ReadonlySessionManager {
101
108
  getBranch(leafId?: string | null): SessionEntry[];
102
109
  getEntry(id: string): SessionEntry | undefined;
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Remote compaction utilities.
3
3
  *
4
- * Provider-side conversation summarization endpoints. Two flavors:
4
+ * Provider-side conversation summarization endpoints. Three flavors:
5
5
  *
6
- * - **OpenAI remote compaction** (`/responses/compact`): preserves encrypted
6
+ * - **OpenAI remote compaction V2** (Responses streaming): appends a
7
+ * `compaction_trigger` input item to the normal stream and stores the returned
8
+ * `compaction` item with retained real user messages in `preserveData`.
9
+ * - **OpenAI remote compaction V1** (`/responses/compact`): preserves encrypted
7
10
  * reasoning across compactions by submitting the full responses-API native
8
11
  * history and storing the returned `compaction` / `compaction_summary`
9
12
  * item in `preserveData` so future turns can replay the encrypted state.
@@ -12,6 +15,7 @@
12
15
  * with `{ summary, shortSummary? }`.
13
16
  */
14
17
  import type { FetchImpl, Message, Model } from "@oh-my-pi/pi-ai/types";
18
+ export * from "./compaction-v2-streaming";
15
19
  export declare const OPENAI_REMOTE_COMPACTION_PRESERVE_KEY = "openaiRemoteCompaction";
16
20
  /**
17
21
  * Hard ceiling on remote compaction HTTP requests. Unlike every provider
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.2.2",
4
+ "version": "16.2.4",
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.2.2",
39
- "@oh-my-pi/pi-catalog": "16.2.2",
40
- "@oh-my-pi/pi-natives": "16.2.2",
41
- "@oh-my-pi/pi-utils": "16.2.2",
42
- "@oh-my-pi/pi-wire": "16.2.2",
43
- "@oh-my-pi/snapcompact": "16.2.2",
38
+ "@oh-my-pi/pi-ai": "16.2.4",
39
+ "@oh-my-pi/pi-catalog": "16.2.4",
40
+ "@oh-my-pi/pi-natives": "16.2.4",
41
+ "@oh-my-pi/pi-utils": "16.2.4",
42
+ "@oh-my-pi/pi-wire": "16.2.4",
43
+ "@oh-my-pi/snapcompact": "16.2.4",
44
44
  "@opentelemetry/api": "^1.9.1"
45
45
  },
46
46
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -1355,7 +1355,10 @@ async function streamAssistantResponse(
1355
1355
 
1356
1356
  const event = next.value;
1357
1357
  if (event.type === "done" || event.type === "error") {
1358
- let finalMessage = retainCompletedToolCalls(await response.result(), completedToolCallIds);
1358
+ let finalMessage = recoverTransientErrorToolTurn(
1359
+ retainCompletedToolCalls(await response.result(), completedToolCallIds),
1360
+ context.tools ?? [],
1361
+ );
1359
1362
  if (harmonyMitigationEnabled) {
1360
1363
  const detection = detectHarmonyLeakInAssistantMessage(finalMessage);
1361
1364
  if (detection) {
@@ -1515,8 +1518,40 @@ function retainCompletedToolCalls(
1515
1518
  : {
1516
1519
  type: STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL,
1517
1520
  category: message.stopDetails?.type ?? null,
1518
- explanation: message.stopDetails?.explanation ?? null,
1521
+ explanation: message.stopDetails?.explanation ?? message.errorMessage ?? null,
1522
+ },
1523
+ };
1524
+ }
1525
+
1526
+ function recoverTransientErrorToolTurn(
1527
+ message: AssistantMessage,
1528
+ availableTools: ReadonlyArray<Pick<AgentTool, "name" | "customWireName">>,
1529
+ ): AssistantMessage {
1530
+ if (message.stopReason !== "error") return message;
1531
+ const toolCalls = message.content.filter(block => block.type === "toolCall");
1532
+ if (toolCalls.length === 0) return message;
1533
+ const availableToolNames = new Set<string>();
1534
+ for (const tool of availableTools) {
1535
+ availableToolNames.add(tool.name);
1536
+ if (tool.customWireName !== undefined) availableToolNames.add(tool.customWireName);
1537
+ }
1538
+ if (!toolCalls.every(toolCall => availableToolNames.has(toolCall.name))) return message;
1539
+ if (!AIError.isStreamReadErrorText(`${message.errorMessage ?? ""}\n${message.stopDetails?.explanation ?? ""}`))
1540
+ return message;
1541
+ return {
1542
+ ...message,
1543
+ stopReason: "toolUse",
1544
+ stopDetails:
1545
+ message.stopDetails?.type === STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL
1546
+ ? message.stopDetails
1547
+ : {
1548
+ type: STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL,
1549
+ category: message.stopDetails?.type ?? null,
1550
+ explanation: message.stopDetails?.explanation ?? message.errorMessage ?? null,
1519
1551
  },
1552
+ errorMessage: undefined,
1553
+ errorId: undefined,
1554
+ errorStatus: undefined,
1520
1555
  };
1521
1556
  }
1522
1557