@skydiveai/pi-server 0.1.0-beta.1310 → 0.1.0-beta.1738

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.
Files changed (2) hide show
  1. package/dist/index.mjs +75 -9
  2. package/package.json +2 -3
package/dist/index.mjs CHANGED
@@ -373,6 +373,57 @@ async function runConversation({ session, prompt, images, log, postPrompt }) {
373
373
  });
374
374
  }
375
375
  /**
376
+ * Track the agent session's built-in model-call auto-retry so it leaves a
377
+ * trace.
378
+ *
379
+ * `AgentSession` already restarts a failed assistant turn in place (via
380
+ * `agent.continue()`, so no prompt is replayed and no tool re-executes) for the
381
+ * transient provider/transport failures pi classifies as retryable -- dropped
382
+ * streams, `terminated`, 5xx, overloaded, rate limits. It is on by default,
383
+ * with its own budget and backoff, and it emits `auto_retry_start` /
384
+ * `auto_retry_end` around each attempt.
385
+ *
386
+ * Nothing consumed those events, so a retry left no trace anywhere: a call that
387
+ * succeeded first try and one that burned the whole budget before failing
388
+ * produced the same terminal error, and the fleet-wide retry rate was
389
+ * unmeasurable. That gap is why a 2026-08-16 investigation into three runs lost
390
+ * to `provider_error: terminated` could not tell whether the budget had run out
391
+ * (ANY-7101).
392
+ *
393
+ * Exposed as a handler rather than its own `session.subscribe` call so each
394
+ * protocol feeds it from the single subscription it already owns -- one
395
+ * subscriber, explicit ordering.
396
+ *
397
+ * `attempts()` reports what has been spent so far, so a terminal error can
398
+ * carry the count to the worker, where it lands in a log group we can query
399
+ * fleet-wide (the sandbox's own logs are not).
400
+ */
401
+ function createAutoRetryObserver(log) {
402
+ let attempts = 0;
403
+ return {
404
+ observe(event) {
405
+ if (event.type === "auto_retry_start") {
406
+ attempts = typeof event.attempt === "number" ? event.attempt : attempts + 1;
407
+ log.warn({
408
+ event: "model_call_auto_retry",
409
+ attempt: event.attempt,
410
+ max_attempts: event.maxAttempts,
411
+ delay_ms: event.delayMs,
412
+ error_message: event.errorMessage
413
+ }, "retrying failed model call in place");
414
+ return;
415
+ }
416
+ if (event.type === "auto_retry_end") log.warn({
417
+ event: "model_call_auto_retry_end",
418
+ attempt: event.attempt,
419
+ success: event.success,
420
+ final_error: event.finalError
421
+ }, event.success ? "model call recovered after retry" : "model call retries exhausted");
422
+ },
423
+ attempts: () => attempts
424
+ };
425
+ }
426
+ /**
376
427
  * Hard-stop the in-flight turn for a session. Shared by every protocol's
377
428
  * `/:id/abort` route: a cancel signals the stop explicitly instead of relying
378
429
  * on a dropped connection. `session.abort()` interrupts the turn and resolves
@@ -1159,12 +1210,14 @@ function runStream$2({ session, sm, prompt, images, id, sessionId, modelName, lo
1159
1210
  textBlockOpen = false;
1160
1211
  };
1161
1212
  let capturedModelError = null;
1213
+ const autoRetry = createAutoRetryObserver(log);
1162
1214
  const emitProviderError = (providerError) => {
1163
1215
  log.warn({
1164
1216
  event: "model_provider_error",
1165
1217
  code: providerError.code,
1166
1218
  upstream_status: providerError.upstreamStatus,
1167
- provider: providerError.provider
1219
+ provider: providerError.provider,
1220
+ retry_attempts: autoRetry.attempts()
1168
1221
  }, "forwarding model-provider error to client");
1169
1222
  sseEvent(writer, "error", {
1170
1223
  type: "error",
@@ -1174,13 +1227,15 @@ function runStream$2({ session, sm, prompt, images, id, sessionId, modelName, lo
1174
1227
  x_model_provider_error: {
1175
1228
  code: providerError.code,
1176
1229
  provider: providerError.provider,
1177
- upstream_status: providerError.upstreamStatus
1230
+ upstream_status: providerError.upstreamStatus,
1231
+ retry_attempts: autoRetry.attempts()
1178
1232
  }
1179
1233
  }
1180
1234
  });
1181
1235
  };
1182
1236
  session.subscribe((event) => {
1183
1237
  const ev = event;
1238
+ autoRetry.observe(ev);
1184
1239
  const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
1185
1240
  if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
1186
1241
  if (ev.type !== "message_update") return;
@@ -1856,6 +1911,7 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
1856
1911
  let usage = null;
1857
1912
  let lastFollowUpCount = 0;
1858
1913
  let capturedModelError = null;
1914
+ const autoRetry = createAutoRetryObserver(log);
1859
1915
  const sendChunk = (delta, finishReason) => {
1860
1916
  if (firstChunkAt === null) {
1861
1917
  firstChunkAt = performance.now();
@@ -1886,6 +1942,7 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
1886
1942
  let _evCount = 0;
1887
1943
  session.subscribe((event) => {
1888
1944
  const ev = event;
1945
+ autoRetry.observe(ev);
1889
1946
  const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
1890
1947
  if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
1891
1948
  if (ev.type === "queue_update") {
@@ -2013,13 +2070,15 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
2013
2070
  code: providerError.code,
2014
2071
  upstream_status: providerError.upstreamStatus,
2015
2072
  provider: providerError.provider,
2073
+ retry_attempts: autoRetry.attempts(),
2016
2074
  source: "stop_reason_error"
2017
2075
  }, "forwarding model-provider error to client");
2018
2076
  emitModelProviderError({
2019
2077
  writer,
2020
2078
  sessionId,
2021
2079
  created,
2022
- providerError
2080
+ providerError,
2081
+ retryAttempts: autoRetry.attempts()
2023
2082
  });
2024
2083
  writer.write("data: [DONE]\n\n");
2025
2084
  } else {
@@ -2058,13 +2117,15 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
2058
2117
  chatcmpl_id: sessionId,
2059
2118
  code: providerError.code,
2060
2119
  upstream_status: providerError.upstreamStatus,
2061
- provider: providerError.provider
2120
+ provider: providerError.provider,
2121
+ retry_attempts: autoRetry.attempts()
2062
2122
  }, "forwarding model-provider error to client");
2063
2123
  emitModelProviderError({
2064
2124
  writer,
2065
2125
  sessionId,
2066
2126
  created,
2067
- providerError
2127
+ providerError,
2128
+ retryAttempts: autoRetry.attempts()
2068
2129
  });
2069
2130
  } else sendChunk({ content: `\n[error: ${err?.message ?? err}]` }, "stop");
2070
2131
  writer.write("data: [DONE]\n\n");
@@ -2105,7 +2166,7 @@ function emitSessionMessagesTrailer({ writer, sm, baselineMessageCount, sessionI
2105
2166
  * cleanly closes the stream for any OpenAI-shaped reader that ignores the
2106
2167
  * extension field.
2107
2168
  */
