@oh-my-pi/pi-ai 18.2.8 → 18.2.9
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 +33 -18
- package/dist/types/auth-gateway/types.d.ts +5 -0
- package/dist/types/auth-storage.d.ts +35 -32
- package/dist/types/index.d.ts +1 -0
- package/dist/types/providers/amazon-bedrock.d.ts +7 -0
- package/dist/types/providers/claude-code-fingerprint.d.ts +22 -3
- package/dist/types/providers/google-gemini-cli.d.ts +0 -2
- package/dist/types/providers/openai-chat-server-schema.d.ts +2 -2
- package/dist/types/usage/claude-api.d.ts +22 -0
- package/dist/types/usage/claude-reset.d.ts +44 -0
- package/dist/types/usage.d.ts +111 -5
- package/dist/types/utils/schema/json-schema-validator.d.ts +5 -2
- package/dist/types/utils/tool-call-loop-guard.d.ts +1 -1
- package/package.json +6 -6
- package/src/auth/sqlite-credential-store.ts +44 -1
- package/src/auth-broker/remote-store.ts +6 -6
- package/src/auth-broker/wire-schemas.ts +14 -0
- package/src/auth-gateway/server.ts +4 -0
- package/src/auth-gateway/types.ts +5 -0
- package/src/auth-storage.ts +263 -140
- package/src/error/flags.ts +10 -0
- package/src/index.ts +1 -0
- package/src/providers/amazon-bedrock.ts +55 -5
- package/src/providers/anthropic.ts +45 -11
- package/src/providers/aws-credentials.ts +124 -11
- package/src/providers/claude-code-fingerprint.ts +55 -3
- package/src/providers/gitlab-duo.ts +20 -4
- package/src/providers/google-gemini-cli.ts +0 -8
- package/src/providers/google-shared.ts +1 -18
- package/src/providers/openai-chat-server-schema.ts +1 -1
- package/src/providers/openai-chat-server.ts +3 -1
- package/src/providers/openai-codex-responses.ts +27 -5
- package/src/providers/openai-completions.ts +122 -19
- package/src/providers/pi-native-server.ts +1 -0
- package/src/registry/oauth/anthropic.ts +2 -3
- package/src/stream.ts +13 -4
- package/src/usage/alibaba-token-plan.ts +7 -1
- package/src/usage/claude-api.ts +66 -0
- package/src/usage/claude-reset.ts +638 -0
- package/src/usage/claude.ts +37 -59
- package/src/usage/kimi.ts +32 -1
- package/src/usage.ts +52 -5
- package/src/utils/schema/json-schema-validator.ts +23 -10
- package/src/utils/tool-call-loop-guard.ts +2 -2
- package/src/utils/validation.ts +145 -50
package/src/utils/validation.ts
CHANGED
|
@@ -17,6 +17,7 @@ import * as AIError from "../error";
|
|
|
17
17
|
import type { Tool, ToolCall } from "../types";
|
|
18
18
|
import { upgradeJsonSchemaTo202012 } from "./schema/draft";
|
|
19
19
|
import {
|
|
20
|
+
getTagSelectedUnionBranch,
|
|
20
21
|
isJsonSchemaValueValid,
|
|
21
22
|
type JsonSchemaValidationIssue,
|
|
22
23
|
validateJsonSchemaValue,
|
|
@@ -665,14 +666,17 @@ function deleteAtSegment(node: unknown, segments: string[], depth: number): unkn
|
|
|
665
666
|
* small and synchronous so validation does not need to compile legacy schemas
|
|
666
667
|
* into another schema language.
|
|
667
668
|
*/
|
|
668
|
-
function branchMatchesSchema(branch: unknown, value: unknown): boolean {
|
|
669
|
-
return isJsonSchemaValueValid(branch, value);
|
|
669
|
+
function branchMatchesSchema(branch: unknown, value: unknown, root: unknown = branch): boolean {
|
|
670
|
+
return isJsonSchemaValueValid(branch, value, root);
|
|
670
671
|
}
|
|
671
672
|
|
|
672
673
|
function normalizeOptionalNullsForSchema(
|
|
673
674
|
schema: unknown,
|
|
674
675
|
value: unknown,
|
|
675
676
|
isRoot = true,
|
|
677
|
+
root: unknown = schema,
|
|
678
|
+
insideContent = false,
|
|
679
|
+
speculativeUnion = false,
|
|
676
680
|
): { value: unknown; changed: boolean } {
|
|
677
681
|
if (value === null || value === undefined) return { value, changed: false };
|
|
678
682
|
if (schema === null || typeof schema !== "object") return { value, changed: false };
|
|
@@ -683,22 +687,64 @@ function normalizeOptionalNullsForSchema(
|
|
|
683
687
|
const branches = schemaObject[keyword];
|
|
684
688
|
if (!Array.isArray(branches)) return { value, changed: false };
|
|
685
689
|
|
|
686
|
-
|
|
687
|
-
|
|
690
|
+
// Prefer an already matching branch before trying repairs against other
|
|
691
|
+
// alternatives. Normalize each match independently: a closed sibling can
|
|
692
|
+
// delete nullable keys and invalidate itself while a later matching branch
|
|
693
|
+
// still requires that data.
|
|
688
694
|
for (const branch of branches) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
695
|
+
if (!branchMatchesSchema(branch, value, root)) continue;
|
|
696
|
+
const normalized = normalizeOptionalNullsForSchema(
|
|
697
|
+
branch,
|
|
698
|
+
value,
|
|
699
|
+
isRoot,
|
|
700
|
+
root,
|
|
701
|
+
insideContent,
|
|
702
|
+
speculativeUnion,
|
|
703
|
+
);
|
|
704
|
+
if (
|
|
705
|
+
branchMatchesSchema(branch, normalized.value, root) &&
|
|
706
|
+
branchMatchesSchema(schemaObject, normalized.value, root)
|
|
707
|
+
) {
|
|
693
708
|
return normalized;
|
|
694
709
|
}
|
|
710
|
+
}
|
|
695
711
|
|
|
696
|
-
|
|
697
|
-
|
|
712
|
+
const selectedBranch = getTagSelectedUnionBranch(branches, value);
|
|
713
|
+
for (const branch of branches) {
|
|
714
|
+
// Only unresolved ancestors restrict descendants. A failed branch
|
|
715
|
+
// with a unique discriminator is still authoritative.
|
|
716
|
+
const normalized = normalizeOptionalNullsForSchema(
|
|
717
|
+
branch,
|
|
718
|
+
value,
|
|
719
|
+
isRoot,
|
|
720
|
+
root,
|
|
721
|
+
insideContent,
|
|
722
|
+
speculativeUnion || selectedBranch !== branch,
|
|
723
|
+
);
|
|
724
|
+
if (!normalized.changed) continue;
|
|
725
|
+
|
|
726
|
+
const branchContext: ValidationContext = {
|
|
727
|
+
kind: "json",
|
|
728
|
+
json: branch,
|
|
729
|
+
root,
|
|
730
|
+
union: schemaObject,
|
|
731
|
+
unionBranches: branches,
|
|
732
|
+
speculativeUnion,
|
|
733
|
+
};
|
|
734
|
+
const result = validateContext(branchContext, normalized.value);
|
|
735
|
+
if (result.success && branchMatchesSchema(branch, normalized.value, root)) return normalized;
|
|
736
|
+
|
|
737
|
+
// Diagnose against the enclosing union to retain authoritative vs.
|
|
738
|
+
// speculative issue provenance, but normalize only this candidate's
|
|
739
|
+
// branch. Failed candidates never escape to a sibling.
|
|
740
|
+
const repaired = runCoercionPasses(branchContext, normalized.value, result, isRoot, insideContent);
|
|
741
|
+
if (repaired.result.success && branchMatchesSchema(branch, repaired.args, root)) {
|
|
742
|
+
return { value: repaired.args, changed: true };
|
|
698
743
|
}
|
|
699
744
|
}
|
|
700
745
|
|
|
701
|
-
|
|
746
|
+
// Never adopt a destructive repair from a branch that still rejects it.
|
|
747
|
+
return { value, changed: false };
|
|
702
748
|
};
|
|
703
749
|
|
|
704
750
|
const anyOfNormalization = normalizeAnyOfLike("anyOf");
|
|
@@ -711,7 +757,14 @@ function normalizeOptionalNullsForSchema(
|
|
|
711
757
|
let changed = false;
|
|
712
758
|
let nextValue: unknown = value;
|
|
713
759
|
for (const branch of schemaObject.allOf) {
|
|
714
|
-
const normalized = normalizeOptionalNullsForSchema(
|
|
760
|
+
const normalized = normalizeOptionalNullsForSchema(
|
|
761
|
+
branch,
|
|
762
|
+
nextValue,
|
|
763
|
+
isRoot,
|
|
764
|
+
root,
|
|
765
|
+
insideContent,
|
|
766
|
+
speculativeUnion,
|
|
767
|
+
);
|
|
715
768
|
if (!normalized.changed) continue;
|
|
716
769
|
nextValue = normalized.value;
|
|
717
770
|
changed = true;
|
|
@@ -728,7 +781,14 @@ function normalizeOptionalNullsForSchema(
|
|
|
728
781
|
let changed = false;
|
|
729
782
|
let nextValue = value;
|
|
730
783
|
for (let i = 0; i < value.length; i += 1) {
|
|
731
|
-
const normalized = normalizeOptionalNullsForSchema(
|
|
784
|
+
const normalized = normalizeOptionalNullsForSchema(
|
|
785
|
+
itemSchema,
|
|
786
|
+
value[i],
|
|
787
|
+
false,
|
|
788
|
+
root,
|
|
789
|
+
insideContent,
|
|
790
|
+
speculativeUnion,
|
|
791
|
+
);
|
|
732
792
|
if (!normalized.changed) continue;
|
|
733
793
|
if (!changed) {
|
|
734
794
|
nextValue = [...value];
|
|
@@ -764,7 +824,7 @@ function normalizeOptionalNullsForSchema(
|
|
|
764
824
|
const currentValue = nextValue[key];
|
|
765
825
|
const isNullish = currentValue === null || currentValue === "null";
|
|
766
826
|
const isInvalidEmptyString =
|
|
767
|
-
currentValue === "" && !required.has(key) && !branchMatchesSchema(propertySchema, currentValue);
|
|
827
|
+
currentValue === "" && !required.has(key) && !branchMatchesSchema(propertySchema, currentValue, root);
|
|
768
828
|
|
|
769
829
|
// Strip null/string "null" from optional fields, and strip empty
|
|
770
830
|
// strings only when the property schema would reject the explicit value.
|
|
@@ -794,7 +854,14 @@ function normalizeOptionalNullsForSchema(
|
|
|
794
854
|
continue;
|
|
795
855
|
}
|
|
796
856
|
}
|
|
797
|
-
const normalized = normalizeOptionalNullsForSchema(
|
|
857
|
+
const normalized = normalizeOptionalNullsForSchema(
|
|
858
|
+
propertySchema,
|
|
859
|
+
currentValue,
|
|
860
|
+
false,
|
|
861
|
+
root,
|
|
862
|
+
insideContent || CONTENT_CARRYING_KEYS.has(key),
|
|
863
|
+
speculativeUnion,
|
|
864
|
+
);
|
|
798
865
|
if (!normalized.changed) continue;
|
|
799
866
|
|
|
800
867
|
if (!changed) {
|
|
@@ -813,7 +880,9 @@ function normalizeOptionalNullsForSchema(
|
|
|
813
880
|
//
|
|
814
881
|
// At the root level unknown null-valued keys stay intact; the
|
|
815
882
|
// post-validation `preserveUnknownRootFields` pass re-attaches root extras.
|
|
816
|
-
|
|
883
|
+
// They also stay intact while guessing a union branch because another
|
|
884
|
+
// candidate may require the nullable data.
|
|
885
|
+
if (!isRoot && !speculativeUnion && schemaObject.additionalProperties === false) {
|
|
817
886
|
const knownKeys = new Set(Object.keys(properties));
|
|
818
887
|
for (const key of Object.keys(nextValue)) {
|
|
819
888
|
if (knownKeys.has(key)) continue;
|
|
@@ -864,26 +933,15 @@ function normalizeEnumStringWhitespace(
|
|
|
864
933
|
return normalizeEnumStringWhitespace(resolved, value, root, new Set([...refs, ref]));
|
|
865
934
|
}
|
|
866
935
|
|
|
867
|
-
const branchMatches = (branch: unknown, candidate: unknown): boolean => {
|
|
868
|
-
if (branch !== null && typeof branch === "object") {
|
|
869
|
-
const branchRef = (branch as Record<string, unknown>).$ref;
|
|
870
|
-
if (typeof branchRef === "string" && !refs.has(branchRef)) {
|
|
871
|
-
const resolved = resolveLocalJsonSchemaRef(root, branchRef);
|
|
872
|
-
if (resolved !== undefined) return branchMatchesSchema(resolved, candidate);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
return branchMatchesSchema(branch, candidate);
|
|
876
|
-
};
|
|
877
|
-
|
|
878
936
|
const normalizeAnyOfLike = (keyword: "anyOf" | "oneOf"): { value: unknown; changed: boolean } => {
|
|
879
937
|
const branches = schemaObject[keyword];
|
|
880
938
|
if (!Array.isArray(branches)) return { value, changed: false };
|
|
881
|
-
if (branches.some(branch =>
|
|
939
|
+
if (branches.some(branch => branchMatchesSchema(branch, value, root))) return { value, changed: false };
|
|
882
940
|
|
|
883
941
|
for (const branch of branches) {
|
|
884
942
|
const normalized = normalizeEnumStringWhitespace(branch, value, root, refs);
|
|
885
943
|
if (!normalized.changed) continue;
|
|
886
|
-
if (
|
|
944
|
+
if (branchMatchesSchema(branch, normalized.value, root)) return normalized;
|
|
887
945
|
}
|
|
888
946
|
return { value, changed: false };
|
|
889
947
|
};
|
|
@@ -1046,7 +1104,11 @@ function trimIdentifierStringLeaf(input: unknown): unknown {
|
|
|
1046
1104
|
* key matches {@link IDENTIFIER_STRING_KEYS}. Runs by property name only so it
|
|
1047
1105
|
* fires uniformly across ArkType and plain JSON Schema tools.
|
|
1048
1106
|
*/
|
|
1049
|
-
function normalizeIdentifierStringWhitespace(
|
|
1107
|
+
function normalizeIdentifierStringWhitespace(
|
|
1108
|
+
value: unknown,
|
|
1109
|
+
insideContent = false,
|
|
1110
|
+
): { value: unknown; changed: boolean } {
|
|
1111
|
+
if (insideContent) return { value, changed: false };
|
|
1050
1112
|
if (Array.isArray(value)) {
|
|
1051
1113
|
let changed = false;
|
|
1052
1114
|
let next = value;
|
|
@@ -1650,6 +1712,10 @@ type ValidationContext =
|
|
|
1650
1712
|
| {
|
|
1651
1713
|
kind: "json";
|
|
1652
1714
|
json: Record<string, unknown>;
|
|
1715
|
+
root?: unknown;
|
|
1716
|
+
union?: Record<string, unknown>;
|
|
1717
|
+
unionBranches?: readonly unknown[];
|
|
1718
|
+
speculativeUnion?: boolean;
|
|
1653
1719
|
};
|
|
1654
1720
|
|
|
1655
1721
|
/**
|
|
@@ -1680,9 +1746,12 @@ function preserveUnknownRootFields(input: unknown, parsed: unknown): unknown {
|
|
|
1680
1746
|
return { ...input, ...parsed };
|
|
1681
1747
|
}
|
|
1682
1748
|
|
|
1683
|
-
function flattenJsonSchemaIssues(
|
|
1749
|
+
function flattenJsonSchemaIssues(
|
|
1750
|
+
issues: ReadonlyArray<JsonSchemaValidationIssue>,
|
|
1751
|
+
speculativeUnion = false,
|
|
1752
|
+
): FlatIssue[] {
|
|
1684
1753
|
return issues.map(issue => {
|
|
1685
|
-
const unionBranch = issue.fromUnionBranch === true;
|
|
1754
|
+
const unionBranch = speculativeUnion || issue.fromUnionBranch === true;
|
|
1686
1755
|
if (issue.keyword === "additionalProperties") {
|
|
1687
1756
|
return {
|
|
1688
1757
|
keyword: "unrecognized",
|
|
@@ -1723,11 +1792,11 @@ function validateContext(ctx: ValidationContext, value: unknown): ContextValidat
|
|
|
1723
1792
|
};
|
|
1724
1793
|
}
|
|
1725
1794
|
|
|
1726
|
-
const result = validateJsonSchemaValue(ctx.json, value);
|
|
1795
|
+
const result = validateJsonSchemaValue(ctx.union ?? ctx.json, value, ctx.root);
|
|
1727
1796
|
if (result.success) return { success: true, value };
|
|
1728
1797
|
return {
|
|
1729
1798
|
success: false,
|
|
1730
|
-
flatIssues: flattenJsonSchemaIssues(result.issues),
|
|
1799
|
+
flatIssues: flattenJsonSchemaIssues(result.issues, ctx.speculativeUnion),
|
|
1731
1800
|
messages: result.issues.map(issue => ` - ${formatIssuePath(issue.path)}: ${issue.message}`),
|
|
1732
1801
|
};
|
|
1733
1802
|
}
|
|
@@ -2065,27 +2134,28 @@ export function validateToolArguments(tool: Tool, toolCall: ToolCall): ToolCall[
|
|
|
2065
2134
|
}
|
|
2066
2135
|
|
|
2067
2136
|
/**
|
|
2068
|
-
* Runs up to {@link MAX_COERCION_PASSES}
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2071
|
-
*
|
|
2137
|
+
* Runs up to {@link MAX_COERCION_PASSES} repair rounds, re-applying the schema
|
|
2138
|
+
* normalizations even when issue-driven coercion cannot change the value.
|
|
2139
|
+
* Later passes (such as identifier trimming) can make an earlier branch-local
|
|
2140
|
+
* repair viable. Stop once neither coercion nor normalization makes progress.
|
|
2072
2141
|
*/
|
|
2073
2142
|
function runCoercionPasses(
|
|
2074
2143
|
ctx: ValidationContext,
|
|
2075
2144
|
args: unknown,
|
|
2076
2145
|
initial: ContextValidationResult,
|
|
2146
|
+
isRoot = true,
|
|
2147
|
+
insideContent = false,
|
|
2077
2148
|
): { args: unknown; result: ContextValidationResult; changed: boolean } {
|
|
2078
2149
|
const { json } = ctx;
|
|
2150
|
+
const root = ctx.kind === "json" ? (ctx.root ?? json) : json;
|
|
2079
2151
|
let normalizedArgs = args;
|
|
2080
2152
|
let result = initial;
|
|
2081
2153
|
let changed = false;
|
|
2082
2154
|
for (let pass = 0; pass < MAX_COERCION_PASSES; pass += 1) {
|
|
2083
2155
|
if (result.success) break;
|
|
2084
2156
|
const coercion = coerceArgsFromIssues(normalizedArgs, result.flatIssues);
|
|
2085
|
-
|
|
2086
|
-
|
|
2157
|
+
let passChanged = coercion.changed;
|
|
2087
2158
|
normalizedArgs = coercion.value;
|
|
2088
|
-
changed = true;
|
|
2089
2159
|
|
|
2090
2160
|
// `coerceArgsFromIssues` may have just parsed a JSON-string container at
|
|
2091
2161
|
// the root or a nested field, exposing double-encoded keys the initial
|
|
@@ -2094,21 +2164,36 @@ function runCoercionPasses(
|
|
|
2094
2164
|
const keyNormalizationPass = normalizeDoubleEncodedKeys(normalizedArgs);
|
|
2095
2165
|
if (keyNormalizationPass.changed) {
|
|
2096
2166
|
normalizedArgs = keyNormalizationPass.value;
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2167
|
+
passChanged = true;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
const speculativeUnion =
|
|
2171
|
+
ctx.kind === "json" &&
|
|
2172
|
+
(ctx.speculativeUnion === true ||
|
|
2173
|
+
(ctx.unionBranches !== undefined && getTagSelectedUnionBranch(ctx.unionBranches, normalizedArgs) !== json));
|
|
2174
|
+
const nullNormalization = normalizeOptionalNullsForSchema(
|
|
2175
|
+
json,
|
|
2176
|
+
normalizedArgs,
|
|
2177
|
+
isRoot,
|
|
2178
|
+
root,
|
|
2179
|
+
insideContent,
|
|
2180
|
+
speculativeUnion,
|
|
2181
|
+
);
|
|
2100
2182
|
if (nullNormalization.changed) {
|
|
2101
2183
|
normalizedArgs = nullNormalization.value;
|
|
2184
|
+
passChanged = true;
|
|
2102
2185
|
}
|
|
2103
2186
|
|
|
2104
|
-
const enumStringNormalizationPass = normalizeEnumStringWhitespace(json, normalizedArgs);
|
|
2187
|
+
const enumStringNormalizationPass = normalizeEnumStringWhitespace(json, normalizedArgs, root);
|
|
2105
2188
|
if (enumStringNormalizationPass.changed) {
|
|
2106
2189
|
normalizedArgs = enumStringNormalizationPass.value;
|
|
2190
|
+
passChanged = true;
|
|
2107
2191
|
}
|
|
2108
2192
|
|
|
2109
|
-
const identifierStringNormalizationPass = normalizeIdentifierStringWhitespace(normalizedArgs);
|
|
2193
|
+
const identifierStringNormalizationPass = normalizeIdentifierStringWhitespace(normalizedArgs, insideContent);
|
|
2110
2194
|
if (identifierStringNormalizationPass.changed) {
|
|
2111
2195
|
normalizedArgs = identifierStringNormalizationPass.value;
|
|
2196
|
+
passChanged = true;
|
|
2112
2197
|
}
|
|
2113
2198
|
|
|
2114
2199
|
// Re-run the union-string coercion because `coerceArgsFromIssues` may
|
|
@@ -2118,21 +2203,31 @@ function runCoercionPasses(
|
|
|
2118
2203
|
const stringEncodedArrayNormPass = normalizeStringEncodedArrayUnions(json, normalizedArgs);
|
|
2119
2204
|
if (stringEncodedArrayNormPass.changed) {
|
|
2120
2205
|
normalizedArgs = stringEncodedArrayNormPass.value;
|
|
2206
|
+
passChanged = true;
|
|
2121
2207
|
}
|
|
2122
2208
|
|
|
2123
|
-
const identifierStringNormalizationAfterArrayPass = normalizeIdentifierStringWhitespace(
|
|
2209
|
+
const identifierStringNormalizationAfterArrayPass = normalizeIdentifierStringWhitespace(
|
|
2210
|
+
normalizedArgs,
|
|
2211
|
+
insideContent,
|
|
2212
|
+
);
|
|
2124
2213
|
if (identifierStringNormalizationAfterArrayPass.changed) {
|
|
2125
2214
|
normalizedArgs = identifierStringNormalizationAfterArrayPass.value;
|
|
2215
|
+
passChanged = true;
|
|
2126
2216
|
}
|
|
2127
2217
|
|
|
2128
2218
|
// Re-run single-string remap: `coerceArgsFromIssues` may have just
|
|
2129
2219
|
// unwrapped a JSON-stringified root object, exposing a mislabelled lone
|
|
2130
2220
|
// string field the initial pre-pass could not see.
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2221
|
+
if (isRoot && !(ctx.kind === "json" && ctx.union)) {
|
|
2222
|
+
const singleStringNormPass = normalizeSingleStringField(json, normalizedArgs);
|
|
2223
|
+
if (singleStringNormPass.changed) {
|
|
2224
|
+
normalizedArgs = singleStringNormPass.value;
|
|
2225
|
+
passChanged = true;
|
|
2226
|
+
}
|
|
2134
2227
|
}
|
|
2135
2228
|
|
|
2229
|
+
if (!passChanged) break;
|
|
2230
|
+
changed = true;
|
|
2136
2231
|
result = validateContext(ctx, normalizedArgs);
|
|
2137
2232
|
}
|
|
2138
2233
|
return { args: normalizedArgs, result, changed };
|