@oh-my-pi/pi-agent-core 14.8.0 → 14.9.0
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 +10 -0
- package/package.json +4 -4
- package/src/agent-loop.ts +8 -1
- package/src/agent.ts +61 -0
- package/src/types.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.9.0] - 2026-05-10
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added `Agent#metadata` field forwarded to every API request; callers can set arbitrary provider metadata (e.g. `metadata.user_id`) once and have it applied to all subsequent stream calls without modifying per-call options
|
|
9
|
+
- Added `Agent#setMetadataResolver(fn)` for installing a function that resolves request metadata at call time. The `metadata` getter dispatches through the resolver on every read (including the snapshot taken per `prompt()`), so callers reflect mutable external state (e.g. live OAuth account UUID after a token refresh) without manual re-syncs. Plain `agent.metadata = …` continues to set a static value and clears any installed resolver.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added an `onSseEvent` agent option and loop config forwarding path for raw provider SSE diagnostics.
|
|
14
|
+
|
|
5
15
|
## [14.7.6] - 2026-05-07
|
|
6
16
|
|
|
7
17
|
### Added
|
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": "14.
|
|
4
|
+
"version": "14.9.0",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
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": "14.
|
|
39
|
-
"@oh-my-pi/pi-natives": "14.
|
|
40
|
-
"@oh-my-pi/pi-utils": "14.
|
|
38
|
+
"@oh-my-pi/pi-ai": "14.9.0",
|
|
39
|
+
"@oh-my-pi/pi-natives": "14.9.0",
|
|
40
|
+
"@oh-my-pi/pi-utils": "14.9.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@sinclair/typebox": "^0.34.49",
|
package/src/agent-loop.ts
CHANGED
|
@@ -385,15 +385,22 @@ async function streamAssistantResponse(
|
|
|
385
385
|
|
|
386
386
|
const streamFunction = streamFn || streamSimple;
|
|
387
387
|
|
|
388
|
-
// Resolve API key (important for expiring tokens)
|
|
388
|
+
// Resolve API key (important for expiring tokens) — do this before resolving
|
|
389
|
+
// metadata so that the session-sticky credential recorded by getApiKey is
|
|
390
|
+
// visible to metadataResolver (e.g. for the correct account_uuid in metadata.user_id).
|
|
389
391
|
const resolvedApiKey =
|
|
390
392
|
(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;
|
|
391
393
|
|
|
394
|
+
// Re-resolve metadata after credential selection so the per-request value
|
|
395
|
+
// reflects the credential actually used, not the snapshot from AgentLoopConfig construction.
|
|
396
|
+
const resolvedMetadata = config.metadataResolver ? config.metadataResolver(config.model.provider) : config.metadata;
|
|
397
|
+
|
|
392
398
|
const dynamicToolChoice = config.getToolChoice?.();
|
|
393
399
|
const dynamicReasoning = config.getReasoning?.();
|
|
394
400
|
const response = await streamFunction(config.model, llmContext, {
|
|
395
401
|
...config,
|
|
396
402
|
apiKey: resolvedApiKey,
|
|
403
|
+
metadata: resolvedMetadata,
|
|
397
404
|
toolChoice: dynamicToolChoice ?? config.toolChoice,
|
|
398
405
|
reasoning: dynamicReasoning ?? config.reasoning,
|
|
399
406
|
signal,
|
package/src/agent.ts
CHANGED
|
@@ -135,6 +135,10 @@ export interface AgentOptions {
|
|
|
135
135
|
* Inspect provider response metadata after headers arrive and before streaming body consumption.
|
|
136
136
|
*/
|
|
137
137
|
onResponse?: SimpleStreamOptions["onResponse"];
|
|
138
|
+
/**
|
|
139
|
+
* Inspect raw Server-Sent Events from HTTP streaming providers.
|
|
140
|
+
*/
|
|
141
|
+
onSseEvent?: SimpleStreamOptions["onSseEvent"];
|
|
138
142
|
/**
|
|
139
143
|
* Inspect assistant streaming events before they are emitted to subscribers.
|
|
140
144
|
* Use this when abort decisions must happen before buffered events continue flowing.
|
|
@@ -233,6 +237,8 @@ export class Agent {
|
|
|
233
237
|
#followUpMode: "all" | "one-at-a-time";
|
|
234
238
|
#interruptMode: "immediate" | "wait";
|
|
235
239
|
#sessionId?: string;
|
|
240
|
+
#metadata?: Record<string, unknown>;
|
|
241
|
+
#metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
|
|
236
242
|
#providerSessionState?: Map<string, ProviderSessionState>;
|
|
237
243
|
#thinkingBudgets?: ThinkingBudgets;
|
|
238
244
|
#temperature?: number;
|
|
@@ -256,6 +262,7 @@ export class Agent {
|
|
|
256
262
|
#getToolChoice?: () => ToolChoice | undefined;
|
|
257
263
|
#onPayload?: SimpleStreamOptions["onPayload"];
|
|
258
264
|
#onResponse?: SimpleStreamOptions["onResponse"];
|
|
265
|
+
#onSseEvent?: SimpleStreamOptions["onSseEvent"];
|
|
259
266
|
#onAssistantMessageEvent?: (message: AssistantMessage, event: AssistantMessageEvent) => void;
|
|
260
267
|
|
|
261
268
|
/** Buffered Cursor tool results with text length at time of call (for correct ordering) */
|
|
@@ -287,6 +294,7 @@ export class Agent {
|
|
|
287
294
|
this.getApiKey = opts.getApiKey;
|
|
288
295
|
this.#onPayload = opts.onPayload;
|
|
289
296
|
this.#onResponse = opts.onResponse;
|
|
297
|
+
this.#onSseEvent = opts.onSseEvent;
|
|
290
298
|
this.#getToolContext = opts.getToolContext;
|
|
291
299
|
this.#cursorExecHandlers = opts.cursorExecHandlers;
|
|
292
300
|
this.#cursorOnToolResult = opts.cursorOnToolResult;
|
|
@@ -313,6 +321,48 @@ export class Agent {
|
|
|
313
321
|
this.#sessionId = value;
|
|
314
322
|
}
|
|
315
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Static metadata forwarded to every API request when no resolver is installed
|
|
326
|
+
* (e.g. `metadata.user_id` for Anthropic session attribution). Setting this
|
|
327
|
+
* clears any installed resolver.
|
|
328
|
+
*
|
|
329
|
+
* For live/provider-aware metadata (e.g. Anthropic OAuth `account_uuid` that
|
|
330
|
+
* must reflect the credential selected per-request), use
|
|
331
|
+
* {@link setMetadataResolver} and read via {@link metadataForProvider}.
|
|
332
|
+
*/
|
|
333
|
+
get metadata(): Record<string, unknown> | undefined {
|
|
334
|
+
return this.#metadata;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
set metadata(value: Record<string, unknown> | undefined) {
|
|
338
|
+
this.#metadata = value;
|
|
339
|
+
this.#metadataResolver = undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Resolve request metadata for the given provider at call time. When a
|
|
344
|
+
* resolver is installed via {@link setMetadataResolver}, it is invoked with
|
|
345
|
+
* the provider string so the result can be scoped (e.g. `account_uuid` is
|
|
346
|
+
* only included for `"anthropic"` requests). Falls back to the static
|
|
347
|
+
* {@link metadata} value when no resolver is set.
|
|
348
|
+
*/
|
|
349
|
+
metadataForProvider(provider: string): Record<string, unknown> | undefined {
|
|
350
|
+
if (this.#metadataResolver) return this.#metadataResolver(provider);
|
|
351
|
+
return this.#metadata;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Install a function that resolves request metadata at call time. The
|
|
356
|
+
* resolver receives the target provider string and can gate provider-specific
|
|
357
|
+
* fields (e.g. `account_uuid` only for `"anthropic"`). Invoked per LLM
|
|
358
|
+
* request by `agent-loop` after `getApiKey` selects the session-sticky
|
|
359
|
+
* credential. Pass `undefined` to clear and revert to the static
|
|
360
|
+
* {@link metadata} value.
|
|
361
|
+
*/
|
|
362
|
+
setMetadataResolver(resolver: ((provider: string) => Record<string, unknown> | undefined) | undefined): void {
|
|
363
|
+
this.#metadataResolver = resolver;
|
|
364
|
+
}
|
|
365
|
+
|
|
316
366
|
/**
|
|
317
367
|
* Get provider-scoped mutable session state store.
|
|
318
368
|
*/
|
|
@@ -435,6 +485,14 @@ export class Agent {
|
|
|
435
485
|
return () => this.#listeners.delete(fn);
|
|
436
486
|
}
|
|
437
487
|
|
|
488
|
+
setProviderResponseInterceptor(fn: SimpleStreamOptions["onResponse"] | undefined): void {
|
|
489
|
+
this.#onResponse = fn;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
setRawSseEventInterceptor(fn: SimpleStreamOptions["onSseEvent"] | undefined): void {
|
|
493
|
+
this.#onSseEvent = fn;
|
|
494
|
+
}
|
|
495
|
+
|
|
438
496
|
setAssistantMessageEventInterceptor(
|
|
439
497
|
fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined,
|
|
440
498
|
): void {
|
|
@@ -777,6 +835,8 @@ export class Agent {
|
|
|
777
835
|
hideThinkingSummary: this.#hideThinkingSummary,
|
|
778
836
|
interruptMode: this.#interruptMode,
|
|
779
837
|
sessionId: this.#sessionId,
|
|
838
|
+
metadata: this.#metadataResolver ? undefined : this.#metadata,
|
|
839
|
+
metadataResolver: this.#metadataResolver,
|
|
780
840
|
providerSessionState: this.#providerSessionState,
|
|
781
841
|
thinkingBudgets: this.#thinkingBudgets,
|
|
782
842
|
maxRetryDelayMs: this.#maxRetryDelayMs,
|
|
@@ -786,6 +846,7 @@ export class Agent {
|
|
|
786
846
|
transformContext: this.#transformContext,
|
|
787
847
|
onPayload: this.#onPayload,
|
|
788
848
|
onResponse: this.#onResponse,
|
|
849
|
+
onSseEvent: this.#onSseEvent,
|
|
789
850
|
getApiKey: this.getApiKey,
|
|
790
851
|
getToolContext: this.#getToolContext,
|
|
791
852
|
syncContextBeforeModelCall: async context => {
|
package/src/types.ts
CHANGED
|
@@ -39,6 +39,16 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
39
39
|
*/
|
|
40
40
|
sessionId?: string;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Optional resolver called per LLM request to produce request metadata.
|
|
44
|
+
* When set, the agent loop evaluates it **after** `getApiKey` resolves the
|
|
45
|
+
* session-sticky credential, ensuring the metadata's `account_uuid` reflects
|
|
46
|
+
* the credential actually used for the request (not the credential that was
|
|
47
|
+
* current when `AgentLoopConfig` was first constructed). Overrides the static
|
|
48
|
+
* `metadata` field when present.
|
|
49
|
+
*/
|
|
50
|
+
metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
|
|
51
|
+
|
|
42
52
|
/**
|
|
43
53
|
* Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.
|
|
44
54
|
*
|