@moxxy/cli 0.14.3 → 0.14.5
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/bin.js +220 -70
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { z as z$1, createMutex, defineTunnelProvider, definePlugin, defineProvider, defineTool, MoxxyError, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, defineSurface, runManualCompaction, isFileDiffDisplay, renderFrontmatter, defineEmbedder, migrateModeName, skillFrontmatterSchema, asSkillId, startChannelWith, parseFrontmatterFile, createDeferredPermissionResolver, getInstallHint, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, encodeLoginPrompt, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, MOXXY_PCM16_24KHZ_MIME, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
|
|
4
4
|
import * as fs32 from 'fs';
|
|
5
|
-
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync,
|
|
5
|
+
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, writeFileSync, rmSync, statSync, readdirSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
|
|
6
6
|
import * as path3 from 'path';
|
|
7
7
|
import path3__default, { join, dirname, resolve, relative, isAbsolute, basename } from 'path';
|
|
8
8
|
import { isCliTunnelAvailable, writeFileAtomic, spawnCliTunnel, moxxyPath, moxxyHome, bearerTokenMatches, resolveChannelToken, rotateChannelToken, readRequestBody, MOXXY_WS_SUBPROTOCOL, bearerGuard, tokenFromWsProtocolHeader, writeFileAtomicSync } from '@moxxy/sdk/server';
|
|
@@ -81553,10 +81553,10 @@ var init_output = __esm({
|
|
|
81553
81553
|
transformers
|
|
81554
81554
|
});
|
|
81555
81555
|
}
|
|
81556
|
-
clip(
|
|
81556
|
+
clip(clip2) {
|
|
81557
81557
|
this.operations.push({
|
|
81558
81558
|
type: "clip",
|
|
81559
|
-
clip
|
|
81559
|
+
clip: clip2
|
|
81560
81560
|
});
|
|
81561
81561
|
}
|
|
81562
81562
|
unclip() {
|
|
@@ -81590,40 +81590,40 @@ var init_output = __esm({
|
|
|
81590
81590
|
const { text, transformers } = operation;
|
|
81591
81591
|
let { x: x4, y: y2 } = operation;
|
|
81592
81592
|
let lines = text.split("\n");
|
|
81593
|
-
const
|
|
81594
|
-
if (
|
|
81595
|
-
const clipHorizontally = typeof
|
|
81596
|
-
const clipVertically = typeof
|
|
81593
|
+
const clip2 = clips.at(-1);
|
|
81594
|
+
if (clip2) {
|
|
81595
|
+
const clipHorizontally = typeof clip2?.x1 === "number" && typeof clip2?.x2 === "number";
|
|
81596
|
+
const clipVertically = typeof clip2?.y1 === "number" && typeof clip2?.y2 === "number";
|
|
81597
81597
|
if (clipHorizontally) {
|
|
81598
81598
|
const width = widestLine(text);
|
|
81599
|
-
if (x4 + width <
|
|
81599
|
+
if (x4 + width < clip2.x1 || x4 > clip2.x2) {
|
|
81600
81600
|
continue;
|
|
81601
81601
|
}
|
|
81602
81602
|
}
|
|
81603
81603
|
if (clipVertically) {
|
|
81604
81604
|
const height = lines.length;
|
|
81605
|
-
if (y2 + height <
|
|
81605
|
+
if (y2 + height < clip2.y1 || y2 > clip2.y2) {
|
|
81606
81606
|
continue;
|
|
81607
81607
|
}
|
|
81608
81608
|
}
|
|
81609
81609
|
if (clipHorizontally) {
|
|
81610
81610
|
lines = lines.map((line) => {
|
|
81611
|
-
const from = x4 <
|
|
81611
|
+
const from = x4 < clip2.x1 ? clip2.x1 - x4 : 0;
|
|
81612
81612
|
const width = stringWidth(line);
|
|
81613
|
-
const to = x4 + width >
|
|
81613
|
+
const to = x4 + width > clip2.x2 ? clip2.x2 - x4 : width;
|
|
81614
81614
|
return sliceAnsi2(line, from, to);
|
|
81615
81615
|
});
|
|
81616
|
-
if (x4 <
|
|
81617
|
-
x4 =
|
|
81616
|
+
if (x4 < clip2.x1) {
|
|
81617
|
+
x4 = clip2.x1;
|
|
81618
81618
|
}
|
|
81619
81619
|
}
|
|
81620
81620
|
if (clipVertically) {
|
|
81621
|
-
const from = y2 <
|
|
81621
|
+
const from = y2 < clip2.y1 ? clip2.y1 - y2 : 0;
|
|
81622
81622
|
const height = lines.length;
|
|
81623
|
-
const to = y2 + height >
|
|
81623
|
+
const to = y2 + height > clip2.y2 ? clip2.y2 - y2 : height;
|
|
81624
81624
|
lines = lines.slice(from, to);
|
|
81625
|
-
if (y2 <
|
|
81626
|
-
y2 =
|
|
81625
|
+
if (y2 < clip2.y1) {
|
|
81626
|
+
y2 = clip2.y1;
|
|
81627
81627
|
}
|
|
81628
81628
|
}
|
|
81629
81629
|
}
|
|
@@ -136051,6 +136051,7 @@ var ARCHITECT_AGENT_ID = "architect";
|
|
|
136051
136051
|
var COLLAB_SCAFFOLD_DIR = ".moxxy-collab";
|
|
136052
136052
|
var CONTRACTS_FILENAME = "CONTRACTS.md";
|
|
136053
136053
|
var ROSTER_FILENAME = "roster.json";
|
|
136054
|
+
var BRIEF_FILENAME = "BRIEF.md";
|
|
136054
136055
|
function collabRunId(sessionId, turnId) {
|
|
136055
136056
|
const tail = (s2) => s2.replace(/[^a-zA-Z0-9]/g, "").slice(-6) || "x";
|
|
136056
136057
|
return `${tail(sessionId)}-${tail(turnId)}`;
|
|
@@ -138658,7 +138659,7 @@ var CollaborationState = class {
|
|
|
138658
138659
|
return;
|
|
138659
138660
|
agent.status = status;
|
|
138660
138661
|
this.emitFn({ kind: "agent_status", agentId, status, ...detail ? { detail } : {} });
|
|
138661
|
-
if (status === "crashed" || status === "killed")
|
|
138662
|
+
if (status === "crashed" || status === "killed" || status === "failed")
|
|
138662
138663
|
this.releaseAllFor(agentId);
|
|
138663
138664
|
}
|
|
138664
138665
|
markDone(agentId, summary, artifacts) {
|
|
@@ -138671,7 +138672,7 @@ var CollaborationState = class {
|
|
|
138671
138672
|
this.emitFn({ kind: "agent_done", agentId, summary, ...artifacts ? { artifacts } : {} });
|
|
138672
138673
|
}
|
|
138673
138674
|
allDone() {
|
|
138674
|
-
const live = this.agentOrder.map((id) => this.agents.get(id)).filter((a2) => a2.status !== "crashed" && a2.status !== "killed");
|
|
138675
|
+
const live = this.agentOrder.map((id) => this.agents.get(id)).filter((a2) => a2.status !== "crashed" && a2.status !== "killed" && a2.status !== "failed");
|
|
138675
138676
|
return live.length > 0 && live.every((a2) => a2.status === "done");
|
|
138676
138677
|
}
|
|
138677
138678
|
roleOf(agentId) {
|
|
@@ -139029,7 +139030,7 @@ async function createCollaborationHub(opts) {
|
|
|
139029
139030
|
connections.delete(peer);
|
|
139030
139031
|
if (agentId) {
|
|
139031
139032
|
const agent = state.rosterView().agents.find((a2) => a2.id === agentId);
|
|
139032
|
-
if (agent && agent.status !== "done" && agent.status !== "killed") {
|
|
139033
|
+
if (agent && agent.status !== "done" && agent.status !== "failed" && agent.status !== "killed") {
|
|
139033
139034
|
state.setStatus(agentId, "crashed");
|
|
139034
139035
|
}
|
|
139035
139036
|
}
|
|
@@ -139100,6 +139101,53 @@ function resolveCollabConfig(persisted, overrides) {
|
|
|
139100
139101
|
function stripUndefined(obj) {
|
|
139101
139102
|
return Object.fromEntries(Object.entries(obj).filter(([, v3]) => v3 !== void 0));
|
|
139102
139103
|
}
|
|
139104
|
+
var MAX_MSG_CHARS = 600;
|
|
139105
|
+
var MAX_TOTAL_CHARS = 6e3;
|
|
139106
|
+
function clip(s2, n2) {
|
|
139107
|
+
const t2 = s2.trim().replace(/\s+/g, " ");
|
|
139108
|
+
return t2.length > n2 ? `${t2.slice(0, n2 - 1)}\u2026` : t2;
|
|
139109
|
+
}
|
|
139110
|
+
function digestTurns(events) {
|
|
139111
|
+
const out = [];
|
|
139112
|
+
for (const raw of events) {
|
|
139113
|
+
const e3 = raw;
|
|
139114
|
+
if (e3.type === "user_prompt" && typeof e3.text === "string" && e3.text.trim()) {
|
|
139115
|
+
out.push({ role: "user", text: e3.text });
|
|
139116
|
+
} else if (e3.type === "assistant_message" && e3.source === "model" && typeof e3.content === "string" && e3.content.trim()) {
|
|
139117
|
+
out.push({ role: "assistant", text: e3.content });
|
|
139118
|
+
}
|
|
139119
|
+
}
|
|
139120
|
+
return out;
|
|
139121
|
+
}
|
|
139122
|
+
function buildBrief(task, events) {
|
|
139123
|
+
const turns = digestTurns(events);
|
|
139124
|
+
const trimmed = turns.length > 0 && turns[turns.length - 1].role === "user" && turns[turns.length - 1].text.trim() === task.trim() ? turns.slice(0, -1) : turns;
|
|
139125
|
+
const recent = trimmed.slice(-12);
|
|
139126
|
+
const lines = [
|
|
139127
|
+
"# Collaboration brief",
|
|
139128
|
+
"",
|
|
139129
|
+
"This is the shared context for the whole team. It is the user's goal and the",
|
|
139130
|
+
"conversation that led to it \u2014 read it before planning so your work fits the",
|
|
139131
|
+
"real intent, not just your narrow sub-task.",
|
|
139132
|
+
"",
|
|
139133
|
+
"## Goal",
|
|
139134
|
+
"",
|
|
139135
|
+
clip(task, 1500) || "(no goal text)"
|
|
139136
|
+
];
|
|
139137
|
+
if (recent.length > 0) {
|
|
139138
|
+
lines.push("", "## Conversation so far", "");
|
|
139139
|
+
for (const t2 of recent) {
|
|
139140
|
+
const who = t2.role === "user" ? "User" : "Assistant";
|
|
139141
|
+
lines.push(`- **${who}:** ${clip(t2.text, MAX_MSG_CHARS)}`);
|
|
139142
|
+
}
|
|
139143
|
+
}
|
|
139144
|
+
let brief = lines.join("\n");
|
|
139145
|
+
if (brief.length > MAX_TOTAL_CHARS) {
|
|
139146
|
+
brief = `${brief.slice(0, MAX_TOTAL_CHARS - 1)}\u2026`;
|
|
139147
|
+
}
|
|
139148
|
+
return `${brief}
|
|
139149
|
+
`;
|
|
139150
|
+
}
|
|
139103
139151
|
var IDENTITY = ["-c", "user.name=moxxy-collab", "-c", "user.email=collab@moxxy.local"];
|
|
139104
139152
|
function git(cwd2, args) {
|
|
139105
139153
|
return new Promise((resolve13) => {
|
|
@@ -139275,13 +139323,24 @@ var PeerSupervisor = class {
|
|
|
139275
139323
|
child.on("exit", () => {
|
|
139276
139324
|
proc.exited = true;
|
|
139277
139325
|
});
|
|
139326
|
+
child.on("error", (err) => {
|
|
139327
|
+
proc.exited = true;
|
|
139328
|
+
proc.stderr.push(`spawn error: ${err.message}`);
|
|
139329
|
+
});
|
|
139278
139330
|
return { socket };
|
|
139279
139331
|
}
|
|
139332
|
+
/** True once the child has exited or its spawn failed. */
|
|
139333
|
+
hasExited(agentId) {
|
|
139334
|
+
const proc = this.peers.get(agentId);
|
|
139335
|
+
return proc ? proc.exited : false;
|
|
139336
|
+
}
|
|
139280
139337
|
/** Last stderr lines from a peer — used to diagnose a crash. */
|
|
139281
139338
|
stderrOf(agentId) {
|
|
139282
139339
|
return this.peers.get(agentId)?.stderr ?? [];
|
|
139283
139340
|
}
|
|
139284
|
-
/**
|
|
139341
|
+
/** Stop a single peer and AWAIT its real exit (with a force-kill fallback), so
|
|
139342
|
+
* callers — e.g. the sequential fallback — can rely on the workspace being
|
|
139343
|
+
* free before the next agent starts. */
|
|
139285
139344
|
async stop(agentId) {
|
|
139286
139345
|
const proc = this.peers.get(agentId);
|
|
139287
139346
|
if (!proc || proc.exited)
|
|
@@ -139290,6 +139349,27 @@ var PeerSupervisor = class {
|
|
|
139290
139349
|
proc.child.kill("SIGTERM");
|
|
139291
139350
|
} catch {
|
|
139292
139351
|
}
|
|
139352
|
+
await new Promise((resolve13) => {
|
|
139353
|
+
if (proc.exited)
|
|
139354
|
+
return resolve13();
|
|
139355
|
+
let settled = false;
|
|
139356
|
+
const done = () => {
|
|
139357
|
+
if (settled)
|
|
139358
|
+
return;
|
|
139359
|
+
settled = true;
|
|
139360
|
+
clearTimeout(timer);
|
|
139361
|
+
resolve13();
|
|
139362
|
+
};
|
|
139363
|
+
proc.child.once("exit", done);
|
|
139364
|
+
const timer = setTimeout(() => {
|
|
139365
|
+
try {
|
|
139366
|
+
proc.child.kill("SIGKILL");
|
|
139367
|
+
} catch {
|
|
139368
|
+
}
|
|
139369
|
+
done();
|
|
139370
|
+
}, FORCE_KILL_GRACE_MS);
|
|
139371
|
+
timer.unref?.();
|
|
139372
|
+
});
|
|
139293
139373
|
}
|
|
139294
139374
|
async shutdownAll(_reason) {
|
|
139295
139375
|
if (this.shuttingDown)
|
|
@@ -139423,6 +139503,7 @@ function releaseCollabLock(sessionId) {
|
|
|
139423
139503
|
|
|
139424
139504
|
// ../mode-collaborative/dist/collab-loop.js
|
|
139425
139505
|
var POLL_MS = 500;
|
|
139506
|
+
var BOOT_DEADLINE_MS = 9e4;
|
|
139426
139507
|
function runCollaborativeMode(ctx) {
|
|
139427
139508
|
return runCollaborative(ctx, {});
|
|
139428
139509
|
}
|
|
@@ -139489,15 +139570,26 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139489
139570
|
};
|
|
139490
139571
|
supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
|
|
139491
139572
|
yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
|
|
139573
|
+
try {
|
|
139574
|
+
mkdirSync(join(cwd2, COLLAB_SCAFFOLD_DIR), { recursive: true });
|
|
139575
|
+
writeFileSync(join(cwd2, COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME), buildBrief(task, ctx.log.slice()));
|
|
139576
|
+
yield await ctx.emit(plugin4(ctx, "collab_brief_written", { path: join(COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME) }));
|
|
139577
|
+
} catch {
|
|
139578
|
+
}
|
|
139492
139579
|
supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
|
|
139493
139580
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: ARCHITECT_AGENT_ID, role: "architect" }));
|
|
139494
|
-
const architectOk = await waitForAgent(hub, ARCHITECT_AGENT_ID, ctx.signal, cfg.wallClockMs);
|
|
139581
|
+
const architectOk = await waitForAgent(hub, supervisor, ARCHITECT_AGENT_ID, ctx.signal, cfg.wallClockMs);
|
|
139495
139582
|
if (ctx.signal.aborted) {
|
|
139496
139583
|
yield await ctx.emit(emitAbort(ctx, "aborted during design"));
|
|
139497
139584
|
return;
|
|
139498
139585
|
}
|
|
139499
139586
|
if (!architectOk) {
|
|
139500
|
-
|
|
139587
|
+
const why = supervisor.stderrOf(ARCHITECT_AGENT_ID).slice(-4).join("\n");
|
|
139588
|
+
yield await ctx.emit(plugin4(ctx, "collab_agent_failed", { id: ARCHITECT_AGENT_ID, status: statusOf(hub, ARCHITECT_AGENT_ID), stderr: supervisor.stderrOf(ARCHITECT_AGENT_ID).slice(-6) }));
|
|
139589
|
+
yield await ctx.emit(assistant(ctx, `The architect did not finish the design \u2014 stopping the collaboration.${why ? `
|
|
139590
|
+
|
|
139591
|
+
Last diagnostics:
|
|
139592
|
+
${why}` : ""}`));
|
|
139501
139593
|
return;
|
|
139502
139594
|
}
|
|
139503
139595
|
let roster = readRoster(join(cwd2, COLLAB_SCAFFOLD_DIR, ROSTER_FILENAME), cfg.maxAgents);
|
|
@@ -139538,7 +139630,8 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139538
139630
|
supervisor.spawn({ entry, cwd: wt3, mode: COLLAB_PEER_MODE_NAME });
|
|
139539
139631
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139540
139632
|
}
|
|
139541
|
-
await waitForAgents(hub, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
|
|
139633
|
+
await waitForAgents(hub, supervisor, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
|
|
139634
|
+
yield* surfaceFailures(ctx, hub, supervisor, roster.map((r2) => r2.id));
|
|
139542
139635
|
for (const r2 of roster)
|
|
139543
139636
|
if (statusOf(hub, r2.id) === "done")
|
|
139544
139637
|
doneIds.push(r2.id);
|
|
@@ -139549,7 +139642,9 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139549
139642
|
hub.state.addAgent(entry);
|
|
139550
139643
|
supervisor.spawn({ entry, cwd: cwd2, mode: COLLAB_PEER_MODE_NAME });
|
|
139551
139644
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139552
|
-
const ok = await waitForAgent(hub, entry.id, ctx.signal, cfg.wallClockMs);
|
|
139645
|
+
const ok = await waitForAgent(hub, supervisor, entry.id, ctx.signal, cfg.wallClockMs);
|
|
139646
|
+
if (!ok)
|
|
139647
|
+
yield* surfaceFailures(ctx, hub, supervisor, [entry.id]);
|
|
139553
139648
|
await supervisor.stop(entry.id);
|
|
139554
139649
|
if (ok && statusOf(hub, entry.id) === "done")
|
|
139555
139650
|
doneIds.push(entry.id);
|
|
@@ -139593,6 +139688,16 @@ ${mergeNote}` : ""}`));
|
|
|
139593
139688
|
if (hub)
|
|
139594
139689
|
await hub.close();
|
|
139595
139690
|
releaseCollabLock(String(ctx.sessionId));
|
|
139691
|
+
for (const wt3 of worktrees.values()) {
|
|
139692
|
+
await removeWorktree(cwd2, wt3).catch(() => void 0);
|
|
139693
|
+
}
|
|
139694
|
+
try {
|
|
139695
|
+
rmSync(collabRunDir(runId), { recursive: true, force: true });
|
|
139696
|
+
rmSync(worktreeRoot(runId), { recursive: true, force: true });
|
|
139697
|
+
} catch {
|
|
139698
|
+
}
|
|
139699
|
+
if (worktrees.size > 0)
|
|
139700
|
+
await git(cwd2, ["worktree", "prune"]).catch(() => void 0);
|
|
139596
139701
|
}
|
|
139597
139702
|
}
|
|
139598
139703
|
function lastUserPromptText(ctx) {
|
|
@@ -139643,30 +139748,62 @@ function slug(s2) {
|
|
|
139643
139748
|
function statusOf(hub, id) {
|
|
139644
139749
|
return hub.state.rosterView().agents.find((a2) => a2.id === id)?.status;
|
|
139645
139750
|
}
|
|
139646
|
-
|
|
139647
|
-
const
|
|
139751
|
+
function agentSettled(hub, supervisor, id, connected, bootDeadlineAt) {
|
|
139752
|
+
const status = statusOf(hub, id);
|
|
139753
|
+
if (status && status !== "pending")
|
|
139754
|
+
connected.add(id);
|
|
139755
|
+
if (status === "done")
|
|
139756
|
+
return "done";
|
|
139757
|
+
if (status === "failed" || status === "crashed" || status === "killed")
|
|
139758
|
+
return "failed";
|
|
139759
|
+
if (supervisor?.hasExited(id))
|
|
139760
|
+
return "failed";
|
|
139761
|
+
if (!connected.has(id) && Date.now() > bootDeadlineAt)
|
|
139762
|
+
return "failed";
|
|
139763
|
+
return void 0;
|
|
139764
|
+
}
|
|
139765
|
+
async function waitForAgent(hub, supervisor, id, signal, wallClockMs) {
|
|
139766
|
+
const wallDeadline = Date.now() + wallClockMs;
|
|
139767
|
+
const bootDeadlineAt = Date.now() + BOOT_DEADLINE_MS;
|
|
139768
|
+
const connected = /* @__PURE__ */ new Set();
|
|
139648
139769
|
for (; ; ) {
|
|
139649
|
-
const
|
|
139650
|
-
if (
|
|
139770
|
+
const settled = agentSettled(hub, supervisor, id, connected, bootDeadlineAt);
|
|
139771
|
+
if (settled === "done")
|
|
139651
139772
|
return true;
|
|
139652
|
-
if (
|
|
139773
|
+
if (settled === "failed")
|
|
139653
139774
|
return false;
|
|
139654
|
-
if (signal.aborted || Date.now() >
|
|
139775
|
+
if (signal.aborted || Date.now() > wallDeadline)
|
|
139655
139776
|
return false;
|
|
139656
139777
|
await sleep5(POLL_MS, signal);
|
|
139657
139778
|
}
|
|
139658
139779
|
}
|
|
139659
|
-
async function waitForAgents(hub, ids, signal,
|
|
139660
|
-
const
|
|
139661
|
-
const
|
|
139780
|
+
async function waitForAgents(hub, supervisor, ids, signal, wallClockMs) {
|
|
139781
|
+
const wallDeadline = Date.now() + wallClockMs;
|
|
139782
|
+
const bootDeadlineAt = Date.now() + BOOT_DEADLINE_MS;
|
|
139783
|
+
const connected = /* @__PURE__ */ new Set();
|
|
139662
139784
|
for (; ; ) {
|
|
139663
|
-
if (ids.every((id) =>
|
|
139785
|
+
if (ids.every((id) => agentSettled(hub, supervisor, id, connected, bootDeadlineAt) !== void 0))
|
|
139664
139786
|
return;
|
|
139665
|
-
if (signal.aborted || Date.now() >
|
|
139787
|
+
if (signal.aborted || Date.now() > wallDeadline)
|
|
139666
139788
|
return;
|
|
139667
139789
|
await sleep5(POLL_MS, signal);
|
|
139668
139790
|
}
|
|
139669
139791
|
}
|
|
139792
|
+
async function* surfaceFailures(ctx, hub, supervisor, ids) {
|
|
139793
|
+
for (const id of ids) {
|
|
139794
|
+
const status = statusOf(hub, id);
|
|
139795
|
+
if (status === "done")
|
|
139796
|
+
continue;
|
|
139797
|
+
if (status !== "failed" && status !== "crashed" && status !== "killed") {
|
|
139798
|
+
hub.state.setStatus(id, "crashed", "did not reach a terminal status");
|
|
139799
|
+
}
|
|
139800
|
+
yield await ctx.emit(plugin4(ctx, "collab_agent_failed", {
|
|
139801
|
+
id,
|
|
139802
|
+
status: statusOf(hub, id),
|
|
139803
|
+
stderr: supervisor.stderrOf(id).slice(-6)
|
|
139804
|
+
}));
|
|
139805
|
+
}
|
|
139806
|
+
}
|
|
139670
139807
|
function sleep5(ms, signal) {
|
|
139671
139808
|
return new Promise((resolve13) => {
|
|
139672
139809
|
if (signal.aborted)
|
|
@@ -139730,7 +139867,11 @@ function emitAbort(ctx, reason) {
|
|
|
139730
139867
|
}
|
|
139731
139868
|
|
|
139732
139869
|
// ../mode-collaborative/dist/prompts.js
|
|
139733
|
-
var COLLAB_COMMON = `You are one agent on a TEAM of separate agents collaborating on one task in a shared
|
|
139870
|
+
var COLLAB_COMMON = `You are one agent on a TEAM of separate agents collaborating on one task in a shared workspace. You are a peer, not in charge \u2014 you cooperate.
|
|
139871
|
+
|
|
139872
|
+
Know the WHOLE picture before you act:
|
|
139873
|
+
- ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} is the shared brief \u2014 the user's overall goal and the conversation/intent behind it. Read it FIRST so your work serves the real goal, not just the literal words of your sub-task.
|
|
139874
|
+
- Before planning, recall() any relevant prior knowledge about this workspace/task. When you discover a durable fact (a decision, a gotcha, an interface, a convention), memory_save it so the team \u2014 and future work \u2014 keeps it.
|
|
139734
139875
|
|
|
139735
139876
|
The team coordinates through a shared hub (use these tools):
|
|
139736
139877
|
- collab_roster \u2014 who is on the team, their roles, sub-tasks, and status.
|
|
@@ -139749,11 +139890,12 @@ Cooperation rules:
|
|
|
139749
139890
|
- When YOUR sub-task is fully complete and verified, call collab_done with a short summary. The run finishes when everyone is done.`;
|
|
139750
139891
|
var COLLAB_PEER_PROMPT = `${COLLAB_COMMON}
|
|
139751
139892
|
|
|
139752
|
-
You are an IMPLEMENTER. Your sub-task is provided. Start by reading ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} and calling collab_contracts, collab_roster, and collab_board so you know the plan and who owns what. Claim your files, implement against the contracts, coordinate on intersections, then call collab_done.`;
|
|
139893
|
+
You are an IMPLEMENTER. Your sub-task is provided. Start by reading ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} (the goal + intent) and ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME}, and calling collab_contracts, collab_roster, and collab_board so you know the plan and who owns what. Claim your files, implement against the contracts, coordinate on intersections, then call collab_done.`;
|
|
139753
139894
|
var COLLAB_ARCHITECT_PROMPT = `${COLLAB_COMMON}
|
|
139754
139895
|
|
|
139755
139896
|
You are the ARCHITECT \u2014 you run FIRST and set the team up for success. Your job, in order:
|
|
139756
|
-
|
|
139897
|
+
0. Read ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} \u2014 the user's goal and the conversation behind it \u2014 so you decompose toward what they actually want.
|
|
139898
|
+
1. Explore the workspace to understand the task and its boundaries.
|
|
139757
139899
|
2. Decompose the task into INDEPENDENT sub-tasks with DISJOINT file ownership (minimize overlap so agents rarely touch the same files).
|
|
139758
139900
|
3. Define the shared CONTRACTS \u2014 the interfaces, types, API shapes, and module boundaries where the implementers' work meets. Publish each with collab_contract_publish (give an owner + consumers).
|
|
139759
139901
|
4. Write two files into the repo:
|
|
@@ -139788,6 +139930,8 @@ async function* runCollabAgentLoop(ctx, opts) {
|
|
|
139788
139930
|
permissions: autoApprove
|
|
139789
139931
|
};
|
|
139790
139932
|
const hub = await getProcessHubClient();
|
|
139933
|
+
if (hub)
|
|
139934
|
+
await hub.setStatus("working").catch(() => void 0);
|
|
139791
139935
|
const detector = createStuckLoopDetector();
|
|
139792
139936
|
const maxIterations = ctx.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
139793
139937
|
let noop3 = 0;
|
|
@@ -144390,12 +144534,9 @@ var REMOTE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
144390
144534
|
// Voice input (capability-probed; transcribe fails coded without a transcriber).
|
|
144391
144535
|
"session.hasTranscriber",
|
|
144392
144536
|
"session.transcribe",
|
|
144393
|
-
//
|
|
144394
|
-
//
|
|
144395
|
-
"chat.
|
|
144396
|
-
"chat.loadSegment",
|
|
144397
|
-
"chat.clearLog",
|
|
144398
|
-
"chat.migrate",
|
|
144537
|
+
// Read a workspace's transcript history from the runner's authoritative log
|
|
144538
|
+
// (a paired phone may read history, scoped to a workspace, not host config).
|
|
144539
|
+
"chat.loadHistory",
|
|
144399
144540
|
// Workflows: READ + run an existing one only. Authoring (`workflows.save`,
|
|
144400
144541
|
// `workflows.validateDraft`, `workflows.setEnabled`) is host-only — a paired
|
|
144401
144542
|
// phone must not rewrite or re-enable the host's workflows.
|
|
@@ -144603,25 +144744,13 @@ var ipcInputSchemas = {
|
|
|
144603
144744
|
"mobileGateway.status": z.undefined(),
|
|
144604
144745
|
"mobileGateway.rotateToken": z.undefined(),
|
|
144605
144746
|
"mobileGateway.setEnabled": z.object({ enabled: z.boolean() }).strict(),
|
|
144606
|
-
|
|
144607
|
-
|
|
144608
|
-
|
|
144609
|
-
|
|
144610
|
-
"chat.loadSegment": z.object({
|
|
144747
|
+
// `before` is a runner `seq` cursor; the page is RAW events. The runner itself
|
|
144748
|
+
// re-validates and caps at its own MAX_HISTORY_PAGE_LIMIT (2000), so bound the
|
|
144749
|
+
// renderer's raw-window request to that ceiling.
|
|
144750
|
+
"chat.loadHistory": z.object({
|
|
144611
144751
|
workspaceId: z.string().min(1).max(256),
|
|
144612
144752
|
before: z.number().int().nonnegative().nullable(),
|
|
144613
|
-
limit: z.number().int().positive().max(
|
|
144614
|
-
}),
|
|
144615
|
-
"chat.clearLog": z.object({ workspaceId: z.string().min(1).max(256) }),
|
|
144616
|
-
// chat.migrate writes the supplied events straight into per-workspace NDJSON
|
|
144617
|
-
// logs on disk, so it's a filesystem-touching command: bound both the number
|
|
144618
|
-
// of workspaces and the events per workspace, and lock the workspaceId to a
|
|
144619
|
-
// non-empty bounded slug so it can't traverse out of the log directory.
|
|
144620
|
-
"chat.migrate": z.object({
|
|
144621
|
-
workspaces: z.array(z.object({
|
|
144622
|
-
workspaceId: z.string().min(1).max(256),
|
|
144623
|
-
events: z.array(z.unknown()).max(1e4)
|
|
144624
|
-
})).max(100)
|
|
144753
|
+
limit: z.number().int().positive().max(2e3)
|
|
144625
144754
|
}),
|
|
144626
144755
|
// Vault writes are security-sensitive: lock the key name to a safe slug
|
|
144627
144756
|
// (letters/digits + . _ / - , no traversal) and bound the secret size.
|
|
@@ -144866,13 +144995,7 @@ var MobileSessionHost = class {
|
|
|
144866
144995
|
this.bus.handle("ask.respond", async ({ requestId, response }) => {
|
|
144867
144996
|
this.answerAsk(requestId, response);
|
|
144868
144997
|
});
|
|
144869
|
-
this.bus.handle("chat.
|
|
144870
|
-
this.bus.handle("chat.append", async () => {
|
|
144871
|
-
});
|
|
144872
|
-
this.bus.handle("chat.clearLog", async () => {
|
|
144873
|
-
});
|
|
144874
|
-
this.bus.handle("chat.migrate", async () => {
|
|
144875
|
-
});
|
|
144998
|
+
this.bus.handle("chat.loadHistory", async () => ({ events: [], prevCursor: null }));
|
|
144876
144999
|
}
|
|
144877
145000
|
/** Stream session events to clients + install the ask resolvers. */
|
|
144878
145001
|
wire() {
|
|
@@ -155884,7 +156007,10 @@ async function runAgentCommand(argv) {
|
|
|
155884
156007
|
const sessionId = process.env.MOXXY_SESSION_ID?.trim();
|
|
155885
156008
|
const mode = process.env.MOXXY_MODE?.trim() || "collab-peer";
|
|
155886
156009
|
const subtask = process.env.MOXXY_COLLAB_SUBTASK ?? "";
|
|
156010
|
+
const parentTask = process.env.MOXXY_COLLAB_PARENT_TASK?.trim() ?? "";
|
|
156011
|
+
const role = process.env.MOXXY_COLLAB_ROLE?.trim() ?? "";
|
|
155887
156012
|
const model = process.env.MOXXY_MODEL?.trim();
|
|
156013
|
+
const seededTurn = buildSeedTurn({ role, parentTask, subtask });
|
|
155888
156014
|
const setup = await bootSessionWithConfig(argv, {
|
|
155889
156015
|
skipKeyPrompt: true,
|
|
155890
156016
|
tolerateNoProvider: true,
|
|
@@ -155906,13 +156032,37 @@ async function runAgentCommand(argv) {
|
|
|
155906
156032
|
const runnerServer = await startRunnerServer(session);
|
|
155907
156033
|
const turnDone = (async () => {
|
|
155908
156034
|
try {
|
|
155909
|
-
for await (const _2 of session.runTurn(
|
|
156035
|
+
for await (const _2 of session.runTurn(seededTurn)) void _2;
|
|
156036
|
+
} catch {
|
|
156037
|
+
}
|
|
156038
|
+
try {
|
|
156039
|
+
const hub = await getProcessHubClient();
|
|
156040
|
+
if (hub) {
|
|
156041
|
+
const mine = (await hub.roster()).agents.find((a2) => a2.id === hub.agentId);
|
|
156042
|
+
if (mine && mine.status !== "done") {
|
|
156043
|
+
await hub.setStatus("failed", "turn ended without calling collab_done");
|
|
156044
|
+
}
|
|
156045
|
+
}
|
|
155910
156046
|
} catch {
|
|
155911
156047
|
}
|
|
155912
156048
|
})();
|
|
155913
156049
|
await runUntilSignal2(runnerServer, session, turnDone);
|
|
155914
156050
|
return 0;
|
|
155915
156051
|
}
|
|
156052
|
+
function buildSeedTurn(args) {
|
|
156053
|
+
const { role, parentTask, subtask } = args;
|
|
156054
|
+
const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (the user's overall goal + the conversation/intent) and `.moxxy-collab/CONTRACTS.md` (the agreed interfaces). Read them before you start so your work fits the real goal.";
|
|
156055
|
+
if (role === "architect" || !parentTask || parentTask === subtask) {
|
|
156056
|
+
return subtask ? `${subtask}
|
|
156057
|
+
|
|
156058
|
+
${pointer}` : pointer;
|
|
156059
|
+
}
|
|
156060
|
+
return `Overall team goal: ${parentTask}
|
|
156061
|
+
|
|
156062
|
+
Your sub-task: ${subtask}
|
|
156063
|
+
|
|
156064
|
+
${pointer}`;
|
|
156065
|
+
}
|
|
155916
156066
|
async function runUntilSignal2(runnerServer, session, turnDone) {
|
|
155917
156067
|
let stopping = false;
|
|
155918
156068
|
const shutdown = async (signal) => {
|