agim-cli 1.2.49 → 1.2.52

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 CHANGED
@@ -4,6 +4,63 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.52] - 2026-05-25
8
+
9
+ ### Fixed
10
+
11
+ - `/models` under cursor returned "未能获取 cursor 的模型列表" on
12
+ systemd-launched agim because the spawned `cursor-agent` couldn't be
13
+ found on the stripped PATH. ModelDriver's listModels now uses the
14
+ same bin-resolution as the cursor adapter (env `IMHUB_CURSOR_BIN`
15
+ → PATH → `~/.local/bin/cursor-agent` → `/usr/local/bin/...` →
16
+ `/opt/cursor/bin/...`), cached for the process lifetime.
17
+ - Tightened ANSI strip regex from `\[[0-9;]*m` (where the leading `\[`
18
+ was silently dropped to `[`, accidentally treating `[` as a class
19
+ member) to `\\[[0-9;]*m` so it matches a literal `[` followed by the
20
+ digit/`;` class.
21
+
22
+ ## [1.2.51] - 2026-05-25
23
+
24
+ ### Fixed
25
+
26
+ - v1.2.50's npm publish workflow gated on `Run tests` failing in CI
27
+ because (a) the legacy `test/unit/model-command.test.ts` asserted
28
+ the old single-agent gating wording and (b) the
29
+ `src/web/agim-skills-api.test.ts` "fresh install count=0" fixture
30
+ was tripping on v1.2.48's multi-root skills scan inheriting
31
+ `~/.claude/skills` etc. on the CI box. Both tests updated to match
32
+ current behaviour.
33
+ - Lint errors that had accumulated across v1.2.48-50 are now zero:
34
+ three `useTemplate` rewrites in `memo-rpc.ts` + `web/server.ts`,
35
+ one ANSI regex now built via `new RegExp` to dodge biome's
36
+ `noControlCharactersInRegex`, one assign-in-while-expression in
37
+ `parsePromptMedia` rewritten to a `for (;;)` loop, one void-return
38
+ in the cursor adapter test stub.
39
+ - All `/model` + cursor adapter functionality from v1.2.50 is in this
40
+ release — v1.2.50 itself never landed on npm.
41
+
42
+ ## [1.2.50] - 2026-05-25
43
+
44
+ ### Added
45
+
46
+ - **`/model` + `/models` now work for cursor too** — the command was
47
+ opencode-only; v1.2.50 refactors it into a per-agent `ModelDriver`
48
+ dispatch. When the active session is on cursor, `/models` runs
49
+ `cursor-agent models` and renders the flat list (auto / composer-2 /
50
+ gpt-5.x / sonnet-4 / claude-opus-4-6 / …). `/model <id>` or
51
+ `/model #N` switches the per-session model and the next cursor turn
52
+ spawns with `--model <id>`. Default model is read from
53
+ `~/.cursor/cli-config.json` `model.modelId`. opencode driver is
54
+ unchanged.
55
+
56
+ ### Notes
57
+
58
+ - Adding a third agent now means filling in a `ModelDriver` (`listModels`
59
+ + `resolveDefault`) and registering it in `MODEL_DRIVERS`. No new flag
60
+ on AgentAdapter / Session.
61
+ - /models output is cached per-driver for 5 min; `/models refresh`
62
+ clears all driver caches.
63
+
7
64
  ## [1.2.49] - 2026-05-24
8
65
 
9
66
  ### Added — Cursor CLI agent
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## Highlights
19
19
 
20
- - **3 messengers + email, 5 CLI agents + native** — WeChat (image / file / voice), Feishu, DingTalk (image / voice with server-side ASR), Email (SMTP); Claude Code, Codex, OpenCode, Antigravity, **Cursor (v1.2.49+ · `/cs`)**, plus the in-process `native` LLM agent and any ACP endpoint
20
+ - **3 messengers + email, 6 agents** — WeChat (image / file / voice), Feishu, DingTalk (image / voice with server-side ASR), Email (SMTP); Claude Code, Codex, OpenCode, Antigravity, **Cursor (v1.2.49+ · `/cs`)**, and the in-process `native` LLM agent (DeepSeek / Kimi / Qwen / OpenAI / Anthropic compat — `/na`). Plus any ACP endpoint.
21
21
  - **Native LLM stack (v1.3.0+, hardened in v1.2.48)** — built-in in-process agent loop (`native` adapter) that talks directly to any OpenAI- / Anthropic-compatible API (DeepSeek, Kimi, GLM, Qwen, Ollama, vLLM, OpenAI, Anthropic). v1.2.48 makes native production-ready: **operator role definition** via `~/.agim-workspaces/native/AGENTS.md` (hot-reloaded, no restart), **multimodal vision** (image attachments → openai-compat `image_url` blocks), **auto-compact** long histories (>60k chars summarised by cheap role), **provider fallback chain** on transient 5xx/timeout, **turn-level heartbeat** every 3 min so multi-hop research turns never look dead, **28-min agent-loop timeout** (was 5-min default, killed long `call_agent` chains). Use it as a cheap router for `memory-distill` / `memory-consolidate` / `intent-llm`, as a local-first chat agent (`/na`), or as a peer in A2A. See [`docs/llm-backends.md`](docs/llm-backends.md) + [`docs/architecture-bridge-and-native.md`](docs/architecture-bridge-and-native.md).
