@buoy-gg/agent-core 7.0.53 → 7.0.55

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.
@@ -0,0 +1,218 @@
1
+ /**
2
+ * The wire contract between Ask Buoy and the hosted gateway at ai.buoy.gg.
3
+ *
4
+ * ONE table, read by both sides: the gateway builds every error reply from
5
+ * `HOSTED_ERRORS`, and the app decides what to do from the same row. So a new
6
+ * code is one entry here, and the two can never disagree about what a code
7
+ * means. Apps already installed keep the table they shipped with, which is why
8
+ * the gateway serves every protocol from `oldestSupported` up (see
9
+ * ASK_BUOY_HOSTED_SPEC.md, "Wire versions"), and why a code is never renamed
10
+ * or given a new meaning once shipped — add a new one instead.
11
+ *
12
+ * Pure data: no imports, no runtime, safe to load in a Worker and in RN.
13
+ */
14
+ export declare const HOSTED_PROTOCOL_VERSION = 1;
15
+ export declare const HOSTED_HEADERS: {
16
+ /** The `buoys1` access token. Not `Authorization`: buoy.gg's Clerk layer claims that one. */
17
+ readonly session: "X-Buoy-Session";
18
+ /** One id for the whole ask, made by the app: `ask_` + ULID. */
19
+ readonly ask: "X-Buoy-Ask";
20
+ /** Model round within the ask, from 1. */
21
+ readonly round: "X-Buoy-Round";
22
+ /** Try number for this round, from 1. */
23
+ readonly attempt: "X-Buoy-Attempt";
24
+ readonly protocol: "X-Buoy-Protocol";
25
+ /** `ask-buoy/<version> <platform>` */
26
+ readonly client: "X-Buoy-Client";
27
+ /** Response: the server-made id shown on error cards and in reports. */
28
+ readonly requestId: "X-Buoy-Request-Id";
29
+ };
30
+ /**
31
+ * What the app does with an error.
32
+ *
33
+ * - `sign_in` — stop; show the sign-in button.
34
+ * - `refresh_retry` — refresh the access token once, then send the round again.
35
+ * - `retry_after` — wait `retry_after_ms` (or 1 s, doubling), then send again.
36
+ * - `trim_retry` — trim older history and send again (the engine's overflow path).
37
+ * - `none` — stop and show the card.
38
+ *
39
+ * Every retrying action shares one budget per round, so no mix of codes can
40
+ * loop.
41
+ */
42
+ export type HostedErrorAction = "sign_in" | "refresh_retry" | "retry_after" | "trim_retry" | "none";
43
+ /** The one button an error card offers, besides Copy details. */
44
+ export type HostedErrorButton = "sign_in" | "see_plans" | "billing" | "own_endpoint" | "new_chat" | "try_again" | "report";
45
+ export interface HostedErrorSpec {
46
+ status: number;
47
+ action: HostedErrorAction;
48
+ /** Shown to the user. `{date}` is replaced with the reset date. */
49
+ message: string;
50
+ buttons: readonly HostedErrorButton[];
51
+ }
52
+ export declare const HOSTED_ERRORS: {
53
+ readonly session_missing: {
54
+ readonly status: 401;
55
+ readonly action: "sign_in";
56
+ readonly message: "Sign in to use hosted Ask Buoy.";
57
+ readonly buttons: readonly ["sign_in"];
58
+ };
59
+ readonly session_expired: {
60
+ readonly status: 401;
61
+ readonly action: "refresh_retry";
62
+ readonly message: "Your sign-in ran out. Sign in again.";
63
+ readonly buttons: readonly ["sign_in"];
64
+ };
65
+ readonly session_revoked: {
66
+ readonly status: 401;
67
+ readonly action: "sign_in";
68
+ readonly message: "You were signed out. Sign in again.";
69
+ readonly buttons: readonly ["sign_in"];
70
+ };
71
+ readonly token_not_allowed: {
72
+ readonly status: 401;
73
+ readonly action: "sign_in";
74
+ readonly message: "This sign-in can't use hosted AI. Sign in as a person.";
75
+ readonly buttons: readonly ["sign_in"];
76
+ };
77
+ readonly client_too_old: {
78
+ readonly status: 426;
79
+ readonly action: "none";
80
+ readonly message: "Update Buoy to keep using hosted Ask Buoy.";
81
+ readonly buttons: readonly [];
82
+ };
83
+ readonly plan_not_eligible: {
84
+ readonly status: 403;
85
+ readonly action: "none";
86
+ readonly message: "Hosted Ask Buoy comes with Pro and Business.";
87
+ readonly buttons: readonly ["see_plans"];
88
+ };
89
+ readonly not_in_pilot: {
90
+ readonly status: 403;
91
+ readonly action: "none";
92
+ readonly message: "Hosted Ask Buoy is in a small test right now.";
93
+ readonly buttons: readonly [];
94
+ };
95
+ readonly team_hosted_off: {
96
+ readonly status: 403;
97
+ readonly action: "none";
98
+ readonly message: "Your team admin turned off hosted AI.";
99
+ readonly buttons: readonly [];
100
+ };
101
+ readonly account_frozen: {
102
+ readonly status: 403;
103
+ readonly action: "none";
104
+ readonly message: "Your account has a billing problem.";
105
+ readonly buttons: readonly ["billing"];
106
+ };
107
+ readonly credits_out: {
108
+ readonly status: 402;
109
+ readonly action: "none";
110
+ readonly message: "You used this week's credits. They come back on {date}.";
111
+ readonly buttons: readonly ["own_endpoint"];
112
+ };
113
+ readonly member_cap: {
114
+ readonly status: 402;
115
+ readonly action: "none";
116
+ readonly message: "You hit your team's limit for you. Ask your admin.";
117
+ readonly buttons: readonly [];
118
+ };
119
+ readonly ask_too_costly: {
120
+ readonly status: 402;
121
+ readonly action: "none";
122
+ readonly message: "This ask got too big. Try a smaller ask.";
123
+ readonly buttons: readonly [];
124
+ };
125
+ readonly ask_busy: {
126
+ readonly status: 409;
127
+ readonly action: "retry_after";
128
+ readonly message: "Another ask is still running.";
129
+ readonly buttons: readonly [];
130
+ };
131
+ readonly duplicate_request: {
132
+ readonly status: 409;
133
+ readonly action: "none";
134
+ readonly message: "That was already sent.";
135
+ readonly buttons: readonly [];
136
+ };
137
+ readonly request_too_large: {
138
+ readonly status: 413;
139
+ readonly action: "trim_retry";
140
+ readonly message: "This chat got too long. Start a new chat.";
141
+ readonly buttons: readonly ["new_chat"];
142
+ };
143
+ readonly media_not_allowed: {
144
+ readonly status: 400;
145
+ readonly action: "none";
146
+ readonly message: "Hosted Ask Buoy can't read images yet.";
147
+ readonly buttons: readonly [];
148
+ };
149
+ readonly bad_request: {
150
+ readonly status: 400;
151
+ readonly action: "none";
152
+ readonly message: "Something in the request was wrong.";
153
+ readonly buttons: readonly ["report"];
154
+ };
155
+ readonly rate_limited: {
156
+ readonly status: 429;
157
+ readonly action: "retry_after";
158
+ readonly message: "Too many asks at once. Trying again.";
159
+ readonly buttons: readonly [];
160
+ };
161
+ readonly hosted_paused: {
162
+ readonly status: 503;
163
+ readonly action: "none";
164
+ readonly message: "Hosted AI is paused right now.";
165
+ readonly buttons: readonly ["own_endpoint"];
166
+ };
167
+ readonly upstream_busy: {
168
+ readonly status: 503;
169
+ readonly action: "retry_after";
170
+ readonly message: "The AI is busy. Trying again.";
171
+ readonly buttons: readonly [];
172
+ };
173
+ readonly upstream_error: {
174
+ readonly status: 502;
175
+ readonly action: "retry_after";
176
+ readonly message: "The AI had a problem.";
177
+ readonly buttons: readonly ["try_again", "report"];
178
+ };
179
+ readonly upstream_refused: {
180
+ readonly status: 403;
181
+ readonly action: "none";
182
+ readonly message: "The AI would not answer that.";
183
+ readonly buttons: readonly ["report"];
184
+ };
185
+ readonly internal: {
186
+ readonly status: 500;
187
+ readonly action: "none";
188
+ readonly message: "Something broke on our side.";
189
+ readonly buttons: readonly ["try_again", "report"];
190
+ };
191
+ };
192
+ export type HostedErrorCode = keyof typeof HOSTED_ERRORS;
193
+ export declare function isHostedErrorCode(code: unknown): code is HostedErrorCode;
194
+ /** The `error` object, sent as the JSON body before a stream, or as a `data:` frame after. */
195
+ export interface HostedErrorBody {
196
+ code: HostedErrorCode;
197
+ message: string;
198
+ request_id: string | null;
199
+ action: HostedErrorAction;
200
+ retry_after_ms: number | null;
201
+ /** ISO time credits come back, for `credits_out` and `member_cap`. */
202
+ reset_at: string | null;
203
+ help_url: string | null;
204
+ }
205
+ /** Frames the gateway adds around the upstream stream. Old apps skip them. */
206
+ export type HostedBuoyFrame = {
207
+ phase: "start";
208
+ request_id: string;
209
+ } | {
210
+ phase: "end";
211
+ request_id: string;
212
+ /** null with `pending: true` when the charge had not settled in time. */
213
+ credits_left: number | null;
214
+ credits_reset: string | null;
215
+ charged: number | null;
216
+ pending?: true;
217
+ };
218
+ //# sourceMappingURL=protocol.d.ts.map
@@ -16,7 +16,9 @@ export { projectSnapshot, SNAPSHOT_ACTION } from "./catalog/snapshotReads";
16
16
  export { normalizeParams, aliasNote, hashQueryKey } from "./catalog/normalizeParams";
