@oh-my-pi/pi-agent-core 13.3.13 → 13.4.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 CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.4.0] - 2026-03-01
6
+ ### Added
7
+
8
+ - Added `getToolChoice` option to dynamically override tool choice per LLM call
9
+
5
10
  ## [13.3.8] - 2026-02-28
6
11
  ### Changed
7
12
 
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": "13.3.13",
4
+ "version": "13.4.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",
@@ -31,8 +31,8 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-ai": "13.3.13",
35
- "@oh-my-pi/pi-utils": "13.3.13"
34
+ "@oh-my-pi/pi-ai": "13.4.0",
35
+ "@oh-my-pi/pi-utils": "13.4.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@sinclair/typebox": "^0.34",
package/src/agent-loop.ts CHANGED
@@ -326,9 +326,11 @@ async function streamAssistantResponse(
326
326
  const resolvedApiKey =
327
327
  (config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;
328
328
 
329
+ const dynamicToolChoice = config.getToolChoice?.();
329
330
  const response = await streamFunction(config.model, llmContext, {
330
331
  ...config,
331
332
  apiKey: resolvedApiKey,
333
+ toolChoice: dynamicToolChoice ?? config.toolChoice,
332
334
  signal,
333
335
  });
334
336
 
package/src/agent.ts CHANGED
@@ -146,6 +146,8 @@ export interface AgentOptions {
146
146
 
147
147
  /** Enable intent tracing schema injection/stripping in the harness. */
148
148
  intentTracing?: boolean;
149
+ /** Dynamic tool choice override, resolved per LLM call. */
150
+ getToolChoice?: () => ToolChoice | undefined;
149
151
 
150
152
  /**
151
153
  * Cursor exec handlers for local tool execution.
@@ -209,6 +211,7 @@ export class Agent {
209
211
  #preferWebsockets?: boolean;
210
212
  #transformToolCallArguments?: (args: Record<string, unknown>, toolName: string) => Record<string, unknown>;
211
213
  #intentTracing: boolean;
214
+ #getToolChoice?: () => ToolChoice | undefined;
212
215
 
213
216
  /** Buffered Cursor tool results with text length at time of call (for correct ordering) */
214
217
  #cursorToolResultBuffer: CursorToolResultEntry[] = [];
@@ -242,6 +245,7 @@ export class Agent {
242
245
  this.#preferWebsockets = opts.preferWebsockets;
243
246
  this.#transformToolCallArguments = opts.transformToolCallArguments;
244
247
  this.#intentTracing = opts.intentTracing === true;
248
+ this.#getToolChoice = opts.getToolChoice;
245
249
  }
246
250
 
247
251
  /**
@@ -709,6 +713,7 @@ export class Agent {
709
713
  cursorOnToolResult,
710
714
  transformToolCallArguments: this.#transformToolCallArguments,
711
715
  intentTracing: this.#intentTracing,
716
+ getToolChoice: this.#getToolChoice,
712
717
  getSteeringMessages: async () => {
713
718
  if (skipInitialSteeringPoll) {
714
719
  skipInitialSteeringPoll = false;
package/src/types.ts CHANGED
@@ -8,6 +8,7 @@ import type {
8
8
  streamSimple,
9
9
  TextContent,
10
10
  Tool,
11
+ ToolChoice,
11
12
  ToolResultMessage,
12
13
  } from "@oh-my-pi/pi-ai";
13
14
  import type { Static, TSchema } from "@sinclair/typebox";
@@ -123,6 +124,12 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
123
124
  * then strips `_i` from arguments before executing tools.
124
125
  */
125
126
  intentTracing?: boolean;
127
+
128
+ /**
129
+ * Dynamic tool choice override, resolved per LLM call.
130
+ * When set and returns a value, overrides the static `toolChoice`.
131
+ */
132
+ getToolChoice?: () => ToolChoice | undefined;
126
133
  }
127
134
 
128
135
  export interface ToolCallContext {
@@ -178,19 +185,15 @@ export interface AgentState {
178
185
  error?: string;
179
186
  }
180
187
 
181
- export interface AgentToolResult<T = any, TNormative extends TSchema = any> {
188
+ export interface AgentToolResult<T = any, _TInput = unknown> {
182
189
  // Content blocks supporting text and images
183
190
  content: (TextContent | ImageContent)[];
184
191
  // Details to be displayed in a UI or logged
185
192
  details?: T;
186
- /** Normative input for the tool result */
187
- $normative?: Static<TNormative>;
188
193
  }
189
194
 
190
195
  // Callback for streaming tool execution updates
191
- export type AgentToolUpdateCallback<T = any, TNormative extends TSchema = any> = (
192
- partialResult: AgentToolResult<T, TNormative>,
193
- ) => void;
196
+ export type AgentToolUpdateCallback<T = any, TInput = unknown> = (partialResult: AgentToolResult<T, TInput>) => void;
194
197
 
195
198
  /** Options passed to renderResult */
196
199
  export interface RenderResultOptions {
@@ -226,6 +229,8 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
226
229
  label: string;
227
230
  /** If true, tool is excluded unless explicitly listed in --tools or agent's tools field */
228
231
  hidden?: boolean;
232
+ /** If true, tool can stage a pending action that requires explicit resolution via the resolve tool. */
233
+ deferrable?: boolean;
229
234
  /** If true, tool execution ignores abort signals (runs to completion) */
230
235
  nonAbortable?: boolean;
231
236
  /**