@oh-my-pi/pi-agent-core 16.0.5 → 16.0.6
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 +6 -0
- package/dist/types/agent.d.ts +11 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/tokenizer.d.ts +1 -0
- package/dist/types/types.d.ts +13 -0
- package/package.json +6 -6
- package/src/agent-loop.ts +24 -3
- package/src/agent.ts +16 -0
- package/src/compaction/compaction.ts +1 -1
- package/src/compaction/openai.ts +1 -1
- package/src/compaction/shake.ts +1 -1
- package/src/index.ts +2 -0
- package/src/proxy.ts +12 -0
- package/src/tokenizer.ts +17 -0
- package/src/types.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.0.6] - 2026-06-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `transformAssistantMessage` hook to `AgentOptions` and `Agent` to allow mutating the finalized assistant message before UI emission, context appending, or tool dispatch
|
|
10
|
+
|
|
5
11
|
## [16.0.5] - 2026-06-17
|
|
6
12
|
|
|
7
13
|
### Breaking Changes
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -158,6 +158,12 @@ export interface AgentOptions {
|
|
|
158
158
|
* message are emitted. See {@link AgentLoopConfig.afterToolCall} for full semantics.
|
|
159
159
|
*/
|
|
160
160
|
afterToolCall?: AgentLoopConfig["afterToolCall"];
|
|
161
|
+
/**
|
|
162
|
+
* Called once an assistant message is finalized, before it reaches the
|
|
163
|
+
* context, the UI, or tool dispatch. May mutate the message in place (text +
|
|
164
|
+
* tool-call arguments). See {@link AgentLoopConfig.transformAssistantMessage}.
|
|
165
|
+
*/
|
|
166
|
+
transformAssistantMessage?: AgentLoopConfig["transformAssistantMessage"];
|
|
161
167
|
/**
|
|
162
168
|
* Opt-in OpenTelemetry instrumentation. Passing `{}` enables the loop's
|
|
163
169
|
* GenAI-semantic-convention spans using the global tracer provider. See
|
|
@@ -187,6 +193,11 @@ export declare class Agent {
|
|
|
187
193
|
* message emission. Reassign at any time to swap the implementation.
|
|
188
194
|
*/
|
|
189
195
|
afterToolCall?: AgentLoopConfig["afterToolCall"];
|
|
196
|
+
/**
|
|
197
|
+
* Hook invoked once an assistant message is finalized, before context append,
|
|
198
|
+
* UI emission, and tool dispatch. Reassign at any time to swap the implementation.
|
|
199
|
+
*/
|
|
200
|
+
transformAssistantMessage?: AgentLoopConfig["transformAssistantMessage"];
|
|
190
201
|
constructor(opts?: AgentOptions);
|
|
191
202
|
/**
|
|
192
203
|
* Get the current session ID used for provider caching.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function countTokens(text: string | string[]): number;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -245,6 +245,19 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
245
245
|
* backlog so advice produced during the wait is injected as an aside).
|
|
246
246
|
*/
|
|
247
247
|
onTurnEnd?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<void> | void;
|
|
248
|
+
/**
|
|
249
|
+
* Called once an assistant message is finalized from the model stream, before
|
|
250
|
+
* it is appended to the context, emitted as `message_end`, or its tool calls
|
|
251
|
+
* are validated and dispatched. The hook may mutate the message in place —
|
|
252
|
+
* both its text content and its tool-call arguments — and those edits are seen
|
|
253
|
+
* by the transcript, the UI, and tool execution alike (single source of truth).
|
|
254
|
+
*
|
|
255
|
+
* Used for inline macro expansion: rewriting `@[[runtime.name(args)]]` tokens
|
|
256
|
+
* to host-computed values before anything downstream consumes the message.
|
|
257
|
+
* Runs at most once per assistant message; must not throw (a throw would abort
|
|
258
|
+
* the turn).
|
|
259
|
+
*/
|
|
260
|
+
transformAssistantMessage?: (message: AssistantMessage, signal?: AbortSignal) => Promise<void> | void;
|
|
248
261
|
/**
|
|
249
262
|
* Called after a tool finishes executing, before `tool_execution_end` and the
|
|
250
263
|
* tool-result message are emitted.
|
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.
|
|
4
|
+
"version": "16.0.6",
|
|
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.
|
|
39
|
-
"@oh-my-pi/pi-catalog": "16.0.
|
|
40
|
-
"@oh-my-pi/pi-natives": "16.0.
|
|
41
|
-
"@oh-my-pi/pi-utils": "16.0.
|
|
42
|
-
"@oh-my-pi/snapcompact": "16.0.
|
|
38
|
+
"@oh-my-pi/pi-ai": "16.0.6",
|
|
39
|
+
"@oh-my-pi/pi-catalog": "16.0.6",
|
|
40
|
+
"@oh-my-pi/pi-natives": "16.0.6",
|
|
41
|
+
"@oh-my-pi/pi-utils": "16.0.6",
|
|
42
|
+
"@oh-my-pi/snapcompact": "16.0.6",
|
|
43
43
|
"@opentelemetry/api": "^1.9.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
import {
|
|
6
6
|
type AssistantMessage,
|
|
7
7
|
type AssistantMessageEvent,
|
|
8
|
+
arkToWireSchema,
|
|
8
9
|
type Context,
|
|
9
10
|
EventStream,
|
|
10
11
|
isApiKeyResolver,
|
|
12
|
+
isArkSchema,
|
|
11
13
|
isZodSchema,
|
|
12
14
|
resolveApiKeyOnce,
|
|
13
15
|
seedApiKeyResolver,
|
|
@@ -566,6 +568,9 @@ export function normalizeTools(
|
|
|
566
568
|
if (isZodSchema(parameters)) {
|
|
567
569
|
const wired = zodToWireSchema(parameters);
|
|
568
570
|
parameters = injectIntentIntoSchema(wired, intentMode) as TSchema;
|
|
571
|
+
} else if (isArkSchema(parameters)) {
|
|
572
|
+
const wired = arkToWireSchema(parameters);
|
|
573
|
+
parameters = injectIntentIntoSchema(wired, intentMode) as TSchema;
|
|
569
574
|
} else {
|
|
570
575
|
parameters = injectIntentIntoSchema(parameters, intentMode) as TSchema;
|
|
571
576
|
}
|
|
@@ -1226,6 +1231,12 @@ async function streamAssistantResponse(
|
|
|
1226
1231
|
}
|
|
1227
1232
|
}
|
|
1228
1233
|
finalMessage = snapshotAssistantMessage(finalMessage);
|
|
1234
|
+
// Expand inline macros (and any other registered rewrite) on the
|
|
1235
|
+
// finalized message before it reaches the context, the UI, or tool
|
|
1236
|
+
// dispatch — so a single mutation is the source of truth for all three.
|
|
1237
|
+
if (config.transformAssistantMessage) {
|
|
1238
|
+
await config.transformAssistantMessage(finalMessage, requestSignal);
|
|
1239
|
+
}
|
|
1229
1240
|
if (addedPartial) {
|
|
1230
1241
|
context.messages[context.messages.length - 1] = finalMessage;
|
|
1231
1242
|
} else {
|
|
@@ -1249,9 +1260,19 @@ async function streamAssistantResponse(
|
|
|
1249
1260
|
switch (event.type) {
|
|
1250
1261
|
case "start":
|
|
1251
1262
|
partialMessage = event.partial;
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1263
|
+
if (addedPartial) {
|
|
1264
|
+
context.messages[context.messages.length - 1] = partialMessage;
|
|
1265
|
+
completedToolCallIds.clear();
|
|
1266
|
+
stream.push({
|
|
1267
|
+
type: "message_update",
|
|
1268
|
+
assistantMessageEvent: snapshotAssistantMessageEvent(event),
|
|
1269
|
+
message: snapshotAssistantMessage(partialMessage),
|
|
1270
|
+
});
|
|
1271
|
+
} else {
|
|
1272
|
+
context.messages.push(partialMessage);
|
|
1273
|
+
addedPartial = true;
|
|
1274
|
+
stream.push({ type: "message_start", message: snapshotAssistantMessage(partialMessage) });
|
|
1275
|
+
}
|
|
1255
1276
|
break;
|
|
1256
1277
|
|
|
1257
1278
|
case "text_start":
|
package/src/agent.ts
CHANGED
|
@@ -257,6 +257,13 @@ export interface AgentOptions {
|
|
|
257
257
|
*/
|
|
258
258
|
afterToolCall?: AgentLoopConfig["afterToolCall"];
|
|
259
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Called once an assistant message is finalized, before it reaches the
|
|
262
|
+
* context, the UI, or tool dispatch. May mutate the message in place (text +
|
|
263
|
+
* tool-call arguments). See {@link AgentLoopConfig.transformAssistantMessage}.
|
|
264
|
+
*/
|
|
265
|
+
transformAssistantMessage?: AgentLoopConfig["transformAssistantMessage"];
|
|
266
|
+
|
|
260
267
|
/**
|
|
261
268
|
* Opt-in OpenTelemetry instrumentation. Passing `{}` enables the loop's
|
|
262
269
|
* GenAI-semantic-convention spans using the global tracer provider. See
|
|
@@ -358,6 +365,11 @@ export class Agent {
|
|
|
358
365
|
* message emission. Reassign at any time to swap the implementation.
|
|
359
366
|
*/
|
|
360
367
|
afterToolCall?: AgentLoopConfig["afterToolCall"];
|
|
368
|
+
/**
|
|
369
|
+
* Hook invoked once an assistant message is finalized, before context append,
|
|
370
|
+
* UI emission, and tool dispatch. Reassign at any time to swap the implementation.
|
|
371
|
+
*/
|
|
372
|
+
transformAssistantMessage?: AgentLoopConfig["transformAssistantMessage"];
|
|
361
373
|
|
|
362
374
|
constructor(opts: AgentOptions = {}) {
|
|
363
375
|
this.#state = { ...this.#state, ...opts.initialState };
|
|
@@ -402,6 +414,7 @@ export class Agent {
|
|
|
402
414
|
this.#onHarmonyLeak = opts.onHarmonyLeak;
|
|
403
415
|
this.beforeToolCall = opts.beforeToolCall;
|
|
404
416
|
this.afterToolCall = opts.afterToolCall;
|
|
417
|
+
this.transformAssistantMessage = opts.transformAssistantMessage;
|
|
405
418
|
this.#telemetry = opts.telemetry;
|
|
406
419
|
this.#appendOnlyContext = opts.appendOnlyContext;
|
|
407
420
|
this.#transformProviderContext = opts.transformProviderContext;
|
|
@@ -1051,6 +1064,9 @@ export class Agent {
|
|
|
1051
1064
|
appendOnlyContext: this.#appendOnlyContext,
|
|
1052
1065
|
beforeToolCall: this.beforeToolCall ? (ctx, signal) => this.beforeToolCall?.(ctx, signal) : undefined,
|
|
1053
1066
|
afterToolCall: this.afterToolCall ? (ctx, signal) => this.afterToolCall?.(ctx, signal) : undefined,
|
|
1067
|
+
transformAssistantMessage: this.transformAssistantMessage
|
|
1068
|
+
? (message, signal) => this.transformAssistantMessage?.(message, signal)
|
|
1069
|
+
: undefined,
|
|
1054
1070
|
onAssistantMessageEvent: this.#onAssistantMessageEvent,
|
|
1055
1071
|
onHarmonyLeak: this.#onHarmonyLeak,
|
|
1056
1072
|
onTurnEnd: (messages, signal) => this.#onTurnEnd?.(messages, signal),
|
|
@@ -20,11 +20,11 @@ import {
|
|
|
20
20
|
} from "@oh-my-pi/pi-ai";
|
|
21
21
|
import { preferredDialect } from "@oh-my-pi/pi-catalog/identity";
|
|
22
22
|
import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
23
|
-
import { countTokens } from "@oh-my-pi/pi-natives";
|
|
24
23
|
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
25
24
|
import * as snapcompact from "@oh-my-pi/snapcompact";
|
|
26
25
|
import { type AgentTelemetry, instrumentedCompleteSimple } from "../telemetry";
|
|
27
26
|
import { ThinkingLevel } from "../thinking";
|
|
27
|
+
import { countTokens } from "../tokenizer";
|
|
28
28
|
import type { AgentMessage } from "../types";
|
|
29
29
|
import type { CompactionEntry, SessionEntry } from "./entries";
|
|
30
30
|
import { type ConvertToLlm, createBranchSummaryMessage, createCustomMessage, defaultConvertToLlm } from "./messages";
|
package/src/compaction/openai.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { ProviderHttpError } from "@oh-my-pi/pi-ai/errors";
|
|
16
|
-
import { parseTextSignature } from "@oh-my-pi/pi-ai/providers/openai-
|
|
16
|
+
import { parseTextSignature } from "@oh-my-pi/pi-ai/providers/openai-shared";
|
|
17
17
|
import { transformMessages } from "@oh-my-pi/pi-ai/providers/transform-messages";
|
|
18
18
|
import type { AssistantMessage, FetchImpl, Message, Model } from "@oh-my-pi/pi-ai/types";
|
|
19
19
|
import {
|
package/src/compaction/shake.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
14
|
-
import { countTokens } from "
|
|
14
|
+
import { countTokens } from "../tokenizer";
|
|
15
15
|
import type { AgentMessage } from "../types";
|
|
16
16
|
import { estimateTokens } from "./compaction";
|
|
17
17
|
import type { CustomMessageEntry, SessionEntry, SessionMessageEntry } from "./entries";
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ export * from "./run-collector";
|
|
|
14
14
|
export * from "./telemetry";
|
|
15
15
|
// Thinking selectors
|
|
16
16
|
export * from "./thinking";
|
|
17
|
+
// Tokenizer choice
|
|
18
|
+
export * from "./tokenizer";
|
|
17
19
|
// Types
|
|
18
20
|
export * from "./types";
|
|
19
21
|
// Yield utilities for Bun event-loop busy-wait prevention
|
package/src/proxy.ts
CHANGED
|
@@ -210,6 +210,18 @@ function processProxyEvent(
|
|
|
210
210
|
): AssistantMessageEvent | undefined {
|
|
211
211
|
switch (proxyEvent.type) {
|
|
212
212
|
case "start":
|
|
213
|
+
partial.content.length = 0;
|
|
214
|
+
partial.usage = {
|
|
215
|
+
input: 0,
|
|
216
|
+
output: 0,
|
|
217
|
+
cacheRead: 0,
|
|
218
|
+
cacheWrite: 0,
|
|
219
|
+
totalTokens: 0,
|
|
220
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
221
|
+
};
|
|
222
|
+
delete (partial as { stopReason?: string }).stopReason;
|
|
223
|
+
delete (partial as { errorMessage?: string }).errorMessage;
|
|
224
|
+
delete (partial as { duration?: number }).duration;
|
|
213
225
|
return { type: "start", partial };
|
|
214
226
|
|
|
215
227
|
case "text_start":
|
package/src/tokenizer.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { countTokens as countTokensNat } from "@oh-my-pi/pi-natives";
|
|
2
|
+
|
|
3
|
+
const accurate = process.env.PI_TOKENIZER_ACCURATE === "1" && Bun.env.NODE_ENV !== "test";
|
|
4
|
+
|
|
5
|
+
function estimateTokens(text: string) {
|
|
6
|
+
return (Buffer.byteLength(text, "utf-8") + 3) >> 2;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function countTokens(text: string | string[]): number {
|
|
10
|
+
if (accurate) {
|
|
11
|
+
return countTokensNat(text);
|
|
12
|
+
} else if (Array.isArray(text)) {
|
|
13
|
+
return text.reduce((sum, t) => sum + estimateTokens(t), 0);
|
|
14
|
+
} else {
|
|
15
|
+
return estimateTokens(text);
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -293,6 +293,20 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
293
293
|
*/
|
|
294
294
|
onTurnEnd?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<void> | void;
|
|
295
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Called once an assistant message is finalized from the model stream, before
|
|
298
|
+
* it is appended to the context, emitted as `message_end`, or its tool calls
|
|
299
|
+
* are validated and dispatched. The hook may mutate the message in place —
|
|
300
|
+
* both its text content and its tool-call arguments — and those edits are seen
|
|
301
|
+
* by the transcript, the UI, and tool execution alike (single source of truth).
|
|
302
|
+
*
|
|
303
|
+
* Used for inline macro expansion: rewriting `@[[runtime.name(args)]]` tokens
|
|
304
|
+
* to host-computed values before anything downstream consumes the message.
|
|
305
|
+
* Runs at most once per assistant message; must not throw (a throw would abort
|
|
306
|
+
* the turn).
|
|
307
|
+
*/
|
|
308
|
+
transformAssistantMessage?: (message: AssistantMessage, signal?: AbortSignal) => Promise<void> | void;
|
|
309
|
+
|
|
296
310
|
/**
|
|
297
311
|
* Called after a tool finishes executing, before `tool_execution_end` and the
|
|
298
312
|
* tool-result message are emitted.
|