@oh-my-pi/pi-agent-core 18.8.0 → 18.8.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.8.1] - 2026-10-07
6
+
7
+ ### Added
8
+
9
+ - Added the public `validateAgentToolArguments()` API for consistent, lenient-aware validation of agent tool arguments across agent and coding-agent workflows.
10
+
5
11
  ## [18.8.0] - 2026-10-07
6
12
 
7
13
  ### Changed
@@ -13,5 +13,6 @@ export * from "./telemetry.js";
13
13
  export * from "./thinking.js";
14
14
  export * from "./tool-context.js";
15
15
  export * from "./tokenizer.js";
16
+ export * from "./tool-arguments.js";
16
17
  export * from "./types.js";
17
18
  export * from "./utils/yield.js";
@@ -1,4 +1,4 @@
1
- import { type AssistantMessage } from "@oh-my-pi/pi-ai";
1
+ import type { AssistantMessage } from "@oh-my-pi/pi-ai";
2
2
  import type { AgentContext, AgentLoopConfig, AgentTool, AgentToolCall, AgentToolResult, SpeculativeAuthorization, SpeculativeChildDefinition, SpeculativeChildHandle, SpeculativeLaunchContext, SpeculativeToolExecutionConfig, ToolSpeculationEffect, ToolSpeculationStreamSession } from "./types.js";
