@oh-my-pi/pi-agent-core 18.12.3 → 18.12.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
+ ## [18.12.4] - 2026-10-20
6
+
7
+ ### Fixed
8
+
9
+ - Enabled native compaction for supported Bedrock Claude models ([#16343](https://github.com/can1357/oh-my-pi/pull/16343) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy))
10
+
5
11
  ## [18.12.3] - 2026-10-19
6
12
 
7
13
  ### Added
@@ -14,6 +14,8 @@ export declare const ANTHROPIC_COMPACTION_PRESERVE_KEY = "anthropicCompaction";
14
14
  export interface AnthropicCompactionPreserveData {
15
15
  provider: string;
16
16
  content: string;
17
+ /** Opaque signed tool-change records; replayed without normalization. */
18
+ toolChanges?: Record<string, unknown>[];
17
19
  /** Signature attached to an on-demand block; replayed verbatim. */
18
20
  signature?: string;
19
21
  /** Legacy threshold block state; replay-only. */
@@ -35,9 +37,11 @@ export interface AnthropicCompactionPreserveData {
35
37
  * lineage the beta documents); endpoint eligibility is resolved the way the
36
38
  * provider routes requests, so a Foundry or `ANTHROPIC_BASE_URL` reroute of a
37
39
  * first-party model is excluded unless the route opted in with
38
- * `remoteCompaction.enabled`.
40
+ * `remoteCompaction.enabled`. Bedrock Converse models compact through a
41
+ * Bedrock Runtime side request; their ordinary turns stay on Converse.
39
42
  */
40
- export declare function shouldUseAnthropicNativeCompaction(model: Model): model is Model<"anthropic-messages">;
43
+ export declare function shouldUseAnthropicNativeCompaction(model: Model): model is Model<"anthropic-messages" | "bedrock-converse-stream">;
44
+ type AnthropicNativeCompactionModel = Model<"anthropic-messages" | "bedrock-converse-stream">;
41
45
  export declare function getPreservedAnthropicCompactionData(preserveData: Record<string, unknown> | undefined): AnthropicCompactionPreserveData | undefined;
42
46
  /** Set or strip the Anthropic compaction slot; a new compaction never inherits a stale summary. */
43
47
  export declare function withAnthropicCompactionPreserveData(preserveData: Record<string, unknown> | undefined, compaction: AnthropicCompactionPreserveData | undefined): Record<string, unknown> | undefined;
@@ -67,6 +71,7 @@ export interface AnthropicNativeCompactionRequest {
67
71
  export interface AnthropicNativeCompactionResponse {
68
72
  content: string;
69
73
  signature: string;
74
+ toolChanges?: Record<string, unknown>[];
70
75
  usage: Usage;
71
76
  model: string;
72
77
  }
@@ -81,4 +86,5 @@ export interface AnthropicNativeCompactionOptions extends Pick<SimpleStreamOptio
81
86
  * without a summary is a native failure, including tool use, refusals and
82
87
  * output limits; the configured method order can then choose a fallback.
83
88
  */
84
- export declare function requestAnthropicNativeCompaction(model: Model<"anthropic-messages">, apiKey: ApiKey, request: AnthropicNativeCompactionRequest, signal: AbortSignal | undefined, options: AnthropicNativeCompactionOptions): Promise<AnthropicNativeCompactionResponse>;
89
+ export declare function requestAnthropicNativeCompaction(model: AnthropicNativeCompactionModel, apiKey: ApiKey, request: AnthropicNativeCompactionRequest, signal: AbortSignal | undefined, options: AnthropicNativeCompactionOptions): Promise<AnthropicNativeCompactionResponse>;
90
+ export {};
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": "18.12.3",
4
+ "version": "18.12.5",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -38,16 +38,16 @@
38
38
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
39
39
  },
40
40
  "dependencies": {
41
- "@oh-my-pi/pi-ai": "18.12.3",
42
- "@oh-my-pi/pi-catalog": "18.12.3",
43
- "@oh-my-pi/pi-natives": "18.12.3",
44
- "@oh-my-pi/pi-utils": "18.12.3",
45
- "@oh-my-pi/pi-wire": "18.12.3",
46
- "@oh-my-pi/snapcompact": "18.12.3",
41
+ "@oh-my-pi/pi-ai": "18.12.5",
42
+ "@oh-my-pi/pi-catalog": "18.12.5",
43
+ "@oh-my-pi/pi-natives": "18.12.5",
44
+ "@oh-my-pi/pi-utils": "18.12.5",
45
+ "@oh-my-pi/pi-wire": "18.12.5",
46
+ "@oh-my-pi/snapcompact": "18.12.5",
47
47
  "@opentelemetry/api": "^1.9.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@oh-my-pi/omptype": "18.12.3",
50
+ "@oh-my-pi/omptype": "18.12.5",
51
51
  "@opentelemetry/context-async-hooks": "^2.9.0",
52
52
  "@opentelemetry/sdk-trace-base": "^2.9.0",
53
53
  "@types/bun": "^1.3.14"
@@ -19,7 +19,10 @@ import type {
19
19
  } from "@oh-my-pi/pi-ai";
20
20
  import * as AIError from "@oh-my-pi/pi-ai/error";
21
21
  import { resolveAnthropicCompactionEffort } from "@oh-my-pi/pi-ai/providers/anthropic";
22
- import { supportsAnthropicCompaction } from "@oh-my-pi/pi-ai/providers/anthropic-compaction";
22
+ import {
23
+ supportsAnthropicCompaction,
24
+ supportsBedrockConverseCompaction,
25
+ } from "@oh-my-pi/pi-ai/providers/anthropic-compaction";
23
26
  import { isRecord, prompt } from "@oh-my-pi/pi-utils";
24
27
  import { type InstrumentedChatSpanOptions, instrumentedCompleteSimple } from "../telemetry";
25
28
  import type { AgentMessage } from "../types";
@@ -31,6 +34,8 @@ export const ANTHROPIC_COMPACTION_PRESERVE_KEY = "anthropicCompaction";
31
34
  export interface AnthropicCompactionPreserveData {
32
35
  provider: string;
33
36
  content: string;
37
+ /** Opaque signed tool-change records; replayed without normalization. */
38
+ toolChanges?: Record<string, unknown>[];
34
39
  /** Signature attached to an on-demand block; replayed verbatim. */
35
40
  signature?: string;
36
41
  /** Legacy threshold block state; replay-only. */
@@ -51,18 +56,28 @@ function isAnthropicMessagesModel(model: Model): model is Model<"anthropic-messa
51
56
  return model.api === "anthropic-messages";
52
57
  }
53
58
 
59
+ function isBedrockConverseModel(model: Model): model is Model<"bedrock-converse-stream"> {
60
+ return model.api === "bedrock-converse-stream";
61
+ }
62
+
54
63
  /**
55
64
  * Whether a model compacts through the Anthropic compaction beta. Model
56
65
  * eligibility is catalog policy (`compat.supportsServerCompaction`, the
57
66
  * lineage the beta documents); endpoint eligibility is resolved the way the
58
67
  * provider routes requests, so a Foundry or `ANTHROPIC_BASE_URL` reroute of a
59
68
  * first-party model is excluded unless the route opted in with
60
- * `remoteCompaction.enabled`.
69
+ * `remoteCompaction.enabled`. Bedrock Converse models compact through a
70
+ * Bedrock Runtime side request; their ordinary turns stay on Converse.
61
71
  */
62
- export function shouldUseAnthropicNativeCompaction(model: Model): model is Model<"anthropic-messages"> {
63
- return isAnthropicMessagesModel(model) && supportsAnthropicCompaction(model);
72
+ export function shouldUseAnthropicNativeCompaction(
73
+ model: Model,
74
+ ): model is Model<"anthropic-messages" | "bedrock-converse-stream"> {
75
+ if (isAnthropicMessagesModel(model)) return supportsAnthropicCompaction(model);
76
+ return isBedrockConverseModel(model) && supportsBedrockConverseCompaction(model);
64
77
  }
65
78
 
79
+ type AnthropicNativeCompactionModel = Model<"anthropic-messages" | "bedrock-converse-stream">;
80
+
66
81
  export function getPreservedAnthropicCompactionData(
67
82
  preserveData: Record<string, unknown> | undefined,
68
83
  ): AnthropicCompactionPreserveData | undefined {
@@ -70,6 +85,9 @@ export function getPreservedAnthropicCompactionData(
70
85
  if (!isRecord(candidate)) return undefined;
71
86
  if (typeof candidate.provider !== "string" || candidate.provider.length === 0) return undefined;
72
87
  if (typeof candidate.content !== "string" || candidate.content.length === 0) return undefined;
88
+ const toolChanges = candidate.toolChanges;
89
+ // The signature covers `tool_changes`: a block missing or mangling them cannot replay.
90
+ if (toolChanges !== undefined && (!Array.isArray(toolChanges) || !toolChanges.every(isRecord))) return undefined;
73
91
  return {
74
92
  provider: candidate.provider,
75
93
  content: candidate.content,
@@ -90,6 +108,7 @@ export function getPreservedAnthropicCompactionData(
90
108
  ),
91
109
  }
92
110
  : {}),
111
+ ...(toolChanges !== undefined ? { toolChanges } : {}),
93
112
  ...(candidate.exactTail === true ? { exactTail: true as const } : {}),
94
113
  ...(typeof candidate.model === "string" ? { model: candidate.model } : {}),
95
114
  ...(typeof candidate.usedTokens === "number" ? { usedTokens: candidate.usedTokens } : {}),
@@ -124,6 +143,7 @@ export function getAnthropicCompactionPayload(
124
143
  ...(preserved.signature ? { signature: preserved.signature } : {}),
125
144
  ...(preserved.encryptedContent ? { encryptedContent: preserved.encryptedContent } : {}),
126
145
  ...(preserved.filesText ? { filesText: preserved.filesText } : {}),
146
+ ...(preserved.toolChanges ? { toolChanges: preserved.toolChanges } : {}),
127
147
  ...(preserved.retainedFiles?.length ? { retainedFiles: preserved.retainedFiles } : {}),
128
148
  ...(preserved.exactTail ? { exactTail: true as const } : {}),
129
149
  };
@@ -196,6 +216,7 @@ export interface AnthropicNativeCompactionRequest {
196
216
  export interface AnthropicNativeCompactionResponse {
197
217
  content: string;
198
218
  signature: string;
219
+ toolChanges?: Record<string, unknown>[];
199
220
  usage: Usage;
200
221
  model: string;
201
222
  }
@@ -224,7 +245,7 @@ export interface AnthropicNativeCompactionOptions
224
245
  * output limits; the configured method order can then choose a fallback.
225
246
  */
226
247
  export async function requestAnthropicNativeCompaction(
227
- model: Model<"anthropic-messages">,
248
+ model: AnthropicNativeCompactionModel,
228
249
  apiKey: ApiKey,
229
250
  request: AnthropicNativeCompactionRequest,
230
251
  signal: AbortSignal | undefined,
@@ -237,7 +258,9 @@ export async function requestAnthropicNativeCompaction(
237
258
  apiKey,
238
259
  signal,
239
260
  maxTokens: request.maxTokens,
240
- reasoning: resolveAnthropicCompactionEffort(model, request.context.messages, request.reasoning),
261
+ reasoning: isAnthropicMessagesModel(model)
262
+ ? resolveAnthropicCompactionEffort(model, request.context.messages, request.reasoning)
263
+ : request.reasoning,
241
264
  initiatorOverride: options.initiatorOverride,
242
265
  metadata: options.metadata,
243
266
  fetch: options.fetch,
@@ -279,6 +302,7 @@ export async function requestAnthropicNativeCompaction(
279
302
  return {
280
303
  content: payload.content,
281
304
  signature: payload.signature,
305
+ ...(payload.toolChanges ? { toolChanges: payload.toolChanges } : {}),
282
306
  usage: response.usage,
283
307
  model: response.model,
284
308
  };
@@ -2024,6 +2024,7 @@ export async function compact(
2024
2024
  let nativeFirstKeptEntryId = firstKeptEntryId;
2025
2025
  let nativeUsedTokens: number | undefined;
2026
2026
  let nativeRetainedFiles: AnthropicCompactionFiles[] | undefined;
2027
+ let nativeToolChanges: Record<string, unknown>[] | undefined;
2027
2028
  if (!usedRemoteCompaction && settings.remoteEnabled !== false && shouldUseAnthropicNativeCompaction(model)) {
2028
2029
  const previousNative = getPreservedAnthropicCompactionData(previousPreserveData);
2029
2030
  // Lead with the previous summary as the live context renders it:
@@ -2052,6 +2053,7 @@ export async function compact(
2052
2053
  : {}),
2053
2054
  ...(previousNative.filesText ? { filesText: previousNative.filesText } : {}),
2054
2055
  ...(previousNative.retainedFiles ? { retainedFiles: previousNative.retainedFiles } : {}),
2056
+ ...(previousNative.toolChanges ? { toolChanges: previousNative.toolChanges } : {}),
2055
2057
  ...(previousNative.exactTail ? { exactTail: true as const } : {}),
2056
2058
  }
2057
2059
  : undefined,
@@ -2148,6 +2150,7 @@ export async function compact(
2148
2150
  );
2149
2151
  nativeSummary = remote.content;
2150
2152
  nativeSignature = remote.signature;
2153
+ nativeToolChanges = remote.toolChanges;
2151
2154
  nativeUsedTokens = calculatePromptTokens(remote.usage);
2152
2155
  usedRemoteCompaction = true;
2153
2156
  } catch (err) {
@@ -2235,16 +2238,18 @@ export async function compact(
2235
2238
  const { readFiles, modifiedFiles } = computeFileLists(fileOps);
2236
2239
  summary = upsertFileOperations(summary, readFiles, modifiedFiles, fileOps.read);
2237
2240
  if (nativeSummary !== undefined) {
2238
- // The replayed block stays byte-identical to the API's summary so it
2239
- // matches its signature. The harness file lists above travel
2240
- // separately: the converter replaces the summary message with the
2241
- // block and skips its text, so they would otherwise be invisible to
2242
- // this provider. Every other provider keeps reading the entry text.
2241
+ // The replayed block (summary and any `tool_changes`) stays
2242
+ // byte-identical to the API's output so it matches its signature. The
2243
+ // harness file lists above travel separately: the converter replaces the
2244
+ // summary message with the block and skips its text, so they would
2245
+ // otherwise be invisible to this provider. Every other provider keeps
2246
+ // reading the entry text.
2243
2247
  const filesText = upsertFileOperations("", readFiles, modifiedFiles, fileOps.read) || undefined;
2244
2248
  preserveData = withAnthropicCompactionPreserveData(preserveData, {
2245
2249
  provider: model.provider,
2246
2250
  content: nativeSummary,
2247
2251
  ...(nativeSignature ? { signature: nativeSignature } : {}),
2252
+ ...(nativeToolChanges ? { toolChanges: nativeToolChanges } : {}),
2248
2253
  ...(filesText ? { filesText } : {}),
2249
2254
  ...(nativeRetainedFiles ? { retainedFiles: nativeRetainedFiles } : {}),
2250
2255
  exactTail: true,