amicus 4.4.1 → 4.5.0
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +130 -0
- package/README.md +15 -2
- package/bin/amicus.js +10 -0
- package/docs/ROADMAP.md +36 -10
- package/docs/configuration.md +24 -0
- package/docs/council.md +59 -0
- package/docs/schemas.md +1 -0
- package/docs/usage.md +151 -1
- package/electron/workspace-ui/workspace-app.js +39 -17
- package/electron/workspace-ui/workspace-panels.js +76 -18
- package/electron/workspace-ui/workspace-render.js +10 -0
- package/package.json +1 -1
- package/schemas/council-run-live.schema.json +1 -1
- package/schemas/council-run.schema.json +14 -0
- package/schemas/error.schema.json +1 -1
- package/schemas/event.schema.json +1 -1
- package/schemas/pack.schema.json +30 -0
- package/schemas/progress.schema.json +1 -1
- package/schemas/run-live.schema.json +1 -1
- package/schemas/run.schema.json +2 -1
- package/schemas/wave-live.schema.json +1 -1
- package/schemas/wave.schema.json +2 -1
- package/skills/second-opinion/SKILL.md +5 -0
- package/src/cli-handlers-council-run.js +51 -8
- package/src/cli-handlers-pack.js +238 -0
- package/src/cli-handlers-run.js +36 -8
- package/src/cli-handlers-template.js +53 -0
- package/src/cli.js +64 -3
- package/src/council/findings.js +4 -41
- package/src/council/presets-cli.js +23 -11
- package/src/council/run-stages.js +12 -9
- package/src/council/run-state.js +17 -0
- package/src/council/run.js +1 -1
- package/src/headless.js +18 -14
- package/src/mcp-council-run.js +108 -4
- package/src/mcp-server.js +203 -7
- package/src/mcp-tools.js +15 -5
- package/src/pack/pack-cli.js +38 -0
- package/src/pack/pack-forward.js +96 -0
- package/src/pack/pack-resolve.js +297 -0
- package/src/pack/pack-store.js +130 -0
- package/src/pack/pack-validate.js +113 -0
- package/src/sidecar/fanout.js +21 -4
- package/src/sidecar/progress.js +34 -0
- package/src/sidecar/start.js +5 -4
- package/src/sidecar/workspace-auto-open.js +69 -0
- package/src/sidecar/workspace-window.js +46 -1
- package/src/template/apply.js +88 -0
- package/src/template/render.js +86 -0
- package/src/template/store.js +106 -0
- package/src/utils/config.js +65 -25
- package/src/utils/error-doc.js +5 -0
- package/src/utils/result-schema-rebuild.js +1 -0
- package/src/utils/result-schema.js +8 -2
- package/src/workspace/artifact-guard.js +44 -6
- package/src/workspace/run-detail.js +6 -0
package/docs/usage.md
CHANGED
|
@@ -42,6 +42,13 @@ amicus council run --prompt-file <b.md> --models a,b,c --chair <m> [--json] # H
|
|
|
42
42
|
amicus council save <name> --models a,b,c # Save a named council preset (>=2 resolvable members)
|
|
43
43
|
amicus council list [--json] # List saved councils + built-ins (free/budget/frontier)
|
|
44
44
|
amicus council show <name> [--json] # Resolve a council (saved or built-in) and show its members
|
|
45
|
+
amicus template list [--json] # List briefing templates (built-ins marked)
|
|
46
|
+
amicus template show <name|path> [--json] # Print a template's raw text
|
|
47
|
+
amicus pack save <name> --kind council|fanout|solo [flags] # Save a full run config (bench/options/template)
|
|
48
|
+
amicus pack save <name> --from-run <id> # ...or build one from an existing run/wave/session
|
|
49
|
+
amicus pack list [--json] # List saved packs
|
|
50
|
+
amicus pack show <name|path> [--json] # Print a pack + its validation report
|
|
51
|
+
amicus pack rm <name> [--json] # Remove a saved pack
|
|
45
52
|
```
|
|
46
53
|
|
|
47
54
|
---
|
|
@@ -85,6 +92,10 @@ amicus start --model deepseek --prompt "Generate tests" --no-ui --timeout 30
|
|
|
85
92
|
| `--setup` | Force-open configuration before launching. Does **not** relax the `--prompt`/`--prompt-file` requirement — `start --setup` still fails fast with "Error: --prompt or --prompt-file is required" if neither is given. | |
|
|
86
93
|
| `--no-validate-model` | Skip model-catalog validation before launch. | validation on |
|
|
87
94
|
| `--gateway <mode>` | Routing override for this launch: `auto` (direct-first), `direct` (require a direct provider key), or `openrouter` (force OpenRouter). Overrides `routing.prefer` for one call. | `auto` |
|
|
95
|
+
| `--pack <name\|path>` | Load a saved [policy pack](#policy-packs) — its `model`/options/template fill in for anything you didn't type explicitly. | |
|
|
96
|
+
| `--template <name\|path>` | Render a [briefing template](#briefing-templates) (`{{prompt}}`, `{{artifact}}`, `{{artifact_path}}`, `{{date}}`, `{{project}}`, `{{var.*}}`). | |
|
|
97
|
+
| `--artifact <file>` | File whose content fills `{{artifact}}`/`{{artifact_path}}` (256 KB cap). Requires `--template`. | |
|
|
98
|
+
| `--var <k=v>` | Set `{{var.<key>}}`; repeatable. Requires `--template`. | |
|
|
88
99
|
|
|
89
100
|
> Agents: **Chat** auto-approves reads and asks before writes/bash (interactive default); **Build** has full tool access (headless default); **Plan** is read-only analysis. `--agent Chat` is interactive-only and incompatible with `--no-ui`.
|
|
90
101
|
|
|
@@ -119,6 +130,10 @@ amicus fanout --council free --prompt "Review this design" --json
|
|
|
119
130
|
| `--no-cost-gate` | Disable the budget gate (per-$/Mtok threshold + ceiling) for this run. |
|
|
120
131
|
| `--no-validate-model` | Skip catalog validation. |
|
|
121
132
|
| `--gateway <mode>` | Routing override applied to every leg: `auto` (direct-first), `direct`, or `openrouter`. |
|
|
133
|
+
| `--pack <name\|path>` | Load a saved [policy pack](#policy-packs) — its bench/options/template fill in for anything you didn't type explicitly. |
|
|
134
|
+
| `--template <name\|path>` | Render a [briefing template](#briefing-templates) (`{{prompt}}`, `{{artifact}}`, `{{artifact_path}}`, `{{date}}`, `{{project}}`, `{{var.*}}`), shared by every leg. |
|
|
135
|
+
| `--artifact <file>` | File whose content fills `{{artifact}}`/`{{artifact_path}}` (256 KB cap). Requires `--template`. |
|
|
136
|
+
| `--var <k=v>` | Set `{{var.<key>}}`; repeatable. Requires `--template`. |
|
|
122
137
|
|
|
123
138
|
**Shared per-leg knobs.** Every leg in the wave also accepts the same per-leg options as `start`:
|
|
124
139
|
`--agent`, `--thinking`, `--timeout`, `--summary-length`, `--no-context`, `--context-*`, `--mcp*`,
|
|
@@ -180,6 +195,10 @@ amicus council run --prompt-file briefing.md --models gemini,glm --chair deepsee
|
|
|
180
195
|
| `--debate` | Adds a Stage-2.5 rebuttal round (provisional tally → defense → re-vote → final tally) between cross-review and the final tally. |
|
|
181
196
|
| `--claude-review <file>` | Enters Claude's own review from a file as judged review N+1 — no leg is ever launched for it; `claude` is a reserved seat name and may not also appear in `--models`, `--chair`, or `--critic` (pre-flight error). |
|
|
182
197
|
| `--no-cost-gate` | Disable the per-leg price gate for the whole run (repairs + chair). |
|
|
198
|
+
| `--pack <name\|path>` | Load a saved [policy pack](#policy-packs) — its bench/chair/critic/lenses/options/template fill in for anything you didn't type explicitly. |
|
|
199
|
+
| `--template <name\|path>` | Render a [briefing template](#briefing-templates) (`{{prompt}}`, `{{artifact}}`, `{{artifact_path}}`, `{{date}}`, `{{project}}`, `{{var.*}}`). |
|
|
200
|
+
| `--artifact <file>` | File whose content fills `{{artifact}}`/`{{artifact_path}}` (256 KB cap). Requires `--template`. |
|
|
201
|
+
| `--var <k=v>` | Set `{{var.<key>}}`; repeatable. Requires `--template`. |
|
|
183
202
|
|
|
184
203
|
**Exit codes:** `0` full run · `2` degraded but reportable (fewer than 2 judges, chair failure —
|
|
185
204
|
`overallVerdict: null` — a cost ceiling hit after the tally, or a `--max-cost` ceiling set over a
|
|
@@ -192,6 +211,14 @@ Field-by-field run-directory contents, the degradation table, and `verdict.json`
|
|
|
192
211
|
`overallVerdict` are documented in **[docs/council.md](./council.md#amicus-council-run)**. This is
|
|
193
212
|
the command the repo's Council Review GitHub Action (v2) runs on labeled PRs.
|
|
194
213
|
|
|
214
|
+
**Auto-open the Council Workspace (v4.5).** When this run is launched through the `amicus_council_run`
|
|
215
|
+
**MCP tool** from Claude Code (local), the same Council Workspace window that `amicus watch <runId>
|
|
216
|
+
--ui` opens by hand also opens automatically, detached, right after the run starts — no extra call
|
|
217
|
+
needed to watch it live. The plain CLI invocation above is unaffected either way: there is no MCP client to
|
|
218
|
+
detect on that path. Full decision order (the `ui` MCP param, the `workspace.autoOpen` config key,
|
|
219
|
+
and the four guards) is in **[docs/council.md's Council Workspace
|
|
220
|
+
section](./council.md#council-workspace-gui)**.
|
|
221
|
+
|
|
195
222
|
---
|
|
196
223
|
|
|
197
224
|
## `amicus council save|list|show` — Council Presets
|
|
@@ -217,6 +244,129 @@ amicus council show budget [--json] # Works on built-ins
|
|
|
217
244
|
|
|
218
245
|
---
|
|
219
246
|
|
|
247
|
+
## Briefing templates
|
|
248
|
+
|
|
249
|
+
Render a `{{variable}}` briefing before it's sent — for `start`, `fanout`, and `council run` alike.
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
amicus template list [--json] # Built-ins marked [built-in]; a same-named user file shadows one
|
|
253
|
+
amicus template show review # Print a template's raw text
|
|
254
|
+
amicus start --model gemini --template review --artifact plan.md --var focus=performance --no-ui --json
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Known variables** (`src/template/render.js`): `{{prompt}}`, `{{artifact}}`, `{{artifact_path}}`, `{{date}}` (`YYYY-MM-DD`), `{{project}}`, `{{var.<key>}}` (from repeatable `--var key=value`). There is no `{{input}}` in v4.5 — that chaining variable, and the `critique`/`refine` built-ins that need it, arrive with v4.6's composable waves.
|
|
258
|
+
|
|
259
|
+
**Strict by design — a typo fails loudly instead of silently dropping text:**
|
|
260
|
+
|
|
261
|
+
| Situation | Result |
|
|
262
|
+
|---|---|
|
|
263
|
+
| Template uses `{{var.foo}}`, no `--var foo=...` given | Error (`TEMPLATE_RENDER`) |
|
|
264
|
+
| `--var foo=...` given, template never uses `{{var.foo}}` | Notice (not an error) — printed to stderr |
|
|
265
|
+
| Template uses `{{prompt}}`, no `--prompt`/`--prompt-file` given | Error |
|
|
266
|
+
| `--prompt`/`--prompt-file` given, template has no `{{prompt}}` slot | Error — the text would be silently dropped |
|
|
267
|
+
| Template uses `{{artifact}}`/`{{artifact_path}}`, no `--artifact` given | Error |
|
|
268
|
+
| `--artifact` given, template has no `{{artifact}}`/`{{artifact_path}}` slot | Error |
|
|
269
|
+
| An unrecognized `{{name}}` appears anywhere in the template | Error — lists the known variables |
|
|
270
|
+
|
|
271
|
+
**Where templates live.** Markdown files in `~/.config/amicus/templates/<name>.md` — the name is the filename minus `.md`. A user file **shadows** a built-in of the same name (`amicus template list` marks it `[shadows built-in]`); there is no `template save`/`rm` — your editor is the manager. v4.5 ships one built-in, `review` (asks for a severity-tagged, artifact-grounded review ending in a one-paragraph verdict).
|
|
272
|
+
|
|
273
|
+
**`--artifact <file>`** reads a file (256 KB cap) into `{{artifact}}` (its content) and `{{artifact_path}}` (its resolved path) — pass the plan/diff/design you want reviewed as a file instead of pasting it into `--prompt`.
|
|
274
|
+
|
|
275
|
+
**MCP.** None of the three run tools (`amicus_start`, `amicus_fanout`, `amicus_council_run`) have a `template` param of their own — a [policy pack](#policy-packs)'s `briefing.template` is the only way a template reaches an MCP-invoked run, rendered against that call's own briefing text at the same single application point a typed `--template` would use.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Policy packs
|
|
280
|
+
|
|
281
|
+
Save a full run configuration — bench, chair/critic/lenses, options, briefing template — and invoke it by name instead of re-typing every flag.
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
amicus pack save <name> --kind council|fanout|solo [flags] # build from flags
|
|
285
|
+
amicus pack save <name> --from-run <id> # build from an existing run/wave/session
|
|
286
|
+
amicus pack list [--json]
|
|
287
|
+
amicus pack show <name|path> [--json]
|
|
288
|
+
amicus pack rm <name> [--json]
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Then invoke it with `--pack <name|path>` on `start` / `fanout` / `council run` — or the `pack` param on the `amicus_start` / `amicus_fanout` / `amicus_council_run` MCP tools.
|
|
292
|
+
|
|
293
|
+
**What a pack can hold, per `kind`:**
|
|
294
|
+
|
|
295
|
+
| Kind | Bench field | Kind-specific fields | Allowed `options.*` |
|
|
296
|
+
|---|---|---|---|
|
|
297
|
+
| `council` | `bench` (a saved council name, or an array of ≥2 members) | `chair`, `critic`, `lenses` | `timeout`, `maxCost`, `gateway`, `debate` |
|
|
298
|
+
| `fanout` | `bench` (a saved council name, or an array of ≥2 members) | — | `timeout`, `maxCost`, `gateway`, `agent`, `thinking`, `summaryLength`, `noContext`, `contextTurns`, `contextMaxTokens` |
|
|
299
|
+
| `solo` | `model` | — | `timeout`, `maxCost`, `gateway`, `agent`, `thinking`, `summaryLength`, `noUi`, `noContext`, `contextTurns`, `contextMaxTokens` |
|
|
300
|
+
|
|
301
|
+
`council` packs do **not** accept `agent`, `thinking`, or `summaryLength` — they were inert on every surface (no council code path, CLI or MCP, ever reads a pack-filled one; the engine hardcodes agent `Plan`/summaryLength `verbose`), so they were dropped before release rather than shipped as dead weight a pack author would reasonably expect to work. A `council` pack that still sets one fails `pack save` with `PACK_INVALID`, naming the key. They remain valid, and functional, on `fanout`/`solo` packs.
|
|
302
|
+
|
|
303
|
+
Every kind may also carry `description`, `version` (semver, default `1.0.0`), and `briefing.template` (a template **reference**, not rendered text — a pack never captures briefing prose).
|
|
304
|
+
|
|
305
|
+
**Precedence: flag > pack > config default > built-in default.** A pack only fills in values you didn't type explicitly on the command line — anything you do pass always wins, and the pack is recorded on the run either way (see below), so a hand-tuned invocation of a saved pack is never ambiguous about what actually ran.
|
|
306
|
+
|
|
307
|
+
**`--from-run <id>`** builds a pack from an existing council run, fanout wave, or solo session instead of flags — resolution order is council pointer → wave `metadata.json` → solo `metadata.json`. It captures the bench/model, chair/critic/lenses, and the run options that were actually used; **briefing text is never captured**, only a template *reference* when the source run recorded one.
|
|
308
|
+
|
|
309
|
+
**Where packs live.** One JSON file per pack, `~/.config/amicus/packs/<name>.json`. Re-saving an unchanged pack is a no-op; saving a changed pack under an unchanged version string auto-bumps its patch version instead of silently overwriting history. Every pack also carries a content hash (sha256 of its canonical, sorted-key JSON form, first 12 hex chars) computed fresh on every read — a hand-edited pack whose `version` field you forgot to bump still gets a distinct hash on any run that used it.
|
|
310
|
+
|
|
311
|
+
**Recorded on every run, whether or not any value was actually overridden:** `pack: {name, version, hash, source}` lands on the resulting solo session `metadata.json`, wave `metadata.json`/`wave.json`, or council `run.json` — `source` is `"dir"` for a saved pack invoked by name, `"path"` for one loaded by file path.
|
|
312
|
+
|
|
313
|
+
**Error codes**, all through the standard `--json` error envelope:
|
|
314
|
+
|
|
315
|
+
| Situation | Code |
|
|
316
|
+
|---|---|
|
|
317
|
+
| `pack show <missing>` | `PACK_NOT_FOUND` |
|
|
318
|
+
| `pack rm <missing>` | `PACK_NOT_FOUND` |
|
|
319
|
+
| `pack save` fails validation | `PACK_INVALID` (hard-fail; non-fatal warnings still print to stderr) |
|
|
320
|
+
| `--pack <name>` at run time is the wrong `kind` (e.g. a `solo` pack passed to `council run`) | `PACK_KIND_MISMATCH` |
|
|
321
|
+
| `pack save --from-run <unknown id>` | `BAD_SESSION` |
|
|
322
|
+
|
|
323
|
+
**Over MCP, pack resolution happens entirely in-process** — `amicus_start`/`amicus_fanout`/`amicus_council_run` never spawn a child with `--pack`; the pack's values are merged onto that call's own input before validation, exactly as they would be for a typed param. Two knobs get special handling for CLI parity: a pack's `options.maxCost` and `briefing.template` have no schema param of their own on `amicus_start`/`amicus_fanout` (neither tool exposes either directly), but they still apply — forwarded to the spawned CLI child's argv as `--max-cost`/`--template` (`amicus_fanout` always spawns; `amicus_start`'s spawn-fallback path does the same), or, on `amicus_start`'s in-process shared-server path, applied via the same budget-gate/template-render code the CLI itself uses, before any session is created. (`amicus_council_run` already has real MCP params for both, so this forwarding never triggers there.) Any *other* pack knob with nowhere to land in a given tool's own MCP schema is never silently dropped either — it comes back as an explicit `Notice: pack '<name>' sets <key>, which <tool> does not support over MCP — ignored.` content block, naming the pack's own camelCase option key (e.g. `contextTurns`, never the CLI's `context-turns`). Concretely, `amicus_fanout` has no MCP destination for `options.contextTurns`/`options.contextMaxTokens` (both notice); `amicus_start` has real `contextTurns`/`contextMaxTokens` params, so no notice there. `council` packs cannot carry `agent`/`thinking`/`summaryLength` at all (see above), so there is nothing left to orphan on that surface.
|
|
324
|
+
|
|
325
|
+
### Worked example — save, inspect, invoke
|
|
326
|
+
|
|
327
|
+
Run end to end against the real CLI (a scratch config dir, so paths below are shown in their normal
|
|
328
|
+
`~/.config/amicus` form rather than the test scratch path):
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
$ amicus pack save review-bench --kind council \
|
|
332
|
+
--bench gemini,deepseek,gpt --chair opus \
|
|
333
|
+
--timeout 20 --max-cost 2 --description "Standard 3-model review bench"
|
|
334
|
+
Saved pack 'review-bench' v1.0.0 → ~/.config/amicus/packs/review-bench.json
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
$ amicus pack show review-bench
|
|
339
|
+
Pack 'review-bench' v1.0.0 [council] (dir: ~/.config/amicus/packs/review-bench.json)
|
|
340
|
+
hash: da084ba56162
|
|
341
|
+
description: Standard 3-model review bench
|
|
342
|
+
bench: gemini, deepseek, gpt
|
|
343
|
+
chair: opus
|
|
344
|
+
options: {"timeout":20,"maxCost":2}
|
|
345
|
+
validation: ok
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
$ amicus council run --pack review-bench --prompt-file briefing.md --out-dir council-run --json
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
No `--models`/`--chair`/`--timeout`/`--max-cost` needed on that last line — they all came from the
|
|
353
|
+
pack. Confirmed against the run's own `run.json` for this exact invocation (irrelevant keys elided):
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"bench": ["gemini", "deepseek", "gpt"],
|
|
358
|
+
"chair": "opus",
|
|
359
|
+
"pack": { "name": "review-bench", "hash": "da084ba56162", "source": "dir" },
|
|
360
|
+
"options": { "timeout": 20, "maxCost": 2, "gateway": "auto", "outDir": "..." }
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Adding an explicit flag overrides just that one value — `... --pack review-bench --chair gpt-pro`
|
|
365
|
+
keeps the pack's bench and cost/timeout options but chairs with `gpt-pro` instead of `opus`, and the
|
|
366
|
+
pack is still recorded on the run either way.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
220
370
|
## `amicus models` — The Model Catalog
|
|
221
371
|
|
|
222
372
|
Amicus does **not** ship a frozen table of model names. Aliases and validation resolve against a **live catalog** fetched from provider APIs and cached at `~/.config/amicus/model-catalog.json` (24-hour TTL; the fetch works without an API key).
|
|
@@ -293,7 +443,7 @@ $ amicus status demo123 --json
|
|
|
293
443
|
"taskId": "demo123",
|
|
294
444
|
"status": "complete",
|
|
295
445
|
"elapsed": "5m 0s",
|
|
296
|
-
"version": "4.
|
|
446
|
+
"version": "4.5.0",
|
|
297
447
|
"model": "google/gemini-2.5-flash",
|
|
298
448
|
"phase": "terminal"
|
|
299
449
|
}
|
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
// null on a non-debate run, an aborted/skipped debate, or a parse failure —
|
|
28
28
|
// drillIntoJudge's judge-*.md fallback covers all three.
|
|
29
29
|
blind: false,
|
|
30
|
+
// Task 19 (RN-5) + fix-wave (RN-5 amendment): the (run id, status) pair renderDetail() last
|
|
31
|
+
// computed state.blind's default for. Together they gate the recompute (in renderDetail(),
|
|
32
|
+
// below) to a run CHANGE or a STATUS change only: a same-run/same-status re-render (the
|
|
33
|
+
// blind toggle) keeps the user's own choice, while a same-run/CHANGED-status re-render (the
|
|
34
|
+
// live loop's running -> terminal refresh, or the abort-confirm re-read) still auto-reveals.
|
|
35
|
+
detailRunId: null,
|
|
36
|
+
detailRunStatus: null,
|
|
30
37
|
labelByModel: {},
|
|
31
38
|
listTimer: null,
|
|
32
39
|
liveTimer: null, // LIVE (Task 15)
|
|
@@ -103,8 +110,23 @@
|
|
|
103
110
|
return;
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
// blind default: computed
|
|
107
|
-
|
|
113
|
+
// blind default: computed from status (resolution 9) — only on a run CHANGE or a STATUS
|
|
114
|
+
// change for the same run. Task 19 (RN-5): a same-run/same-status re-render
|
|
115
|
+
// (renderDetail_preserveBlind, below, calls straight back in here on every blind toggle)
|
|
116
|
+
// must keep the user's own choice instead of recomputing the default every call —
|
|
117
|
+
// recomputing unconditionally is what forced the old code to paint twice and, via
|
|
118
|
+
// wireLazyPanels() a few lines down, collapse any lazy panel the user had open.
|
|
119
|
+
// ⚠️ Fix-wave (RN-5 amendment, controller ruling): run id ALONE also suppressed the
|
|
120
|
+
// pre-existing running -> terminal auto-reveal, since the live loop's terminal refresh
|
|
121
|
+
// (workspace-verbs.js's startLiveLoop tick) and the abort-confirm re-read both call
|
|
122
|
+
// openRun() on the SAME run id — same run, but a real status transition. Keying on run id
|
|
123
|
+
// AND status recomputes (auto-reveals) on that transition while still preserving a same-
|
|
124
|
+
// run/same-status call (the blind toggle).
|
|
125
|
+
if (state.detailRunId !== d.runId || state.detailRunStatus !== d.run.status) {
|
|
126
|
+
state.blind = window.AmicusLive.defaultBlind(d.run.status);
|
|
127
|
+
state.detailRunId = d.runId;
|
|
128
|
+
state.detailRunStatus = d.run.status;
|
|
129
|
+
}
|
|
108
130
|
$('blind-toggle').checked = state.blind;
|
|
109
131
|
state.labelByModel = {};
|
|
110
132
|
d.derived.names.forEach(function (p) { state.labelByModel[p.model] = p.label; });
|
|
@@ -178,7 +200,6 @@
|
|
|
178
200
|
});
|
|
179
201
|
|
|
180
202
|
function renderDetail_preserveBlind() {
|
|
181
|
-
var keep = state.blind;
|
|
182
203
|
// ⚠️ Fix-wave item 1: renderDetail() itself early-returns safely for an unreadable run
|
|
183
204
|
// (!d || d.error || !d.derived) — but this wrapper used to run past that guard
|
|
184
205
|
// unconditionally, dereferencing the (nonexistent) derived model via renderSeatsPanel()
|
|
@@ -187,22 +208,22 @@
|
|
|
187
208
|
// the error branch unhides #run-view before the derived-model guard, so the Blind
|
|
188
209
|
// checkbox is live with nothing behind it.
|
|
189
210
|
if (!state.detail || state.detail.error || !state.detail.derived) {
|
|
190
|
-
$('blind-toggle').checked =
|
|
211
|
+
$('blind-toggle').checked = state.blind;
|
|
191
212
|
return;
|
|
192
213
|
}
|
|
214
|
+
// ⚠️ Task 19 (RN-5): this used to call renderDetail() (which unconditionally stomped
|
|
215
|
+
// state.blind back to the run's status default), restore the user's pre-call value,
|
|
216
|
+
// and then re-paint header chips/seats/matrix/verdict/cost a SECOND time to compensate
|
|
217
|
+
// for the first call having painted with the wrong (default) blind state — a double
|
|
218
|
+
// paint, and (via wireLazyPanels(), called inside that first renderDetail()) a collapse
|
|
219
|
+
// of any lazy panel the user had open. Fixed at the root instead of compensated for:
|
|
220
|
+
// renderDetail() now recomputes the blind default only on a run CHANGE or a STATUS change
|
|
221
|
+
// (state.detailRunId/state.detailRunStatus, above) — this call changes neither, so it
|
|
222
|
+
// keeps state.blind exactly as the change listener above just set it, and
|
|
223
|
+
// workspace-panels.js's wireLazyPanels() (its own same-run guard, `lastWiredRunId`)
|
|
224
|
+
// refreshes any open panel in place rather than collapsing it (fix-wave, Fix 1). One
|
|
225
|
+
// renderDetail() call now paints correctly the first time — nothing left to restore.
|
|
193
226
|
renderDetail();
|
|
194
|
-
state.blind = keep;
|
|
195
|
-
$('blind-toggle').checked = keep;
|
|
196
|
-
// ⚠️ R4 COUNCIL REVIEW (fourth live paid council, major, unanimous): renderDetail() (just
|
|
197
|
-
// called above) resets state.blind to the run's DEFAULT before this function restores the
|
|
198
|
-
// user's chosen `keep` value — renderHeaderChips was painted during that inner call with
|
|
199
|
-
// the (temporarily wrong) default blind state and, unlike seats/matrix/verdict/cost below,
|
|
200
|
-
// was never repainted afterward. Re-render it here too, now that state.blind is correct.
|
|
201
|
-
window.AmicusRender.renderHeaderChips($('run-chips'), state.detail.run, state.blind, labelOf);
|
|
202
|
-
P.renderSeatsPanel();
|
|
203
|
-
P.renderMatrixPanel();
|
|
204
|
-
P.renderVerdictPanel();
|
|
205
|
-
window.AmicusRender.renderCost($('cost-body'), state.detail.derived.cost, state.blind, labelOf);
|
|
206
227
|
}
|
|
207
228
|
|
|
208
229
|
$('run-list').addEventListener('keydown', function (e) {
|
|
@@ -227,7 +248,8 @@
|
|
|
227
248
|
|
|
228
249
|
// ⚠️ PRE-FLIGHT (P4) + DE-ROT (F09): register the three prose `toggle` listeners exactly ONCE,
|
|
229
250
|
// here at boot. wireLazyPanels() (called from renderDetail, on every run-open and blind-toggle)
|
|
230
|
-
//
|
|
251
|
+
// rewrites the per-run `loaders` spec map every call and — fix-wave, Fix 1 — refreshes any
|
|
252
|
+
// already-open lazy panel in place on a same-run call; it never adds a listener.
|
|
231
253
|
P.proseLoader('reviews-panel');
|
|
232
254
|
P.proseLoader('bundle-panel');
|
|
233
255
|
P.proseLoader('judges-panel');
|
|
@@ -19,6 +19,24 @@
|
|
|
19
19
|
// inverted back to the model id that keys state.labelByModel, so blind labels would break.
|
|
20
20
|
function sanitizeName(model) { return String(model).replace(/[^a-zA-Z0-9._-]/g, '-'); }
|
|
21
21
|
|
|
22
|
+
// ⚠️ Task 18 (RN-1): review-/judge- filenames used to be recomputed here via a bare
|
|
23
|
+
// sanitizeName(model) call, which is NOT injective — two distinct bench models that sanitize
|
|
24
|
+
// to the same name would both resolve to the SAME filename, so drillIntoJudge's
|
|
25
|
+
// `[data-artifact="..."]` lookup handed back whichever section matched first (model A's prose
|
|
26
|
+
// rendered under model B's name). derived.artifactsByModel (src/workspace/artifact-guard.js's
|
|
27
|
+
// artifactAllowlist, threaded through by run-detail.js) already carries the disambiguated
|
|
28
|
+
// (possibly `~2`/`~3`-suffixed) name per raw model — consult it FIRST. Fall back to the legacy
|
|
29
|
+
// computation only when the map itself is absent: older detail payloads (pre-v4.5 runs,
|
|
30
|
+
// live-doc consumers not yet updated to build the map) never carry it, and re-deriving via
|
|
31
|
+
// sanitizeName is exactly what those payloads always did, so it stays correct for them too.
|
|
32
|
+
function resolveArtifactName(model, kind) {
|
|
33
|
+
var A = window.AmicusApp;
|
|
34
|
+
var map = A.state.detail.derived && A.state.detail.derived.artifactsByModel;
|
|
35
|
+
var entry = map && map[model];
|
|
36
|
+
if (entry && entry[kind]) { return entry[kind]; }
|
|
37
|
+
return kind + '-' + sanitizeName(model) + '.md';
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
function renderSeatsPanel() {
|
|
23
41
|
var A = window.AmicusApp;
|
|
24
42
|
var d = A.state.detail;
|
|
@@ -60,12 +78,16 @@
|
|
|
60
78
|
// ⚠️ PRE-FLIGHT (P4): the load is AWAITABLE — drillIntoJudge needs to know when it has
|
|
61
79
|
// settled (the old code guessed with setTimeout(render, 300), which could fire before an
|
|
62
80
|
// unbounded N-artifact IPC round trip finished and silently render nothing). loadPanel()
|
|
63
|
-
// is idempotent per panel id and returns its in-flight promise;
|
|
64
|
-
// (`loading`) and the per-run spec (`loaders`) are keyed by panel id
|
|
65
|
-
// by wireLazyPanels() on
|
|
66
|
-
// artifact requests.
|
|
81
|
+
// is idempotent per panel id and returns its in-flight promise; the promise cache
|
|
82
|
+
// (`loading`) and the per-run spec (`loaders`) are both keyed by panel id, but only
|
|
83
|
+
// `loading` is cleared by wireLazyPanels() — on a run CHANGE only (Task 19, RN-5) — and
|
|
84
|
+
// that clearing is what stops F09's stale-run artifact requests.
|
|
67
85
|
var loaders = {}; // panelId -> {bodyId, files} (rewritten per run by wireLazyPanels)
|
|
68
86
|
var loading = {}; // panelId -> Promise (cleared per run by wireLazyPanels)
|
|
87
|
+
// Task 19 (RN-5): the run wireLazyPanels() last reset panels/loading for — gates the reset
|
|
88
|
+
// below to run CHANGES only. A same-run call (renderDetail() runs this on every blind toggle
|
|
89
|
+
// too, and on the live loop's terminal refresh) instead refreshes any open panel (Fix 1).
|
|
90
|
+
var lastWiredRunId = null;
|
|
69
91
|
|
|
70
92
|
function loadPanel(panelId, bodyId, files) {
|
|
71
93
|
var A = window.AmicusApp;
|
|
@@ -107,18 +129,31 @@
|
|
|
107
129
|
}
|
|
108
130
|
|
|
109
131
|
/**
|
|
110
|
-
* Rewrites the per-run spec map
|
|
111
|
-
*
|
|
112
|
-
*
|
|
132
|
+
* Rewrites the per-run spec map on every call. On a run CHANGE (tracked via the module-level
|
|
133
|
+
* `lastWiredRunId`, above), resets panel open/loaded state and drops the previous run's
|
|
134
|
+
* cached load promises — exactly what F09's stale-run protection needs. On a SAME-run call
|
|
135
|
+
* (Task 19, RN-5: renderDetail() calls this on every blind toggle too, and the live loop's
|
|
136
|
+
* terminal refresh) any panel the user already has open is instead refreshed in place — see
|
|
137
|
+
* Fix 1 below — never left showing stale-blind content, never collapsed. Registers no
|
|
138
|
+
* listeners itself.
|
|
113
139
|
*/
|
|
114
140
|
function wireLazyPanels() {
|
|
115
141
|
var A = window.AmicusApp;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
142
|
+
// ⚠️ Fix-wave (Fix 4): keyed off `A.state.detail.runId`, not `A.state.runId` — the latter is
|
|
143
|
+
// set synchronously at the top of openRun(), before its workspace:get-run reply lands, so an
|
|
144
|
+
// out-of-order reply could make the two diverge. workspace-app.js's own run-change gate
|
|
145
|
+
// (renderDetail(), above `d.runId`) reads off the SAME `state.detail.runId`, so the two
|
|
146
|
+
// provably agree on whether this is a run change.
|
|
147
|
+
var sameRun = A.state.detail.runId === lastWiredRunId;
|
|
148
|
+
if (!sameRun) {
|
|
149
|
+
['reviews-panel', 'bundle-panel', 'judges-panel'].forEach(function (id) {
|
|
150
|
+
var p = A.$(id);
|
|
151
|
+
p.dataset.loaded = '0';
|
|
152
|
+
p.open = false;
|
|
153
|
+
delete loading[id];
|
|
154
|
+
});
|
|
155
|
+
lastWiredRunId = A.state.detail.runId;
|
|
156
|
+
}
|
|
122
157
|
var bench = A.state.detail.run.bench || [];
|
|
123
158
|
var debated = !!A.state.detail.run.debate;
|
|
124
159
|
// ⚠️ CODE REVIEW (round 2, finding 2): readRunArtifact's error for a genuinely-missing
|
|
@@ -141,7 +176,7 @@
|
|
|
141
176
|
loaders['reviews-panel'] = { bodyId: 'reviews-body', files: function () {
|
|
142
177
|
return bench.map(function (m) {
|
|
143
178
|
var label = A.state.labelByModel[m];
|
|
144
|
-
return { name:
|
|
179
|
+
return { name: resolveArtifactName(m, 'review'), title: window.AmicusRender.display({ model: m, label: label }, A.state.blind) };
|
|
145
180
|
}).filter(function (f) { return present(f.name); });
|
|
146
181
|
} };
|
|
147
182
|
loaders['bundle-panel'] = { bodyId: 'bundle-body', files: function () {
|
|
@@ -157,7 +192,7 @@
|
|
|
157
192
|
loaders['judges-panel'] = { bodyId: 'judges-body', files: function () {
|
|
158
193
|
var files = bench.map(function (m) {
|
|
159
194
|
var label = A.state.labelByModel[m];
|
|
160
|
-
return { name:
|
|
195
|
+
return { name: resolveArtifactName(m, 'judge'), title: 'Judge ' + window.AmicusRender.display({ model: m, label: label }, A.state.blind) };
|
|
161
196
|
});
|
|
162
197
|
if (debated) {
|
|
163
198
|
// ⚠️ DE-ROT (F38): on a --debate run, a matrix dispute cell can be a RE-VOTE whose
|
|
@@ -168,13 +203,31 @@
|
|
|
168
203
|
// judge- titles above, which mirror the brief verbatim), so it goes through
|
|
169
204
|
// AmicusRender.display() — the single blind-flip definition — rather than adding a
|
|
170
205
|
// fourth hand-rolled copy of the same ternary.
|
|
206
|
+
// ⚠️ Task 18 fix-wave (RN-1, review finding 1): this name used to be recomputed via a
|
|
207
|
+
// bare sanitizeName(m) call, ignoring the disambiguation map entirely — for a colliding
|
|
208
|
+
// pair BOTH models resolved to the same bare revote-<sanitized>.md name, reintroducing
|
|
209
|
+
// for re-votes the exact cross-match bug Task 18 fixed for review-/judge-. Routed
|
|
210
|
+
// through resolveArtifactName(m, 'revote') like the other three sites; its built-in
|
|
211
|
+
// legacy fallback keeps older detail payloads (no artifactsByModel map) correct too.
|
|
171
212
|
files = files.concat(bench.map(function (m) {
|
|
172
213
|
var label = A.state.labelByModel[m];
|
|
173
|
-
return { name:
|
|
214
|
+
return { name: resolveArtifactName(m, 'revote'), title: 'Re-vote ' + window.AmicusRender.display({ model: m, label: label }, A.state.blind) };
|
|
174
215
|
}));
|
|
175
216
|
}
|
|
176
217
|
return files.filter(function (f) { return present(f.name); });
|
|
177
218
|
} };
|
|
219
|
+
// ⚠️ Fix-wave (Fix 1, RN-9): a same-run call (the blind toggle, or the live loop's
|
|
220
|
+
// running -> terminal refresh) must re-render any panel the user already has open, or it
|
|
221
|
+
// keeps showing content painted under the PREVIOUS blind state. renderProseSections()
|
|
222
|
+
// (workspace-render.js) clears its container before repainting, so this replaces sections
|
|
223
|
+
// in place rather than appending duplicates. Drop the cached promise first so loadPanel()
|
|
224
|
+
// actually re-fetches instead of returning its already-settled one.
|
|
225
|
+
if (sameRun) {
|
|
226
|
+
['reviews-panel', 'bundle-panel', 'judges-panel'].forEach(function (id) {
|
|
227
|
+
var p = A.$(id);
|
|
228
|
+
if (p.open) { delete loading[id]; loadPanel(id, loaders[id].bodyId, loaders[id].files); }
|
|
229
|
+
});
|
|
230
|
+
}
|
|
178
231
|
}
|
|
179
232
|
|
|
180
233
|
// ⚠️ DE-ROT (F38): on a --debate run the FINAL tally.json is rebuilt from the debate's
|
|
@@ -194,9 +247,14 @@
|
|
|
194
247
|
var rv = ((A.state.debate && A.state.debate.revotes) || []).find(function (r) {
|
|
195
248
|
return r.judge === judgePair.model && r.id === findingId;
|
|
196
249
|
});
|
|
250
|
+
// ⚠️ Task 18 fix-wave (RN-1, review finding 1): this branch used to recompute the name via
|
|
251
|
+
// bare sanitizeName(judgePair.model), independently of the (already-fixed) judge branch
|
|
252
|
+
// right below it — for a colliding pair, drilling a re-vote on the SECOND model resolved
|
|
253
|
+
// to the bare name and cross-matched the FIRST model's genuine revote section. Both arms
|
|
254
|
+
// of this ternary now go through the same disambiguation-aware helper.
|
|
197
255
|
var artifactName = rv
|
|
198
|
-
?
|
|
199
|
-
:
|
|
256
|
+
? resolveArtifactName(judgePair.model, 'revote')
|
|
257
|
+
: resolveArtifactName(judgePair.model, 'judge');
|
|
200
258
|
var section = A.$('judges-body').querySelector('[data-artifact="' + artifactName + '"]');
|
|
201
259
|
// A genuinely absent artifact is not an error here — the panel renders its own
|
|
202
260
|
// "<file> not written yet" empty state (spec §9, last row).
|
|
@@ -205,8 +205,18 @@
|
|
|
205
205
|
cells.forEach(function (c, i) {
|
|
206
206
|
var td = row.children[i];
|
|
207
207
|
if (td && td.textContent !== c) { td.textContent = c; }
|
|
208
|
+
if (td) { td.className = i >= 4 && i <= 6 ? 'num' : (i === 8 ? 'stalled-flag' : ''); }
|
|
208
209
|
});
|
|
209
210
|
});
|
|
211
|
+
// RN-11 (v4.5): the keyed update added and removed rows but never MOVED
|
|
212
|
+
// them, so table order was frozen at first render — wrong the moment a
|
|
213
|
+
// repair solo or new wave changes the composed doc's leg order mid-run.
|
|
214
|
+
seats.forEach(function (seat, i) {
|
|
215
|
+
var key = String(seat.id || seat.model);
|
|
216
|
+
var current = tbody.children[i];
|
|
217
|
+
var target = Array.prototype.slice.call(tbody.children).find(function (r) { return r.dataset.key === key; });
|
|
218
|
+
if (target && target !== current) { tbody.insertBefore(target, current || null); }
|
|
219
|
+
});
|
|
210
220
|
Array.prototype.slice.call(tbody.children).forEach(function (row) {
|
|
211
221
|
if (!seen[row.dataset.key]) { row.remove(); }
|
|
212
222
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"mcpName": "io.github.BourbonDog/amicus",
|
|
5
5
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
6
6
|
"keywords": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/council-run-live.schema.json",
|
|
4
4
|
"title": "Amicus composed live council-run doc (amicus_status/buildCouncilStatusPayload, view:'live')",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"required": ["taskId", "type", "runId", "runDir", "status", "currentStage", "stages", "legsTotal", "legsComplete", "elapsed", "exitCode", "version"],
|
|
@@ -30,8 +30,22 @@
|
|
|
30
30
|
"chair": { "type": "string" },
|
|
31
31
|
"critic": { "type": ["string", "null"] },
|
|
32
32
|
"lenses": { "type": ["array", "null"], "items": { "type": "string" } },
|
|
33
|
+
"droppedMembers": {
|
|
34
|
+
"description": "Council preset members dropped during resolution before this run started (v4.5 Wave 2): an alias that no longer resolves, or a resolved id absent from a non-empty cached model catalog (a catalog-unknown/offline member is never dropped this way). Present only when at least one member was actually dropped; absent — never an empty array — otherwise. `bench` above already reflects the survivors only; this is purely an additional observability signal so a scripted/MCP caller can detect a shrunken bench without diffing `bench` against the preset's nominal member list.",
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["member", "reason"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"member": { "type": "string" },
|
|
41
|
+
"reason": { "type": "string" }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
33
45
|
"labelMap": { "type": ["object", "null"], "additionalProperties": { "type": "string" } },
|
|
34
46
|
"options": { "type": "object" },
|
|
47
|
+
"pack": { "type": "object" },
|
|
48
|
+
"template": { "type": "object" },
|
|
35
49
|
"usage": { "type": "object" },
|
|
36
50
|
"exitCode": { "type": ["number", "null"] },
|
|
37
51
|
"budgetRefusals": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"type": "object",
|
|
14
14
|
"required": ["code", "message"],
|
|
15
15
|
"properties": {
|
|
16
|
-
"code": { "enum": ["BAD_ARGS", "MISSING_PROMPT", "BAD_MODEL", "MISSING_KEY", "BAD_SESSION", "BUDGET_EXCEEDED", "INTERNAL", "COUNCIL_QUORUM", "COST_EXCEEDED", "COUNCIL_CLAUDE_REVIEW_INVALID"] },
|
|
16
|
+
"code": { "enum": ["BAD_ARGS", "MISSING_PROMPT", "BAD_MODEL", "MISSING_KEY", "BAD_SESSION", "BUDGET_EXCEEDED", "INTERNAL", "COUNCIL_QUORUM", "COST_EXCEEDED", "COUNCIL_CLAUDE_REVIEW_INVALID", "TEMPLATE_NOT_FOUND", "TEMPLATE_RENDER", "PACK_NOT_FOUND", "PACK_INVALID", "PACK_KIND_MISMATCH"] },
|
|
17
17
|
"message": { "type": "string" },
|
|
18
18
|
"hint": { "type": ["string", "null"] },
|
|
19
19
|
"command": { "type": ["string", "null"] }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/event.schema.json",
|
|
4
4
|
"title": "Amicus observability event (events.jsonl line)",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"required": ["schemaVersion", "type", "event", "ts", "id"],
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/pack.schema.json",
|
|
4
|
+
"title": "Amicus policy pack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "type", "name", "version", "kind"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": { "const": 1 },
|
|
9
|
+
"type": { "const": "pack" },
|
|
10
|
+
"name": { "type": "string", "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$" },
|
|
11
|
+
"version": { "type": "string" },
|
|
12
|
+
"kind": { "enum": ["council", "fanout", "solo"] },
|
|
13
|
+
"description": { "type": "string" },
|
|
14
|
+
"bench": {
|
|
15
|
+
"oneOf": [
|
|
16
|
+
{ "type": "string" },
|
|
17
|
+
{ "type": "array", "items": { "type": "string" }, "minItems": 2 }
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"model": { "type": "string" },
|
|
21
|
+
"chair": { "type": ["string", "null"] },
|
|
22
|
+
"critic": { "type": ["string", "null"] },
|
|
23
|
+
"lenses": { "type": ["array", "null"], "items": { "type": "string" } },
|
|
24
|
+
"options": { "type": "object" },
|
|
25
|
+
"briefing": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": { "template": { "type": ["string", "null"] } }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/progress.schema.json",
|
|
4
4
|
"title": "Amicus leg/solo progress snapshot (progress.json)",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"required": ["schemaVersion", "type", "stage", "updatedAt"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/run-live.schema.json",
|
|
4
4
|
"title": "Amicus composed live single-session doc (amicus_status, view:'live')",
|
|
5
5
|
"description": "amicus_status's single-session response always stamps type:'run' (stampEnvelope); documented here as a const rather than the brief's 'may be absent' guess to match real output.",
|
|
6
6
|
"type": "object",
|
package/schemas/run.schema.json
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"durationMs": { "type": ["number", "null"] },
|
|
22
22
|
"sessionDir": { "type": ["string", "null"] },
|
|
23
23
|
"opencodeSessionId": { "type": ["string", "null"] },
|
|
24
|
-
"usage": { "type": ["object", "null"] }
|
|
24
|
+
"usage": { "type": ["object", "null"] },
|
|
25
|
+
"pack": { "type": "object" }
|
|
25
26
|
}
|
|
26
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/wave-live.schema.json",
|
|
4
4
|
"title": "Amicus composed live wave doc (amicus_status, view:'live')",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"required": ["taskId", "type", "status", "legs"],
|
package/schemas/wave.schema.json
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"completedAt": { "type": ["string", "null"] },
|
|
29
29
|
"durationMs": { "type": ["number", "null"] },
|
|
30
30
|
"usage": { "type": "object" },
|
|
31
|
-
"notices": { "type": "array", "items": { "type": "string" } }
|
|
31
|
+
"notices": { "type": "array", "items": { "type": "string" } },
|
|
32
|
+
"pack": { "type": "object" }
|
|
32
33
|
}
|
|
33
34
|
}
|