@bramburn/pi-model-council 1.6.2 → 1.6.11
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/CHANGELOG.md +342 -0
- package/CODE_OF_CONDUCT.md +134 -0
- package/CONTRIBUTING.md +128 -0
- package/DISCLAIMER.md +62 -0
- package/LICENSE +21 -0
- package/README.md +460 -0
- package/SECURITY.md +80 -0
- package/SUPPORT.md +72 -0
- package/commandParser.ts +333 -0
- package/councilRunner.ts +499 -0
- package/index.ts +304 -0
- package/markdown.ts +113 -0
- package/openrouterClient.ts +244 -0
- package/package.json +76 -1
- package/prompts.ts +299 -0
- package/retry.ts +92 -0
- package/runnerHelpers.ts +130 -0
- package/schemas.ts +44 -0
- package/searchSelector.ts +313 -0
- package/secondOpinionRunner.ts +203 -0
- package/settings-ui.ts +488 -0
- package/settings.ts +135 -0
- package/structuredOutput.ts +500 -0
- package/types.ts +169 -0
- package/index.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Bhavesh Ramburn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# model-council
|
|
2
|
+
|
|
3
|
+
Pi extension for multi-model coding decisions via OpenRouter.
|
|
4
|
+
|
|
5
|
+
Ask three independent AI models for a second opinion, then have a fourth model synthesise them into a single actionable plan. Use the fast `/opinion` command for quick checks, and `/council` for higher-stakes architectural decisions.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Multi-model council** — Three OpenRouter models deliberate independently, then a fourth model (you pick) synthesises the final decision. Returned as a structured plan you (or Pi) can implement.
|
|
10
|
+
- **Single-model opinions** — Get a quick second opinion from any model with valid auth, using Pi's built-in model registry.
|
|
11
|
+
- **Pi-native auth** — Re-uses `OPENROUTER_API_KEY` (or `/login openrouter`) so the API key lives in one place. No separate key prompt required when Pi already knows OpenRouter.
|
|
12
|
+
- **Persistent settings** — Stored in `~/.pi/agent/council-settings.json` (or project-scoped when trusted). Includes the three council members, the synthesis model, and the opinion model.
|
|
13
|
+
- **Secure** — Settings are gitignored; keys never logged.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
`pi install` accepts three source types: **npm**, **git**, and **local path**. Pick the one that matches how you want to consume the extension.
|
|
20
|
+
|
|
21
|
+
### Prerequisites
|
|
22
|
+
|
|
23
|
+
- **Pi** installed (`pi --version` should print ≥ 1.0)
|
|
24
|
+
- **Node.js 22+** (matches the extension's CI runner)
|
|
25
|
+
- An **OpenRouter API key** — get one at [openrouter.ai/keys](https://openrouter.ai/keys)
|
|
26
|
+
|
|
27
|
+
### Pick the install method
|
|
28
|
+
|
|
29
|
+
| Use case | Command | Where it lands |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| **npm** (recommended once published) | `pi install npm:pi-model-council` | `~/.pi/agent/npm/pi-model-council` |
|
|
32
|
+
| **GitHub via SSH** (most common) | `pi install git:github.com/bramburn/pi-model-council` | `~/.pi/agent/git/github.com/bramburn/pi-model-council` |
|
|
33
|
+
| **GitHub via HTTPS + PAT** (private repos, CI) | `pi install https://x-access-token:<TOKEN>@github.com/bramburn/pi-model-council.git` | `~/.pi/agent/git/github.com/bramburn/pi-model-council` |
|
|
34
|
+
| **Local clone** (development) | `pi install ./pi-model-council` | registered in-place, not copied |
|
|
35
|
+
| **Try without installing** (one-shot) | `pi -e git:github.com/bramburn/pi-model-council` | temp directory |
|
|
36
|
+
|
|
37
|
+
Pin to a specific tag for reproducibility (recommended for shared / CI use):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pi install git:github.com/bramburn/pi-model-council@v1.5.0
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Detailed walkthroughs
|
|
44
|
+
|
|
45
|
+
#### Option 1 — npm (once the package is published)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pi install npm:pi-model-council
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Updates use the regular npm flow: `pi update --extensions`.
|
|
52
|
+
|
|
53
|
+
#### Option 2 — GitHub via SSH
|
|
54
|
+
|
|
55
|
+
If you've already added an SSH key to your GitHub account (the same key you'd use for `git push` to this repo), Pi uses it automatically:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pi install git:github.com/bramburn/pi-model-council
|
|
59
|
+
# Or pinned to a tag:
|
|
60
|
+
pi install git:github.com/bramburn/pi-model-council@v1.5.0
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The clone is placed under `~/.pi/agent/git/github.com/bramburn/pi-model-council/`.
|
|
64
|
+
|
|
65
|
+
#### Option 3 — GitHub via HTTPS with a Personal Access Token
|
|
66
|
+
|
|
67
|
+
Use this when SSH isn't an option. Create a **fine-grained** token at <https://github.com/settings/tokens?type=beta> with **read** access scoped to just `bramburn/pi-model-council`:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pi install https://x-access-token:<TOKEN>@github.com/bramburn/pi-model-council.git
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> The PAT will be embedded in `~/.pi/agent/settings.json`. The settings file is created with `0600` permissions, but treat it as a secret and don't commit it.
|
|
74
|
+
|
|
75
|
+
#### Option 4 — Local clone (recommended for development)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Clone once anywhere
|
|
79
|
+
git clone https://github.com/bramburn/pi-model-council.git ~/code/pi-model-council
|
|
80
|
+
cd ~/code/pi-model-council
|
|
81
|
+
npm install
|
|
82
|
+
|
|
83
|
+
# Register the local path with pi (no copy — edits take effect immediately)
|
|
84
|
+
pi install ./pi-model-council
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
To update later:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cd ~/code/pi-model-council
|
|
91
|
+
git pull && npm install
|
|
92
|
+
# The next pi launch picks up the new code automatically; no reinstall needed.
|
|
93
|
+
# If you changed pi-extension metadata, force a refresh:
|
|
94
|
+
pi update --self --force
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Option 5 — Try without installing (one-shot smoke test)
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pi -e git:github.com/bramburn/pi-model-council
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Pi clones to a temp directory and uses the extension for the current run only. Nothing is written to your settings.
|
|
104
|
+
|
|
105
|
+
### Project-scoped install (team sharing)
|
|
106
|
+
|
|
107
|
+
By default, `pi install` writes to **user** settings (`~/.pi/agent/settings.json`). To write to **project** settings (`.pi/settings.json`) so the install is checked into your team's repo and auto-applied when a teammate opens the project:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pi install -l git:github.com/bramburn/pi-model-council@v1.5.0
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
After this, commit `.pi/settings.json` to your project. When a teammate clones and Pi trusts the project, the extension installs automatically.
|
|
114
|
+
|
|
115
|
+
### Verify the install
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pi list # shows installed packages and their sources
|
|
119
|
+
pi config # toggle the extension on/off
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
You should see `pi-model-council` listed and enabled. Then launch Pi and try:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pi
|
|
126
|
+
> /council-settings
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If the council UI opens, the extension is loaded correctly.
|
|
130
|
+
|
|
131
|
+
### Updating an installed version
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Update every installed extension to the ref pinned in settings
|
|
135
|
+
pi update --extensions
|
|
136
|
+
|
|
137
|
+
# Update only this one to a new tag/commit
|
|
138
|
+
pi install git:github.com/bramburn/pi-model-council@v1.6.0 # overwrites
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Uninstalling
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pi remove pi-model-council
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Or manually delete the entry from `~/.pi/agent/settings.json` and remove the clone under `~/.pi/agent/git/github.com/bramburn/pi-model-council/`. Your settings file (`council-settings.json`) is kept — re-installing will pick them back up.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Workflow: Getting Started in 3 Steps
|
|
152
|
+
|
|
153
|
+
The whole setup takes about a minute. Follow this order — each step builds on the previous one.
|
|
154
|
+
|
|
155
|
+
### Step 1 — Authenticate with OpenRouter (one-time)
|
|
156
|
+
|
|
157
|
+
Pi already ships with an OpenRouter provider. Give it your API key once and Pi will list every supported OpenRouter model under `provider === "openrouter"`.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Option A — environment variable (recommended for CI / dotfiles)
|
|
161
|
+
export OPENROUTER_API_KEY="sk-or-v1-..."
|
|
162
|
+
|
|
163
|
+
# Option B — interactive login (stores in ~/.pi/agent/auth.json with 0600 perms)
|
|
164
|
+
# Inside Pi, run:
|
|
165
|
+
/login openrouter
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Get a key from [openrouter.ai/keys](https://openrouter.ai/keys).
|
|
169
|
+
|
|
170
|
+
Verify Pi sees the models:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pi --list-models
|
|
174
|
+
# Look for entries like:
|
|
175
|
+
# openrouter/anthropic/claude-3.5-sonnet
|
|
176
|
+
# openrouter/openai/gpt-4o
|
|
177
|
+
# openrouter/qwen/qwen3.7-max
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Step 2 — Configure the extension
|
|
181
|
+
|
|
182
|
+
Run the council settings UI. Pi will detect OpenRouter in your model registry and skip the API-key prompt entirely.
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
/council-settings
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The UI uses a **typeahead-searchable, scrollable picker** (same UX as `/model`), so you can filter through hundreds of OpenRouter models by typing a few characters — fuzzy-matched across model name and id. Up/Down to navigate, Enter to select, Esc to cancel.
|
|
189
|
+
|
|
190
|
+
The UI walks you through:
|
|
191
|
+
|
|
192
|
+
1. **Council Model 1 of 3** — first dissenting voice
|
|
193
|
+
2. **Council Model 2 of 3** — second dissenting voice (already-picked models are filtered out)
|
|
194
|
+
3. **Council Model 3 of 3** — third dissenting voice
|
|
195
|
+
4. **Synthesis Model** — reads all three opinions and writes the final plan. Defaults to "Council Model 1" since you already trust it; pick any OpenRouter model you like.
|
|
196
|
+
5. **Second Opinion Model** — the model used by `/opinion` for quick checks
|
|
197
|
+
6. **Structured Output** — JSON schema for faster parsing (recommended: yes)
|
|
198
|
+
|
|
199
|
+
If Pi doesn't see OpenRouter yet (because you skipped Step 1), the UI falls back to a manual flow: it asks for an API key, pings OpenRouter to verify, fetches the live model list, and proceeds the same way.
|
|
200
|
+
|
|
201
|
+
Re-run anytime to swap models:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
/council-settings list # show current config
|
|
205
|
+
/council-settings reset # wipe and reconfigure
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
For just the opinion model:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
/opinion-settings
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Step 3 — Use the council
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Three deliberating models + one synthesis model
|
|
218
|
+
/council fix "The login fails on mobile devices"
|
|
219
|
+
/council ask "Should we use hooks or context for state?"
|
|
220
|
+
/council architecture "Where should auth state live?"
|
|
221
|
+
|
|
222
|
+
# Single-model quick check (uses your /opinion model)
|
|
223
|
+
/opinion "How should I refactor this function?"
|
|
224
|
+
/opinion fix "Lesson progress resets after navigation"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The full report is saved to `.pi/council/last-decision.md` (and `.pi/council/last-opinion.md` for `/opinion`). The Pi agent can read this file when continuing the conversation.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## How Model Selection Works
|
|
232
|
+
|
|
233
|
+
The extension uses two layers:
|
|
234
|
+
|
|
235
|
+
| Layer | Source | Purpose |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| **Discovery** | `ctx.modelRegistry.getAvailable()` | Lists every OpenRouter model Pi knows about (the curated list ships with Pi). Filtered to `provider === "openrouter"`. |
|
|
238
|
+
| **Validation** | Same registry, then OpenRouter `/models` fallback | Confirms each chosen model ID still exists at runtime. |
|
|
239
|
+
| **Inference** | Direct OpenRouter REST call (`openrouterClient.ts`) | Sends the council prompts to each model and the synthesis prompt to the chosen synthesiser. |
|
|
240
|
+
|
|
241
|
+
This means:
|
|
242
|
+
|
|
243
|
+
- If Pi already knows about OpenRouter (Step 1 above), `ctx.modelRegistry.getAvailable()` provides the model list — no extra HTTP call.
|
|
244
|
+
- If the registry is empty (e.g. fresh install, no API key yet), the UI prompts for a key and fetches the live model list directly from OpenRouter.
|
|
245
|
+
- The chosen model IDs are stored in `council-settings.json` and used to make the actual inference calls. They are not changed when you swap Pi providers.
|
|
246
|
+
|
|
247
|
+
### Why a separate Synthesis Model?
|
|
248
|
+
|
|
249
|
+
The three council members argue from different angles; the synthesis model is the judge that weighs them and produces a single plan. By default it's "Council Model 1" because you already trust that model. For higher-stakes decisions, pick a model known for careful reasoning (e.g. `anthropic/claude-3.5-sonnet`, `openai/o1`) — it's a 4th API call on top of the three council calls.
|
|
250
|
+
|
|
251
|
+
### The 4-Model Orchestration Flow
|
|
252
|
+
|
|
253
|
+
When you run `/council`, a single `council_decide` tool call internally fans out to four models. Pi sees only the final synthesised report — all sub-calls are hidden behind one logical tool:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
/council fix "..." (slash command)
|
|
257
|
+
│
|
|
258
|
+
▼
|
|
259
|
+
┌───────────────────────────────┐
|
|
260
|
+
│ council_decide tool │ (single Pi tool call)
|
|
261
|
+
│ councilRunner.ts │
|
|
262
|
+
└───────────────────────────────┘
|
|
263
|
+
│
|
|
264
|
+
┌─────────────────┼─────────────────┐
|
|
265
|
+
│ │ │
|
|
266
|
+
▼ ▼ ▼
|
|
267
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
268
|
+
│ Model 1 │ │ Model 2 │ │ Model 3 │ ← Promise.all
|
|
269
|
+
│ (council)│ │ (council)│ │ (council)│ parallel
|
|
270
|
+
└────┬─────┘ └────┬─────┘ └────┬─────┘ fan-out
|
|
271
|
+
│ │ │
|
|
272
|
+
▼ ▼ ▼
|
|
273
|
+
opinion A opinion B opinion C (JSON)
|
|
274
|
+
│ │ │
|
|
275
|
+
└──────────────────┼──────────────────┘
|
|
276
|
+
▼
|
|
277
|
+
┌──────────────┐
|
|
278
|
+
│ Model 4 │ ← synthesis step
|
|
279
|
+
│ (synthesis) │ reads 3 opinions
|
|
280
|
+
└──────┬───────┘ writes 1 decision
|
|
281
|
+
▼
|
|
282
|
+
┌──────────────┐
|
|
283
|
+
│ Pi receives │ ← tool result returned to agent
|
|
284
|
+
│ Markdown │
|
|
285
|
+
│ report │
|
|
286
|
+
└──────────────┘
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Key properties:**
|
|
290
|
+
|
|
291
|
+
- All four calls happen **inside one tool execution**, so the agent sees a single tool call in its reasoning graph — no multi-turn coordination overhead.
|
|
292
|
+
- The three council calls run **in parallel** via `Promise.all`. Each call has its own `withTimeout` + `retry` wrapper, so a slow or failing model doesn't block the others.
|
|
293
|
+
- If 1 of 3 council models fails, the synthesis step still proceeds with the 2 successful opinions. If all 3 fail, the runner surfaces a clear error.
|
|
294
|
+
- The synthesis call has a longer timeout (`synthesisTimeoutMs`) because the synthesis prompt includes all three opinions.
|
|
295
|
+
- `ctx.signal` is threaded through every downstream `fetch()`, so pressing Esc during a long council cancels all in-flight calls (Pi's `withTimeout` helper combines the parent signal with per-call timeouts).
|
|
296
|
+
|
|
297
|
+
### Where the API Key Comes From
|
|
298
|
+
|
|
299
|
+
Resolution order:
|
|
300
|
+
|
|
301
|
+
1. `council-settings.json` → `openRouter.apiKey` (the legacy explicit-prompt flow)
|
|
302
|
+
2. Pi's auth storage → `modelRegistry.getApiKeyForProvider("openrouter")` (when you've set `OPENROUTER_API_KEY` or run `/login openrouter`)
|
|
303
|
+
3. `process.env.OPENROUTER_API_KEY`
|
|
304
|
+
|
|
305
|
+
If none of these resolve, `/council` and `/opinion` both fail fast with a setup error pointing you at `/council-settings`.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Commands
|
|
310
|
+
|
|
311
|
+
### `/council`
|
|
312
|
+
|
|
313
|
+
Three OpenRouter models + one synthesis model.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
/council fix "The login fails on mobile devices"
|
|
317
|
+
/council ask "Should we use hooks or context for state?"
|
|
318
|
+
/council architecture "Where should auth state live?"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Modes:
|
|
322
|
+
|
|
323
|
+
- `fix` — debug a known problem
|
|
324
|
+
- `ask` — open technical question
|
|
325
|
+
- `architecture` — design-level decision
|
|
326
|
+
- (default) — generic second opinion
|
|
327
|
+
|
|
328
|
+
### `/opinion`
|
|
329
|
+
|
|
330
|
+
Single-model quick check.
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
/opinion "How should I refactor this function?"
|
|
334
|
+
/opinion fix "The lesson progress resets after navigation"
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Modes: `fix`, `ask`, `architecture`, `general`.
|
|
338
|
+
|
|
339
|
+
### `/council-settings`
|
|
340
|
+
|
|
341
|
+
Configure the OpenRouter setup.
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
/council-settings # Open the settings UI (3 council + 1 synthesis + opinion)
|
|
345
|
+
/council-settings list # Show current settings (API key redacted)
|
|
346
|
+
/council-settings reset # Reset all council settings
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### `/opinion-settings`
|
|
350
|
+
|
|
351
|
+
Configure just the `/opinion` model.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
/opinion-settings # Open the settings UI
|
|
355
|
+
/opinion-settings list # Show current model
|
|
356
|
+
/opinion-settings reset # Reset to default
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Models
|
|
362
|
+
|
|
363
|
+
### Council (3 Members)
|
|
364
|
+
|
|
365
|
+
The three models you pick in `/council-settings` for independent deliberation. Recommended starting set (all available on OpenRouter):
|
|
366
|
+
|
|
367
|
+
- `anthropic/claude-3.5-sonnet` — careful reasoning, code quality
|
|
368
|
+
- `openai/gpt-4o` — broad knowledge, multimodal
|
|
369
|
+
- `qwen/qwen3.7-max` — strong code model, cost-effective
|
|
370
|
+
|
|
371
|
+
Pick models that disagree productively. If they all share a training cutoff, you get correlated blind spots.
|
|
372
|
+
|
|
373
|
+
### Synthesis Model
|
|
374
|
+
|
|
375
|
+
The fourth model that reads the three opinions and writes the single recommendation. Defaults to "Council Model 1". For high-stakes decisions, consider a reasoning-tuned model:
|
|
376
|
+
|
|
377
|
+
- `openai/o1` — strong step-by-step reasoning
|
|
378
|
+
- `anthropic/claude-3.5-sonnet` — careful, balanced
|
|
379
|
+
- `deepseek/deepseek-r1` — reasoning model with low cost
|
|
380
|
+
|
|
381
|
+
### Second Opinion (`/opinion`)
|
|
382
|
+
|
|
383
|
+
Any model with valid auth works. Pi's `modelRegistry.getAvailable()` is the source of truth, so any provider you've configured (Anthropic, OpenAI, Google, OpenRouter, etc.) is selectable.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Architecture
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
pi-model-council/
|
|
391
|
+
├── index.ts # Extension entry point
|
|
392
|
+
├── councilRunner.ts # 3-model + synthesis logic
|
|
393
|
+
├── secondOpinionRunner.ts # Single-model logic
|
|
394
|
+
├── settings.ts # Settings persistence
|
|
395
|
+
├── settings-ui.ts # Settings TUI components (registry-aware)
|
|
396
|
+
├── openrouterClient.ts # OpenRouter REST client + JSON repair
|
|
397
|
+
├── prompts.ts # Proposal + synthesis prompts (blind-label)
|
|
398
|
+
├── structuredOutput.ts # JSON schemas + repair
|
|
399
|
+
├── markdown.ts # Decision report formatter
|
|
400
|
+
├── searchSelector.ts # Searchable model picker (custom TUI)
|
|
401
|
+
├── retry.ts # Timeout + retry helpers
|
|
402
|
+
├── schemas.ts # TypeBox tool parameter schemas
|
|
403
|
+
├── commandParser.ts # CLI argument parsers
|
|
404
|
+
└── types.ts # Shared TypeScript types
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Development
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
# Install dependencies
|
|
413
|
+
npm install
|
|
414
|
+
|
|
415
|
+
# Type check
|
|
416
|
+
npm run typecheck
|
|
417
|
+
|
|
418
|
+
# Lint
|
|
419
|
+
npm run lint
|
|
420
|
+
|
|
421
|
+
# Run tests
|
|
422
|
+
npm test
|
|
423
|
+
|
|
424
|
+
# Watch mode
|
|
425
|
+
npm run test:watch
|
|
426
|
+
|
|
427
|
+
# Coverage
|
|
428
|
+
npm run test:coverage
|
|
429
|
+
|
|
430
|
+
# Audit dependencies
|
|
431
|
+
npm run audit
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Security
|
|
437
|
+
|
|
438
|
+
See [SECURITY.md](SECURITY.md) for the security policy and reporting vulnerabilities.
|
|
439
|
+
|
|
440
|
+
API keys are stored in `council-settings.json` (when set explicitly) or in Pi's `~/.pi/agent/auth.json` (when set via env var or `/login`). Both files use `0600` permissions and are gitignored. The extension never logs keys, but does echo a redacted preview when listing settings.
|
|
441
|
+
|
|
442
|
+
## Disclaimer
|
|
443
|
+
|
|
444
|
+
This extension sends your prompts to OpenRouter and the model providers behind it. AI-generated suggestions can be wrong, outdated, or unsafe — always review before applying. See [DISCLAIMER.md](DISCLAIMER.md) for the full text.
|
|
445
|
+
|
|
446
|
+
## Contributing
|
|
447
|
+
|
|
448
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, code style, and the PR process.
|
|
449
|
+
|
|
450
|
+
## Code of Conduct
|
|
451
|
+
|
|
452
|
+
By participating, you agree to the Contributor Covenant in [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
453
|
+
|
|
454
|
+
## Support
|
|
455
|
+
|
|
456
|
+
Open an issue using the templates in [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/). See [SUPPORT.md](SUPPORT.md) for where to ask questions.
|
|
457
|
+
|
|
458
|
+
## License
|
|
459
|
+
|
|
460
|
+
MIT — see [LICENSE](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 0.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability, please report it via:
|
|
12
|
+
|
|
13
|
+
1. **GitHub Security Advisories** (preferred):
|
|
14
|
+
https://github.com/bramburn/pi-model-council/security/advisories/new
|
|
15
|
+
|
|
16
|
+
2. **Email**: (TBD - report via GitHub for now)
|
|
17
|
+
|
|
18
|
+
Please do NOT report security issues via public GitHub issues.
|
|
19
|
+
|
|
20
|
+
For critical vulnerabilities, we aim to respond within **48 hours** and publish a fix within **7 days**.
|
|
21
|
+
|
|
22
|
+
## Security Best Practices (for users)
|
|
23
|
+
|
|
24
|
+
### Protecting Your API Key
|
|
25
|
+
|
|
26
|
+
- **Never commit your `council-settings.json` to git** — it contains your OpenRouter API key
|
|
27
|
+
- The file is already in `.gitignore`, but verify it's not being tracked
|
|
28
|
+
- Run `git status` to check before any commit
|
|
29
|
+
|
|
30
|
+
### Verifying the Extension
|
|
31
|
+
|
|
32
|
+
Before installing or updating:
|
|
33
|
+
|
|
34
|
+
1. Review the source code in this repository
|
|
35
|
+
2. Check the commit history for any suspicious changes
|
|
36
|
+
3. Report concerns via GitHub Security Advisories
|
|
37
|
+
|
|
38
|
+
### Keeping Dependencies Updated
|
|
39
|
+
|
|
40
|
+
The extension uses minimal dependencies. Keep them updated:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm run audit
|
|
44
|
+
npm update
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or use the automatic Dependabot PRs that are created weekly.
|
|
48
|
+
|
|
49
|
+
## Extension Security Notes
|
|
50
|
+
|
|
51
|
+
### What This Extension Can Do
|
|
52
|
+
|
|
53
|
+
This extension runs with the same permissions as pi itself:
|
|
54
|
+
- Read and write files in your project
|
|
55
|
+
- Execute shell commands
|
|
56
|
+
- Call LLM APIs with your API keys
|
|
57
|
+
|
|
58
|
+
### Trust Decision
|
|
59
|
+
|
|
60
|
+
When you first use this extension in a project, pi will ask if you trust the project. Only trust projects where:
|
|
61
|
+
- The `.pi/extensions/` directory is controlled by you
|
|
62
|
+
- No untrusted code can modify the extension files
|
|
63
|
+
- You're comfortable with the extension having full file system access
|
|
64
|
+
|
|
65
|
+
## Incident Response
|
|
66
|
+
|
|
67
|
+
If you believe your API key has been compromised:
|
|
68
|
+
|
|
69
|
+
1. **Revoke the key immediately** at https://openrouter.ai/keys
|
|
70
|
+
2. **Generate a new key** at https://openrouter.ai/keys
|
|
71
|
+
3. **Update your settings** via `/council-settings`
|
|
72
|
+
4. **Report the incident** via GitHub Security Advisories
|
|
73
|
+
|
|
74
|
+
## Security Dependencies
|
|
75
|
+
|
|
76
|
+
This extension is kept intentionally simple with minimal dependencies to reduce attack surface:
|
|
77
|
+
|
|
78
|
+
- `@sinclair/typebox` — JSON schema validation only
|
|
79
|
+
- No network calls except to OpenRouter API
|
|
80
|
+
- No shell command execution beyond what pi itself allows
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Support
|
|
2
|
+
|
|
3
|
+
Need help with `pi-model-council`? Here's where to look.
|
|
4
|
+
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
Start with [README.md](README.md) — it covers:
|
|
8
|
+
|
|
9
|
+
- The 3-step Quick Start workflow
|
|
10
|
+
- All four commands (`/council`, `/opinion`, `/council-settings`, `/opinion-settings`)
|
|
11
|
+
- How model selection works (Pi model registry vs OpenRouter REST)
|
|
12
|
+
- Where the API key is resolved from
|
|
13
|
+
- Architecture and development workflow
|
|
14
|
+
|
|
15
|
+
For deeper topics:
|
|
16
|
+
|
|
17
|
+
- [SECURITY.md](SECURITY.md) — Reporting vulnerabilities, API key hygiene
|
|
18
|
+
- [DISCLAIMER.md](DISCLAIMER.md) — AI accuracy, cost, third-party terms
|
|
19
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — Setting up the project, PR process
|
|
20
|
+
- [CHANGELOG.md](CHANGELOG.md) — What changed in each release
|
|
21
|
+
|
|
22
|
+
## Asking a question
|
|
23
|
+
|
|
24
|
+
**GitHub Discussions** is the preferred place for "how do I…?" questions. Search first; if your question isn't already answered, open a new discussion with:
|
|
25
|
+
|
|
26
|
+
- What you were trying to do
|
|
27
|
+
- The exact command you ran (e.g. `/council fix "..."`)
|
|
28
|
+
- What you expected to happen
|
|
29
|
+
- What actually happened (full error if any)
|
|
30
|
+
- Pi version, Node version, OS
|
|
31
|
+
|
|
32
|
+
## Filing a bug
|
|
33
|
+
|
|
34
|
+
Use the **Bug report** template at
|
|
35
|
+
[`.github/ISSUE_TEMPLATE/bug_report.md`](.github/ISSUE_TEMPLATE/bug_report.md).
|
|
36
|
+
Include the same details as above plus a minimal reproduction.
|
|
37
|
+
|
|
38
|
+
## Requesting a feature
|
|
39
|
+
|
|
40
|
+
Use the **Feature request** template at
|
|
41
|
+
[`.github/ISSUE_TEMPLATE/feature_request.md`](.github/ISSUE_TEMPLATE/feature_request.md).
|
|
42
|
+
Describe the use case first, then the proposed solution.
|
|
43
|
+
|
|
44
|
+
## Security issues
|
|
45
|
+
|
|
46
|
+
**Do not open a public issue for security vulnerabilities.** Follow
|
|
47
|
+
[SECURITY.md](SECURITY.md) — use the GitHub Security Advisories page
|
|
48
|
+
(preferred) or email the maintainer privately.
|
|
49
|
+
|
|
50
|
+
## Response times
|
|
51
|
+
|
|
52
|
+
This is a small project maintained on personal time. Expect:
|
|
53
|
+
|
|
54
|
+
- Bug reports — acknowledgement within a week, fix on a best-effort basis
|
|
55
|
+
- Feature requests — review and triage within a few weeks
|
|
56
|
+
- Security reports — within 48 hours per the policy in SECURITY.md
|
|
57
|
+
|
|
58
|
+
If you need a guaranteed response window, this project isn't a good fit; use a
|
|
59
|
+
commercial alternative.
|
|
60
|
+
|
|
61
|
+
## Paid support
|
|
62
|
+
|
|
63
|
+
This project does not offer paid support. If you need urgent help integrating
|
|
64
|
+
`pi-model-council` into a commercial workflow, consider:
|
|
65
|
+
|
|
66
|
+
- Hiring a Pi-extension developer from the Pi community
|
|
67
|
+
- Forking the project and maintaining your own internal version
|
|
68
|
+
|
|
69
|
+
## Code of Conduct
|
|
70
|
+
|
|
71
|
+
All community spaces (issues, discussions, PRs) are governed by our
|
|
72
|
+
[Code of Conduct](CODE_OF_CONDUCT.md). Be kind, be specific, be patient.
|