17
17
  export { createAnthropicProvider } from "./providers/anthropic";
18
18
  export { createOpenAIProvider } from "./providers/openai";
19
- export type { AgentMessage, Provider, ProviderConfig, ProviderRequest, StreamEvent, ThinkingBlock, TokenUsage, ToolCall, ToolResult, } from "./providers/types";
19
+ export * from "./hosted/protocol";
20
+ export { hostedErrorOf } from "./providers/problem";
21
+ export type { AgentMessage, Provider, ProviderConfig, ProviderRequest, RequestMeta, StreamEvent, ThinkingBlock, TokenUsage, ToolCall, ToolResult, } from "./providers/types";
20
22
  export { runAgentTurn, runGatedAction, readTargetDigest, digestOf, type GatedActionInput, type GatedActionOutcome, type RunTurnInput, type TurnEvent, type ApprovalAnswer, type StopReason, } from "./engine/runAgentTurn";
21
23
  export { EvidenceStore, type EvidenceEntry } from "./engine/evidence";
22
24
  export { TokenCalibration } from "./engine/tokenCalibration";
@@ -1,10 +1,17 @@
1
- export type ProblemKind = "throttled" | "overflow" | "auth" | "other";
1
+ import { type HostedErrorBody } from "../hosted/protocol";
2
+ /**
3
+ * `blocked` is a hosted gateway refusal whose action is not a retry (out of
4
+ * credits, not signed in, paused…): reported at once, never sent again.
5
+ */
6
+ export type ProblemKind = "throttled" | "overflow" | "auth" | "blocked" | "other";
2
7
  export interface StreamProblem {
3
8
  kind: ProblemKind;
4
9
  /** HTTP status, when the failure was an HTTP response. */
5
10
  status?: number;
6
11
  /** A `Retry-After` the endpoint sent, in ms — honoured over the backoff. */
7
12
  retryAfterMs?: number;
13
+ /** The hosted gateway's own error (hosted/protocol.ts), when it sent one. */
14
+ hosted?: HostedErrorBody;
8
15
  }
