@mu-crew/mu 4.0.0 → 4.1.1
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/README.md +1 -1
- package/dist/{chunk-5HECDBK3.js → chunk-B74N2EAS.js} +16 -16
- package/dist/{chunk-GQNHIPJN.js → chunk-DEYBQDTO.js} +21 -421
- package/dist/chunk-DEYBQDTO.js.map +1 -0
- package/dist/{chunk-S424D5EP.js → chunk-L7I3BLXP.js} +635 -142
- package/dist/chunk-L7I3BLXP.js.map +1 -0
- package/dist/{chunk-AW2NONYU.js → chunk-LBBZAG5C.js} +81 -7
- package/dist/chunk-LBBZAG5C.js.map +1 -0
- package/dist/{cli-GB2CAVVW.js → cli-H2PWJAJN.js} +346 -44
- package/dist/cli-H2PWJAJN.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{errors-KHOYKD7D.js → errors-G5IVXSMY.js} +2 -2
- package/dist/extension/mu-pi.js +28 -14
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +63 -11
- package/dist/index.js +15 -15
- package/dist/{tui-ALPHYTA2.js → tui-EN2CDP6G.js} +102 -27
- package/dist/tui-EN2CDP6G.js.map +1 -0
- package/docs/ARCHITECTURE.md +5 -4
- package/docs/VOCABULARY.md +6 -5
- package/docs/guide/cleanup.md +8 -0
- package/docs/guide/delegate.md +11 -3
- package/docs/guide/remote.md +19 -12
- package/docs/guide/sql.md +1 -0
- package/docs/reference/env.md +1 -0
- package/docs/reference/naming.md +2 -0
- package/package.json +1 -1
- package/skills/mu/recipes/remote-workers.md +7 -6
- package/dist/chunk-AW2NONYU.js.map +0 -1
- package/dist/chunk-GQNHIPJN.js.map +0 -1
- package/dist/chunk-S424D5EP.js.map +0 -1
- package/dist/cli-GB2CAVVW.js.map +0 -1
- package/dist/tui-ALPHYTA2.js.map +0 -1
- /package/dist/{chunk-5HECDBK3.js.map → chunk-B74N2EAS.js.map} +0 -0
- /package/dist/{errors-KHOYKD7D.js.map → errors-G5IVXSMY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ package), or spawn into the reserved `scratch` workstream:
|
|
|
83
83
|
Run `mu` with no arguments for the read-only dashboard. It has five tabs
|
|
84
84
|
(Overview, Tasks, DAG, Agents, Log), with what needs a human on top: a
|
|
85
85
|
dead or stalled worker, one waiting on a permission or on input, a
|
|
86
|
-
`human-needed` task.
|
|
86
|
+
`human-needed` task, a refused or failed delegate call.
|
|
87
87
|
|
|
88
88
|

