amicus 4.4.0 → 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 +162 -0
- package/README.md +17 -2
- package/bin/amicus.js +10 -0
- package/docs/DISTRIBUTION.md +234 -0
- package/docs/ROADMAP.md +226 -0
- package/docs/SHIMS.md +62 -0
- package/docs/architecture.md +104 -0
- package/docs/configuration.md +395 -0
- package/docs/council.md +970 -0
- package/docs/doc-system.md +92 -0
- package/docs/electron-testing.md +471 -0
- package/docs/jsdoc-setup.md +75 -0
- package/docs/opencode-integration.md +114 -0
- package/docs/publishing.md +60 -0
- package/docs/schemas.md +56 -0
- package/docs/testing.md +589 -0
- package/docs/troubleshooting.md +298 -0
- package/docs/usage.md +849 -0
- package/electron/fold.js +1 -1
- package/electron/main.js +4 -1
- package/electron/setup-ui-aliases.js +6 -6
- package/electron/workspace-ui/live-model.js +12 -1
- package/electron/workspace-ui/md-lite.js +52 -8
- package/electron/workspace-ui/workspace-app.js +39 -17
- package/electron/workspace-ui/workspace-matrix.js +46 -9
- package/electron/workspace-ui/workspace-panels.js +88 -19
- package/electron/workspace-ui/workspace-render.js +17 -1
- package/electron/workspace-ui/workspace-verbs.js +48 -2
- package/package.json +8 -3
- package/schemas/council-run-live.schema.json +1 -1
- package/schemas/council-run.schema.json +34 -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 +13 -1
- package/schemas/run-live.schema.json +1 -1
- package/schemas/run.schema.json +2 -1
- package/schemas/spend.schema.json +52 -4
- 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-spend.js +20 -2
- package/src/cli-handlers-template.js +53 -0
- package/src/cli-handlers-watch.js +11 -0
- package/src/cli.js +68 -5
- package/src/council/briefings-debate.js +27 -7
- package/src/council/briefings-stage2.js +155 -25
- package/src/council/briefings.js +24 -1
- package/src/council/findings.js +199 -9
- package/src/council/parse-stage2.js +10 -2
- package/src/council/presets-cli.js +23 -11
- package/src/council/report.js +19 -8
- package/src/council/run-assemble.js +42 -1
- package/src/council/run-budget.js +64 -11
- package/src/council/run-chair.js +4 -1
- package/src/council/run-debate.js +4 -2
- package/src/council/run-finalize.js +102 -0
- package/src/council/run-launch.js +29 -1
- package/src/council/run-server.js +248 -0
- package/src/council/run-stage2.js +118 -0
- package/src/council/run-stages.js +134 -110
- package/src/council/run-state.js +40 -1
- package/src/council/run.js +45 -47
- package/src/council/tally.js +10 -0
- package/src/headless.js +180 -7
- package/src/mcp-council-run.js +108 -4
- package/src/mcp-server.js +203 -7
- package/src/mcp-tools.js +15 -5
- package/src/observe/council-legs.js +60 -3
- package/src/observe/live-doc.js +18 -1
- package/src/observe/watch-render.js +4 -1
- 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/child-sessions.js +1 -2
- package/src/sidecar/fanout-leg-fallback.js +69 -21
- package/src/sidecar/fanout-leg.js +6 -0
- package/src/sidecar/fanout-signals.js +61 -0
- package/src/sidecar/fanout-wave-io.js +75 -0
- package/src/sidecar/fanout.js +82 -74
- package/src/sidecar/progress-fields.js +26 -4
- package/src/sidecar/progress.js +42 -1
- package/src/sidecar/session-utils.js +23 -14
- 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/spend-query.js +17 -5
- 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/lifecycle.js +37 -1
- package/src/utils/path-fence.js +39 -1
- package/src/utils/pricing.js +26 -10
- package/src/utils/result-schema-rebuild.js +1 -0
- package/src/utils/result-schema.js +8 -2
- package/src/utils/server-setup.js +79 -1
- package/src/utils/spend-ledger.js +24 -3
- package/src/workspace/artifact-guard.js +66 -7
- package/src/workspace/fold-format.js +33 -4
- package/src/workspace/live-normalize.js +28 -15
- package/src/workspace/run-detail.js +13 -1
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": [
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"commands/",
|
|
44
44
|
".claude-plugin/",
|
|
45
45
|
"CHANGELOG.md",
|
|
46
|
+
"docs/*.md",
|
|
46
47
|
"scripts/postinstall.js",
|
|
47
48
|
"scripts/setup-hooks.js"
|
|
48
49
|
],
|
|
@@ -50,11 +51,11 @@
|
|
|
50
51
|
"start": "node --experimental-top-level-await --experimental-vm-modules bin/amicus.js",
|
|
51
52
|
"test": "jest",
|
|
52
53
|
"test:integration": "node scripts/run-integration-keyless.js",
|
|
53
|
-
"test:integration:live": "jest --testPathIgnorePatterns='worktrees' --testMatch='**/tests/**/*.integration.test.js'",
|
|
54
|
+
"test:integration:live": "jest --runInBand --testPathIgnorePatterns='worktrees' --testMatch='**/tests/**/*.integration.test.js'",
|
|
54
55
|
"test:all": "jest --testPathIgnorePatterns='/node_modules/' --testPathIgnorePatterns='worktrees' && node scripts/mark-test-passed.js",
|
|
55
56
|
"test:e2e:mcp": "jest tests/mcp-repomix-e2e.integration.test.js --testTimeout=180000 --forceExit",
|
|
56
57
|
"posttest": "node scripts/mark-test-passed.js",
|
|
57
|
-
"lint": "eslint src/",
|
|
58
|
+
"lint": "eslint src/ electron/",
|
|
58
59
|
"postinstall": "node scripts/postinstall.js",
|
|
59
60
|
"test:thinking": "node scripts/benchmark-thinking.js",
|
|
60
61
|
"test:thinking:quick": "MODELS=gemini node scripts/benchmark-thinking.js",
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
"ajv": "^8.20.0",
|
|
89
90
|
"chrome-remote-interface": "^0.33.3",
|
|
90
91
|
"eslint": "^8.0.0",
|
|
92
|
+
"espree": "^9.6.1",
|
|
91
93
|
"jest": "^29.0.0",
|
|
92
94
|
"lint-staged": "^16.3.2",
|
|
93
95
|
"puppeteer": "^24.36.0",
|
|
@@ -100,6 +102,9 @@
|
|
|
100
102
|
"lint-staged": {
|
|
101
103
|
"src/**/*.js": [
|
|
102
104
|
"eslint --fix"
|
|
105
|
+
],
|
|
106
|
+
"electron/**/*.js": [
|
|
107
|
+
"eslint --fix"
|
|
103
108
|
]
|
|
104
109
|
}
|
|
105
110
|
}
|
|
@@ -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": {
|
|
@@ -48,6 +62,26 @@
|
|
|
48
62
|
}
|
|
49
63
|
}
|
|
50
64
|
},
|
|
65
|
+
"sharedServer": {
|
|
66
|
+
"description": "The run acquired its single shared OpenCode server and threaded it through every wave (v4.4.1 Task 0.5). Present only on a successful acquisition; mutually exclusive with sharedServerUnavailable. `goPid` is the shared server's process id — no wave writes a goPid into its own metadata.json while riding an injected server, so this field is the POSITIVE, direct evidence that one server served the run, replacing the goPid-absence inference.",
|
|
67
|
+
"type": "object",
|
|
68
|
+
"required": ["acquired"],
|
|
69
|
+
"properties": {
|
|
70
|
+
"acquired": { "const": true },
|
|
71
|
+
"at": { "type": "string" },
|
|
72
|
+
"goPid": { "type": ["number", "null"] },
|
|
73
|
+
"models": { "type": "number" }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"sharedServerUnavailable": {
|
|
77
|
+
"description": "The run could not start its single shared OpenCode server and fell back to one server per wave (v4.4.1 Task 0.5). Present only when the acquisition failed. The run CONTINUES — this is never fatal — but the per-wave configuration is the one that races on OpenCode's SQLite, so results may be degraded.",
|
|
78
|
+
"type": "object",
|
|
79
|
+
"required": ["error"],
|
|
80
|
+
"properties": {
|
|
81
|
+
"error": { "type": "string" },
|
|
82
|
+
"at": { "type": "string" }
|
|
83
|
+
}
|
|
84
|
+
},
|
|
51
85
|
"debate": {
|
|
52
86
|
"type": "object",
|
|
53
87
|
"properties": {
|
|
@@ -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"],
|
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
"updatedAt": { "type": "string", "format": "date-time" },
|
|
13
13
|
"messagesReceived": { "type": "number" },
|
|
14
14
|
"latestTool": { "type": "string" },
|
|
15
|
+
"toolSettleTimedOut": {
|
|
16
|
+
"description": "v4.4 B4. The leg was completed while at least one tool call was still non-terminal, after AMICUS_TOOL_SETTLE_GRACE_MS elapsed. Its partial output was kept and it was NOT failed, but its reported cost is a floor. Present only when true.",
|
|
17
|
+
"const": true
|
|
18
|
+
},
|
|
19
|
+
"unsettledToolCalls": {
|
|
20
|
+
"description": "How many tool calls were still live when the settle grace was exceeded. A count here (progress.json is a compact snapshot); the full list rides the runHeadless result. Present only alongside toolSettleTimedOut.",
|
|
21
|
+
"type": "number"
|
|
22
|
+
},
|
|
23
|
+
"toolSettleAborted": {
|
|
24
|
+
"description": "v4.4.1 LC-2. Whether the leg's OpenCode session was successfully aborted at the settle ceiling so it would stop billing. `false` means the abort was attempted and failed — the session may still be billing. Present only alongside toolSettleTimedOut.",
|
|
25
|
+
"type": "boolean"
|
|
26
|
+
},
|
|
15
27
|
"usage": {
|
|
16
28
|
"type": "object",
|
|
17
29
|
"properties": {
|
|
@@ -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
|
}
|
|
@@ -9,8 +9,28 @@
|
|
|
9
9
|
"schemaVersion": { "const": 2 },
|
|
10
10
|
"type": { "const": "spend" },
|
|
11
11
|
"windowDays": { "type": ["number", "null"] },
|
|
12
|
-
"total": {
|
|
13
|
-
|
|
12
|
+
"total": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"amount": { "type": "number" },
|
|
16
|
+
"runs": { "type": "number" },
|
|
17
|
+
"unpricedRows": { "$ref": "#/$defs/unpricedRows" },
|
|
18
|
+
"unattributedSubtreeRows": { "$ref": "#/$defs/unattributedSubtreeRows" }
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"byModel": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"model": { "type": "string" },
|
|
27
|
+
"amount": { "type": "number" },
|
|
28
|
+
"runs": { "type": "number" },
|
|
29
|
+
"unpricedRows": { "$ref": "#/$defs/unpricedRows" },
|
|
30
|
+
"unattributedSubtreeRows": { "$ref": "#/$defs/unattributedSubtreeRows" }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
14
34
|
"credit": { "type": ["object", "null"] },
|
|
15
35
|
"filters": { "type": "object" },
|
|
16
36
|
"groupBy": { "enum": ["model", "wave", "council", "project", "op", "day"] },
|
|
@@ -22,7 +42,9 @@
|
|
|
22
42
|
"properties": {
|
|
23
43
|
"key": { "type": "string" },
|
|
24
44
|
"amount": { "type": "number" },
|
|
25
|
-
"runs": { "type": "number" }
|
|
45
|
+
"runs": { "type": "number" },
|
|
46
|
+
"unpricedRows": { "$ref": "#/$defs/unpricedRows" },
|
|
47
|
+
"unattributedSubtreeRows": { "$ref": "#/$defs/unattributedSubtreeRows" }
|
|
26
48
|
}
|
|
27
49
|
}
|
|
28
50
|
},
|
|
@@ -32,10 +54,36 @@
|
|
|
32
54
|
"properties": {
|
|
33
55
|
"amount": { "type": "number" },
|
|
34
56
|
"runs": { "type": "number" },
|
|
57
|
+
"unpricedRows": { "$ref": "#/$defs/unpricedRows" },
|
|
58
|
+
"unattributedSubtreeRows": { "$ref": "#/$defs/unattributedSubtreeRows" },
|
|
35
59
|
"byStatus": { "type": "object" }
|
|
36
60
|
}
|
|
37
61
|
},
|
|
38
|
-
"rows": {
|
|
62
|
+
"rows": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"description": "A raw spend-ledger.jsonl row (internal v1 shape, echoed verbatim when --rows is passed). Always an object: readSpendRows drops any ledger line that does not parse as one, scalars and arrays included. Only the fields this document makes claims about are pinned beyond that.",
|
|
67
|
+
"properties": {
|
|
68
|
+
"subtreeUnknown": {
|
|
69
|
+
"type": "boolean",
|
|
70
|
+
"description": "v4.4.1. Present (and always true) only when this leg's own cost resolved but it spawned a child session whose spend could not be determined — the row is PRICED yet its cost is a floor. OMITTED entirely otherwise, so an ordinary row is byte-identical to a pre-4.4.1 one."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
39
75
|
"rowsTruncated": { "type": "boolean" }
|
|
76
|
+
},
|
|
77
|
+
"$defs": {
|
|
78
|
+
"unpricedRows": {
|
|
79
|
+
"type": "integer",
|
|
80
|
+
"minimum": 0,
|
|
81
|
+
"description": "Rows that contributed NOTHING to `amount` (no numeric cost). The bucket's figure omits them entirely: real spend is at least this much."
|
|
82
|
+
},
|
|
83
|
+
"unattributedSubtreeRows": {
|
|
84
|
+
"type": "integer",
|
|
85
|
+
"minimum": 0,
|
|
86
|
+
"description": "v4.4.1 CA-2. Rows that ARE priced and DO contribute to `amount`, but whose leg spawned a subagent whose child-session spend could not be determined — the second, independent reason the figure is a floor. A row can be counted in both this and `unpricedRows`; the two are never substitutes."
|
|
87
|
+
}
|
|
40
88
|
}
|
|
41
89
|
}
|
|
@@ -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
|
}
|
|
@@ -211,6 +211,11 @@ mangled alias (instant arg-parse failure). For a free council, swap `--models "<
|
|
|
211
211
|
`--council free`. Run it in the background (`run_in_background: true`); you are notified on
|
|
212
212
|
completion — do not poll.
|
|
213
213
|
|
|
214
|
+
**Saved run configs (v4.5).** Every flag above this line can also come from a saved
|
|
215
|
+
[policy pack](../../docs/usage.md#policy-packs) — `--pack <name>` loads its bench/chair/critic/
|
|
216
|
+
lenses/options/template as defaults (an explicit flag still overrides it), the same on the
|
|
217
|
+
`amicus_council_run`/`amicus_fanout`/`amicus_start` MCP tools' `pack` param.
|
|
218
|
+
|
|
214
219
|
**Budget gate — one flag for the whole run.** By default the gate refuses any leg whose price
|
|
215
220
|
exceeds the per-$/Mtok threshold (the o3/o3-pro guard). To run an intentionally expensive model the
|
|
216
221
|
user asked for by name, pass `--no-cost-gate`; to raise only the total ceiling, pass
|
|
@@ -71,9 +71,11 @@ function resolveBench(args, useJson) {
|
|
|
71
71
|
if (expanded.dropped && expanded.dropped.length && !useJson) {
|
|
72
72
|
process.stderr.write(`Notice: dropped unavailable council member(s): ${expanded.dropped.join(', ')}\n`);
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
// v4.5 Wave 2: threaded into runCouncil's options — the ONLY prior signal
|
|
75
|
+
// was the stderr-only Notice above, which --json mode never even prints.
|
|
76
|
+
return { bench: expanded.models, presetName, droppedMembers: expanded.droppedMembers || [] };
|
|
75
77
|
}
|
|
76
|
-
return { bench: parseList(args.models), presetName: null };
|
|
78
|
+
return { bench: parseList(args.models), presetName: null, droppedMembers: [] };
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
function renderRunHuman(run) {
|
|
@@ -115,6 +117,27 @@ function renderRunHuman(run) {
|
|
|
115
117
|
async function handleCouncilRun(args) {
|
|
116
118
|
const useJson = !!args.json;
|
|
117
119
|
|
|
120
|
+
// v4.5 Task 12 (B7/F5): resolve --pack FIRST, above the Task-5 template
|
|
121
|
+
// block, so a pack-filled args.template renders through that single
|
|
122
|
+
// existing application point exactly like a typed --template.
|
|
123
|
+
let packRecord = null;
|
|
124
|
+
const explicitKeys = args.__explicit || new Set();
|
|
125
|
+
if (args.pack !== undefined) {
|
|
126
|
+
const { applyPackToArgs } = require('./pack/pack-resolve');
|
|
127
|
+
const pr = applyPackToArgs({
|
|
128
|
+
packRef: args.pack, expectedKind: 'council', args,
|
|
129
|
+
explicit: explicitKeys, useJson,
|
|
130
|
+
});
|
|
131
|
+
if (pr.error) { return failJson(useJson, pr.error); }
|
|
132
|
+
for (const n of pr.notices) { process.stderr.write(n + '\n'); }
|
|
133
|
+
packRecord = pr.packRecord;
|
|
134
|
+
}
|
|
135
|
+
// 2026-07-28 ruling (Task-11 review): attribute a pre-flight failure to the
|
|
136
|
+
// pack that supplied the failing value — ONLY when the pack filled it (an
|
|
137
|
+
// explicit flag always wins and is never "blamed" on the pack).
|
|
138
|
+
const packSuffix = (key) => (packRecord && args[key] !== undefined && !explicitKeys.has(key))
|
|
139
|
+
? ` (set by pack '${packRecord.name}')` : '';
|
|
140
|
+
|
|
118
141
|
// --prompt-file required; inline --prompt rejected (councils always have
|
|
119
142
|
// real briefings — same rationale as MCP fanout's briefing-via-file).
|
|
120
143
|
if (args.prompt !== undefined) {
|
|
@@ -123,9 +146,26 @@ async function handleCouncilRun(args) {
|
|
|
123
146
|
hint: 'write the briefing to a file and pass --prompt-file <path>' });
|
|
124
147
|
}
|
|
125
148
|
const { resolvePromptSource } = require('./utils/prompt-source');
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
149
|
+
// F9 (v4.5): with --template and no {{prompt}} slot, --prompt-file may be
|
|
150
|
+
// absent (mirrors handleFanout's guard); byte-identical without --template.
|
|
151
|
+
let promptRes;
|
|
152
|
+
if (args.prompt !== undefined || args['prompt-file'] !== undefined || args.template === undefined) {
|
|
153
|
+
promptRes = resolvePromptSource(args);
|
|
154
|
+
if (promptRes.error) { return failJson(useJson, { code: ERROR_CODES.MISSING_PROMPT, message: promptRes.error }); }
|
|
155
|
+
} else {
|
|
156
|
+
promptRes = { prompt: undefined, promptMeta: null };
|
|
157
|
+
}
|
|
158
|
+
let templateMeta = null;
|
|
159
|
+
if (args.template !== undefined) {
|
|
160
|
+
const { applyTemplate } = require('./template/apply');
|
|
161
|
+
const t = applyTemplate({ templateRef: args.template, prompt: promptRes.prompt,
|
|
162
|
+
artifactFile: args.artifact, varList: args.var, project: args.cwd || process.cwd() });
|
|
163
|
+
if (t.error) { return failJson(useJson, t.error); }
|
|
164
|
+
for (const n of t.notices) { process.stderr.write(n + '\n'); }
|
|
165
|
+
promptRes = { prompt: t.prompt, promptMeta: t.promptMeta };
|
|
166
|
+
templateMeta = t.promptMeta.template;
|
|
167
|
+
} else if (args.artifact !== undefined || args.var !== undefined) {
|
|
168
|
+
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: 'Error: --artifact/--var require --template (expansion happens only in template files)' });
|
|
129
169
|
}
|
|
130
170
|
|
|
131
171
|
const benchRes = resolveBench(args, useJson);
|
|
@@ -153,19 +193,19 @@ async function handleCouncilRun(args) {
|
|
|
153
193
|
? args.chair.trim() : CHAIR_DEFAULT;
|
|
154
194
|
if (bench.includes(chair)) {
|
|
155
195
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
156
|
-
message: `Error: chair '${chair}' is a bench seat — the chair must not review`,
|
|
196
|
+
message: `Error: chair '${chair}' is a bench seat — the chair must not review${packSuffix('chair')}`,
|
|
157
197
|
hint: `pick a chair outside --models (default: ${CHAIR_DEFAULT}), or remove '${chair}' from the bench` });
|
|
158
198
|
}
|
|
159
199
|
const critic = (typeof args.critic === 'string' && args.critic.trim()) ? args.critic.trim() : null;
|
|
160
200
|
if (critic && !bench.includes(critic)) {
|
|
161
201
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
162
|
-
message: `Error: critic '${critic}' must be one of the bench seats`,
|
|
202
|
+
message: `Error: critic '${critic}' must be one of the bench seats${packSuffix('critic')}`,
|
|
163
203
|
hint: `--critic swaps one seat's brief; pass one of: ${bench.join(', ')}` });
|
|
164
204
|
}
|
|
165
205
|
const lenses = (typeof args.lenses === 'string' && args.lenses.trim()) ? parseList(args.lenses) : null;
|
|
166
206
|
if (critic && lenses) {
|
|
167
207
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
168
|
-
message:
|
|
208
|
+
message: `Error: --critic and --lenses are mutually exclusive in v4.0${packSuffix('critic') || packSuffix('lenses')}` });
|
|
169
209
|
}
|
|
170
210
|
if (lenses && lenses.length !== bench.length) {
|
|
171
211
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
@@ -211,6 +251,9 @@ async function handleCouncilRun(args) {
|
|
|
211
251
|
noValidateModel: !!args['no-validate-model'],
|
|
212
252
|
date: new Date().toISOString().slice(0, 10),
|
|
213
253
|
councilName,
|
|
254
|
+
template: templateMeta, // F9 (v4.5): null when no --template; additive on the run.json seed (run-state.js).
|
|
255
|
+
pack: packRecord, // v4.5 Task 12 (B7/F5): null when no --pack; additive on the run.json seed (run-state.js).
|
|
256
|
+
droppedMembers: benchRes.droppedMembers, // v4.5 Wave 2: [] when nothing dropped; additive on the run.json seed (run-state.js).
|
|
214
257
|
// v4.1 §4.5b/§4.5d. `--claude-review` is resolved here but VALIDATED by the
|
|
215
258
|
// engine's preflightClaudeReview (run-assemble.js): the reserved-seat and
|
|
216
259
|
// 'claude may not chair' guards live there on purpose so MCP, the GitHub
|