@yemi33/minions 0.1.2083 → 0.1.2084

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/docs/README.md CHANGED
@@ -15,10 +15,13 @@ Architecture, design proposals, and lifecycle references for people working on t
15
15
 
16
16
  - [command-center.md](command-center.md) — Command Center (CC) chat panel: persistent Sonnet sessions, `--resume` semantics, system-prompt invalidation, and per-tab session storage.
17
17
  - [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.
18
+ - [constants.md](constants.md) — Cross-cutting status / type / condition constants (`WI_STATUS`, `WORK_TYPE`, `PR_STATUS`, `WATCH_CONDITION`, …) and the no-magic-strings invariant.
18
19
  - [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.
20
+ - [cooldown-merge-semantics.md](cooldown-merge-semantics.md) — Scoping deliverable defining merge semantics for `saveCooldowns` (longer-of TTL merge, key-level upserts, gitignored on-disk format).
19
21
  - [copilot-cli-schema.md](copilot-cli-schema.md) — Behavior and schema reference for the GitHub Copilot CLI adapter (capability flags, stdin vs `-p`, model discovery, effort levels).
20
- - [design-state-storage.md](design-state-storage.md) — Design proposal evaluating five database options for replacing Minions' file-based JSON state; recommends `node:sqlite` as the medium-term target.
22
+ - [design-state-storage.md](design-state-storage.md) — Design proposal evaluating five database options for replacing Minions' file-based JSON state; recommends `node:sqlite` as the medium-term target (accepted; implementation tracked in CHANGELOG.md Phases 0–7).
21
23
  - [kb-sweep.md](kb-sweep.md) — Knowledge-base consolidation sweep (hash dedup → LLM batch dedup/reclassify → per-entry compress) and the detached runner that keeps it alive across `minions restart`.
24
+ - [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.
22
25
  - [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.
23
26
  - [plan-lifecycle.md](plan-lifecycle.md) — Full plan pipeline from `/plan` through PRD materialization, dispatch with dependency gating, verify task, and human archive.
24
27
  - [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.
@@ -4,4 +4,33 @@ The Command Center (CC) is the dashboard's conversational chat panel. It opens f
4
4
 
5
5
  CC is intentionally a thin wrapper around the runtime CLI: state changes happen via `Bash`-tool `curl` calls to the dashboard's own REST API, not via parsed delimiter blocks. The end-to-end flow is `dashboard/js/command-center.js` `_ccDoSend()` → `POST /api/command-center` (or `/api/command-center/stream`) in `dashboard.js` (`handleCommandCenter`) → `engine/llm.js` `callLLM({ direct: true })` → claude/copilot CLI session persisted in `engine/cc-sessions.json`. Per-turn API mutations are correlated via the `X-CC-Turn-Id` header and surfaced as standalone `role='action'` chips rendered outside the assistant bubble (`_ccActionResultLine` + `addMsg('action', ...)`).
6
6
 
7
- For canonical detail (system prompt, session lifecycle, turn-ID surfacing pipeline, doc-chat integration, and CC API contract), read [`CLAUDE.md`](../CLAUDE.md) — see the **CC API Contract** and **Sessions** sections — and the source in [`dashboard/js/command-center.js`](../dashboard/js/command-center.js), [`dashboard.js`](../dashboard.js) (`handleCommandCenter`), and [`prompts/cc-system.md`](../prompts/cc-system.md). This pointer file exists so the docs index has an entry for "Command Center"; the implementation details are deliberately not duplicated here because they drift.
7
+ For canonical detail (system prompt, session lifecycle, turn-ID surfacing pipeline, doc-chat integration, and CC API contract), read [`CLAUDE.md`](../CLAUDE.md) — see the **CC API Contract** and **Sessions** sections — and the source in [`dashboard/js/command-center.js`](../dashboard/js/command-center.js), [`dashboard.js`](../dashboard.js) (`handleCommandCenter`), and [`prompts/cc-system.md`](../prompts/cc-system.md).
8
+
9
+ ## Error surfacing contract (W-mpmwxni2000c25c7-d)
10
+
11
+ Both CC and Doc-Chat emit a typed error envelope so the dashboard can render a red `.cc-error` bubble (role=alert) with a Retry button and stop the spinner immediately. Errors arrive in two shapes:
12
+
13
+ 1. **SSE mid-stream** (most common). `writeCcEvent` / `writeDocEvent` write the wire as `event: error\ndata: {…envelope…}\n\n` so consumers using `addEventListener('error', …)` see them as named events. The JSON payload still carries `type: 'error'` for clients that only read the `data:` line, so both parser strategies keep working.
14
+ 2. **Non-2xx POST response** (pre-stream — `readBody` guard, prototype-pollution rejection, etc.). The body is the same envelope as JSON; the dashboard's `if (!res.ok)` branch parses it and stashes it on the thrown Error as `_ccErrorEnvelope`, then renders the same red bubble.
15
+
16
+ Canonical envelope (`_buildCcErrorEnvelope` in `dashboard.js`):
17
+
18
+ ```json
19
+ { "type": "error",
20
+ "message": "Human-readable cause + remediation hint",
21
+ "code": "model-unavailable | auth-failure | context-limit | budget-exceeded | crash | cc-turn-timeout | worker-spawn-failed | acp-handshake-failed | worker-died",
22
+ "retryable": false,
23
+ "availableModels": ["gpt-4o", "gpt-5.4", "..."],
24
+ "runtime": "copilot"
25
+ }
26
+ ```
27
+
28
+ `code` is clamped to the allowlist (`CC_ERROR_CODES` constant); unknown codes collapse to `crash`. `retryable: true` is informational — there is **no auto-retry**; the dashboard always offers a manual Retry button instead. Auto-retrying these errors is a footgun because most are operator-fix categories (auth, budget, missing model) where re-spawning makes no progress.
29
+
30
+ **Watchdog (`engine.ccTurnTimeoutMs`, default 5 min, clamped 10s–1h).** Each turn arms a `setTimeout` that fires `event: error` with `code: 'cc-turn-timeout'`, aborts the in-flight LLM call, and ends the stream when no terminal event (`done`/`error`) arrives in time. Independent of `CC_CALL_TIMEOUT_MS` (the outer 1h hard ceiling); the watchdog is the *visible-to-user* no-progress cap. Surfaced in Settings → CC overrides.
31
+
32
+ **No auto-retry policy.** The backend never re-spawns the LLM after an error envelope. The client never silently resends the user's turn. Retry is a single-click manual action — guards against silent budget burn on `budget-exceeded`, infinite loops on `auth-failure`, and accidental re-charges on `context-limit`. The 429 + reconnect paths (rate-limited fetch retry, SSE reconnect-after-disconnect) remain — those are transport-level, not error-envelope-level.
33
+
34
+ ## Per-turn surfacing pipeline
35
+
36
+ CC handler generates `ccTurnId = 'cct-' + shared.uid()` per request; injected into sysprompt AND prompt body via `_ccTurnHeaderPart(turnId)` (load-bearing: on resumed sessions `engine/llm.js` skips re-sending the sysprompt, so without body injection CC keeps the stale turn ID). Handler reads via `_readCcTurnIdHeader(req)` and calls `_recordCcTurnCreation(turnId, ...)` on success. End-of-turn: `_buildSyntheticActionResultsForTurn` produces synthetic `{action, result}` pairs (`_serverExecuted: true`). Client renders as standalone `role='action'` messages outside the assistant bubble. TTL: 5 min. Endpoints wired: `/api/work-items`, `/api/notes`, `/api/plan`, `/api/knowledge`, `/api/watches`.
@@ -0,0 +1,32 @@
1
+ # Constants — No Magic Strings
2
+
3
+ All cross-cutting status / type / condition values are defined in [`engine/shared.js`](../engine/shared.js). Engine and dashboard code **never** compares against raw strings; tests enforce.
4
+
5
+ ```js
6
+ WI_STATUS = { PENDING, DISPATCHED, DONE, FAILED, PAUSED, QUEUED, DECOMPOSED, CANCELLED }
7
+ DONE_STATUSES = Set([WI_STATUS.DONE, 'in-pr', 'implemented', 'complete']) // legacy aliases on read only
8
+ WORK_TYPE = { IMPLEMENT, IMPLEMENT_LARGE, FIX, REVIEW, VERIFY, PLAN, PLAN_TO_PRD,
9
+ DECOMPOSE, MEETING, EXPLORE, ASK, TEST, DOCS, SETUP }
10
+ PLAN_STATUS = { ACTIVE, AWAITING_APPROVAL, APPROVED, PAUSED, REJECTED, COMPLETED, REVISION_REQUESTED }
11
+ PRD_ITEM_STATUS = { MISSING, UPDATED, DONE }; PRD_MATERIALIZABLE = Set([MISSING, UPDATED])
12
+ PR_STATUS = { ACTIVE, MERGED, ABANDONED, CLOSED, LINKED }; PR_POLLABLE_STATUSES = Set([ACTIVE, LINKED])
13
+ DISPATCH_RESULT = { SUCCESS, ERROR, TIMEOUT }
14
+ WATCH_STATUS = { ACTIVE, PAUSED, TRIGGERED, EXPIRED }
15
+ WATCH_CONDITION = { MERGED, BUILD_FAIL, BUILD_PASS, COMPLETED, FAILED, STATUS_CHANGE, ANY, NEW_COMMENTS,
16
+ VOTE_CHANGE, CONCLUDED, APPROVED, REJECTED, STAGE_COMPLETE, RAN, ENABLED, DISABLED, ACTIVITY_CHANGE,
17
+ HEAD_COMMIT_CHANGE, MERGEABLE_FLIPPED, READY_FOR_MERGE, BEHIND_MASTER, DRAFT_FLIPPED,
18
+ STALLED, RETRY_LIMIT_REACHED, DEPENDENCY_MET, ALL_ITEMS_DONE, ITEM_FAILED_N_TIMES,
19
+ STAGE_ADVANCED, STUCK_IN_STAGE }
20
+ WATCH_ABSOLUTE_CONDITIONS = Set([MERGED, BUILD_FAIL, BUILD_PASS, COMPLETED, FAILED, CONCLUDED, APPROVED,
21
+ REJECTED, READY_FOR_MERGE, RETRY_LIMIT_REACHED, ALL_ITEMS_DONE, ITEM_FAILED_N_TIMES]) // fire-once
22
+ ```
23
+
24
+ ## Engine defaults
25
+
26
+ Retry/timeout limits, sweep cadences, fleet ceilings, and managed-spawn caps live in `ENGINE_DEFAULTS` (also in `engine/shared.js`). Read defaults from there rather than re-declaring; per-deployment overrides go in `config.engine.*` and resolve through the per-knob helpers (`resolveAgentMaxBudget`, etc.).
27
+
28
+ ## Invariants
29
+
30
+ - **Write only `WI_STATUS.DONE`.** Legacy aliases (`in-pr`, `implemented`, `complete`) are accepted on read for backward compat but never written. `updateWorkItemStatus()` validates writes against `WI_STATUS`.
31
+ - **`mutateJsonFileLocked` for RMW.** All shared-JSON status writes go through the locked mutator wrappers (`mutateDispatch`, `mutateWorkItems`, `mutatePullRequests`) — see [`CLAUDE.md`](../CLAUDE.md) → **Concurrency**.
32
+ - **No string comparisons.** `pr.status === 'active'` ⇒ `pr.status === PR_STATUS.ACTIVE`. Source-inspection tests grep for the constant form.
@@ -1,6 +1,8 @@
1
1
  # Design: Replacing File-Based State with a Structured Database
2
2
 
3
- > Author: Rebecca (Architect) | Date: 2026-04-07 | Status: Proposal
3
+ > Author: Rebecca (Architect) | Date: 2026-04-07 | Status: **Accepted — implementation in progress**
4
+
5
+ > **Implementation status (as of 2026-05):** The `node:sqlite` recommendation in §3 has been adopted ahead of schedule. Phases 0–7 have shipped (events, dispatches, work_items, pull_requests, logs, metrics, watches, schedule_runs + pipeline_runs + managed_processes + worktree_pool — see `CHANGELOG.md`). The SQLite schema lives under `engine/db/migrations/` and the singleton opens `engine/state.db` in WAL mode. The "Phase 2: estimated Node 26 LTS" timeline in §3 is now historical context; treat sections 1–3 as design rationale rather than a forward plan.
4
6
 
5
7
  ## Executive Summary
6
8
 
@@ -0,0 +1,47 @@
1
+ # `keep_processes` sidecar
2
+
3
+ > Opt-in mechanism for agents that spawn detached children themselves and need to leave them running past their own exit. The companion to engine-owned [managed-spawn](managed-spawn.md).
4
+
5
+ ## When to use `keep_processes`
6
+
7
+ | Need | Use this |
8
+ |---|---|
9
+ | Long-running dev server / emulator the engine should own across restarts | [managed-spawn](managed-spawn.md) |
10
+ | Short-lived helper the *same* agent needs alive past exit (e.g. `gradle --daemon` for the next gradle invocation) | `meta.keep_processes` |
11
+ | Executable outside the managed-spawn allowlist | `meta.keep_processes` |
12
+ | Service with no healthcheck | `meta.keep_processes` |
13
+ | One-shot script that exits on its own | Neither |
14
+
15
+ Both can coexist on the same WI. There's no plan to deprecate `keep_processes`; a future cleanup PR can revisit if managed-spawn fully subsumes its use cases.
16
+
17
+ ## Sidecar contract
18
+
19
+ Set `meta.keep_processes: true` on the WI; agent writes `agents/<id>/keep-pids.json` before exit:
20
+
21
+ ```jsonc
22
+ {
23
+ "pids": [12345], // ≤5 entries
24
+ "purpose": "gradle daemon for follow-up test WI",
25
+ "cwd": "D:/repos/my-android-app", // MUST be a real git worktree
26
+ "ports": [8080], // advisory, engine doesn't bind
27
+ "expires_at": "2026-04-29T00:00:00Z", // TTL ≤1440 min; default 60
28
+ "written_by": "ripley",
29
+ "wi_id": "W-mpqtg16a0007f5bb"
30
+ }
31
+ ```
32
+
33
+ `buildKeepProcessesHint` bakes the Windows-correct detached-spawn pattern (`spawn(..., { detached: true, stdio: 'ignore' }).unref()` plus the `start /B` fallback on cmd-only shells) into the agent prompt automatically — agents should not hand-roll the detach call.
34
+
35
+ ## Lifecycle
36
+
37
+ - **Validation:** sidecar shape + workdir checked in `onAgentClose`. Failure → non-retryable `failure_class: invalid-keep-processes-{workdir,schema}`.
38
+ - **Sweep:** [`engine/keep-process-sweep.js`](../engine/keep-process-sweep.js) reaps at boot and every 30 ticks. Dead PIDs are pruned; expired TTL → process killed via `shared.killGracefully` then `killImmediate`.
39
+ - **No healthcheck:** unlike managed-spawn, `keep_processes` does not gate WI completion on first-healthy. If the child crashes immediately the agent still succeeds — by design (ad-hoc helper semantics).
40
+
41
+ ## Caps
42
+
43
+ - **PIDs:** ≤5 per sidecar.
44
+ - **TTL:** ≤1440 min (24h). Default 60.
45
+ - **Cwd:** must exist and resolve to inside a git worktree (`requireGitWorkdir: true`). Monorepo subdirs walk up to `gitWorktreeMaxParentDepth` (default 6).
46
+
47
+ See also: [`CLAUDE.md`](../CLAUDE.md) → **Agent Spawn**, [managed-spawn.md](managed-spawn.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2083",
3
+ "version": "0.1.2084",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"