@yemi33/minions 0.1.2220 → 0.1.2222
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/dashboard/js/refresh.js +1 -1
- package/dashboard/js/render-prd.js +12 -2
- package/dashboard/js/settings.js +3 -1
- package/dashboard.js +15 -1
- package/docs/README.md +3 -1
- package/docs/auto-discovery.md +1 -1
- package/docs/deprecated.json +13 -0
- package/docs/named-agents.md +216 -0
- package/docs/specs/agent-configurability.md +268 -127
- package/docs/specs/agent-rename.md +163 -0
- package/docs/worktree-lifecycle.md +117 -0
- package/engine/cleanup.js +23 -2
- package/engine/lifecycle.js +6 -4
- package/engine/playbook.js +2 -2
- package/engine/queries.js +4 -1
- package/engine/shared.js +132 -5
- package/engine/worktree-gc.js +161 -0
- package/engine.js +241 -20
- package/package.json +1 -1
package/dashboard/js/refresh.js
CHANGED
|
@@ -143,9 +143,19 @@ function renderPrd(prd, prog) {
|
|
|
143
143
|
section.innerHTML = '';
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// Color a PR status for the PRD page pills/glyphs: merged -> green,
|
|
147
|
+
// abandoned/closed (any terminal-non-merged) -> red, active/other -> neutral
|
|
148
|
+
// default text color (no accent highlight).
|
|
149
|
+
function _prStatusColor(status) {
|
|
150
|
+
if (status === 'merged') return 'var(--green)';
|
|
151
|
+
if (!status || status === 'active') return 'var(--text)';
|
|
152
|
+
return 'var(--red)';
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
function _renderPrLink(pr, opts) {
|
|
147
156
|
var size = (opts && opts.size) || '10px';
|
|
148
157
|
var statusIcon = pr.status === 'merged' ? '✓' : pr.status === 'abandoned' ? '✗' : '○';
|
|
158
|
+
var iconColor = _prStatusColor(pr.status);
|
|
149
159
|
// P-79b47b0c — render as an in-stack modal chip via renderArtifactLink
|
|
150
160
|
// (was a raw <a target="_blank">). The status icon stays as a non-link
|
|
151
161
|
// prefix so the chip body remains the canonical id, matching the WI/PRD
|
|
@@ -154,7 +164,7 @@ function _renderPrLink(pr, opts) {
|
|
|
154
164
|
? renderArtifactLink({ type: 'pr', id: pr.id, label: pr.id, title: (pr.title || '') + ' (' + (pr.status || 'active') + ')' })
|
|
155
165
|
: '<code>' + escHtml(pr.id) + '</code>';
|
|
156
166
|
return '<span style="font-size:' + size + ';margin-left:4px;display:inline-flex;align-items:center;gap:3px">' +
|
|
157
|
-
'<span title="' + escHtml(pr.status || 'active') + '">' + statusIcon + '</span>' + chip + '</span>';
|
|
167
|
+
'<span style="color:' + iconColor + '" title="' + escHtml(pr.status || 'active') + '">' + statusIcon + '</span>' + chip + '</span>';
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
function _renderPrdWorkItemIdBadge(workItemId, prdItemId, size, padding) {
|
|
@@ -621,7 +631,7 @@ function renderPrdProgress(prog) {
|
|
|
621
631
|
if (prs.length > 0) {
|
|
622
632
|
html += '<div style="font-size:var(--text-sm);font-weight:600;color:var(--blue);margin-bottom:4px">E2E Aggregate PRs</div>';
|
|
623
633
|
html += prs.map(pr => {
|
|
624
|
-
const statusColor =
|
|
634
|
+
const statusColor = _prStatusColor(pr.status);
|
|
625
635
|
// P-79b47b0c — render PR id as in-stack chip (was raw <a target="_blank">).
|
|
626
636
|
const prChip = (typeof renderArtifactLink === 'function')
|
|
627
637
|
? renderArtifactLink({ type: 'pr', id: pr.id, label: pr.id, title: pr.title || pr.id })
|
package/dashboard/js/settings.js
CHANGED
|
@@ -70,7 +70,7 @@ async function openSettings() {
|
|
|
70
70
|
return '<tr>' +
|
|
71
71
|
'<td style="font-weight:600">' + escHtml(a.emoji || '') + ' ' + escHtml(a.name || id) + '</td>' +
|
|
72
72
|
'<td><input data-agent="' + escHtml(id) + '" data-field="role" value="' + escHtml(a.role || '') + '" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-base)"></td>' +
|
|
73
|
-
'<td><input data-agent="' + escHtml(id) + '" data-field="
|
|
73
|
+
'<td><input data-agent="' + escHtml(id) + '" data-field="expertise" value="' + escHtml((a.expertise || []).join(', ')) + '" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-base)"></td>' +
|
|
74
74
|
'<td data-runtime-cli="' + escHtml(id) + '" style="min-width:110px">' +
|
|
75
75
|
// Initial loading placeholder — initRuntimeFleetUI() replaces this with a
|
|
76
76
|
// <select> populated from /api/runtimes once the registry resolves.
|
|
@@ -379,6 +379,7 @@ async function openSettings() {
|
|
|
379
379
|
settingsField('Worktree Root', 'set-worktreeRoot', e.worktreeRoot || '../worktrees', '', 'Relative or absolute path for git worktrees; on Windows prefer a short path like C:\\wt') +
|
|
380
380
|
settingsField('Assert-Clean Status Probe Timeout', 'set-assertCleanStatusTimeoutMs', e.assertCleanStatusTimeoutMs || 10000, 'ms', 'Timeout for the `git status --porcelain` preflight probe in assertCleanSharedWorktree. Raise this (e.g. 60000) on GVFS/Plastic-backed monorepos where sparse hydration runs longer than the 10s default. On timeout the engine quarantines the bad worktree and emits a RETRYABLE failure so the next dispatch starts fresh. Clamped 1000–120000ms.') +
|
|
381
381
|
settingsField('Orphan-holder scan timeout', 'set-orphanHolderScanTimeoutMs', e.orphanHolderScanTimeoutMs || 5000, 'ms', 'Cap on the cross-platform scan (PowerShell / /proc walk / lsof) that identifies the OS process holding a stuck worktree\'s cwd. Bumps up only matter on heavily-loaded hosts where the holder scan races the sweep tick. Clamped 1000–30000ms.') +
|
|
382
|
+
settingsField('Worktree-holder reap probe timeout', 'set-statusProbeKillTimeoutMs', e.statusProbeKillTimeoutMs || 12000, 'ms', 'Windows-only. Timeout for the pre-quarantine worktree-holder reap probe (legacy git.exe-cmdline sweep + PEB-CWD scan that kills an orphaned agent process tree whose CWD pins the worktree dir, causing EBUSY on rename AND `git worktree remove --force`). The old hardcoded 2000ms was the proximate cause of the reaper being a no-op under concurrent-agent load (PowerShell cold-start + process enumeration exceeded 2s → ETIMEDOUT → reaped nothing). ETIMEDOUT is always swallowed (reap nothing; quarantine still proceeds), so raising this only widens the success window. Clamped 2000–60000ms.') +
|
|
382
383
|
'</div>';
|
|
383
384
|
|
|
384
385
|
const paneCopilot =
|
|
@@ -998,6 +999,7 @@ async function saveSettings() {
|
|
|
998
999
|
ccUseWorkerPool: !!document.getElementById('set-ccUseWorkerPool')?.checked,
|
|
999
1000
|
autoReapOrphanWorktreeHolders: !!document.getElementById('set-autoReapOrphanWorktreeHolders')?.checked,
|
|
1000
1001
|
orphanHolderScanTimeoutMs: document.getElementById('set-orphanHolderScanTimeoutMs')?.value,
|
|
1002
|
+
statusProbeKillTimeoutMs: document.getElementById('set-statusProbeKillTimeoutMs')?.value,
|
|
1001
1003
|
adoPollEnabled: document.getElementById('set-adoPollEnabled').checked,
|
|
1002
1004
|
ghPollEnabled: document.getElementById('set-ghPollEnabled').checked,
|
|
1003
1005
|
pollingPaused: document.getElementById('set-pollingPaused').checked,
|
package/dashboard.js
CHANGED
|
@@ -10437,6 +10437,12 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
10437
10437
|
// 30s ceiling (the scan runs once per orphan-sweep tick and a slow
|
|
10438
10438
|
// scan must not block the rest of the sweep for minutes).
|
|
10439
10439
|
orphanHolderScanTimeoutMs: [1000, 30000],
|
|
10440
|
+
// W-mqila0t5 — Windows worktree-holder reap probe timeout. 2s floor
|
|
10441
|
+
// (PowerShell cold-start + PEB enumeration needs headroom; the old
|
|
10442
|
+
// 2000ms hardcode was the no-op bug); 60s ceiling (the reap runs once
|
|
10443
|
+
// on the quarantine path and ETIMEDOUT is swallowed, so a long probe
|
|
10444
|
+
// never wedges dispatch — it just falls through to force-remove).
|
|
10445
|
+
statusProbeKillTimeoutMs: [2000, 60000],
|
|
10440
10446
|
idleAlertMinutes: [1], shutdownTimeout: [30000], restartGracePeriod: [60000],
|
|
10441
10447
|
meetingRoundTimeout: [60000],
|
|
10442
10448
|
// W-mq066js7000fff1f-c (Gap B/C): steering safety-net knobs.
|
|
@@ -10678,7 +10684,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
10678
10684
|
for (const [id, updates] of Object.entries(body.agents)) {
|
|
10679
10685
|
if (!config.agents[id]) continue;
|
|
10680
10686
|
if (updates.role !== undefined) config.agents[id].role = String(updates.role);
|
|
10681
|
-
|
|
10687
|
+
// Descriptive capability tags. Field renamed `skills` -> `expertise`
|
|
10688
|
+
// (issue: name collision with executable runtime skills). Accept the
|
|
10689
|
+
// legacy `skills` key from older clients and persist as `expertise`.
|
|
10690
|
+
const expertiseUpdate = updates.expertise !== undefined ? updates.expertise
|
|
10691
|
+
: (updates.skills !== undefined ? updates.skills : undefined);
|
|
10692
|
+
if (expertiseUpdate !== undefined) {
|
|
10693
|
+
config.agents[id].expertise = Array.isArray(expertiseUpdate) ? expertiseUpdate : String(expertiseUpdate).split(',').map(s => s.trim()).filter(Boolean);
|
|
10694
|
+
delete config.agents[id].skills;
|
|
10695
|
+
}
|
|
10682
10696
|
if (updates.monthlyBudgetUsd !== undefined) {
|
|
10683
10697
|
const val = updates.monthlyBudgetUsd === '' || updates.monthlyBudgetUsd === null ? undefined : Number(updates.monthlyBudgetUsd);
|
|
10684
10698
|
if (val === undefined || isNaN(val)) delete config.agents[id].monthlyBudgetUsd;
|
package/docs/README.md
CHANGED
|
@@ -15,7 +15,8 @@ Architecture, design proposals, and lifecycle references for people working on t
|
|
|
15
15
|
|
|
16
16
|
- [branch-derivation.md](branch-derivation.md) — Engine-side branch fallback (`work/<wi-id>`) vs. agent-authored long form, the structured-vs-loose PR-pointer extractors, and the canonical PR-fix duplication incident.
|
|
17
17
|
- [command-center.md](command-center.md) — Command Center (CC) chat panel: persistent Sonnet sessions, `--resume` semantics, system-prompt invalidation, and per-tab session storage.
|
|
18
|
-
- [specs/agent-
|
|
18
|
+
- [specs/agent-rename.md](specs/agent-rename.md) — Design spec for **agent rename & name decoupling** — the prerequisite for the Role/Agent Library. Formalizes the stable opaque agent id, makes charters name-agnostic, and adds `POST /api/agents/rename` (display name + emoji). Feature-flagged (`agent-rename`) and fully backward-compatible.
|
|
19
|
+
- [specs/agent-configurability.md](specs/agent-configurability.md) — Design spec for the user-configurable Role / Agent Library, feature-flagged behind `agent-library` and fully backward-compatible. Builds on `agent-rename.md`. Role = the reusable durable charter; agent = a thin instance (identity + variable expertise + role pointer). Phase 0 renames `skills`→`expertise`; Phase 1 exposes/edits role charters + per-agent expertise; Phase 2 adds role/agent CRUD with builtin delete-protection.
|
|
19
20
|
- [completion-reports.md](completion-reports.md) — Canonical schema for the per-spawn completion JSON: trust nonce, `failure_class` enum, `noop` semantics, `retryable` / `needs_rerun` shape, and the artifacts array.
|
|
20
21
|
- [constants.md](constants.md) — Cross-cutting status / type / condition constants (`WI_STATUS`, `WORK_TYPE`, `PR_STATUS`, `WATCH_CONDITION`, …) and the no-magic-strings invariant.
|
|
21
22
|
- [constellation-bridge.md](constellation-bridge.md) — Read-only cross-repo bridge: `engine.constellationBridge.enabled` flag, marker-file contract, and the `minions bridge` subcommand for local debugging.
|
|
@@ -33,6 +34,7 @@ Architecture, design proposals, and lifecycle references for people working on t
|
|
|
33
34
|
- [keep-processes.md](keep-processes.md) — `meta.keep_processes` sidecar contract: when to use it vs managed-spawn, sidecar schema, caps, and the [`engine/keep-process-sweep.js`](../engine/keep-process-sweep.js) lifecycle.
|
|
34
35
|
- [live-checkout-mode.md](live-checkout-mode.md) — Per-project opt-in `checkoutMode: 'live'`: skips `git worktree add` and dispatches in-place inside `project.localPath` for `repo`-managed trees, submodule-heavy repos, deep Windows paths, and native build state. Includes the refuse-on-dirty contract and the per-project mutating-concurrency cap of 1.
|
|
35
36
|
- [managed-spawn.md](managed-spawn.md) — Engine-owned long-running services (managed-spawn primitive): sidecar schema, healthcheck examples, lifecycle, dashboard API, and the WI 1 (build) → WI 2 (test) chained-validation pattern.
|
|
37
|
+
- [named-agents.md](named-agents.md) — The named-agent roster (Ripley / Dallas / Lambert / Rebecca / Ralph): per-agent `config.json` shape, the per-agent → `engine.*` → runtime resolution chain, how `routing.md` (not the `role`/`skills` metadata) drives dispatch, descriptive vs runtime skills, and per-agent memory files.
|
|
36
38
|
- [plan-lifecycle.md](plan-lifecycle.md) — Full plan pipeline from `/plan` through PRD materialization, dispatch with dependency gating, verify task, and human archive.
|
|
37
39
|
- [pr-auto-fix-dispatch.md](pr-auto-fix-dispatch.md) — Short reference table mapping each PR auto-fix / review dispatch site in `engine.js#discoverFromPrs` to its gate flag, plus the `pollingPaused` / `autoFixPaused` master kill-switches and the per-provider polling gates.
|
|
38
40
|
- [pr-comment-followup.md](pr-comment-followup.md) — PR-comment follow-up dispatch contract: fix/review agents may spin off a new WI via `POST /api/work-items` with `meta.pr_followup` instead of broadening the current PR or rebutting the comment.
|
package/docs/auto-discovery.md
CHANGED
|
@@ -19,7 +19,7 @@ tick()
|
|
|
19
19
|
2.5 runCleanup() Periodic cleanup (every 60 ticks ≈ 10min)
|
|
20
20
|
2.52 sweepKeepProcesses() keep_processes TTL/dead-PID sweep (every 180 ticks)
|
|
21
21
|
2.53 sweepManagedSpawn() managed_spawn TTL/dead-PID/log-rotate sweep (every 180 ticks)
|
|
22
|
-
2.54 pruneWorktreesPeriodic() Periodic worktree GC: in-root + out-of-root git registry sweep (every worktreePruneIntervalTicks ≈ 30 ticks; catches Windows EPERM/EBUSY stragglers
|
|
22
|
+
2.54 pruneWorktreesPeriodic() Periodic worktree GC: in-root + out-of-root git registry sweep + locked-`initializing` missing-dir reclaim (every worktreePruneIntervalTicks ≈ 30 ticks; catches Windows EPERM/EBUSY stragglers, `git worktree list` entries outside worktreeRoot, and branch-bricking locked missing-dir entries plain prune can't reap)
|
|
23
23
|
2.55 checkWatches() Persistent watch jobs (every 18 tick-equivalents)
|
|
24
24
|
2.6 pollPrStatus() Poll ADO + GitHub for build, review, merge status (wall-clock cadence from prPollStatusEvery × tickInterval, default ≈ 12min)
|
|
25
25
|
processPendingRebases() Run any rebase work queued from the previous tick
|
package/docs/deprecated.json
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"id": "agent-config-skills-field",
|
|
4
|
+
"description": "Legacy per-agent descriptive-metadata array `agents.<id>.skills` in config.json, renamed to `agents.<id>.expertise` to remove the name collision with executable runtime/harness skills (SKILL.md). The field is metadata only (capability tags like `architecture`, `bug-fixes`); nothing in the dispatch path reads it for behavior. A read-compat shim honors the old key so operator configs still carrying `skills` (and no `expertise`) keep working.",
|
|
5
|
+
"code": [
|
|
6
|
+
{ "file": "engine/playbook.js", "lines": "950", "note": "buildSystemPrompt reads `agent.expertise ?? agent.skills ?? []` for the `Expertise:` identity line" },
|
|
7
|
+
{ "file": "engine/lifecycle.js", "lines": "4620-4621", "note": "pickReReviewAgentHints reads `agent.expertise` with an `agent.skills` array fallback" },
|
|
8
|
+
{ "file": "engine/queries.js", "lines": "731", "note": "getAgents normalizes `expertise: a.expertise ?? a.skills ?? []` so the dashboard/settings UI always receives `expertise`" },
|
|
9
|
+
{ "file": "dashboard.js", "lines": "10708-10716", "note": "settings POST accepts a legacy `updates.skills` key, persists as `config.agents[id].expertise`, and deletes the old `skills` key" }
|
|
10
|
+
],
|
|
11
|
+
"removalGate": "Telemetry / a config sweep across all known engines must show no persisted `config.agents.<id>.skills` key (only `expertise`) for >=30 consecutive days, confirming every operator config has been re-saved through the dashboard (which drops the legacy key) or hand-migrated.",
|
|
12
|
+
"targetRemovalDate": "2026-09-17",
|
|
13
|
+
"notes": "Safe to remove on or after 2026-09-17 (~3 release windows) once the removal gate clears. Removal scope: drop the `?? agent.skills` / `?? a.skills` fallbacks in engine/playbook.js:950, engine/lifecycle.js:4620-4621, and engine/queries.js:731; drop the `updates.skills` legacy branch + `delete config.agents[id].skills` in dashboard.js:10708-10716; and update docs/named-agents.md to stop mentioning the legacy key. Does NOT touch the unrelated executable-skills system (queries.js harnesses, runtime adapters, dashboard renderSkills, SKILL.md tooling)."
|
|
14
|
+
},
|
|
2
15
|
{
|
|
3
16
|
"id": "qa-json-sidecars",
|
|
4
17
|
"location": "engine/small-state-store.js _mirrorQaRunsJson + _mirrorQaSessionsJson; engine/shared.js _qaMutator (mirror call); dashboard/js/settings.js set-qaDualWriteJson toggle",
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Named Agents — configuration, roles, and skills
|
|
2
|
+
|
|
3
|
+
Minions dispatches a small roster of **named agents** — personas like Ripley or Dallas
|
|
4
|
+
that own work items, open PRs, and review each other's changes. This doc explains who
|
|
5
|
+
they are, the full per-agent config shape, how work actually routes to one of them, what
|
|
6
|
+
the descriptive `expertise` tags and the real executable `skills` are (and why they used to
|
|
7
|
+
share a name), and how each agent accrues personal memory across dispatches.
|
|
8
|
+
|
|
9
|
+
> Orientation only — the load-bearing contracts live in code. Cross-references:
|
|
10
|
+
> [`CLAUDE.md`](../CLAUDE.md), [`.github/copilot-instructions.md`](../.github/copilot-instructions.md),
|
|
11
|
+
> [`docs/runtime-adapters.md`](runtime-adapters.md), [`docs/workspace-manifests.md`](workspace-manifests.md),
|
|
12
|
+
> [`docs/team-memory.md`](team-memory.md), and [`docs/skills.md`](skills.md).
|
|
13
|
+
|
|
14
|
+
## 1. The roster
|
|
15
|
+
|
|
16
|
+
The five named agents are defined under `agents` in `config.json` (gitignored; the
|
|
17
|
+
shipped `config.template.json` carries no agents — operators populate the roster locally).
|
|
18
|
+
Each entry is keyed by a lowercase agent id (`ripley`, `dallas`, …).
|
|
19
|
+
|
|
20
|
+
| Id | Name | Emoji | Role | `expertise` (descriptive) | `cli` |
|
|
21
|
+
|----|------|-------|------|------------------------|-------|
|
|
22
|
+
| `ripley` | Ripley | 🏗️ | Lead / Explorer | `architecture`, `codebase-exploration`, `design-review` | `copilot` |
|
|
23
|
+
| `dallas` | Dallas | 🔧 | Engineer | `implementation`, `typescript`, `docker`, `testing` | `copilot` |
|
|
24
|
+
| `lambert` | Lambert | 📊 | Analyst | `gap-analysis`, `requirements`, `documentation` | `copilot` |
|
|
25
|
+
| `rebecca` | Rebecca | 🧠 | Architect | `system-design`, `api-design`, `scalability`, `implementation` | `copilot` |
|
|
26
|
+
| `ralph` | Ralph | ⚙️ | Engineer | `implementation`, `bug-fixes`, `testing`, `scaffolding` | `copilot` |
|
|
27
|
+
|
|
28
|
+
The `name`, `emoji`, `role`, and `expertise` fields are **human-facing metadata** — they
|
|
29
|
+
label the agent in the dashboard and document intent. They do **not** drive dispatch (see
|
|
30
|
+
§3) and the `expertise` array is not executable (see §4).
|
|
31
|
+
|
|
32
|
+
## 2. How an agent is configured
|
|
33
|
+
|
|
34
|
+
The full per-agent config shape supported under `config.agents.<id>`:
|
|
35
|
+
|
|
36
|
+
| Field | Type | Meaning |
|
|
37
|
+
|-------|------|---------|
|
|
38
|
+
| `name` | string | Display name (e.g. `"Ripley"`). |
|
|
39
|
+
| `role` | string | Human-facing role label (e.g. `"Lead / Explorer"`). Metadata only. |
|
|
40
|
+
| `emoji` | string | Dashboard glyph. |
|
|
41
|
+
| `expertise` | string[] | Descriptive capability tags. Metadata only — see §4. Legacy configs that still use the old key name `skills` are read transparently. |
|
|
42
|
+
| `cli` | string | Runtime CLI for this agent's spawns: `copilot`, `claude`, or `codex`. |
|
|
43
|
+
| `model` | string | Per-agent model override (omitted → runtime picks its own default). |
|
|
44
|
+
| `maxBudgetUsd` | number | Per-spawn USD cap. **`0` is a valid cap** (read-only / dry-run agents), not "unlimited". |
|
|
45
|
+
| `bareMode` | boolean | Run Claude in `--bare` mode (Claude-runtime knob). |
|
|
46
|
+
| `hermeticHarness` | boolean | Strip user-scope skill/command/MCP roots from the worktree; `--add-dir` resolves to `[minionsDir]` only. |
|
|
47
|
+
| `workspace_manifest` | object | Optional permission scoping — see below. |
|
|
48
|
+
|
|
49
|
+
### Three-tier resolution chain
|
|
50
|
+
|
|
51
|
+
Most knobs resolve **per-agent override → `engine.*` fleet default → runtime default**.
|
|
52
|
+
The resolver helpers live in [`engine/shared.js`](../engine/shared.js):
|
|
53
|
+
|
|
54
|
+
| Helper | Chain |
|
|
55
|
+
|--------|-------|
|
|
56
|
+
| `resolveAgentCli(agent, engine)` | `agent.cli` → `engine.defaultCli` → `ENGINE_DEFAULTS.defaultCli` (`'copilot'`). |
|
|
57
|
+
| `resolveAgentModel(agent, engine)` | `agent.model` → `engine.defaultModel` → `undefined` (adapter omits `--model`, CLI uses its own default). |
|
|
58
|
+
| `resolveAgentMaxBudget(agent, engine)` | `agent.maxBudgetUsd` → `engine.maxBudgetUsd` → `undefined` (no cap). Nullish-coalesced so `0` is honored. |
|
|
59
|
+
| `resolveAgentBareMode(agent, engine)` | `agent.bareMode` → `engine.claudeBareMode` → `false`. Strict null check so a per-agent `false` overrides an engine `true`. |
|
|
60
|
+
| `resolveAgentHermeticHarness(agent, engine)` | `agent.hermeticHarness` → `engine.hermeticHarness` → `false`. Same strict-override semantics. |
|
|
61
|
+
|
|
62
|
+
Empty string / `null` / `undefined` all count as "unset" for the string knobs, so an agent
|
|
63
|
+
inherits the fleet default rather than blanking the value.
|
|
64
|
+
|
|
65
|
+
### Agent vs Command Center independence
|
|
66
|
+
|
|
67
|
+
The Command Center (CC) / doc-chat path is a fleet-wide singleton with **no notion of
|
|
68
|
+
"which agent"**, so it resolves through separate helpers: `resolveCcCli(engine)`
|
|
69
|
+
(`engine.ccCli` → `engine.defaultCli` → fallback) and `resolveCcModel(engine)`
|
|
70
|
+
(`engine.ccModel` → `engine.defaultModel` → `undefined`). These **do not cross over**:
|
|
71
|
+
`ccCli` / `ccModel` never affect a named-agent spawn, and `agent.cli` / `agent.model` never
|
|
72
|
+
affect CC. Both paths share the `engine.defaultCli` / `engine.defaultModel` fleet defaults,
|
|
73
|
+
but only as the middle tier.
|
|
74
|
+
|
|
75
|
+
### Workspace manifest (optional)
|
|
76
|
+
|
|
77
|
+
`agents.<id>.workspace_manifest` declares declarative permission scoping. Shape and
|
|
78
|
+
defaults (`WORKSPACE_MANIFEST_DEFAULTS` in `engine/shared.js`):
|
|
79
|
+
|
|
80
|
+
| Key | Default | Effect |
|
|
81
|
+
|-----|---------|--------|
|
|
82
|
+
| `allowed_tools` | `null` (permissive) | Whitelist of tool names; merged into the runtime `--allowedTools` flag. `[]` = deny-all. |
|
|
83
|
+
| `allowed_repos` | `null` (permissive) | Repo allow-list; enforced at dispatch time. `[]` = deny-all. |
|
|
84
|
+
| `allowed_external_urls` | `null` (permissive) | URL allow-list (supports `*.example.com` wildcards). |
|
|
85
|
+
| `memory_scope` | `'shared'` | One of `private`, `shared`, `read-only-shared`. |
|
|
86
|
+
|
|
87
|
+
Enforcement helpers (also in `engine/shared.js`): `validateWorkspaceManifest`,
|
|
88
|
+
`resolveAgentManifest`, `agentCanUseRepo`, `agentCanUseTool`, `agentCanFetchUrl`,
|
|
89
|
+
`agentMemoryScope`, `mergeManifestAllowedTools`. Today the engine actively enforces (a) the
|
|
90
|
+
**repo gate** at dispatch time in `engine.js spawnAgent`
|
|
91
|
+
(`FAILURE_CLASS.WORKSPACE_MANIFEST_REPO`, non-retryable) and (b) the **tool merge** into the
|
|
92
|
+
runtime `--allowedTools` flag at spawn time. Defaults are permissive — an agent with no
|
|
93
|
+
manifest is unaffected. Full schema and rollout guidance:
|
|
94
|
+
[`docs/workspace-manifests.md`](workspace-manifests.md).
|
|
95
|
+
|
|
96
|
+
## 3. How roles are defined / how work routes to an agent
|
|
97
|
+
|
|
98
|
+
The `role` and `expertise` config fields are **descriptive metadata**. The actual dispatch
|
|
99
|
+
decision is **data-driven by [`routing.md`](../routing.md)**, which `engine.js` parses — so
|
|
100
|
+
keep that file's table format exact.
|
|
101
|
+
|
|
102
|
+
Routing maps a `work_type` to a `preferred` agent and a `fallback`:
|
|
103
|
+
|
|
104
|
+
| Work Type | Preferred | Fallback |
|
|
105
|
+
|-----------|-----------|----------|
|
|
106
|
+
| implement | dallas | ralph |
|
|
107
|
+
| implement:large | rebecca | dallas |
|
|
108
|
+
| review | ripley | lambert |
|
|
109
|
+
| fix | `_author_` | `_any_` |
|
|
110
|
+
| plan | ripley | rebecca |
|
|
111
|
+
| plan-to-prd | lambert | rebecca |
|
|
112
|
+
| explore | ripley | rebecca |
|
|
113
|
+
| test | dallas | ralph |
|
|
114
|
+
| ask | ripley | rebecca |
|
|
115
|
+
| verify | dallas | ralph |
|
|
116
|
+
| decompose | ripley | rebecca |
|
|
117
|
+
| meeting | ripley | lambert |
|
|
118
|
+
| docs | lambert | `_any_` |
|
|
119
|
+
| setup | dallas | `_any_` |
|
|
120
|
+
| qa-validate / qa-session-* | dallas | ralph |
|
|
121
|
+
|
|
122
|
+
**Sentinels.** `_author_` routes a `fix` (review-feedback) item back to the PR author for
|
|
123
|
+
context; `_any_` routes to any available idle agent (lowest error rate first).
|
|
124
|
+
`implement:large` is selected for items with `estimated_complexity: "large"`. If both
|
|
125
|
+
preferred and fallback are busy, the engine falls back to any idle agent.
|
|
126
|
+
|
|
127
|
+
**Dispatch rules** (from `routing.md`):
|
|
128
|
+
|
|
129
|
+
- **Eager by default** — the engine spawns every agent that can start work, not one at a time.
|
|
130
|
+
- **No self-review** — auto review dispatch always picks a non-author. When no non-author
|
|
131
|
+
reviewer is idle, the dispatch waits in `dispatch.pending` with
|
|
132
|
+
`_pendingReason: 'no_non_author_reviewer'` and promotes automatically once one frees up.
|
|
133
|
+
An explicit operator dispatch (`agent: <author>`) is honored as an override but logged as
|
|
134
|
+
a warning.
|
|
135
|
+
- **Routing selects an owner; it does not narrow the task.** The assigned agent behaves as
|
|
136
|
+
if the user typed the same task into a CLI directly — Minions adds only safety, status,
|
|
137
|
+
and review guardrails.
|
|
138
|
+
|
|
139
|
+
### Per-agent retry reassignment
|
|
140
|
+
|
|
141
|
+
Total retries are capped by `ENGINE_DEFAULTS.maxRetries` (default 3). Separately, each work
|
|
142
|
+
item tracks per-agent attempts in `_retriesByAgent: { agentId: count }`. When the **same**
|
|
143
|
+
agent fails the **same** item `maxRetriesPerAgent` times (default 2; overridable via
|
|
144
|
+
`engine.maxRetriesPerAgent` and the dashboard Settings field), discovery force-reassigns to
|
|
145
|
+
a different eligible agent via `resolveAgent(workType, config, { excludeAgent })`. If no
|
|
146
|
+
alternate is available it stays with the same agent and dedups an engine inbox note. Setting
|
|
147
|
+
`agentLock: true` (or `hardAgent: true`) hard-pins the agent and bypasses reassignment —
|
|
148
|
+
operator intent wins. The counter is cleared on successful completion alongside
|
|
149
|
+
`_retryCount`. Helpers: `bumpAgentRetryCount`, `getAgentRetryCount`,
|
|
150
|
+
`resolveMaxRetriesPerAgent` in `engine/shared.js`.
|
|
151
|
+
|
|
152
|
+
## 4. How skills are defined
|
|
153
|
+
|
|
154
|
+
Historically "skills" meant two unrelated things in Minions. The descriptive per-agent
|
|
155
|
+
metadata array was renamed `skills` → `expertise` to remove that collision, so the only
|
|
156
|
+
"skills" left are the real executable ones. The two concepts to keep separate:
|
|
157
|
+
|
|
158
|
+
**(a) The descriptive `expertise` array in config** (`agents.<id>.expertise`). A list of
|
|
159
|
+
capability tags like `architecture` or `bug-fixes`. This is **metadata only** — labels for
|
|
160
|
+
humans, not executable behavior. Nothing in the dispatch path reads it. (This field was
|
|
161
|
+
formerly named `skills`; legacy configs that still use that key are read transparently via
|
|
162
|
+
an `expertise ?? skills` fallback — see [`docs/deprecated.json`](deprecated.json).)
|
|
163
|
+
|
|
164
|
+
**(b) Real runtime skills** — `SKILL.md` files the runtime CLI actually loads. These are
|
|
165
|
+
auto-extracted from agent output: when an agent emits a fenced ```` ```skill ```` block
|
|
166
|
+
(with YAML frontmatter carrying at least a `name`), `extractSkillsFromOutput` in
|
|
167
|
+
[`engine/lifecycle.js`](../engine/lifecycle.js) writes it out. The engine also scans
|
|
168
|
+
`notes/inbox/` notes for skill blocks, since agents often write them there instead of stdout.
|
|
169
|
+
|
|
170
|
+
- **`scope: minions`** (the default when `scope` is omitted) → written to the runtime's
|
|
171
|
+
**personal** skill directory as `<name>/SKILL.md`, so it becomes a user-level skill
|
|
172
|
+
available in normal runtime windows too.
|
|
173
|
+
- **`scope: project`** (with a `project:` field) → the engine queues a low-priority
|
|
174
|
+
`implement` work item to **PR the skill into the project repo's** skill directory rather
|
|
175
|
+
than writing it directly.
|
|
176
|
+
|
|
177
|
+
Near-duplicate names are flagged to `engine/skills-flagged.json` and skipped (no existing
|
|
178
|
+
skill is deleted or renamed). See [`docs/skills.md`](skills.md) for the block format.
|
|
179
|
+
|
|
180
|
+
**Runtime differences.** The destination directory is runtime-specific
|
|
181
|
+
(`getSkillRoots` / `getSkillWriteTargets` in the runtime adapters under
|
|
182
|
+
[`engine/runtimes/`](../engine/runtimes/)):
|
|
183
|
+
|
|
184
|
+
| Runtime | Personal write target | Project write target | Also reads |
|
|
185
|
+
|---------|-----------------------|----------------------|------------|
|
|
186
|
+
| Claude (`claude.js`) | `~/.claude/skills/` | `<repo>/.claude/skills/` | `~/.agents/skills/` |
|
|
187
|
+
| Copilot (`copilot.js`) | `~/.copilot/skills/` | `<repo>/.github/skills/` | `~/.copilot`, `~/.agents/skills/` |
|
|
188
|
+
|
|
189
|
+
The capability matrix for each runtime is in [`docs/runtime-adapters.md`](runtime-adapters.md).
|
|
190
|
+
|
|
191
|
+
## 5. Per-agent memory & charters
|
|
192
|
+
|
|
193
|
+
Each named agent has a personal memory file at
|
|
194
|
+
`knowledge/agents/<agentId>.md` (e.g. `knowledge/agents/dallas.md`) — a personal notebook
|
|
195
|
+
injected into that agent's prompt on every dispatch. The format convention is documented in
|
|
196
|
+
[`knowledge/agents/README.md`](../knowledge/agents/README.md).
|
|
197
|
+
|
|
198
|
+
**Written exclusively by the consolidation pipeline.** `appendToAgentMemory` in
|
|
199
|
+
[`engine/consolidation.js`](../engine/consolidation.js) routes each `notes/inbox/` finding
|
|
200
|
+
to its author's file. Authorship comes from the inbox note's YAML frontmatter `agent:`
|
|
201
|
+
field, with the **filename prefix** (`<agent>-…md`) as fallback (`extractInboxAgent`). Key
|
|
202
|
+
properties:
|
|
203
|
+
|
|
204
|
+
- **Append-only** — agents must not edit their own memory files directly; write to
|
|
205
|
+
`notes/inbox/` and let the sweep route it.
|
|
206
|
+
- **~25 KB cap** (`AGENT_MEMORY_BUDGET_BYTES`) — oldest sections pruned at section
|
|
207
|
+
boundaries to stay under budget.
|
|
208
|
+
- **Only configured `config.agents` keys get a file** — the agent must be in the known-team
|
|
209
|
+
set; `temp-*` ids are skipped. This is a strict superset of broadcast consolidation: the
|
|
210
|
+
shared `notes.md` digest still happens; per-agent routing is *in addition*.
|
|
211
|
+
|
|
212
|
+
**Prompt injection order.** [`engine/playbook.js`](../engine/playbook.js) injects, in this
|
|
213
|
+
order, `pinned.md` → `notes.md` → `knowledge/agents/<agentId>.md` (when present). Missing
|
|
214
|
+
files are silently skipped. The per-agent file is fenced as `<UNTRUSTED-INPUT>` and capped
|
|
215
|
+
to the notes prompt budget before injection. The companion read-side reference is
|
|
216
|
+
[`docs/team-memory.md`](team-memory.md).
|