@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 +7 -0
- package/dist/heart/core.js +41 -19
- package/package.json +1 -1
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": [
|
package/dist/heart/core.js
CHANGED
|
@@ -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
|
-
|
|
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[
|
|
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 =
|
|
999
|
-
|
|
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)();
|