22
22
  - **Skills multi-root scan (v1.2.48+)** — `IMHUB_SKILLS_MODE=auto` (default) merges `~/.agim/skills/` (write target) with `~/.claude/skills/`, `~/.config/opencode/skills/`, `~/.codex/skills/` so a fresh install sees every skill you already have from CLI agents. Workspace skills shadow same-named inherited entries. Settings → 技能 has 2 subtabs (Installed editor + skillhub 热门); top-level `/skills` nav merged into Settings.
23
23
  - **Cross-channel outbox alert (v1.2.48+)** — when a thread's outbox is dead (e.g. wechat session expired), agim auto-invalidates the stale context-token + pushes a "⚠️ outbox 告警" to your configured backup channel (`IMHUB_OPERATOR_ALERT_PLATFORM` / `IMHUB_OPERATOR_ALERT_THREAD_ID`) so you find out before the user does.
@@ -98,7 +98,7 @@ units that call `agim` continue to work without any extra setup.
98
98
  | Category | Details |
99
99
  |----------|---------|
100
100
  | **Messengers** | WeChat (iLink — image / file / voice / video), Feishu (WebSocket), DingTalk (Stream mode — image / voice with built-in ASR), Email (SMTP, push-only) |
101
- | **Agents** | Claude Code, Codex, OpenCode, Antigravity (all CLI via shared `AgentBase`); **`native`** in-process LLM loop; any HTTP agent via ACP — all share one `AgentAdapter` interface |
101
+ | **Agents** | 6 built-in: Claude Code, Codex, OpenCode, Antigravity, Cursor (CLI via shared `AgentBase`), and **`native`** (in-process LLM loop, OpenAI/Anthropic compat). Any HTTP agent via ACP — all 7 share one `AgentAdapter` interface |
102
102
  | **Native LLM** | OpenAI- + Anthropic-compatible providers, external MCP server attach, multi-iteration agent loop with policy approval gate, role bindings (`cheap` / `evaluator` / `native-chat`) |
103
103
  | **Reminders** | `/remind` slash, LLM intent detection, LLM-polished delivery, MCP tools for agents (claude-code + opencode), web `/reminders` UI |
104
104
  | **Goals + Heartbeat + Ask** | `/goal` per-thread objective, `/heartbeat` periodic DECIDE/EXECUTE/EVALUATE tick, `mcp__imhub__ask_user` structured multi-choice with `/tasks/{goals,heartbeat,asks}` cross-thread admin |
@@ -153,7 +153,7 @@ agim token bootstrap # Print one-shot web-bootstrap token (`~/.agim/admin-
153
153
  | any text | Route to agent (sticky session, intent-classified) |
154
154
  | `/<agent> <prompt>` | Switch agent — `/cc`, `/oc`, `/cx`, `/co` |
155
155
  | `/new` | New conversation (clear context) |
156
- | `/model [provider/model]` | View or switch model |
156
+ | `/model [id]` / `/models [filter]` | View / list / switch the active agent's model. Works for opencode (`provider/model`) and cursor (flat names like `gpt-5.2`, `sonnet-4-thinking`). v1.2.50+ |
157
157
  | `/think on\|off` | Toggle extended thinking |
