@cydm/pie 2.7.5 → 2.7.7

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.
@@ -1,9 +1,9 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createAskUserCapability
4
- } from "../../../chunks/chunk-BFHCZVNO.js";
5
- import "../../../chunks/chunk-MLRF72MK.js";
6
- import "../../../chunks/chunk-SUPAWIKP.js";
4
+ } from "../../../chunks/chunk-UPGEXDEQ.js";
5
+ import "../../../chunks/chunk-W5B3OXHQ.js";
6
+ import "../../../chunks/chunk-2FEDB5GR.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
8
8
  import "../../../chunks/chunk-LZQXQOPJ.js";
9
9
 
@@ -4,12 +4,13 @@ import {
4
4
  createExecutionStateFromPlan,
5
5
  createPlanCapability,
6
6
  extractPlanTodoItems,
7
+ getToolCapabilityMetadata,
7
8
  isPlanModeSafeCommand,
8
9
  markCompletedPlanSteps,
9
10
  restoreExecutionState
10
- } from "../../../chunks/chunk-BFHCZVNO.js";
11
- import "../../../chunks/chunk-MLRF72MK.js";
12
- import "../../../chunks/chunk-SUPAWIKP.js";
11
+ } from "../../../chunks/chunk-UPGEXDEQ.js";
12
+ import "../../../chunks/chunk-W5B3OXHQ.js";
13
+ import "../../../chunks/chunk-2FEDB5GR.js";
13
14
  import {
14
15
  Type
15
16
  } from "../../../chunks/chunk-XXGYUYXI.js";
@@ -258,7 +259,8 @@ Update the plan accordingly and call plan_submit again with the revised steps. D
258
259
  }
259
260
  });
