@oh-my-pi/pi-ai 18.2.0 → 18.2.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 +53 -0
- package/README.md +2 -0
- package/dist/types/auth/sqlite-credential-store.d.ts +2 -1
- package/dist/types/auth-broker/remote-store.d.ts +17 -0
- package/dist/types/auth-gateway/index.d.ts +1 -0
- package/dist/types/auth-gateway/session-state.d.ts +118 -0
- package/dist/types/auth-storage.d.ts +17 -0
- package/dist/types/error/body-error.d.ts +15 -0
- package/dist/types/error/flags.d.ts +16 -0
- package/dist/types/error/index.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/oneshot-retry.d.ts +6 -0
- package/dist/types/provider-session-state.d.ts +46 -0
- package/dist/types/providers/amazon-bedrock.d.ts +3 -0
- package/dist/types/providers/aws-sigv4.d.ts +12 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +27 -0
- package/dist/types/providers/openai-responses.d.ts +15 -0
- package/dist/types/providers/openai-shared.d.ts +20 -3
- package/dist/types/registry/oauth/perplexity.d.ts +1 -7
- package/dist/types/registry/oauth/types.d.ts +8 -0
- package/dist/types/stream.d.ts +2 -0
- package/dist/types/types.d.ts +3 -1
- package/dist/types/usage/openai-codex.d.ts +3 -1
- package/dist/types/usage.d.ts +11 -1
- package/dist/types/utils/block-symbols.d.ts +36 -0
- package/dist/types/utils/openai-http.d.ts +2 -0
- package/dist/types/utils/retry-after.d.ts +2 -0
- package/dist/types/utils/schema/wire.d.ts +4 -5
- package/dist/types/utils.d.ts +9 -0
- package/package.json +6 -6
- package/src/auth/sqlite-credential-store.ts +8 -33
- package/src/auth-broker/remote-store.ts +73 -8
- package/src/auth-broker/wire-schemas.ts +1 -0
- package/src/auth-gateway/index.ts +1 -0
- package/src/auth-gateway/server.ts +186 -74
- package/src/auth-gateway/session-state.ts +312 -0
- package/src/auth-storage.ts +146 -15
- package/src/error/body-error.ts +310 -0
- package/src/error/flags.ts +63 -13
- package/src/error/index.ts +1 -0
- package/src/error/retryable.ts +2 -0
- package/src/index.ts +1 -0
- package/src/oneshot-retry.ts +13 -3
- package/src/provider-session-state.ts +56 -0
- package/src/providers/amazon-bedrock.ts +20 -3
- package/src/providers/anthropic-messages-server.ts +104 -23
- package/src/providers/anthropic-signature.ts +5 -2
- package/src/providers/anthropic.ts +101 -15
- package/src/providers/aws-sigv4.ts +16 -5
- package/src/providers/cursor.ts +60 -10
- package/src/providers/devin.ts +82 -28
- package/src/providers/openai-chat-server.ts +4 -0
- package/src/providers/openai-codex/request-transformer.ts +36 -0
- package/src/providers/openai-codex-responses.ts +35 -12
- package/src/providers/openai-completions.ts +49 -12
- package/src/providers/openai-reasoning-fallback.ts +6 -6
- package/src/providers/openai-responses-server.ts +2 -1
- package/src/providers/openai-responses.ts +52 -4
- package/src/providers/openai-shared.ts +199 -51
- package/src/registry/oauth/perplexity.ts +94 -28
- package/src/registry/oauth/types.ts +9 -0
- package/src/stream.ts +23 -2
- package/src/types.ts +3 -0
- package/src/usage/claude.ts +33 -0
- package/src/usage/google-antigravity.ts +8 -2
- package/src/usage/openai-codex.ts +94 -11
- package/src/usage.ts +8 -1
- package/src/utils/block-symbols.ts +57 -0
- package/src/utils/http-inspector.ts +20 -0
- package/src/utils/openai-http.ts +39 -3
- package/src/utils/retry-after.ts +12 -0
- package/src/utils/schema/normalize.ts +3 -3
- package/src/utils/schema/stamps.ts +33 -45
- package/src/utils/schema/wire.ts +9 -7
- package/src/utils.ts +67 -22
|
@@ -187,7 +187,7 @@ function collectMessageParts(error: unknown, captured: CapturedHttpErrorResponse
|
|
|
187
187
|
* the same rejection with `Supported values are: …`, so value lists count too.
|
|
188
188
|
*/
|
|
189
189
|
const REASONING_EFFORT_FIELD_PATTERN =
|
|
190
|
-
/reasoning[_. ]effort|reasoning value|(?:valid|supported|allowed) (?:levels?|values?)/i;
|
|
190
|
+
/reasoning[_. ]?effort|reasoning value|(?:valid|supported|allowed) (?:levels?|values?)/i;
|
|
191
191
|
|
|
192
192
|
function mentionsReasoningEffort(error: unknown, captured: CapturedHttpErrorResponse | undefined): boolean {
|
|
193
193
|
const param = capturedStringField(captured, "param");
|
|
@@ -224,17 +224,17 @@ interface EffortRejectionSignal {
|
|
|
224
224
|
rejectedMatches: boolean;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
const EFFORT_FIELD_PATTERN = /reasoning[_. ]effort|reasoning value/i;
|
|
227
|
+
const EFFORT_FIELD_PATTERN = /reasoning[_. ]?effort|reasoning value/i;
|
|
228
228
|
const ALLOWED_LEVELS_PATTERN = /(?:valid|supported|allowed) levels?/i;
|
|
229
229
|
|
|
230
230
|
/** Fielded rejection verdicts in any word order: verdict-first, field-first, or bare mention plus verdict. */
|
|
231
231
|
function messageCarriesEffortVerdict(message: string): boolean {
|
|
232
232
|
return (
|
|
233
|
-
/invalid[^\n]*(?:reasoning[_. ]effort|reasoning value)/i.test(message) ||
|
|
234
|
-
/(?:reasoning[_. ]effort|reasoning value)[^\n]*(?:invalid|unsupported|not supported|not permitted|must be|expected|unknown|unexpected|unrecognized)/i.test(
|
|
233
|
+
/invalid[^\n]*(?:reasoning[_. ]?effort|reasoning value)/i.test(message) ||
|
|
234
|
+
/(?:reasoning[_. ]?effort|reasoning value)[^\n]*(?:invalid|unsupported|not supported|not permitted|must be|expected|unknown|unexpected|unrecognized)/i.test(
|
|
235
235
|
message,
|
|
236
236
|
) ||
|
|
237
|
-
/(?:unsupported|not supported|not permitted|unknown|unexpected|unrecognized|extra)[^\n]*(?:reasoning[_. ]effort|reasoning value)/i.test(
|
|
237
|
+
/(?:unsupported|not supported|not permitted|unknown|unexpected|unrecognized|extra)[^\n]*(?:reasoning[_. ]?effort|reasoning value)/i.test(
|
|
238
238
|
message,
|
|
239
239
|
)
|
|
240
240
|
);
|
|
@@ -367,7 +367,7 @@ function nearestEnabledReasoningFallback(currentEffort: string, allowed: Set<str
|
|
|
367
367
|
* supported`).
|
|
368
368
|
*/
|
|
369
369
|
const TEMPLATE_KWARG_EFFORT_PATTERN =
|
|
370
|
-
/chat_template_kwargs[^\n]{0,120}reasoning[_. ]effort|reasoning[_. ]effort[^\n]{0,120}chat_template_kwargs/i;
|
|
370
|
+
/chat_template_kwargs[^\n]{0,120}reasoning[_. ]?effort|reasoning[_. ]?effort[^\n]{0,120}chat_template_kwargs/i;
|
|
371
371
|
const FIELD_REJECTION_PATTERN =
|
|
372
372
|
/invalid|unsupported|not supported|not permitted|unknown|unexpected|unrecognized|rejected|extra input/i;
|
|
373
373
|
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
type OpenAIResponsesTool,
|
|
44
44
|
openaiResponsesRequestSchema,
|
|
45
45
|
} from "./openai-responses-server-schema";
|
|
46
|
-
import { encodeTextSignatureV1, parseTextSignature } from "./openai-shared";
|
|
46
|
+
import { coerceNullMessageContentInPlace, encodeTextSignatureV1, parseTextSignature } from "./openai-shared";
|
|
47
47
|
|
|
48
48
|
export type { ParsedRequest };
|
|
49
49
|
|
|
@@ -365,6 +365,7 @@ export function parseRequest(body: unknown, headers?: Headers): ParsedRequest {
|
|
|
365
365
|
// `resolvePromptCacheKey` call further down.
|
|
366
366
|
|
|
367
367
|
rejectUnsupportedExplicitPromptCacheFields(body);
|
|
368
|
+
coerceNullMessageContentInPlace(isObj(body) ? body.input : undefined);
|
|
368
369
|
const data = openaiResponsesRequestSchema(body);
|
|
369
370
|
if (data instanceof type.errors) {
|
|
370
371
|
throw new AIError.ValidationError(`openai-responses: ${data.summary}`);
|
|
@@ -295,6 +295,33 @@ function resetOpenAIResponsesChainState(state: OpenAIResponsesChainState): void
|
|
|
295
295
|
state.lastPromptCacheBreakpointPolicy = undefined;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Drop the account-bound half of every retained `openai-responses` record in
|
|
300
|
+
* `states`: the stateful `previous_response_id` chain baselines.
|
|
301
|
+
*
|
|
302
|
+
* Chaining stores the turn server-side under the account that created it, so a
|
|
303
|
+
* baseline minted by one credential is dead weight the moment the session is
|
|
304
|
+
* switched to a sibling account — the next delta request answers
|
|
305
|
+
* `Previous response not found` and burns a turn re-learning that. Everything
|
|
306
|
+
* else this record holds describes the *deployment*, not the account
|
|
307
|
+
* (strict-tools demotion, reasoning-effort fallback, native-history-replay
|
|
308
|
+
* warmup, the chaining circuit breaker), and is deliberately preserved:
|
|
309
|
+
* re-learning an endpoint's limits on every credential switch is the cost this
|
|
310
|
+
* state exists to avoid.
|
|
311
|
+
*/
|
|
312
|
+
export function resetOpenAIResponsesAccountScopedState(states: Map<string, ProviderSessionState>): void {
|
|
313
|
+
for (const [key, value] of states) {
|
|
314
|
+
if (!key.startsWith(OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX)) continue;
|
|
315
|
+
const state = value as OpenAIResponsesProviderSessionState;
|
|
316
|
+
for (const chain of state.chains.values()) {
|
|
317
|
+
resetOpenAIResponsesChainState(chain);
|
|
318
|
+
// The stale-failure counter tallies the previous account's 404s; a
|
|
319
|
+
// fresh account must not inherit a tripped circuit breaker.
|
|
320
|
+
chain.staleFailures = 0;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
298
325
|
interface OpenAIResponsesChainedParams {
|
|
299
326
|
params: OpenAIResponsesSamplingParams;
|
|
300
327
|
/** Set iff the params carry previous_response_id (delta request). */
|
|
@@ -422,6 +449,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
422
449
|
let rawRequestDump: RawHttpRequestDump | undefined;
|
|
423
450
|
let chainState: OpenAIResponsesChainState | undefined;
|
|
424
451
|
let sentPreviousResponseId: string | undefined;
|
|
452
|
+
let lastSubmittedRequestWasFullReplay: boolean | undefined;
|
|
425
453
|
const abortTracker = createAbortSourceTracker(options?.signal);
|
|
426
454
|
const firstEventTimeoutAbortError = new AIError.StreamTimeoutError(OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE);
|
|
427
455
|
const { requestAbortController, requestSignal } = abortTracker;
|
|
@@ -552,6 +580,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
552
580
|
typeof requestParams.model === "string" ? requestParams.model : model.id,
|
|
553
581
|
);
|
|
554
582
|
activeRequestParams = requestParams;
|
|
583
|
+
lastSubmittedRequestWasFullReplay = requestParams.previous_response_id === undefined;
|
|
555
584
|
let requestTimeout: NodeJS.Timeout | undefined;
|
|
556
585
|
if (requestTimeoutMs !== undefined) {
|
|
557
586
|
requestTimeout = setTimeout(
|
|
@@ -576,6 +605,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
576
605
|
copilotCacheKey,
|
|
577
606
|
copilotCacheSnapshot,
|
|
578
607
|
),
|
|
608
|
+
shouldRetryResponse: (response, bodyText) =>
|
|
609
|
+
!AIError.isRequestBodyReadTimeout(response.status, bodyText) ||
|
|
610
|
+
lastSubmittedRequestWasFullReplay !== true,
|
|
579
611
|
// Transient 408/429/5xx get Retry-After-aware transport
|
|
580
612
|
// retries; the first-event watchdog aborts `requestSignal`,
|
|
581
613
|
// so retries cannot extend the caller's deadline.
|
|
@@ -878,7 +910,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
878
910
|
: activeParams,
|
|
879
911
|
);
|
|
880
912
|
chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
|
|
881
|
-
if (output.responseId) {
|
|
913
|
+
if (output.responseId && replayableResponseItems.length === nativeOutputItems.length) {
|
|
882
914
|
chainState.lastResponseId = output.responseId;
|
|
883
915
|
chainState.lastResponseItems = replayableResponseItems;
|
|
884
916
|
chainState.canAppend = true;
|
|
@@ -886,8 +918,12 @@ const streamOpenAIResponsesOnce = (
|
|
|
886
918
|
// full-context success must not mask categorical rejection.
|
|
887
919
|
if (sentPreviousResponseId) chainState.staleFailures = 0;
|
|
888
920
|
} else {
|
|
889
|
-
//
|
|
921
|
+
// No response id, or replay sanitization dropped an item the server
|
|
922
|
+
// still holds. Sanitization is 1:1-or-fewer, so either case makes the
|
|
923
|
+
// append baseline untrustworthy; next turn must replay in full.
|
|
890
924
|
chainState.canAppend = false;
|
|
925
|
+
chainState.lastResponseId = undefined;
|
|
926
|
+
chainState.lastResponseItems = undefined;
|
|
891
927
|
}
|
|
892
928
|
}
|
|
893
929
|
} else if (chainState) {
|
|
@@ -926,6 +962,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
926
962
|
output.errorStatus = result.status;
|
|
927
963
|
output.errorId = result.id;
|
|
928
964
|
output.errorMessage = result.message;
|
|
965
|
+
if (AIError.isRequestBodyReadTimeout(result.status, result.message) && lastSubmittedRequestWasFullReplay) {
|
|
966
|
+
output.requestBodyReadTimeoutFullReplay = true;
|
|
967
|
+
}
|
|
929
968
|
// Some providers via OpenRouter include extra details here.
|
|
930
969
|
const rawMetadata = (error as { error?: { metadata?: { raw?: string } } })?.error?.metadata?.raw;
|
|
931
970
|
if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
|
|
@@ -1153,6 +1192,11 @@ export function buildParams(
|
|
|
1153
1192
|
});
|
|
1154
1193
|
const strictResponsesPairing = policy.tools.strictResponsesPairing;
|
|
1155
1194
|
const shouldReplayNativeHistory = providerSessionState?.nativeHistoryReplayWarmed ?? true;
|
|
1195
|
+
// Filtering native reasoning must not be undone by reconstruction when the
|
|
1196
|
+
// target also rejects synthetic items (Muse on OpenRouter). Unfiltered targets
|
|
1197
|
+
// retain required text/placeholder replay, including DeepSeek's #10690 fallback.
|
|
1198
|
+
const canReconstructReasoningReplay =
|
|
1199
|
+
!policy.reasoning.filterReasoningHistory || policy.reasoning.allowsSyntheticReasoningContentForToolCalls;
|
|
1156
1200
|
const messages = buildResponsesInput({
|
|
1157
1201
|
model,
|
|
1158
1202
|
context,
|
|
@@ -1164,9 +1208,13 @@ export function buildParams(
|
|
|
1164
1208
|
},
|
|
1165
1209
|
includeThinkingSignatures: shouldReplayNativeHistory && !policy.reasoning.filterReasoningHistory,
|
|
1166
1210
|
requiresReasoningReplayForAllTurns:
|
|
1167
|
-
policy.reasoning.enabled &&
|
|
1211
|
+
policy.reasoning.enabled &&
|
|
1212
|
+
policy.reasoning.requiresReasoningContentForAllAssistantTurns &&
|
|
1213
|
+
canReconstructReasoningReplay,
|
|
1168
1214
|
requiresReasoningReplayForToolCalls:
|
|
1169
|
-
policy.reasoning.enabled &&
|
|
1215
|
+
policy.reasoning.enabled &&
|
|
1216
|
+
policy.reasoning.requiresReasoningContentForToolCalls &&
|
|
1217
|
+
canReconstructReasoningReplay,
|
|
1170
1218
|
repairOrphanOutputs: true,
|
|
1171
1219
|
});
|
|
1172
1220
|
|