@oh-my-pi/pi-ai 17.0.0 → 17.0.2
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 +28 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +13 -0
- package/dist/types/auth-storage.d.ts +2 -0
- package/dist/types/error/auth-classify.d.ts +2 -0
- package/dist/types/error/rate-limit.d.ts +9 -5
- package/dist/types/providers/cursor.d.ts +2 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +9 -3
- package/dist/types/providers/openai-shared.d.ts +12 -6
- package/dist/types/providers/transform-messages.d.ts +5 -9
- package/dist/types/types.d.ts +6 -1
- package/dist/types/utils/empty-completion-retry.d.ts +3 -3
- package/dist/types/utils/schema/normalize.d.ts +3 -1
- package/package.json +4 -4
- package/src/auth-broker/wire-schemas.ts +1 -0
- package/src/auth-retry.ts +2 -2
- package/src/auth-storage.ts +120 -79
- package/src/dialect/owned-stream.ts +11 -0
- package/src/dialect/thinking.ts +186 -16
- package/src/error/auth-classify.ts +13 -0
- package/src/error/flags.ts +2 -2
- package/src/error/rate-limit.ts +28 -9
- package/src/providers/__tests__/kimi-code-thinking.test.ts +62 -7
- package/src/providers/anthropic.ts +82 -25
- package/src/providers/cursor.ts +53 -30
- package/src/providers/gitlab-duo-workflow.ts +6 -2
- package/src/providers/kimi.ts +1 -4
- package/src/providers/openai-codex/request-transformer.ts +12 -3
- package/src/providers/openai-codex-responses.ts +22 -4
- package/src/providers/openai-completions.ts +28 -22
- package/src/providers/openai-responses.ts +5 -1
- package/src/providers/openai-shared.ts +66 -14
- package/src/providers/transform-messages.ts +171 -1
- package/src/stream.ts +2 -0
- package/src/types.ts +9 -1
- package/src/utils/empty-completion-retry.ts +6 -3
- package/src/utils/leaked-thinking-stream.ts +19 -1
- package/src/utils/proxy.ts +1 -1
- package/src/utils/schema/CONSTRAINTS.md +1 -0
- package/src/utils/schema/fields.ts +3 -0
- package/src/utils/schema/normalize.ts +94 -24
- package/src/utils.ts +4 -1
|
@@ -26,9 +26,11 @@ import { enter, epochNext, exit, once, stamp } from "./stamps";
|
|
|
26
26
|
import { isJsonObject, isJsonObjectEmpty, type JsonObject } from "./types";
|
|
27
27
|
import { decontaminateZodInstance } from "./zod-decontaminate";
|
|
28
28
|
|
|
29
|
-
export type ResidualSchemaIncompatibility = "type-array" | "type-null" | "nullable" | "combiners";
|
|
29
|
+
export type ResidualSchemaIncompatibility = "type-array" | "type-null" | "nullable" | "combiners" | "not";
|
|
30
30
|
|
|
31
31
|
export interface NormalizeSchemaOptions {
|
|
32
|
+
/** Coerce JSON Schema boolean subschemas for providers whose wire cannot encode them. */
|
|
33
|
+
coerceBooleanSubschemas?: boolean;
|
|
32
34
|
unsupportedFields: (key: string) => boolean;
|
|
33
35
|
normalizeFieldNames: boolean;
|
|
34
36
|
collapseNullFields: boolean;
|
|
@@ -55,7 +57,14 @@ export interface NormalizeSchemaOptions {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
interface NormalizeSchemaWalkOptions extends NormalizeSchemaOptions {
|
|
58
|
-
|
|
60
|
+
insideSchemaMap: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* True when the value currently being walked occupies a JSON Schema
|
|
63
|
+
* *subschema* slot (root, combiner branch, `items`, a property value, …).
|
|
64
|
+
* Only then is a bare `true`/`false` a boolean subschema to coerce; in a
|
|
65
|
+
* keyword slot (`nullable`, `enum` entries, `additionalProperties`) it stays.
|
|
66
|
+
*/
|
|
67
|
+
booleanIsSubschema: boolean;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
interface ResidualIncompatibilityChecks {
|
|
@@ -63,6 +72,7 @@ interface ResidualIncompatibilityChecks {
|
|
|
63
72
|
typeNull: boolean;
|
|
64
73
|
nullable: boolean;
|
|
65
74
|
combiners: boolean;
|
|
75
|
+
not: boolean;
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
const SNAKE_TO_CAMEL_RENAMES = new Map<string, string>([
|
|
@@ -75,6 +85,42 @@ const SNAKE_TO_CAMEL_RENAMES = new Map<string, string>([
|
|
|
75
85
|
const JSON_SCHEMA_COMBINERS = ["anyOf", "oneOf"] as const;
|
|
76
86
|
const CCA_FORBIDDEN_COMBINERS = new Set(["anyOf", "oneOf", "allOf"]);
|
|
77
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Keywords whose value is a single subschema (draft 2020-12). A bare `true` /
|
|
90
|
+
* `false` in one of these slots is a boolean subschema to coerce (issue #5604).
|
|
91
|
+
*/
|
|
92
|
+
const SUBSCHEMA_VALUE_KEYS: Record<string, true> = {
|
|
93
|
+
items: true,
|
|
94
|
+
additionalItems: true,
|
|
95
|
+
unevaluatedItems: true,
|
|
96
|
+
not: true,
|
|
97
|
+
if: true,
|
|
98
|
+
// biome-ignore lint/suspicious/noThenProperty: JSON Schema keyword
|
|
99
|
+
then: true,
|
|
100
|
+
else: true,
|
|
101
|
+
contains: true,
|
|
102
|
+
propertyNames: true,
|
|
103
|
+
contentSchema: true,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Keywords whose value is an array of subschemas. */
|
|
107
|
+
const SUBSCHEMA_ARRAY_KEYS: Record<string, true> = {
|
|
108
|
+
anyOf: true,
|
|
109
|
+
oneOf: true,
|
|
110
|
+
allOf: true,
|
|
111
|
+
prefixItems: true,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Keywords whose object value maps arbitrary names to subschemas. */
|
|
115
|
+
const SUBSCHEMA_MAP_KEYS: Record<string, true> = {
|
|
116
|
+
properties: true,
|
|
117
|
+
patternProperties: true,
|
|
118
|
+
dependencies: true,
|
|
119
|
+
dependentSchemas: true,
|
|
120
|
+
$defs: true,
|
|
121
|
+
definitions: true,
|
|
122
|
+
};
|
|
123
|
+
|
|
78
124
|
const CLOUD_CODE_ASSIST_CLAUDE_FALLBACK_SCHEMA = {
|
|
79
125
|
type: "object",
|
|
80
126
|
properties: {},
|
|
@@ -236,6 +282,15 @@ function normalizeSchemaNode(value: unknown, options: NormalizeSchemaWalkOptions
|
|
|
236
282
|
exit(value);
|
|
237
283
|
}
|
|
238
284
|
}
|
|
285
|
+
if (typeof value === "boolean") {
|
|
286
|
+
// A bare boolean is a JSON Schema subschema only in a subschema slot.
|
|
287
|
+
// The Google/CCA protobuf Schema wire has no representation for it
|
|
288
|
+
// (issue #5604): `true` accepts anything -> `{}`, `false` accepts nothing
|
|
289
|
+
// -> `{ not: {} }`. In a keyword slot (`nullable`, `enum` entry, …) a
|
|
290
|
+
// boolean is a plain value and is left untouched.
|
|
291
|
+
if (!options.coerceBooleanSubschemas || !options.booleanIsSubschema) return value;
|
|
292
|
+
return value ? {} : { not: {} };
|
|
293
|
+
}
|
|
239
294
|
if (!isJsonObject(value)) {
|
|
240
295
|
return value;
|
|
241
296
|
}
|
|
@@ -250,8 +305,8 @@ function normalizeSchemaNode(value: unknown, options: NormalizeSchemaWalkOptions
|
|
|
250
305
|
}
|
|
251
306
|
|
|
252
307
|
function normalizeSchemaObjectNode(value: JsonObject, options: NormalizeSchemaWalkOptions): unknown {
|
|
253
|
-
let obj = options.normalizeFieldNames && !options.
|
|
254
|
-
if (options.collapseNullFields && !options.
|
|
308
|
+
let obj = options.normalizeFieldNames && !options.insideSchemaMap ? applySnakeCaseRenames(value) : value;
|
|
309
|
+
if (options.collapseNullFields && !options.insideSchemaMap) {
|
|
255
310
|
obj = preHandleNullFields(obj);
|
|
256
311
|
}
|
|
257
312
|
const result: JsonObject = {};
|
|
@@ -298,14 +353,18 @@ function normalizeSchemaObjectNode(value: JsonObject, options: NormalizeSchemaWa
|
|
|
298
353
|
for (const key in obj) {
|
|
299
354
|
if (!Object.hasOwn(obj, key) || key === combiner || outHasOwn(result, key)) continue;
|
|
300
355
|
const entry = obj[key];
|
|
301
|
-
if (!options.
|
|
356
|
+
if (!options.insideSchemaMap && options.unsupportedFields(key)) {
|
|
302
357
|
spill = pushStrippedDescriptionEntry(spill, key, entry, options);
|
|
303
358
|
continue;
|
|
304
359
|
}
|
|
305
360
|
if (options.stripNullableKeyword && key === "nullable") continue;
|
|
306
361
|
result[key] = normalizeSchemaNode(entry, {
|
|
307
362
|
...options,
|
|
308
|
-
|
|
363
|
+
insideSchemaMap: !options.insideSchemaMap && Object.hasOwn(SUBSCHEMA_MAP_KEYS, key),
|
|
364
|
+
booleanIsSubschema:
|
|
365
|
+
options.insideSchemaMap ||
|
|
366
|
+
Object.hasOwn(SUBSCHEMA_VALUE_KEYS, key) ||
|
|
367
|
+
Object.hasOwn(SUBSCHEMA_ARRAY_KEYS, key),
|
|
309
368
|
});
|
|
310
369
|
}
|
|
311
370
|
applyDescriptionSpill(result, spill, options);
|
|
@@ -316,7 +375,7 @@ function normalizeSchemaObjectNode(value: JsonObject, options: NormalizeSchemaWa
|
|
|
316
375
|
for (const key in obj) {
|
|
317
376
|
if (!Object.hasOwn(obj, key)) continue;
|
|
318
377
|
const entry = obj[key];
|
|
319
|
-
if (!options.
|
|
378
|
+
if (!options.insideSchemaMap && options.unsupportedFields(key)) {
|
|
320
379
|
spill = pushStrippedDescriptionEntry(spill, key, entry, options);
|
|
321
380
|
continue;
|
|
322
381
|
}
|
|
@@ -327,7 +386,11 @@ function normalizeSchemaObjectNode(value: JsonObject, options: NormalizeSchemaWa
|
|
|
327
386
|
}
|
|
328
387
|
result[key] = normalizeSchemaNode(entry, {
|
|
329
388
|
...options,
|
|
330
|
-
|
|
389
|
+
insideSchemaMap: !options.insideSchemaMap && Object.hasOwn(SUBSCHEMA_MAP_KEYS, key),
|
|
390
|
+
booleanIsSubschema:
|
|
391
|
+
options.insideSchemaMap ||
|
|
392
|
+
Object.hasOwn(SUBSCHEMA_VALUE_KEYS, key) ||
|
|
393
|
+
Object.hasOwn(SUBSCHEMA_ARRAY_KEYS, key),
|
|
331
394
|
});
|
|
332
395
|
}
|
|
333
396
|
|
|
@@ -835,6 +898,7 @@ function createResidualIncompatibilityChecks(
|
|
|
835
898
|
typeNull: false,
|
|
836
899
|
nullable: false,
|
|
837
900
|
combiners: false,
|
|
901
|
+
not: false,
|
|
838
902
|
};
|
|
839
903
|
for (const check of checks) {
|
|
840
904
|
switch (check) {
|
|
@@ -847,6 +911,9 @@ function createResidualIncompatibilityChecks(
|
|
|
847
911
|
case "nullable":
|
|
848
912
|
result.nullable = true;
|
|
849
913
|
break;
|
|
914
|
+
case "not":
|
|
915
|
+
result.not = true;
|
|
916
|
+
break;
|
|
850
917
|
case "combiners":
|
|
851
918
|
result.combiners = true;
|
|
852
919
|
break;
|
|
@@ -859,10 +926,11 @@ function hasResidualSchemaIncompatibilities(
|
|
|
859
926
|
value: unknown,
|
|
860
927
|
checks: ResidualIncompatibilityChecks,
|
|
861
928
|
epoch: number = epochNext(),
|
|
929
|
+
insideSchemaMap = false,
|
|
862
930
|
): boolean {
|
|
863
931
|
if (Array.isArray(value)) {
|
|
864
932
|
if (!once(value, epoch)) return false;
|
|
865
|
-
return value.some(entry => hasResidualSchemaIncompatibilities(entry, checks, epoch));
|
|
933
|
+
return value.some(entry => hasResidualSchemaIncompatibilities(entry, checks, epoch, insideSchemaMap));
|
|
866
934
|
}
|
|
867
935
|
if (!isJsonObject(value)) {
|
|
868
936
|
return false;
|
|
@@ -874,14 +942,22 @@ function hasResidualSchemaIncompatibilities(
|
|
|
874
942
|
if (checks.typeArray && Array.isArray(value.type)) return true;
|
|
875
943
|
if (checks.typeNull && value.type === "null") return true;
|
|
876
944
|
if (checks.nullable && Object.hasOwn(value, "nullable")) return true;
|
|
877
|
-
if (checks.
|
|
945
|
+
if (!insideSchemaMap && checks.not && Object.hasOwn(value, "not")) return true;
|
|
946
|
+
if (!insideSchemaMap && checks.combiners) {
|
|
878
947
|
for (const combiner of CCA_FORBIDDEN_COMBINERS) {
|
|
879
948
|
if (Array.isArray(value[combiner])) return true;
|
|
880
949
|
}
|
|
881
950
|
}
|
|
882
951
|
for (const k in value) {
|
|
883
952
|
if (!Object.hasOwn(value, k)) continue;
|
|
884
|
-
if (
|
|
953
|
+
if (
|
|
954
|
+
hasResidualSchemaIncompatibilities(
|
|
955
|
+
value[k],
|
|
956
|
+
checks,
|
|
957
|
+
epoch,
|
|
958
|
+
!insideSchemaMap && Object.hasOwn(SUBSCHEMA_MAP_KEYS, k),
|
|
959
|
+
)
|
|
960
|
+
) {
|
|
885
961
|
return true;
|
|
886
962
|
}
|
|
887
963
|
}
|
|
@@ -894,7 +970,8 @@ export function normalizeSchema(value: unknown, options: NormalizeSchemaOptions)
|
|
|
894
970
|
const dereferenced = dereferenceJsonSchema(upgraded);
|
|
895
971
|
let normalized = normalizeSchemaNode(dereferenced, {
|
|
896
972
|
...options,
|
|
897
|
-
|
|
973
|
+
insideSchemaMap: false,
|
|
974
|
+
booleanIsSubschema: true,
|
|
898
975
|
});
|
|
899
976
|
if (options.stripResidualCombinersFixpoint) {
|
|
900
977
|
normalized = stripResidualCombiners(normalized);
|
|
@@ -916,6 +993,7 @@ export function normalizeSchema(value: unknown, options: NormalizeSchemaOptions)
|
|
|
916
993
|
|
|
917
994
|
export function normalizeSchemaForGoogle(value: unknown): unknown {
|
|
918
995
|
return normalizeSchema(value, {
|
|
996
|
+
coerceBooleanSubschemas: true,
|
|
919
997
|
unsupportedFields: isGoogleUnsupportedSchemaField,
|
|
920
998
|
normalizeFieldNames: true,
|
|
921
999
|
collapseNullFields: true,
|
|
@@ -937,6 +1015,7 @@ export function normalizeSchemaForGoogle(value: unknown): unknown {
|
|
|
937
1015
|
|
|
938
1016
|
export function normalizeSchemaForCCA(value: unknown): unknown {
|
|
939
1017
|
return normalizeSchema(value, {
|
|
1018
|
+
coerceBooleanSubschemas: true,
|
|
940
1019
|
unsupportedFields: isGoogleUnsupportedSchemaField,
|
|
941
1020
|
normalizeFieldNames: true,
|
|
942
1021
|
collapseNullFields: false,
|
|
@@ -953,7 +1032,7 @@ export function normalizeSchemaForCCA(value: unknown): unknown {
|
|
|
953
1032
|
inferTypeForBareEnum: true,
|
|
954
1033
|
dropNonScalarEnum: false,
|
|
955
1034
|
foldOneOfIntoAnyOf: false,
|
|
956
|
-
rejectResidualIncompatibilities: ["type-array", "type-null", "nullable", "combiners"],
|
|
1035
|
+
rejectResidualIncompatibilities: ["type-array", "type-null", "nullable", "combiners", "not"],
|
|
957
1036
|
validateAndFallback: { fallback: CLOUD_CODE_ASSIST_CLAUDE_FALLBACK_SCHEMA },
|
|
958
1037
|
});
|
|
959
1038
|
}
|
|
@@ -1031,15 +1110,6 @@ export function normalizeSchemaForMoonshot(value: unknown): unknown {
|
|
|
1031
1110
|
// Ollama — Go schema parser compatibility
|
|
1032
1111
|
// ---------------------------------------------------------------------------
|
|
1033
1112
|
|
|
1034
|
-
const OLLAMA_SCHEMA_ARRAY_KEYS = new Set(["anyOf", "oneOf", "allOf", "prefixItems"]);
|
|
1035
|
-
const OLLAMA_SCHEMA_MAP_KEYS = new Set([
|
|
1036
|
-
"properties",
|
|
1037
|
-
"patternProperties",
|
|
1038
|
-
"dependencies",
|
|
1039
|
-
"dependentSchemas",
|
|
1040
|
-
"$defs",
|
|
1041
|
-
"definitions",
|
|
1042
|
-
]);
|
|
1043
1113
|
const OLLAMA_SCHEMA_VALUE_KEYS = new Set([
|
|
1044
1114
|
"items",
|
|
1045
1115
|
"additionalItems",
|
|
@@ -1121,7 +1191,7 @@ export function sanitizeSchemaForOllama(schema: JsonObject): JsonObject {
|
|
|
1121
1191
|
}
|
|
1122
1192
|
|
|
1123
1193
|
let next = child;
|
|
1124
|
-
if (
|
|
1194
|
+
if (Object.hasOwn(SUBSCHEMA_MAP_KEYS, key) && isJsonObject(child)) {
|
|
1125
1195
|
let mapChanged = false;
|
|
1126
1196
|
const mapOutput: JsonObject = {};
|
|
1127
1197
|
for (const childKey in child) {
|
|
@@ -1132,7 +1202,7 @@ export function sanitizeSchemaForOllama(schema: JsonObject): JsonObject {
|
|
|
1132
1202
|
mapOutput[childKey] = normalizedChild;
|
|
1133
1203
|
}
|
|
1134
1204
|
next = mapChanged ? mapOutput : child;
|
|
1135
|
-
} else if (
|
|
1205
|
+
} else if (Object.hasOwn(SUBSCHEMA_ARRAY_KEYS, key) && Array.isArray(child)) {
|
|
1136
1206
|
let arrayChanged = false;
|
|
1137
1207
|
const arrayOutput = child.map(item => {
|
|
1138
1208
|
const normalizedItem = normalizeNode(item);
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import type { ResponseInput, ResponseInputItem } from "./providers/openai-responses-wire";
|
|
3
|
+
import { redactSensitiveCredentials } from "./providers/transform-messages";
|
|
3
4
|
import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } from "./types";
|
|
4
5
|
|
|
5
6
|
type OpenAIResponsesReplayItem = ResponseInput[number];
|
|
@@ -9,7 +10,9 @@ export { isRecord } from "@oh-my-pi/pi-utils";
|
|
|
9
10
|
export function normalizeSystemPrompts(systemPrompt: readonly string[] | string | undefined | null): string[] {
|
|
10
11
|
if (systemPrompt === undefined || systemPrompt === null) return [];
|
|
11
12
|
const prompts = Array.isArray(systemPrompt) ? systemPrompt : typeof systemPrompt === "string" ? [systemPrompt] : [];
|
|
12
|
-
return prompts
|
|
13
|
+
return prompts
|
|
14
|
+
.map(prompt => redactSensitiveCredentials(prompt.toWellFormed()))
|
|
15
|
+
.filter(prompt => prompt.trim().length > 0);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export function normalizeToolCallId(id: string): string {
|