@ouro.bot/cli 0.1.0-alpha.235 → 0.1.0-alpha.236

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.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.236",
6
+ "changes": [
7
+ "fix(engine): provider-aware retry classification — retries now use both generic isTransientError AND provider classifyError",
8
+ "feat(engine): retry instrumentation — engine.provider_retry / engine.provider_retry_skip events with full diagnostics"
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.235",
6
13
  "changes": [
@@ -959,20 +959,48 @@ async function runAgent(messages, callbacks, channel, signal, options) {
959
959
  callbacks.onError(new Error("context trimmed, retrying..."), "transient");
960
960
  continue;
961
961
  }
962
- // Transient errors: retry with exponential backoff
963
- if (isTransientError(e) && retryCount < MAX_RETRIES) {
962
+ // Transient errors: retry with exponential backoff.
963
+ // Two-layer detection: generic isTransientError (HTTP codes, network patterns)
964
+ // PLUS provider-specific classifyError (catches SDK-wrapped errors that
965
+ // generic detection might miss).
966
+ const errorForClassification = e instanceof Error ? e : /* v8 ignore next -- defensive @preserve */ new Error(String(e));
967
+ let providerClassification;
968
+ try {
969
+ providerClassification = providerRuntime.classifyError(errorForClassification);
970
+ }
971
+ catch {
972
+ /* v8 ignore next -- defensive: classifyError should not throw @preserve */
973
+ providerClassification = "unknown";
974
+ }
975
+ const retryableByClassification = providerClassification === "rate-limit"
976
+ || providerClassification === "server-error"
977
+ || providerClassification === "network-error";
978
+ const retryableByGeneric = isTransientError(e);
979
+ const shouldRetry = (retryableByGeneric || retryableByClassification) && retryCount < MAX_RETRIES;
980
+ (0, runtime_1.emitNervesEvent)({
981
+ level: shouldRetry ? "info" : "warn",
982
+ event: shouldRetry ? "engine.provider_retry" : "engine.provider_retry_skip",
983
+ component: "engine",
984
+ message: shouldRetry
985
+ ? `provider error is retryable (attempt ${retryCount + 1}/${MAX_RETRIES})`
986
+ : `provider error is not retryable or retries exhausted`,
987
+ meta: {
988
+ provider: providerRuntime.id,
989
+ model: providerRuntime.model,
990
+ retryCount,
991
+ maxRetries: MAX_RETRIES,
992
+ retryableByGeneric,
993
+ retryableByClassification,
994
+ providerClassification,
995
+ errorMessage: errorForClassification.message.slice(0, 200),
996
+ httpStatus: e.status ?? null,
997
+ },
998
+ });
999
+ if (shouldRetry) {
964
1000
  retryCount++;
965
1001
  const delay = RETRY_BASE_MS * Math.pow(2, retryCount - 1);
966
- let classification;
967
- try {
968
- classification = providerRuntime.classifyError(e instanceof Error ? e : /* v8 ignore next -- defensive: errors are always Error instances @preserve */ new Error(String(e)));
969
- }
970
- catch {
971
- /* v8 ignore next -- defensive: classifyError should not throw @preserve */
972
- classification = classifyTransientError(e);
973
- }
974
1002
  /* v8 ignore next -- defensive: all classifications have labels @preserve */
975
- const cause = TRANSIENT_RETRY_LABELS[classification] ?? classification;
1003
+ const cause = TRANSIENT_RETRY_LABELS[providerClassification] ?? providerClassification;
976
1004
  callbacks.onError(new Error(`${cause}, retrying in ${delay / 1000}s (${retryCount}/${MAX_RETRIES})...`), "transient");
977
1005
  // Wait with abort support
978
1006
  const aborted = await new Promise((resolve) => {
@@ -995,14 +1023,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
995
1023
  providerRuntime.resetTurnState(messages);
996
1024
  continue;
997
1025
  }
998
- terminalError = e instanceof Error ? e : new Error(String(e));
999
- try {
1000
- terminalErrorClassification = providerRuntime.classifyError(terminalError);
1001
- }
1002
- catch {
1003
- /* v8 ignore next -- defensive: classifyError should not throw @preserve */
1004
- terminalErrorClassification = "unknown";
1005
- }
1026
+ terminalError = errorForClassification;
1027
+ terminalErrorClassification = providerClassification;
1006
1028
  /* v8 ignore start — auth-failure guidance: tested via provider error classification tests @preserve */
1007
1029
  if (terminalErrorClassification === "auth-failure") {
1008
1030
  const agentName = (0, identity_2.getAgentName)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.235",
3
+ "version": "0.1.0-alpha.236",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",