@moxxy/cli 0.14.5 → 0.14.7
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 +300 -50
- 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, writeFileSync, rmSync, statSync, readdirSync,
|
|
5
|
+
import fs32__default, { existsSync, promises, readFileSync, ReadStream, mkdirSync, writeFileSync, rmSync, statSync, readdirSync, 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';
|
|
@@ -85713,17 +85713,25 @@ function handleCollabEvent(e3, ref, root) {
|
|
|
85713
85713
|
if (typeof item.id === "string") {
|
|
85714
85714
|
const existing = block.tasks.find((x4) => x4.id === item.id);
|
|
85715
85715
|
const owner = typeof item.owner === "string" ? item.owner : null;
|
|
85716
|
+
const paths = Array.isArray(item.paths) ? item.paths.filter((x4) => typeof x4 === "string") : void 0;
|
|
85717
|
+
const detail = typeof item.detail === "string" ? item.detail : void 0;
|
|
85716
85718
|
if (!existing) {
|
|
85717
85719
|
block.tasks.push({
|
|
85718
85720
|
id: item.id,
|
|
85719
85721
|
title: String(item.title ?? ""),
|
|
85720
85722
|
status: String(item.status ?? "open"),
|
|
85721
|
-
owner
|
|
85723
|
+
owner,
|
|
85724
|
+
...paths && paths.length > 0 ? { paths } : {},
|
|
85725
|
+
...detail ? { detail } : {}
|
|
85722
85726
|
});
|
|
85723
85727
|
} else {
|
|
85724
85728
|
existing.title = String(item.title ?? existing.title);
|
|
85725
85729
|
existing.status = String(item.status ?? existing.status);
|
|
85726
85730
|
existing.owner = owner ?? existing.owner;
|
|
85731
|
+
if (paths && paths.length > 0)
|
|
85732
|
+
existing.paths = paths;
|
|
85733
|
+
if (detail)
|
|
85734
|
+
existing.detail = detail;
|
|
85727
85735
|
}
|
|
85728
85736
|
}
|
|
85729
85737
|
break;
|
|
@@ -136052,6 +136060,7 @@ var COLLAB_SCAFFOLD_DIR = ".moxxy-collab";
|
|
|
136052
136060
|
var CONTRACTS_FILENAME = "CONTRACTS.md";
|
|
136053
136061
|
var ROSTER_FILENAME = "roster.json";
|
|
136054
136062
|
var BRIEF_FILENAME = "BRIEF.md";
|
|
136063
|
+
var CONVERSATION_FILENAME = "CONVERSATION.md";
|
|
136055
136064
|
function collabRunId(sessionId, turnId) {
|
|
136056
136065
|
const tail = (s2) => s2.replace(/[^a-zA-Z0-9]/g, "").slice(-6) || "x";
|
|
136057
136066
|
return `${tail(sessionId)}-${tail(turnId)}`;
|
|
@@ -136065,6 +136074,9 @@ function hubSocketPath(runId) {
|
|
|
136065
136074
|
function peerSocketPath(runId, agentId) {
|
|
136066
136075
|
return join(collabRunDir(runId), `p-${agentId}.sock`);
|
|
136067
136076
|
}
|
|
136077
|
+
function charterFilePath(runId, agentId) {
|
|
136078
|
+
return join(collabRunDir(runId), `charter-${agentId}.md`);
|
|
136079
|
+
}
|
|
136068
136080
|
function worktreeRoot(runId) {
|
|
136069
136081
|
return join(tmpdir(), "moxxy-collab", runId);
|
|
136070
136082
|
}
|
|
@@ -138242,7 +138254,9 @@ var COLLAB_ENV = {
|
|
|
138242
138254
|
Role: "MOXXY_COLLAB_ROLE",
|
|
138243
138255
|
Subtask: "MOXXY_COLLAB_SUBTASK",
|
|
138244
138256
|
ParentTask: "MOXXY_COLLAB_PARENT_TASK",
|
|
138245
|
-
RunnerSocket: "MOXXY_RUNNER_SOCKET"
|
|
138257
|
+
RunnerSocket: "MOXXY_RUNNER_SOCKET",
|
|
138258
|
+
/** Path (never the body) of this agent's architect-authored role charter. */
|
|
138259
|
+
CharterFile: "MOXXY_COLLAB_CHARTER_FILE"
|
|
138246
138260
|
};
|
|
138247
138261
|
var clientPromise = null;
|
|
138248
138262
|
function getProcessHubClient() {
|
|
@@ -139103,6 +139117,9 @@ function stripUndefined(obj) {
|
|
|
139103
139117
|
}
|
|
139104
139118
|
var MAX_MSG_CHARS = 600;
|
|
139105
139119
|
var MAX_TOTAL_CHARS = 6e3;
|
|
139120
|
+
var CONVERSATION_MSG_CHARS = 1200;
|
|
139121
|
+
var CONVERSATION_TOTAL_CHARS = 48e3;
|
|
139122
|
+
var SUMMARY_GUARD_CHARS = 4e3;
|
|
139106
139123
|
function clip(s2, n2) {
|
|
139107
139124
|
const t2 = s2.trim().replace(/\s+/g, " ");
|
|
139108
139125
|
return t2.length > n2 ? `${t2.slice(0, n2 - 1)}\u2026` : t2;
|
|
@@ -139119,35 +139136,141 @@ function digestTurns(events) {
|
|
|
139119
139136
|
}
|
|
139120
139137
|
return out;
|
|
139121
139138
|
}
|
|
139122
|
-
function
|
|
139123
|
-
const
|
|
139124
|
-
|
|
139125
|
-
|
|
139139
|
+
function withoutGoalTail(turns, task) {
|
|
139140
|
+
const last = turns[turns.length - 1];
|
|
139141
|
+
return turns.length > 0 && last.role === "user" && last.text.trim() === task.trim() ? turns.slice(0, -1) : turns;
|
|
139142
|
+
}
|
|
139143
|
+
function buildBrief(task, summary) {
|
|
139126
139144
|
const lines = [
|
|
139127
139145
|
"# Collaboration brief",
|
|
139128
139146
|
"",
|
|
139129
|
-
"This is the shared
|
|
139130
|
-
"
|
|
139131
|
-
"
|
|
139147
|
+
"This is the team's shared brief \u2014 the goal and the key requirements,",
|
|
139148
|
+
"constraints, and decisions distilled from the user's conversation. The full",
|
|
139149
|
+
"transcript is NOT in your context; if you need a specific detail this summary",
|
|
139150
|
+
"omits, read or grep `.moxxy-collab/CONVERSATION.md` (do not load it wholesale).",
|
|
139151
|
+
"",
|
|
139152
|
+
"## Goal",
|
|
139153
|
+
"",
|
|
139154
|
+
clip(task, 1500) || "(no goal text)",
|
|
139155
|
+
"",
|
|
139156
|
+
"## Summary",
|
|
139157
|
+
"",
|
|
139158
|
+
clip(summary, SUMMARY_GUARD_CHARS) || "(no summary available)"
|
|
139159
|
+
];
|
|
139160
|
+
return `${lines.join("\n")}
|
|
139161
|
+
`;
|
|
139162
|
+
}
|
|
139163
|
+
function heuristicSummary(task, events) {
|
|
139164
|
+
const recent = withoutGoalTail(digestTurns(events), task).slice(-12);
|
|
139165
|
+
if (recent.length === 0)
|
|
139166
|
+
return "(no prior conversation to summarize)";
|
|
139167
|
+
const lines = ["(heuristic summary \u2014 LLM summarizer unavailable; recent turns:)"];
|
|
139168
|
+
for (const t2 of recent) {
|
|
139169
|
+
lines.push(`- **${t2.role === "user" ? "User" : "Assistant"}:** ${clip(t2.text, MAX_MSG_CHARS)}`);
|
|
139170
|
+
}
|
|
139171
|
+
let body = lines.join("\n");
|
|
139172
|
+
if (body.length > MAX_TOTAL_CHARS)
|
|
139173
|
+
body = `${body.slice(0, MAX_TOTAL_CHARS - 1)}\u2026`;
|
|
139174
|
+
return body;
|
|
139175
|
+
}
|
|
139176
|
+
function buildConversation(task, events) {
|
|
139177
|
+
const turns = digestTurns(events);
|
|
139178
|
+
const lines = [
|
|
139179
|
+
"# Full conversation (recall-only)",
|
|
139180
|
+
"",
|
|
139181
|
+
"Not loaded into any agent by default. Read or grep this only when you need a",
|
|
139182
|
+
"specific detail the brief summary omits.",
|
|
139132
139183
|
"",
|
|
139133
139184
|
"## Goal",
|
|
139134
139185
|
"",
|
|
139135
|
-
clip(task, 1500) || "(no goal text)"
|
|
139186
|
+
clip(task, 1500) || "(no goal text)",
|
|
139187
|
+
"",
|
|
139188
|
+
"## Conversation",
|
|
139189
|
+
""
|
|
139136
139190
|
];
|
|
139137
|
-
if (
|
|
139138
|
-
lines.push("
|
|
139139
|
-
|
|
139140
|
-
|
|
139141
|
-
lines.push(`- **${
|
|
139191
|
+
if (turns.length === 0) {
|
|
139192
|
+
lines.push("(no prior conversation)");
|
|
139193
|
+
} else {
|
|
139194
|
+
for (const t2 of turns) {
|
|
139195
|
+
lines.push(`- **${t2.role === "user" ? "User" : "Assistant"}:** ${clip(t2.text, CONVERSATION_MSG_CHARS)}`);
|
|
139142
139196
|
}
|
|
139143
139197
|
}
|
|
139144
|
-
let
|
|
139145
|
-
if (
|
|
139146
|
-
|
|
139147
|
-
}
|
|
139148
|
-
return `${brief}
|
|
139198
|
+
let body = lines.join("\n");
|
|
139199
|
+
if (body.length > CONVERSATION_TOTAL_CHARS)
|
|
139200
|
+
body = `${body.slice(0, CONVERSATION_TOTAL_CHARS - 1)}\u2026`;
|
|
139201
|
+
return `${body}
|
|
139149
139202
|
`;
|
|
139150
139203
|
}
|
|
139204
|
+
|
|
139205
|
+
// ../mode-collaborative/dist/summarize.js
|
|
139206
|
+
var MAX_SUMMARIZE_INPUT_CHARS = 48e3;
|
|
139207
|
+
var SUMMARY_MAX_TOKENS = 700;
|
|
139208
|
+
var COLLAB_SUMMARY_SYSTEM = `You write a SHORT shared brief for a team of AI agents about to build ONE deliverable together. From the user's conversation, extract ONLY:
|
|
139209
|
+
1. The overall goal, in one or two sentences.
|
|
139210
|
+
2. The concrete requirements and constraints.
|
|
139211
|
+
3. Any decisions already made, and the reason.
|
|
139212
|
+
4. Explicit do-nots / out-of-scope.
|
|
139213
|
+
Use terse bullet points. Do NOT restate the raw conversation, do NOT invent details, omit chit-chat and pleasantries. Output ONLY the brief text \u2014 no preamble, no sign-off. Keep it well under 400 words.`;
|
|
139214
|
+
async function summarizeConversation(args) {
|
|
139215
|
+
const { task, events, provider, model, signal } = args;
|
|
139216
|
+
if (!provider || !model)
|
|
139217
|
+
return null;
|
|
139218
|
+
const turns = digestTurns(events);
|
|
139219
|
+
if (turns.length === 0)
|
|
139220
|
+
return null;
|
|
139221
|
+
const joined = turns.map((t2) => `[${t2.role}] ${t2.text}`).join("\n");
|
|
139222
|
+
const input = joined.length > MAX_SUMMARIZE_INPUT_CHARS ? `${joined.slice(0, MAX_SUMMARIZE_INPUT_CHARS / 2)}
|
|
139223
|
+
[... transcript truncated ...]
|
|
139224
|
+
${joined.slice(-MAX_SUMMARIZE_INPUT_CHARS / 2)}` : joined;
|
|
139225
|
+
try {
|
|
139226
|
+
let out = "";
|
|
139227
|
+
for await (const event of provider.stream({
|
|
139228
|
+
model,
|
|
139229
|
+
system: COLLAB_SUMMARY_SYSTEM,
|
|
139230
|
+
messages: [
|
|
139231
|
+
{
|
|
139232
|
+
role: "user",
|
|
139233
|
+
content: [
|
|
139234
|
+
{
|
|
139235
|
+
type: "text",
|
|
139236
|
+
text: `Task headline: ${task}
|
|
139237
|
+
|
|
139238
|
+
The user's conversation that produced this task:
|
|
139239
|
+
|
|
139240
|
+
${input}`
|
|
139241
|
+
}
|
|
139242
|
+
]
|
|
139243
|
+
}
|
|
139244
|
+
],
|
|
139245
|
+
maxTokens: SUMMARY_MAX_TOKENS,
|
|
139246
|
+
...signal ? { signal } : {}
|
|
139247
|
+
})) {
|
|
139248
|
+
if (event.type === "text_delta")
|
|
139249
|
+
out += event.delta;
|
|
139250
|
+
if (event.type === "error")
|
|
139251
|
+
return null;
|
|
139252
|
+
}
|
|
139253
|
+
const trimmed = out.trim();
|
|
139254
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
139255
|
+
} catch {
|
|
139256
|
+
return null;
|
|
139257
|
+
}
|
|
139258
|
+
}
|
|
139259
|
+
function moxxyHome2() {
|
|
139260
|
+
return process.env.MOXXY_HOME ?? join(homedir(), ".moxxy");
|
|
139261
|
+
}
|
|
139262
|
+
function collabRunsDir() {
|
|
139263
|
+
return join(moxxyHome2(), "collab", "runs");
|
|
139264
|
+
}
|
|
139265
|
+
function writeRunRecord(rec) {
|
|
139266
|
+
try {
|
|
139267
|
+
const dir = collabRunsDir();
|
|
139268
|
+
mkdirSync(dir, { recursive: true });
|
|
139269
|
+
writeFileSync(join(dir, `${rec.runId}.json`), `${JSON.stringify(rec, null, 2)}
|
|
139270
|
+
`);
|
|
139271
|
+
} catch {
|
|
139272
|
+
}
|
|
139273
|
+
}
|
|
139151
139274
|
var IDENTITY = ["-c", "user.name=moxxy-collab", "-c", "user.email=collab@moxxy.local"];
|
|
139152
139275
|
function git(cwd2, args) {
|
|
139153
139276
|
return new Promise((resolve13) => {
|
|
@@ -139297,6 +139420,7 @@ var PeerSupervisor = class {
|
|
|
139297
139420
|
[COLLAB_ENV.Subtask]: args.entry.subtask,
|
|
139298
139421
|
[COLLAB_ENV.ParentTask]: this.opts.parentTask,
|
|
139299
139422
|
[COLLAB_ENV.RunnerSocket]: socket,
|
|
139423
|
+
...args.charterFile ? { [COLLAB_ENV.CharterFile]: args.charterFile } : {},
|
|
139300
139424
|
MOXXY_SESSION_ID: `${this.opts.coordinatorSessionId}::${args.entry.id}`,
|
|
139301
139425
|
MOXXY_MODE: args.mode
|
|
139302
139426
|
};
|
|
@@ -139526,14 +139650,22 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139526
139650
|
return;
|
|
139527
139651
|
}
|
|
139528
139652
|
const runId = collabRunId(String(ctx.sessionId), String(ctx.turnId));
|
|
139653
|
+
const startedAtMs = Date.now();
|
|
139529
139654
|
const worktrees = /* @__PURE__ */ new Map();
|
|
139530
139655
|
let hub = null;
|
|
139531
139656
|
let supervisor = null;
|
|
139532
139657
|
let unsubscribe = null;
|
|
139658
|
+
let archiveParallel = false;
|
|
139659
|
+
let archiveGitRepo = false;
|
|
139660
|
+
let briefText = "";
|
|
139661
|
+
let mergeForArchive;
|
|
139662
|
+
let completed = false;
|
|
139533
139663
|
try {
|
|
139534
139664
|
mkdirSync(collabRunDir(runId), { recursive: true });
|
|
139535
139665
|
const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
|
|
139536
139666
|
const parallel = cfg.concurrency === "parallel" && gitRepo;
|
|
139667
|
+
archiveParallel = parallel;
|
|
139668
|
+
archiveGitRepo = gitRepo;
|
|
139537
139669
|
if (!parallel) {
|
|
139538
139670
|
yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
|
|
139539
139671
|
reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
|
|
@@ -139571,9 +139703,24 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139571
139703
|
supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
|
|
139572
139704
|
yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
|
|
139573
139705
|
try {
|
|
139706
|
+
const events = ctx.log.slice();
|
|
139574
139707
|
mkdirSync(join(cwd2, COLLAB_SCAFFOLD_DIR), { recursive: true });
|
|
139575
|
-
writeFileSync(join(cwd2, COLLAB_SCAFFOLD_DIR,
|
|
139576
|
-
|
|
139708
|
+
writeFileSync(join(cwd2, COLLAB_SCAFFOLD_DIR, CONVERSATION_FILENAME), buildConversation(task, events));
|
|
139709
|
+
const llmSummary = await summarizeConversation({
|
|
139710
|
+
task,
|
|
139711
|
+
events,
|
|
139712
|
+
provider: ctx.provider,
|
|
139713
|
+
model: ctx.model,
|
|
139714
|
+
signal: ctx.signal
|
|
139715
|
+
}).catch(() => null);
|
|
139716
|
+
const summary = llmSummary ?? heuristicSummary(task, events);
|
|
139717
|
+
briefText = buildBrief(task, summary);
|
|
139718
|
+
writeFileSync(join(cwd2, COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME), briefText);
|
|
139719
|
+
yield await ctx.emit(plugin4(ctx, "collab_brief_written", {
|
|
139720
|
+
path: join(COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME),
|
|
139721
|
+
conversationPath: join(COLLAB_SCAFFOLD_DIR, CONVERSATION_FILENAME),
|
|
139722
|
+
summarized: Boolean(llmSummary)
|
|
139723
|
+
}));
|
|
139577
139724
|
} catch {
|
|
139578
139725
|
}
|
|
139579
139726
|
supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
|
|
@@ -139600,8 +139747,12 @@ ${why}` : ""}`));
|
|
|
139600
139747
|
if (cfg.requireRosterApproval && ctx.approval) {
|
|
139601
139748
|
const decision = await ctx.approval.confirm({
|
|
139602
139749
|
title: `Team of ${roster.length} agent${roster.length === 1 ? "" : "s"} \u2014 review before launch`,
|
|
139603
|
-
body: roster.map((r2, i2) =>
|
|
139604
|
-
|
|
139750
|
+
body: roster.map((r2, i2) => {
|
|
139751
|
+
const charterLine = r2.charter ? `
|
|
139752
|
+
charter: ${r2.charter.replace(/\s+/g, " ").slice(0, 140)}${r2.charter.length > 140 ? "\u2026" : ""}` : "";
|
|
139753
|
+
return `${i2 + 1}. [${r2.id}] ${r2.name} \u2014 ${r2.role}
|
|
139754
|
+
${r2.subtask}${charterLine}`;
|
|
139755
|
+
}).join("\n\n"),
|
|
139605
139756
|
kind: "collab.roster",
|
|
139606
139757
|
defaultOptionId: "launch",
|
|
139607
139758
|
options: [
|
|
@@ -139627,7 +139778,8 @@ ${why}` : ""}`));
|
|
|
139627
139778
|
const wt3 = worktreePath(runId, entry.id);
|
|
139628
139779
|
await addWorktree({ repoCwd: cwd2, path: wt3, branch: collabBranch(runId, entry.id), baseSha });
|
|
139629
139780
|
worktrees.set(entry.id, wt3);
|
|
139630
|
-
|
|
139781
|
+
const charterFile = writeCharterFile(runId, entry);
|
|
139782
|
+
supervisor.spawn({ entry, cwd: wt3, mode: COLLAB_PEER_MODE_NAME, ...charterFile ? { charterFile } : {} });
|
|
139631
139783
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139632
139784
|
}
|
|
139633
139785
|
await waitForAgents(hub, supervisor, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
|
|
@@ -139640,7 +139792,8 @@ ${why}` : ""}`));
|
|
|
139640
139792
|
if (ctx.signal.aborted)
|
|
139641
139793
|
break;
|
|
139642
139794
|
hub.state.addAgent(entry);
|
|
139643
|
-
|
|
139795
|
+
const charterFile = writeCharterFile(runId, entry);
|
|
139796
|
+
supervisor.spawn({ entry, cwd: cwd2, mode: COLLAB_PEER_MODE_NAME, ...charterFile ? { charterFile } : {} });
|
|
139644
139797
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139645
139798
|
const ok = await waitForAgent(hub, supervisor, entry.id, ctx.signal, cfg.wallClockMs);
|
|
139646
139799
|
if (!ok)
|
|
@@ -139665,6 +139818,12 @@ ${why}` : ""}`));
|
|
|
139665
139818
|
board: hub.state.boardItems(),
|
|
139666
139819
|
mergePolicy: cfg.mergePolicy
|
|
139667
139820
|
});
|
|
139821
|
+
mergeForArchive = {
|
|
139822
|
+
merged: result.merged,
|
|
139823
|
+
promoted: result.promoted,
|
|
139824
|
+
conflicts: result.conflicts.length,
|
|
139825
|
+
...result.stagingBranch ? { stagingBranch: result.stagingBranch } : {}
|
|
139826
|
+
};
|
|
139668
139827
|
yield await ctx.emit(plugin4(ctx, "collab_merge", result));
|
|
139669
139828
|
for (const c2 of result.conflicts) {
|
|
139670
139829
|
yield await ctx.emit(plugin4(ctx, "collab_conflict", c2));
|
|
@@ -139678,10 +139837,54 @@ ${why}` : ""}`));
|
|
|
139678
139837
|
${summaryBlock}${mergeNote ? `
|
|
139679
139838
|
|
|
139680
139839
|
${mergeNote}` : ""}`));
|
|
139840
|
+
completed = true;
|
|
139681
139841
|
yield await ctx.emit(plugin4(ctx, "collab_completed", { done: doneIds, total: roster.length }));
|
|
139682
139842
|
} finally {
|
|
139683
139843
|
if (supervisor)
|
|
139684
139844
|
await supervisor.shutdownAll("collaboration complete");
|
|
139845
|
+
if (hub) {
|
|
139846
|
+
try {
|
|
139847
|
+
const agents = hub.state.rosterView().agents;
|
|
139848
|
+
const implementers = agents.filter((a2) => a2.role !== "architect");
|
|
139849
|
+
writeRunRecord({
|
|
139850
|
+
runId,
|
|
139851
|
+
task,
|
|
139852
|
+
startedAtMs,
|
|
139853
|
+
finishedAtMs: Date.now(),
|
|
139854
|
+
outcome: completed ? "completed" : ctx.signal.aborted ? "aborted" : "failed",
|
|
139855
|
+
parallel: archiveParallel,
|
|
139856
|
+
gitRepo: archiveGitRepo,
|
|
139857
|
+
agents: agents.map((a2) => ({
|
|
139858
|
+
id: a2.id,
|
|
139859
|
+
name: a2.name,
|
|
139860
|
+
role: a2.role,
|
|
139861
|
+
status: a2.status,
|
|
139862
|
+
subtask: a2.subtask,
|
|
139863
|
+
...a2.doneSummary ? { doneSummary: a2.doneSummary } : {}
|
|
139864
|
+
})),
|
|
139865
|
+
doneCount: implementers.filter((a2) => a2.status === "done").length,
|
|
139866
|
+
totalCount: implementers.length,
|
|
139867
|
+
board: hub.state.boardItems().map((b3) => ({
|
|
139868
|
+
id: b3.id,
|
|
139869
|
+
title: b3.title,
|
|
139870
|
+
status: b3.status,
|
|
139871
|
+
...b3.owner ? { owner: b3.owner } : {},
|
|
139872
|
+
...b3.paths ? { paths: b3.paths } : {}
|
|
139873
|
+
})),
|
|
139874
|
+
contracts: hub.state.contractList().map((c2) => ({
|
|
139875
|
+
id: c2.id,
|
|
139876
|
+
title: c2.title,
|
|
139877
|
+
owner: c2.owner,
|
|
139878
|
+
status: c2.status,
|
|
139879
|
+
version: c2.version
|
|
139880
|
+
})),
|
|
139881
|
+
messageCount: hub.state.allMessages().length,
|
|
139882
|
+
...mergeForArchive ? { merge: mergeForArchive } : {},
|
|
139883
|
+
...briefText ? { brief: briefText } : {}
|
|
139884
|
+
});
|
|
139885
|
+
} catch {
|
|
139886
|
+
}
|
|
139887
|
+
}
|
|
139685
139888
|
if (unsubscribe)
|
|
139686
139889
|
unsubscribe();
|
|
139687
139890
|
unregisterActiveHub(String(ctx.sessionId));
|
|
@@ -139729,10 +139932,13 @@ function readRoster(path62, maxAgents) {
|
|
|
139729
139932
|
out.push({
|
|
139730
139933
|
id,
|
|
139731
139934
|
name: typeof r2.name === "string" ? r2.name : id,
|
|
139732
|
-
role
|
|
139935
|
+
// Carry the architect's proposed role (pm/designer/developer/qa/writer/…)
|
|
139936
|
+
// so the team is cross-functional, not a pool of identical implementers.
|
|
139937
|
+
role: cleanRole(r2.role),
|
|
139733
139938
|
subtask: r2.subtask,
|
|
139734
139939
|
...Array.isArray(r2.ownedPaths) ? { ownedPaths: r2.ownedPaths.filter((p3) => typeof p3 === "string") } : {},
|
|
139735
|
-
...typeof r2.model === "string" ? { model: r2.model } : {}
|
|
139940
|
+
...typeof r2.model === "string" ? { model: r2.model } : {},
|
|
139941
|
+
...typeof r2.charter === "string" && r2.charter.trim() ? { charter: cleanCharter(r2.charter) } : {}
|
|
139736
139942
|
});
|
|
139737
139943
|
if (out.length >= maxAgents)
|
|
139738
139944
|
break;
|
|
@@ -139745,6 +139951,29 @@ function readRoster(path62, maxAgents) {
|
|
|
139745
139951
|
function slug(s2) {
|
|
139746
139952
|
return s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
|
|
139747
139953
|
}
|
|
139954
|
+
function cleanRole(raw) {
|
|
139955
|
+
if (typeof raw !== "string")
|
|
139956
|
+
return "implementer";
|
|
139957
|
+
const r2 = raw.toLowerCase().replace(/[^a-z0-9 -]/g, "").replace(/\s+/g, " ").trim().slice(0, 24);
|
|
139958
|
+
if (!r2 || r2 === ARCHITECT_AGENT_ID)
|
|
139959
|
+
return "implementer";
|
|
139960
|
+
return r2;
|
|
139961
|
+
}
|
|
139962
|
+
function cleanCharter(raw) {
|
|
139963
|
+
return raw.replace(/\u0000/g, "").trim().slice(0, 2e3);
|
|
139964
|
+
}
|
|
139965
|
+
function writeCharterFile(runId, entry) {
|
|
139966
|
+
if (!entry.charter)
|
|
139967
|
+
return void 0;
|
|
139968
|
+
const p3 = charterFilePath(runId, entry.id);
|
|
139969
|
+
try {
|
|
139970
|
+
writeFileSync(p3, `${entry.charter}
|
|
139971
|
+
`);
|
|
139972
|
+
return p3;
|
|
139973
|
+
} catch {
|
|
139974
|
+
return void 0;
|
|
139975
|
+
}
|
|
139976
|
+
}
|
|
139748
139977
|
function statusOf(hub, id) {
|
|
139749
139978
|
return hub.state.rosterView().agents.find((a2) => a2.id === id)?.status;
|
|
139750
139979
|
}
|
|
@@ -139870,7 +140099,7 @@ function emitAbort(ctx, reason) {
|
|
|
139870
140099
|
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
140100
|
|
|
139872
140101
|
Know the WHOLE picture before you act:
|
|
139873
|
-
- ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} is the shared brief \u2014 the user's
|
|
140102
|
+
- ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} is the shared brief \u2014 a concise summary of the user's goal and the key requirements/constraints/decisions. Read it FIRST so your work serves the real goal, not just the literal words of your sub-task. The full conversation is NOT in your context; if you need a specific detail the brief omits, read or grep ${COLLAB_SCAFFOLD_DIR}/${CONVERSATION_FILENAME} \u2014 do NOT load it wholesale.
|
|
139874
140103
|
- 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.
|
|
139875
140104
|
|
|
139876
140105
|
The team coordinates through a shared hub (use these tools):
|
|
@@ -139885,25 +140114,34 @@ The team coordinates through a shared hub (use these tools):
|
|
|
139885
140114
|
|
|
139886
140115
|
Cooperation rules:
|
|
139887
140116
|
- Build strictly to the shared contracts. If you must change a shared boundary, use collab_contract_propose_change and wait for acks \u2014 never break a contract unilaterally.
|
|
139888
|
-
- The human may step in at any time
|
|
140117
|
+
- The human may step in at any time. When you receive a HUMAN directive or a message from "human", treat it as authoritative (it overrides your current plan), and REPLY to them with collab_send to "human" \u2014 acknowledge it and say what you'll do (or ask a brief clarifying question). Don't go silent on the human. If the team is paused, finish your current edit and wait.
|
|
139889
140118
|
- Keep teammates informed: broadcast meaningful progress and blockers.
|
|
139890
140119
|
- When YOUR sub-task is fully complete and verified, call collab_done with a short summary. The run finishes when everyone is done.`;
|
|
139891
140120
|
var COLLAB_PEER_PROMPT = `${COLLAB_COMMON}
|
|
139892
140121
|
|
|
139893
|
-
You are
|
|
140122
|
+
You are a TEAM MEMBER with a specific role (given below) and sub-task. Work as that role \u2014 a writer writes, a designer designs, a developer builds, a QA reviews, a PM sequences + verifies. 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, deliver your part against the contracts, coordinate on intersections, then call collab_done.`;
|
|
139894
140123
|
var COLLAB_ARCHITECT_PROMPT = `${COLLAB_COMMON}
|
|
139895
140124
|
|
|
139896
140125
|
You are the ARCHITECT \u2014 you run FIRST and set the team up for success. Your job, in order:
|
|
139897
|
-
0. Read ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} \u2014 the
|
|
140126
|
+
0. Read ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} \u2014 the goal + key-requirements summary \u2014 so you decompose toward what the user actually wants. If you need a detail it omits, grep ${COLLAB_SCAFFOLD_DIR}/${CONVERSATION_FILENAME}.
|
|
139898
140127
|
1. Explore the workspace to understand the task and its boundaries.
|
|
139899
|
-
2.
|
|
139900
|
-
3. Define the shared CONTRACTS \u2014 the interfaces, types, API shapes,
|
|
140128
|
+
2. Assemble the RIGHT TEAM for THIS deliverable and decompose into INDEPENDENT sub-tasks with DISJOINT ownership (minimize overlap). Pick the roles the work actually needs \u2014 a coding task wants developers + a QA reviewer; a document/plan/design deliverable wants a writer, a researcher, a designer, an editor; a product effort may want a PM to sequence + verify. Don't default everyone to a generic "implementer".
|
|
140129
|
+
3. Define the shared CONTRACTS \u2014 the interfaces, types, API shapes, section outlines, or boundaries where the team's work meets. Publish each with collab_contract_publish (give an owner + consumers).
|
|
139901
140130
|
4. Write two files into the repo:
|
|
139902
|
-
- ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} \u2014 human-readable contracts/boundaries the
|
|
139903
|
-
- ${COLLAB_SCAFFOLD_DIR}/${ROSTER_FILENAME} \u2014 a JSON array proposing the
|
|
140131
|
+
- ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} \u2014 human-readable contracts/boundaries the team must follow.
|
|
140132
|
+
- ${COLLAB_SCAFFOLD_DIR}/${ROSTER_FILENAME} \u2014 a JSON array proposing the team. Each entry: { "id": "kebab-slug", "name": "Display Name", "role": "<function>", "subtask": "what this agent delivers", "ownedPaths": ["dir/", "file.md"], "charter": "..." }. "role" is the agent's FUNCTION \u2014 e.g. "developer", "designer", "pm", "qa", "writer", "researcher", "editor" \u2014 choose what fits. For EACH agent write a tailored "charter": a short system-prompt-style brief (roughly 4-8 sentences, plain prose in the second person \u2014 "You are \u2026", no markdown headings) giving THIS agent, for THIS task: (a) its persona/expertise, (b) its concrete responsibilities and scope, (c) the quality bar it must hit, (d) how it works with the rest of the team, (e) its definition of done. Make each charter specific to the deliverable \u2014 this is how you create proper, task-suited roles instead of generic workers. Do NOT include yourself, and do NOT use "architect" (that's you).
|
|
139904
140133
|
5. Broadcast a short kickoff summary, then call collab_done.
|
|
139905
140134
|
|
|
139906
140135
|
After the implementers start, you stay available as the BROKER: answer interface questions, and when an implementer proposes a contract change, review it and (if sound) commit it with collab_contract_update so everyone re-syncs.`;
|
|
140136
|
+
function peerPromptWithCharter(charter) {
|
|
140137
|
+
if (!charter || !charter.trim())
|
|
140138
|
+
return COLLAB_PEER_PROMPT;
|
|
140139
|
+
return `${COLLAB_PEER_PROMPT}
|
|
140140
|
+
|
|
140141
|
+
## Your charter
|
|
140142
|
+
|
|
140143
|
+
${charter.trim()}`;
|
|
140144
|
+
}
|
|
139907
140145
|
var COLLAB_DONE_TOOL = "collab_done";
|
|
139908
140146
|
var DEFAULT_MAX_ITERATIONS = 60;
|
|
139909
140147
|
var MAX_NOOP_ITERATIONS = 3;
|
|
@@ -140169,11 +140407,21 @@ var collabArchitectMode = defineMode({
|
|
|
140169
140407
|
badge: { label: "ARCHITECT", tone: "attention" },
|
|
140170
140408
|
run: (ctx) => runCollabAgentLoop(ctx, { systemPrompt: COLLAB_ARCHITECT_PROMPT })
|
|
140171
140409
|
});
|
|
140410
|
+
function peerSystemPrompt(env3) {
|
|
140411
|
+
const path62 = env3[COLLAB_ENV.CharterFile]?.trim();
|
|
140412
|
+
if (!path62)
|
|
140413
|
+
return COLLAB_PEER_PROMPT;
|
|
140414
|
+
try {
|
|
140415
|
+
return peerPromptWithCharter(readFileSync(path62, "utf8"));
|
|
140416
|
+
} catch {
|
|
140417
|
+
return COLLAB_PEER_PROMPT;
|
|
140418
|
+
}
|
|
140419
|
+
}
|
|
140172
140420
|
var collabPeerMode = defineMode({
|
|
140173
140421
|
name: COLLAB_PEER_MODE_NAME,
|
|
140174
|
-
description: "Collaboration peer (internal):
|
|
140422
|
+
description: "Collaboration peer (internal): a team member building to the shared contracts.",
|
|
140175
140423
|
badge: { label: "TEAM", tone: "attention" },
|
|
140176
|
-
run: (ctx) => runCollabAgentLoop(ctx, { systemPrompt:
|
|
140424
|
+
run: (ctx) => runCollabAgentLoop(ctx, { systemPrompt: peerSystemPrompt(ctx.env) })
|
|
140177
140425
|
});
|
|
140178
140426
|
|
|
140179
140427
|
// ../mode-collaborative/dist/index.js
|
|
@@ -140188,8 +140436,8 @@ var collaborativeModePlugin = definePlugin({
|
|
|
140188
140436
|
version: "0.0.0",
|
|
140189
140437
|
modes: [collaborativeMode, collabArchitectMode, collabPeerMode]
|
|
140190
140438
|
});
|
|
140191
|
-
var
|
|
140192
|
-
var
|
|
140439
|
+
var MAX_SUMMARIZE_INPUT_CHARS2 = 48e3;
|
|
140440
|
+
var SUMMARY_MAX_TOKENS2 = 1024;
|
|
140193
140441
|
var FALLBACK_DIGEST_CHARS = 6e3;
|
|
140194
140442
|
var SUMMARY_SYSTEM_PROMPT = "You compress conversation history for an AI agent so it can keep working with less context. You are given a line-per-event digest of earlier turns. Write a dense, factual brief the agent can rely on: the task and its current state, key decisions and their reasons, important file paths / identifiers / values, tool outcomes (including failures), and any unresolved questions or TODOs. Do not editorialize, do not invent details, output ONLY the summary text.";
|
|
140195
140443
|
function createSummarizeCompactor(opts = {}) {
|
|
@@ -140256,9 +140504,9 @@ async function providerSummary(text, ctx) {
|
|
|
140256
140504
|
const model = ctx.model ?? provider.models[0]?.id;
|
|
140257
140505
|
if (!model)
|
|
140258
140506
|
return null;
|
|
140259
|
-
const input = text.length >
|
|
140507
|
+
const input = text.length > MAX_SUMMARIZE_INPUT_CHARS2 ? `${text.slice(0, MAX_SUMMARIZE_INPUT_CHARS2 / 2)}
|
|
140260
140508
|
[... digest truncated ...]
|
|
140261
|
-
${text.slice(-
|
|
140509
|
+
${text.slice(-MAX_SUMMARIZE_INPUT_CHARS2 / 2)}` : text;
|
|
140262
140510
|
try {
|
|
140263
140511
|
let out = "";
|
|
140264
140512
|
for await (const event of provider.stream({
|
|
@@ -140272,7 +140520,7 @@ ${text.slice(-MAX_SUMMARIZE_INPUT_CHARS / 2)}` : text;
|
|
|
140272
140520
|
${input}` }]
|
|
140273
140521
|
}
|
|
140274
140522
|
],
|
|
140275
|
-
maxTokens:
|
|
140523
|
+
maxTokens: SUMMARY_MAX_TOKENS2,
|
|
140276
140524
|
...ctx.signal ? { signal: ctx.signal } : {}
|
|
140277
140525
|
})) {
|
|
140278
140526
|
if (event.type === "text_delta")
|
|
@@ -149574,13 +149822,13 @@ async function runWorkflow(workflow, deps, opts = {}) {
|
|
|
149574
149822
|
const startedAt = (deps.now ?? Date.now)();
|
|
149575
149823
|
const result = await executor.run(workflow, deps);
|
|
149576
149824
|
if (opts.recordDir !== null) {
|
|
149577
|
-
await
|
|
149825
|
+
await writeRunRecord2(workflow, result, startedAt, executor.name, deps, opts.recordDir ?? defaultRunRecordDir()).catch((err) => deps.logger?.warn?.("workflow: failed to write run record", {
|
|
149578
149826
|
error: err instanceof Error ? err.message : String(err)
|
|
149579
149827
|
}));
|
|
149580
149828
|
}
|
|
149581
149829
|
return result;
|
|
149582
149830
|
}
|
|
149583
|
-
async function
|
|
149831
|
+
async function writeRunRecord2(workflow, result, startedAt, executorName, deps, dir) {
|
|
149584
149832
|
await promises.mkdir(dir, { recursive: true });
|
|
149585
149833
|
const stamp = new Date(startedAt).toISOString().replace(/[:.]/g, "-");
|
|
149586
149834
|
const file = path3.join(dir, `${stamp}-${workflow.name}-${ulid().slice(-6)}.jsonl`);
|
|
@@ -153961,11 +154209,11 @@ function servicePlatform() {
|
|
|
153961
154209
|
if (process.platform === "linux") return "linux";
|
|
153962
154210
|
return "unsupported";
|
|
153963
154211
|
}
|
|
153964
|
-
function
|
|
154212
|
+
function moxxyHome3() {
|
|
153965
154213
|
return process.env.MOXXY_HOME ?? path3__default.join(homedir(), ".moxxy");
|
|
153966
154214
|
}
|
|
153967
154215
|
function serviceLogPath(spec) {
|
|
153968
|
-
return path3__default.join(
|
|
154216
|
+
return path3__default.join(moxxyHome3(), "services", `${spec.id}.log`);
|
|
153969
154217
|
}
|
|
153970
154218
|
function nodeBin() {
|
|
153971
154219
|
return process.execPath;
|
|
@@ -156051,13 +156299,15 @@ async function runAgentCommand(argv) {
|
|
|
156051
156299
|
}
|
|
156052
156300
|
function buildSeedTurn(args) {
|
|
156053
156301
|
const { role, parentTask, subtask } = args;
|
|
156054
|
-
const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (the user's
|
|
156302
|
+
const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (a concise summary of the user's goal + key requirements) and `.moxxy-collab/CONTRACTS.md` (the agreed interfaces). Read them before you start so your work fits the real goal. If you need a detail the brief omits, read or grep `.moxxy-collab/CONVERSATION.md` (the full transcript) \u2014 do not load it wholesale.";
|
|
156055
156303
|
if (role === "architect" || !parentTask || parentTask === subtask) {
|
|
156056
156304
|
return subtask ? `${subtask}
|
|
156057
156305
|
|
|
156058
156306
|
${pointer}` : pointer;
|
|
156059
156307
|
}
|
|
156060
|
-
|
|
156308
|
+
const roleLine = role && role !== "implementer" ? `Your role on the team: ${role}.
|
|
156309
|
+
` : "";
|
|
156310
|
+
return `${roleLine}Overall team goal: ${parentTask}
|
|
156061
156311
|
|
|
156062
156312
|
Your sub-task: ${subtask}
|
|
156063
156313
|
|