260
261
  ctx.on("tool:call", async (event) => {
261
- if (!planModeEnabled || event.toolName !== "bash") return;
262
+ if (!planModeEnabled) return;
263
+ if (getToolCapabilityMetadata({ name: event.toolName })?.executesShell !== true) return;
262
264
  const command = typeof event.args === "object" && event.args !== null ? event.args.command : void 0;
263
265
  if (typeof command === "string" && !isPlanModeSafeCommand(command)) {
264
266
  ctx.ui.notify(
@@ -4,14 +4,14 @@ import {
4
4
  formatTransportLifecycle,
5
5
  getConfigDir,
6
6
  logTransportLifecycle
7
- } from "../../../chunks/chunk-OOUL3DRE.js";
7
+ } from "../../../chunks/chunk-IU5L5DED.js";
8
8
  import {
9
9
  SUBAGENT_SESSION_RECORD_VERSION,
10
10
  createSharedFileSystemTools,
11
11
  createSubagentCapability
12
- } from "../../../chunks/chunk-BFHCZVNO.js";
13
- import "../../../chunks/chunk-MLRF72MK.js";
14
- import "../../../chunks/chunk-SUPAWIKP.js";
12
+ } from "../../../chunks/chunk-UPGEXDEQ.js";
13
+ import "../../../chunks/chunk-W5B3OXHQ.js";
14
+ import "../../../chunks/chunk-2FEDB5GR.js";
15
15
  import "../../../chunks/chunk-XXGYUYXI.js";
16
16
  import "../../../chunks/chunk-LZQXQOPJ.js";
17
17
 
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-BFHCZVNO.js";
12
- import "../../../chunks/chunk-MLRF72MK.js";
13
- import "../../../chunks/chunk-SUPAWIKP.js";
11
+ } from "../../../chunks/chunk-UPGEXDEQ.js";
12
+ import "../../../chunks/chunk-W5B3OXHQ.js";
13
+ import "../../../chunks/chunk-2FEDB5GR.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16
 
@@ -21,7 +21,7 @@ snapshot. Keep the named session on every command.
21
21
 
22
22
  Agent-ergonomic interface for controlling a real Chrome browser session (Chrome DevTools Protocol). Prefer this over other browser automation approaches. Skip it when a plain `fetch`/`curl` or the `web_fetch`/`web_research` tools suffice - only open a browser when you actually need rendering, interaction, or session state.
23
23
 
24
- Use Pie's packaged wrapper. Resolve `chrome-devtools-axi-cli.js` against this skill's `baseDir` and run it with `bash`:
24
+ Use Pie's packaged wrapper. Resolve `chrome-devtools-axi-cli.js` against this skill's `baseDir` and run it with the `shell` tool:
25
25
 
26
26
  ```bash
27
27
  node <baseDir>/chrome-devtools-axi-cli.js <command> [args] [flags]
@@ -29,7 +29,7 @@ node <baseDir>/chrome-devtools-axi-cli.js <command> [args] [flags]
29
29
 
30
30
  No manual dependency install is required when this skill is shipped with Pie.
31
31
 
32
- `bash` is the Pie tool name, not a requirement to launch a Unix shell.
32
+ `shell` is the Pie tool name, not a requirement to launch a Unix shell.
33
33
  On Windows use `shell:auto` or PowerShell; do not force `bash.exe`/WSL.
34
34
  Set environment variables with PowerShell syntax, such as
35
35
  `$env:CHROME_DEVTOOLS_AXI_SESSION='my-task'`, in each command that needs them.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: computer-use-axi
3
- description: "Drive desktop apps and managed CDP browser pages through the computer-use-axi CLI run via bash: list UI roots, capture bounded outlines, search/expand/inspect element refs, perform checked actions with postconditions, and wait for conditions. Use whenever a task needs real UI interaction - clicking through a native app, reading a screen, driving a desktop window, or acting on a browser page state. Read this before the first UI-control task or after any command usage error."
3
+ description: "Drive desktop apps and managed CDP browser pages through the computer-use-axi CLI run via the shell tool: list UI roots, capture bounded outlines, search/expand/inspect element refs, perform checked actions with postconditions, and wait for conditions. Use whenever a task needs real UI interaction - clicking through a native app, reading a screen, driving a desktop window, or acting on a browser page state. Read this before the first UI-control task or after any command usage error."
4
4
  ---
5
5
 
6
6
  # computer-use-axi
@@ -16,7 +16,7 @@ Do not install a global CLI or fetch a different version. Pie provides the PTY
16
16
  runtime and dependencies. First use requires no network access after Pie is
17
17
  installed. Run the wrapper with `--help` or `<command> --help` for exact flags.
18
18
 
19
- Use ordinary `bash` for non-interactive commands.
19
+ Use the ordinary `shell` tool for non-interactive commands.
20
20
 
21
21
  ## Core Loop
22
22
 
@@ -4067,6 +4067,9 @@ function convertMessages(model, context, compat) {
4067
4067
  return params;
4068
4068
  }
4069
4069
  function convertCheckedContent(item, model) {
4070
+ if (item.type === "hostContext") {
4071
+ throw new Error(`Unprojected hostContext block reached openai-compat conversion (${item.kind}); the request path must project host envelopes to text first.`);
4072
+ }
4070
4073
  const modality = item.type === "fileRef" ? item.modality : item.type;
4071
4074
  if (modality === "image" || modality === "video" || modality === "audio") {
4072
4075
  if (!model.input.includes(modality)) return [{ type: "text", text: `[${modality} attachment omitted: current model does not support this modality. Re-read with a compatible model.]` }];
@@ -4091,6 +4094,9 @@ function audioFormat(mimeType) {
4091
4094
  return format;
4092
4095
  }
4093
4096
  function convertUserContentItem(item) {
4097
+ if (item.type === "hostContext") {
4098
+ throw new Error(`Unprojected hostContext block reached openai-compat conversion (${item.kind}); the request path must project host envelopes to text first.`);
4099
+ }
4094
4100
  if (item.type === "text") return [{ type: "text", text: item.text }];
4095
4101
  if (item.type === "image") return [{ type: "image_url", image_url: { url: `data:${item.mimeType};base64,${item.data}` } }];
4096
4102
  if (item.type === "video") {
@@ -4501,6 +4507,7 @@ var streamOpenAICompletions = (model, context, options) => {
4501
4507
  }
4502
4508
  if (chunk.usage) {
4503
4509
  usageSnapshot = mergeOpenAIUsage(usageSnapshot, chunk.usage);
4510
+ output.usageReported = [usageSnapshot.promptTokens, usageSnapshot.completionTokens].every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0);
4504
4511
  const normalizedUsage = normalizeOpenAIUsage(usageSnapshot);
4505
4512
  output.usage = {
4506
4513
  input: normalizedUsage.input,
@@ -4841,6 +4848,10 @@ function mapStopReason(reason) {
4841
4848
  }
4842
4849
  }
4843
4850
  function convertContentBlocks(content) {
4851
+ const hostBlock = content.find((block) => block.type === "hostContext");
4852
+ if (hostBlock) {
4853
+ throw new Error(`Unprojected hostContext block reached anthropic conversion (${hostBlock.kind}); the request path must project host envelopes to text first.`);
4854
+ }
4844
4855
  const supportedBlocks = content.map((block) => block.type === "text" || block.type === "image" ? block : { type: "text", text: `[${block.type} attachment omitted: unsupported by this provider. Re-read with a compatible model.]` });
4845
4856
  const hasImages = supportedBlocks.some((c) => c.type === "image");
4846
4857
  if (!hasImages) {
@@ -4867,6 +4878,10 @@ function convertContentBlocks(content) {
4867
4878
  return blocks;
4868
4879
  }
4869
4880
  function convertUserContentBlocks(content) {
4881
+ const hostBlock = content.find((block) => block.type === "hostContext");
4882
+ if (hostBlock) {
4883
+ throw new Error(`Unprojected hostContext block reached anthropic user-message conversion (${hostBlock.kind}); the request path must project host envelopes to text first.`);
4884
+ }
4870
4885
  const blocks = [];
4871
4886
  for (const item of content) {
4872
4887
  if (item.type === "text") {
@@ -5078,9 +5093,12 @@ var streamAnthropic = (model, context, options) => {
5078
5093
  const anthropicStream = client.messages.stream({ ...params, stream: true }, { signal: options?.signal });
5079
5094
  stream.push({ type: "start", partial: output });
5080
5095
  const blocks = output.content;
5096
+ let hasReportedInputUsage = false;
5081
5097
  for await (const event of anthropicStream) {
5082
5098
  if (event.type === "message_start") {
5083
5099
  applyAnthropicUsageUpdate(model, output.usage, event.message.usage);
5100
+ hasReportedInputUsage = typeof event.message.usage.input_tokens === "number" && Number.isFinite(event.message.usage.input_tokens) && event.message.usage.input_tokens >= 0;
5101
+ output.usageReported = false;
5084
5102
  } else if (event.type === "content_block_start") {
5085
5103
  if (event.content_block.type === "text") {
5086
5104
  const block = { type: "text", text: "", index: event.index };
@@ -5185,6 +5203,7 @@ var streamAnthropic = (model, context, options) => {
5185
5203
  output.stopReason = mapStopReason(event.delta.stop_reason);
5186
5204
  }
5187
5205
  applyAnthropicUsageUpdate(model, output.usage, event.usage);
5206
+ if (hasReportedInputUsage && typeof event.usage.output_tokens === "number" && Number.isFinite(event.usage.output_tokens) && event.usage.output_tokens >= 0) output.usageReported = true;
5188
5207
  }
5189
5208
  }
5190
5209
  if (options?.signal?.aborted) {
@@ -5318,6 +5337,9 @@ function encodeUserMessage(content, model) {
5318
5337
  return { role: "user", type: "message", content: parts };
5319
5338
  }
5320
5339
  function encodeUserContentBlock(block, model) {
5340
+ if (block.type === "hostContext") {
5341
+ throw new ResponsesProtocolError(`Unprojected hostContext block reached Responses conversion (${block.kind}); the request path must project host envelopes to text first.`);
5342
+ }
5321
5343
  const modality = block.type === "fileRef" ? block.modality : block.type;
5322
5344
  if (modality !== "text" && !model.input.includes(modality)) {
5323
5345
  throw new ResponsesProtocolError(`Current model does not support ${modality} input.`);
@@ -5445,6 +5467,68 @@ function buildResponsesCreateEvent(model, context, options) {
5445
5467
  return { type: "response.create", ...params };
5446
5468
  }
5447
5469
 
5470
+ // ../../packages/ai/src/providers/responses/responses-remote-failure.ts
5471
+ var MAX_TEXT = 4096;
5472
+ var transientCodes = /* @__PURE__ */ new Set(["server_error", "rate_limit_exceeded", "slow_down", "rate_limit_error", "server_is_overloaded", "service_unavailable_error"]);
5473
+ function responsesRemoteFailure(value, status, headers) {
5474
+ const root = isRecord2(value) ? value : {};
5475
+ const response = isRecord2(root.response) ? root.response : {};
5476
+ const error = isRecord2(root.error) ? root.error : isRecord2(response.error) ? response.error : root;
5477
+ const code = typeof error.code === "string" ? error.code.slice(0, 128).toLowerCase() : "";
5478
+ const type = typeof error.type === "string" ? error.type.slice(0, 128).toLowerCase() : "";
5479
+ const message = (typeof value === "string" ? value : typeof error.message === "string" ? error.message : "").slice(0, MAX_TEXT).toLowerCase();
5480
+ const httpStatus = status ?? root.status ?? root.statusCode;
5481
+ const fields = `${code} ${type}`;
5482
+ let category = "other";
5483
+ if (/context_length_exceeded|context_window_exceeded|context_overflow/.test(fields) || /context (?:length|window).{0,60}(?:exceed|too (?:long|large))|maximum context length|exceeds.{0,30}context window/.test(message)) category = "context_overflow";
5484
+ else if (/billing|insufficient_quota|insufficient_balance|credit|usage_limit|spend_limit/.test(fields) || /billing|(?:account|monthly) quota|exceeded your current quota|insufficient (?:credit|balance)|credit balance|spend(?:ing)? limit/.test(message)) category = "other";
5485
+ else if (typeof httpStatus === "number" && httpStatus >= 400 && httpStatus < 500 && httpStatus !== 429) category = "other";
5486
+ else if (httpStatus === 429 || typeof httpStatus === "number" && httpStatus >= 500 && httpStatus <= 599 || transientCodes.has(code) || transientCodes.has(type) || /server (?:is )?overloaded|temporarily unavailable|internal server error|rate limit (?:exceeded|reached)/.test(message)) category = "transient";
5487
+ const retryAfterMs = parseRetryAfter(headers ?? root.headers);
5488
+ return { category, ...retryAfterMs !== void 0 ? { retryAfterMs } : {} };
5489
+ }
5490
+ function parseRetryAfter(headers) {
5491
+ if (!headers || typeof headers !== "object") return void 0;
5492
+ let raw;
5493
+ if ("get" in headers && typeof headers.get === "function") raw = headers.get("retry-after");
5494
+ else for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === "retry-after") raw = value;
5495
+ if (typeof raw !== "string" || raw.length > 128) return void 0;
5496
+ const text = raw.trim();
5497
+ if (/^\d+(?:\.\d+)?$/.test(text)) {
5498
+ const delay = Number(text) * 1e3;
5499
+ return Number.isFinite(delay) ? delay : Number.MAX_VALUE;
5500
+ }
5501
+ if (!/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(text)) return void 0;
5502
+ const date = Date.parse(text);
5503
+ if (!Number.isFinite(date) || new Date(date).toUTCString() !== text) return void 0;
5504
+ return Math.max(0, date - Date.now());
5505
+ }
5506
+ async function readResponsesErrorBody(read, signal) {
5507
+ let timer;
5508
+ let onAbort;
5509
+ try {
5510
+ return await Promise.race([
5511
+ Promise.resolve().then(read).then((text) => text.slice(0, MAX_TEXT)).catch(() => ""),
5512
+ new Promise((resolve) => {
5513
+ onAbort = () => resolve("");
5514
+ if (signal.aborted) resolve("");
5515
+ else signal.addEventListener("abort", onAbort, { once: true });
5516
+ timer = setTimeout(() => resolve(""), 250);
5517
+ })
5518
+ ]);
5519
+ } finally {
5520
+ if (timer !== void 0) clearTimeout(timer);
5521
+ if (onAbort) signal.removeEventListener("abort", onAbort);
5522
+ }
5523
+ }
5524
+ function parseResponsesErrorBody(text) {
5525
+ try {
5526
+ return JSON.parse(text);
5527
+ } catch {
5528
+ return text;
5529
+ }
5530
+ }
5531
+
5448
5532
  // ../../packages/ai/src/providers/responses/responses-usage.ts
5449
5533
  function tokenCount(value) {
5450
5534
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
@@ -5515,6 +5599,19 @@ var ResponsesEventReducer = class {
5515
5599
  }
5516
5600
  /** Feed one raw wire event; returns the AssistantMessageEvents to emit. */
5517
5601
  feed(raw) {
5602
+ if (this.terminal) return [];
5603
+ try {
5604
+ const events = this.feedInbound(raw);
5605
+ if (this.isTerminal && this.output.stopReason === "error") {
5606
+ this.output.remoteFailure = responsesRemoteFailure(raw);
5607
+ }
5608
+ return events;
5609
+ } catch (error) {
5610
+ this.terminal = null;
5611
+ throw error;
5612
+ }
5613
+ }
5614
+ feedInbound(raw) {
5518
5615
  const events = [];
5519
5616
  if (!isResponsesEventLike(raw)) {
5520
5617
  throw new ResponsesProtocolError("Responses stream returned an event without a string type.");
@@ -6033,6 +6130,7 @@ function streamResponsesSSE(model, context, options) {
6033
6130
  let providerErrorMessage;
6034
6131
  let transportLost = false;
6035
6132
  let endedWithoutTerminal = false;
6133
+ let responseId;
6036
6134
  const requestAbort = new AbortController();
6037
6135
  const onUserAbort = () => requestAbort.abort();
6038
6136
  const clearTotalTimer = () => {
@@ -6082,8 +6180,8 @@ function streamResponsesSSE(model, context, options) {
6082
6180
  timeoutKind: "request_total",
6083
6181
  terminalAccepted: false,
6084
6182
  cancelSource: "internal_timeout",
6085
- retryAllowed: !submitted,
6086
- retryDecision: submitted ? "not_retryable_after_submission" : "not_submitted"
6183
+ retryAllowed: !!reducer.message.remoteFailure || !submitted,
6184
+ retryDecision: reducer.message.remoteFailure ? `remote_${reducer.message.remoteFailure.category}` : submitted ? "not_retryable_after_submission" : "not_submitted"
6087
6185
  });
6088
6186
  requestAbort.abort();
6089
6187
  }, resolved.requestTimeoutMs);
@@ -6128,6 +6226,7 @@ function streamResponsesSSE(model, context, options) {
6128
6226
  const status = error && typeof error === "object" ? error.status ?? error.statusCode : void 0;
6129
6227
  if (typeof status === "number" && status >= 400) {
6130
6228
  providerRejected = true;
6229
+ reducer.message.remoteFailure = responsesRemoteFailure(error, status);
6131
6230
  providerErrorMessage = `API error ${status}: ${error instanceof Error ? error.message : "request rejected"}`;
6132
6231
  }
6133
6232
  transportLost = !providerRejected && !(error instanceof ResponsesProtocolError || error instanceof SyntaxError);
@@ -6136,8 +6235,13 @@ function streamResponsesSSE(model, context, options) {
6136
6235
  if (!response.ok) {
6137
6236
  providerRejected = true;
6138
6237
  providerErrorMessage = `API error ${response.status}`;
6139
- const errorBody = await response.text().catch(() => "");
6140
- if (errorBody) providerErrorMessage += `: ${errorBody}`;
6238
+ reducer.message.remoteFailure = responsesRemoteFailure({}, response.status, response.headers);
6239
+ const errorBody = await readResponsesErrorBody(() => response.text(), requestAbort.signal);
6240
+ if (errorBody) {
6241
+ providerErrorMessage += `: ${errorBody}`;
6242
+ reducer.message.remoteFailure = responsesRemoteFailure(parseResponsesErrorBody(errorBody), response.status, response.headers);
6243
+ }
6244
+ requestAbort.abort();
6141
6245
  throw new Error(providerErrorMessage);
6142
6246
  }
6143
6247
  if (!response.readSSELines) {
@@ -6161,21 +6265,39 @@ function streamResponsesSSE(model, context, options) {
6161
6265
  if (data === "[DONE]") {
6162
6266
  continue;
6163
6267
  }
6164
- let event;
6268
+ if (requestAbort.signal.aborted) {
6269
+ throw new Error(internalTimeout ? `Responses request timeout (${internalTimeout})` : "Request was aborted");
6270
+ }
6271
+ let events;
6165
6272
  try {
6166
- event = JSON.parse(data);
6273
+ let event;
6274
+ try {
6275
+ event = JSON.parse(data);
6276
+ } catch (error) {
6277
+ const message = error instanceof Error ? error.message : String(error);
6278
+ throw new ResponsesProtocolError(`Responses SSE stream returned malformed JSON: ${message}`);
6279
+ }
6280
+ if (!isResponsesEventLike(event)) {
6281
+ throw new ResponsesProtocolError("Responses SSE payload is not an event object with a string type.");
6282
+ }
6283
+ if (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning")) {
6284
+ sawModelContent = true;
6285
+ }
6286
+ const incomingResponse = event.response;
6287
+ if (event.type === "response.created" && (!isRecord2(incomingResponse) || typeof incomingResponse.id !== "string" || !incomingResponse.id)) {
6288
+ throw new ResponsesProtocolError("response.created requires a response id.");
6289
+ }
6290
+ if (isRecord2(incomingResponse) && typeof incomingResponse.id === "string") {
6291
+ if (responseId && (incomingResponse.id !== responseId || event.type === "response.created")) {
6292
+ throw new ResponsesProtocolError("Response identity changed within a request.");
6293
+ }
6294
+ responseId = incomingResponse.id;
6295
+ }
6296
+ events = reducer.feed(event);
6167
6297
  } catch (error) {
6168
- const message = error instanceof Error ? error.message : String(error);
6169
- throw new ResponsesProtocolError(`Responses SSE stream returned malformed JSON: ${message}`);
6170
- }
6171
- if (!isResponsesEventLike(event)) {
6172
- throw new ResponsesProtocolError("Responses SSE payload is not an event object with a string type.");
6173
- }
6174
- if (typeof event.type === "string" && (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning"))) {
6175
- sawModelContent = true;
6298
+ reducer.message.remoteFailure = { category: "other" };
6299
+ throw error;
6176
6300
  }
6177
- const events = reducer.feed(event);
6178
- if (reducer.isTerminal && reducer.message.stopReason === "error") reducer.message.retryable = false;
6179
6301
  for (const assistantEvent of events) {
6180
6302
  stream.push(assistantEvent);
6181
6303
  }
@@ -6183,7 +6305,7 @@ function streamResponsesSSE(model, context, options) {
6183
6305
  terminalAccepted = true;
6184
6306
  phase = "terminal_accepted";
6185
6307
  clearTotalTimer();
6186
- lifecycle({ retryAllowed: reducer.message.retryable !== false, retryDecision: "terminal" });
6308
+ lifecycle({ retryAllowed: reducer.message.retryable !== false, retryDecision: reducer.message.remoteFailure ? `remote_${reducer.message.remoteFailure.category}` : "terminal" });
6187
6309
  break;
6188
6310
  }
6189
6311
  }
@@ -6204,6 +6326,7 @@ function streamResponsesSSE(model, context, options) {
6204
6326
  const message = reducer.message;
6205
6327
  const userAborted = options?.signal?.aborted === true;
6206
6328
  message.stopReason = userAborted ? "aborted" : "error";
6329
+ if (userAborted) delete message.remoteFailure;
6207
6330
  const adapterTimeoutKind = error instanceof Error ? error.timeoutKind : void 0;
6208
6331
  if (!internalTimeout && (adapterTimeoutKind === "first_byte" || adapterTimeoutKind === "stream_idle")) {
6209
6332
  internalTimeout = adapterTimeoutKind === "first_byte" ? "first_response" : "stream_idle";
@@ -6212,19 +6335,19 @@ function streamResponsesSSE(model, context, options) {
6212
6335
  message.errorMessage = providerErrorMessage ?? (internalTimeout && !protocolFailure ? `Responses request timeout (${internalTimeout}) after ${Math.round(monotonicNow() - startedAt)}ms` : error instanceof Error ? error.message : JSON.stringify(error));
6213
6336
  const compat = getResponsesCompat(model);
6214
6337
  const streamLost = (endedWithoutTerminal || transportLost) && !protocolFailure && !internalTimeout && submitted && !localFailure && !providerRejected && message.stopReason === "error";
6215
- if (internalTimeout && !protocolFailure && submitted && !providerRejected && !localFailure && !userAborted && compat.timeoutReplay) {
6338
+ if (internalTimeout && !message.remoteFailure && !protocolFailure && submitted && !providerRejected && !localFailure && !userAborted && compat.timeoutReplay) {
6216
6339
  message.replayEligible = true;
6217
6340
  message.replayClass = internalTimeout;
6218
- } else if (streamLost && compat.timeoutReplay) {
6341
+ } else if (streamLost && !message.remoteFailure && compat.timeoutReplay) {
6219
6342
  message.replayEligible = true;
6220
6343
  message.replayClass = receivedMessageCount === 0 ? "stream_lost" : "stream_lost_partial";
6221
6344
  }
6222
- if ((submitted || localFailure) && !(streamLost && receivedMessageCount === 0)) message.retryable = false;
6345
+ if (!message.remoteFailure && (userAborted || (submitted || localFailure) && !(streamLost && receivedMessageCount === 0))) message.retryable = false;
6223
6346
  phase = "cleanup";
6224
6347
  lifecycle({
6225
6348
  timeoutKind: internalTimeout,
6226
6349
  retryAllowed: !userAborted && !localFailure && message.retryable !== false,
6227
- retryDecision: streamLost ? receivedMessageCount === 0 ? message.replayEligible ? "replay_exempt_stream_lost" : "stream_lost_no_evidence" : message.replayEligible ? "replay_exempt_after_submission" : "not_retryable_after_submission" : submitted ? "not_retryable_after_submission" : localFailure ? "local_configuration_error" : "not_submitted",
6350
+ retryDecision: message.remoteFailure ? `remote_${message.remoteFailure.category}` : streamLost ? receivedMessageCount === 0 ? message.replayEligible ? "replay_exempt_stream_lost" : "stream_lost_no_evidence" : message.replayEligible ? "replay_exempt_after_submission" : "not_retryable_after_submission" : submitted ? "not_retryable_after_submission" : localFailure ? "local_configuration_error" : "not_submitted",
6228
6351
  cancelSource: userAborted ? "user" : internalTimeout ? "internal_timeout" : "none",
6229
6352
  cleanupOutcome: "request_abandoned"
6230
6353
  });
@@ -6431,6 +6554,7 @@ function streamResponsesWS(model, context, options) {
6431
6554
  if (state.busy) {
6432
6555
  reducer.message.stopReason = "error";
6433
6556
  reducer.message.errorMessage = "Responses owner already has an in-flight request.";
6557
+ reducer.message.retryable = false;
6434
6558
  stream.push({ type: "error", reason: "error", error: reducer.message });
6435
6559
  stream.end();
6436
6560
  return stream;
@@ -6687,12 +6811,18 @@ function streamResponsesWS(model, context, options) {
6687
6811
  });
6688
6812
  } catch (error) {
6689
6813
  if (error instanceof WebSocketTransportError && error.kind === "timeout") timedOut = "connect";
6814
+ const status = error && typeof error === "object" ? error.status ?? error.statusCode : void 0;
6815
+ if (typeof status === "number" && status >= 400) {
6816
+ providerRejected = true;
6817
+ reducer.message.remoteFailure = responsesRemoteFailure(error, status);
6818
+ }
6690
6819
  if (error instanceof WebSocketTransportError && error.kind === "handshake" && !options?.signal?.aborted && !state.abort.signal.aborted) {
6691
- const status = error.status ?? 0;
6692
- providerRejected = status > 0;
6693
- const endpointHasNoWebsockets = options?.transport === "auto" && [404, 405, 426, 501].includes(status);
6694
- const wsEdgeRefused = status === 0 || status === 429 || status >= 500 && status <= 599;
6820
+ const status2 = error.status ?? 0;
6821
+ providerRejected = status2 > 0;
6822
+ const endpointHasNoWebsockets = options?.transport === "auto" && [404, 405, 426, 501].includes(status2);
6823
+ const wsEdgeRefused = status2 === 0 || status2 === 429 || status2 >= 500 && status2 <= 599;
6695
6824
  if (endpointHasNoWebsockets || wsEdgeRefused) {
6825
+ delete reducer.message.remoteFailure;
6696
6826
  await runSSEFallback();
6697
6827
  return;
6698
6828
  }
@@ -6774,32 +6904,38 @@ function streamResponsesWS(model, context, options) {
6774
6904
  if (state.abort.signal.aborted || requestAbort.signal.aborted) {
6775
6905
  throw new Error(timedOut ? `Responses request timeout (${timedOut})` : "Request aborted");
6776
6906
  }
6777
- if (message.isBinary) throw new ResponsesProtocolError("Responses requires JSON text messages.");
6778
- const event = JSON.parse(message.data);
6779
- if (!isRecord2(event)) throw new ResponsesProtocolError("Invalid Responses WebSocket event.");
6780
- if (typeof event.type === "string") {
6781
- if (event.type === "response.created") sawResponseCreated = true;
6782
- else if (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning")) sawModelContent = true;
6783
- }
6784
- if (!started && attempt === 0 && payload.previous_response_id && event.type === "error" && event.status === 400 && isRecord2(event.error) && event.error.code === "previous_response_not_found" && event.error.param === "previous_response_id") {
6785
- delete payload.previous_response_id;
6786
- payload.input = fullInput;
6787
- continuationReason = "previous_response_not_found";
6788
- rebuild = true;
6789
- break;
6790
- }
6791
- if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
6792
- if (!started && event.type !== "error") {
6793
- if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || event.response.id === state.lastResponseId) {
6794
- throw new ResponsesProtocolError("Expected a new response.created before output.");
6907
+ let events;
6908
+ try {
6909
+ if (message.isBinary) throw new ResponsesProtocolError("Responses requires JSON text messages.");
6910
+ const event = JSON.parse(message.data);
6911
+ if (!isRecord2(event)) throw new ResponsesProtocolError("Invalid Responses WebSocket event.");
6912
+ if (typeof event.type === "string") {
6913
+ if (event.type === "response.created") sawResponseCreated = true;
6914
+ else if (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning")) sawModelContent = true;
6915
+ }
6916
+ if (!started && attempt === 0 && payload.previous_response_id && event.type === "error" && event.status === 400 && isRecord2(event.error) && event.error.code === "previous_response_not_found" && event.error.param === "previous_response_id") {
6917
+ delete payload.previous_response_id;
6918
+ payload.input = fullInput;
6919
+ continuationReason = "previous_response_not_found";
6920
+ rebuild = true;
6921
+ break;
6922
+ }
6923
+ if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
6924
+ if (started && event.type === "response.created") throw new ResponsesProtocolError("Duplicate response.created within a request.");
6925
+ if (!started && event.type !== "error") {
6926
+ if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || !event.response.id || event.response.id === state.lastResponseId) {
6927
+ throw new ResponsesProtocolError("Expected a new response.created before output.");
6928
+ }
6929
+ started = true;
6930
+ responseId = event.response.id;
6795
6931
  }
6796
- started = true;
6797
- responseId = event.response.id;
6932
+ if (responseId && isRecord2(event.response) && event.response.id !== responseId) throw new ResponsesProtocolError("Response identity changed within a request.");
6933
+ events = reducer.feed(event);
6934
+ } catch (error) {
6935
+ reducer.message.remoteFailure = { category: "other" };
6936
+ throw error;
6798
6937
  }
6799
- if (responseId && isRecord2(event.response) && event.response.id !== responseId) throw new ResponsesProtocolError("Response identity changed within a request.");
6800
- const events = reducer.feed(event);
6801
6938
  if (reducer.isTerminal) {
6802
- if (reducer.message.stopReason === "error") reducer.message.retryable = false;
6803
6939
  const response = reducer.terminalResponse;
6804
6940
  state.lastResponseId = response?.id;
6805
6941
  if (!options?.onPayload && compat.responsesContinuation && identity && response?.status === "completed" && reducer.message.stopReason !== "error") {
@@ -6818,10 +6954,11 @@ function streamResponsesWS(model, context, options) {
6818
6954
  }
6819
6955
  finished = true;
6820
6956
  clearRequestTimers();
6957
+ retryDecision = reducer.message.remoteFailure ? `remote_${reducer.message.remoteFailure.category}` : "terminal";
6821
6958
  lifecycle("terminal_accepted", {
6822
6959
  terminalAccepted: true,
6823
6960
  retryAllowed: reducer.message.retryable !== false,
6824
- retryDecision: "terminal"
6961
+ retryDecision
6825
6962
  });
6826
6963
  for (const e of events) publish(e);
6827
6964
  break;
@@ -6847,28 +6984,30 @@ function streamResponsesWS(model, context, options) {
6847
6984
  const userAborted = options?.signal?.aborted === true;
6848
6985
  const ownerDisposed = !userAborted && !timedOut && state.abort.signal.aborted;
6849
6986
  message.stopReason = userAborted || ownerDisposed ? "aborted" : "error";
6987
+ if (userAborted || ownerDisposed) delete message.remoteFailure;
6850
6988
  const protocolFailure = !endedWithoutTerminal && (error instanceof ResponsesProtocolError || error instanceof SyntaxError || !transportLost && error instanceof TypeError || error instanceof WebSocketTransportError && (error.kind === "protocol" || error.kind === "overflow"));
6851
6989
  const cause = providerRejected && error instanceof WebSocketTransportError ? `${error.message} (HTTP status ${error.status})` : timedOut && !protocolFailure ? `Responses request timeout (${timedOut}) after ${ageMs()}ms` : error instanceof Error ? error.message : String(error);
6852
6990
  message.errorMessage = cause;
6853
6991
  const streamLost = (endedWithoutTerminal || transportLost) && !protocolFailure && sent && !timedOut && !providerRejected && message.stopReason === "error";
6854
- if (timedOut && timedOut !== "connect" && !protocolFailure && !localFailure && sent && !providerRejected && !userAborted && !ownerDisposed && compat.timeoutReplay) {
6992
+ if (timedOut && timedOut !== "connect" && !message.remoteFailure && !protocolFailure && !localFailure && sent && !providerRejected && !userAborted && !ownerDisposed && compat.timeoutReplay) {
6855
6993
  message.replayEligible = true;
6856
6994
  message.replayClass = timedOut;
6857
- } else if (streamLost && compat.timeoutReplay) {
6995
+ } else if (streamLost && !message.remoteFailure && compat.timeoutReplay) {
6858
6996
  message.replayEligible = true;
6859
6997
  message.replayClass = receivedMessageCount === 0 ? "stream_lost" : "stream_lost_partial";
6860
6998
  }
6861
- if (localFailure || providerRejected || protocolFailure) {
6999
+ if (!message.remoteFailure && (localFailure || providerRejected || protocolFailure || userAborted || ownerDisposed)) {
6862
7000
  message.retryable = false;
6863
7001
  }
6864
7002
  if (sent) {
6865
7003
  if (streamLost && receivedMessageCount === 0) {
6866
7004
  retryDecision = message.replayEligible ? "replay_exempt_stream_lost" : "stream_lost_no_evidence";
6867
7005
  } else {
6868
- message.retryable = false;
7006
+ if (!message.remoteFailure) message.retryable = false;
6869
7007
  if (!timedOut) retryDecision = message.replayEligible ? "replay_exempt_after_submission" : "not_retryable_after_submission";
6870
7008
  }
6871
7009
  }
7010
+ if (message.remoteFailure) retryDecision = `remote_${message.remoteFailure.category}`;
6872
7011
  cleanupCancelSource = userAborted ? "user" : timedOut ? "internal_timeout" : ownerDisposed ? "owner_disposal" : "none";
6873
7012
  stream.push({ type: "error", reason: message.stopReason, error: message });
6874
7013
  }
@@ -6890,7 +7029,7 @@ function streamResponsesWS(model, context, options) {
6890
7029
  terminalAccepted: finished,
6891
7030
  timeoutKind: timedOut,
6892
7031
  submissionAttempted: sent,
6893
- retryAllowed: !finished && reducer.message.stopReason !== "aborted" && reducer.message.retryable !== false,
7032
+ retryAllowed: (!finished || !!reducer.message.remoteFailure) && reducer.message.stopReason !== "aborted" && reducer.message.retryable !== false,
6894
7033
  cancelSource,
6895
7034
  retryDecision,
6896
7035
  cleanupOutcome