9
16
  export interface ClassifyInput {
10
17
  status?: number;
@@ -13,6 +20,8 @@ export interface ClassifyInput {
13
20
  message: string;
14
21
  /** Raw `Retry-After` header value — seconds, or an HTTP date. */
15
22
  retryAfter?: string | null;
23
+ /** The hosted gateway's error body. Its code decides, ahead of every guess below. */
24
+ hosted?: HostedErrorBody;
16
25
  }
17
26
  export declare function classifyProblem(input: ClassifyInput): StreamProblem;
18
27
  /**
@@ -27,4 +36,10 @@ export declare function parseRetryAfter(value: string | null | undefined): numbe
27
36
  * anything that is not that shape; the message phrases still apply.
28
37
  */
29
38
  export declare function errorTypeOf(body: string): string | undefined;
39
+ /**
40
+ * The hosted gateway's error, from a JSON body or a stream frame's `error`
41
+ * object. Undefined for anything else, including another provider's errors
42
+ * that happen to have a `code`.
43
+ */
44
+ export declare function hostedErrorOf(value: string | Record<string, unknown> | null | undefined): HostedErrorBody | undefined;
30
45
  //# sourceMappingURL=problem.d.ts.map
@@ -85,6 +85,7 @@ export type StreamOutcome =
85
85
  * two are this side deciding the response cannot be trusted.
86
86
  */
87
87
  import type { StreamProblem } from "./problem";
88
+ import type { HostedBuoyFrame } from "../hosted/protocol";
88
89
  export type StreamErrorKind =
89
90
  /** The provider reported it: HTTP error, or an `error` event on a 200. */
90
91
  "provider"
@@ -105,6 +106,11 @@ export type StreamEvent = {
105
106
  type: "thinking";
106
107
  block: ThinkingBlock;
107
108
  }
109
+ /** Credits and the request id, from the hosted gateway's own frames. */
110
+ | {
111
+ type: "meter";
112
+ frame: HostedBuoyFrame;
113
+ }
108
114
  /** Mid-stream provider error. Anthropic can emit these on a committed 200. */
109
115
  | {
110
116
  type: "error";
@@ -148,6 +154,18 @@ export interface TokenUsage {
148
154
  cacheRead?: number;
149
155
  cacheWrite?: number;
150
156
  }
157
+ /**
158
+ * Which call this is, for a gateway that meters per ask (the hosted Ask Buoy
159
+ * gateway sends these as X-Buoy-Ask / -Round / -Attempt; see hosted/protocol.ts).
160
+ */
161
+ export interface RequestMeta {
162
+ /** One id for the whole turn. */
163
+ askId: string;
164
+ /** Model round within the turn, from 1. */
165
+ round: number;
166
+ /** Try number for this round, from 1. */
167
+ attempt: number;
168
+ }
151
169
  export interface ProviderRequest {
152
170
  messages: AgentMessage[];
153
171
  system: string;
@@ -162,6 +180,7 @@ export interface ProviderRequest {
162
180
  model: string;
163
181
  maxTokens: number;
164
182
  signal?: AbortSignal;
183
+ meta?: RequestMeta;
165
184
  }
166
185
  export interface ProviderConfig {
167
186
  /** The org's gateway, or the provider's own URL for the dev-only direct mode. */
@@ -170,9 +189,15 @@ export interface ProviderConfig {
170
189
  * Fresh headers per request, so short-lived tokens from the host app's own
171
190
  * session work. This is why Buoy stores no credential of its own.
172
191
  */
173
- headers?: () => Record<string, string> | Promise<Record<string, string>>;
192
+ headers?: (meta?: RequestMeta) => Record<string, string> | Promise<Record<string, string>>;
174
193
  /** Dev-only direct mode. Never for a distributed build. */
175
194
  apiKey?: string;
195
+ /**
196
+ * The endpoint is Buoy's hosted gateway (ai.buoy.gg, hosted/protocol.ts).
197
+ * Only then are its error codes and `buoy` frames trusted: your own
198
+ * endpoint could send a lookalike code and change what the engine retries.
199
+ */
200
+ hosted?: boolean;
176
201
  model: string;
177
202
  maxTokens?: number;
178
203
  /** Anthropic API version header. Ignored by the OpenAI adapter. */