@oh-my-pi/pi-agent-core 16.2.1 → 16.2.3

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,27 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.2.3] - 2026-06-28
6
+
7
+ ### Changed
8
+
9
+ - 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.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed an issue where assistant responses and encrypted reasoning could be lost during local history trimming.
14
+ - Added `title_change` session metadata to the compaction entry type union to maintain type compatibility for hosts with title audit entries.
15
+
16
+ ## [16.2.2] - 2026-06-27
17
+
18
+ ### Added
19
+
20
+ - Added optional AgentTool.matcherPaths(args) and AgentTool.matcherEntries(args) hooks to allow tools to surface target file paths and isolate file evaluations for path-scoped stream matchers (e.g., when handling multi-file payloads or embedded paths in streamed arguments).
21
+
22
+ ### Removed
23
+
24
+ - Removed support for Pi dialect integration.
25
+
5
26
  ## [16.2.0] - 2026-06-27
6
27
 
7
28
  ### 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
@@ -545,6 +545,28 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
545
545
  * matching.
546
546
  */
547
547
  matcherDigest?: (args: unknown) => string | undefined;
548
+ /**
549
+ * Surface the target file paths a (potentially partial) streamed call would
550
+ * touch, so path-scoped stream matchers (e.g. TTSR `tool:edit(*.ts)` globs)
551
+ * can match without a top-level `path`/`paths` argument. Used for tools whose
552
+ * wire grammar embeds paths inside the streamed payload (hashline section
553
+ * headers, apply_patch envelope markers). Return `undefined` (or an empty
554
+ * array) to fall back to the caller's top-level argument scan.
555
+ */
556
+ matcherPaths?: (args: unknown) => readonly string[] | undefined;
557
+ /**
558
+ * Per-file projection of a (potentially partial) streamed call, pairing each
559
+ * touched file path with the digest of only the lines added to that file.
560
+ * Path-scoped stream matchers (TTSR) evaluate each entry in isolation, so a
561
+ * scoped rule like `tool:edit(*.ts)` never fires on text that actually
562
+ * belongs to a sibling Markdown hunk in a multi-file payload. Takes
563
+ * precedence over {@link matcherDigest} + {@link matcherPaths} when present;
564
+ * returns `undefined` (or empty) to fall back to the combined hooks.
565
+ */
566
+ matcherEntries?: (args: unknown) => readonly {
567
+ path: string;
568
+ digest: string;
569
+ }[] | undefined;
548
570
  /** Capability tier declaration used by approval gates. Omitted means "exec". */
549
571
  approval?: ToolApproval;
550
572
  /** Lines appended after the standard approval prompt header. */
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.1",
4
+ "version": "16.2.3",
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.1",
39
- "@oh-my-pi/pi-catalog": "16.2.1",
40
- "@oh-my-pi/pi-natives": "16.2.1",
41
- "@oh-my-pi/pi-utils": "16.2.1",
42
- "@oh-my-pi/pi-wire": "16.2.1",
43
- "@oh-my-pi/snapcompact": "16.2.1",
38
+ "@oh-my-pi/pi-ai": "16.2.3",
39
+ "@oh-my-pi/pi-catalog": "16.2.3",
40
+ "@oh-my-pi/pi-natives": "16.2.3",
41
+ "@oh-my-pi/pi-utils": "16.2.3",
42
+ "@oh-my-pi/pi-wire": "16.2.3",
43
+ "@oh-my-pi/snapcompact": "16.2.3",
44
44
  "@opentelemetry/api": "^1.9.1"
45
45
  },
46
46
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  renderToolExamples,
26
26
  wrapInbandToolStream,
27
27
  } from "@oh-my-pi/pi-ai/dialect";
28
+ import * as AIError from "@oh-my-pi/pi-ai/error";
28
29
  import {
29
30
  createHarmonyAuditEvent,
30
31
  detectHarmonyLeakInAssistantMessage,
@@ -134,7 +135,6 @@ export function resolveOwnedDialectFromEnv(value: string | undefined): Dialect |
134
135
  case "anthropic":
135
136
  case "deepseek":
136
137
  case "harmony":
137
- case "pi":
138
138
  case "qwen3":
139
139
  case "gemini":
140
140
  case "gemma":
@@ -1557,8 +1557,12 @@ function emitAbortedAssistantMessage(
1557
1557
  requestSignal: AbortSignal | undefined,
1558
1558
  ): AssistantMessage {
1559
1559
  const errorMessage = abortReasonText(requestSignal);
1560
+ const errorId =
1561
+ errorMessage === "Request was aborted"
1562
+ ? AIError.create(AIError.Flag.Abort)
1563
+ : AIError.classify(requestSignal?.reason) || undefined;
1560
1564
  const base: AssistantMessage = partialMessage
1561
- ? { ...partialMessage, stopReason: "aborted", errorMessage }
1565
+ ? { ...partialMessage, stopReason: "aborted", errorMessage, errorId }
1562
1566
  : {
1563
1567
  role: "assistant",
1564
1568
  content: [],
@@ -1575,6 +1579,7 @@ function emitAbortedAssistantMessage(
1575
1579
  },
1576
1580
  stopReason: "aborted",
1577
1581
  errorMessage,
1582
+ errorId,
1578
1583
  timestamp: Date.now(),
1579
1584
  };
1580
1585
  // Only tool calls that reached `toolcall_end` survive abort/error replay. A