@yemi33/minions 0.1.2143 → 0.1.2145
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/bin/minions.js +85 -0
- package/bin/minions.js.rej +16 -0
- package/dashboard/js/refresh.js +14 -0
- package/dashboard/js/render-pinned.js +119 -3
- package/dashboard/js/utils.js +20 -0
- package/dashboard/layout.html +1 -0
- package/dashboard/slim/body.html +113 -1
- package/dashboard/slim/body.html.rej +11 -0
- package/dashboard/slim/js/command-send.js.rej +12 -0
- package/dashboard/slim/js/helpers.js +9 -0
- package/dashboard/slim/js/history.js +153 -88
- package/dashboard/slim/js/history.js.rej +26 -0
- package/dashboard/slim/js/modals-tiles.js +8 -2
- package/dashboard/slim/js/pinned.js +182 -0
- package/dashboard/slim/js/settings.js +126 -6
- package/dashboard/slim/js/status.js +9 -6
- package/dashboard/slim/layout.html +1 -0
- package/dashboard/slim/styles.css +77 -2
- package/dashboard/slim/styles.css.rej +124 -0
- package/dashboard/styles.css +19 -0
- package/dashboard-build.js +9 -2
- package/dashboard.js +44 -1
- package/docs/README.md.rej +9 -0
- package/docs/auto-discovery.md +2 -2
- package/docs/constellation-style-telemetry.md +161 -0
- package/docs/engine-restart.md +1 -1
- package/docs/kb-sweep.md +2 -2
- package/docs/managed-spawn.md +1 -1
- package/docs/watches.md +11 -11
- package/engine/cli.js +57 -12
- package/engine/features.js +11 -0
- package/engine/lifecycle.js +266 -0
- package/engine/queries.js +75 -1
- package/engine/shared.js +173 -36
- package/engine/watchdog.js +458 -0
- package/package.json +1 -1
package/dashboard.js
CHANGED
|
@@ -240,6 +240,27 @@ function removePinnedEntryLocked(title) {
|
|
|
240
240
|
return !missing;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// Edit a pinned note in place: replace the entry keyed on originalTitle with a
|
|
244
|
+
// fresh title/content/level block, atomically (single lock — never a remove+add
|
|
245
|
+
// race that could drop the note if the second write failed). Returns true if the
|
|
246
|
+
// original was found and replaced; false means it was appended as a new entry
|
|
247
|
+
// (upsert) so an edit never silently loses the content.
|
|
248
|
+
function updatePinnedEntryLocked({ originalTitle, title, content, level }, now = new Date()) {
|
|
249
|
+
const levelTag = level === 'critical' ? '🔴 ' : level === 'warning' ? '🟡 ' : '';
|
|
250
|
+
const block = '\n\n### ' + levelTag + title + '\n\n' + content + '\n\n*Pinned by human on ' + now.toISOString().slice(0, 10) + '*';
|
|
251
|
+
let found = false;
|
|
252
|
+
mutateTextFileLocked(PINNED_PATH, existing => {
|
|
253
|
+
const base = existing || PINNED_DEFAULT_CONTENT;
|
|
254
|
+
const regex = new RegExp('\\n\\n###\\s*(?:🔴\\s*|🟡\\s*)?' + originalTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\n[\\s\\S]*?(?=\\n\\n###|$)', 'i');
|
|
255
|
+
if (regex.test(base)) {
|
|
256
|
+
found = true;
|
|
257
|
+
return base.replace(regex, block);
|
|
258
|
+
}
|
|
259
|
+
return base + block;
|
|
260
|
+
}, { defaultValue: PINNED_DEFAULT_CONTENT });
|
|
261
|
+
return found;
|
|
262
|
+
}
|
|
263
|
+
|
|
243
264
|
function setKbPinsLocked(pins) {
|
|
244
265
|
return mutateJsonFileLocked(KB_PINS_PATH, () => pins, { defaultValue: [], skipWriteIfUnchanged: true });
|
|
245
266
|
}
|
|
@@ -9786,7 +9807,18 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
9786
9807
|
async function serveSlimUx(req, res) {
|
|
9787
9808
|
try {
|
|
9788
9809
|
const { buildSlimHtml } = require('./dashboard-build');
|
|
9789
|
-
|
|
9810
|
+
// Inject the same feature-flag bootstrap the classic dashboard ships so
|
|
9811
|
+
// Slim UX can gate client behavior (e.g. the slim-ux-promo welcome popup)
|
|
9812
|
+
// off window.MINIONS_FEATURES. Built per request so a flag toggle takes
|
|
9813
|
+
// effect on the next slim load with no restart.
|
|
9814
|
+
const slimFeaturesBoot = (() => {
|
|
9815
|
+
const boot = { flags: {}, defaults: {} };
|
|
9816
|
+
try {
|
|
9817
|
+
for (const f of features.listFeatures(CONFIG)) { boot.flags[f.id] = f.enabled; boot.defaults[f.id] = f.default; }
|
|
9818
|
+
} catch { /* keep empty bootstrap on registry error */ }
|
|
9819
|
+
return boot;
|
|
9820
|
+
})();
|
|
9821
|
+
const html = buildSlimHtml({ featuresJson: JSON.stringify(slimFeaturesBoot) })
|
|
9790
9822
|
.replace(/\{\{favicon_emoji\}\}/g, FAVICON_EMOJI)
|
|
9791
9823
|
.replace(/\{\{title_suffix\}\}/g, TITLE_SUFFIX);
|
|
9792
9824
|
res.statusCode = 200;
|
|
@@ -11330,6 +11362,17 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
11330
11362
|
invalidateStatusCache({ includeSlow: true });
|
|
11331
11363
|
return jsonReply(res, 200, { ok: true });
|
|
11332
11364
|
}},
|
|
11365
|
+
{ method: 'POST', path: '/api/pinned/update', desc: 'Edit a pinned note (replace the entry keyed on originalTitle)', params: 'originalTitle, title, content, level?', handler: async (req, res) => {
|
|
11366
|
+
const body = await readBody(req);
|
|
11367
|
+
const originalTitle = (body.originalTitle || '').trim();
|
|
11368
|
+
const title = (body.title || '').trim();
|
|
11369
|
+
const { content, level } = body;
|
|
11370
|
+
if (!originalTitle || !title || !content) return jsonReply(res, 400, { error: 'originalTitle, title and content required' });
|
|
11371
|
+
const found = updatePinnedEntryLocked({ originalTitle, title, content, level });
|
|
11372
|
+
// pinned.md is in slow-state cache — opt-in invalidation so the edit is visible immediately
|
|
11373
|
+
invalidateStatusCache({ includeSlow: true });
|
|
11374
|
+
return jsonReply(res, 200, { ok: true, updated: found });
|
|
11375
|
+
}},
|
|
11333
11376
|
|
|
11334
11377
|
// KB pin state (server-side so CC can pin items)
|
|
11335
11378
|
{ method: 'GET', path: '/api/kb-pins', desc: 'Get pinned KB item keys', handler: async (req, res) => {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
diff a/docs/README.md b/docs/README.md (rejected hunks)
|
|
2
|
+
@@ -16,6 +16,7 @@ Architecture, design proposals, and lifecycle references for people working on t
|
|
3
|
+
- [command-center.md](command-center.md) — Command Center (CC) chat panel: persistent Sonnet sessions, `--resume` semantics, system-prompt invalidation, and per-tab session storage.
|
|
4
|
+
- [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.
|
|
5
|
+
- [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.
|
|
6
|
+
+- [constellation-style-telemetry.md](constellation-style-telemetry.md) — Feasibility study for a local-first usage/analytics layer modeled on Constellation's telemetry (typed event log + daily rollup + dashboard view), and why a 1:1 PostgreSQL/multi-tenant port is the wrong goal for Minions.
|
|
7
|
+
- [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).
|
|
8
|
+
- [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.
|
|
9
|
+
- [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`.
|
package/docs/auto-discovery.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Auto-Discovery & Execution Pipeline
|
|
2
2
|
|
|
3
|
-
> Last verified: 2026-
|
|
3
|
+
> Last verified: 2026-06-06 against `engine.js` `tickInner()` and `routing.md`.
|
|
4
4
|
|
|
5
5
|
How the minions engine finds work and dispatches agents automatically.
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ tick()
|
|
|
16
16
|
1c. meetingTimeouts() Advance round-based meetings whose timer fired
|
|
17
17
|
2. consolidateInbox() Merge learnings into notes.md (Haiku-powered)
|
|
18
18
|
2.1 autoSweepKb() Periodic KB sweep (opt-in via engine.autoConsolidateMemory, 4h cadence)
|
|
19
|
-
2.5 runCleanup() Periodic cleanup (every
|
|
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
22
|
2.55 checkWatches() Persistent watch jobs (every 18 tick-equivalents)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Constellation-style telemetry for Minions — feasibility study
|
|
2
|
+
|
|
3
|
+
**Status:** Design proposal (not implemented). **Audience:** Contributors.
|
|
4
|
+
**Question answered:** Constellation (`~/code/constellation`) has a mature telemetry/usage
|
|
5
|
+
stack. Should Minions build something similar, and if so, what shape?
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
A **1:1 port is the wrong goal.** Constellation's telemetry is built for a *hosted,
|
|
10
|
+
multi-tenant, server-side fleet*: PostgreSQL storage, WebSocket ingestion,
|
|
11
|
+
per-constellation/per-device tenancy, and a bearer-authed global-admin dashboard.
|
|
12
|
+
Minions is the structural opposite — *single-machine, local-first, zero-deps-beyond-Node,
|
|
13
|
+
JSON-file state, loopback dashboard*.
|
|
14
|
+
|
|
15
|
+
But the **valuable, portable subset is very feasible**, and Minions already has ~60% of it.
|
|
16
|
+
The realistic target is a **local-first usage/analytics layer**: a typed append-only event
|
|
17
|
+
log + a retention/rollup discipline + a dedicated dashboard "Usage" page, optionally a
|
|
18
|
+
`/metrics` Prometheus endpoint. **No database, no new runtime dependencies, no new network
|
|
19
|
+
egress** (preserves the "nothing leaves the machine" property — Minions' own code has no
|
|
20
|
+
external telemetry today).
|
|
21
|
+
|
|
22
|
+
## What Constellation does
|
|
23
|
+
|
|
24
|
+
Database-first, no external SaaS (no Kusto/App Insights/Sentry/Segment/Datadog). Events are
|
|
25
|
+
fire-and-forget INSERTs into PostgreSQL on the WebSocket hot path, spread across ~8 tables:
|
|
26
|
+
|
|
27
|
+
| Table | Retention | Role |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `events` | 7d | Session/device/tool-call/auth lifecycle (`event_type` + JSON `payload`) |
|
|
30
|
+
| `sessions` | 30d | Session state + metadata |
|
|
31
|
+
| `execution_log` | 30d | Mission/agent runs: `status`, `cost_usd`, `duration_ms`, `error_category` |
|
|
32
|
+
| `session_launches` | 30d | Mission-wizard lifecycle + structured error capture (`error_code/category/message/stage`) |
|
|
33
|
+
| `app_events` | 30d | Cross-app analytics (e.g. PRism PR review/resolve) |
|
|
34
|
+
| `device_connection_log` | 90d | Per-day (device, agent_version) fingerprints |
|
|
35
|
+
| `metrics_daily` | **unlimited** | Rolled-up daily aggregates that survive retention |
|
|
36
|
+
|
|
37
|
+
Key design properties:
|
|
38
|
+
|
|
39
|
+
- **Rollup-before-delete invariant.** A 6-hourly scheduler (`packages/server/src/utils/retention.ts`
|
|
40
|
+
`startCleanupScheduler`) runs `runMetricsRollup()` (`packages/server/src/utils/metrics-rollup.ts`)
|
|
41
|
+
**before** `runDataCleanup()`. If the rollup throws, retention is skipped that tick — live rows
|
|
42
|
+
are never deleted before they're aggregated. A sentinel watermark row tracks the last fully
|
|
43
|
+
aggregated UTC date; today is excluded (live queries cover it).
|
|
44
|
+
- **Structured error taxonomy.** `WIZARD_ERROR_CATEGORIES` in `packages/shared/src/constants.ts`
|
|
45
|
+
(`validation`, `parse_error`, `agent_unavailable`, `agent_error`, `file_write`, `unknown_tool`,
|
|
46
|
+
`timeout`, `unknown`) drives failure dashboards.
|
|
47
|
+
- **Emit API.** Direct DB writes (`db.execute('INSERT INTO events ...')`) at ~60–70 call sites;
|
|
48
|
+
a typed `logAuditEvent()` helper (`packages/server/src/utils/audit.ts`) for security events;
|
|
49
|
+
a frontend `recordPrismEvent()` using `navigator.sendBeacon` →
|
|
50
|
+
`POST /api/v1/prism/telemetry` (`packages/server/src/api/prism.ts`).
|
|
51
|
+
- **Admin surface.** `GET /api/v1/global-admin/overview?window=24h|3d|7d|30d|90d|all`
|
|
52
|
+
(`packages/server/src/api/global-admin.ts`) returns windowed totals + daily time-series +
|
|
53
|
+
top-failing missions + recent wizard failures; queries live tables inside retention and
|
|
54
|
+
`metrics_daily` beyond it, merging by dimension. Separately, `GET /metrics`
|
|
55
|
+
(`packages/server/src/api/metrics.ts`) exposes Prometheus counters from an in-memory registry
|
|
56
|
+
(`terminal-metrics.ts`) — no Prometheus client library.
|
|
57
|
+
- **Privacy.** Path truncation + field redaction (`packages/shared/src/privacy.ts`), configurable
|
|
58
|
+
retention windows, no sampling.
|
|
59
|
+
- **Dependencies.** Only telemetry-relevant runtime dep is `pg`. No analytics SDKs anywhere.
|
|
60
|
+
|
|
61
|
+
## What Minions already has (≈60%)
|
|
62
|
+
|
|
63
|
+
| Constellation concept | Minions equivalent today |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `metrics_daily` rollups | `metrics.json._daily.<YYYY-MM-DD>` → `{costUsd, inputTokens, outputTokens, cacheRead, tasks}` |
|
|
66
|
+
| Per-entity aggregates | Per-agent counters: `tasksCompleted/Errored`, `prsCreated/Approved/Rejected/Merged`, `reviewsDone`, `totalCostUsd`, `totalInputTokens/OutputTokens/CacheRead`, `totalRuntimeMs`, `model`, `timedTasks` |
|
|
67
|
+
| App/engine analytics | `metrics.json._engine`: `command-center`, `agent-dispatch`, `consolidation`, `agent_memory_reconcile` |
|
|
68
|
+
| Emit helpers | `llm.trackEngineUsage()`, `trackReviewMetric()`, `updateMetrics()` (`engine/lifecycle.js`, `engine/llm.js`, `engine/shared.js`) |
|
|
69
|
+
| Structured error taxonomy | `FAILURE_CLASS` constants (recorded per work-item; not yet aggregated as a metric) |
|
|
70
|
+
| Diagnostic stream | `log.json` ring buffer (2500, secret-redacted via `redactSecrets()`) + `engine/dashboard-diagnostics.log` |
|
|
71
|
+
| Admin overview query | `getMetrics()` (`engine/queries.js`) surfaced via `/api/status` |
|
|
72
|
+
|
|
73
|
+
## The gap (the 40% worth building)
|
|
74
|
+
|
|
75
|
+
1. **No typed, append-only *event log*.** Minions has aggregate *counters* and a generic
|
|
76
|
+
*diagnostic* log, but nothing like Constellation's `events`/`execution_log`/`session_launches`:
|
|
77
|
+
domain events with `event_type` + structured `payload` + timestamp that can be **sliced over
|
|
78
|
+
time** ("dispatches by type per day", "`failure_class` trend", "plan→PRD failures with full
|
|
79
|
+
error text"). This is the single biggest capability difference.
|
|
80
|
+
2. **No dedicated usage/analytics *view*.** Dashboard pages are home/work/prs/plans/inbox/tools/
|
|
81
|
+
schedule/watches/pipelines/meetings + qa/engine. There is no cost trend, failure-category
|
|
82
|
+
breakdown, or throughput chart. `getMetrics()` data is collected but barely visualized.
|
|
83
|
+
3. **No retention discipline on a structured event stream.** The `_daily` rollup exists, but there
|
|
84
|
+
is no domain-event log to retain/prune under the rollup-before-delete invariant.
|
|
85
|
+
|
|
86
|
+
## Portability matrix
|
|
87
|
+
|
|
88
|
+
| Constellation component | Verdict | Rationale |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| Typed event taxonomy + error categories | **Port (adapt)** | Reuse existing `WORK_TYPE` / `FAILURE_CLASS`; high value, low cost |
|
|
91
|
+
| Append-only event log | **Port → JSONL** | `engine/events.jsonl` (one event/line, atomic append); matches zero-dep ethos, not a DB table |
|
|
92
|
+
| Daily rollup + **rollup-before-delete** | **Adopt the discipline** | Generalize existing `_daily` to also fold event counts / failure categories; prune JSONL after rollup |
|
|
93
|
+
| Retention windows | **Port (config knob)** | `engine.telemetryRetentionDays`; reuse an existing `cleanup` tick phase |
|
|
94
|
+
| Usage/analytics dashboard page | **Port (scaled down)** | New `dashboard/pages/usage.html` + renderer over `getMetrics()` + rollups; inline SVG, no chart lib |
|
|
95
|
+
| `GET /metrics` Prometheus endpoint | **Optional, easy** | ~40 lines, in-memory text exposition |
|
|
96
|
+
| PostgreSQL backend | **Drop** | Violates "zero deps beyond Node built-ins"; JSON/JSONL suffice at single-machine volume. (See `design-state-storage.md` — if a DB ever lands, `node:sqlite` is the candidate, and this log would migrate with it.) |
|
|
97
|
+
| Multi-tenant constellation/device model | **Drop** | Minions is one fleet on one machine |
|
|
98
|
+
| WebSocket ingestion + `sendBeacon` | **Drop / reuse existing** | Engine already observes agents directly; dashboard already POSTs diagnostics |
|
|
99
|
+
| Bearer-auth global-admin | **Drop** | Local loopback dashboard, no tenancy |
|
|
100
|
+
|
|
101
|
+
## Recommended shape (phased, local-first)
|
|
102
|
+
|
|
103
|
+
### Phase 1 — Event log + emit helper (small, ~½ day)
|
|
104
|
+
Add `engine/telemetry.js` exporting `recordEvent(type, payload)` that appends a single redacted
|
|
105
|
+
JSON line to `engine/events.jsonl`. Event types live in a `TELEMETRY_EVENT` constant set (no magic
|
|
106
|
+
strings, per repo convention). Reuse `shared.redactSecrets()`. Wire ~10–15 high-value sites already
|
|
107
|
+
on the tick path: dispatch start/done/fail (+`failure_class`), PR poll outcomes, plan→PRD,
|
|
108
|
+
review verdicts, CC turns. Gitignored runtime file (add to the State Files list in `CLAUDE.md`).
|
|
109
|
+
|
|
110
|
+
### Phase 2 — Rollup + retention (small, ~½ day)
|
|
111
|
+
Extend the existing daily-rollup path so it also folds event counts + failure-category tallies into
|
|
112
|
+
`metrics.json._daily`. Prune `events.jsonl` lines older than `telemetryRetentionDays` **after** the
|
|
113
|
+
rollup write succeeds (Constellation's invariant). Slots into an existing periodic tick phase.
|
|
114
|
+
All `metrics.json` writes stay inside `mutateJsonFileLocked()`.
|
|
115
|
+
|
|
116
|
+
### Phase 3 — Usage dashboard page (medium, ~1–1.5 days)
|
|
117
|
+
New sidebar page reading `getMetrics()` + `_daily`: cost/token trend, dispatches-by-type,
|
|
118
|
+
`failure_class` breakdown, per-agent throughput, CC usage. Inline SVG sparklines — **no chart
|
|
119
|
+
dependency**. Follow the fragment-assembly model (`dashboard/pages/usage.html` + a
|
|
120
|
+
`dashboard/js/render-usage.js`) and bump `RENDER_VERSIONS` per the render-cache rules. Add a
|
|
121
|
+
Settings toggle if gated behind a feature flag.
|
|
122
|
+
|
|
123
|
+
### Phase 4 (optional) — `GET /metrics` (~2 hrs)
|
|
124
|
+
Prometheus text exposition of the same counters for external scrapers. Strict-CSP-exempt route like
|
|
125
|
+
the other HTML entry points; counters from an in-memory registry, no client library.
|
|
126
|
+
|
|
127
|
+
## Effort & risk
|
|
128
|
+
|
|
129
|
+
- **Effort:** Phase 1 ~½ day · Phase 2 ~½ day · Phase 3 ~1–1.5 days · Phase 4 ~2 hrs.
|
|
130
|
+
**Total ≈ 2.5–3 days** for the full local-first system; **Phases 1–2 alone deliver the core
|
|
131
|
+
capability in ~1 day.**
|
|
132
|
+
- **Risk: low.** No new deps, no schema migrations, no network surface, no new external egress.
|
|
133
|
+
Watch-items: (a) JSONL append volume — bounded by retention + rollup; (b) `mutateJsonFileLocked`
|
|
134
|
+
concurrency rules for the rollup write; (c) source-inspection tests for any new dashboard page;
|
|
135
|
+
(d) CSP/route placement if Phase 4 ships.
|
|
136
|
+
- **Cross-repo:** lands on `yemi33` origin and auto-mirrors to `opg`; nothing here touches
|
|
137
|
+
enterprise-only files.
|
|
138
|
+
|
|
139
|
+
## Honest caveats
|
|
140
|
+
|
|
141
|
+
- Constellation's *real* power is the **central warehouse aggregating many machines** — a SaaS
|
|
142
|
+
capability Minions structurally cannot replicate without a server + DB + tenancy, which would
|
|
143
|
+
break its design philosophy. If cross-machine fleet analytics is ever the actual goal, that is a
|
|
144
|
+
separate, much larger project (a hosted collector), not a port. (`constellation-bridge.md`
|
|
145
|
+
already defines a read-only cross-repo bridge; a telemetry collector would be a different beast.)
|
|
146
|
+
- Much of Constellation's table sprawl (`sessions`, `devices`, `app_events`) maps to things Minions
|
|
147
|
+
either doesn't have or already tracks in JSON. **Don't recreate tables 1:1** — port the *event-log
|
|
148
|
+
+ rollup + view* pattern, not the schema.
|
|
149
|
+
|
|
150
|
+
## References
|
|
151
|
+
|
|
152
|
+
- Constellation rollup: `packages/server/src/utils/metrics-rollup.ts`,
|
|
153
|
+
`packages/server/src/utils/retention.ts`
|
|
154
|
+
- Constellation emit/admin: `packages/server/src/utils/audit.ts`,
|
|
155
|
+
`packages/server/src/api/global-admin.ts`, `packages/server/src/api/metrics.ts`,
|
|
156
|
+
`packages/server/src/api/prism.ts`, `packages/dashboard/src/prism/prism-telemetry.ts`
|
|
157
|
+
- Constellation taxonomy/privacy: `packages/shared/src/constants.ts`,
|
|
158
|
+
`packages/shared/src/privacy.ts`
|
|
159
|
+
- Minions today: `engine/metrics.json`, `getMetrics()` (`engine/queries.js`),
|
|
160
|
+
`trackEngineUsage()` (`engine/llm.js`), `FAILURE_CLASS` (`engine/shared.js`),
|
|
161
|
+
related design doc `design-state-storage.md`.
|
package/docs/engine-restart.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Engine Restart & Agent Survival
|
|
2
2
|
|
|
3
|
-
> Last verified: 2026-
|
|
3
|
+
> Last verified: 2026-06-06 against `engine.js` (`engineRestartGraceUntil`, line 177) and `engine/shared.js` `ENGINE_DEFAULTS` (`restartGracePeriod: 1200000`, `heartbeatTimeout: 300000`, `agentTimeout: 18000000`).
|
|
4
4
|
|
|
5
5
|
## The Problem
|
|
6
6
|
|
package/docs/kb-sweep.md
CHANGED
|
@@ -37,7 +37,7 @@ The remaining survivors are sent to Claude Haiku in batches of `LLM_BATCH_SIZE =
|
|
|
37
37
|
|
|
38
38
|
Each action archives the file via the same `_archiveKbFile()` helper used by Pass 1; reclassification rewrites the `category:` frontmatter line and moves the file into the new category directory (source: [`engine/kb-sweep.js:243-279`](../engine/kb-sweep.js#L243)).
|
|
39
39
|
|
|
40
|
-
Reclassification targets are validated against `shared.KB_CATEGORIES` (`architecture`, `conventions`, `project-notes`, `build-reports`, `reviews` — source: [`engine/shared.js:
|
|
40
|
+
Reclassification targets are validated against `shared.KB_CATEGORIES` (`architecture`, `conventions`, `project-notes`, `build-reports`, `reviews` — source: [`engine/shared.js:2139`](../engine/shared.js#L2139)); unknown categories are silently dropped.
|
|
41
41
|
|
|
42
42
|
If a batch returns invalid JSON or the runtime is unavailable, that batch is skipped with a warning and the rest of the sweep continues (source: [`engine/kb-sweep.js:139-151`](../engine/kb-sweep.js#L139)).
|
|
43
43
|
|
|
@@ -116,7 +116,7 @@ Memory still wins when present; the disk file is a fallback (source: [`engine/kb
|
|
|
116
116
|
|
|
117
117
|
## Automatic Periodic Sweep (opt-in)
|
|
118
118
|
|
|
119
|
-
The engine tick loop can also auto-spawn the KB sweep without dashboard interaction. Gated by `engine.autoConsolidateMemory` (default `false` — source: [`engine/shared.js:
|
|
119
|
+
The engine tick loop can also auto-spawn the KB sweep without dashboard interaction. Gated by `engine.autoConsolidateMemory` (default `false` — source: [`engine/shared.js:2253`](../engine/shared.js#L2253)):
|
|
120
120
|
|
|
121
121
|
- When `engine.autoConsolidateMemory: true`, every tick the engine consults `shouldAutoSweep()` from [`engine/kb-sweep.js`](../engine/kb-sweep.js) and, when the 4-hour cadence has elapsed since the last completion, calls `spawnSweepRunnerDetached()` to fire-and-forget a fresh `engine/kb-sweep-runner.js` process (source: [`engine.js`](../engine.js) tick step 2.1).
|
|
122
122
|
- The inbox→`notes.md` consolidation runs every tick *regardless* of this flag via `consolidateInbox()`; `autoConsolidateMemory` controls **only** the heavier `knowledge/` sweep.
|
package/docs/managed-spawn.md
CHANGED
|
@@ -203,7 +203,7 @@ Killing a spec from outside Minions (raw `Stop-Process`) leaves a stale row in `
|
|
|
203
203
|
|
|
204
204
|
## Configuration
|
|
205
205
|
|
|
206
|
-
All knobs live under `engine.managedSpawn` in `engine/shared.js:
|
|
206
|
+
All knobs live under `engine.managedSpawn` in `engine/shared.js:2494` (`ENGINE_DEFAULTS.managedSpawn`). Override per install via `config.json`:
|
|
207
207
|
|
|
208
208
|
| Key | Default | Notes |
|
|
209
209
|
|---|---|---|
|
package/docs/watches.md
CHANGED
|
@@ -24,7 +24,7 @@ A watch is a small JSON record persisted to `engine/watches.json`. It binds:
|
|
|
24
24
|
|
|
25
25
|
## Lifecycle (`WATCH_STATUS`)
|
|
26
26
|
|
|
27
|
-
Defined in `engine/shared.js:
|
|
27
|
+
Defined in `engine/shared.js:3163` (`WATCH_STATUS`):
|
|
28
28
|
|
|
29
29
|
| Status | Meaning |
|
|
30
30
|
|-------------|-------------------------------------------------------------------------|
|
|
@@ -37,10 +37,10 @@ Pause/resume flips the `status` field via `POST /api/watches/update` *(source: `
|
|
|
37
37
|
|
|
38
38
|
## Conditions (`WATCH_CONDITION`)
|
|
39
39
|
|
|
40
|
-
Defined in `engine/shared.js:
|
|
40
|
+
Defined in `engine/shared.js:3179-3220` (`WATCH_CONDITION`). Conditions split into two families:
|
|
41
41
|
|
|
42
42
|
### Absolute conditions (`WATCH_ABSOLUTE_CONDITIONS`)
|
|
43
|
-
*(source: `engine/shared.js:
|
|
43
|
+
*(source: `engine/shared.js:3226-3245`)*
|
|
44
44
|
|
|
45
45
|
`merged`, `build-fail`, `build-pass`, `completed`, `failed`, `concluded`, `approved`, `rejected`, `ready-for-merge`, `retry-limit-reached`, `all-items-done`, `item-failed-n-times`.
|
|
46
46
|
|
|
@@ -49,12 +49,12 @@ When `stopAfter === 0`, these are **fire-once** — the engine flips the watch t
|
|
|
49
49
|
> **Per-target override (W-mp7hg58e000b5212):** the global `WATCH_ABSOLUTE_CONDITIONS` set is the legacy fallback. Each target type now declares its own `absoluteConditions: [...]` array in its spec; `registerTargetType` normalizes that into a `Set` that takes precedence at evaluation time. The plugin contract (see below) uses this to keep absolute-vs-change semantics local to each target type. Plugins that omit `absoluteConditions` get an empty set (all change-based).
|
|
50
50
|
|
|
51
51
|
### Change-based conditions
|
|
52
|
-
`status-change`, `any`, `new-comments`, `vote-change`, `stage-complete`, `ran`, `enabled`, `disabled`, `activity-change`, plus the predicate conditions added under P-w4e2f6a1 / P-w5b8d2c9 for the `pr`, `work-item`, `plan`, and `pipeline` target types (`head-commit-change`, `mergeable-flipped`, `behind-master`, `draft-flipped`, `stalled`, `dependency-met`, `stage-advanced`, `stuck-in-stage`). See `engine/shared.js:
|
|
52
|
+
`status-change`, `any`, `new-comments`, `vote-change`, `stage-complete`, `ran`, `enabled`, `disabled`, `activity-change`, plus the predicate conditions added under P-w4e2f6a1 / P-w5b8d2c9 for the `pr`, `work-item`, `plan`, and `pipeline` target types (`head-commit-change`, `mergeable-flipped`, `behind-master`, `draft-flipped`, `stalled`, `dependency-met`, `stage-advanced`, `stuck-in-stage`). See `engine/shared.js:3179-3220` for the canonical enum.
|
|
53
53
|
|
|
54
54
|
These compare the live entity against the watch's `_lastState` snapshot and run forever when `stopAfter === 0`. Baseline `_lastState` is captured on the first check so the very next change triggers the watch *(source: `engine/watches.js:434, 520`)*.
|
|
55
55
|
|
|
56
56
|
### Tick-counted conditions
|
|
57
|
-
`stalled`, `stuck-in-stage` — require N consecutive unchanged captures (default `WATCH_STALLED_DEFAULT_TICKS = 12`, `WATCH_STUCK_STAGE_DEFAULT_TICKS = 12`, both in `engine/shared.js:
|
|
57
|
+
`stalled`, `stuck-in-stage` — require N consecutive unchanged captures (default `WATCH_STALLED_DEFAULT_TICKS = 12`, `WATCH_STUCK_STAGE_DEFAULT_TICKS = 12`, both in `engine/shared.js:3222-3223`). Counters (`_unchangedTicks`, `_stuckStageTicks`) are recomputed inside `_captureState` by comparing the fresh snapshot against `prevState`.
|
|
58
58
|
|
|
59
59
|
### Predicate conditions
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ Several condition keys evaluate a derived predicate on the captured entity/state
|
|
|
65
65
|
- **plan** — `all-items-done` (`items_done === items_total > 0`), `item-failed-n-times` (any `missing_features[*]._retryCount >= ENGINE_DEFAULTS.maxRetries`).
|
|
66
66
|
- **pipeline** — `stage-advanced` (`current_stage_id` changed within the same `runId`), `stuck-in-stage` (current stage unchanged for `WATCH_STUCK_STAGE_DEFAULT_TICKS` checks, default 12).
|
|
67
67
|
|
|
68
|
-
Compound state-assertion predicates (`ready-for-merge`, `retry-limit-reached`, `all-items-done`, `item-failed-n-times`) live in `WATCH_ABSOLUTE_CONDITIONS` so they fire-once when `stopAfter === 0` — without that they would re-fire every tick while the assertion holds *(source: `engine/shared.js:
|
|
68
|
+
Compound state-assertion predicates (`ready-for-merge`, `retry-limit-reached`, `all-items-done`, `item-failed-n-times`) live in `WATCH_ABSOLUTE_CONDITIONS` so they fire-once when `stopAfter === 0` — without that they would re-fire every tick while the assertion holds *(source: `engine/shared.js:3226` `WATCH_ABSOLUTE_CONDITIONS`)*.
|
|
69
69
|
|
|
70
70
|
## Target Types — `TARGET_TYPES` Registry
|
|
71
71
|
|
|
@@ -89,7 +89,7 @@ Canonical example: `watches.d/http.js` (W-mp7i22mu00191b07) — a generic HTTP p
|
|
|
89
89
|
|
|
90
90
|
### Built-in target types
|
|
91
91
|
|
|
92
|
-
The eight built-ins are registered at module load *(source: `engine/watches.js:
|
|
92
|
+
The eight built-ins are registered at module load *(source: `engine/watches.js:669-1268`)*. Constants live at `engine/shared.js:3169-3177` (`WATCH_TARGET_TYPE`).
|
|
93
93
|
|
|
94
94
|
| `targetType` | Target value | Conditions | Notes |
|
|
95
95
|
|---------------|--------------------------------------|----------------------------------------------------------------------------|-------|
|
|
@@ -133,7 +133,7 @@ Resolution is `path.join(shared.MINIONS_DIR, 'watches.d')` so it works in both d
|
|
|
133
133
|
|
|
134
134
|
## Tick Integration
|
|
135
135
|
|
|
136
|
-
`engine.js` calls `checkWatches(config, state)` every `ENGINE_DEFAULTS.watchPollEvery` ticks (default 18 ⇒ ~3 min at the default 10s tick) inside its own `safe('checkWatches', ...)` block *(source: `engine.js:
|
|
136
|
+
`engine.js` calls `checkWatches(config, state)` every `ENGINE_DEFAULTS.watchPollEvery` ticks (default 18 ⇒ ~3 min at the default 10s tick) inside its own `safe('checkWatches', ...)` block *(source: `engine.js:7439-7470`)*. The engine builds the state object from cached project files + module reads:
|
|
137
137
|
|
|
138
138
|
```
|
|
139
139
|
{
|
|
@@ -175,7 +175,7 @@ I/O happens **outside the lock**: notifications via `writeToInbox`, follow-up ac
|
|
|
175
175
|
| `resume-plan` | Set PRD `status=PLAN_STATUS.ACTIVE` and clear `planStale` |
|
|
176
176
|
| `cc-triage` | Invoke Command Center headlessly via the loopback `POST /api/command-center/triage` endpoint with the trigger context (and optional completion-report / live-output artifacts). Wraps the prompt in `<UNTRUSTED-INPUT>`, uses a default 10-min timeout (capped at 1 h), and is isolated from the user CC session |
|
|
177
177
|
|
|
178
|
-
Constants live in `WATCH_ACTION_TYPE` (`engine/shared.js:
|
|
178
|
+
Constants live in `WATCH_ACTION_TYPE` (`engine/shared.js:3248`); handlers in `engine/watch-actions.js`.
|
|
179
179
|
|
|
180
180
|
### Templating
|
|
181
181
|
|
|
@@ -242,11 +242,11 @@ Absolute conditions firing under `stopAfter === 0` flip `status` to `expired`; `
|
|
|
242
242
|
| Webhook action returns `"only http/https allowed"` | URLs must use `http://` or `https://` schemes; other protocols are rejected by design *(source: `engine/watch-actions.js` `WEBHOOK` handler)* |
|
|
243
243
|
| Trigger fires but follow-up `dispatch-work-item` is missing | Check the engine log for `Watch <id> action <type>: <summary>`. Common reasons: missing `title`, the project's `work-items.json` couldn't be written, or the WI landed in central `work-items.json` because no project was specified |
|
|
244
244
|
| Watch `_lastActionResult` shows `"timeout"` for webhook | Webhooks have a 10s safety timeout to keep the watches tick fast *(source: `engine/watch-actions.js:482-484`)* |
|
|
245
|
-
| `checkWatches` block crashes silently | Wrapped in `safe('checkWatches', ...)` so one failure doesn't abort the tick *(source: `engine.js:
|
|
245
|
+
| `checkWatches` block crashes silently | Wrapped in `safe('checkWatches', ...)` so one failure doesn't abort the tick *(source: `engine.js:7443`)*. Inspect `engine/log.json` for `Watch check error (<id>)` lines. Regression #1088: the block must use `getProjects(config)`, never the long-removed `PROJECTS` constant |
|
|
246
246
|
|
|
247
247
|
## See Also
|
|
248
248
|
|
|
249
|
-
- `engine/shared.js:
|
|
249
|
+
- `engine/shared.js:3163-3275` — `WATCH_STATUS`, `WATCH_TARGET_TYPE`, `WATCH_CONDITION`, `WATCH_ABSOLUTE_CONDITIONS`, `WATCH_ACTION_TYPE` constants
|
|
250
250
|
- `engine/watches.js` — registry, lifecycle, tick integration, `watches.d/` plugin loader
|
|
251
251
|
- `engine/watch-actions.js` — action registry and built-in handlers (including `minions-api`)
|
|
252
252
|
- `watches.d/http.js` — canonical user-extensible target type plugin
|
package/engine/cli.js
CHANGED
|
@@ -159,17 +159,20 @@ function handleCommand(cmd, args) {
|
|
|
159
159
|
if (!cmd) {
|
|
160
160
|
return commands.start();
|
|
161
161
|
} else if (commands[cmd]) {
|
|
162
|
-
// W-mq07mjzi000s1cc9
|
|
162
|
+
// W-mq07mjzi000s1cc9 + W-mq0770160001ee50: Help-flag interception.
|
|
163
163
|
//
|
|
164
|
-
// `minions work --help`
|
|
164
|
+
// `minions work --help` used to create ghost work items with title='--help'
|
|
165
165
|
// because the bare-string `title` was truthy and bypassed the `!title`
|
|
166
|
-
// usage check.
|
|
167
|
-
//
|
|
166
|
+
// usage check. The fix lives in per-command guards (`_isHelpArg` /
|
|
167
|
+
// `looksLikeFlagOrHelp`) on `work`/`spawn`/`plan`/`complete`, which print
|
|
168
|
+
// command-specific `Usage:` output. `pr` and `bridge` handle help inline.
|
|
168
169
|
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
|
|
170
|
+
// For other commands (start/stop/status/etc.), fall back to printing the
|
|
171
|
+
// global command list — they take no positional args so `--help` is
|
|
172
|
+
// otherwise harmless. Commands with per-command help handling are excluded
|
|
173
|
+
// so their `Usage:` text is what the user sees.
|
|
174
|
+
const COMMANDS_WITH_OWN_HELP = new Set(['pr', 'bridge', 'work', 'spawn', 'plan', 'complete']);
|
|
175
|
+
if (!COMMANDS_WITH_OWN_HELP.has(cmd) && isHelpToken(args && args[0])) {
|
|
173
176
|
console.log('Commands:');
|
|
174
177
|
for (const line of formatCliCommandHelpLines()) console.log(line);
|
|
175
178
|
return;
|
|
@@ -245,6 +248,15 @@ function formatCliCommandHelpLines() {
|
|
|
245
248
|
return entries.map(([, { summary }], i) => lefts[i].padEnd(padTo) + summary);
|
|
246
249
|
}
|
|
247
250
|
|
|
251
|
+
// W-mq0770160001ee50 — `minions work --help` used to swallow `--help` as the
|
|
252
|
+
// work-item title and queue a literal junk item titled "--help"; `minions plan
|
|
253
|
+
// --help` dispatched a plan-to-prd agent against the inline text "--help".
|
|
254
|
+
// Every handler that takes a free-form positional arg must call this BEFORE
|
|
255
|
+
// using it as data so users get usage text instead of a state mutation.
|
|
256
|
+
function _isHelpArg(arg) {
|
|
257
|
+
return arg === '--help' || arg === '-h' || arg === 'help';
|
|
258
|
+
}
|
|
259
|
+
|
|
248
260
|
// ─── Runtime fleet flags (--cli / --model / --effort) ────────────────────────
|
|
249
261
|
//
|
|
250
262
|
// Shared by `start`, `restart`, and `config set-cli`. Single source of truth
|
|
@@ -522,6 +534,39 @@ const commands = {
|
|
|
522
534
|
try { shared.applyLegacyCcModelMigration(config, { logger: e.log }); }
|
|
523
535
|
catch (err) { e.log('warn', `legacy ccModel migration failed: ${err.message}`); }
|
|
524
536
|
|
|
537
|
+
// W-mq5s5ttx000j7ab8-a — One-shot canonical-gate migration. Project the
|
|
538
|
+
// legacy `_contextOnly` / `_autoObserve` / `_manual` keys onto the
|
|
539
|
+
// canonical `contextOnly` field on every `projects/<name>/pull-
|
|
540
|
+
// requests.json` record BEFORE the first tick fires. `isAutoManagedPrRecord`
|
|
541
|
+
// now reads `contextOnly` only, so any record the migration doesn't reach
|
|
542
|
+
// before tick #1 would flip its auto-managed verdict. Idempotent.
|
|
543
|
+
try {
|
|
544
|
+
const projectsRoot = path.join(shared.MINIONS_DIR, 'projects');
|
|
545
|
+
shared.migratePrGateFlags(projectsRoot);
|
|
546
|
+
} catch (err) { e.log('warn', `pr-gate-migration failed: ${err.message}`); }
|
|
547
|
+
|
|
548
|
+
// One-time force-on of the CC worker pool. The pool has been the resolved
|
|
549
|
+
// default for copilot CC since PR #2492, but configs still carrying an
|
|
550
|
+
// explicit `ccUseWorkerPool: false` (set before the default flipped) stay
|
|
551
|
+
// opted out forever. Flip those to ON once, persisting the
|
|
552
|
+
// `engine._ccPoolForcedOnV1` marker so a deliberate later opt-out sticks.
|
|
553
|
+
// Disk-side re-derives from the on-disk copy so a concurrent dashboard
|
|
554
|
+
// write isn't clobbered; skipWriteIfUnchanged makes it a no-op once marked.
|
|
555
|
+
try {
|
|
556
|
+
const forced = shared.applyCcWorkerPoolForceOnMigration(config);
|
|
557
|
+
if (forced.changed) {
|
|
558
|
+
const configPath = path.join(shared.MINIONS_DIR, 'config.json');
|
|
559
|
+
shared.mutateJsonFileLocked(configPath, (onDisk) => {
|
|
560
|
+
shared.applyCcWorkerPoolForceOnMigration(onDisk);
|
|
561
|
+
return onDisk;
|
|
562
|
+
}, { defaultValue: {}, skipWriteIfUnchanged: true });
|
|
563
|
+
if (forced.flipped.length) {
|
|
564
|
+
e.log('info', `Forced CC worker pool ON — flipped explicit opt-out in: ${forced.flipped.join(', ')}`);
|
|
565
|
+
console.log(` Forced CC worker pool ON (was explicitly off in: ${forced.flipped.join(', ')}).`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} catch (err) { e.log('warn', `cc worker pool force-on migration failed: ${err.message}`); }
|
|
569
|
+
|
|
525
570
|
// Auto-heal projects missing workSources (cloned-repo / hand-rolled-config
|
|
526
571
|
// footgun): without this block, discoverFromWorkItems / discoverFromPrs
|
|
527
572
|
// bail silently and the engine looks healthy but never dispatches. The
|
|
@@ -1355,7 +1400,7 @@ const commands = {
|
|
|
1355
1400
|
},
|
|
1356
1401
|
|
|
1357
1402
|
complete(id) {
|
|
1358
|
-
if (!id) {
|
|
1403
|
+
if (!id || _isHelpArg(id)) {
|
|
1359
1404
|
console.log('Usage: minions complete <dispatch-id>');
|
|
1360
1405
|
return;
|
|
1361
1406
|
}
|
|
@@ -1398,7 +1443,7 @@ const commands = {
|
|
|
1398
1443
|
spawn(agentId, ...promptParts) {
|
|
1399
1444
|
const e = engine();
|
|
1400
1445
|
const prompt = promptParts.join(' ');
|
|
1401
|
-
if (!agentId || !prompt) {
|
|
1446
|
+
if (!agentId || _isHelpArg(agentId) || !prompt) {
|
|
1402
1447
|
console.log('Usage: node .minions/engine.js spawn <agent-id> "<prompt>"');
|
|
1403
1448
|
return;
|
|
1404
1449
|
}
|
|
@@ -1433,7 +1478,7 @@ const commands = {
|
|
|
1433
1478
|
|
|
1434
1479
|
work(title, ...rest) {
|
|
1435
1480
|
const e = engine();
|
|
1436
|
-
if (!title) {
|
|
1481
|
+
if (!title || _isHelpArg(title)) {
|
|
1437
1482
|
console.log('Usage: node .minions/engine.js work "<title>" [options-json]');
|
|
1438
1483
|
console.log('Options: {"id":"W-customid","type":"implement","priority":"high","agent":"dallas","description":"...","branch":"feature/...","project":"minions"}');
|
|
1439
1484
|
console.log(' id Optional caller-supplied work item ID. Defaults to a cuid-style W-<id>.');
|
|
@@ -1523,7 +1568,7 @@ const commands = {
|
|
|
1523
1568
|
|
|
1524
1569
|
plan(source, projectName) {
|
|
1525
1570
|
const e = engine();
|
|
1526
|
-
if (!source) {
|
|
1571
|
+
if (!source || _isHelpArg(source)) {
|
|
1527
1572
|
console.log('Usage: node .minions/engine.js plan <source> [project]');
|
|
1528
1573
|
console.log('');
|
|
1529
1574
|
console.log('Source can be:');
|
package/engine/features.js
CHANGED
|
@@ -32,6 +32,17 @@ const FEATURES = {
|
|
|
32
32
|
addedIn: '0.1.1757',
|
|
33
33
|
expires: '2026-11-01',
|
|
34
34
|
},
|
|
35
|
+
// slim-ux-promo — gates the Slim UX *entry point*, not Slim UX itself. When
|
|
36
|
+
// off (default) the "Try new Slim UX" button is hidden on the classic
|
|
37
|
+
// dashboard header AND the first-visit welcome popup is suppressed inside
|
|
38
|
+
// Slim UX. Slim UX stays reachable via the `slim-ux` flag / Settings; this
|
|
39
|
+
// only controls whether it's advertised to users.
|
|
40
|
+
'slim-ux-promo': {
|
|
41
|
+
description: 'Show the "Try new Slim UX" entry-point button on the classic dashboard and the first-visit welcome popup inside Slim UX. Off by default — Slim UX itself is still reachable via the slim-ux flag.',
|
|
42
|
+
default: false,
|
|
43
|
+
addedIn: '0.1.2057',
|
|
44
|
+
expires: '2026-11-01',
|
|
45
|
+
},
|
|
35
46
|
// ccUseWorkerPool — sub-tasks B/C/D of W-mp2w003600196c51 (CC perf).
|
|
36
47
|
// Routes Command Center / doc-chat through engine/cc-worker-pool.js
|
|
37
48
|
// (persistent `copilot --acp` per CC tab) instead of spawning a fresh CLI
|