2108
- function emitModelProviderError({ writer, sessionId, created, providerError }) {
2169
+ function emitModelProviderError({ writer, sessionId, created, providerError, retryAttempts }) {
2109
2170
  const chunk = {
2110
2171
  id: sessionId,
2111
2172
  object: "chat.completion.chunk",
@@ -2120,7 +2181,8 @@ function emitModelProviderError({ writer, sessionId, created, providerError }) {
2120
2181
  message: providerError.message,
2121
2182
  provider: providerError.provider,
2122
2183
  type: providerError.type,
2123
- upstream_status: providerError.upstreamStatus
2184
+ upstream_status: providerError.upstreamStatus,
2185
+ retry_attempts: retryAttempts
2124
2186
  }
2125
2187
  };
2126
2188
  writer.write(`data: ${JSON.stringify(chunk)}\n\n`);
@@ -2500,6 +2562,7 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
2500
2562
  const tcByContentIdx = /* @__PURE__ */ new Map();
2501
2563
  let nextOutputIndex = 0;
2502
2564
  let capturedModelError = null;
2565
+ const autoRetry = createAutoRetryObserver(log);
2503
2566
  const send = (event) => {
2504
2567
  writer.write(`data: ${JSON.stringify(event)}\n\n`);
2505
2568
  };
@@ -2508,7 +2571,8 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
2508
2571
  event: "model_provider_error",
2509
2572
  code: providerError.code,
2510
2573
  upstream_status: providerError.upstreamStatus,
2511
- provider: providerError.provider
2574
+ provider: providerError.provider,
2575
+ retry_attempts: autoRetry.attempts()
2512
2576
  }, "forwarding model-provider error to client");
2513
2577
  send({
2514
2578
  type: "response.failed",
@@ -2522,7 +2586,8 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
2522
2586
  x_model_provider_error: {
2523
2587
  provider: providerError.provider,
2524
2588
  type: providerError.type,
2525
- upstream_status: providerError.upstreamStatus
2589
+ upstream_status: providerError.upstreamStatus,
2590
+ retry_attempts: autoRetry.attempts()
2526
2591
  }
2527
2592
  }
2528
2593
  }
@@ -2558,6 +2623,7 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
2558
2623
  };
2559
2624
  session.subscribe((event) => {
2560
2625
  const ev = event;
2626
+ autoRetry.observe(ev);
2561
2627
  const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
2562
2628
  if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
2563
2629
  if (ev.type !== "message_update") return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/pi-server",
3
- "version": "0.1.0-beta.1310",
3
+ "version": "0.1.0-beta.1738",
4
4
  "homepage": "https://skydive.com",
5
5
  "license": "MIT",
6
6
  "author": "Create, Inc.",
@@ -29,8 +29,7 @@
29
29
  "build": "tsdown",
30
30
  "typecheck": "tsgo --noEmit",
31
31
  "test:unit": "vitest run --passWithNoTests",
32
- "test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests",
33
- "publish:system-artifacts": "doppler run --preserve-env -- node ../../scripts/anyone/publish-system-artifact.mjs"
32
+ "test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests"
34
33
  },
35
34
  "dependencies": {
36
35
  "@a2a-js/sdk": "^0.3.13",