@oh-my-pi/pi-ai 16.3.2 → 16.3.3

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.
@@ -1081,6 +1081,54 @@ function normalizeStringEncodedArrayUnions(schema: unknown, value: unknown): { v
1081
1081
  return { value: changed ? nextValue : valueObject, changed };
1082
1082
  }
1083
1083
 
1084
+ /**
1085
+ * Name of the sole property when a schema declares exactly one required string
1086
+ * field, else `undefined`. Recognizes the closed single-argument tool shape
1087
+ * (`{ type: "object", properties: { X: { type: "string" } }, required: ["X"] }`).
1088
+ */
1089
+ function singleRequiredStringKey(schema: unknown): string | undefined {
1090
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return undefined;
1091
+ const obj = schema as Record<string, unknown>;
1092
+ if (obj.type !== "object") return undefined;
1093
+ const properties = obj.properties;
1094
+ if (!properties || typeof properties !== "object") return undefined;
1095
+ const keys = Object.keys(properties as Record<string, unknown>);
1096
+ if (keys.length !== 1) return undefined;
1097
+ const key = keys[0];
1098
+ const required = obj.required;
1099
+ if (!Array.isArray(required) || required.length !== 1 || required[0] !== key) return undefined;
1100
+ const propertySchema = (properties as Record<string, unknown>)[key];
1101
+ if (!propertySchema || typeof propertySchema !== "object") return undefined;
1102
+ return (propertySchema as Record<string, unknown>).type === "string" ? key : undefined;
1103
+ }
1104
+
1105
+ /**
1106
+ * LLM-quirk repair for single-argument tools. When a tool declares exactly one
1107
+ * property — a required string — some providers deliver the payload under a
1108
+ * different key (e.g. the `edit` tool's patch arriving as `input`/`_input`, or
1109
+ * any single-string tool whose argument the model mislabels). When the declared
1110
+ * key is absent but another field holds a string, adopt the first such string
1111
+ * as the declared key so the call validates instead of failing with "<key> was
1112
+ * missing". A present-but-wrong-type value is left alone so its real type error
1113
+ * still surfaces.
1114
+ */
1115
+ function normalizeSingleStringField(schema: unknown, value: unknown): { value: unknown; changed: boolean } {
1116
+ const key = singleRequiredStringKey(schema);
1117
+ if (key === undefined) return { value, changed: false };
1118
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return { value, changed: false };
1119
+ const record = value as Record<string, unknown>;
1120
+ if (record[key] !== undefined) return { value, changed: false };
1121
+ for (const candidate in record) {
1122
+ if (candidate === key || !Object.hasOwn(record, candidate)) continue;
1123
+ const candidateValue = record[candidate];
1124
+ if (typeof candidateValue !== "string") continue;
1125
+ const next = { ...record, [key]: candidateValue };
1126
+ delete next[candidate];
1127
+ return { value: next, changed: true };
1128
+ }
1129
+ return { value, changed: false };
1130
+ }
1131
+
1084
1132
  // ============================================================================
1085
1133
  // Zod issue → coercion bridge
1086
1134
  // ============================================================================
@@ -1447,6 +1495,14 @@ export function validateToolArguments(tool: Tool, toolCall: ToolCall): ToolCall[
1447
1495
  changed = true;
1448
1496
  }
1449
1497
 
1498
+ // Single-argument tools (e.g. `edit`): if the model put the lone required
1499
+ // string under a different key, adopt the first string field as that key.
1500
+ const singleStringNorm = normalizeSingleStringField(json, normalizedArgs);
1501
+ if (singleStringNorm.changed) {
1502
+ normalizedArgs = singleStringNorm.value;
1503
+ changed = true;
1504
+ }
1505
+
1450
1506
  let result = validateContext(ctx, normalizedArgs);
1451
1507
  if (result.success) return result.value as ToolCall["arguments"];
1452
1508
 
@@ -1480,6 +1536,14 @@ export function validateToolArguments(tool: Tool, toolCall: ToolCall): ToolCall[
1480
1536
  normalizedArgs = stringEncodedArrayNormPass.value;
1481
1537
  }
1482
1538
 
1539
+ // Re-run single-string remap: `coerceArgsFromIssues` may have just
1540
+ // unwrapped a JSON-stringified root object, exposing a mislabelled lone
1541
+ // string field the initial pre-pass could not see.
1542
+ const singleStringNormPass = normalizeSingleStringField(json, normalizedArgs);
1543
+ if (singleStringNormPass.changed) {
1544
+ normalizedArgs = singleStringNormPass.value;
1545
+ }
1546
+
1483
1547
  result = validateContext(ctx, normalizedArgs);
1484
1548
  if (result.success) return result.value as ToolCall["arguments"];
1485
1549
  }