@oh-my-pi/pi-agent-core 15.9.1 → 15.9.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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.9.5] - 2026-06-05
6
+
7
+ ### Fixed
8
+
9
+ - Surfaced Anthropic stream failures whose message starts with `Output blocked by conten` as normal assistant error lifecycle events, so interactive clients render content-filter blocks instead of silently dropping the streaming bubble at `agent_end`.
10
+
5
11
  ## [15.8.3] - 2026-06-03
6
12
  ### Added
7
13
 
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": "15.9.1",
4
+ "version": "15.9.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,9 +35,9 @@
35
35
  "fmt": "biome format --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/pi-ai": "15.9.1",
39
- "@oh-my-pi/pi-natives": "15.9.1",
40
- "@oh-my-pi/pi-utils": "15.9.1",
38
+ "@oh-my-pi/pi-ai": "15.9.5",
39
+ "@oh-my-pi/pi-natives": "15.9.5",
40
+ "@oh-my-pi/pi-utils": "15.9.5",
41
41
  "@opentelemetry/api": "^1.9.1"
42
42
  },
43
43
  "devDependencies": {
package/src/agent.ts CHANGED
@@ -44,6 +44,12 @@ function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
44
44
  return messages.filter((m): m is Message => m.role === "user" || m.role === "assistant" || m.role === "toolResult");
45
45
  }
46
46
 
47
+ const ANTHROPIC_OUTPUT_BLOCKED_PREFIX = "Output blocked by conten";
48
+
49
+ function isAnthropicOutputBlockedError(message: string): boolean {
50
+ return message.includes(ANTHROPIC_OUTPUT_BLOCKED_PREFIX);
51
+ }
52
+
47
53
  function refreshToolChoiceForActiveTools(
48
54
  toolChoice: ToolChoice | undefined,
49
55
  tools: AgentContext["tools"] = [],
@@ -1046,29 +1052,50 @@ export class Agent {
1046
1052
  }
1047
1053
  }
1048
1054
  }
1049
- } catch (err: any) {
1050
- const errorMsg: AgentMessage = {
1051
- role: "assistant",
1052
- content: [{ type: "text", text: "" }],
1053
- api: model.api,
1054
- provider: model.provider,
1055
- model: model.id,
1056
- usage: {
1057
- input: 0,
1058
- output: 0,
1059
- cacheRead: 0,
1060
- cacheWrite: 0,
1061
- totalTokens: 0,
1062
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
1063
- },
1064
- stopReason: this.#abortController?.signal.aborted ? "aborted" : "error",
1065
- errorMessage: err?.message || String(err),
1066
- timestamp: Date.now(),
1067
- } as AgentMessage;
1068
-
1069
- this.appendMessage(errorMsg);
1070
- this.#state.error = err?.message || String(err);
1071
- this.#emit({ type: "agent_end", messages: [errorMsg] });
1055
+ } catch (err) {
1056
+ const errorMessage = err instanceof Error ? err.message : String(err);
1057
+ const stoppedForAbort = this.#abortController?.signal.aborted === true;
1058
+ const shouldEmitVisibleOutputBlockedError = !stoppedForAbort && isAnthropicOutputBlockedError(errorMessage);
1059
+ const assistantPartial = partial?.role === "assistant" ? partial : undefined;
1060
+ const hadAssistantStart = assistantPartial !== undefined;
1061
+ const errorMsg: AssistantMessage =
1062
+ shouldEmitVisibleOutputBlockedError && assistantPartial
1063
+ ? { ...assistantPartial, stopReason: "error", errorMessage }
1064
+ : {
1065
+ role: "assistant",
1066
+ content: [{ type: "text", text: "" }],
1067
+ api: model.api,
1068
+ provider: model.provider,
1069
+ model: model.id,
1070
+ usage: {
1071
+ input: 0,
1072
+ output: 0,
1073
+ cacheRead: 0,
1074
+ cacheWrite: 0,
1075
+ totalTokens: 0,
1076
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
1077
+ },
1078
+ stopReason: stoppedForAbort ? "aborted" : "error",
1079
+ errorMessage,
1080
+ timestamp: Date.now(),
1081
+ };
1082
+
1083
+ if (shouldEmitVisibleOutputBlockedError) {
1084
+ if (!hadAssistantStart) {
1085
+ this.#state.streamMessage = errorMsg;
1086
+ this.#emit({ type: "message_start", message: errorMsg });
1087
+ }
1088
+ this.#state.streamMessage = null;
1089
+ this.appendMessage(errorMsg);
1090
+ this.#state.error = errorMessage;
1091
+ this.#emit({ type: "message_end", message: errorMsg });
1092
+ this.#emit({ type: "turn_end", message: errorMsg, toolResults: [] });
1093
+ this.#emit({ type: "agent_end", messages: [errorMsg] });
1094
+ } else {
1095
+ this.appendMessage(errorMsg);
1096
+ this.#state.error = errorMessage;
1097
+ this.#emit({ type: "agent_end", messages: [errorMsg] });
1098
+ }
1072
1099
  } finally {
1073
1100
  this.#state.isStreaming = false;
1074
1101
  this.#state.streamMessage = null;