3
3
  export type SpeculativeRawOutcome = {
4
4
  result: AgentToolResult<unknown>;
@@ -0,0 +1,19 @@
1
+ import { type ToolCall } from "@oh-my-pi/pi-ai";
2
+ import type { AgentTool } from "./types.js";
3
+ /**
4
+ * Where a call's `__parseError`/`__rawJson` keys came from.
5
+ * - `"parsed"`: a provider stream parsed the arguments, so `__parseError` is a
6
+ * genuine parse-failure sentinel and is never handed to a tool.
7
+ * - `"payload"`: the arguments are a payload the model wrote (eval tool bridge,
8
+ * `xd://` device content), so those keys are forged and only stripped.
9
+ */
10
+ export type ToolArgumentSource = "parsed" | "payload";
11
+ /**
12
+ * Validate a call's arguments against `tool`'s schema, honoring
13
+ * {@link AgentTool.lenientArgValidation}: on a schema mismatch a lenient tool
14
+ * receives its raw arguments minus the internal `__parseError`/`__rawJson`
15
+ * sentinels. Malformed JSON from a provider stream is never lenient.
16
+ *
17
+ * @throws the validation error when the tool is strict or the arguments failed to parse.
18
+ */
19
+ export declare function validateAgentToolArguments(tool: AgentTool, toolCall: ToolCall, source?: ToolArgumentSource): ToolCall["arguments"];
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.8.0",
4
+ "version": "18.8.1",
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.8.0",
42
- "@oh-my-pi/pi-catalog": "18.8.0",
43
- "@oh-my-pi/pi-natives": "18.8.0",
44
- "@oh-my-pi/pi-utils": "18.8.0",
45
- "@oh-my-pi/pi-wire": "18.8.0",
46
- "@oh-my-pi/snapcompact": "18.8.0",
41
+ "@oh-my-pi/pi-ai": "18.8.1",
42
+ "@oh-my-pi/pi-catalog": "18.8.1",
43
+ "@oh-my-pi/pi-natives": "18.8.1",
44
+ "@oh-my-pi/pi-utils": "18.8.1",
45
+ "@oh-my-pi/pi-wire": "18.8.1",
46
+ "@oh-my-pi/snapcompact": "18.8.1",
47
47
  "@opentelemetry/api": "^1.9.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@oh-my-pi/omptype": "18.8.0",
50
+ "@oh-my-pi/omptype": "18.8.1",
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"
package/src/agent-loop.ts CHANGED
@@ -23,7 +23,6 @@ import {
23
23
  type TSchema,
24
24
  toolWireSchema,
25
25
  type UserMessage,
26
- validateToolArguments,
27
26
  } from "@oh-my-pi/pi-ai";
28
27
  import {
29
28
  type Dialect,
@@ -76,6 +75,7 @@ import {
76
75
  startExecuteToolSpan,
77
76
  startInvokeAgentSpan,
78
77
  } from "./telemetry";
78
+ import { validateAgentToolArguments } from "./tool-arguments";
79
79
  import { createAdditionalContextMessage, isNonBlankContext, joinAdditionalContext } from "./tool-context";
80
80
  import dsmlToolCallLeakPrompt from "./prompts/dsml-tool-call-leak.md" with { type: "text" };
81
81
  import type {
@@ -3062,15 +3062,9 @@ async function prepareToolCallDispatch(
3062
3062
  if (!tool) {
3063
3063
  throw new Error(formatToolNotFoundMessage(toolCall.name, context.tools, suggestFallbackToolNames?.()));
3064
3064
  }
3065
- return validateToolArguments(tool, { ...toolCall, arguments: args });
3065
+ return validateAgentToolArguments(tool, { ...toolCall, arguments: args });
3066
3066
  } catch (validationError) {
3067
- // Lenience covers schema mismatches; a parse failure has no args to hand over.
3068
3067
  const parseFailed = "__parseError" in args;
3069
- if (tool?.lenientArgValidation && !parseFailed) {
3070
- const fallback = { ...args };
3071
- delete fallback.__rawJson;
3072
- return fallback;
3073
- }
3074
3068
  entry.args = parseFailed ? { __parseError: args.__parseError } : args;
3075
3069
  entry.validationErrorMessage =
3076
3070
  validationError instanceof Error ? validationError.message : String(validationError);
package/src/index.ts CHANGED
@@ -28,6 +28,8 @@ export * from "./thinking";
28
28
  export * from "./tool-context";
29
29
  // Tokenizer choice
30
30
  export * from "./tokenizer";
31
+ // Lenient-aware tool argument validation
32
+ export * from "./tool-arguments";
31
33
  // Types
32
34
  export * from "./types";
33
35
  // Yield utilities for Bun event-loop busy-wait prevention
@@ -1,5 +1,6 @@
1
1
  import * as path from "node:path";
2
- import { type AssistantMessage, validateToolArguments } from "@oh-my-pi/pi-ai";
2
+ import type { AssistantMessage } from "@oh-my-pi/pi-ai";
3
+ import { validateAgentToolArguments } from "./tool-arguments";
3
4
  import type {
4
5
  AgentContext,
5
6
  AgentLoopConfig,
@@ -712,15 +713,10 @@ export class SpeculativeOperationCoordinator {
712
713
  }
713
714
  let validatedArgs: Record<string, unknown>;
714
715
  try {
715
- validatedArgs = validateToolArguments(tool, toolCall);
716
+ validatedArgs = validateAgentToolArguments(tool, toolCall);
716
717
  } catch {
717
- // Lenience covers schema mismatches; a parse failure has no args to hand over.
718
- if (!tool.lenientArgValidation || "__parseError" in toolCall.arguments) {
719
- this.ineligible(toolCall, "tool arguments are not valid", source, parentToolCallId);
720
- return undefined;
721
- }
722
- validatedArgs = { ...toolCall.arguments };
723
- delete validatedArgs.__rawJson;
718
+ this.ineligible(toolCall, "tool arguments are not valid", source, parentToolCallId);
719
+ return undefined;
724
720
  }
725
721
  let executionArgs: Record<string, unknown>;
726
722
  try {
@@ -0,0 +1,38 @@
1
+ import { type ToolCall, validateToolArguments } from "@oh-my-pi/pi-ai";
2
+ import type { AgentTool } from "./types";
3
+
4
+ /**
5
+ * Where a call's `__parseError`/`__rawJson` keys came from.
6
+ * - `"parsed"`: a provider stream parsed the arguments, so `__parseError` is a
7
+ * genuine parse-failure sentinel and is never handed to a tool.
8
+ * - `"payload"`: the arguments are a payload the model wrote (eval tool bridge,
9
+ * `xd://` device content), so those keys are forged and only stripped.
10
+ */
11
+ export type ToolArgumentSource = "parsed" | "payload";
12
+
13
+ /**
14
+ * Validate a call's arguments against `tool`'s schema, honoring
15
+ * {@link AgentTool.lenientArgValidation}: on a schema mismatch a lenient tool
16
+ * receives its raw arguments minus the internal `__parseError`/`__rawJson`
17
+ * sentinels. Malformed JSON from a provider stream is never lenient.
18
+ *
19
+ * @throws the validation error when the tool is strict or the arguments failed to parse.
20
+ */
21
+ export function validateAgentToolArguments(
22
+ tool: AgentTool,
23
+ toolCall: ToolCall,
24
+ source: ToolArgumentSource = "parsed",
25
+ ): ToolCall["arguments"] {
26
+ try {
27
+ return validateToolArguments(tool, toolCall);
28
+ } catch (error) {
29
+ const args: unknown = toolCall.arguments;
30
+ if (!tool.lenientArgValidation) throw error;
31
+ if (args === null || typeof args !== "object" || Array.isArray(args)) return args as ToolCall["arguments"];
32
+ if (source === "parsed" && "__parseError" in args) throw error;
33
+ const fallback = { ...(args as Record<string, unknown>) };
34
+ delete fallback.__parseError;
35
+ delete fallback.__rawJson;
36
+ return fallback;
37
+ }
38
+ }