@mu-crew/mu 3.2.1 → 3.4.0
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/AGENTS.md +10 -5
- package/README.md +83 -22
- package/dist/cli.js +294 -98
- package/dist/cli.js.map +1 -1
- package/dist/extension/mu-pi.js +346 -85
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +68 -13
- package/dist/index.js +159 -71
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -0
- package/docs/VISION.md +3 -1
- package/docs/VOCABULARY.md +6 -1
- package/docs/architecture/control-socket.md +17 -3
- package/docs/architecture/dag.md +3 -3
- package/docs/architecture/mux.md +3 -4
- package/docs/guide/README.md +5 -0
- package/docs/guide/delegate.md +30 -3
- package/docs/guide/dispatch.md +11 -4
- package/docs/guide/getting-started.md +3 -0
- package/docs/guide/recovery.md +3 -1
- package/docs/guide/remote.md +1 -1
- package/docs/history/CHANGELOG-pre-3.md +1 -1
- package/docs/reference/env.md +2 -1
- package/docs/reference/naming.md +18 -0
- package/docs/specs/2026-09-29-task-substates.md +1 -1
- package/docs/specs/2026-10-03-keep-driving-nudge.md +142 -0
- package/package.json +2 -1
- package/prompts/mu-debug.md +8 -0
- package/prompts/mu-refute.md +8 -0
- package/prompts/mu-research.md +8 -0
- package/prompts/mu-review.md +8 -0
- package/prompts/mu-rules-audit.md +8 -0
- package/prompts/mu-sweep.md +8 -0
- package/prompts/mu-tournament.md +9 -0
- package/prompts/mu-until.md +8 -0
- package/prompts/ultrathink.md +10 -0
- package/skills/mu/SKILL.md +124 -145
- package/skills/mu/recipes/adversarial-review.md +91 -0
- package/skills/mu/recipes/backlog-triage.md +43 -0
- package/skills/mu/recipes/brief.md +53 -0
- package/skills/mu/recipes/codemode-driver.md +102 -0
- package/skills/mu/recipes/deep-research.md +67 -0
- package/skills/mu/recipes/fan-out.md +40 -0
- package/skills/mu/recipes/findings.md +103 -0
- package/skills/mu/recipes/hypothesis-panel.md +45 -0
- package/skills/mu/recipes/long-run.md +20 -0
- package/skills/mu/recipes/loop-until-done.md +42 -0
- package/skills/mu/recipes/orchestrator-loop.md +98 -0
- package/skills/mu/recipes/plan.md +59 -0
- package/skills/mu/recipes/recovery.md +28 -0
- package/skills/mu/recipes/refute.md +51 -0
- package/skills/mu/{REMOTE_WORKERS.md → recipes/remote-workers.md} +7 -2
- package/skills/mu/recipes/review-panel.md +62 -0
- package/skills/mu/recipes/rules-audit.md +83 -0
- package/skills/mu/recipes/tasks-or-calls.md +67 -0
- package/skills/mu/recipes/tournament.md +40 -0
- package/skills/mu/recipes/ultrathink.md +82 -0
- package/skills/mu/recipes/watcher.md +19 -0
- package/skills/mu/recipes/waves.md +20 -0
- package/skills/mu/recipes/worker.md +26 -0
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { createConnection } from "net";
|
|
|
19
19
|
|
|
20
20
|
// src/ctl/protocol.ts
|
|
21
21
|
var CTL_PROTOCOL_VERSION = 1;
|
|
22
|
+
var CTL_COMMANDS = ["new", "reload", "compact"];
|
|
22
23
|
var UNKNOWN_OP_PREFIX = "unknown op: ";
|
|
23
24
|
function encode(msg) {
|
|
24
25
|
return `${JSON.stringify(msg)}
|
|
@@ -66,7 +67,7 @@ var CtlUnknownOpError = class extends Error {
|
|
|
66
67
|
};
|
|
67
68
|
function clientTimeout(req, override) {
|
|
68
69
|
if (override !== void 0) return override;
|
|
69
|
-
if (req.op === "fresh") return CTL_FRESH_TIMEOUT_MS;
|
|
70
|
+
if (req.op === "fresh" || req.op === "command") return CTL_FRESH_TIMEOUT_MS;
|
|
70
71
|
if (req.op !== "wait") return CTL_DEFAULT_TIMEOUT_MS;
|
|
71
72
|
return req.timeoutMs === void 0 ? void 0 : req.timeoutMs + CTL_WAIT_SLACK_MS;
|
|
72
73
|
}
|
|
@@ -358,7 +359,7 @@ function isTaskStatus(s) {
|
|
|
358
359
|
}
|
|
359
360
|
var TASK_STATUS_LIST = TASK_STATUSES.join(" | ");
|
|
360
361
|
var TASK_SUBSTATES = {
|
|
361
|
-
OPEN: ["todo", "parked"],
|
|
362
|
+
OPEN: ["todo", "parked", "triage"],
|
|
362
363
|
IN_PROGRESS: ["active"],
|
|
363
364
|
CLOSED: ["done", "rejected", "wontfix", "duplicate", "superseded"]
|
|
364
365
|
};
|
|
@@ -589,7 +590,7 @@ CREATE VIEW ready AS
|
|
|
589
590
|
SELECT t.*
|
|
590
591
|
FROM tasks t
|
|
591
592
|
WHERE t.status = 'OPEN'
|
|
592
|
-
AND t.substate
|
|
593
|
+
AND t.substate NOT IN ('parked', 'triage')
|
|
593
594
|
AND NOT EXISTS (
|
|
594
595
|
SELECT 1
|
|
595
596
|
FROM task_edges e
|
|
@@ -1339,27 +1340,6 @@ async function awaitPaneQuiescence(paneId, budgetMs) {
|
|
|
1339
1340
|
await currentSleep(Math.min(SEND_READINESS_POLL_MS, remaining));
|
|
1340
1341
|
}
|
|
1341
1342
|
}
|
|
1342
|
-
async function awaitNewSession(paneId, baseline, budgetMs) {
|
|
1343
|
-
if (budgetMs <= 0) return true;
|
|
1344
|
-
const deadline = Date.now() + budgetMs;
|
|
1345
|
-
let previous = baseline;
|
|
1346
|
-
let stable = 0;
|
|
1347
|
-
for (; ; ) {
|
|
1348
|
-
const scrollback = await capturePane(paneId, { lines: 50 }).catch(() => void 0);
|
|
1349
|
-
if (scrollback !== void 0 && scrollback !== baseline) {
|
|
1350
|
-
if (scrollback === previous) {
|
|
1351
|
-
stable++;
|
|
1352
|
-
if (stable >= QUIESCENCE_CONFIRMATIONS) return true;
|
|
1353
|
-
} else {
|
|
1354
|
-
stable = 0;
|
|
1355
|
-
}
|
|
1356
|
-
previous = scrollback;
|
|
1357
|
-
}
|
|
1358
|
-
const remaining = deadline - Date.now();
|
|
1359
|
-
if (remaining <= 0) return false;
|
|
1360
|
-
await currentSleep(Math.min(SEND_READINESS_POLL_MS, remaining));
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
1343
|
function pasteProbe(text) {
|
|
1364
1344
|
const firstLine = (text.split("\n")[0] ?? "").trim();
|
|
1365
1345
|
return firstLine.slice(0, 24);
|
|
@@ -1387,7 +1367,6 @@ async function sendToPane(paneId, text, opts = {}) {
|
|
|
1387
1367
|
assertValidPaneId(paneId);
|
|
1388
1368
|
const readinessMs = opts.readinessMs ?? defaultSendReadinessMs();
|
|
1389
1369
|
const quiesced = await awaitPaneQuiescence(paneId, readinessMs);
|
|
1390
|
-
const newSessionBaseline = readinessMs > 0 && text.trim() === "/new" ? await capturePane(paneId, { lines: 50 }).catch(() => void 0) : void 0;
|
|
1391
1370
|
const copyResult = await currentExecutor(["copy-mode", "-q", "-t", paneId]);
|
|
1392
1371
|
if (copyResult.exitCode !== 0 && /can't find pane|no current target/i.test(copyResult.stderr)) {
|
|
1393
1372
|
throw new TmuxError(
|
|
@@ -1410,15 +1389,6 @@ async function sendToPane(paneId, text, opts = {}) {
|
|
|
1410
1389
|
if (delay2 > 0) await currentSleep(delay2);
|
|
1411
1390
|
await tmux(["send-keys", "-t", paneId, "Enter"]);
|
|
1412
1391
|
if (readinessMs <= 0) return;
|
|
1413
|
-
if (newSessionBaseline !== void 0 && !await awaitNewSession(paneId, newSessionBaseline, readinessMs)) {
|
|
1414
|
-
const warn3 = opts.onUndelivered ?? defaultUndeliveredWarning;
|
|
1415
|
-
warn3({
|
|
1416
|
-
paneId,
|
|
1417
|
-
reason: "transition-unconfirmed",
|
|
1418
|
-
message: `send to ${paneId} submitted /new, but the new session did not become ready within ${readinessMs}ms. Wait for the pane to settle before sending more work.`
|
|
1419
|
-
});
|
|
1420
|
-
return;
|
|
1421
|
-
}
|
|
1422
1392
|
const probe = pasteProbe(text);
|
|
1423
1393
|
if (probe.length === 0) return;
|
|
1424
1394
|
for (let attempt = 0; attempt <= SUBMIT_RETRIES; attempt++) {
|
|
@@ -4590,6 +4560,30 @@ var TaskParkStateError = class extends Error {
|
|
|
4590
4560
|
];
|
|
4591
4561
|
}
|
|
4592
4562
|
};
|
|
4563
|
+
var TaskInTriageError = class extends Error {
|
|
4564
|
+
constructor(taskId, workstream) {
|
|
4565
|
+
super(`task ${taskId} is in triage (OPEN/triage); accept it or claim with --force`);
|
|
4566
|
+
this.taskId = taskId;
|
|
4567
|
+
this.workstream = workstream;
|
|
4568
|
+
}
|
|
4569
|
+
taskId;
|
|
4570
|
+
workstream;
|
|
4571
|
+
name = "TaskInTriageError";
|
|
4572
|
+
errorNextSteps() {
|
|
4573
|
+
const ws = this.workstream;
|
|
4574
|
+
return [
|
|
4575
|
+
{
|
|
4576
|
+
intent: "Accept it as work, then claim",
|
|
4577
|
+
command: `mu task accept ${this.taskId} -w ${ws}`
|
|
4578
|
+
},
|
|
4579
|
+
{
|
|
4580
|
+
intent: "Decline it",
|
|
4581
|
+
command: `mu task close ${this.taskId} --as rejected --why "<reason>" -w ${ws}`
|
|
4582
|
+
},
|
|
4583
|
+
{ intent: "Claim it anyway", command: `mu task claim ${this.taskId} --force -w ${ws}` }
|
|
4584
|
+
];
|
|
4585
|
+
}
|
|
4586
|
+
};
|
|
4593
4587
|
var TaskParkedError = class extends Error {
|
|
4594
4588
|
constructor(taskId, workstream) {
|
|
4595
4589
|
super(`task ${taskId} is parked (OPEN/parked); unpark it or claim with --force`);
|
|
@@ -5045,8 +5039,17 @@ function addTaskImpl(db, opts) {
|
|
|
5045
5039
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5046
5040
|
const insertResult = db.prepare(
|
|
5047
5041
|
`INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact, effort_days, created_at, updated_at)
|
|
5048
|
-
VALUES (?, ?, ?, 'OPEN',
|
|
5049
|
-
).run(
|
|
5042
|
+
VALUES (?, ?, ?, 'OPEN', ?, ?, ?, ?, ?)`
|
|
5043
|
+
).run(
|
|
5044
|
+
wsId,
|
|
5045
|
+
opts.localId,
|
|
5046
|
+
opts.title,
|
|
5047
|
+
opts.triage === true ? "triage" : "todo",
|
|
5048
|
+
opts.impact,
|
|
5049
|
+
opts.effortDays,
|
|
5050
|
+
now,
|
|
5051
|
+
now
|
|
5052
|
+
);
|
|
5050
5053
|
const newTaskId = Number(insertResult.lastInsertRowid);
|
|
5051
5054
|
if (opts.blockedBy && opts.blockedBy.length > 0) {
|
|
5052
5055
|
const blockerLookupSameWs = db.prepare(
|
|
@@ -5321,6 +5324,28 @@ function parkTask(db, localId, opts) {
|
|
|
5321
5324
|
})()
|
|
5322
5325
|
);
|
|
5323
5326
|
}
|
|
5327
|
+
function acceptTask(db, localId, opts) {
|
|
5328
|
+
return withOpContext(
|
|
5329
|
+
db,
|
|
5330
|
+
{ intent: "task.accept", group: "new" },
|
|
5331
|
+
() => db.transaction(() => {
|
|
5332
|
+
const before = getTask(db, localId, opts.workstream);
|
|
5333
|
+
if (!before) throw new TaskNotFoundError(localId);
|
|
5334
|
+
if (before.status !== "OPEN" || before.substate !== "triage") {
|
|
5335
|
+
return {
|
|
5336
|
+
previousStatus: before.status,
|
|
5337
|
+
status: before.status,
|
|
5338
|
+
previousSubstate: before.substate,
|
|
5339
|
+
substate: before.substate,
|
|
5340
|
+
changed: false
|
|
5341
|
+
};
|
|
5342
|
+
}
|
|
5343
|
+
const r = setTaskStatus(db, localId, "OPEN", { workstream: opts.workstream });
|
|
5344
|
+
recordEvidenceNote(db, localId, before.workstreamName, "ACCEPT", opts);
|
|
5345
|
+
return r;
|
|
5346
|
+
})()
|
|
5347
|
+
);
|
|
5348
|
+
}
|
|
5324
5349
|
function unparkTask(db, localId, opts) {
|
|
5325
5350
|
return withOpContext(
|
|
5326
5351
|
db,
|
|
@@ -5451,6 +5476,9 @@ function assertNotParked(task, opts) {
|
|
|
5451
5476
|
if (task.status === "OPEN" && task.substate === "parked" && opts.force !== true) {
|
|
5452
5477
|
throw new TaskParkedError(task.name, task.workstreamName);
|
|
5453
5478
|
}
|
|
5479
|
+
if (task.status === "OPEN" && task.substate === "triage" && opts.force !== true) {
|
|
5480
|
+
throw new TaskInTriageError(task.name, task.workstreamName);
|
|
5481
|
+
}
|
|
5454
5482
|
}
|
|
5455
5483
|
async function resolveActorIdentity() {
|
|
5456
5484
|
const worker = await resolveWorkerIdentity();
|
|
@@ -6485,7 +6513,7 @@ async function spawnAgent(db, opts) {
|
|
|
6485
6513
|
agent = created.row;
|
|
6486
6514
|
const startAgent = (await activeMux()).startAgentInPane;
|
|
6487
6515
|
if (startAgent === void 0) {
|
|
6488
|
-
await awaitSpawnLiveness(paneId, opts.name,
|
|
6516
|
+
await awaitSpawnLiveness(paneId, opts.name, command);
|
|
6489
6517
|
} else {
|
|
6490
6518
|
await startAgent({ paneId, name: opts.name, cli, command, commandSource });
|
|
6491
6519
|
}
|
|
@@ -6625,7 +6653,7 @@ function detectSpawnStartupError(scrollback) {
|
|
|
6625
6653
|
}
|
|
6626
6654
|
return void 0;
|
|
6627
6655
|
}
|
|
6628
|
-
async function awaitSpawnLiveness(paneId, agentName,
|
|
6656
|
+
async function awaitSpawnLiveness(paneId, agentName, command) {
|
|
6629
6657
|
const ms = defaultSpawnLivenessMs();
|
|
6630
6658
|
if (ms === 0) return;
|
|
6631
6659
|
await sleep(ms);
|
|
@@ -6640,31 +6668,6 @@ async function awaitSpawnLiveness(paneId, agentName, workstreamName, command) {
|
|
|
6640
6668
|
throw new AgentSpawnStartupError(agentName, paneId, matchedLine, scrollback);
|
|
6641
6669
|
}
|
|
6642
6670
|
}
|
|
6643
|
-
await awaitSpawnReadiness(paneId, agentName, workstreamName, command);
|
|
6644
|
-
}
|
|
6645
|
-
function defaultSpawnReadinessMs() {
|
|
6646
|
-
const raw = process.env.MU_SPAWN_READINESS_MS;
|
|
6647
|
-
if (raw === void 0) return 1e4;
|
|
6648
|
-
const parsed = Number.parseInt(raw, 10);
|
|
6649
|
-
if (Number.isNaN(parsed) || parsed < 0) return 1e4;
|
|
6650
|
-
return parsed;
|
|
6651
|
-
}
|
|
6652
|
-
var READINESS_POLL_INTERVAL_MS = 250;
|
|
6653
|
-
async function awaitSpawnReadiness(paneId, agentName, workstreamName, command) {
|
|
6654
|
-
const budgetMs = defaultSpawnReadinessMs();
|
|
6655
|
-
if (budgetMs === 0 || !murmurAvailable()) return;
|
|
6656
|
-
const mux = await activeMux();
|
|
6657
|
-
const deadline = Date.now() + budgetMs;
|
|
6658
|
-
const agent = { name: agentName, workstreamName, paneId };
|
|
6659
|
-
while (Date.now() < deadline) {
|
|
6660
|
-
if (!await mux.paneExists(paneId)) {
|
|
6661
|
-
const scrollback = await mux.capturePane(paneId, { lines: 50 }).catch(() => void 0);
|
|
6662
|
-
throw new AgentDiedOnSpawnError(agentName, paneId, scrollback, command);
|
|
6663
|
-
}
|
|
6664
|
-
const reading = (await readAgentStates([agent])).get(agentKey(agent));
|
|
6665
|
-
if (reading?.source === "murmur") return;
|
|
6666
|
-
await sleep(Math.min(READINESS_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
|
|
6667
|
-
}
|
|
6668
6671
|
}
|
|
6669
6672
|
async function createOrReusePane(opts) {
|
|
6670
6673
|
const mux = await activeMux();
|
|
@@ -7087,23 +7090,47 @@ var AgentExtensionOutdatedError = class extends Error {
|
|
|
7087
7090
|
}
|
|
7088
7091
|
};
|
|
7089
7092
|
var AgentBusyError = class extends Error {
|
|
7090
|
-
constructor(agentName, workstream) {
|
|
7091
|
-
super(`agent ${agentName} is busy: --fresh would abandon its running turn`);
|
|
7093
|
+
constructor(agentName, workstream, command) {
|
|
7094
|
+
super(`agent ${agentName} is busy: ${command ?? "--fresh"} would abandon its running turn`);
|
|
7092
7095
|
this.agentName = agentName;
|
|
7093
7096
|
this.workstream = workstream;
|
|
7097
|
+
this.command = command;
|
|
7094
7098
|
}
|
|
7095
7099
|
agentName;
|
|
7096
7100
|
workstream;
|
|
7101
|
+
command;
|
|
7097
7102
|
name = "AgentBusyError";
|
|
7098
7103
|
errorNextSteps() {
|
|
7104
|
+
const retry = this.command === void 0 ? `mu agent send ${this.agentName} --fresh --force '...' -w ${this.workstream}` : `mu agent send ${this.agentName} '${this.command}' --force -w ${this.workstream}`;
|
|
7099
7105
|
return [
|
|
7100
7106
|
{
|
|
7101
7107
|
intent: "Stop the turn first, then retry",
|
|
7102
7108
|
command: `mu agent abort ${this.agentName} -w ${this.workstream}`
|
|
7103
7109
|
},
|
|
7110
|
+
{ intent: "Or abandon the turn in one step", command: retry }
|
|
7111
|
+
];
|
|
7112
|
+
}
|
|
7113
|
+
};
|
|
7114
|
+
var AgentSlashCommandUnsupportedError = class extends Error {
|
|
7115
|
+
constructor(agentName, workstream, text, supported) {
|
|
7116
|
+
super(
|
|
7117
|
+
`agent ${agentName} runs pi: over the control socket mu runs only ${supported.join(", ")}; ${JSON.stringify(text.split("\n")[0])} is not one of them. Pass --via mux to type it into the pane`
|
|
7118
|
+
);
|
|
7119
|
+
this.agentName = agentName;
|
|
7120
|
+
this.workstream = workstream;
|
|
7121
|
+
this.text = text;
|
|
7122
|
+
this.supported = supported;
|
|
7123
|
+
}
|
|
7124
|
+
agentName;
|
|
7125
|
+
workstream;
|
|
7126
|
+
text;
|
|
7127
|
+
supported;
|
|
7128
|
+
name = "AgentSlashCommandUnsupportedError";
|
|
7129
|
+
errorNextSteps() {
|
|
7130
|
+
return [
|
|
7104
7131
|
{
|
|
7105
|
-
intent: "
|
|
7106
|
-
command: `mu agent send ${this.agentName}
|
|
7132
|
+
intent: "Type it into the pane on purpose (tmux paste; unconfirmed)",
|
|
7133
|
+
command: `mu agent send ${this.agentName} '${this.text.split("\n")[0]}' --via mux -w ${this.workstream}`
|
|
7107
7134
|
}
|
|
7108
7135
|
];
|
|
7109
7136
|
}
|
|
@@ -7116,10 +7143,22 @@ function agentCtlSocket(db, agent) {
|
|
|
7116
7143
|
function expectsCtl(agent, sock = ctlSocketPath(agent.workstreamName, agent.name)) {
|
|
7117
7144
|
return speaksMuCtl(agent.cli, resolveCliCommand(agent.cli)) || existsSync8(sock);
|
|
7118
7145
|
}
|
|
7119
|
-
function chooseTransport(agent,
|
|
7120
|
-
if (text.startsWith("/")) return "mux";
|
|
7146
|
+
function chooseTransport(agent, sock) {
|
|
7121
7147
|
return expectsCtl(agent, sock) ? "ctl" : "mux";
|
|
7122
7148
|
}
|
|
7149
|
+
var CTL_SLASH_COMMANDS = CTL_COMMANDS.map((n) => `/${n}`);
|
|
7150
|
+
function parseSessionCommand(text) {
|
|
7151
|
+
const t = text.trim();
|
|
7152
|
+
if (t === "/new") return { name: "new" };
|
|
7153
|
+
if (t === "/reload") return { name: "reload" };
|
|
7154
|
+
const m = /^\/compact(?:\s+([\s\S]*))?$/.exec(t);
|
|
7155
|
+
if (!m) return void 0;
|
|
7156
|
+
const instructions = m[1]?.trim();
|
|
7157
|
+
return instructions ? { name: "compact", instructions } : { name: "compact" };
|
|
7158
|
+
}
|
|
7159
|
+
function isSlashCommand(text) {
|
|
7160
|
+
return /^\/[A-Za-z][\w:-]*(?:\s|$)/.test(text.trimStart());
|
|
7161
|
+
}
|
|
7123
7162
|
async function extensionOutdated(agent, sock, e) {
|
|
7124
7163
|
let extVersion;
|
|
7125
7164
|
try {
|
|
@@ -7148,11 +7187,23 @@ async function sendViaTransport(agent, text, opts) {
|
|
|
7148
7187
|
if (opts.fresh && (opts.via === "mux" || !expectsCtl(agent, sock))) {
|
|
7149
7188
|
throw new AgentFreshNeedsCtlError(agent.name, agent.workstreamName, agent.cli);
|
|
7150
7189
|
}
|
|
7151
|
-
const transport = opts.fresh ? "ctl" : opts.via ?? chooseTransport(agent,
|
|
7190
|
+
const transport = opts.fresh ? "ctl" : opts.via ?? chooseTransport(agent, sock);
|
|
7152
7191
|
if (transport === "mux") {
|
|
7153
7192
|
await (await activeMux()).sendToPane(agent.paneId, text, opts);
|
|
7154
7193
|
return { transport };
|
|
7155
7194
|
}
|
|
7195
|
+
if (!opts.fresh) {
|
|
7196
|
+
const cmd = parseSessionCommand(text);
|
|
7197
|
+
if (cmd) return runSessionCommand(agent, sock, cmd, opts.force === true);
|
|
7198
|
+
if (isSlashCommand(text)) {
|
|
7199
|
+
throw new AgentSlashCommandUnsupportedError(
|
|
7200
|
+
agent.name,
|
|
7201
|
+
agent.workstreamName,
|
|
7202
|
+
text,
|
|
7203
|
+
CTL_SLASH_COMMANDS
|
|
7204
|
+
);
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7156
7207
|
const reply = await ctlRequestFor(
|
|
7157
7208
|
agent,
|
|
7158
7209
|
sock,
|
|
@@ -7166,6 +7217,25 @@ async function sendViaTransport(agent, text, opts) {
|
|
|
7166
7217
|
}
|
|
7167
7218
|
return reply.state === void 0 ? { transport } : { transport, state: reply.state };
|
|
7168
7219
|
}
|
|
7220
|
+
async function runSessionCommand(agent, sock, cmd, force) {
|
|
7221
|
+
const reply = await ctlRequestFor(agent, sock, {
|
|
7222
|
+
op: "command",
|
|
7223
|
+
name: cmd.name,
|
|
7224
|
+
...cmd.instructions !== void 0 ? { instructions: cmd.instructions } : {},
|
|
7225
|
+
...force ? { force: true } : {}
|
|
7226
|
+
});
|
|
7227
|
+
if (!reply.ok) {
|
|
7228
|
+
if (reply.error === "busy") {
|
|
7229
|
+
throw new AgentBusyError(agent.name, agent.workstreamName, `/${cmd.name}`);
|
|
7230
|
+
}
|
|
7231
|
+
throw new Error(`pi refused /${cmd.name}: ${reply.error}`);
|
|
7232
|
+
}
|
|
7233
|
+
return {
|
|
7234
|
+
transport: "ctl",
|
|
7235
|
+
command: cmd.name,
|
|
7236
|
+
...reply.state !== void 0 ? { state: reply.state } : {}
|
|
7237
|
+
};
|
|
7238
|
+
}
|
|
7169
7239
|
|
|
7170
7240
|
// src/agent-state.ts
|
|
7171
7241
|
var CTL_STATE_PROBE_MS = 1e3;
|
|
@@ -8948,6 +9018,15 @@ function murmurDoctorCheck(agentStateSource, opts = {}) {
|
|
|
8948
9018
|
};
|
|
8949
9019
|
}
|
|
8950
9020
|
var RELINK = "run mu link pi";
|
|
9021
|
+
function extFeatures(env) {
|
|
9022
|
+
const features = [
|
|
9023
|
+
["mu_delegate", "MU_DELEGATE"],
|
|
9024
|
+
["nudge", "MU_NUDGE"]
|
|
9025
|
+
];
|
|
9026
|
+
const on = ["ctl", ...features.filter(([, v]) => env[v] !== "0").map(([f]) => f)];
|
|
9027
|
+
const off = features.filter(([, v]) => env[v] === "0").map(([f, v]) => `${f} disabled by ${v}=0`);
|
|
9028
|
+
return `(${on.join(", ")}${off.length ? `; ${off.join("; ")}` : ""})`;
|
|
9029
|
+
}
|
|
8951
9030
|
function ctlExtensionDoctorCheck(opts) {
|
|
8952
9031
|
const { extension } = inspectLinks(opts);
|
|
8953
9032
|
switch (extension.state) {
|
|
@@ -8955,7 +9034,7 @@ function ctlExtensionDoctorCheck(opts) {
|
|
|
8955
9034
|
return {
|
|
8956
9035
|
name: "mu ext",
|
|
8957
9036
|
status: "ok",
|
|
8958
|
-
detail: process.env
|
|
9037
|
+
detail: extFeatures(process.env)
|
|
8959
9038
|
};
|
|
8960
9039
|
case "stale-copy":
|
|
8961
9040
|
return { name: "mu ext", status: "warn", detail: `stale copy: ${RELINK}` };
|
|
@@ -10001,6 +10080,10 @@ async function loadWorkstreamSnapshotFast(db, workstream, opts = {}) {
|
|
|
10001
10080
|
blocked: listBlocked(db, workstream),
|
|
10002
10081
|
recentClosed: listRecentClosed(db, workstream),
|
|
10003
10082
|
parkedCount: listTasks(db, workstream, { status: "OPEN", substate: "parked" }).length,
|
|
10083
|
+
triage: listTasks(db, workstream, { status: "OPEN", substate: "triage" }),
|
|
10084
|
+
taskCount: db.prepare(
|
|
10085
|
+
"SELECT count(*) AS n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ?"
|
|
10086
|
+
).get(workstream).n,
|
|
10004
10087
|
allTasks: opts.withAllTasks === true ? listTasks(db, workstream) : [],
|
|
10005
10088
|
workspaces: listWorkspaces2(db, workstream),
|
|
10006
10089
|
workspaceOrphans: listWorkspaceOrphans(db, workstream),
|
|
@@ -10074,6 +10157,8 @@ function minimalSnapshot(workstream) {
|
|
|
10074
10157
|
blocked: [],
|
|
10075
10158
|
recentClosed: [],
|
|
10076
10159
|
parkedCount: 0,
|
|
10160
|
+
triage: [],
|
|
10161
|
+
taskCount: 0,
|
|
10077
10162
|
allTasks: [],
|
|
10078
10163
|
workspaces: [],
|
|
10079
10164
|
workspaceOrphans: [],
|
|
@@ -10919,6 +11004,7 @@ export {
|
|
|
10919
11004
|
AgentFreshNeedsCtlError,
|
|
10920
11005
|
AgentNotFoundError,
|
|
10921
11006
|
AgentNotInWorkstreamError,
|
|
11007
|
+
AgentSlashCommandUnsupportedError,
|
|
10922
11008
|
AgentSpawnCliNotFoundError,
|
|
10923
11009
|
AgentSpawnStartupError,
|
|
10924
11010
|
CAPTURE_TRIGGER_DDL,
|
|
@@ -10969,6 +11055,7 @@ export {
|
|
|
10969
11055
|
TASK_SUBSTATE_ROWS,
|
|
10970
11056
|
TaskAlreadyOwnedError,
|
|
10971
11057
|
TaskExistsError,
|
|
11058
|
+
TaskInTriageError,
|
|
10972
11059
|
TaskNotFoundError,
|
|
10973
11060
|
TaskNotInWorkstreamError,
|
|
10974
11061
|
TaskParkStateError,
|
|
@@ -10985,6 +11072,7 @@ export {
|
|
|
10985
11072
|
WorkstreamExistsError,
|
|
10986
11073
|
WorkstreamNameInvalidError,
|
|
10987
11074
|
abortAgent,
|
|
11075
|
+
acceptTask,
|
|
10988
11076
|
activeMux,
|
|
10989
11077
|
addBlockEdge,
|
|
10990
11078
|
addNote,
|