@oh-my-pi/pi-agent-core 16.0.0 → 16.0.2

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,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.0.1] - 2026-06-15
6
+
7
+ ### Fixed
8
+
9
+ - Fixed transient provider errors after streamed tool-call arguments so incomplete tool calls are marked as interrupted output instead of eligible for automatic retry ([#2683](https://github.com/can1357/oh-my-pi/issues/2683)).
10
+ - Fixed `@oh-my-pi/pi-agent-core` telemetry content capture crashing every chat turn with `TypeError: systemPrompt.map is not a function` when `captureMessageContent` is enabled (`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). `ChatRequestSnapshot.systemPrompt` now accepts `string | readonly string[]` and the telemetry serializers normalize a bare string to a single-element array — previously the full-system serializer called `.map` on a string (the `.length` guard passed, so it threw) and the request-message serializer iterated the string into one `system` message per character.
11
+
5
12
  ## [16.0.0] - 2026-06-15
6
13
 
7
14
  ### Breaking Changes
@@ -6,6 +6,8 @@ import { type Context, EventStream } from "@oh-my-pi/pi-ai";
6
6
  import { type Dialect } from "@oh-my-pi/pi-ai/dialect";
7
7
  import { type AgentRunCoverage, type AgentRunSummary } from "./run-collector";
8
8
  import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types";
9
+ /** Stop-details marker for a provider error after assistant content/tool args already streamed. */
10
+ export declare const STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL = "stream_interrupted_after_content";
9
11
  /**
10
12
  * Start an agent loop with a new prompt message.
11
13
  * The prompt is added to the context and events are emitted for it.
@@ -369,7 +369,7 @@ export interface ChatRequestSnapshot {
369
369
  readonly tools?: readonly {
370
370
  readonly name: string;
371
371
  }[];
372
- readonly systemPrompt?: readonly string[];
372
+ readonly systemPrompt?: string | readonly string[];
373
373
  readonly messages?: readonly Message[];
374
374
  }
375
375
  /**
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.0.0",
4
+ "version": "16.0.2",
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,11 +35,11 @@
35
35
  "fmt": "biome format --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/pi-ai": "16.0.0",
39
- "@oh-my-pi/pi-catalog": "16.0.0",
40
- "@oh-my-pi/pi-natives": "16.0.0",
41
- "@oh-my-pi/pi-utils": "16.0.0",
42
- "@oh-my-pi/snapcompact": "16.0.0",
38
+ "@oh-my-pi/pi-ai": "16.0.2",
39
+ "@oh-my-pi/pi-catalog": "16.0.2",
40
+ "@oh-my-pi/pi-natives": "16.0.2",
41
+ "@oh-my-pi/pi-utils": "16.0.2",
42
+ "@oh-my-pi/snapcompact": "16.0.2",
43
43
  "@opentelemetry/api": "^1.9.1"
44
44
  },
45
45
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -63,6 +63,9 @@ import type {
63
63
  } from "./types";
64
64
  import { yieldIfDue } from "./utils/yield";
65
65
 
66
+ /** Stop-details marker for a provider error after assistant content/tool args already streamed. */
67
+ export const STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL = "stream_interrupted_after_content";
68
+
66
69
  /** Sentinel returned by the abort race in `streamAssistantResponse`. */
67
70
  const ABORTED: unique symbol = Symbol("agent-loop-aborted");
68
71
 
@@ -1355,14 +1358,26 @@ function retainCompletedToolCalls(
1355
1358
  completedToolCallIds: ReadonlySet<string>,
1356
1359
  ): AssistantMessage {
1357
1360
  if (message.stopReason !== "error" && message.stopReason !== "aborted") return message;
1358
- let changed = false;
1361
+ let droppedIncompleteToolCall = false;
1359
1362
  const content = message.content.filter(block => {
1360
1363
  if (block.type !== "toolCall") return true;
1361
1364
  const keep = completedToolCallIds.has(block.id);
1362
- if (!keep) changed = true;
1365
+ if (!keep) droppedIncompleteToolCall = true;
1363
1366
  return keep;
1364
1367
  });
1365
- return changed ? { ...message, content } : message;
1368
+ if (!droppedIncompleteToolCall) return message;
1369
+ return {
1370
+ ...message,
1371
+ content,
1372
+ stopDetails:
1373
+ message.stopDetails?.type === STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL
1374
+ ? message.stopDetails
1375
+ : {
1376
+ type: STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL,
1377
+ category: message.stopDetails?.type ?? null,
1378
+ explanation: message.stopDetails?.explanation ?? null,
1379
+ },
1380
+ };
1366
1381
  }
1367
1382
 
1368
1383
  function emitDiscardedHarmonyPartial(
package/src/telemetry.ts CHANGED
@@ -731,7 +731,7 @@ export interface ChatRequestSnapshot {
731
731
  readonly reasoningEffort?: string;
732
732
  readonly toolChoice?: ToolChoice;
733
733
  readonly tools?: readonly { readonly name: string }[];
734
- readonly systemPrompt?: readonly string[];
734
+ readonly systemPrompt?: string | readonly string[];
735
735
  readonly messages?: readonly Message[];
736
736
  }
737
737
 
@@ -796,6 +796,11 @@ function applyContentCaptureForResponse(telemetry: AgentTelemetry, span: Span, m
796
796
  }
797
797
  }
798
798
 
799
+ function normalizeSystemPromptParts(systemPrompt: string | readonly string[] | undefined): readonly string[] {
800
+ if (!systemPrompt) return [];
801
+ return typeof systemPrompt === "string" ? [systemPrompt] : systemPrompt;
802
+ }
803
+
799
804
  function serializeRequestMessagesForTelemetry(
800
805
  telemetry: AgentTelemetry,
801
806
  request: ChatRequestSnapshot,
@@ -803,10 +808,8 @@ function serializeRequestMessagesForTelemetry(
803
808
  const serializer = telemetry.config.contentSerializer?.requestMessages;
804
809
  if (serializer) return callContentSerializer(telemetry, "requestMessages", () => serializer(request));
805
810
  const messages: TelemetryMessageSummary[] = [];
806
- if (request.systemPrompt) {
807
- for (const text of request.systemPrompt)
808
- messages.push({ role: "system", content: summarizeTelemetryValue(text) });
809
- }
811
+ for (const text of normalizeSystemPromptParts(request.systemPrompt))
812
+ messages.push({ role: "system", content: summarizeTelemetryValue(text) });
810
813
  if (request.messages) {
811
814
  for (const message of request.messages) {
812
815
  messages.push({ role: message.role, content: summarizeTelemetryValue(message.content) });
@@ -874,8 +877,8 @@ interface OtelOutputMessage extends OtelInputMessage {
874
877
  }
875
878
 
876
879
  function serializeFullSystemInstructionsForTelemetry(request: ChatRequestSnapshot): string | undefined {
877
- const systemPrompt = request.systemPrompt;
878
- if (!systemPrompt || systemPrompt.length === 0) return undefined;
880
+ const systemPrompt = normalizeSystemPromptParts(request.systemPrompt);
881
+ if (systemPrompt.length === 0) return undefined;
879
882
  return stringifyJsonAttribute(systemPrompt.map(text => ({ type: "text", content: text }) satisfies OtelMessagePart));
880
883
  }
881
884