158
158
  | `/remind …` | Reminders — see [Reminders](#reminders) below |
159
159
  | `/memo …` | 5W1H persistent memory — see [Memos](#memos) below (aliases `/记`, `/note`) |
@@ -166,6 +166,8 @@ agim token bootstrap # Print one-shot web-bootstrap token (`~/.agim/admin-
166
166
  | `/heartbeat …` | Periodic DECIDE→EXECUTE→EVALUATE tick — `bind / unbind / show / pause / resume / status` (v1.3.0+) |
167
167
  | `/skill list\|show <name>\|refresh` | Inspect agim Skills loaded from `~/.agim/skills/` (v1.3.0+) |
168
168
  | `/cc native …` / `/na …` / `/llm …` | Route to the in-process native LLM agent (v1.3.0+) |
169
+ | `/cs …` / `/cur …` / `/cc cursor …` | Route to Cursor CLI agent (v1.2.49+); `/cs plan` enters plan mode for one turn |
170
+ | `/agy …` / `/cc antigravity …` | Route to Google Antigravity (agy) CLI agent |
169
171
  | `y` / `n` / `批准` / `拒绝` | Approve / deny Claude tool call (or reminder confirmation card) |
170
172
  | `1` / `2` | After a service restart, reply `1` to redo the interrupted message or `2` to cancel (10 min window) |
171
173
 
package/README.zh-CN.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  ## 亮点
15
15
 
16
- - **3 种 IM + 邮件,5 CLI Agent + native** — 微信(图片 / 文件 / 语音)、飞书、钉钉(图片 / 语音,自带服务端 ASR)、Email(SMTP);Claude Code、Codex、OpenCode、Antigravity、**Cursor(v1.2.49+ · `/cs`)**,以及内置 native 与任意 ACP 端点
16
+ - **3 种 IM + 邮件,6 Agent** — 微信(图片 / 文件 / 语音)、飞书、钉钉(图片 / 语音,自带服务端 ASR)、Email(SMTP);Claude Code、Codex、OpenCode、Antigravity、**Cursor(v1.2.49+ · `/cs`)**,以及内置 `native` LLM agent(DeepSeek / Kimi / Qwen / OpenAI / Anthropic 兼容 · `/na`)。另支持任意 ACP 端点。
17
17
  - **原生 LLM 栈(v1.3.0+,v1.2.48 强化)** — 内置 in-process agent loop(`native` adapter),直连任何 OpenAI / Anthropic 兼容 API(DeepSeek / Kimi / GLM / Qwen / Ollama / vLLM / OpenAI / Anthropic)。v1.2.48 让 native 真正进入生产可用:**角色定义** 通过 `~/.agim-workspaces/native/AGENTS.md`(热加载,无需重启);**多模态**(图片附件 → openai-compat `image_url`);**自动压缩**长对话(>60k 字符 → 由 cheap role 摘要);**Provider 失败链路降级**(5xx/超时自动切下一个 role);**轮级心跳**每 3 分钟一次"⏳ 还在处理"避免多步研究看似卡死;**agent-loop 超时拉到 28 分钟**(旧 5 分钟会切断 multi-hop `call_agent`)。可用作 `memory-distill` / `memory-consolidate` / `intent-llm` 的便宜路由,可作为本地优先的聊天 agent(`/na`),也可作 A2A 的 peer。详见 [`docs/llm-backends.md`](docs/llm-backends.md) 与 [`docs/architecture-bridge-and-native.md`](docs/architecture-bridge-and-native.md)
18
18
  - **Skills 多目录扫描(v1.2.48+)** — `IMHUB_SKILLS_MODE=auto`(默认)把 `~/.agim/skills/`(写入目标)与 `~/.claude/skills/`、`~/.config/opencode/skills/`、`~/.codex/skills/` 合并;新装即可看到所有已有 CLI agent 的技能。同名时 workspace 覆盖。Settings → 技能 改成两个子 tab(已安装编辑器 + skillhub 热门);顶部导航条的 `/skills` 已合并进 Settings
19
19
  - **跨通道出口告警(v1.2.48+)** — 某条会话 outbox 失败 ≥3 次时(如 wechat session 过期),agim 自动失效 stale 的 context-token,并向你预设的备用通道(`IMHUB_OPERATOR_ALERT_PLATFORM` / `IMHUB_OPERATOR_ALERT_THREAD_ID`)推一条"⚠️ outbox 告警",让你比用户更早发现
@@ -90,7 +90,7 @@ npm 包名是 `agim-cli`(短名 `agim` 被 npm 反 typo-squat 保留了);
90
90
  | 分类 | 内容 |
91
91
  |------|------|
92
92
  | **IM 通道** | 微信(iLink — 图片 / 文件 / 语音 / 视频)、飞书(WebSocket)、钉钉(Stream 模式 — 图片 / 语音,自带服务端 ASR)、Email(SMTP,仅推送) |
93
- | **Agent** | Claude Code、Codex、OpenCode、Antigravity(统一 `AgentBase`);**`native`** in-process LLM loop;任何 HTTP Agent 通过 ACP 接入——所有后端走同一份 `AgentAdapter` 接口 |
93
+ | **Agent** | 6 个内置:Claude Code、Codex、OpenCode、Antigravity、Cursor(CLI,统一 `AgentBase`),以及 **`native`**(in-process LLM loop,OpenAI/Anthropic 兼容)。任何 HTTP Agent 通过 ACP 接入——所有 7 类后端走同一份 `AgentAdapter` 接口 |
94
94
  | **原生 LLM** | OpenAI / Anthropic 兼容 provider,外挂 MCP 服务器,多轮 agent loop + 策略式审批门,角色绑定(`cheap` / `evaluator` / `native-chat`) |
95
95
  | **提醒** | `/remind` slash、LLM 意图识别、LLM 润色投递、Agent MCP 工具(claude-code + opencode)、Web `/reminders` 页面 |
96
96
  | **长目标 + 心跳 + 提问** | `/goal` 会话级目标、`/heartbeat` 周期 DECIDE/EXECUTE/EVALUATE 节拍、`mcp__imhub__ask_user` 结构化选择题,跨会话视图在 `/tasks/{goals,heartbeat,asks}` |
@@ -145,7 +145,7 @@ agim token bootstrap # 输出一次性 web-bootstrap token(`~/.agim/admi
145
145
  | 任意文本 | 路由到 Agent(Sticky 会话 + 意图分类) |
146
146
  | `/<agent> <内容>` | 切换 Agent — `/cc`、`/oc`、`/cx`、`/co` |
147
147
  | `/new` | 开新会话(清空历史) |
148
- | `/model [provider/model]` | 查看或切换模型 |
148
+ | `/model [id]` / `/models [filter]` | 查看 / 列出 / 切换当前 agent 的模型。支持 opencode(`provider/model` 格式)与 cursor(扁平名,如 `gpt-5.2`、`sonnet-4-thinking`)。v1.2.50+ |
149
149
  | `/think on\|off` | 切换深度思考模式 |
150
150
  | `/remind …` | 提醒子系统 — 详见 [提醒](#提醒) |
151
151
  | `/memo …` | 5W1H 持久记忆库 — 详见 [备忘](#备忘)(别名 `/记`、`/note`)|
@@ -158,6 +158,8 @@ agim token bootstrap # 输出一次性 web-bootstrap token(`~/.agim/admi
158
158
  | `/heartbeat …` | 周期 DECIDE→EXECUTE→EVALUATE 节拍——`bind / unbind / show / pause / resume / status`(v1.3.0+) |
159
159
  | `/skill list\|show <name>\|refresh` | 查看从 `~/.agim/skills/` 加载的 agim Skills(v1.3.0+) |
160
160
  | `/cc native …` / `/na …` / `/llm …` | 切换到 in-process 原生 LLM agent(v1.3.0+) |
161
+ | `/cs …` / `/cur …` / `/cc cursor …` | 切换到 Cursor CLI agent(v1.2.49+);`/cs plan` 单条进入 plan 模式 |
162
+ | `/agy …` / `/cc antigravity …` | 切换到 Google Antigravity (agy) CLI agent |
161
163
  | `y` / `n` / `批准` / `拒绝` | 同意 / 拒绝(工具调用 或 提醒确认卡片) |
162
164
  | `1` / `2` | 服务重启后看到「上次消息中断」提示时,回 `1` 重发 / `2` 取消(10 分钟内有效) |
163
165
 
@@ -1,6 +1,6 @@
1
1
  import type { RouteContext } from '../router.js';
2
- export declare function handleModelCommand(args: string, ctx: RouteContext): Promise<string>;
3
2
  declare function invalidateModelCache(): void;
3
+ export declare function handleModelCommand(args: string, ctx: RouteContext): Promise<string>;
4
4
  /** Test-only diagnostic: clear caches between cases. */
5
5
  export declare const _internal: {
6
6
  invalidateModelCache: typeof invalidateModelCache;
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/core/commands/model.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAoBhD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BzF;AA+FD,iBAAS,oBAAoB,IAAI,IAAI,CAGpC;AAuDD,wDAAwD;AACxD,eAAO,MAAM,SAAS;;CAA2B,CAAA"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/core/commands/model.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AA6KhD,iBAAS,oBAAoB,IAAI,IAAI,CAGpC;AAkBD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzF;AA4FD,wDAAwD;AACxD,eAAO,MAAM,SAAS;;CAA2B,CAAA"}
@@ -1,78 +1,242 @@
1
1
  // /model [name|#N] + /models [filter|refresh] — model switching & listing
2
2
  //
3
- // Currently only the opencode adapter ships variants/models that the
4
- // router can switch between, so this command is opencode-scoped. When
5
- // the active agent isn't opencode we surface a friendly "not supported"
6
- // message instead of querying the wrong CLI.
3
+ // Originally opencode-only. v1.2.50 generalised the dispatch into a
4
+ // per-agent `ModelDriver`, so the command works for any CLI that
5
+ // (a) lists models via its own CLI subcommand, (b) honours a per-call
6
+ // `--model <id>` flag, and (c) lets agim persist the picked model on
7
+ // the session row for the next turn.
8
+ //
9
+ // Drivers shipped today: opencode, cursor. To add a third, fill in
10
+ // `listModels()` + `resolveDefault()` and register in MODEL_DRIVERS.
7
11
  import { spawn } from 'node:child_process';
12
+ import { existsSync, statSync } from 'node:fs';
13
+ import { homedir } from 'node:os';
14
+ import { join } from 'node:path';
8
15
  import { sessionManager } from '../session.js';
9
- const SUPPORTED_AGENT = 'opencode';
16
+ /** Resolve cursor-agent the same way the adapter does so systemd's
17
+ * stripped PATH doesn't break `/models` while the adapter still works. */
18
+ let cachedCursorBin = null;
19
+ function resolveCursorBin() {
20
+ const override = process.env.IMHUB_CURSOR_BIN;
21
+ if (override)
22
+ return override;
23
+ if (cachedCursorBin !== null)
24
+ return cachedCursorBin ?? 'cursor-agent';
25
+ const candidates = [
26
+ join(homedir(), '.local', 'bin', 'cursor-agent'),
27
+ '/usr/local/bin/cursor-agent',
28
+ '/opt/cursor/bin/cursor-agent',
29
+ ];
30
+ for (const p of candidates) {
31
+ try {
32
+ const s = statSync(p);
33
+ if (s.isFile() && (s.mode & 0o111) !== 0) {
34
+ cachedCursorBin = p;
35
+ return p;
36
+ }
37
+ }
38
+ catch { /* not here */ }
39
+ if (!existsSync(p))
40
+ continue;
41
+ }
42
+ cachedCursorBin = undefined;
43
+ return 'cursor-agent';
44
+ }
10
45
  const MODEL_LIST_TTL_MS = 5 * 60 * 1000; // 5 minutes — picks up newly-added models
11
46
  function sessionKey(ctx) {
12
- // Mirrors SessionManager's key shape so the cached numbered list lives
13
- // alongside the actual session state (no userId mismatch in groups).
14
47
  return `${ctx.platform}:${ctx.channelId}:${ctx.threadId}`;
15
48
  }
16
49
  function parseModelArgs(args) {
17
50
  const p = args.trim().split(/\s+/);
18
51
  return { sub: p[0] || '', rest: p.slice(1).join(' ') };
19
52
  }
53
+ // ─── opencode driver (legacy default) ────────────────────────────────
54
+ const opencodeDriver = {
55
+ label: 'opencode',
56
+ namespaced: true,
57
+ switchHint: '例: /model deepseek/deepseek-v4-pro',
58
+ listModels: () => new Promise((resolve) => {
59
+ const proc = spawn('opencode', ['models'], { stdio: ['ignore', 'pipe', 'pipe'] });
60
+ let out = '';
61
+ proc.stdout?.on('data', d => out += d.toString());
62
+ proc.on('close', () => {
63
+ const lines = out.trim().split('\n').filter(Boolean);
64
+ resolve(lines);
65
+ });
66
+ proc.on('error', () => resolve([]));
67
+ }),
68
+ async resolveDefault() {
69
+ try {
70
+ const { readFile } = await import('node:fs/promises');
71
+ const { homedir } = await import('node:os');
72
+ const { join } = await import('node:path');
73
+ const configPath = join(homedir(), '.config', 'opencode', 'opencode.json');
74
+ const raw = await readFile(configPath, 'utf-8');
75
+ const config = JSON.parse(raw);
76
+ const provider = config.provider;
77
+ if (provider) {
78
+ for (const [, p] of Object.entries(provider)) {
79
+ if (p.models && typeof p.models === 'object') {
80
+ const keys = Object.keys(p.models);
81
+ if (keys.length > 0)
82
+ return keys[0];
83
+ }
84
+ }
85
+ }
86
+ }
87
+ catch { /* fall through */ }
88
+ const models = await opencodeDriver.listModels();
89
+ return models[0] || 'unknown';
90
+ },
91
+ };
92
+ // ─── cursor driver ───────────────────────────────────────────────────
93
+ const cursorDriver = {
94
+ label: 'cursor',
95
+ namespaced: false,
96
+ switchHint: '例: /model gpt-5.2 / sonnet-4-thinking / claude-opus-4-6',
97
+ listModels: () => new Promise((resolve) => {
98
+ // cursor-agent emits:
99
+ // Available models
100
+ //
101
+ // <id> - <display name>
102
+ // <id> - <display name>
103
+ // ...
104
+ // (sometimes piped through ANSI colors — strip on parse).
105
+ //
106
+ // Bin resolution mirrors cursor adapter's: env override → PATH →
107
+ // ~/.local/bin → /usr/local/bin → /opt. systemd-launched agim has
108
+ // a stripped PATH that often misses ~/.local/bin, so the bare name
109
+ // fails ENOENT even when the binary is sitting there.
110
+ const bin = resolveCursorBin();
111
+ const proc = spawn(bin, ['models'], { stdio: ['ignore', 'pipe', 'pipe'] });
112
+ let out = '';
113
+ proc.stdout?.on('data', d => out += d.toString());
114
+ proc.on('close', () => {
115
+ const ids = [];
116
+ // ANSI CSI strip. Build via RegExp constructor so the literal
117
+ // ESC byte (0x1b) never lands in source (biome
118
+ // noControlCharactersInRegex).
119
+ const ansi = new RegExp(`${String.fromCharCode(0x1b)}\\[[0-9;]*m`, 'g');
120
+ for (const lineRaw of out.split('\n')) {
121
+ const line = lineRaw.replace(ansi, '').trim();
122
+ if (!line)
123
+ continue;
124
+ if (line.toLowerCase().startsWith('available models'))
125
+ continue;
126
+ const m = line.match(/^([A-Za-z0-9._-]+)\s+-\s+/);
127
+ if (m)
128
+ ids.push(m[1]);
129
+ }
130
+ resolve(ids);
131
+ });
132
+ proc.on('error', () => resolve([]));
133
+ }),
134
+ async resolveDefault() {
135
+ // ~/.cursor/cli-config.json carries model.modelId.
136
+ try {
137
+ const { readFile } = await import('node:fs/promises');
138
+ const { homedir } = await import('node:os');
139
+ const { join } = await import('node:path');
140
+ const configPath = join(homedir(), '.cursor', 'cli-config.json');
141
+ const raw = await readFile(configPath, 'utf-8');
142
+ const config = JSON.parse(raw);
143
+ const model = config.model;
144
+ if (model?.modelId)
145
+ return model.modelId;
146
+ }
147
+ catch { /* fall through */ }
148
+ const models = await cursorDriver.listModels();
149
+ return models[0] || 'auto';
150
+ },
151
+ };
152
+ const MODEL_DRIVERS = {
153
+ opencode: opencodeDriver,
154
+ cursor: cursorDriver,
155
+ };
156
+ const SUPPORTED_AGENTS = Object.keys(MODEL_DRIVERS);
20
157
  /** Per-session cache: last /models ordered result, indexed from 1 */
21
158
  const sessionModelIndex = new Map();
159
+ /** Per-agent list cache. Keyed by driver label. */
160
+ const modelListCache = new Map();
161
+ /** Per-agent default-model cache. Keyed by driver label. */
162
+ const defaultModelCache = new Map();
163
+ function invalidateModelCache() {
164
+ modelListCache.clear();
165
+ defaultModelCache.clear();
166
+ }
167
+ async function getModelList(driver) {
168
+ const cached = modelListCache.get(driver.label);
169
+ if (cached && Date.now() - cached.ts < MODEL_LIST_TTL_MS)
170
+ return cached.value;
171
+ const value = await driver.listModels();
172
+ modelListCache.set(driver.label, { value, ts: Date.now() });
173
+ return value;
174
+ }
175
+ async function resolveDefaultModel(driver) {
176
+ const cached = defaultModelCache.get(driver.label);
177
+ if (cached && Date.now() - cached.ts < MODEL_LIST_TTL_MS)
178
+ return cached.value;
179
+ const value = await driver.resolveDefault();
180
+ defaultModelCache.set(driver.label, { value, ts: Date.now() });
181
+ return value;
182
+ }
22
183
  export async function handleModelCommand(args, ctx) {
23
184
  const { sub, rest } = parseModelArgs(args);
24
- // /model refresh — force re-query the underlying CLI (M-5)
25
185
  if (sub === 'refresh') {
26
186
  invalidateModelCache();
27
187
  return '✅ 模型缓存已清空。下次 /models 将重新查询。';
28
188
  }
29
- // Determine which agent the active session is using; fall back to default
30
189
  const session = await sessionManager.getExistingSession(ctx.platform, ctx.channelId, ctx.threadId);
31
190
  const activeAgent = session?.agent || ctx.defaultAgent;
32
- if (activeAgent !== SUPPORTED_AGENT) {
33
- return `ℹ️ \`/model\` 仅在 \`${SUPPORTED_AGENT}\` 下可用,当前 agent: \`${activeAgent}\`。\n\n切换到 opencode:/oc 或 /opencode`;
191
+ const driver = MODEL_DRIVERS[activeAgent];
192
+ if (!driver) {
193
+ const switchHint = SUPPORTED_AGENTS.map((a) => `/${a === 'opencode' ? 'oc' : a === 'cursor' ? 'cs' : a}`).join(' / ');
194
+ return `ℹ️ \`/model\` 在 \`${activeAgent}\` 下不可用。当前支持:${SUPPORTED_AGENTS.join(' / ')}\n\n切换:${switchHint}`;
34
195
  }
35
- // /models or /models <filter> — list all models
36
196
  if (sub === 'list') {
37
- return handleModelList(rest, ctx);
197
+ return handleModelList(driver, rest, ctx);
38
198
  }
39
- // /model (no args) — show current model
40
199
  if (args.trim() === '') {
41
- return handleModelCurrent(ctx);
200
+ return handleModelCurrent(driver, ctx);
42
201
  }
43
- // /model <name|#N> — switch model (by full name, short name, or index)
44
- return handleModelSwitch(args.trim(), ctx);
202
+ return handleModelSwitch(driver, args.trim(), ctx);
45
203
  }
46
- /** /models list available models with optional filter, numbered */
47
- async function handleModelList(filter, ctx) {
48
- const models = await getModelList();
49
- const defaultModel = await resolveDefaultModel();
204
+ async function handleModelList(driver, filter, ctx) {
205
+ const models = await getModelList(driver);
206
+ const defaultModel = await resolveDefaultModel(driver);
50
207
  const lowered = filter.toLowerCase();
51
208
  const filtered = lowered ? models.filter(m => m.toLowerCase().includes(lowered)) : models;
52
209
  if (filtered.length === 0) {
53
210
  if (filter)
54
211
  return `🔍 没有匹配 "${filter}" 的模型。`;
55
- return `⚠️ 未能获取模型列表。请确认 opencode 已安装且配置了模型。`;
212
+ return `⚠️ 未能获取 ${driver.label} 的模型列表。请确认 CLI 已安装并已登录。`;
56
213
  }
57
214
  const session = await sessionManager.getOrCreateSession(ctx.platform, ctx.channelId, ctx.threadId, ctx.defaultAgent);
58
215
  const current = session?.model || defaultModel;
59
- // Build a flat indexed list for /model #N lookup
60
216
  let idx = 1;
61
217
  const flat = [];
62
218
  const groups = new Map();
63
219
  for (const m of filtered) {
64
- const parts = m.split('/');
65
- const prov = parts.length >= 2 ? parts.slice(0, -1).join('/') : m;
66
- const short = m.split('/').pop();
67
- if (!groups.has(prov))
68
- groups.set(prov, []);
69
- groups.get(prov)?.push({ num: idx, short, full: m });
220
+ if (driver.namespaced) {
221
+ const parts = m.split('/');
222
+ const prov = parts.length >= 2 ? parts.slice(0, -1).join('/') : m;
223
+ const short = parts.pop();
224
+ if (!groups.has(prov))
225
+ groups.set(prov, []);
226
+ groups.get(prov)?.push({ num: idx, short, full: m });
227
+ }
228
+ else {
229
+ // Flat list — bucket under the driver label so the rendered card
230
+ // still has a header line.
231
+ if (!groups.has(driver.label))
232
+ groups.set(driver.label, []);
233
+ groups.get(driver.label)?.push({ num: idx, short: m, full: m });
234
+ }
70
235
  flat.push({ num: idx, full: m });
71
236
  idx++;
72
237
  }
73
- // Cache indexed list keyed identically to SessionManager.
74
238
  sessionModelIndex.set(sessionKey(ctx), flat.map(f => f.full));
75
- let msg = `📋 模型列表 (${filtered.length})\n\n`;
239
+ let msg = `📋 ${driver.label} 模型列表 (${filtered.length})\n\n`;
76
240
  for (const [prov, list] of groups) {
77
241
  msg += `**${prov}**\n`;
78
242
  for (const { num, short, full } of list) {
@@ -80,19 +244,17 @@ async function handleModelList(filter, ctx) {
80
244
  msg += ` ${String(num).padStart(3, ' ')}. \`${short}\`${tag}\n`;
81
245
  }
82
246
  }
83
- msg += `\n当前: \`${current}\` ⭐\n/model <全名> 或 /model #序号 切换\n/models refresh 强制刷新缓存`;
247
+ msg += `\n当前: \`${current}\` ⭐\n/model <名称> 或 /model #序号 切换\n/models refresh 强制刷新缓存`;
84
248
  return msg;
85
249
  }
86
- /** /model show currently selected model */
87
- async function handleModelCurrent(ctx) {
250
+ async function handleModelCurrent(driver, ctx) {
88
251
  const session = await sessionManager.getOrCreateSession(ctx.platform, ctx.channelId, ctx.threadId, ctx.defaultAgent);
89
- const defaultModel = await resolveDefaultModel();
252
+ const defaultModel = await resolveDefaultModel(driver);
90
253
  const current = session?.model || defaultModel;
91
- return `🧠 当前模型: \`${current}\`\n\n使用 /model <全名> 或 /model #序号 切换\n使用 /models 查看所有模型`;
254
+ return `🧠 当前 ${driver.label} 模型: \`${current}\`\n\n使用 /model <名称> 或 /model #序号 切换\n使用 /models 查看所有模型`;
92
255
  }
93
- /** /model <name|#N> switch to a different model */
94
- async function handleModelSwitch(input, ctx) {
95
- // Check if input is an index reference #N or just N
256
+ async function handleModelSwitch(driver, input, ctx) {
257
+ // /model #N (or /model N) — index lookup
96
258
  const indexMatch = input.match(/^#?(\d+)$/);
97
259
  if (indexMatch) {
98
260
  const num = parseInt(indexMatch[1], 10);
@@ -106,77 +268,21 @@ async function handleModelSwitch(input, ctx) {
106
268
  const model = cached[num - 1];
107
269
  await sessionManager.getOrCreateSession(ctx.platform, ctx.channelId, ctx.threadId, ctx.defaultAgent);
108
270
  await sessionManager.patchSession(ctx.platform, ctx.channelId, ctx.threadId, { model });
109
- return `✅ 模型已切换: \`${model}\``;
271
+ return `✅ ${driver.label} 模型已切换: \`${model}\``;
110
272
  }
111
- // Check if input is a short name (no /) but looks like a model name
112
- if (!input.includes('/')) {
113
- return `⚠️ 模型格式: provider/model\n例: /model deepseek/deepseek-v4-pro\n或: /model #5 (序号切换)\n\n先 /models 查看`;
273
+ // Namespaced drivers require `provider/model`; flat drivers accept bare names.
274
+ if (driver.namespaced && !input.includes('/')) {
275
+ return `⚠️ 模型格式: provider/model\n${driver.switchHint}\n或: /model #N (序号切换)\n\n先 /models 查看`;
114
276
  }
115
- const models = await getModelList();
116
- const exactMatch = models.find(m => m === input || m.endsWith(`/${input}`));
277
+ const models = await getModelList(driver);
278
+ const exactMatch = models.find(m => m === input || (driver.namespaced && m.endsWith(`/${input}`)));
117
279
  if (!exactMatch && models.length > 0) {
118
- return `⚠️ 模型 "${input}" 不在列表中。\n\n先 /models 查看可用模型。`;
280
+ return `⚠️ 模型 "${input}" 不在 ${driver.label} 列表中。\n\n先 /models 查看可用模型。`;
119
281
  }
120
282
  const resolved = exactMatch || input;
121
283
  await sessionManager.getOrCreateSession(ctx.platform, ctx.channelId, ctx.threadId, ctx.defaultAgent);
122
284
  await sessionManager.patchSession(ctx.platform, ctx.channelId, ctx.threadId, { model: resolved });
123
- return `✅ 模型已切换: \`${resolved}\``;
124
- }
125
- let modelListCache = null;
126
- let defaultModelCache = null;
127
- function invalidateModelCache() {
128
- modelListCache = null;
129
- defaultModelCache = null;
130
- }
131
- function getModelList() {
132
- return new Promise((resolve) => {
133
- if (modelListCache && Date.now() - modelListCache.ts < MODEL_LIST_TTL_MS) {
134
- return resolve(modelListCache.value);
135
- }
136
- const proc = spawn('opencode', ['models'], { stdio: ['ignore', 'pipe', 'pipe'] });
137
- let out = '';
138
- proc.stdout?.on('data', d => out += d.toString());
139
- proc.on('close', () => {
140
- const lines = out.trim().split('\n').filter(Boolean);
141
- modelListCache = { value: lines, ts: Date.now() };
142
- resolve(lines);
143
- });
144
- proc.on('error', () => resolve([]));
145
- });
146
- }
147
- /** Resolve opencode's actual default model name */
148
- async function resolveDefaultModel() {
149
- if (defaultModelCache && Date.now() - defaultModelCache.ts < MODEL_LIST_TTL_MS) {
150
- return defaultModelCache.value;
151
- }
152
- // Read opencode.json to find the first configured model as the default
153
- try {
154
- const { readFile } = await import('node:fs/promises');
155
- const { homedir } = await import('node:os');
156
- const { join } = await import('node:path');
157
- const configPath = join(homedir(), '.config', 'opencode', 'opencode.json');
158
- const raw = await readFile(configPath, 'utf-8');
159
- const config = JSON.parse(raw);
160
- const provider = config.provider;
161
- if (provider) {
162
- for (const [, p] of Object.entries(provider)) {
163
- if (p.models && typeof p.models === 'object') {
164
- const keys = Object.keys(p.models);
165
- if (keys.length > 0) {
166
- defaultModelCache = { value: keys[0], ts: Date.now() };
167
- return keys[0];
168
- }
169
- }
170
- }
171
- }
172
- }
173
- catch { /* fall through */ }
174
- const models = await getModelList();
175
- if (models.length > 0) {
176
- defaultModelCache = { value: models[0], ts: Date.now() };
177
- return models[0];
178
- }
179
- return 'unknown';
285
+ return `✅ ${driver.label} 模型已切换: \`${resolved}\``;
180
286
  }
181
287
  /** Test-only diagnostic: clear caches between cases. */
182
288
  export const _internal = { invalidateModelCache };
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/commands/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/commands/model.ts"],"names":[],"mappings":"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"}
@@ -110,7 +110,7 @@ async function handleSave(payload, ctx) {
110
110
  }
111
111
  const geo = await geocodeAddress(address);
112
112
  if (!geo.ok) {
113
- return { ok: false, error: `geocode failed: ${geo.reason}${geo.message ? ' (' + geo.message + ')' : ''}` };
113
+ return { ok: false, error: `geocode failed: ${geo.reason}${geo.message ? ` (${geo.message})` : ''}` };
114
114
  }
115
115
  whereLat = geo.lat;
116
116
  whereLng = geo.lng;
@@ -247,7 +247,7 @@ async function handleUpdate(payload, ctx) {
247
247
  }
248
248
  const geo = await geocodeAddress(payload.address.trim());
249
249
  if (!geo.ok) {
250
- return { ok: false, error: `geocode failed: ${geo.reason}${geo.message ? ' (' + geo.message + ')' : ''}` };
250
+ return { ok: false, error: `geocode failed: ${geo.reason}${geo.message ? ` (${geo.message})` : ''}` };
251
251
  }
252
252
  u.whereLat = geo.lat;
253
253
  u.whereLng = geo.lng;