@oh-my-pi/pi-agent-core 15.3.0 → 15.3.1
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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent loop that works with AgentMessage throughout.
|
|
3
3
|
* Transforms to Message[] only at the LLM call boundary.
|
|
4
4
|
*/
|
|
5
|
-
import { EventStream } from "@oh-my-pi/pi-ai";
|
|
5
|
+
import { type Context, EventStream } from "@oh-my-pi/pi-ai";
|
|
6
6
|
import { type AgentRunCoverage, type AgentRunSummary } from "./run-collector";
|
|
7
7
|
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types";
|
|
8
8
|
/**
|
|
@@ -52,3 +52,4 @@ export declare function agentLoopContinueDetailed(context: AgentContext, config:
|
|
|
52
52
|
readonly detailed: () => Promise<AgentLoopDetailedResult>;
|
|
53
53
|
};
|
|
54
54
|
export declare const INTENT_FIELD = "_i";
|
|
55
|
+
export declare function normalizeTools(tools: AgentContext["tools"], injectIntent: boolean): Context["tools"];
|
|
@@ -21,6 +21,11 @@ export interface StablePrefixSnapshot {
|
|
|
21
21
|
tools: Tool[];
|
|
22
22
|
fingerprint: string;
|
|
23
23
|
}
|
|
24
|
+
/** Options threaded through `build()` so the snapshot reflects loop-time settings. */
|
|
25
|
+
export interface BuildOptions {
|
|
26
|
+
/** Inject the `_i` intent field into tool schemas (must match agent-loop's normalizeTools). */
|
|
27
|
+
intentTracing: boolean;
|
|
28
|
+
}
|
|
24
29
|
/**
|
|
25
30
|
* A frozen prefix (system prompt + tools) that produces stable byte
|
|
26
31
|
* sequences across `build()` calls.
|
|
@@ -38,7 +43,7 @@ export declare class StablePrefix {
|
|
|
38
43
|
* Build or rebuild from live context.
|
|
39
44
|
* Returns `true` if the prefix actually changed (cache miss imminent).
|
|
40
45
|
*/
|
|
41
|
-
build(context: AgentContext): boolean;
|
|
46
|
+
build(context: AgentContext, options: BuildOptions): boolean;
|
|
42
47
|
/** Force rebuild on the next `build()` call. */
|
|
43
48
|
invalidate(): void;
|
|
44
49
|
/**
|
|
@@ -89,7 +94,7 @@ export declare class AppendOnlyContextManager {
|
|
|
89
94
|
#private;
|
|
90
95
|
readonly prefix: StablePrefix;
|
|
91
96
|
readonly log: AppendOnlyLog;
|
|
92
|
-
build(context: AgentContext): Context;
|
|
97
|
+
build(context: AgentContext, options: BuildOptions): Context;
|
|
93
98
|
/**
|
|
94
99
|
* Sync normalized (provider-level) messages into the append-only log.
|
|
95
100
|
*
|
|
@@ -104,5 +109,5 @@ export declare class AppendOnlyContextManager {
|
|
|
104
109
|
appendMessage(message: any): void;
|
|
105
110
|
replaceTailMessage(message: any): void;
|
|
106
111
|
invalidate(): void;
|
|
107
|
-
reset(context: AgentContext): void;
|
|
112
|
+
reset(context: AgentContext, options: BuildOptions): void;
|
|
108
113
|
}
|
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.3.
|
|
4
|
+
"version": "15.3.1",
|
|
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.3.
|
|
39
|
-
"@oh-my-pi/pi-natives": "15.3.
|
|
40
|
-
"@oh-my-pi/pi-utils": "15.3.
|
|
38
|
+
"@oh-my-pi/pi-ai": "15.3.1",
|
|
39
|
+
"@oh-my-pi/pi-natives": "15.3.1",
|
|
40
|
+
"@oh-my-pi/pi-utils": "15.3.1",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -363,7 +363,7 @@ function injectIntentIntoSchema(schema: unknown, mode: "require" | "optional" =
|
|
|
363
363
|
};
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
function normalizeTools(tools: AgentContext["tools"], injectIntent: boolean): Context["tools"] {
|
|
366
|
+
export function normalizeTools(tools: AgentContext["tools"], injectIntent: boolean): Context["tools"] {
|
|
367
367
|
injectIntent = injectIntent && Bun.env.PI_NO_INTENT !== "1";
|
|
368
368
|
return tools?.map(t => {
|
|
369
369
|
const intentMode = resolveIntentMode(t.intent);
|
|
@@ -652,7 +652,7 @@ async function streamAssistantResponse(
|
|
|
652
652
|
let llmContext: Context;
|
|
653
653
|
if (config.appendOnlyContext) {
|
|
654
654
|
config.appendOnlyContext.syncMessages(normalizedMessages);
|
|
655
|
-
llmContext = config.appendOnlyContext.build(context);
|
|
655
|
+
llmContext = config.appendOnlyContext.build(context, { intentTracing: !!config.intentTracing });
|
|
656
656
|
} else {
|
|
657
657
|
llmContext = {
|
|
658
658
|
systemPrompt: context.systemPrompt,
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { Context, Message, Tool } from "@oh-my-pi/pi-ai";
|
|
18
|
-
import
|
|
18
|
+
import { normalizeTools } from "./agent-loop";
|
|
19
|
+
import type { AgentContext } from "./types";
|
|
19
20
|
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
21
22
|
// StablePrefix (formerly ImmutablePrefix)
|
|
@@ -28,6 +29,12 @@ export interface StablePrefixSnapshot {
|
|
|
28
29
|
fingerprint: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/** Options threaded through `build()` so the snapshot reflects loop-time settings. */
|
|
33
|
+
export interface BuildOptions {
|
|
34
|
+
/** Inject the `_i` intent field into tool schemas (must match agent-loop's normalizeTools). */
|
|
35
|
+
intentTracing: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
/**
|
|
32
39
|
* A frozen prefix (system prompt + tools) that produces stable byte
|
|
33
40
|
* sequences across `build()` calls.
|
|
@@ -54,8 +61,8 @@ export class StablePrefix {
|
|
|
54
61
|
* Build or rebuild from live context.
|
|
55
62
|
* Returns `true` if the prefix actually changed (cache miss imminent).
|
|
56
63
|
*/
|
|
57
|
-
build(context: AgentContext): boolean {
|
|
58
|
-
const snapshot = takeSnapshot(context);
|
|
64
|
+
build(context: AgentContext, options: BuildOptions): boolean {
|
|
65
|
+
const snapshot = takeSnapshot(context, options);
|
|
59
66
|
if (this.#snapshot && this.#snapshot.fingerprint === snapshot.fingerprint) {
|
|
60
67
|
return false;
|
|
61
68
|
}
|
|
@@ -154,8 +161,8 @@ export class AppendOnlyContextManager {
|
|
|
154
161
|
/** Rolling digest of synced message content — detects in-place rewrites. */
|
|
155
162
|
#syncedDigest = 0;
|
|
156
163
|
|
|
157
|
-
build(context: AgentContext): Context {
|
|
158
|
-
this.prefix.build(context);
|
|
164
|
+
build(context: AgentContext, options: BuildOptions): Context {
|
|
165
|
+
this.prefix.build(context, options);
|
|
159
166
|
const { systemPrompt, tools } = this.prefix.toContext();
|
|
160
167
|
return { systemPrompt, messages: this.log.toMessages(), tools };
|
|
161
168
|
}
|
|
@@ -219,25 +226,36 @@ export class AppendOnlyContextManager {
|
|
|
219
226
|
this.prefix.invalidate();
|
|
220
227
|
}
|
|
221
228
|
|
|
222
|
-
reset(context: AgentContext): void {
|
|
229
|
+
reset(context: AgentContext, options: BuildOptions): void {
|
|
223
230
|
this.prefix.invalidate();
|
|
224
231
|
this.log.clear();
|
|
225
232
|
this.#lastSyncCount = 0;
|
|
226
233
|
this.#syncedDigest = 0;
|
|
227
|
-
this.prefix.build(context);
|
|
234
|
+
this.prefix.build(context, options);
|
|
228
235
|
}
|
|
229
236
|
|
|
230
|
-
/**
|
|
231
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Deterministic digest over every field the provider may serialize — role,
|
|
239
|
+
* content, tool calls (both `toolCalls` and OpenAI-wire `tool_calls`),
|
|
240
|
+
* `tool_call_id`, `name`, `id`. Hashed with the same FNV-style rolling
|
|
241
|
+
* accumulator so in-place rewrites of *any* of these fields are visible.
|
|
242
|
+
*/
|
|
243
|
+
#computeDigest(messages: readonly unknown[]): number {
|
|
232
244
|
let hash = 0;
|
|
233
245
|
for (let i = 0; i < messages.length; i++) {
|
|
234
246
|
const msg = messages[i];
|
|
235
|
-
if (msg
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
if (!msg || typeof msg !== "object") continue;
|
|
248
|
+
const m = msg as Record<string, unknown>;
|
|
249
|
+
const payload = JSON.stringify({
|
|
250
|
+
r: m.role ?? null,
|
|
251
|
+
c: m.content ?? null,
|
|
252
|
+
tc: m.toolCalls ?? m.tool_calls ?? null,
|
|
253
|
+
tcid: m.tool_call_id ?? null,
|
|
254
|
+
n: m.name ?? null,
|
|
255
|
+
id: m.id ?? null,
|
|
256
|
+
});
|
|
257
|
+
for (let j = 0; j < payload.length; j++) {
|
|
258
|
+
hash = ((hash << 5) - hash + payload.charCodeAt(j)) | 0;
|
|
241
259
|
}
|
|
242
260
|
}
|
|
243
261
|
return hash >>> 0;
|
|
@@ -248,30 +266,17 @@ export class AppendOnlyContextManager {
|
|
|
248
266
|
// Snapshot helpers
|
|
249
267
|
// ---------------------------------------------------------------------------
|
|
250
268
|
|
|
251
|
-
|
|
252
|
-
* Produce a stable serialization of tools that matches what
|
|
253
|
-
* `normalizeTools(tools, false)` outputs (no intent injection).
|
|
254
|
-
*
|
|
255
|
-
* The spread `{ ...agentTool }` preserves all own enumerable properties
|
|
256
|
-
* that survive JSON.stringify — functions are dropped, but strings,
|
|
257
|
-
* booleans, objects are included.
|
|
258
|
-
*/
|
|
259
|
-
function normalizeTool(t: AgentTool): Tool {
|
|
260
|
-
const description = t.description ?? "";
|
|
261
|
-
return { ...t, parameters: t.parameters, description };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function takeSnapshot(context: AgentContext): StablePrefixSnapshot {
|
|
269
|
+
function takeSnapshot(context: AgentContext, options: BuildOptions): StablePrefixSnapshot {
|
|
265
270
|
const systemPrompt = [...context.systemPrompt];
|
|
266
|
-
const tools = (context.tools ?? []
|
|
271
|
+
const tools = normalizeTools(context.tools, options.intentTracing) ?? [];
|
|
267
272
|
return {
|
|
268
273
|
systemPrompt,
|
|
269
274
|
tools,
|
|
270
|
-
fingerprint: computeFingerprint(systemPrompt, tools),
|
|
275
|
+
fingerprint: computeFingerprint(systemPrompt, tools, options),
|
|
271
276
|
};
|
|
272
277
|
}
|
|
273
278
|
|
|
274
|
-
function computeFingerprint(systemPrompt: string[], tools: Tool[]): string {
|
|
279
|
+
function computeFingerprint(systemPrompt: string[], tools: Tool[], options: BuildOptions): string {
|
|
275
280
|
const payload = JSON.stringify({
|
|
276
281
|
s: systemPrompt,
|
|
277
282
|
t: tools.map(t => ({
|
|
@@ -282,6 +287,7 @@ function computeFingerprint(systemPrompt: string[], tools: Tool[]): string {
|
|
|
282
287
|
cf: t.customFormat,
|
|
283
288
|
cw: t.customWireName,
|
|
284
289
|
})),
|
|
290
|
+
i: options.intentTracing,
|
|
285
291
|
});
|
|
286
292
|
let hash = 0;
|
|
287
293
|
for (let i = 0; i < payload.length; i++) {
|