@oh-my-pi/pi-ai 16.2.0 → 16.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 +27 -0
- package/dist/types/auth-gateway/server.d.ts +0 -19
- package/dist/types/auth-retry.d.ts +2 -10
- package/dist/types/auth-storage.d.ts +1 -2
- package/dist/types/dialect/demotion.d.ts +22 -0
- package/dist/types/dialect/index.d.ts +2 -0
- package/dist/types/error/abort.d.ts +14 -0
- package/dist/types/error/auth-classify.d.ts +16 -0
- package/dist/types/error/auth.d.ts +27 -0
- package/dist/types/error/aws.d.ts +23 -0
- package/dist/types/error/classes.d.ts +102 -0
- package/dist/types/error/finalize.d.ts +39 -0
- package/dist/types/error/flags.d.ts +70 -0
- package/dist/types/error/format.d.ts +20 -0
- package/dist/types/error/gateway.d.ts +20 -0
- package/dist/types/error/index.d.ts +13 -0
- package/dist/types/error/oauth.d.ts +43 -0
- package/dist/types/error/provider.d.ts +42 -0
- package/dist/types/{rate-limit-utils.d.ts → error/rate-limit.d.ts} +14 -1
- package/dist/types/error/retryable.d.ts +27 -0
- package/dist/types/error/validation.d.ts +32 -0
- package/dist/types/index.d.ts +1 -3
- package/dist/types/providers/amazon-bedrock.d.ts +0 -5
- package/dist/types/providers/anthropic-client.d.ts +2 -16
- package/dist/types/providers/anthropic.d.ts +6 -1
- package/dist/types/providers/aws-eventstream.d.ts +2 -1
- package/dist/types/providers/cursor.d.ts +8 -7
- package/dist/types/providers/google-gemini-cli.d.ts +0 -5
- package/dist/types/providers/google-shared.d.ts +0 -5
- package/dist/types/providers/ollama.d.ts +0 -5
- package/dist/types/providers/openai-codex/request-transformer.d.ts +2 -2
- package/dist/types/providers/openai-codex/response-handler.d.ts +1 -1
- package/dist/types/providers/openai-codex-responses.d.ts +2 -2
- package/dist/types/providers/openai-responses-server-schema.d.ts +6 -0
- package/dist/types/providers/openai-responses-wire.d.ts +1 -1
- package/dist/types/providers/openai-responses.d.ts +1 -0
- package/dist/types/providers/openai-shared.d.ts +8 -6
- package/dist/types/providers/pi-native-client.d.ts +0 -9
- package/dist/types/registry/oauth/xai-oauth.d.ts +0 -9
- package/dist/types/types.d.ts +6 -0
- package/dist/types/utils/block-symbols.d.ts +20 -0
- package/dist/types/utils/openai-http.d.ts +4 -8
- package/dist/types/utils/retry.d.ts +2 -14
- package/dist/types/utils/thinking-loop.d.ts +3 -1
- package/package.json +8 -4
- package/src/api-registry.ts +3 -1
- package/src/auth-broker/discover.ts +7 -2
- package/src/auth-broker/remote-store.ts +8 -7
- package/src/auth-gateway/server.ts +27 -115
- package/src/auth-retry.ts +9 -21
- package/src/auth-storage.ts +34 -49
- package/src/dialect/demotion.ts +31 -0
- package/src/dialect/factory.ts +0 -2
- package/src/dialect/index.ts +2 -0
- package/src/dialect/owned-stream.ts +0 -1
- package/src/dialect/thinking.ts +22 -10
- package/src/error/abort.ts +18 -0
- package/src/error/auth-classify.ts +30 -0
- package/src/error/auth.ts +48 -0
- package/src/error/aws.ts +31 -0
- package/src/error/classes.ts +186 -0
- package/src/error/finalize.ts +69 -0
- package/src/error/flags.ts +486 -0
- package/src/error/format.ts +36 -0
- package/src/error/gateway.ts +96 -0
- package/src/error/index.ts +13 -0
- package/src/error/oauth.ts +58 -0
- package/src/error/provider.ts +56 -0
- package/src/{rate-limit-utils.ts → error/rate-limit.ts} +9 -3
- package/src/error/retryable.ts +70 -0
- package/src/error/validation.ts +44 -0
- package/src/index.ts +1 -3
- package/src/providers/amazon-bedrock.ts +61 -57
- package/src/providers/anthropic-client.ts +13 -41
- package/src/providers/anthropic-messages-server.ts +9 -2
- package/src/providers/anthropic.ts +84 -147
- package/src/providers/aws-credentials.ts +41 -11
- package/src/providers/aws-eventstream.ts +12 -21
- package/src/providers/azure-openai-responses.ts +27 -17
- package/src/providers/cursor.ts +59 -53
- package/src/providers/devin.ts +28 -20
- package/src/providers/gitlab-duo-workflow.ts +30 -10
- package/src/providers/gitlab-duo.ts +22 -6
- package/src/providers/google-auth.ts +15 -5
- package/src/providers/google-gemini-cli.ts +46 -60
- package/src/providers/google-shared.ts +40 -38
- package/src/providers/google-vertex.ts +3 -2
- package/src/providers/google.ts +5 -1
- package/src/providers/mock.ts +10 -7
- package/src/providers/ollama.ts +34 -29
- package/src/providers/openai-chat-server.ts +2 -1
- package/src/providers/openai-codex/request-transformer.ts +13 -12
- package/src/providers/openai-codex/response-handler.ts +1 -1
- package/src/providers/openai-codex-responses.ts +931 -1012
- package/src/providers/openai-completions.ts +46 -36
- package/src/providers/openai-responses-server-schema.ts +3 -0
- package/src/providers/openai-responses-server.ts +82 -30
- package/src/providers/openai-responses-wire.ts +1 -1
- package/src/providers/openai-responses.ts +46 -22
- package/src/providers/openai-shared.ts +118 -51
- package/src/providers/pi-native-client.ts +12 -18
- package/src/providers/pi-native-server.ts +9 -6
- package/src/providers/register-builtins.ts +5 -2
- package/src/providers/transform-messages.ts +31 -63
- package/src/registry/alibaba-coding-plan.ts +6 -5
- package/src/registry/api-key-login.ts +4 -3
- package/src/registry/api-key-validation.ts +4 -3
- package/src/registry/cloudflare-ai-gateway.ts +4 -3
- package/src/registry/coreweave.ts +2 -1
- package/src/registry/deepseek.ts +2 -1
- package/src/registry/google-antigravity.ts +2 -1
- package/src/registry/google-gemini-cli.ts +2 -1
- package/src/registry/kagi.ts +4 -3
- package/src/registry/kilo.ts +32 -10
- package/src/registry/litellm.ts +4 -3
- package/src/registry/llama-cpp.ts +3 -2
- package/src/registry/lm-studio.ts +3 -2
- package/src/registry/nvidia.ts +7 -7
- package/src/registry/oauth/anthropic.ts +23 -6
- package/src/registry/oauth/callback-server.ts +5 -3
- package/src/registry/oauth/cursor.ts +18 -4
- package/src/registry/oauth/devin.ts +14 -3
- package/src/registry/oauth/github-copilot.ts +21 -10
- package/src/registry/oauth/gitlab-duo-workflow.ts +15 -3
- package/src/registry/oauth/gitlab-duo.ts +30 -6
- package/src/registry/oauth/google-antigravity.ts +14 -5
- package/src/registry/oauth/google-gemini-cli.ts +24 -7
- package/src/registry/oauth/google-oauth-shared.ts +6 -3
- package/src/registry/oauth/index.ts +19 -8
- package/src/registry/oauth/kimi.ts +40 -10
- package/src/registry/oauth/openai-codex.ts +26 -11
- package/src/registry/oauth/opencode.ts +4 -3
- package/src/registry/oauth/perplexity.ts +33 -11
- package/src/registry/oauth/xai-oauth.ts +62 -18
- package/src/registry/oauth/xiaomi.ts +21 -9
- package/src/registry/ollama-cloud.ts +5 -4
- package/src/registry/ollama.ts +3 -2
- package/src/registry/parallel.ts +4 -3
- package/src/registry/qwen-portal.ts +4 -3
- package/src/registry/tavily.ts +4 -3
- package/src/registry/vercel-ai-gateway.ts +4 -3
- package/src/registry/vllm.ts +3 -2
- package/src/stream.ts +79 -24
- package/src/types.ts +6 -0
- package/src/usage/claude.ts +2 -1
- package/src/usage/github-copilot.ts +4 -3
- package/src/usage/google-antigravity.ts +2 -1
- package/src/utils/abort.ts +4 -2
- package/src/utils/block-symbols.ts +32 -0
- package/src/utils/event-stream.ts +15 -3
- package/src/utils/http-inspector.ts +4 -4
- package/src/utils/idle-iterator.ts +6 -5
- package/src/utils/openai-http.ts +11 -46
- package/src/utils/parse-bind.ts +7 -5
- package/src/utils/proxy.ts +2 -1
- package/src/utils/retry.ts +7 -23
- package/src/utils/schema/normalize.ts +3 -2
- package/src/utils/thinking-loop.ts +15 -11
- package/src/utils/validation.ts +4 -3
- package/dist/types/dialect/pi.d.ts +0 -9
- package/dist/types/errors.d.ts +0 -24
- package/dist/types/utils/overflow.d.ts +0 -55
- package/src/dialect/pi.md +0 -55
- package/src/dialect/pi.ts +0 -600
- package/src/errors.ts +0 -32
- package/src/utils/overflow.ts +0 -140
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { attach, create, Flag } from "./flags";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* No API key / credential was available to dispatch a request.
|
|
5
|
+
*
|
|
6
|
+
* The default message preserves the historical `"No API key for provider: X"`
|
|
7
|
+
* wording, which {@link Flag.AuthFailed}'s regex (`no api key`) keys off — but
|
|
8
|
+
* the flag is also attached structurally so classification never depends on the
|
|
9
|
+
* exact phrasing.
|
|
10
|
+
*/
|
|
11
|
+
export class MissingApiKeyError extends Error {
|
|
12
|
+
readonly provider: string | undefined;
|
|
13
|
+
|
|
14
|
+
constructor(provider?: string, message?: string) {
|
|
15
|
+
super(message ?? (provider ? `No API key for provider: ${provider}` : "No API key available"));
|
|
16
|
+
this.name = "MissingApiKeyError";
|
|
17
|
+
this.provider = provider;
|
|
18
|
+
attach(this, create(Flag.AuthFailed));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A user-facing login flow required an `onPrompt` callback that was not supplied. */
|
|
23
|
+
export class OnPromptRequiredError extends Error {
|
|
24
|
+
constructor(providerLabel: string) {
|
|
25
|
+
super(`${providerLabel} login requires onPrompt callback`);
|
|
26
|
+
this.name = "OnPromptRequiredError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** An interactive login asked for an API key but the user supplied an empty value. */
|
|
31
|
+
export class ApiKeyRequiredError extends Error {
|
|
32
|
+
constructor(message = "API key is required") {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "ApiKeyRequiredError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A user cancelled an interactive login / device flow. Classified as an abort
|
|
40
|
+
* so it is never surfaced as a retryable transient failure.
|
|
41
|
+
*/
|
|
42
|
+
export class LoginCancelledError extends Error {
|
|
43
|
+
constructor(message = "Login cancelled") {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "LoginCancelledError";
|
|
46
|
+
attach(this, create(Flag.Abort));
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/error/aws.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Which AWS credential-resolution path failed. */
|
|
2
|
+
export type AwsCredentialsErrorKind =
|
|
3
|
+
/** No usable credential source resolved (chain exhausted). */
|
|
4
|
+
| "resolution"
|
|
5
|
+
/** SSO cache token missing (`aws sso login` not run). */
|
|
6
|
+
| "sso-token-missing"
|
|
7
|
+
/** SSO cache token present but expired. */
|
|
8
|
+
| "sso-token-expired"
|
|
9
|
+
/** SSO `GetRoleCredentials` call failed or returned no role. */
|
|
10
|
+
| "sso-role"
|
|
11
|
+
/** External `credential_process` failed, timed out, or emitted bad output. */
|
|
12
|
+
| "credential-process";
|
|
13
|
+
|
|
14
|
+
/** A failure resolving AWS credentials for the Bedrock provider. */
|
|
15
|
+
export class AwsCredentialsError extends Error {
|
|
16
|
+
readonly kind: AwsCredentialsErrorKind;
|
|
17
|
+
|
|
18
|
+
constructor(message: string, kind: AwsCredentialsErrorKind, options?: { cause?: unknown }) {
|
|
19
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
20
|
+
this.name = "AwsCredentialsError";
|
|
21
|
+
this.kind = kind;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** A malformed AWS event-stream frame (bad length, CRC mismatch, unknown header type). */
|
|
26
|
+
export class EventStreamFrameError extends Error {
|
|
27
|
+
constructor(detail: string) {
|
|
28
|
+
super(`eventstream: ${detail}`);
|
|
29
|
+
this.name = "EventStreamFrameError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { CapturedHttpErrorResponse } from "../utils/http-inspector";
|
|
2
|
+
|
|
3
|
+
/** Prefix on errors raised when an Anthropic SSE stream envelope is malformed. */
|
|
4
|
+
export const STREAM_ENVELOPE_ERROR_PREFIX = "Anthropic stream envelope error:";
|
|
5
|
+
|
|
6
|
+
/** Structured HTTP errors thrown by provider clients. */
|
|
7
|
+
export interface ProviderHttpErrorOptions {
|
|
8
|
+
/** Response headers; enables `retry-after`/rate-limit extraction downstream. */
|
|
9
|
+
headers?: Headers;
|
|
10
|
+
/** Machine-readable error code from the response body (`error.code` / `error.type`). */
|
|
11
|
+
code?: string;
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Non-2xx HTTP response from a provider. */
|
|
16
|
+
export class ProviderHttpError extends Error {
|
|
17
|
+
readonly status: number;
|
|
18
|
+
readonly headers: Headers | undefined;
|
|
19
|
+
readonly code: string | undefined;
|
|
20
|
+
|
|
21
|
+
constructor(message: string, status: number, options?: ProviderHttpErrorOptions) {
|
|
22
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
23
|
+
this.name = "ProviderHttpError";
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.headers = options?.headers;
|
|
26
|
+
this.code = options?.code;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Non-2xx response from an OpenAI-wire endpoint, with the decoded body attached. */
|
|
31
|
+
export class OpenAIHttpError extends ProviderHttpError {
|
|
32
|
+
readonly captured: CapturedHttpErrorResponse;
|
|
33
|
+
|
|
34
|
+
constructor(message: string, captured: CapturedHttpErrorResponse, code?: string, cause?: unknown) {
|
|
35
|
+
super(message, captured.status, { headers: captured.headers, code, cause });
|
|
36
|
+
this.name = "OpenAIHttpError";
|
|
37
|
+
this.captured = captured;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Pull a human-readable message and machine code out of an OpenAI-style error
|
|
42
|
+
* envelope (`{ error: { message, code, type } }`), tolerating the flat shapes
|
|
43
|
+
* compat hosts return (`{ error: "..." }`, `{ message: "..." }`) and falling
|
|
44
|
+
* back to the raw body text.
|
|
45
|
+
*/
|
|
46
|
+
static parseEnvelope(
|
|
47
|
+
bodyJson: unknown,
|
|
48
|
+
bodyText: string | undefined,
|
|
49
|
+
): { detail: string | undefined; code: string | undefined } {
|
|
50
|
+
if (typeof bodyJson === "object" && bodyJson !== null) {
|
|
51
|
+
const envelope = bodyJson as { error?: unknown; message?: unknown };
|
|
52
|
+
const error = envelope.error;
|
|
53
|
+
if (typeof error === "object" && error !== null) {
|
|
54
|
+
const { message, code, type } = error as { message?: unknown; code?: unknown; type?: unknown };
|
|
55
|
+
return {
|
|
56
|
+
detail: typeof message === "string" && message.length > 0 ? message : bodyText,
|
|
57
|
+
code: typeof code === "string" ? code : typeof type === "string" ? type : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (typeof error === "string" && error.length > 0) {
|
|
61
|
+
return { detail: error, code: undefined };
|
|
62
|
+
}
|
|
63
|
+
if (typeof envelope.message === "string" && envelope.message.length > 0) {
|
|
64
|
+
return { detail: envelope.message, code: undefined };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { detail: bodyText, code: undefined };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Non-2xx response from the Anthropic API. */
|
|
72
|
+
export class AnthropicApiError extends ProviderHttpError {
|
|
73
|
+
declare readonly headers: Headers;
|
|
74
|
+
readonly requestId: string | null;
|
|
75
|
+
|
|
76
|
+
constructor(status: number, message: string, headers: Headers) {
|
|
77
|
+
super(message, status, { headers });
|
|
78
|
+
this.name = "AnthropicApiError";
|
|
79
|
+
this.requestId = headers.get("request-id");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static async fromResponse(response: Response): Promise<AnthropicApiError> {
|
|
83
|
+
const body = await response.text().catch(() => "");
|
|
84
|
+
const detail = body.trim() || "status code (no body)";
|
|
85
|
+
return new AnthropicApiError(response.status, `${response.status} ${detail}`, response.headers);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Network-level failure (DNS, TLS, socket reset) after retries were exhausted. */
|
|
90
|
+
export class AnthropicConnectionError extends Error {
|
|
91
|
+
constructor(cause: unknown) {
|
|
92
|
+
super("Connection error.", { cause });
|
|
93
|
+
this.name = "AnthropicConnectionError";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** No response headers arrived within the configured request timeout. */
|
|
98
|
+
export class AnthropicConnectionTimeoutError extends Error {
|
|
99
|
+
constructor() {
|
|
100
|
+
super("Request timed out.");
|
|
101
|
+
this.name = "AnthropicConnectionTimeoutError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A malformed Anthropic SSE stream envelope — events arriving out of order
|
|
107
|
+
* (before `message_start`) or otherwise violating the message-event grammar.
|
|
108
|
+
* The message is prefixed with {@link STREAM_ENVELOPE_ERROR_PREFIX} so the
|
|
109
|
+
* shared envelope predicates classify it.
|
|
110
|
+
*/
|
|
111
|
+
export class AnthropicStreamEnvelopeError extends Error {
|
|
112
|
+
constructor(detail: string) {
|
|
113
|
+
super(`${STREAM_ENVELOPE_ERROR_PREFIX} ${detail}`);
|
|
114
|
+
this.name = "AnthropicStreamEnvelopeError";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Non-2xx response (or in-stream exception event) from the Bedrock runtime API. */
|
|
119
|
+
export class BedrockApiError extends ProviderHttpError {
|
|
120
|
+
override readonly name = "BedrockApiError";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Non-2xx response (or in-stream error chunk) from the Cloud Code Assist API. */
|
|
124
|
+
export class GeminiCliApiError extends ProviderHttpError {
|
|
125
|
+
override readonly name = "GeminiCliApiError";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Non-2xx response (or in-stream error chunk) from the Google Generative Language / Vertex API. */
|
|
129
|
+
export class GoogleApiError extends ProviderHttpError {
|
|
130
|
+
override readonly name = "GoogleApiError";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Non-2xx response from the Ollama `/api/chat` endpoint. */
|
|
134
|
+
export class OllamaApiError extends ProviderHttpError {
|
|
135
|
+
override readonly name = "OllamaApiError";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Auth gateway HTTP failure. */
|
|
139
|
+
export class AuthGatewayError extends ProviderHttpError {
|
|
140
|
+
constructor(message: string, status: number, headers?: Headers, code?: string) {
|
|
141
|
+
super(message, status, { headers, code });
|
|
142
|
+
this.name = "AuthGatewayError";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class CodexWebSocketTransportError extends Error {
|
|
147
|
+
constructor(detail: string) {
|
|
148
|
+
super(`Codex websocket transport failure: ${detail}`);
|
|
149
|
+
this.name = "CodexWebSocketTransportError";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export class CodexWhitespaceToolCallLoopError extends Error {
|
|
154
|
+
constructor(message: string) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "CodexWhitespaceToolCallLoopError";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export class CodexProviderStreamError extends Error {
|
|
161
|
+
readonly retryable: boolean;
|
|
162
|
+
|
|
163
|
+
constructor(message: string, options?: { retryable?: boolean; cause?: unknown }) {
|
|
164
|
+
super(message, { cause: options?.cause });
|
|
165
|
+
this.name = "CodexProviderStreamError";
|
|
166
|
+
this.retryable = options?.retryable !== false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export class AuthBrokerError extends Error {
|
|
171
|
+
readonly status: number | undefined;
|
|
172
|
+
readonly body: string | undefined;
|
|
173
|
+
constructor(message: string, opts: { status?: number; body?: string; cause?: unknown } = {}) {
|
|
174
|
+
super(message, { cause: opts.cause });
|
|
175
|
+
this.name = "AuthBrokerError";
|
|
176
|
+
this.status = opts.status;
|
|
177
|
+
this.body = opts.body;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export class AuthBrokerStreamUnsupportedError extends AuthBrokerError {
|
|
182
|
+
constructor(message = "Auth broker does not support /v1/snapshot/stream") {
|
|
183
|
+
super(message, { status: 404 });
|
|
184
|
+
this.name = "AuthBrokerStreamUnsupportedError";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Api } from "../types";
|
|
2
|
+
import type { AbortSourceTracker } from "../utils/abort";
|
|
3
|
+
import type { CapturedHttpErrorResponse, RawHttpRequestDump } from "../utils/http-inspector";
|
|
4
|
+
import { classify, classifyMessage, status } from "./flags";
|
|
5
|
+
import { formatMessage } from "./format";
|
|
6
|
+
|
|
7
|
+
/** Context a provider catch block hands to {@link finalize}. */
|
|
8
|
+
export interface FinalizeOptions {
|
|
9
|
+
/** Wire API, for api-specific text classification (e.g. stale-responses items). */
|
|
10
|
+
api?: Api;
|
|
11
|
+
/** Provider id; forwarded to the message formatter for copilot rewrites. */
|
|
12
|
+
provider?: string;
|
|
13
|
+
/** Caller signal, for providers that don't run an abort tracker. */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
/** Abort tracker, preferred over `signal`: distinguishes caller vs. local aborts. */
|
|
16
|
+
abortTracker?: AbortSourceTracker;
|
|
17
|
+
/** Raw request, dumped into the message for 400-class failures. */
|
|
18
|
+
rawRequestDump?: RawHttpRequestDump;
|
|
19
|
+
/** Captured non-2xx response body, used for status fallback and message detail. */
|
|
20
|
+
capturedErrorResponse?: CapturedHttpErrorResponse;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The full bundle a provider assigns onto its `AssistantMessage` error fields. */
|
|
24
|
+
export interface FinalizeResult {
|
|
25
|
+
/** Structured flag id from {@link classify}. */
|
|
26
|
+
id: number;
|
|
27
|
+
/** HTTP status, from the error or the captured response. */
|
|
28
|
+
status: number | undefined;
|
|
29
|
+
/** `"aborted"` when the caller cancelled, otherwise `"error"`. */
|
|
30
|
+
stopReason: "aborted" | "error";
|
|
31
|
+
/** User-facing message from {@link formatMessage}, or a local abort reason. */
|
|
32
|
+
message: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build the complete error bundle for a provider catch block, replacing the
|
|
37
|
+
* `stopReason` / `errorStatus` / `errorId` / `errorMessage` boilerplate.
|
|
38
|
+
*
|
|
39
|
+
* `stopReason` comes from the abort tracker (caller intent dominates) or, when
|
|
40
|
+
* no tracker is supplied, the raw `signal.aborted`. A local abort reason (e.g. a
|
|
41
|
+
* first-event timeout) supersedes the formatted message. Message formatting is
|
|
42
|
+
* wrapped so a formatter throw can never skip the caller's `stream.end()`.
|
|
43
|
+
*/
|
|
44
|
+
export async function finalize(error: unknown, opts: FinalizeOptions = {}): Promise<FinalizeResult> {
|
|
45
|
+
const aborted = opts.abortTracker ? opts.abortTracker.wasCallerAbort() : opts.signal?.aborted === true;
|
|
46
|
+
const currentStatus = status(error) ?? opts.capturedErrorResponse?.status;
|
|
47
|
+
|
|
48
|
+
let message: string;
|
|
49
|
+
try {
|
|
50
|
+
const localReason = opts.abortTracker?.getLocalAbortReason();
|
|
51
|
+
message = localReason?.message ?? (await formatMessage(error, opts));
|
|
52
|
+
} catch {
|
|
53
|
+
message = error instanceof Error ? error.message : String(error);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const id = classifyMessage({
|
|
57
|
+
api: opts.api,
|
|
58
|
+
errorId: classify(error, opts.api),
|
|
59
|
+
errorMessage: message,
|
|
60
|
+
errorStatus: currentStatus,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
id,
|
|
65
|
+
status: currentStatus,
|
|
66
|
+
stopReason: aborted ? "aborted" : "error",
|
|
67
|
+
message,
|
|
68
|
+
};
|
|
69
|
+
}
|