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

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,18 @@
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
+
5
17
  ## [16.2.3] - 2026-06-28
6
18
 
7
19
  ### Changed
@@ -4,7 +4,7 @@
4
4
  * When navigating to a different point in the session tree, this generates
5
5
  * a summary of the branch being left so context isn't lost.
6
6
  */
7
- import type { ApiKey, Model } from "@oh-my-pi/pi-ai";
7
+ import type { Api, ApiKey, AssistantMessage, Context, Model, SimpleStreamOptions } from "@oh-my-pi/pi-ai";
8
8
  import { type AgentTelemetry } from "../telemetry";
9
9
  import type { AgentMessage } from "../types";
10
10
  import type { ReadonlySessionManager, SessionEntry } from "./entries";
@@ -57,6 +57,13 @@ export interface GenerateBranchSummaryOptions {
57
57
  * wrapped in an OTEL chat span tagged with `pi.gen_ai.oneshot.kind = "branch_summary"`.
58
58
  */
59
59
  telemetry?: AgentTelemetry;
60
+ /**
61
+ * Optional completion transport override (same contract as
62
+ * {@link SummaryOptions.completeImpl}). Lets the host route the branch
63
+ * summary HTTP request through its provider-concurrency limiter instead
64
+ * of the default `completeSimple` transport.
65
+ */
66
+ completeImpl?: <TApi extends Api>(model: Model<TApi>, ctx: Context, options: SimpleStreamOptions) => Promise<AssistantMessage>;
60
67
  }
61
68
  /**
62
69
  * Collect entries that should be summarized when navigating from one position to another.
@@ -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 ApiKey, 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 Context, type FetchImpl, type MessageAttribution, type Model, type SimpleStreamOptions, type Tool, type Usage } from "@oh-my-pi/pi-ai";
8
8
  import { type AgentTelemetry } from "../telemetry";
9
9
  import { ThinkingLevel } from "../thinking";
10
10
  import type { AgentMessage } from "../types";
@@ -161,6 +161,15 @@ export interface SummaryOptions {
161
161
  tools?: Tool[];
162
162
  /** Optional fetch implementation threaded into remote compaction calls. */
163
163
  fetch?: FetchImpl;
164
+ /**
165
+ * Optional completion transport override for host-level request wrappers
166
+ * (e.g. the coding-agent provider-concurrency limiter). When provided,
167
+ * every local summarization oneshot (`generateSummary`,
168
+ * `generateTurnPrefixSummary`, `generateShortSummary`) routes through it
169
+ * instead of the default `completeSimple`, so cap policies enforced on
170
+ * the live agent turn also bracket compaction HTTP requests.
171
+ */
172
+ completeImpl?: <TApi extends Api>(model: Model<TApi>, ctx: Context, options: SimpleStreamOptions) => Promise<AssistantMessage>;
164
173
  }
165
174
  export declare function generateSummary(currentMessages: AgentMessage[], model: Model, reserveTokens: number, apiKey: ApiKey, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, options?: SummaryOptions): Promise<string>;
