mtech-ai-local-runner 0.1.63 → 0.1.65
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/src/adapters/claude-code/adapter.js +52 -36
- package/dist/src/adapters/claude-code/runtime-options.js +5 -1
- package/dist/src/cli.js +4 -0
- package/dist/src/commands/claude-release.js +105 -0
- package/dist/src/safety/claude-production-host.js +2 -2
- package/dist/src/safety/claude-release-copy.js +61 -25
- package/dist/src/safety/claude-release.js +18 -1
- package/dist/src/safety/claude-review-campaign.js +125 -0
- package/dist/src/safety/claude-review-evidence.js +96 -0
- package/dist/src/safety/claude-review-fetch.js +72 -0
- package/dist/src/safety/claude-review-layout.js +92 -0
- package/dist/src/safety/claude-review-provider.js +154 -0
- package/dist/src/safety/claude-review-run.js +148 -0
- package/dist/src/safety/claude-review-sandbox.js +35 -0
- package/package.json +1 -1
- package/dist/src/daemon/job-poller.js +0 -42
- package/dist/src/daemon/session-open-poller.js +0 -123
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export class JobPoller {
|
|
2
|
-
deps;
|
|
3
|
-
stopped = false;
|
|
4
|
-
failures = 0;
|
|
5
|
-
constructor(deps) {
|
|
6
|
-
this.deps = deps;
|
|
7
|
-
}
|
|
8
|
-
stop() { this.stopped = true; }
|
|
9
|
-
async tick() {
|
|
10
|
-
if (this.stopped)
|
|
11
|
-
return;
|
|
12
|
-
let stage = "heartbeat";
|
|
13
|
-
try {
|
|
14
|
-
await this.deps.heartbeat?.();
|
|
15
|
-
stage = "wait";
|
|
16
|
-
const claim = await this.deps.wait();
|
|
17
|
-
stage = "execute";
|
|
18
|
-
if (claim.status === "claimed" && claim.job)
|
|
19
|
-
await this.deps.execute(claim.job);
|
|
20
|
-
this.failures = 0;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
try {
|
|
24
|
-
this.deps.reportError?.({ stage, code: stage === "execute" ? "EXECUTION_UNACKNOWLEDGED" : "GATEWAY_UNAVAILABLE" });
|
|
25
|
-
}
|
|
26
|
-
catch { /* Observational logging cannot bypass stop/backoff or discard recovery state. */ }
|
|
27
|
-
// Unknown execution/terminal outcomes must reconcile before any new claim.
|
|
28
|
-
if (stage === "execute") {
|
|
29
|
-
this.stop();
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
this.failures += 1;
|
|
33
|
-
const base = Math.min(30_000, 1000 * (2 ** Math.min(5, this.failures - 1)));
|
|
34
|
-
const jitter = Math.floor(base * 0.2 * (this.deps.random ?? Math.random)());
|
|
35
|
-
await this.deps.delay(base + jitter);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async run() {
|
|
39
|
-
while (!this.stopped)
|
|
40
|
-
await this.tick();
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
const MINIMUM_CYCLE_MS = 1000;
|
|
2
|
-
/** Pauses before the second and third report attempts. The server treats a repeat report as idempotent. */
|
|
3
|
-
const REPORT_RETRY_DELAYS_MS = [1000, 2000];
|
|
4
|
-
/** The caught error's own failure code (a GatewayToolError code such as GATEWAY_HTTP_401), never its message, which may carry server text. */
|
|
5
|
-
function causeOf(error) {
|
|
6
|
-
const code = error?.code;
|
|
7
|
-
return typeof code === "string" && /^[A-Z0-9_]{1,64}$/.test(code) ? code : undefined;
|
|
8
|
-
}
|
|
9
|
-
/** Resolves after the delay or at once on abort, and never leaves a listener on a long-lived signal. */
|
|
10
|
-
export function abortableDelay(milliseconds, signal) {
|
|
11
|
-
return new Promise(resolve => {
|
|
12
|
-
if (signal.aborted) {
|
|
13
|
-
resolve();
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const onAbort = () => { clearTimeout(timer); resolve(); };
|
|
17
|
-
const timer = setTimeout(() => { signal.removeEventListener("abort", onAbort); resolve(); }, milliseconds);
|
|
18
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
export class SessionOpenPoller {
|
|
22
|
-
deps;
|
|
23
|
-
controller = new AbortController();
|
|
24
|
-
failures = 0;
|
|
25
|
-
constructor(deps) {
|
|
26
|
-
this.deps = deps;
|
|
27
|
-
}
|
|
28
|
-
stop() { this.controller.abort(); }
|
|
29
|
-
fail(stage, error, code = `SESSION_OPEN_${stage.toUpperCase()}_FAILED`) {
|
|
30
|
-
const cause = causeOf(error);
|
|
31
|
-
try {
|
|
32
|
-
this.deps.reportError?.({ stage, code, ...(cause ? { cause } : {}) });
|
|
33
|
-
}
|
|
34
|
-
catch { /* Logging cannot stop the loop. */ }
|
|
35
|
-
}
|
|
36
|
-
async pause(milliseconds, signal) {
|
|
37
|
-
await this.deps.delay(milliseconds, signal).catch(() => undefined);
|
|
38
|
-
}
|
|
39
|
-
/** Opens one claimed request and reports it. A failure here never reaches the rest of the batch. */
|
|
40
|
-
async handle(request, signal) {
|
|
41
|
-
let result;
|
|
42
|
-
try {
|
|
43
|
-
result = await this.deps.open(request);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
if (!signal.aborted)
|
|
47
|
-
this.fail("open", error);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const openId = request?.open_id;
|
|
51
|
-
if (typeof openId !== "string" || openId === "")
|
|
52
|
-
return;
|
|
53
|
-
for (let attempt = 0;; attempt += 1) {
|
|
54
|
-
try {
|
|
55
|
-
await this.deps.report(openId, result, signal);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
if (signal.aborted)
|
|
60
|
-
return;
|
|
61
|
-
const retryDelay = REPORT_RETRY_DELAYS_MS[attempt];
|
|
62
|
-
if (retryDelay === undefined) {
|
|
63
|
-
this.fail("report", error);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
await this.pause(retryDelay, signal);
|
|
67
|
-
if (signal.aborted)
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
async tick() {
|
|
73
|
-
const signal = this.controller.signal;
|
|
74
|
-
if (signal.aborted)
|
|
75
|
-
return;
|
|
76
|
-
const now = this.deps.now ?? (() => performance.now());
|
|
77
|
-
const started = now();
|
|
78
|
-
let opens;
|
|
79
|
-
let retryAfterSeconds;
|
|
80
|
-
try {
|
|
81
|
-
({ opens, retryAfterSeconds } = await this.deps.wait(signal));
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (signal.aborted)
|
|
85
|
-
return;
|
|
86
|
-
this.fail("wait", error);
|
|
87
|
-
this.failures += 1;
|
|
88
|
-
const base = Math.min(30_000, 1000 * (2 ** Math.min(5, this.failures - 1)));
|
|
89
|
-
const jitter = Math.floor(base * 0.2 * (this.deps.random ?? Math.random)());
|
|
90
|
-
await this.pause(base + jitter, signal);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
this.failures = 0;
|
|
94
|
-
const claimed = Array.isArray(opens) ? opens : [];
|
|
95
|
-
for (const request of claimed) {
|
|
96
|
-
if (signal.aborted)
|
|
97
|
-
return;
|
|
98
|
-
await this.handle(request, signal);
|
|
99
|
-
}
|
|
100
|
-
if (claimed.length > 0 || signal.aborted)
|
|
101
|
-
return;
|
|
102
|
-
// A wait that returns at once with nothing (a Gateway wait of 0) must not become a hot loop, and while the Gateway's
|
|
103
|
-
// session open switch is off it asks for a longer pause (retry_after_seconds). The clock is monotonic and clamped.
|
|
104
|
-
const elapsed = Math.max(0, now() - started);
|
|
105
|
-
const remainder = elapsed < MINIMUM_CYCLE_MS ? MINIMUM_CYCLE_MS - elapsed : 0;
|
|
106
|
-
const wanted = retryAfterSeconds === undefined ? remainder : Math.max(remainder, retryAfterSeconds * 1000);
|
|
107
|
-
if (wanted > 0)
|
|
108
|
-
await this.pause(wanted, signal);
|
|
109
|
-
}
|
|
110
|
-
async run() {
|
|
111
|
-
const signal = this.controller.signal;
|
|
112
|
-
while (!signal.aborted) {
|
|
113
|
-
try {
|
|
114
|
-
await this.tick();
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
this.fail("wait", undefined, "SESSION_OPEN_LOOP_FAILED");
|
|
118
|
-
if (!signal.aborted)
|
|
119
|
-
await this.pause(1000, signal);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|