|
|
89
89
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { createRequire as __muCreateRequire } from "node:module"; const require = __muCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
DelegateCallConflictError,
|
|
4
|
-
DelegateNotFoundError,
|
|
5
|
-
DelegateStateError,
|
|
6
3
|
IN_FLIGHT_STATES,
|
|
7
|
-
TERMINAL_DELEGATE_STATES,
|
|
8
4
|
ambientFlush,
|
|
9
5
|
ambientIngest,
|
|
10
6
|
authorisedReady,
|
|
@@ -13,15 +9,10 @@ import {
|
|
|
13
9
|
currentCallers,
|
|
14
10
|
deliveredOf,
|
|
15
11
|
endAttempt,
|
|
16
|
-
finishDelegate,
|
|
17
12
|
foreignVersionMessage,
|
|
18
13
|
getAutoConfig,
|
|
19
|
-
getDelegateCall,
|
|
20
|
-
listDelegates,
|
|
21
|
-
listDelegatesOf,
|
|
22
14
|
liveAgentToOutput,
|
|
23
15
|
localMachineId,
|
|
24
|
-
markDelegate,
|
|
25
16
|
openHumanNeeds,
|
|
26
17
|
ownSegmentForeignLine,
|
|
27
18
|
pendingAttempts,
|
|
@@ -30,12 +21,9 @@ import {
|
|
|
30
21
|
projectLog,
|
|
31
22
|
readAttempt,
|
|
32
23
|
recordHumanNeeded,
|
|
33
|
-
registerDelegateCall,
|
|
34
24
|
reserveAttempt,
|
|
35
|
-
reserveRetry
|
|
36
|
-
|
|
37
|
-
verdictTally
|
|
38
|
-
} from "./chunk-GQNHIPJN.js";
|
|
25
|
+
reserveRetry
|
|
26
|
+
} from "./chunk-DEYBQDTO.js";
|
|
39
27
|
import {
|
|
40
28
|
AgentLockBusyError,
|
|
41
29
|
AgentNotFoundError,
|
|
@@ -46,11 +34,15 @@ import {
|
|
|
46
34
|
CtlWriteVetoedError,
|
|
47
35
|
DEFAULT_SUBSTATE,
|
|
48
36
|
DELEGATE_TOKEN,
|
|
37
|
+
DelegateCallConflictError,
|
|
38
|
+
DelegateNotFoundError,
|
|
39
|
+
DelegateStateError,
|
|
49
40
|
NoMultiplexerError,
|
|
50
41
|
RemoteRecoveryRefusedError,
|
|
51
42
|
SYNCED_ENTITIES,
|
|
52
43
|
SpawnAttemptBusyError,
|
|
53
44
|
SpawnAttemptGuardError,
|
|
45
|
+
TERMINAL_DELEGATE_STATES,
|
|
54
46
|
TaskNotFoundError,
|
|
55
47
|
WorkstreamNotFoundError,
|
|
56
48
|
activeMux,
|
|
@@ -77,7 +69,9 @@ import {
|
|
|
77
69
|
endWait,
|
|
78
70
|
expectsCtl,
|
|
79
71
|
fenceAgentRef,
|
|
72
|
+
finishDelegate,
|
|
80
73
|
getAgent,
|
|
74
|
+
getDelegateCall,
|
|
81
75
|
getReceipt,
|
|
82
76
|
getTask,
|
|
83
77
|
getTaskNote,
|
|
@@ -87,6 +81,8 @@ import {
|
|
|
87
81
|
isConsumed,
|
|
88
82
|
isValidTaskId,
|
|
89
83
|
listBlocked,
|
|
84
|
+
listDelegates,
|
|
85
|
+
listDelegatesOf,
|
|
90
86
|
listInProgress,
|
|
91
87
|
listReady,
|
|
92
88
|
listTasks,
|
|
@@ -94,6 +90,7 @@ import {
|
|
|
94
90
|
localPaneAlive,
|
|
95
91
|
locksDir,
|
|
96
92
|
logStallOnce,
|
|
93
|
+
markDelegate,
|
|
97
94
|
newMsgId,
|
|
98
95
|
observeAgentStates,
|
|
99
96
|
openDb,
|
|
@@ -106,6 +103,7 @@ import {
|
|
|
106
103
|
recordSend,
|
|
107
104
|
recoveryGone,
|
|
108
105
|
recoveryHolds,
|
|
106
|
+
registerDelegateCall,
|
|
109
107
|
registerOwnedWait,
|
|
110
108
|
releaseTask,
|
|
111
109
|
resolveCliCommand,
|
|
@@ -120,16 +118,18 @@ import {
|
|
|
120
118
|
spawnAutoAttempt,
|
|
121
119
|
spawnForTask,
|
|
122
120
|
speaksMuCtl,
|
|
121
|
+
tallyCalls,
|
|
123
122
|
taskIdFor,
|
|
124
123
|
taskRevision,
|
|
125
124
|
tryResolveLiveWorkstreamId,
|
|
125
|
+
verdictTally,
|
|
126
126
|
watchAgentRun,
|
|
127
127
|
withAgentLock,
|
|
128
128
|
withCaptureSuppressed,
|
|
129
129
|
withFileLock,
|
|
130
130
|
withHostChannel,
|
|
131
131
|
withOpContext
|
|
132
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-L7I3BLXP.js";
|
|
133
133
|
|
|
134
134
|
// src/rebuild.ts
|
|
135
135
|
import { existsSync, mkdirSync, rmSync, statSync } from "fs";
|
|
@@ -4479,4 +4479,4 @@ export {
|
|
|
4479
4479
|
saveResume,
|
|
4480
4480
|
loadResume
|
|
4481
4481
|
};
|
|
4482
|
-
//# sourceMappingURL=chunk-
|
|
4482
|
+
//# sourceMappingURL=chunk-B74N2EAS.js.map
|
|
@@ -15,10 +15,7 @@ import {
|
|
|
15
15
|
TASK_FROM_JOIN,
|
|
16
16
|
TASK_KINDS,
|
|
17
17
|
TaskNotFoundError,
|
|
18
|
-
addNote,
|
|
19
|
-
addTask,
|
|
20
18
|
addTasks,
|
|
21
|
-
closeTask,
|
|
22
19
|
compareHlc,
|
|
23
20
|
compareStrings,
|
|
24
21
|
decodeLine,
|
|
@@ -48,7 +45,6 @@ import {
|
|
|
48
45
|
rowFromDb,
|
|
49
46
|
savedIdentity,
|
|
50
47
|
say,
|
|
51
|
-
setTaskStatus,
|
|
52
48
|
shellWord,
|
|
53
49
|
taskIdFor,
|
|
54
50
|
taskRevision,
|
|
@@ -56,7 +52,7 @@ import {
|
|
|
56
52
|
withCaptureSuppressed,
|
|
57
53
|
withFileLock,
|
|
58
54
|
withOpContext
|
|
59
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-L7I3BLXP.js";
|
|
60
56
|
|
|
61
57
|
// src/fold.ts
|
|
62
58
|
function fieldsOf(payload) {
|
|
@@ -1034,8 +1030,8 @@ function recordedFingerprint(db, watermarkKey) {
|
|
|
1034
1030
|
function segmentCaughtUp(db, watermarkKey, path, lines) {
|
|
1035
1031
|
const recorded = recordedFingerprint(db, watermarkKey);
|
|
1036
1032
|
if (recorded === null) return false;
|
|
1037
|
-
const
|
|
1038
|
-
return
|
|
1033
|
+
const now = segmentFingerprint(path);
|
|
1034
|
+
return now !== null && now.count === lines && now.key === decodeFingerprint(recorded).key;
|
|
1039
1035
|
}
|
|
1040
1036
|
function resumePoint(db, watermarkKey, lines) {
|
|
1041
1037
|
const recorded = recordedFingerprint(db, watermarkKey);
|
|
@@ -1415,14 +1411,14 @@ function getWatermark(db, watermarkKey) {
|
|
|
1415
1411
|
return row?.n ?? 0;
|
|
1416
1412
|
}
|
|
1417
1413
|
function setWatermark(db, watermarkKey, value, fingerprint) {
|
|
1418
|
-
const
|
|
1414
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1419
1415
|
prepareCached(
|
|
1420
1416
|
db,
|
|
1421
1417
|
`INSERT INTO sync_peers (machine_id, last_applied_seq, last_seen_at)
|
|
1422
1418
|
VALUES (@machineId, @value, @seenAt)
|
|
1423
1419
|
ON CONFLICT (machine_id) DO UPDATE
|
|
1424
1420
|
SET last_applied_seq = @value, last_seen_at = @seenAt`
|
|
1425
|
-
).run({ machineId: watermarkKey, value, seenAt:
|
|
1421
|
+
).run({ machineId: watermarkKey, value, seenAt: now });
|
|
1426
1422
|
if (fingerprint == null) {
|
|
1427
1423
|
prepareCached(db, "DELETE FROM sync_fingerprints WHERE machine_id = ?").run(watermarkKey);
|
|
1428
1424
|
} else {
|
|
@@ -1768,7 +1764,7 @@ function authorizeTasks(db, ids, opts) {
|
|
|
1768
1764
|
if (!opts.off) refuseUnauthorisable(task, me);
|
|
1769
1765
|
plan.push({ task, id });
|
|
1770
1766
|
}
|
|
1771
|
-
const
|
|
1767
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1772
1768
|
let changed = false;
|
|
1773
1769
|
for (const { task, id } of plan) {
|
|
1774
1770
|
const row = db.prepare("SELECT cli, worktree, max_runs, enabled FROM task_auto WHERE task_id = ?").get(id);
|
|
@@ -1778,7 +1774,7 @@ function authorizeTasks(db, ids, opts) {
|
|
|
1778
1774
|
changed = true;
|
|
1779
1775
|
}
|
|
1780
1776
|
if (task.dispatchHost === me) {
|
|
1781
|
-
setHost(db, id, null,
|
|
1777
|
+
setHost(db, id, null, now);
|
|
1782
1778
|
changed = true;
|
|
1783
1779
|
}
|
|
1784
1780
|
continue;
|
|
@@ -1787,7 +1783,7 @@ function authorizeTasks(db, ids, opts) {
|
|
|
1787
1783
|
if (row === void 0) {
|
|
1788
1784
|
db.prepare(
|
|
1789
1785
|
"INSERT INTO task_auto (task_id, cli, worktree, max_runs, created_at) VALUES (?, ?, ?, ?, ?)"
|
|
1790
|
-
).run(id, opts.cli, wt, opts.maxRuns,
|
|
1786
|
+
).run(id, opts.cli, wt, opts.maxRuns, now);
|
|
1791
1787
|
changed = true;
|
|
1792
1788
|
} else if (row.cli !== opts.cli || row.worktree !== wt || row.max_runs !== opts.maxRuns || row.enabled !== 1) {
|
|
1793
1789
|
db.prepare(
|
|
@@ -1796,7 +1792,7 @@ function authorizeTasks(db, ids, opts) {
|
|
|
1796
1792
|
changed = true;
|
|
1797
1793
|
}
|
|
1798
1794
|
if (task.dispatchHost !== me) {
|
|
1799
|
-
setHost(db, id, me,
|
|
1795
|
+
setHost(db, id, me, now);
|
|
1800
1796
|
changed = true;
|
|
1801
1797
|
}
|
|
1802
1798
|
}
|
|
@@ -1809,10 +1805,10 @@ function tidOf(db, task) {
|
|
|
1809
1805
|
if (id === null) throw new TaskNotFoundError(task.name, task.workstreamName);
|
|
1810
1806
|
return id;
|
|
1811
1807
|
}
|
|
1812
|
-
function setHost(db, taskId, host,
|
|
1808
|
+
function setHost(db, taskId, host, now) {
|
|
1813
1809
|
db.prepare("UPDATE tasks SET dispatch_host = ?, updated_at = ? WHERE id = ?").run(
|
|
1814
1810
|
host,
|
|
1815
|
-
|
|
1811
|
+
now,
|
|
1816
1812
|
taskId
|
|
1817
1813
|
);
|
|
1818
1814
|
}
|
|
@@ -2618,390 +2614,6 @@ function okEnvelope(data) {
|
|
|
2618
2614
|
}
|
|
2619
2615
|
var RESULT_BYTES = 4096;
|
|
2620
2616
|
|
|
2621
|
-
// src/delegate-verdict.ts
|
|
2622
|
-
var VERDICT_LABELS = ["CONFIRMED", "REFUTED", "UNVERIFIED"];
|
|
2623
|
-
function formatElapsed(ms) {
|
|
2624
|
-
const t = Math.max(0, Math.round(ms / 1e3));
|
|
2625
|
-
if (t < 60) return `${t}s`;
|
|
2626
|
-
const m = Math.floor(t / 60);
|
|
2627
|
-
if (m < 60) return `${m}m ${String(t % 60).padStart(2, "0")}s`;
|
|
2628
|
-
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
2629
|
-
}
|
|
2630
|
-
var VERDICT_LINE = /^(?:\w+: )?VERDICT:/;
|
|
2631
|
-
var LABEL_RE = new RegExp(`\\b(${VERDICT_LABELS.join("|")})\\b`);
|
|
2632
|
-
function firstVerdictLine(lines) {
|
|
2633
|
-
return lines.findIndex((l) => VERDICT_LINE.test(l));
|
|
2634
|
-
}
|
|
2635
|
-
function parseVerdict(answer) {
|
|
2636
|
-
const lines = answer.trimEnd().split("\n");
|
|
2637
|
-
const at = firstVerdictLine(lines);
|
|
2638
|
-
if (at < 0) return void 0;
|
|
2639
|
-
const after = (lines[at] ?? "").slice((lines[at] ?? "").indexOf("VERDICT:") + "VERDICT:".length);
|
|
2640
|
-
return LABEL_RE.exec(after)?.[1];
|
|
2641
|
-
}
|
|
2642
|
-
function recordNote(label, name, outcome, lastText, elapsedMs) {
|
|
2643
|
-
if (outcome !== "done" || !lastText?.trim())
|
|
2644
|
-
return `REFUTER ${label}: no verdict (${outcome === "done" ? "empty" : outcome ?? "no result"})`;
|
|
2645
|
-
const lines = lastText.trimEnd().split("\n");
|
|
2646
|
-
const at = firstVerdictLine(lines);
|
|
2647
|
-
const body = at >= 0 ? lines.slice(at).join("\n") : `NO VERDICT LINE: ${lastText.trimEnd()}`;
|
|
2648
|
-
return `REFUTER ${label} (${name}, ${formatElapsed(elapsedMs)}):
|
|
2649
|
-
${body}`;
|
|
2650
|
-
}
|
|
2651
|
-
|
|
2652
|
-
// src/delegates.ts
|
|
2653
|
-
import { createHash as createHash4 } from "crypto";
|
|
2654
|
-
var TERMINAL_DELEGATE_STATES = ["done", "failed", "cancelled"];
|
|
2655
|
-
var TERMINAL = TERMINAL_DELEGATE_STATES;
|
|
2656
|
-
var isTerminal = (s) => TERMINAL.includes(s);
|
|
2657
|
-
var DelegateCallConflictError = class extends Error {
|
|
2658
|
-
constructor(callId, field) {
|
|
2659
|
-
super(say("delegate.conflict", callId, field));
|
|
2660
|
-
this.callId = callId;
|
|
2661
|
-
this.field = field;
|
|
2662
|
-
}
|
|
2663
|
-
callId;
|
|
2664
|
-
field;
|
|
2665
|
-
name = "DelegateCallConflictError";
|
|
2666
|
-
};
|
|
2667
|
-
var DelegateStateError = class extends Error {
|
|
2668
|
-
constructor(callId, from, to) {
|
|
2669
|
-
super(say("delegate.state", callId, from, to));
|
|
2670
|
-
this.callId = callId;
|
|
2671
|
-
this.from = from;
|
|
2672
|
-
this.to = to;
|
|
2673
|
-
}
|
|
2674
|
-
callId;
|
|
2675
|
-
from;
|
|
2676
|
-
to;
|
|
2677
|
-
name = "DelegateStateError";
|
|
2678
|
-
};
|
|
2679
|
-
var DelegateNotFoundError = class extends Error {
|
|
2680
|
-
constructor(callId) {
|
|
2681
|
-
super(say("delegate.not_found", callId));
|
|
2682
|
-
this.callId = callId;
|
|
2683
|
-
}
|
|
2684
|
-
callId;
|
|
2685
|
-
name = "DelegateNotFoundError";
|
|
2686
|
-
};
|
|
2687
|
-
var SELECT_CALL = `SELECT d.*, t.local_id AS local_id, w.name AS workstream, t.wave AS wave
|
|
2688
|
-
FROM delegate_calls d
|
|
2689
|
-
JOIN tasks t ON t.id = d.task_id
|
|
2690
|
-
JOIN workstreams w ON w.id = t.workstream_id`;
|
|
2691
|
-
function rowOf(r) {
|
|
2692
|
-
let request = { text: "" };
|
|
2693
|
-
try {
|
|
2694
|
-
if (r.request) request = JSON.parse(r.request);
|
|
2695
|
-
} catch {
|
|
2696
|
-
}
|
|
2697
|
-
return {
|
|
2698
|
-
callId: r.call_id,
|
|
2699
|
-
task: r.local_id,
|
|
2700
|
-
workstream: r.workstream,
|
|
2701
|
-
caller: r.caller,
|
|
2702
|
-
state: r.state,
|
|
2703
|
-
wave: r.wave,
|
|
2704
|
-
record: r.record_workstream !== null && r.record_task !== null ? { workstream: r.record_workstream, task: r.record_task, label: r.record_label } : null,
|
|
2705
|
-
request,
|
|
2706
|
-
agent: r.agent,
|
|
2707
|
-
verdict: r.verdict ?? null,
|
|
2708
|
-
error: r.error,
|
|
2709
|
-
updatedAt: r.updated_at
|
|
2710
|
-
};
|
|
2711
|
-
}
|
|
2712
|
-
function getDelegateCall(db, callId) {
|
|
2713
|
-
const r = db.prepare(`${SELECT_CALL} WHERE d.call_id = ?`).get(callId);
|
|
2714
|
-
return r ? rowOf(r) : void 0;
|
|
2715
|
-
}
|
|
2716
|
-
function mustGet(db, callId) {
|
|
2717
|
-
const row = getDelegateCall(db, callId);
|
|
2718
|
-
if (!row) throw new DelegateNotFoundError(callId);
|
|
2719
|
-
return row;
|
|
2720
|
-
}
|
|
2721
|
-
function listDelegates(db, caller, workstream, opts = {}) {
|
|
2722
|
-
const where = [];
|
|
2723
|
-
const args = [];
|
|
2724
|
-
if (caller !== void 0) {
|
|
2725
|
-
where.push("d.caller = ?");
|
|
2726
|
-
args.push(caller);
|
|
2727
|
-
}
|
|
2728
|
-
if (workstream !== void 0) {
|
|
2729
|
-
where.push("w.name = ?");
|
|
2730
|
-
args.push(workstream);
|
|
2731
|
-
}
|
|
2732
|
-
if (opts.wave !== void 0) {
|
|
2733
|
-
where.push("t.wave = ?");
|
|
2734
|
-
args.push(opts.wave);
|
|
2735
|
-
}
|
|
2736
|
-
if (opts.states !== void 0) {
|
|
2737
|
-
where.push(`d.state IN (${opts.states.map(() => "?").join(",")})`);
|
|
2738
|
-
args.push(...opts.states);
|
|
2739
|
-
}
|
|
2740
|
-
const sql = `${SELECT_CALL}${where.length ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY d.task_id`;
|
|
2741
|
-
return db.prepare(sql).all(...args).map(rowOf);
|
|
2742
|
-
}
|
|
2743
|
-
function listDelegatesOf(db, callers, workstream, opts = {}) {
|
|
2744
|
-
const set = [...new Set(callers)];
|
|
2745
|
-
if (set.length === 0) return [];
|
|
2746
|
-
const where = [`d.caller IN (${set.map(() => "?").join(",")})`];
|
|
2747
|
-
const args = [...set];
|
|
2748
|
-
if (workstream !== void 0) {
|
|
2749
|
-
where.push("w.name = ?");
|
|
2750
|
-
args.push(workstream);
|
|
2751
|
-
}
|
|
2752
|
-
if (opts.wave !== void 0) {
|
|
2753
|
-
where.push("t.wave = ?");
|
|
2754
|
-
args.push(opts.wave);
|
|
2755
|
-
}
|
|
2756
|
-
const sql = `${SELECT_CALL} WHERE ${where.join(" AND ")} ORDER BY d.task_id`;
|
|
2757
|
-
return db.prepare(sql).all(...args).map(rowOf);
|
|
2758
|
-
}
|
|
2759
|
-
function canonical(v) {
|
|
2760
|
-
if (Array.isArray(v)) return `[${v.map(canonical).join(",")}]`;
|
|
2761
|
-
if (typeof v === "object" && v !== null) {
|
|
2762
|
-
const o = v;
|
|
2763
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonical(o[k])}`).join(",")}}`;
|
|
2764
|
-
}
|
|
2765
|
-
return JSON.stringify(v);
|
|
2766
|
-
}
|
|
2767
|
-
function taskIdFor2(callId, label) {
|
|
2768
|
-
const stem = (label ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 20).replace(/-+$/, "");
|
|
2769
|
-
const h = createHash4("sha256").update(callId).digest("hex").slice(0, 8);
|
|
2770
|
-
return `delegate_${stem ? `${stem}_` : ""}${h}`;
|
|
2771
|
-
}
|
|
2772
|
-
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
2773
|
-
function registerDelegateCall(db, opts) {
|
|
2774
|
-
const state = opts.state ?? "queued";
|
|
2775
|
-
return withOpContext(
|
|
2776
|
-
db,
|
|
2777
|
-
{ intent: "delegate.register", actor: opts.caller, group: "new" },
|
|
2778
|
-
() => db.transaction(() => {
|
|
2779
|
-
const existing = getDelegateCall(db, opts.callId);
|
|
2780
|
-
if (existing) {
|
|
2781
|
-
const same = (field, a, b) => {
|
|
2782
|
-
if (canonical(a) !== canonical(b))
|
|
2783
|
-
throw new DelegateCallConflictError(opts.callId, field);
|
|
2784
|
-
};
|
|
2785
|
-
same("caller", existing.caller, opts.caller);
|
|
2786
|
-
same("workstream", existing.workstream, opts.workstream);
|
|
2787
|
-
same("wave", existing.wave, opts.wave ?? null);
|
|
2788
|
-
same(
|
|
2789
|
-
"record target",
|
|
2790
|
-
existing.record ? { workstream: existing.record.workstream, task: existing.record.task } : null,
|
|
2791
|
-
opts.record ?? null
|
|
2792
|
-
);
|
|
2793
|
-
same("request", existing.request, opts.request);
|
|
2794
|
-
return { call: existing, created: false };
|
|
2795
|
-
}
|
|
2796
|
-
if (opts.record && !getTask(db, opts.record.task, opts.record.workstream))
|
|
2797
|
-
throw new TaskNotFoundError(opts.record.task, opts.record.workstream);
|
|
2798
|
-
const local = taskIdFor2(opts.callId, opts.request.label);
|
|
2799
|
-
const headline = (opts.request.label ?? opts.request.text).replace(/\s+/g, " ").trim();
|
|
2800
|
-
addTask(db, {
|
|
2801
|
-
localId: local,
|
|
2802
|
-
workstream: opts.workstream,
|
|
2803
|
-
title: `delegate: ${headline.slice(0, 60)}`,
|
|
2804
|
-
kind: "delegate",
|
|
2805
|
-
...opts.wave !== void 0 ? { wave: opts.wave } : {}
|
|
2806
|
-
});
|
|
2807
|
-
addNote(db, local, opts.request.text, {
|
|
2808
|
-
workstream: opts.workstream,
|
|
2809
|
-
kind: "brief",
|
|
2810
|
-
author: opts.caller,
|
|
2811
|
-
group: "inherit"
|
|
2812
|
-
});
|
|
2813
|
-
if (state !== "starting")
|
|
2814
|
-
setTaskStatus(db, local, "OPEN", { workstream: opts.workstream, substate: "parked" });
|
|
2815
|
-
const taskId = db.prepare(
|
|
2816
|
-
"SELECT t.id AS id FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ? AND t.local_id = ?"
|
|
2817
|
-
).get(opts.workstream, local).id;
|
|
2818
|
-
db.prepare(
|
|
2819
|
-
`INSERT INTO delegate_calls
|
|
2820
|
-
(task_id, caller, call_id, state, record_workstream, record_task, record_label, updated_at, request)
|
|
2821
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2822
|
-
).run(
|
|
2823
|
-
taskId,
|
|
2824
|
-
opts.caller,
|
|
2825
|
-
opts.callId,
|
|
2826
|
-
state,
|
|
2827
|
-
opts.record?.workstream ?? null,
|
|
2828
|
-
opts.record?.task ?? null,
|
|
2829
|
-
opts.request.label ?? null,
|
|
2830
|
-
now(),
|
|
2831
|
-
canonical(opts.request)
|
|
2832
|
-
);
|
|
2833
|
-
return { call: mustGet(db, opts.callId), created: true };
|
|
2834
|
-
}).immediate()
|
|
2835
|
-
);
|
|
2836
|
-
}
|
|
2837
|
-
var MOVES = {
|
|
2838
|
-
queued: ["refused"],
|
|
2839
|
-
starting: ["queued", "refused"],
|
|
2840
|
-
running: ["starting"],
|
|
2841
|
-
refused: ["queued", "starting"]
|
|
2842
|
-
};
|
|
2843
|
-
function markDelegate(db, callId, to, extra = {}) {
|
|
2844
|
-
const row = mustGet(db, callId);
|
|
2845
|
-
const from = MOVES[to] ?? [];
|
|
2846
|
-
if (!from.includes(row.state)) throw new DelegateStateError(callId, row.state, to);
|
|
2847
|
-
return withOpContext(
|
|
2848
|
-
db,
|
|
2849
|
-
{ intent: `delegate.${to}`, actor: row.caller, group: "new" },
|
|
2850
|
-
() => db.transaction(() => {
|
|
2851
|
-
const r = db.prepare(
|
|
2852
|
-
`UPDATE delegate_calls
|
|
2853
|
-
SET state = ?, updated_at = ?, agent = COALESCE(?, agent),
|
|
2854
|
-
error = CASE WHEN ? = 'refused' THEN COALESCE(?, error) ELSE NULL END
|
|
2855
|
-
WHERE call_id = ? AND state = ?`
|
|
2856
|
-
).run(to, now(), extra.agent ?? null, to, extra.error ?? null, callId, row.state);
|
|
2857
|
-
if (r.changes !== 1) throw new DelegateStateError(callId, mustGet(db, callId).state, to);
|
|
2858
|
-
if (to === "starting")
|
|
2859
|
-
setTaskStatus(db, row.task, "OPEN", { workstream: row.workstream, substate: "todo" });
|
|
2860
|
-
if (to === "refused")
|
|
2861
|
-
setTaskStatus(db, row.task, "OPEN", { workstream: row.workstream, substate: "parked" });
|
|
2862
|
-
return mustGet(db, callId);
|
|
2863
|
-
}).immediate()
|
|
2864
|
-
);
|
|
2865
|
-
}
|
|
2866
|
-
function closedBy(db, task, workstream) {
|
|
2867
|
-
const row = db.prepare(
|
|
2868
|
-
`SELECT n.author AS author FROM task_notes n
|
|
2869
|
-
JOIN tasks t ON t.id = n.task_id JOIN workstreams w ON w.id = t.workstream_id
|
|
2870
|
-
WHERE w.name = ? AND t.local_id = ? AND t.status = 'CLOSED' AND n.kind = 'close'
|
|
2871
|
-
ORDER BY n.id DESC LIMIT 1`
|
|
2872
|
-
).get(workstream, task);
|
|
2873
|
-
return row?.author ?? null;
|
|
2874
|
-
}
|
|
2875
|
-
function finishText(outcome, text) {
|
|
2876
|
-
if (outcome === "done") return text ?? "";
|
|
2877
|
-
return `no answer (${outcome})${text?.trim() ? `
|
|
2878
|
-
|
|
2879
|
-
${text.trimEnd()}` : ""}`;
|
|
2880
|
-
}
|
|
2881
|
-
function finishDelegate(db, callId, opts) {
|
|
2882
|
-
const before = mustGet(db, callId);
|
|
2883
|
-
const unchanged = (row) => ({
|
|
2884
|
-
changed: false,
|
|
2885
|
-
state: row.state,
|
|
2886
|
-
task: row.task,
|
|
2887
|
-
record: row.record ? { workstream: row.record.workstream, task: row.record.task } : null,
|
|
2888
|
-
recorded: false,
|
|
2889
|
-
verdict: row.verdict
|
|
2890
|
-
});
|
|
2891
|
-
if (isTerminal(before.state)) return unchanged(before);
|
|
2892
|
-
db.prepare(
|
|
2893
|
-
"UPDATE delegate_calls SET state = 'recording', updated_at = ? WHERE call_id = ? AND state NOT IN ('done','failed','cancelled')"
|
|
2894
|
-
).run(now(), callId);
|
|
2895
|
-
const outcome = opts.outcome === "done" && !opts.text?.trim() ? "empty" : opts.outcome;
|
|
2896
|
-
const answered = outcome === "done";
|
|
2897
|
-
const cancelled = outcome === "cancelled";
|
|
2898
|
-
const label = before.record?.label ?? before.request.label ?? opts.author;
|
|
2899
|
-
const verdict = answered ? parseVerdict(opts.text ?? "") ?? null : null;
|
|
2900
|
-
const record = before.record ? { workstream: before.record.workstream, task: before.record.task } : null;
|
|
2901
|
-
const report = finishText(outcome, opts.text);
|
|
2902
|
-
const wantRecord = opts.record !== false;
|
|
2903
|
-
const write = (recordIt, error) => withOpContext(
|
|
2904
|
-
db,
|
|
2905
|
-
{ intent: "delegate.finish", actor: opts.author, group: "new" },
|
|
2906
|
-
() => db.transaction(() => {
|
|
2907
|
-
const cur = mustGet(db, callId);
|
|
2908
|
-
if (isTerminal(cur.state)) return unchanged(cur);
|
|
2909
|
-
if (recordIt && record)
|
|
2910
|
-
addNote(
|
|
2911
|
-
db,
|
|
2912
|
-
record.task,
|
|
2913
|
-
recordNote(label, opts.author, outcome, opts.text, opts.elapsedMs ?? 0),
|
|
2914
|
-
{
|
|
2915
|
-
workstream: record.workstream,
|
|
2916
|
-
kind: "verdict",
|
|
2917
|
-
author: opts.author,
|
|
2918
|
-
group: "inherit"
|
|
2919
|
-
}
|
|
2920
|
-
);
|
|
2921
|
-
const ok = answered && error === void 0;
|
|
2922
|
-
const state = ok ? "done" : cancelled ? "cancelled" : "failed";
|
|
2923
|
-
const selfClosed = cur.agent !== null && closedBy(db, cur.task, cur.workstream) === cur.agent;
|
|
2924
|
-
if (selfClosed)
|
|
2925
|
-
addNote(db, cur.task, report, {
|
|
2926
|
-
workstream: cur.workstream,
|
|
2927
|
-
kind: "report",
|
|
2928
|
-
author: opts.author,
|
|
2929
|
-
group: "inherit"
|
|
2930
|
-
});
|
|
2931
|
-
closeTask(db, cur.task, {
|
|
2932
|
-
workstream: cur.workstream,
|
|
2933
|
-
author: opts.author,
|
|
2934
|
-
group: "inherit",
|
|
2935
|
-
...selfClosed ? {} : { report },
|
|
2936
|
-
as: ok ? "done" : cancelled ? "wontfix" : "rejected",
|
|
2937
|
-
...ok ? { evidence: `answered ${verdict ?? "with no VERDICT line"} (call ${callId})` } : { why: error ?? `${outcome}: no answer (call ${callId})` }
|
|
2938
|
-
});
|
|
2939
|
-
db.prepare(
|
|
2940
|
-
"UPDATE delegate_calls SET state = ?, verdict = ?, error = ?, updated_at = ? WHERE call_id = ?"
|
|
2941
|
-
).run(state, ok ? verdict : null, error ?? null, now(), callId);
|
|
2942
|
-
return {
|
|
2943
|
-
changed: true,
|
|
2944
|
-
state,
|
|
2945
|
-
task: cur.task,
|
|
2946
|
-
record,
|
|
2947
|
-
recorded: recordIt && record !== null,
|
|
2948
|
-
verdict: ok ? verdict : null,
|
|
2949
|
-
...error !== void 0 ? { error } : {}
|
|
2950
|
-
};
|
|
2951
|
-
}).immediate()
|
|
2952
|
-
);
|
|
2953
|
-
try {
|
|
2954
|
-
return write(wantRecord);
|
|
2955
|
-
} catch (e) {
|
|
2956
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2957
|
-
return write(
|
|
2958
|
-
false,
|
|
2959
|
-
`record ${record ? `${record.workstream}/${record.task} ` : ""}failed: ${msg}`
|
|
2960
|
-
);
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
function verdictTally(db, workstream, wave, callers) {
|
|
2964
|
-
return tallyCalls(
|
|
2965
|
-
callers === void 0 ? listDelegates(db, void 0, workstream, { wave }) : listDelegatesOf(db, callers, workstream, { wave })
|
|
2966
|
-
);
|
|
2967
|
-
}
|
|
2968
|
-
function tallyCalls(calls) {
|
|
2969
|
-
const out = { findings: [], queued: 0, running: 0, refused: [] };
|
|
2970
|
-
const byId = /* @__PURE__ */ new Map();
|
|
2971
|
-
for (const c of calls) {
|
|
2972
|
-
const id = c.record ? `${c.record.workstream}/${c.record.task}` : `${c.workstream}/${c.task}`;
|
|
2973
|
-
let f = byId.get(id);
|
|
2974
|
-
if (!f) {
|
|
2975
|
-
f = { id, confirmed: 0, refuted: 0, unverified: 0, pending: 0 };
|
|
2976
|
-
byId.set(id, f);
|
|
2977
|
-
out.findings.push(f);
|
|
2978
|
-
}
|
|
2979
|
-
switch (c.state) {
|
|
2980
|
-
case "done":
|
|
2981
|
-
if (c.verdict === "CONFIRMED") f.confirmed++;
|
|
2982
|
-
else if (c.verdict === "REFUTED") f.refuted++;
|
|
2983
|
-
else f.unverified++;
|
|
2984
|
-
break;
|
|
2985
|
-
case "failed":
|
|
2986
|
-
case "cancelled":
|
|
2987
|
-
f.unverified++;
|
|
2988
|
-
break;
|
|
2989
|
-
case "refused":
|
|
2990
|
-
f.pending++;
|
|
2991
|
-
out.refused.push(c.callId);
|
|
2992
|
-
break;
|
|
2993
|
-
case "queued":
|
|
2994
|
-
f.pending++;
|
|
2995
|
-
out.queued++;
|
|
2996
|
-
break;
|
|
2997
|
-
default:
|
|
2998
|
-
f.pending++;
|
|
2999
|
-
out.running++;
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
return out;
|
|
3003
|
-
}
|
|
3004
|
-
|
|
3005
2617
|
// src/human-needed.ts
|
|
3006
2618
|
var HumanNeededInvalidError = class extends Error {
|
|
3007
2619
|
name = "HumanNeededInvalidError";
|
|
@@ -3171,7 +2783,7 @@ function ownedWaitMove(db, callers, ws) {
|
|
|
3171
2783
|
}
|
|
3172
2784
|
var COURSE_CHECK_CLOSED = 10;
|
|
3173
2785
|
var COURSE_CHECK_AGE_MS = 24 * 36e5;
|
|
3174
|
-
function courseCheckHint(db, ws,
|
|
2786
|
+
function courseCheckHint(db, ws, now = Date.now()) {
|
|
3175
2787
|
const row = db.prepare(
|
|
3176
2788
|
`WITH w AS (SELECT id, created_at FROM workstreams WHERE name = ? AND torn_down_at IS NULL),
|
|
3177
2789
|
a AS (SELECT COALESCE(
|
|
@@ -3185,7 +2797,7 @@ function courseCheckHint(db, ws, now2 = Date.now()) {
|
|
|
3185
2797
|
FROM a`
|
|
3186
2798
|
).get(ws);
|
|
3187
2799
|
if (row === void 0 || row.closed === 0) return void 0;
|
|
3188
|
-
const old =
|
|
2800
|
+
const old = now - Date.parse(row.since) >= COURSE_CHECK_AGE_MS;
|
|
3189
2801
|
if (row.closed < COURSE_CHECK_CLOSED && !old) return void 0;
|
|
3190
2802
|
return `course check due: ${row.closed} closed since ${row.since.slice(0, 16)}Z. Stop dispatching: finish merging, then follow recipes/course-correct.md`;
|
|
3191
2803
|
}
|
|
@@ -3204,13 +2816,13 @@ function computeNext(db, ctx) {
|
|
|
3204
2816
|
}
|
|
3205
2817
|
return capPlan(renderPlan("alt", moves)).next;
|
|
3206
2818
|
}
|
|
3207
|
-
function computeNotices(db, ctx,
|
|
3208
|
-
const hint = ctx.workstream === void 0 || isScratchWorkstream(ctx.workstream) ? void 0 : courseCheckHint(db, ctx.workstream,
|
|
2819
|
+
function computeNotices(db, ctx, now = Date.now()) {
|
|
2820
|
+
const hint = ctx.workstream === void 0 || isScratchWorkstream(ctx.workstream) ? void 0 : courseCheckHint(db, ctx.workstream, now);
|
|
3209
2821
|
return hint === void 0 ? [] : [hint];
|
|
3210
2822
|
}
|
|
3211
|
-
function noticesField(db, workstreams,
|
|
2823
|
+
function noticesField(db, workstreams, now = Date.now()) {
|
|
3212
2824
|
const notices = [...new Set(workstreams)].flatMap(
|
|
3213
|
-
(workstream) => computeNotices(db, { callers: [], workstream },
|
|
2825
|
+
(workstream) => computeNotices(db, { callers: [], workstream }, now)
|
|
3214
2826
|
);
|
|
3215
2827
|
return notices.length > 0 ? { notices } : {};
|
|
3216
2828
|
}
|
|
@@ -3260,7 +2872,7 @@ var PEER_STALE_MS = 24 * 60 * 60 * 1e3;
|
|
|
3260
2872
|
var PEER_SHORT_LEN = 8;
|
|
3261
2873
|
function peerStatuses(db, dir) {
|
|
3262
2874
|
const self = localMachineId(db);
|
|
3263
|
-
const
|
|
2875
|
+
const now = Date.now();
|
|
3264
2876
|
const rows = discoverPeers(dir, self).map((peer) => {
|
|
3265
2877
|
let lastSeenMs = null;
|
|
3266
2878
|
try {
|
|
@@ -3271,7 +2883,7 @@ function peerStatuses(db, dir) {
|
|
|
3271
2883
|
const watermarkKey = peerWatermarkKey(peer);
|
|
3272
2884
|
const watermark = getWatermark(db, watermarkKey);
|
|
3273
2885
|
const total = segmentCaughtUp(db, watermarkKey, peer.path, watermark) ? watermark : segmentLineCount(peer.path);
|
|
3274
|
-
const ageMs = lastSeenMs === null ? null : Math.max(0,
|
|
2886
|
+
const ageMs = lastSeenMs === null ? null : Math.max(0, now - lastSeenMs);
|
|
3275
2887
|
return {
|
|
3276
2888
|
machineId: peer.machineId,
|
|
3277
2889
|
short: peer.machineId.slice(0, PEER_SHORT_LEN),
|
|
@@ -3491,6 +3103,7 @@ function agentToOutput(row, workstreamId2, live = {}) {
|
|
|
3491
3103
|
spawner: row.spawner,
|
|
3492
3104
|
host: row.host,
|
|
3493
3105
|
collected_head: row.collectedHead,
|
|
3106
|
+
repo: row.repo,
|
|
3494
3107
|
gate_token: row.gateToken,
|
|
3495
3108
|
created_at: row.createdAt,
|
|
3496
3109
|
updated_at: row.updatedAt,
|
|
@@ -4012,19 +3625,6 @@ export {
|
|
|
4012
3625
|
logPage,
|
|
4013
3626
|
logOut,
|
|
4014
3627
|
logReceipt,
|
|
4015
|
-
parseVerdict,
|
|
4016
|
-
TERMINAL_DELEGATE_STATES,
|
|
4017
|
-
DelegateCallConflictError,
|
|
4018
|
-
DelegateStateError,
|
|
4019
|
-
DelegateNotFoundError,
|
|
4020
|
-
getDelegateCall,
|
|
4021
|
-
listDelegates,
|
|
4022
|
-
listDelegatesOf,
|
|
4023
|
-
registerDelegateCall,
|
|
4024
|
-
markDelegate,
|
|
4025
|
-
finishDelegate,
|
|
4026
|
-
verdictTally,
|
|
4027
|
-
tallyCalls,
|
|
4028
3628
|
HumanNeededInvalidError,
|
|
4029
3629
|
recordHumanNeeded,
|
|
4030
3630
|
openHumanNeeds,
|
|
@@ -4057,4 +3657,4 @@ export {
|
|
|
4057
3657
|
ingestFromDb,
|
|
4058
3658
|
currentCallers
|
|
4059
3659
|
};
|
|
4060
|
-
//# sourceMappingURL=chunk-
|
|
3660
|
+
//# sourceMappingURL=chunk-DEYBQDTO.js.map
|