166
175
  export interface HandoffOptions {
@@ -197,6 +206,8 @@ export interface HandoffFromContextOptions {
197
206
  * anything provided here.
198
207
  */
199
208
  streamOptions: SimpleStreamOptions;
209
+ /** Optional completion transport override for host-level request wrappers. */
210
+ completeImpl?: <TApi extends Api>(model: Model<TApi>, ctx: Context, options: SimpleStreamOptions) => Promise<AssistantMessage>;
200
211
  /** See {@link HandoffOptions.telemetry}. */
201
212
  telemetry?: AgentTelemetry;
202
213
  /** See {@link HandoffOptions.thinkingLevel}. */
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.3",
4
+ "version": "16.2.5",
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.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",
38
+ "@oh-my-pi/pi-ai": "16.2.5",
39
+ "@oh-my-pi/pi-catalog": "16.2.5",
40
+ "@oh-my-pi/pi-natives": "16.2.5",
41
+ "@oh-my-pi/pi-utils": "16.2.5",
42
+ "@oh-my-pi/pi-wire": "16.2.5",
43
+ "@oh-my-pi/snapcompact": "16.2.5",
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
 
@@ -5,7 +5,7 @@
5
5
  * a summary of the branch being left so context isn't lost.
6
6
  */
7
7
 
8
- import type { ApiKey, Model } from "@oh-my-pi/pi-ai";
8
+ import type { Api, ApiKey, AssistantMessage, Context, Model, SimpleStreamOptions } from "@oh-my-pi/pi-ai";
9
9
  import { preferredDialect } from "@oh-my-pi/pi-catalog/identity";
10
10
  import { prompt } from "@oh-my-pi/pi-utils";
11
11
  import { type AgentTelemetry, instrumentedCompleteSimple } from "../telemetry";
@@ -88,6 +88,17 @@ export interface GenerateBranchSummaryOptions {
88
88
  * wrapped in an OTEL chat span tagged with `pi.gen_ai.oneshot.kind = "branch_summary"`.
89
89
  */
90
90
  telemetry?: AgentTelemetry;
91
+ /**
92
+ * Optional completion transport override (same contract as
93
+ * {@link SummaryOptions.completeImpl}). Lets the host route the branch
94
+ * summary HTTP request through its provider-concurrency limiter instead
95
+ * of the default `completeSimple` transport.
96
+ */
97
+ completeImpl?: <TApi extends Api>(
98
+ model: Model<TApi>,
99
+ ctx: Context,
100
+ options: SimpleStreamOptions,
101
+ ) => Promise<AssistantMessage>;
91
102
  }
92
103
 
93
104
  // ============================================================================
@@ -310,7 +321,7 @@ export async function generateBranchSummary(
310
321
  model,
311
322
  { systemPrompt: [SUMMARIZATION_SYSTEM_PROMPT], messages: summarizationMessages },
312
323
  { apiKey, signal, maxTokens: 2048, metadata },
313
- { telemetry: options.telemetry, oneshotKind: "branch_summary" },
324
+ { telemetry: options.telemetry, oneshotKind: "branch_summary", completeImpl: options.completeImpl },
314
325
  );
315
326
 
316
327
  // Check if aborted or errored
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import {
9
+ type Api,
9
10
  type ApiKey,
10
11
  type AssistantMessage,
11
12
  type Context,
@@ -679,6 +680,19 @@ export interface SummaryOptions {
679
680
  tools?: Tool[];
680
681
  /** Optional fetch implementation threaded into remote compaction calls. */
681
682
  fetch?: FetchImpl;
683
+ /**
684
+ * Optional completion transport override for host-level request wrappers
685
+ * (e.g. the coding-agent provider-concurrency limiter). When provided,
686
+ * every local summarization oneshot (`generateSummary`,
687
+ * `generateTurnPrefixSummary`, `generateShortSummary`) routes through it
688
+ * instead of the default `completeSimple`, so cap policies enforced on
689
+ * the live agent turn also bracket compaction HTTP requests.
690
+ */
691
+ completeImpl?: <TApi extends Api>(
692
+ model: Model<TApi>,
693
+ ctx: Context,
694
+ options: SimpleStreamOptions,
695
+ ) => Promise<AssistantMessage>;
682
696
  }
683
697
 
684
698
  function formatPreviousSnapcompactArchive(archiveText: string): string {
@@ -768,7 +782,7 @@ export async function generateSummary(
768
782
  initiatorOverride: options?.initiatorOverride,
769
783
  metadata: options?.metadata,
770
784
  },
771
- { telemetry: options?.telemetry, oneshotKind: "compaction_summary" },
785
+ { telemetry: options?.telemetry, oneshotKind: "compaction_summary", completeImpl: options?.completeImpl },
772
786
  );
773
787
 
774
788
  if (response.stopReason === "error") {
@@ -828,6 +842,12 @@ export interface HandoffFromContextOptions {
828
842
  * anything provided here.
829
843
  */
830
844
  streamOptions: SimpleStreamOptions;
845
+ /** Optional completion transport override for host-level request wrappers. */
846
+ completeImpl?: <TApi extends Api>(
847
+ model: Model<TApi>,
848
+ ctx: Context,
849
+ options: SimpleStreamOptions,
850
+ ) => Promise<AssistantMessage>;
831
851
  /** See {@link HandoffOptions.telemetry}. */
832
852
  telemetry?: AgentTelemetry;
833
853
  /** See {@link HandoffOptions.thinkingLevel}. */
@@ -859,7 +879,7 @@ export async function generateHandoffFromContext(
859
879
  reasoning: resolveCompactionEffort(model, options.thinkingLevel),
860
880
  toolChoice: "none",
861
881
  },
862
- { telemetry: options.telemetry, oneshotKind: "handoff" },
882
+ { telemetry: options.telemetry, oneshotKind: "handoff", completeImpl: options.completeImpl },
863
883
  );
864
884
 
865
885
  if (response.stopReason === "error") {
@@ -953,7 +973,7 @@ async function generateShortSummary(
953
973
  initiatorOverride: options?.initiatorOverride,
954
974
  metadata: options?.metadata,
955
975
  },
956
- { telemetry: options?.telemetry, oneshotKind: "compaction_short_summary" },
976
+ { telemetry: options?.telemetry, oneshotKind: "compaction_short_summary", completeImpl: options?.completeImpl },
957
977
  );
958
978
 
959
979
  if (response.stopReason === "error") {
@@ -1222,6 +1242,7 @@ export async function compact(
1222
1242
  promptCacheKey: options?.promptCacheKey,
1223
1243
  tools: options?.tools,
1224
1244
  fetch: options?.fetch,
1245
+ completeImpl: options?.completeImpl,
1225
1246
  };
1226
1247
 
1227
1248
  const previousSnapcompactArchive = snapcompact.getPreservedArchive(previousPreserveData);
@@ -1406,6 +1427,7 @@ export async function compact(
1406
1427
  // resolves its own reasoning via resolveCompactionEffort.
1407
1428
  thinkingLevel: options?.thinkingLevel,
1408
1429
  fetch: summaryOptions.fetch,
1430
+ completeImpl: summaryOptions.completeImpl,
1409
1431
  });
1410
1432
 
1411
1433
  // Compute file lists and append to summary
@@ -1469,7 +1491,7 @@ async function generateTurnPrefixSummary(
1469
1491
  initiatorOverride: options?.initiatorOverride,
1470
1492
  metadata: options?.metadata,
1471
1493
  },
1472
- { telemetry: options?.telemetry, oneshotKind: "compaction_turn_prefix" },
1494
+ { telemetry: options?.telemetry, oneshotKind: "compaction_turn_prefix", completeImpl: options?.completeImpl },
1473
1495
  );
1474
1496
 
1475
1497
  if (response.stopReason === "error") {