@sagentlab/navarch-runtime 0.1.77 → 0.1.78
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/dist/completion-retry.cjs +48 -0
- package/dist/session.cjs +6 -3
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMPLETION_TRANSIENT_RETRY_DELAYS_MS = void 0;
|
|
4
|
+
exports.isTransientCompletionError = isTransientCompletionError;
|
|
5
|
+
exports.completeWithTransientRetry = completeWithTransientRetry;
|
|
6
|
+
const api_cjs_1 = require("./api.cjs");
|
|
7
|
+
/**
|
|
8
|
+
* Backoff before each re-send of a completion that failed in transit. About
|
|
9
|
+
* two minutes in total: long enough to ride out a Wi-Fi drop or an edge blip,
|
|
10
|
+
* short enough that the lease is still live when the report finally lands.
|
|
11
|
+
*/
|
|
12
|
+
exports.COMPLETION_TRANSIENT_RETRY_DELAYS_MS = [2_000, 5_000, 15_000, 30_000, 60_000];
|
|
13
|
+
/** Statuses the edge or a gateway answers for an unreachable origin; the completion route never chose them. */
|
|
14
|
+
const TRANSIENT_STATUSES = new Set([408, 429, 502, 503, 504, 520, 521, 522, 523, 524]);
|
|
15
|
+
/**
|
|
16
|
+
* A failure that says nothing about the completion itself: the request never
|
|
17
|
+
* got an answer from the completion route. A plain 500 is the route's own
|
|
18
|
+
* answer and keeps the existing crash path.
|
|
19
|
+
*/
|
|
20
|
+
function isTransientCompletionError(err) {
|
|
21
|
+
return (err instanceof api_cjs_1.NavarchTransportError ||
|
|
22
|
+
(err instanceof api_cjs_1.NavarchApiError && TRANSIENT_STATUSES.has(err.status)));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sends a lease completion, re-sending it while the control plane is
|
|
26
|
+
* unreachable. Without this, one dropped connection turned a finished session
|
|
27
|
+
* (pull request already open) into `Session crashed:`, which spent an
|
|
28
|
+
* infrastructure retry and discarded the report and evidence the review loop
|
|
29
|
+
* needed (carova#310). A re-send whose earlier copy did land gets the
|
|
30
|
+
* `already_released` 409, which callers already treat as success.
|
|
31
|
+
*/
|
|
32
|
+
async function completeWithTransientRetry(send, options = {}) {
|
|
33
|
+
const delays = options.delaysMs ?? exports.COMPLETION_TRANSIENT_RETRY_DELAYS_MS;
|
|
34
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
35
|
+
for (let attempt = 0;; attempt += 1) {
|
|
36
|
+
try {
|
|
37
|
+
await send();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const delayMs = delays[attempt];
|
|
42
|
+
if (delayMs === undefined || !isTransientCompletionError(err))
|
|
43
|
+
throw err;
|
|
44
|
+
options.onRetry?.(attempt + 1, delayMs, err);
|
|
45
|
+
await sleep(delayMs);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/session.cjs
CHANGED
|
@@ -25,6 +25,7 @@ const delivery_cjs_1 = require("./delivery.cjs");
|
|
|
25
25
|
const worktree_guard_cjs_1 = require("./worktree-guard.cjs");
|
|
26
26
|
const adapter_watchdog_cjs_1 = require("./adapter-watchdog.cjs");
|
|
27
27
|
const lease_heartbeat_cjs_1 = require("./lease-heartbeat.cjs");
|
|
28
|
+
const completion_retry_cjs_1 = require("./completion-retry.cjs");
|
|
28
29
|
const adapter_capacity_cjs_1 = require("./adapter-capacity.cjs");
|
|
29
30
|
const agent_auth_cjs_1 = require("./agent-auth.cjs");
|
|
30
31
|
const browser_evidence_cjs_1 = require("./browser-evidence.cjs");
|
|
@@ -730,7 +731,9 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
|
|
|
730
731
|
transcript_url: completion.transcript_url,
|
|
731
732
|
};
|
|
732
733
|
try {
|
|
733
|
-
await api.completeLease(leaseId, completion)
|
|
734
|
+
await (0, completion_retry_cjs_1.completeWithTransientRetry)(() => api.completeLease(leaseId, completion), {
|
|
735
|
+
onRetry: (attempt, delayMs, err) => log.warn(`completion for ${leaseId} did not reach the control plane (${(0, redact_cjs_1.redactText)(String(err), knownSecrets)}); re-sending in ${delayMs / 1000}s (${attempt}).`),
|
|
736
|
+
});
|
|
734
737
|
if (deliveredPullRequest && completion.status === "completed" && gitWorktree.restoredHostedCheckpoint) {
|
|
735
738
|
// The pull request carries the hosted attempt's work now; a later
|
|
736
739
|
// hosted attempt must not resume the stale snapshot.
|
|
@@ -766,7 +769,7 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
|
|
|
766
769
|
log.error(`completion for ${leaseId} was rejected as ${rejection.code}, which is not remediable; failing the lease.`);
|
|
767
770
|
}
|
|
768
771
|
try {
|
|
769
|
-
await api.completeLease(leaseId, {
|
|
772
|
+
await (0, completion_retry_cjs_1.completeWithTransientRetry)(() => api.completeLease(leaseId, {
|
|
770
773
|
...completion,
|
|
771
774
|
status: "failed",
|
|
772
775
|
report: remediable
|
|
@@ -774,7 +777,7 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
|
|
|
774
777
|
: `${redactedRejection}\n\n---\n\n${completion.report}`,
|
|
775
778
|
failure_summary: redactedRejection,
|
|
776
779
|
exit_status: "failed",
|
|
777
|
-
});
|
|
780
|
+
}));
|
|
778
781
|
}
|
|
779
782
|
catch (fallbackErr) {
|
|
780
783
|
// Same race as the primary completion above: if something else released
|
package/package.json
CHANGED