@pithosai/pithosai 1.0.7 → 1.0.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pithosai/pithosai",
3
- "version": "1.0.7",
3
+ "version": "1.0.32",
4
4
  "description": "Pithos AI CLI — multi-role coding and product agents (bundled distribution).",
5
5
  "keywords": [
6
6
  "ai",
@@ -14,19 +14,27 @@
14
14
  "main": "./dist/pithosai.mjs",
15
15
  "bin": {
16
16
  "pithosai": "./dist/pithosai.mjs",
17
- "pmsai": "./dist/pmsai.mjs",
18
- "qasai": "./dist/qasai.mjs",
19
- "rdsai": "./dist/rdsai.mjs",
20
- "cosai": "./dist/cosai.mjs",
21
- "mktai": "./dist/mktai.mjs",
17
+ "pmsai": "./dist/pmsai",
18
+ "qasai": "./dist/qasai",
19
+ "rdsai": "./dist/rdsai",
20
+ "cosai": "./dist/cosai",
21
+ "mktai": "./dist/mktai",
22
22
  "email-monitor": "./dist/heartbeat.mjs",
23
- "wrsai": "./dist/wrsai.mjs",
23
+ "wrsai": "./dist/wrsai",
24
24
  "render-markdown": "./dist/render-markdown.mjs",
25
- "init": "./dist/init.mjs"
25
+ "init": "./dist/init.mjs",
26
+ "pithosai-git-ai-adapter": "./dist/pithosai-git-ai-adapter.mjs"
26
27
  },
27
28
  "files": [
28
29
  "LICENSE",
30
+ "CHANGELOG.md",
31
+ "docs/hooks-usage.md",
32
+ "docs/KROUTER.md",
33
+ "src/models/README.md",
34
+ "src/models/krouter.README.md",
35
+ "src/agent-orch/READEM.md",
29
36
  "dist",
37
+ "!dist/npc-platform-client",
30
38
  "shims",
31
39
  "scripts/npm-global-install.js",
32
40
  "scripts/postinstall-init.js"
@@ -37,36 +45,54 @@
37
45
  },
38
46
  "dependencies": {
39
47
  "@clack/prompts": "^1.0.1",
40
- "@journeyapps/sqlcipher": "^5.3.1",
41
- "@mariozechner/pi-agent-core": "^0.52.5",
42
- "@mariozechner/pi-coding-agent": "^0.52.7",
43
- "@mariozechner/pi-tui": "^0.52.12",
48
+ "@earendil-works/pi-agent-core": "0.85.1",
49
+ "@earendil-works/pi-ai": "0.85.1",
50
+ "@earendil-works/pi-coding-agent": "0.85.1",
51
+ "@earendil-works/pi-tui": "0.85.1",
52
+ "@marianmeres/actor": "^1.6.1",
53
+ "@sinclair/typebox": "^0.34.49",
54
+ "adm-zip": "^0.5.18",
44
55
  "better-sqlite3": "^12.6.2",
45
56
  "chalk": "^5.6.2",
46
57
  "chokidar": "^5.0.0",
58
+ "cli-table3": "^0.6.5",
47
59
  "commander": "^14.0.3",
60
+ "cron-parser": "^4.9.0",
48
61
  "date-fns": "^4.1.0",
49
62
  "handlebars": "^4.7.8",
50
63
  "i18next": "^25.8.4",
51
64
  "imap": "^0.8.19",
65
+ "isomorphic-git": "^1.38.6",
66
+ "json5": "^2.2.3",
52
67
  "log4js": "^6.9.1",
68
+ "lookpath": "^1.2.3",
53
69
  "mailparser": "^3.7.1",
54
70
  "marked": "^17.0.2",
71
+ "node-cron": "^3.0.3",
55
72
  "nodemailer": "^6.9.16",
73
+ "ollama": "^0.6.3",
56
74
  "pdf-parse": "^1.1.1",
57
75
  "playwright": "^1.58.2",
58
76
  "proper-lockfile": "^4.1.2",
59
77
  "readdirp": "^5.0.0",
60
- "uuid": "^13.0.0"
78
+ "tinyld": "^1.3.4",
79
+ "uuid": "^14.0.0",
80
+ "ws": "^8.20.0"
61
81
  },
62
82
  "devDependencies": {
63
83
  "@rspack/cli": "^1.7.6",
64
84
  "@rspack/core": "^1.7.6",
85
+ "dompurify": "^3.3.0",
86
+ "jsdom": "^27.0.1",
87
+ "semver": "^7.7.3",
65
88
  "vitest": "^4.0.18",
66
89
  "webpack-node-externals": "^3.0.0"
67
90
  },
91
+ "optionalDependencies": {
92
+ "@journeyapps/sqlcipher": "5.3.1"
93
+ },
68
94
  "engines": {
69
- "node": ">=18.12.0"
95
+ "node": ">=22.19.0"
70
96
  },
71
97
  "publishConfig": {
72
98
  "access": "public"
@@ -0,0 +1,62 @@
1
+ # agent-orch
2
+
3
+ **Agent orchestration layer** written in shell scripts.
4
+
5
+ `pithosai.js` supports running agents from shell commands using a predefined `taskId`, `--report-json`, a specific `role`, and the user request. By chaining these roles sequentially, we can orchestrate agents to complete large, complex tasks.
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ ./run.sh --roles MKT,ARCH,PM,RD --request "Your question here" [--title "Title"] [--lang zh]
11
+ ```
12
+
13
+ ### Arguments
14
+
15
+ | Argument | Required | Description |
16
+ |---|---|---|
17
+ | `--roles` | Yes | Comma-separated list of roles (e.g. `MKT,ARCH,PM,RD,QA,CO,WR,SRE`) |
18
+ | `--request` | Yes* | The user request/question. Can also be passed as the first positional arg. |
19
+ | `--title` | No | Project title (default: `pithosai智能编排服务`) |
20
+ | `--lang` | No | Language code (default: `zh`) |
21
+
22
+ ### Example
23
+
24
+ ```bash
25
+ # Dispatch through MKT → ARCH → PM → RD
26
+ ./run.sh --roles MKT,ARCH,PM,RD \
27
+ --request "分析一下AI智能体平台的市场前景" \
28
+ --lang zh
29
+ ```
30
+
31
+ ## How it works
32
+
33
+ 1. A unique `task_id` is generated for the entire orchestration run.
34
+ 2. Each role is dispatched **sequentially** via `role_run()` from `base.sh`.
35
+ 3. After a role completes, its report JSON is read and passed as context to the next role.
36
+ 4. The final role's report JSON path is printed to stdout.
37
+
38
+ ## API (for shell scripts)
39
+
40
+ ### `dispatch_roles` (in `base.sh`)
41
+
42
+ ```bash
43
+ source "$SCRIPT_DIR/base.sh"
44
+
45
+ task_id="$(uuidgen)"
46
+ dispatch_roles "$task_id" "$title" "$request" "$lang" MKT ARCH PM RD
47
+ ```
48
+
49
+ Feeds each role sequentially, automatically reading previous role reports as context. Returns the final report JSON path on stdout.
50
+
51
+ ## Role helpers
52
+
53
+ Each role has a dedicated helper in `base.sh`:
54
+
55
+ - `mkt_run` — Marketing analysis
56
+ - `arch_run` — Architecture design
57
+ - `rd_run` — Research & Development
58
+ - `pm_run` — Product Management
59
+ - `qa_run` — Quality Assurance
60
+ - `co_run` — Company Operations
61
+ - `wr_run` — Writing
62
+ - `sre_run` — Site Reliability Engineering
@@ -0,0 +1,469 @@
1
+ # Model configuration (`src/models`)
2
+
3
+ **Release 1.0.15** — Global OpenRouter models: Anthropic, OpenAI, Google, Meta, Mistral and more are now available via `OPENROUTER_API_KEY`. Refactored `openrouter-reasoning.js` → `enhanceOpenRouterModel()` applies compat (mandatory-thinking, developer-role) to all OpenRouter reasoning models. Legacy `enhanceOpenRouterChinaModel()` alias preserved.
4
+
5
+ **Release 1.0.14** — TUI full-screen modes, `/shell cd` with history, `/list` project scope, `/model` autocomplete, `/grill-me` plan alignment, `--task-id` for stable task identity.
6
+
7
+ **Release 1.0.13** — REPL `/model` and `/mode` inspect/switch, script-mode JSON reports (`--report-json`, `--json`), and cron jobs with optional model keys.
8
+
9
+ **Release 1.0.12** — China-mainland providers (pi-ai direct + OpenRouter), OpenRouter compat patches (`openrouter-reasoning.js`), agent session cache keys tied to resolved models, and Ollama Qwen tool calling.
10
+
11
+ Pithosai resolves LLM backends through a single map, **`KEY_TO_MODEL`**, and two entry points:
12
+
13
+ Static catalogs (CLI keys, model ids, China-direct prefixes/env vars, OpenRouter China slugs, default URLs) are seeded from **[`catalog.json`](./catalog.json)**. On first Node run, pithosai copies that file to **`~/.pithosai/models.json5`** (JSON5) if it does not exist, then loads the catalog from the user file. Override the path with `PITHOSAI_MODELS_PATH`. Resolver logic, Cursor CLI streaming, and OpenRouter compat patches stay in JS.
14
+
15
+ | Function | When used |
16
+ |----------|-----------|
17
+ | `resolveModelConfig(options)` | Default run / agent bootstrap when no explicit `--model` key |
18
+ | `resolveModelConfigByKey(key, KEY_TO_MODEL)` | CLI `--model`, persisted `task.model`, REPL model switch |
19
+
20
+ Both return `{ model, getApiKey }` where `model` is a [pi-ai](https://www.npmjs.com/package/@earendil-works/pi-ai) model object and `getApiKey` supplies the API key at call time.
21
+
22
+ Project-wide setup examples live in the root [README.md](../../README.md).
23
+
24
+ ---
25
+
26
+ ## How `KEY_TO_MODEL` is built
27
+
28
+ At module load, keys are merged in this order (later layers override earlier on collision):
29
+
30
+ 1. **OpenRouter (China-mainland slugs)** — from `getModels("openrouter")`, filtered by `openrouter.chinaMainlandSlugs` in the catalog; passes through `enhanceOpenRouterModel()` for compat
31
+ 2. **OpenRouter (global slugs)** — from `getModels("openrouter")`, for non-China providers (Anthropic, OpenAI, Google, Meta, Mistral, …) when `OPENROUTER_API_KEY` is set; also passes through `enhanceOpenRouterModel()`. Self-referential `openrouter/` slug models (e.g. `openrouter/auto`) are skipped to avoid duplicating entries from step 1.
32
+ 3. **pi-ai direct China providers** — from `getModels(provider)` when the matching API key env is set (`china-pi-ai-providers.js`, metadata from the catalog)
33
+ 4. **Hand-crafted entries** — DeepSeek, Ollama, Cursor, Krouter (`deepseek.js`, `ollama.js`, `cursor.js`, `krouter.js`) built from catalog `handCrafted`
34
+
35
+ Keys are kebab-case, e.g. `qwen-qwen3-235b-a22b-instruct`, `moonshotai-kimi-k2-6`, `deepseek-flash`.
36
+
37
+ List available keys at runtime:
38
+
39
+ ```bash
40
+ node -e "import { getPublicModelKeys } from './src/models/key-to-model.js'; console.log(getPublicModelKeys().join('\n'))"
41
+ # or: pithosai models # public keys + discovered krouter composites
42
+ ```
43
+
44
+ (Set the env vars you care about first; the map is built from the current environment.)
45
+
46
+ ### What changed in 1.0.13
47
+
48
+ | Area | Behavior |
49
+ |------|----------|
50
+ | **`/model` REPL** | No arg or `list` shows the current model label and all available `KEY_TO_MODEL` keys; `/model <key>` switches the session model for subsequent runs (`request-command.js`). |
51
+ | **`/mode` REPL** | No arg shows current run mode; `list` shows `agent`, `dialog`, `plan`; `/mode <mode>` switches mode. Mode affects tool availability with the resolved model (dialog/plan disable shell and write tools). |
52
+ | **`--task-id`** | Main `pithosai` command accepts `--task-id <id>` for a stable task id (default: new UUID per invocation). Combine with `--model` in script runs to resume the same persisted task. |
53
+ | **Script-mode reports** | `--no-interactive --report-json <path>` writes a v1 JSON array after the run; `--json` prints the same to stdout. Each entry includes `taskId`, `role`, `mode`, file change buckets, and exit metadata (not the model key yet — use `--model` explicitly in the command line). Interactive `--report-json` appends one object per agent turn and flushes on `/exit`, `/role`, or `/mode`. |
54
+ | **Cron jobs** | Agent tools `create_cron_task` / `update_cron_task` accept an optional `model` string (a `KEY_TO_MODEL` key). Schedules run via `pithosai cron-daemon`. |
55
+ | **Orchestration scripts** | `src/agent-orch/` shell helpers call `pithosai --no-interactive --report-json` per role with a shared `--task-id` for multi-step agent pipelines. |
56
+
57
+ ### What changed in 1.0.12
58
+
59
+ | Area | Behavior |
60
+ |------|----------|
61
+ | **China direct APIs** | Catalogs from pi-ai `getModels(provider)` when `KIMI_API_KEY`, `MOONSHOT_API_KEY`, `MINIMAX_*`, `ZAI_API_KEY`, or `XIAOMI_API_KEY` is set (`china-pi-ai-providers.js`). |
62
+ | **OpenRouter (China + Global)** | Slugs filtered in `key-to-model.js` (China‑only and global); entries pass through `enhanceOpenRouterModel()` for mandatory‑thinking and `developer`‑role compat (`openrouter-reasoning.js`). China‑only slugs (Qwen, Baidu, …) disable developer role via `compat.supportsDeveloperRole: false`; Anthropic reasoning models also disable developer role. |
63
+ | **Agent session cache** | `buildAgentSessionCacheKey` uses explicit `--model` keys or `resolved:{provider}:{id}` from `resolveModelConfigForAgentOpts` so env/provider switches invalidate the cached CLI agent (`agent-session.js`). |
64
+ | **Ollama Qwen** | `ollama-qwen25` and `ollama-qwen25-coder` use `supportsTools: true` (full agent tools); `ollama-deepseek-r1` stays chat-only. |
65
+ | **Env helpers** | `provider-env.js` wraps pi-ai `findEnvKeys` / `getEnvApiKey` (trimmed); shared by `config.js` and `key-to-model.js`. |
66
+
67
+ ---
68
+
69
+ ## Provider categories
70
+
71
+ ### 1. DeepSeek (direct API)
72
+
73
+ | Env | `DEEPSEEK_API_KEY` |
74
+ | Key prefix | `deepseek-*` |
75
+ | Source | Hand-crafted (`deepseek.js`) |
76
+ | API | `https://api.deepseek.com` |
77
+
78
+ | CLI key | API model id | Notes |
79
+ |---------|--------------|--------|
80
+ | `deepseek-flash`, `deepseek-chat` | `deepseek-v4-flash` | Default chat / daily use |
81
+ | `deepseek-coder`, `deepseek-pro` | `deepseek-v4-pro` | Stronger / coding (`--pro`) |
82
+ | `deepseek-reasoner` | `deepseek-v4-pro` | Thinking enabled (`--reasoner`) |
83
+
84
+ Env detection uses pi-ai `findEnvKeys` / `getEnvApiKey` via `provider-env.js`.
85
+
86
+ When `DEEPSEEK_API_KEY` is set, OpenRouter **deepseek** models are omitted from `KEY_TO_MODEL` (direct API is preferred).
87
+
88
+ ---
89
+
90
+ ### 2. China pi-ai direct APIs
91
+
92
+ Catalog and env var names come from **pi-ai**. Pithosai only adds CLI prefixes and OpenRouter dedupe rules in `china-pi-ai-providers.js`.
93
+
94
+ | pi-ai provider | Env var(s) | CLI key prefix | Default model (env-only bootstrap) |
95
+ |----------------|------------|----------------|-------------------------------------|
96
+ | `kimi-coding` | `KIMI_API_KEY` | `kimi-*` | `kimi-for-coding` |
97
+ | `moonshotai-cn` | `MOONSHOT_API_KEY` | `moonshotai-cn-*` | `kimi-k2.6` |
98
+ | `moonshotai` | `MOONSHOT_API_KEY` | `moonshotai-*` | `kimi-k2.6` |
99
+ | `zai` | `ZAI_API_KEY` | `z-ai-*` | `glm-4.7` |
100
+ | `minimax-cn` | `MINIMAX_CN_API_KEY` | `minimax-cn-*` | `MiniMax-M2.7` |
101
+ | `minimax` | `MINIMAX_API_KEY` | `minimax-*` | `MiniMax-M2.7` |
102
+ | `xiaomi` | `XIAOMI_API_KEY` | `xiaomi-*` | `mimo-v2.5` |
103
+
104
+ Example:
105
+
106
+ ```bash
107
+ export MOONSHOT_API_KEY=sk-...
108
+ pithosai request "..." --model moonshotai-kimi-k2-6
109
+ ```
110
+
111
+ When a direct key is set for a provider, the matching **OpenRouter slug** (`moonshotai`, `minimax`, `z-ai`, `xiaomi`, …) is skipped so listings do not duplicate the same models.
112
+
113
+ **Default env precedence** (first configured wins): Kimi Code → Moonshot CN → Moonshot → Z.ai → MiniMax CN → MiniMax → Xiaomi.
114
+
115
+ Model ids and endpoints are whatever pi-ai ships for that provider (run `getModels("<provider>")` from `@earendil-works/pi-ai` to inspect).
116
+
117
+ ---
118
+
119
+ ### 3. Local Ollama
120
+
121
+ | Env | `OPENAI_LOCAL_API_BASE`, `OPENAI_LOCAL_API_KEY` (both required for listing) |
122
+ | Key prefix | `ollama-*` |
123
+ | Source | Hand-crafted (`ollama.js`) |
124
+ | Default base | `http://localhost:11434/v1` |
125
+
126
+ | CLI key | Ollama model id | Agent tools |
127
+ |---------|-----------------|-------------|
128
+ | `ollama-qwen25` | `qwen2.5:14b` | Yes (`supportsTools: true`) |
129
+ | `ollama-qwen25-coder` | `qwen2.5-coder:14b` | Yes (`supportsTools: true`) |
130
+ | `ollama-deepseek-r1` | `deepseek-r1:14b` | No (chat-only; reasoning model) |
131
+ | `ollama-qwythos-9b-q8` | `qwythos-9b-q8` | Yes (`api: "ollama-native"`) |
132
+
133
+ Qwen keys appear in `/model` autocomplete when `OPENAI_LOCAL_API_*` is set; `ollama-deepseek-r1` is omitted from autocomplete because tools are disabled.
134
+
135
+ Use explicit Ollama model keys in `resolveModelConfig` / agent runs, e.g. `model: "ollama-qwen25-coder"` or composite `model: "ollama/qwen2.5-coder:14b"`.
136
+
137
+ ---
138
+
139
+ ### 3b. Krouter (Kiro provider, OpenAI-compatible)
140
+
141
+ Install the local gateway: [`docs/KROUTER.md`](../../docs/KROUTER.md). Combo + composite guide: [`krouter.README.md`](./krouter.README.md).
142
+
143
+ | Env | On by default. Optional `KROUTER_API_BASE` (default `http://localhost:20128/v1`); optional `KROUTER_API_KEY`. Opt out with `KROUTER_DISABLED=1`. Legacy `KIRO_*` aliases work. |
144
+ | Key prefix | `krouter-*` (combos) and composite `krouter/<id>` |
145
+ | Source | Catalog combos + lazy `GET /v1/models` metadata (`krouter.js`) |
146
+ | Default base | `http://localhost:20128/v1` |
147
+ | API | `openai-completions` → Krouter `/v1/chat/completions` |
148
+ | Listed combos | `RD`, `ARCH`, `PM`, `QA` |
149
+ | Env-walk default | `RD` (`krouter-rd`); other combos are explicit `--model` only |
150
+
151
+ | CLI key | Krouter model id |
152
+ |---------|------------------|
153
+ | `krouter-rd` | `RD` |
154
+ | `krouter-arch` | `ARCH` |
155
+ | `krouter-pm` | `PM` |
156
+ | `krouter-qa` | `QA` |
157
+
158
+ Other gateway ids: `--model krouter/<id>` (alias `kiro/...`), e.g. `krouter/cu/default`. `/model` lists combos only.
159
+
160
+ ```bash
161
+ export KROUTER_API_BASE=http://localhost:20128/v1
162
+ pithosai request "..." --model krouter-rd
163
+ pithosai request "..." --model krouter/cu/default
164
+ ```
165
+
166
+ ---
167
+
168
+ ### 4. Cursor CLI
169
+
170
+ | Env | `CURSOR_MODEL` (required for keys to appear in `KEY_TO_MODEL`) |
171
+ | Key prefix | `cursor-*` |
172
+ | Source | Hand-crafted (`cursor.js`) |
173
+ | API key | None (Cursor CLI auth) |
174
+
175
+ | CLI key | Typical Cursor model id |
176
+ |---------|-------------------------|
177
+ | `cursor-auto-model` | `auto` |
178
+ | `cursor-sonnet` | `sonnet-4.6` |
179
+ | `cursor-sonnet-thinking` | `sonnet-4.6-thinking` |
180
+ | `cursor-opus` | `opus` |
181
+ | `cursor-gpt` | `gpt` |
182
+
183
+ Use `--cursor` or `cursor: true` in `resolveModelConfig`; optional string sets an explicit model id.
184
+
185
+ ---
186
+
187
+ ### 5. OpenRouter (China-mainland + Global)
188
+
189
+ | Env | `OPENROUTER_API_KEY` |
190
+ | Key pattern | `{provider-slug}-{model-id}` (OpenRouter slug + model id, kebab-cased) |
191
+ | Source | `getModels("openrouter")` filtered in `key-to-model.js` |
192
+
193
+ Keys are built in `key-to-model.js` from each OpenRouter id `provider/model-id`:
194
+
195
+ 1. Drop the `provider/` prefix from the model id.
196
+ 2. Join `provider` and the rest with `-`, lowercased, splitting on `-`, `_`, `.`, `/` (colons `:` are kept, e.g. `z-ai-glm-4-5-air:free`).
197
+
198
+ #### China-mainland slugs
199
+
200
+ `deepseek`, `qwen`, `moonshotai`, `minimax`, `z-ai`, `xiaomi`, `baidu`, `bytedance-seed`, `stepfun`, `tencent`, `kwaipilot`, `inclusionai`
201
+
202
+ | OpenRouter slug | CLI key pattern | Example keys (from current pi-ai catalog) |
203
+ |-----------------|-----------------|-------------------------------------------|
204
+ | `deepseek` | `deepseek-{model-id}` | `deepseek-deepseek-v4-flash`, `deepseek-deepseek-v4-pro`, `deepseek-deepseek-chat-v3-0324` |
205
+ | `qwen` | `qwen-{model-id}` | `qwen-qwen-plus`, `qwen-qwen3-235b-a22b`, `qwen-qwen3-vl-235b-a22b-thinking` (reasoning required) |
206
+ | `moonshotai` | `moonshotai-{model-id}` | `moonshotai-kimi-k2-6`, `moonshotai-kimi-k2-5`, `moonshotai-kimi-k2-thinking` |
207
+ | `minimax` | `minimax-{model-id}` | `minimax-minimax-m2-5`, `minimax-minimax-m2-7`, `minimax-minimax-m1` |
208
+ | `z-ai` | `z-ai-{model-id}` | `z-ai-glm-4-7`, `z-ai-glm-4-5`, `z-ai-glm-4-5-air:free`, `z-ai-glm-5-1` |
209
+ | `xiaomi` | `xiaomi-{model-id}` | `xiaomi-mimo-v2-5`, `xiaomi-mimo-v2-flash`, `xiaomi-mimo-v2-5-pro` |
210
+ | `baidu` | `baidu-{model-id}` | `baidu-ernie-4-5-21b-a3b`, `baidu-ernie-4-5-vl-28b-a3b` |
211
+ | `bytedance-seed` | `bytedance-seed-{model-id}` | `bytedance-seed-seed-1-6`, `bytedance-seed-seed-1-6-flash`, `bytedance-seed-seed-2-0-lite` |
212
+ | `stepfun` | `stepfun-{model-id}` | `stepfun-step-3-5-flash`, `stepfun-step-3-7-flash` |
213
+ | `tencent` | `tencent-{model-id}` | `tencent-hy3-preview` |
214
+ | `kwaipilot` | `kwaipilot-{model-id}` | `kwaipilot-kat-coder-pro-v2` |
215
+ | `inclusionai` | `inclusionai-{model-id}` | `inclusionai-ling-2-6-flash`, `inclusionai-ling-2-6-1t`, `inclusionai-ring-2-6-1t` |
216
+
217
+ #### Global slugs (1.0.15+)
218
+
219
+ When `OPENROUTER_API_KEY` is set, non-China models are also indexed into `KEY_TO_MODEL`. These use the same `{provider-slug}-{model-id}` pattern:
220
+
221
+ | OpenRouter slug | CLI key pattern | Example keys |
222
+ |-----------------|-----------------|-------------------------------------------|
223
+ | `anthropic` / `~anthropic` | `anthropic-{model-id}` | `anthropic-claude-sonnet-4`, `anthropic-claude-sonnet-4-6`, `anthropic-claude-opus-4` |
224
+ | `openai` | `openai-{model-id}` | `openai-gpt-4o`, `openai-gpt-4o-mini`, `openai-o3`, `openai-o4-mini` |
225
+ | `google` | `google-{model-id}` | `google-gemini-2-5-flash`, `google-gemini-2-5-pro` |
226
+ | `meta` | `meta-{model-id}` | `meta-llama-4-maverick`, `meta-llama-4-scout` |
227
+ | `mistral` | `mistral-{model-id}` | `mistral-mistral-small-3-1`, `mistral-mistral-large-2411` |
228
+ | `cohere` | `cohere-{model-id}` | `cohere-command-a`, `cohere-command-r-plus` |
229
+ | … | … | Full set changes with the pi-ai OpenRouter catalog. |
230
+
231
+ The full set depends on the OpenRouter catalog bundled in `@earendil-works/pi-ai` and changes when that package is upgraded. Wildcard usage: `--model qwen-qwen-plus` matches one concrete key, not a pattern.
232
+
233
+ Slugs listed above are **skipped** when the same provider is already available via a direct pi-ai API key (see section 2). `deepseek` is always skipped when `DEEPSEEK_API_KEY` is set.
234
+
235
+ Default OpenRouter bootstrap (no `--model` key): `deepseek/deepseek-v4-flash`; `--pro` / `--reasoner` use `deepseek/deepseek-v4-pro`.
236
+
237
+ **OpenRouter-only China providers** (no pi-ai direct catalog in this repo): e.g. `qwen-*`, `baidu-*`, `stepfun-*`, `tencent-*`, `bytedance-seed-*`, `kwaipilot-*`, `inclusionai-*`.
238
+
239
+ **OpenRouter-only global providers** (no pi-ai direct catalog in this repo): e.g. `anthropic-*`, `cohere-*`, `ai21-*`, …
240
+
241
+ **Mandatory-thinking models** — Any OpenRouter model with `reasoning: true` is flagged as mandatory-thinking by `enhanceOpenRouterModel()`. This includes:
242
+ - Models with `thinking` in id or name (e.g. `qwen-qwen3-vl-235b-a22b-thinking`)
243
+ - Hybrid reasoning models (e.g. `anthropic-claude-sonnet-4`) where OpenRouter requires `reasoning` in the request even when effort is optional
244
+
245
+ pi-ai would otherwise send `reasoning: { effort: "none" }` for thinking-level `off`, which returns HTTP 400 from OpenRouter. Pithosai marks these in `openrouter-reasoning.js`, sets default agent `thinkingLevel` to `medium`, and avoids disabling reasoning on the stream path.
246
+
247
+ **Developer role:** For China OpenRouter slugs and Anthropic reasoning models, pi-ai would put the system prompt in a `developer` message when `reasoning: true`. These providers reject that role. `enhanceOpenRouterModel()` sets `compat.supportsDeveloperRole: false` on applicable catalog entries; bootstrap applies the same patch and `messages-pi-ai-compat.js` mirrors it for message conversion tests.
248
+
249
+ **Thinking model examples:**
250
+
251
+ ```bash
252
+ export OPENROUTER_API_KEY=sk-or-v1-...
253
+ pithosai --no-interactive --model qwen-qwen3-vl-235b-a22b-thinking "Plan the module layout"
254
+ pithosai --no-interactive --model qwen-qwen-plus-2025-07-28:thinking "Summarize this file"
255
+ ```
256
+
257
+ Use `/new` or restart the REPL after switching to a thinking model so the agent bootstraps with default `thinkingLevel: medium`.
258
+
259
+ #### Example CLI commands
260
+
261
+ Set OpenRouter and avoid higher-priority providers if you want routing to stay on OpenRouter (unset `DEEPSEEK_API_KEY`, `KIMI_API_KEY`, etc.):
262
+
263
+ ```bash
264
+ export OPENROUTER_API_KEY=sk-or-v1-...
265
+ ```
266
+
267
+ Default agent run (no `--model`; uses OpenRouter DeepSeek V4 Flash when no other provider env is set):
268
+
269
+ ```bash
270
+ pithosai --no-interactive "Summarize this repo"
271
+ pithosai --no-interactive --pro "Refactor the auth module"
272
+ pithosai --no-interactive --reasoner "Design a migration plan"
273
+ ```
274
+
275
+ Pick an explicit OpenRouter-backed key with `--model` (value must match a key from `KEY_TO_MODEL`):
276
+
277
+ ```bash
278
+ # Qwen (OpenRouter slug qwen)
279
+ pithosai --no-interactive --model qwen-qwen-plus "Explain this function"
280
+ pithosai task --file ./tasks.md --model qwen-qwen3-235b-a22b-2507
281
+
282
+ # Baidu ERNIE
283
+ pithosai --no-interactive --model baidu-ernie-4-5-21b-a3b "Draft API docs"
284
+
285
+ # StepFun
286
+ pithosai --no-interactive --model stepfun-step-3-5-flash "Quick code review"
287
+
288
+ # ByteDance Seed
289
+ pithosai --no-interactive --model bytedance-seed-seed-1-6 "Plan the feature"
290
+
291
+ # InclusionAI / Kwaipilot / Tencent (examples; ids vary with OpenRouter catalog)
292
+ pithosai --no-interactive --model inclusionai-ling-2-6-flash "Triage these bugs"
293
+ pithosai --no-interactive --model kwaipilot-kat-coder-pro-v2 "Implement the handler"
294
+ pithosai --no-interactive --model tencent-hy3-preview "Summarize the spec"
295
+ ```
296
+
297
+ In an interactive session, switch model without restarting:
298
+
299
+ ```text
300
+ /model qwen-qwen-plus
301
+ ```
302
+
303
+ List keys available in your current environment (after exporting `OPENROUTER_API_KEY`):
304
+
305
+ ```bash
306
+ node -e "import { getPublicModelKeys } from './src/models/key-to-model.js'; getPublicModelKeys().filter(k => k.startsWith('qwen-')).slice(0,10).forEach(console.log)"
307
+ ```
308
+
309
+ Note: if `MOONSHOT_API_KEY`, `ZAI_API_KEY`, or similar direct keys are set, overlapping `moonshotai-*`, `z-ai-*`, `minimax-*`, and `xiaomi-*` OpenRouter entries are omitted from `KEY_TO_MODEL`; use the direct APIs in [section 2](#2-china-pi-ai-direct-apis) or unset those env vars to use OpenRouter for those families.
310
+
311
+ ---
312
+
313
+ ## REPL `/model` and `/mode`
314
+
315
+ In an interactive session, inspect or switch the active model and run mode without restarting:
316
+
317
+ ```text
318
+ /model # current label + available keys + usage
319
+ /model list # same as /model with no arg
320
+ /model qwen-qwen-plus
321
+ /mode # current mode + usage
322
+ /mode list # current mode + agent, dialog, plan
323
+ /mode plan
324
+ ```
325
+
326
+ **Current model label** (`resolveCurrentModelLabel`): explicit `runOpts.model` (a `KEY_TO_MODEL` key) when set; otherwise fallbacks such as `deepseek-reasoner` (`--reasoner`), `deepseek-coder` (`--pro`), `cursor:<id>` (`--cursor`), or `deepseek-chat`.
327
+
328
+ **Run modes** (`agent`, `dialog`, `plan`) change which tools the agent may call with the resolved model — not the model itself. Autocomplete excludes the current model key and current mode from suggestions (`repl-completer.js`).
329
+
330
+ After `/model` to a different provider or a mandatory-thinking OpenRouter key, use `/new` or restart so the agent bootstraps with the correct defaults (see [Thinking model examples (1.0.12)](#thinking-model-examples-1012) above).
331
+
332
+ ---
333
+
334
+ ## Script mode (automation)
335
+
336
+ Use `--no-interactive` with an explicit model key and optional stable task id:
337
+
338
+ ```bash
339
+ export OPENROUTER_API_KEY=sk-or-v1-...
340
+
341
+ # One-shot run + file report (exit 0 = success, 1 = failure, 2 = stopped)
342
+ pithosai --no-interactive \
343
+ --model qwen-qwen-plus \
344
+ --task-id my-feature-spec \
345
+ --report-json ./runs/spec.json \
346
+ "Draft the API spec"
347
+
348
+ # JSON on stdout (banner and agent output suppressed)
349
+ pithosai --no-interactive --json --model moonshotai-kimi-k2-6 "Summarize README"
350
+ ```
351
+
352
+ Report objects include `taskId`, `role`, `mode`, `files`, and timing — useful for shell orchestration (`src/agent-orch/base.sh` passes `--task-id` and `--report-json` across role steps). Cron-created jobs may also set `model` to any key from `getPublicModelKeys()`.
353
+
354
+ ---
355
+
356
+ ## Interactive agent session cache
357
+
358
+ When `agentSessionRef` is set (interactive CLI), `resolveAgentForRun` reuses one bootstrapped agent until `/new` or the cache key changes. The key includes task id, role, mode, and a model segment from `buildModelCacheSegmentForAgentSession()`:
359
+
360
+ - Explicit `opts.model` → the KEY_TO_MODEL string (e.g. `qwen-qwen-plus`)
361
+ - Otherwise → `resolved:{provider}:{id}` from `resolveModelConfigForAgentOpts` (e.g. `resolved:kimi-coding:kimi-for-coding`)
362
+ - If nothing resolves → legacy flags (`__reasoner__`, `__pro__`, `__default__`, …)
363
+
364
+ This prevents reusing a DeepSeek agent after switching env to `KIMI_API_KEY` only, or after `/model` to another provider.
365
+
366
+ ---
367
+
368
+ ## Default resolution order (`resolveModelConfig`)
369
+
370
+ When no explicit model key is passed:
371
+
372
+ 1. `options.cursor` — Cursor CLI
373
+ 2. `options.local` — explicit Ollama option
374
+ 3. `DEEPSEEK_API_KEY` — DeepSeek V4 (flash / pro / reasoner from flags)
375
+ 4. China pi-ai direct env keys — see section 2 precedence
376
+ 5. `OPENAI_LOCAL_API_*` — Ollama via env
377
+ 6. Krouter — on by default (default `http://localhost:20128/v1`); opt out with `KROUTER_DISABLED` / `KIRO_DISABLED`
378
+ 7. `OPENROUTER_API_KEY` — OpenRouter DeepSeek V4 slugs
379
+
380
+ ---
381
+
382
+ ## Key routing (`resolveModelConfigByKey`)
383
+
384
+ | Key prefix / model | API key source |
385
+ |--------------------|----------------|
386
+ | `cursor-*` | Placeholder (`cursor-cli-no-key`) |
387
+ | `deepseek-*` | `getPiAiEnvApiKey("deepseek")` |
388
+ | China direct (`model.provider` in `CHINA_DIRECT_PROVIDER_META`) | `getPiAiEnvApiKey(model.provider)` |
389
+ | `ollama-*` | `OPENAI_LOCAL_API_KEY` + `OPENAI_LOCAL_API_BASE` on model |
390
+ | `krouter-*` | Optional `KROUTER_API_KEY` (default dummy `krouter`); `KROUTER_API_BASE` on model |
391
+ | Other keys in `KEY_TO_MODEL` | `OPENROUTER_API_KEY` |
392
+
393
+ #### China-mainland model environment variables
394
+
395
+ Env var names follow [pi-ai `getEnvApiKey`](https://www.npmjs.com/package/@earendil-works/pi-ai) / `findEnvKeys` where applicable. Values must be non-empty after trim.
396
+
397
+ | Environment variable | Used for | CLI key prefix(es) | Notes |
398
+ |----------------------|----------|-------------------|--------|
399
+ | `DEEPSEEK_API_KEY` | DeepSeek direct API | `deepseek-*` | Also gates hand-crafted DeepSeek keys; skips OpenRouter `deepseek-*` when set |
400
+ | `KIMI_API_KEY` | Kimi Code (`kimi-coding`) | `kimi-*` | e.g. `kimi-for-coding` |
401
+ | `MOONSHOT_API_KEY` | Moonshot intl + CN (`moonshotai`, `moonshotai-cn`) | `moonshotai-*`, `moonshotai-cn-*` | Same env var for both pi-ai providers |
402
+ | `MINIMAX_API_KEY` | MiniMax international (`minimax`) | `minimax-*` | Skips OpenRouter `minimax-*` when set |
403
+ | `MINIMAX_CN_API_KEY` | MiniMax China (`minimax-cn`) | `minimax-cn-*` | China endpoint via pi-ai |
404
+ | `ZAI_API_KEY` | Z.ai / GLM (`zai`) | `z-ai-*` | Skips OpenRouter `z-ai-*` when set |
405
+ | `XIAOMI_API_KEY` | Xiaomi MiMo (`xiaomi`) | `xiaomi-*` | Skips OpenRouter `xiaomi-*` when set |
406
+ | `OPENROUTER_API_KEY` | OpenRouter proxy | China: `qwen-*`, `baidu-*`, `stepfun-*`, `tencent-*`, `bytedance-seed-*`, `kwaipilot-*`, `inclusionai-*`, and any slug not skipped by a direct key. Global (1.0.15+): `anthropic-*`, `openai-*`, `google-*`, `meta-*`, `mistral-*`, `cohere-*`, … | Required for all OpenRouter-backed keys; also used when no direct key is set for `deepseek`, `moonshotai`, `minimax`, `z-ai`, `xiaomi` |
407
+
408
+ Minimal setup examples:
409
+
410
+ ```bash
411
+ # Direct DeepSeek only
412
+ export DEEPSEEK_API_KEY=sk-...
413
+
414
+ # Direct Kimi Code only
415
+ export KIMI_API_KEY=sk-...
416
+
417
+ # OpenRouter for Qwen / Baidu / etc. (unset direct keys for overlapping families)
418
+ export OPENROUTER_API_KEY=sk-or-v1-...
419
+
420
+ # Moonshot CN + international catalogs (both listed when this is set)
421
+ export MOONSHOT_API_KEY=sk-...
422
+ ```
423
+
424
+ pi-ai also defines `XIAOMI_TOKEN_PLAN_CN_API_KEY`, `XIAOMI_TOKEN_PLAN_AMS_API_KEY`, and `XIAOMI_TOKEN_PLAN_SGP_API_KEY` for other Xiaomi providers; pithosai’s China direct catalog currently wires **`XIAOMI_API_KEY`** → `xiaomi` only.
425
+
426
+ ---
427
+
428
+ ## Module map
429
+
430
+ | File | Role |
431
+ |------|------|
432
+ | `catalog.json` | Bundled seed catalog: slugs, China-direct metadata, defaults, hand-crafted models. Copied to `~/.pithosai/models.json5` on first run |
433
+ | `~/.pithosai/models.json5` | Runtime catalog (JSON5). Edit this file to add or change models without rebuilding pithosai. Override path with `PITHOSAI_MODELS_PATH` |
434
+ | `catalog.js` | Loads/freezes the catalog from `models.json5` (seeded from `catalog.json`); CLI-key and id lookup; listed KEY_TO_MODEL key maps |
435
+ | `user-catalog-file.js` | Resolves `~/.pithosai/models.json5`, seeds JSON5 from bundled JSON, parses JSON5 |
436
+ | `key-to-model.js` | Builds `KEY_TO_MODEL`, `getPublicModelKeys()` |
437
+ | `config.js` | `resolveModelConfig*`, bootstrap flag helpers |
438
+ | `provider-env.js` | pi-ai `findEnvKeys` / `getEnvApiKey` wrappers, `has*` helpers |
439
+ | `china-pi-ai-providers.js` | Direct China catalogs, CLI prefixes, OpenRouter skip set |
440
+ | `openrouter-reasoning.js` | OpenRouter compat for all models: `supportsDeveloperRole: false` (China slugs + Anthropic), mandatory-thinking `thinkingLevelMap` / default `thinkingLevel`, and `isMandatoryOpenRouterReasoningModel` helper |
441
+ | `deepseek.js` | DeepSeek V4 hand-crafted models |
442
+ | `ollama.js` | Local Ollama hand-crafted models |
443
+ | `cursor.js` | Cursor CLI models + `registerCursorProvider()` |
444
+ | `krouter.js` | Krouter combos + `/v1/models` fetch helpers |
445
+ | `index.js` | Public re-exports for the rest of the app |
446
+
447
+ ---
448
+
449
+ ## Tests
450
+
451
+ ```bash
452
+ npm test -- src/models/
453
+ ```
454
+
455
+ Coverage includes `catalog`, `config`, `key-to-model`, `china-pi-ai-providers`, `provider-env`, `openrouter-reasoning`, `ollama`, and the other hand-crafted provider modules.
456
+
457
+ ### Add a model from catalog only
458
+
459
+ Edit **`~/.pithosai/models.json5`** (created from [`catalog.json`](./catalog.json) on first run). No JS changes are required for listing or `--model` lookup. To change the seed that new installs copy, edit [`catalog.json`](./catalog.json).
460
+
461
+ | What to add | Where in `catalog.json` |
462
+ |-------------|-------------------------|
463
+ | OpenRouter China slug (Qwen, Baidu, …) | `openrouter.chinaMainlandSlugs` — global OpenRouter models already appear from pi-ai when `OPENROUTER_API_KEY` is set |
464
+ | Direct China / DeepSeek provider | One object in `chinaDirectProviders` (`id`, `envVar`, `keyPrefix`, `openRouterProvider`, `defaultModelId`). Env-walk order is this array’s order. |
465
+ | Krouter combo | One object in `handCrafted.krouter.combos` (`id`, `name`). CLI key is derived (`RD` → `krouter-rd`). Other gateway ids use composite `krouter/<id>`. |
466
+ | Krouter combo | One object in `handCrafted.krouter.combos` with `id` (dashboard combo name, e.g. `RD`) and `name`. CLI key is derived (`RD` → `krouter-rd`). Do not prefix combos with `kr/` — that prefix is for Kiro provider models. Fallback order lives in the kRouter dashboard, not the catalog. Combos are a single CLI key; they are not expanded into `-agentic` / `-thinking` variants. |
467
+ | Ollama / Cursor / DeepSeek hand-crafted model | One object in `handCrafted.ollama` / `cursor` / `deepseek` `models` with `cliKeys`, `id`, `name` |
468
+
469
+ Keep routing and Cursor CLI streaming in JS. Named `export const …Model` wrappers are optional compatibility aliases, not required to list a new key.