@sentropic/h2a 0.85.18 → 0.85.20
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/bin.js +6 -6
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +26 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/local-files/index.d.ts +1 -1
- package/dist/runtime/local-files/index.d.ts.map +1 -1
- package/dist/runtime/local-files/index.js +1 -1
- package/dist/runtime/local-files/index.js.map +1 -1
- package/dist/runtime/local-files/lease.d.ts +21 -0
- package/dist/runtime/local-files/lease.d.ts.map +1 -1
- package/dist/runtime/local-files/lease.js +25 -0
- package/dist/runtime/local-files/lease.js.map +1 -1
- package/dist/runtime/loop/engine/execute.d.ts +3 -1
- package/dist/runtime/loop/engine/execute.d.ts.map +1 -1
- package/dist/runtime/loop/engine/execute.js +8 -1
- package/dist/runtime/loop/engine/execute.js.map +1 -1
- package/dist/runtime/loop/engine/tick.d.ts +1 -0
- package/dist/runtime/loop/engine/tick.d.ts.map +1 -1
- package/dist/runtime/loop/engine/tick.js +11 -7
- package/dist/runtime/loop/engine/tick.js.map +1 -1
- package/dist/runtime/loop/executor-lease.d.ts +44 -0
- package/dist/runtime/loop/executor-lease.d.ts.map +1 -0
- package/dist/runtime/loop/executor-lease.js +63 -0
- package/dist/runtime/loop/executor-lease.js.map +1 -0
- package/dist/runtime/loop/index.d.ts +58 -0
- package/dist/runtime/loop/index.d.ts.map +1 -1
- package/dist/runtime/loop/index.js +91 -1
- package/dist/runtime/loop/index.js.map +1 -1
- package/dist/runtime/loop/supervisor.d.ts +155 -0
- package/dist/runtime/loop/supervisor.d.ts.map +1 -0
- package/dist/runtime/loop/supervisor.js +263 -0
- package/dist/runtime/loop/supervisor.js.map +1 -0
- package/package.json +2 -2
- package/skills/harness/using-harness/SKILL.md +18 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable objective-loop supervisor (L1 Lot 1b).
|
|
3
|
+
*
|
|
4
|
+
* THIS is the piece that makes an h2a objective loop auto-relaunch on its own,
|
|
5
|
+
* the way `/loop` does on Claude — but server-side and host-agnostic. Each beat
|
|
6
|
+
* it takes the loops that have OPTED IN (`policy.autoTick`, Lot 1a) and, for
|
|
7
|
+
* each one it can lease as the SINGLE writer (`acquireLoopExecutorLease`, Lot 0),
|
|
8
|
+
* runs exactly one `tick + execute` of the existing engine under that lease,
|
|
9
|
+
* then releases it. It reuses the engine — it does not re-implement ticking.
|
|
10
|
+
*
|
|
11
|
+
* Safety, per the double-opus B′ consensus (tmp/L1-decision-reconciled.md):
|
|
12
|
+
* - OPT-IN ONLY: `listAutoTickLoops` already filters to opted-in, LIVE loops;
|
|
13
|
+
* a global kill-switch (`H2A_LOOP_AUTOTICK_OFF`) freezes every beat.
|
|
14
|
+
* - SINGLE-WRITER (bounded): a loop is ticked only while its per-loop lease is
|
|
15
|
+
* held, AND each tick is bounded by a lease-safety timeout that is a fraction
|
|
16
|
+
* of the lease TTL — so a cooperative tick always completes and releases
|
|
17
|
+
* BEFORE its lease could become stealable, and the dozen `mcp-serve`
|
|
18
|
+
* processes on a host cannot double-tick it. A pathological tick that overruns
|
|
19
|
+
* the timeout is ABORTED (fail-closed): the engine stops firing new
|
|
20
|
+
* non-idempotent actions and the beat moves on. Residual limits (a single
|
|
21
|
+
* in-flight action cannot be un-fired, and the underlying lease-release has a
|
|
22
|
+
* known TOCTOU on overrun) are tracked follow-ups — full effect-fencing
|
|
23
|
+
* (thread the fencing token into the sink + re-check before each action, and
|
|
24
|
+
* make adapters cancellable) is deferred, safe under the opt-in-off default.
|
|
25
|
+
* - FAIL-CLOSED ISOLATION: one loop throwing or timing out never aborts the
|
|
26
|
+
* beat or the supervisor; that loop simply goes un-ticked this beat and its
|
|
27
|
+
* heartbeat goes stale, which `loopAttendance` surfaces as `unattended`.
|
|
28
|
+
* - HONEST ATTENDANCE: each successful tick stamps an executor heartbeat;
|
|
29
|
+
* `loopAttendance` is a pure read-side predicate ANY reader (status/doctor)
|
|
30
|
+
* can evaluate — an opted-in loop with no fresh heartbeat reads `unattended`
|
|
31
|
+
* (fail-closed: missing / stale / implausibly-future ⇒ unattended), so a down
|
|
32
|
+
* supervisor is visible even under clock skew.
|
|
33
|
+
* - DRAINABLE: `signal` is honored BETWEEN loops within a beat and threaded
|
|
34
|
+
* into the tick, so a SIGTERM stops launching new work promptly instead of
|
|
35
|
+
* draining every remaining eligible loop first.
|
|
36
|
+
*
|
|
37
|
+
* Golden rule: this module must NOT statically import `@sentropic/h2a-runtime`.
|
|
38
|
+
* It imports only the loop store, the executor lease, and the engine tick (which
|
|
39
|
+
* is itself adapters-gated). Imported constants are referenced at CALL time, not
|
|
40
|
+
* aliased at module-eval, to avoid any import-cycle temporal-dead-zone.
|
|
41
|
+
*/
|
|
42
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
43
|
+
import { hostname } from "node:os";
|
|
44
|
+
import { join } from "node:path";
|
|
45
|
+
import { localStorePaths, safePathSegment } from "../local-files/paths.js";
|
|
46
|
+
import { autoTickGloballyDisabled, isLoopAutoTickEligible, listAutoTickLoops } from "./index.js";
|
|
47
|
+
import { acquireLoopExecutorLease } from "./executor-lease.js";
|
|
48
|
+
import { runTick } from "./engine/tick.js";
|
|
49
|
+
/** Default supervisor beat interval. Each eligible loop is ticked every beat. */
|
|
50
|
+
export const DEFAULT_SUPERVISOR_INTERVAL_MS = 30000;
|
|
51
|
+
/**
|
|
52
|
+
* Executor-lease TTL the supervisor acquires with — deliberately LARGER than the
|
|
53
|
+
* per-loop-lease default (30s) so a normal tick (which may boot an agent) fits
|
|
54
|
+
* comfortably inside it. The lease-safety timeout below is a fraction of this, so
|
|
55
|
+
* a cooperative tick always releases before the lease could be stolen.
|
|
56
|
+
*/
|
|
57
|
+
export const DEFAULT_SUPERVISOR_LEASE_MS = 120000;
|
|
58
|
+
/**
|
|
59
|
+
* Fraction of the executor lease after which an in-flight tick is aborted
|
|
60
|
+
* (fail-closed). 0.5 leaves a full half-TTL of margin between "we gave up on this
|
|
61
|
+
* tick and released" and "the lease becomes stealable", so a cooperative tick can
|
|
62
|
+
* never overlap a successor executor.
|
|
63
|
+
*/
|
|
64
|
+
export const TICK_TIMEOUT_FRACTION = 0.5;
|
|
65
|
+
/**
|
|
66
|
+
* How many of a loop's own `tickMs` may elapse with no executor heartbeat before
|
|
67
|
+
* an opted-in loop is judged `unattended`. K=3 tolerates a couple of missed
|
|
68
|
+
* beats (a busy host, a lease held by a slow peer) before raising the flag.
|
|
69
|
+
*/
|
|
70
|
+
export const DEFAULT_UNATTENDED_TICKS = 3;
|
|
71
|
+
/** Fallback per-loop cadence when a loop's policy carries no `tickMs`. */
|
|
72
|
+
const FALLBACK_TICK_MS = 60000;
|
|
73
|
+
/** Default heartbeat/lease holder label — host + pid, so racing writers differ. */
|
|
74
|
+
function defaultHolder() {
|
|
75
|
+
return `${hostname()}:${process.pid}`;
|
|
76
|
+
}
|
|
77
|
+
/** Absolute path of a loop's executor heartbeat file. */
|
|
78
|
+
export function loopExecutorHeartbeatPath(root, loopId) {
|
|
79
|
+
return join(localStorePaths(root).root, "loops", safePathSegment(loopId), "executor.heartbeat");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Record that an executor just ticked `loopId`. Written after a successful tick,
|
|
83
|
+
* inside the loop's own dir (created by the lease acquisition). Best-effort: a
|
|
84
|
+
* write failure must not crash the beat, so callers wrap it — but it throws
|
|
85
|
+
* nothing on the happy path.
|
|
86
|
+
*/
|
|
87
|
+
export function stampExecutorHeartbeat(root, loopId, entry) {
|
|
88
|
+
const hb = {
|
|
89
|
+
at: new Date(entry.at ?? Date.now()).toISOString(),
|
|
90
|
+
holder: entry.holder,
|
|
91
|
+
fencingToken: entry.fencingToken
|
|
92
|
+
};
|
|
93
|
+
writeFileSync(loopExecutorHeartbeatPath(root, loopId), JSON.stringify(hb));
|
|
94
|
+
}
|
|
95
|
+
/** Read a loop's executor heartbeat, or `null` if absent/unparseable. */
|
|
96
|
+
export function readExecutorHeartbeat(root, loopId) {
|
|
97
|
+
let raw;
|
|
98
|
+
try {
|
|
99
|
+
raw = readFileSync(loopExecutorHeartbeatPath(root, loopId), "utf8");
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const p = JSON.parse(raw);
|
|
106
|
+
if (typeof p.at === "string" && typeof p.holder === "string" && typeof p.fencingToken === "number") {
|
|
107
|
+
return { at: p.at, holder: p.holder, fencingToken: p.fencingToken };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* malformed — treat as absent */
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Read-side, fail-closed attendance of an opted-in loop. Any reader can call
|
|
117
|
+
* this without a supervisor running:
|
|
118
|
+
* - `not-applicable` — the loop is not auto-tick eligible (not opted-in,
|
|
119
|
+
* terminal, or the kill-switch is on): nothing should tick
|
|
120
|
+
* it, so attendance is moot.
|
|
121
|
+
* - `unattended` — eligible but NO fresh executor heartbeat (missing,
|
|
122
|
+
* malformed, or older than K×tickMs). This is the
|
|
123
|
+
* fail-closed default: absence of proof ⇒ unattended, so a
|
|
124
|
+
* down/absent supervisor is visible.
|
|
125
|
+
* - `attended` — eligible and a heartbeat exists within K×tickMs.
|
|
126
|
+
*/
|
|
127
|
+
export function loopAttendance(root, loop, env = process.env, now = Date.now(), k = DEFAULT_UNATTENDED_TICKS) {
|
|
128
|
+
if (!isLoopAutoTickEligible(loop, env))
|
|
129
|
+
return "not-applicable";
|
|
130
|
+
const hb = readExecutorHeartbeat(root, loop.id);
|
|
131
|
+
if (!hb)
|
|
132
|
+
return "unattended";
|
|
133
|
+
const at = Date.parse(hb.at);
|
|
134
|
+
if (Number.isNaN(at))
|
|
135
|
+
return "unattended";
|
|
136
|
+
const tickMs = loop.policy?.tickMs && loop.policy.tickMs > 0 ? loop.policy.tickMs : FALLBACK_TICK_MS;
|
|
137
|
+
const age = now - at;
|
|
138
|
+
// Fail-closed on an implausibly FUTURE heartbeat: a beat stamped by a skewed
|
|
139
|
+
// or lying holder (`at` far ahead of the reader's clock) must NOT read
|
|
140
|
+
// `attended` forever. One tick of skew tolerance, then unattended.
|
|
141
|
+
if (age < -tickMs)
|
|
142
|
+
return "unattended";
|
|
143
|
+
// Fail-closed on staleness.
|
|
144
|
+
return age > k * tickMs ? "unattended" : "attended";
|
|
145
|
+
}
|
|
146
|
+
function delay(ms, signal) {
|
|
147
|
+
return new Promise((resolve) => {
|
|
148
|
+
if (signal?.aborted)
|
|
149
|
+
return resolve();
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
signal?.removeEventListener("abort", onAbort);
|
|
152
|
+
resolve();
|
|
153
|
+
}, ms);
|
|
154
|
+
function onAbort() {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
resolve();
|
|
157
|
+
}
|
|
158
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Run ONE supervisor beat: tick+execute every eligible loop we can lease, in
|
|
163
|
+
* isolation. Exposed so a caller (or a test) can drive a single beat without the
|
|
164
|
+
* timer loop. Never throws: a loop's failure is captured in `errored`.
|
|
165
|
+
*/
|
|
166
|
+
export async function runSupervisorBeat(root, options = {}) {
|
|
167
|
+
const env = options.env ?? process.env;
|
|
168
|
+
const nowFn = options.now ?? (() => Date.now());
|
|
169
|
+
const tick = options.runTickFn ?? runTick;
|
|
170
|
+
const holder = options.holder ?? defaultHolder();
|
|
171
|
+
const leaseMs = options.leaseMs ?? DEFAULT_SUPERVISOR_LEASE_MS;
|
|
172
|
+
const tickTimeoutMs = options.tickTimeoutMs ?? Math.floor(leaseMs * TICK_TIMEOUT_FRACTION);
|
|
173
|
+
const ticked = [];
|
|
174
|
+
const skippedLocked = [];
|
|
175
|
+
const errored = [];
|
|
176
|
+
// Kill-switch: freeze the entire beat, tick nothing. Checked here AND inside
|
|
177
|
+
// listAutoTickLoops (redundant-but-safe) so a race that flips the switch
|
|
178
|
+
// BEFORE the list call yields an empty set either way.
|
|
179
|
+
if (autoTickGloballyDisabled(env)) {
|
|
180
|
+
return { ticked, skippedLocked, errored, frozen: true };
|
|
181
|
+
}
|
|
182
|
+
for (const loop of listAutoTickLoops(root, env)) {
|
|
183
|
+
// DRAIN: honor a shutdown signal BETWEEN loops so SIGTERM stops launching new
|
|
184
|
+
// work promptly instead of ticking every remaining eligible loop first.
|
|
185
|
+
if (options.signal?.aborted)
|
|
186
|
+
break;
|
|
187
|
+
const lease = acquireLoopExecutorLease(root, loop.id, { leaseMs, holder });
|
|
188
|
+
if (!lease) {
|
|
189
|
+
skippedLocked.push(loop.id);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// Bound the tick: abort it once tickTimeoutMs elapses (fail-closed) OR the
|
|
193
|
+
// supervisor is shutting down. The engine stops firing new actions on abort.
|
|
194
|
+
const tickAc = new AbortController();
|
|
195
|
+
const onOuterAbort = () => tickAc.abort();
|
|
196
|
+
options.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
197
|
+
let timedOut = false;
|
|
198
|
+
const timer = setTimeout(() => {
|
|
199
|
+
timedOut = true;
|
|
200
|
+
tickAc.abort();
|
|
201
|
+
}, tickTimeoutMs);
|
|
202
|
+
try {
|
|
203
|
+
// Race the tick against its own abort so a hung tick never wedges the beat:
|
|
204
|
+
// if it overruns / is cancelled, we stop waiting, mark it errored (no
|
|
205
|
+
// heartbeat → the loop reads `unattended`), and move on.
|
|
206
|
+
await Promise.race([
|
|
207
|
+
tick(root, loop.id, { execute: true, signal: tickAc.signal }),
|
|
208
|
+
new Promise((resolve) => {
|
|
209
|
+
tickAc.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
210
|
+
})
|
|
211
|
+
]);
|
|
212
|
+
if (tickAc.signal.aborted) {
|
|
213
|
+
errored.push({
|
|
214
|
+
loopId: loop.id,
|
|
215
|
+
error: timedOut ? `tick exceeded ${tickTimeoutMs}ms lease-safety timeout` : "aborted"
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
stampExecutorHeartbeat(root, loop.id, {
|
|
220
|
+
holder,
|
|
221
|
+
fencingToken: lease.token,
|
|
222
|
+
at: nowFn()
|
|
223
|
+
});
|
|
224
|
+
ticked.push(loop.id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
// FAIL-CLOSED ISOLATION: this loop is simply not attended this beat; its
|
|
229
|
+
// heartbeat stays stale → surfaced as `unattended`. The beat continues.
|
|
230
|
+
errored.push({ loopId: loop.id, error: err instanceof Error ? err.message : String(err) });
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
options.signal?.removeEventListener("abort", onOuterAbort);
|
|
235
|
+
lease.release();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { ticked, skippedLocked, errored, frozen: false };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Run the durable supervisor until `signal` aborts (or `max` beats elapse). This
|
|
242
|
+
* is what the systemd `--user` unit runs (Lot 1c). It is NEVER on any default
|
|
243
|
+
* code path — existing users see no change until they explicitly launch it.
|
|
244
|
+
*/
|
|
245
|
+
export async function runLoopSupervisor(root, options = {}) {
|
|
246
|
+
const intervalMs = options.intervalMs && options.intervalMs > 0
|
|
247
|
+
? options.intervalMs
|
|
248
|
+
: DEFAULT_SUPERVISOR_INTERVAL_MS;
|
|
249
|
+
let beats = 0;
|
|
250
|
+
for (;;) {
|
|
251
|
+
const summary = await runSupervisorBeat(root, options);
|
|
252
|
+
await options.onBeat?.(summary);
|
|
253
|
+
beats += 1;
|
|
254
|
+
if (options.max !== undefined && beats >= options.max)
|
|
255
|
+
return beats;
|
|
256
|
+
if (options.signal?.aborted)
|
|
257
|
+
return beats;
|
|
258
|
+
await delay(intervalMs, options.signal);
|
|
259
|
+
if (options.signal?.aborted)
|
|
260
|
+
return beats;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=supervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../src/runtime/loop/supervisor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentropic/h2a",
|
|
3
|
-
"version": "0.85.
|
|
3
|
+
"version": "0.85.20",
|
|
4
4
|
"description": "h2a — the unified CLI + core for human-to-agent coordination.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@hono/mcp": "^0.3.0",
|
|
52
52
|
"@hono/node-server": "^2.0.4",
|
|
53
53
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54
|
-
"@sentropic/track": "^0.85.
|
|
54
|
+
"@sentropic/track": "^0.85.20",
|
|
55
55
|
"hono": "^4.12.23"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
@@ -70,6 +70,24 @@ established tracking flow; otherwise recommend it without recording it. Never cr
|
|
|
70
70
|
duplicate follow-up work from speculation. A handoff closes the diagnosed blocking item; it does not
|
|
71
71
|
satisfy or defer any remaining explicit objective.
|
|
72
72
|
|
|
73
|
+
## Rebase, never cherry-pick
|
|
74
|
+
|
|
75
|
+
To move committed work between branches, rebase — never cherry-pick. Cherry-pick copies a commit under a
|
|
76
|
+
new SHA, so the same change now exists twice with different identities: history diverges, a later merge or
|
|
77
|
+
rebase sees "different" commits, and the duplicate resurfaces in diffs, reviews, and merge-loss checks.
|
|
78
|
+
|
|
79
|
+
- To retarget a branch onto an updated base, `git rebase <base>` (or `git rebase --onto <newbase> <upstream>`
|
|
80
|
+
to replay only a specific range). The commits keep their logical identity and the branch stays linear.
|
|
81
|
+
- To isolate one feature's commits from an entangled branch, `git rebase --onto origin/main <commit>~1 <branch>`
|
|
82
|
+
replays exactly that range onto the base — a rebase, not a copy.
|
|
83
|
+
- `git cherry-pick` is reserved for the narrow case of applying a change to a branch that will NEVER share
|
|
84
|
+
history with the source (e.g. a backport to a frozen release line). It is not a tool for normal
|
|
85
|
+
branch-to-branch movement, and never a substitute for rebasing a feature onto a fresh base.
|
|
86
|
+
|
|
87
|
+
Rationale: this is the same non-duplication discipline the branch-lifecycle event-containment gate enforces
|
|
88
|
+
for `.track` — a copied commit is a second identity for one change, and every downstream tool then has to
|
|
89
|
+
reconcile two things that should have been one.
|
|
90
|
+
|
|
73
91
|
## Discipline first
|
|
74
92
|
|
|
75
93
|
Process skills (`harness/brainstorm`, `harness/debug`) decide HOW to approach the task — invoke them
|