min-agent 0.4.1 → 0.5.1

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.
Files changed (69) hide show
  1. package/README.md +46 -2
  2. package/dist/agent.js +89 -29
  3. package/dist/cli/commands/chat.js +3 -0
  4. package/dist/cli/commands/ctx.js +7 -0
  5. package/dist/cli/commands/exec.js +3 -0
  6. package/dist/cli/commands/index.js +32 -7
  7. package/dist/cli/commands/memory.js +33 -15
  8. package/dist/cli/commands/setup.js +55 -3
  9. package/dist/cli/commands/shared.js +10 -1
  10. package/dist/cli/commands/think.js +12 -0
  11. package/dist/cli/commands/write-config.js +22 -0
  12. package/dist/cli/option-helpers.js +13 -1
  13. package/dist/cli/program.js +57 -14
  14. package/dist/cli/setup/detect.js +17 -0
  15. package/dist/cli/setup/flags.js +12 -0
  16. package/dist/cli/setup/flow.js +108 -0
  17. package/dist/cli/setup/provider-form.js +102 -0
  18. package/dist/cli/setup/ui.js +534 -0
  19. package/dist/code-mode.js +1 -1
  20. package/dist/config.js +93 -159
  21. package/dist/context-window.js +39 -49
  22. package/dist/ctx-cli.js +30 -0
  23. package/dist/ctx.js +80 -0
  24. package/dist/memory-cli.js +33 -0
  25. package/dist/memory.js +127 -46
  26. package/dist/model-catalog.js +285 -0
  27. package/dist/ollama-model.js +234 -0
  28. package/dist/ollama-openai-bridge.js +383 -0
  29. package/dist/permission-cli.js +1 -4
  30. package/dist/provider.js +4 -1
  31. package/dist/reasoning-stream.js +158 -0
  32. package/dist/sandbox-cli.js +1 -4
  33. package/dist/scope.js +23 -0
  34. package/dist/serve/common.js +22 -1
  35. package/dist/serve/routes-chat.js +21 -1
  36. package/dist/serve/routes-memory.js +31 -2
  37. package/dist/serve/routes-meta.js +69 -6
  38. package/dist/think-cli.js +36 -0
  39. package/dist/thinking-wire.js +239 -0
  40. package/dist/thinking.js +166 -0
  41. package/dist/token-display.js +10 -7
  42. package/dist/tools/todo.js +22 -8
  43. package/dist/tui/App.js +48 -8
  44. package/dist/tui/CtxPicker.js +68 -0
  45. package/dist/tui/InputBar.js +112 -37
  46. package/dist/tui/MessageList.js +53 -22
  47. package/dist/tui/StatusBar.js +7 -3
  48. package/dist/tui/ThinkPicker.js +75 -0
  49. package/dist/tui/bracketed-paste.js +37 -0
  50. package/dist/tui/caret-pos.js +10 -8
  51. package/dist/tui/index.js +13 -1
  52. package/dist/tui/layout.js +17 -0
  53. package/dist/tui/overlay-input.js +12 -0
  54. package/dist/tui/paste-draft.js +173 -0
  55. package/dist/tui/selection.js +8 -2
  56. package/dist/tui/slash-commands.js +24 -1
  57. package/dist/tui/slash-handler.js +88 -18
  58. package/dist/tui/text-width.js +6 -6
  59. package/dist/tui-chat.js +85 -7
  60. package/docs/API.md +69 -6
  61. package/docs/superpowers/plans/2026-08-23-cli-setup.md +501 -0
  62. package/docs/superpowers/plans/2026-08-23-input-paste-attachments.md +475 -0
  63. package/docs/superpowers/plans/2026-08-23-thinking-wire-profile.md +450 -0
  64. package/docs/superpowers/specs/2026-08-23-cli-setup-design.md +282 -0
  65. package/docs/superpowers/specs/2026-08-23-input-paste-attachments-design.md +174 -0
  66. package/docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md +140 -0
  67. package/package.json +1 -1
  68. package/skills/self-config/SKILL.md +7 -4
  69. package/skills/self-config/reference.md +12 -6
@@ -0,0 +1,450 @@
1
+ # 思考线协议适配 — 实现计划
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** 用户仍只配置 `off|low|medium|high|max`;发出 chat 请求时按 `(host, model)` 选择 `thinking.type` / `reasoning_effort` 的线协议。MiniMax 启发式先发 `adaptive`;其余若 400 且错误可解析,静默改 profile、同一请求只重试一次,并缓存 7 天。
6
+
7
+ **Architecture:** 用户意图留在 `ThinkingRequestBody`(ALS)。新建 `thinking-wire.ts`:启发式、学习缓存、`parseThinkingWireHint`、`fetchWithThinkingWire`。`applyThinkingToChatBody` 增加 profile 参数。`modelFetch` 改为 `fetchWithThinkingWire(inner, …).then(foldReasoningInChatResponse)`。Lonae 目录不改。
8
+
9
+ **Tech Stack:** TypeScript, bun:test
10
+
11
+ **Spec:** `docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md`
12
+
13
+ 测试命令(`packages/min-agent` 下):`bun test tests/<file>`;类型:`npx tsc --noEmit`;仓库根目录:`bun run typecheck`、`bun run check`。
14
+
15
+ ---
16
+
17
+ ## 文件结构
18
+
19
+ | 文件 | 职责 |
20
+ |------|------|
21
+ | `src/thinking-wire.ts`(新) | profile 类型、默认值、缓存键、启发式、缓存、hint 解析、`fetchWithThinkingWire` |
22
+ | `src/thinking.ts`(改) | `applyThinkingToChatBody(body, spec, profile?)` |
23
+ | `src/provider.ts`(改) | `modelFetch` 走 `fetchWithThinkingWire` |
24
+ | `tests/thinking-wire.test.ts`(新) | 启发式、hint、缓存、fetch 重试 |
25
+ | `tests/thinking.test.ts`(改) | 三种 thinkingType × 开/关、effort omit |
26
+ | `skills/self-config/SKILL.md`(改) | 映射说明 + 缓存文件名 |
27
+ | `skills/self-config/reference.md`(改) | `MIN_AGENT_CONFIG_DIR` 旁注明缓存文件 |
28
+
29
+ `docs/API.md`、`README.md`、`/help`、Lonae catalog、`/think` UI **不改**。
30
+
31
+ ---
32
+
33
+ ## Task 1: thinking-wire 纯函数与缓存
34
+
35
+ **Files:**
36
+ - Create: `src/thinking-wire.ts`
37
+ - Test: `tests/thinking-wire.test.ts`
38
+
39
+ - [ ] **Step 1: 写失败测试**
40
+
41
+ ```ts
42
+ import { expect, test, afterEach } from "bun:test"
43
+ import { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs"
44
+ import os from "os"
45
+ import path from "path"
46
+ import {
47
+ DEFAULT_THINKING_WIRE,
48
+ THINKING_WIRE_TTL,
49
+ clearThinkingWireCache,
50
+ inferThinkingWire,
51
+ parseThinkingWireHint,
52
+ resolveThinkingWireProfile,
53
+ saveLearnedThinkingWire,
54
+ thinkingWireCacheKey,
55
+ } from "../src/thinking-wire.js"
56
+
57
+ function withIsolatedConfig(fn: () => void): void {
58
+ const home = realpathSync(mkdtempSync(path.join(os.tmpdir(), "min-agent-wire-")))
59
+ const old = process.env.MIN_AGENT_CONFIG_DIR
60
+ process.env.MIN_AGENT_CONFIG_DIR = home
61
+ clearThinkingWireCache()
62
+ try {
63
+ fn()
64
+ } finally {
65
+ clearThinkingWireCache()
66
+ if (old) process.env.MIN_AGENT_CONFIG_DIR = old
67
+ else delete process.env.MIN_AGENT_CONFIG_DIR
68
+ rmSync(home, { recursive: true, force: true })
69
+ }
70
+ }
71
+
72
+ afterEach(() => clearThinkingWireCache())
73
+
74
+ test("cache key lowercases host and model", () => {
75
+ expect(thinkingWireCacheKey("Api.Example.COM", "MiniMax-M3")).toBe("api.example.com::minimax-m3")
76
+ expect(thinkingWireCacheKey("", "m")).toBe("m")
77
+ })
78
+
79
+ test("inferThinkingWire prefers minimax then openai then default", () => {
80
+ expect(inferThinkingWire("minimax-m3", "api.console.go", "openai-compatible")).toEqual({
81
+ thinkingType: "adaptive-disabled",
82
+ effort: "reasoning_effort",
83
+ })
84
+ expect(inferThinkingWire("gpt-4", "api.minimax.io", undefined)).toEqual({
85
+ thinkingType: "adaptive-disabled",
86
+ effort: "reasoning_effort",
87
+ })
88
+ expect(inferThinkingWire("gpt-5", "api.openai.com", "openai")).toEqual({
89
+ thinkingType: "omit",
90
+ effort: "reasoning_effort",
91
+ })
92
+ expect(inferThinkingWire("deepseek-v3", "api.deepseek.com", "openai-compatible")).toEqual({
93
+ thinkingType: "enabled-disabled",
94
+ effort: "reasoning_effort",
95
+ })
96
+ })
97
+
98
+ test("parseThinkingWireHint reads MiniMax allowed list", () => {
99
+ const raw =
100
+ 'AI_APICallError: Error from provider (Console Go): Upstream request failed: [bad_request_error] invalid params, invalid thinking.type: "enabled" (allowed: adaptive, disabled) (2013)'
101
+ expect(parseThinkingWireHint(raw)).toEqual({ thinkingType: "adaptive-disabled" })
102
+ expect(parseThinkingWireHint('invalid thinking.type: "adaptive" (allowed: enabled, disabled)')).toEqual({
103
+ thinkingType: "enabled-disabled",
104
+ })
105
+ })
106
+
107
+ test("parseThinkingWireHint reads unknown parameter", () => {
108
+ expect(parseThinkingWireHint("Unknown parameter: 'thinking'")).toEqual({ thinkingType: "omit" })
109
+ expect(parseThinkingWireHint("unknown parameter: reasoning_effort")).toEqual({ effort: "omit" })
110
+ expect(parseThinkingWireHint("rate limit exceeded")).toBeNull()
111
+ })
112
+
113
+ test("resolveThinkingWireProfile prefers learned cache over heuristic", () => {
114
+ withIsolatedConfig(() => {
115
+ expect(resolveThinkingWireProfile("h.example", "minimax-m3").thinkingType).toBe("adaptive-disabled")
116
+ saveLearnedThinkingWire("h.example", "minimax-m3", {
117
+ ...DEFAULT_THINKING_WIRE,
118
+ thinkingType: "omit",
119
+ effort: "omit",
120
+ })
121
+ const learned = resolveThinkingWireProfile("h.example", "minimax-m3")
122
+ expect(learned.thinkingType).toBe("omit")
123
+ expect(learned.effort).toBe("omit")
124
+ const disk = JSON.parse(readFileSync(path.join(process.env.MIN_AGENT_CONFIG_DIR!, "thinking-wire-cache.json"), "utf-8"))
125
+ expect(disk["h.example::minimax-m3"].thinkingType).toBe("omit")
126
+ })
127
+ })
128
+
129
+ test("expired or wrong version cache is ignored", () => {
130
+ withIsolatedConfig(() => {
131
+ const file = path.join(process.env.MIN_AGENT_CONFIG_DIR!, "thinking-wire-cache.json")
132
+ writeFileSync(
133
+ file,
134
+ JSON.stringify({
135
+ "h::m": { version: 1, thinkingType: "omit", effort: "omit", timestamp: Date.now() - THINKING_WIRE_TTL - 1 },
136
+ "h::old": { version: 99, thinkingType: "omit", effort: "omit", timestamp: Date.now() },
137
+ }),
138
+ )
139
+ expect(resolveThinkingWireProfile("h", "m", "openai-compatible").thinkingType).toBe("enabled-disabled")
140
+ expect(resolveThinkingWireProfile("h", "old", "openai-compatible").thinkingType).toBe("enabled-disabled")
141
+ })
142
+ })
143
+ ```
144
+
145
+ 先跑确认失败:
146
+
147
+ ```bash
148
+ cd packages/min-agent && bun test tests/thinking-wire.test.ts
149
+ ```
150
+
151
+ - [ ] **Step 2: 实现**
152
+
153
+ `src/thinking-wire.ts`(与 `model-catalog.ts` 同样用 `getConfigDir()` + `atomicWriteFileSync`):
154
+
155
+ ```ts
156
+ export const THINKING_WIRE_VERSION = 1
157
+ export const THINKING_WIRE_TTL = 7 * 24 * 60 * 60 * 1000
158
+ export const DEFAULT_THINKING_WIRE = {
159
+ version: THINKING_WIRE_VERSION,
160
+ thinkingType: "enabled-disabled" as const,
161
+ effort: "reasoning_effort" as const,
162
+ }
163
+
164
+ export type ThinkingTypeMode = "enabled-disabled" | "adaptive-disabled" | "omit"
165
+ export type EffortMode = "reasoning_effort" | "omit"
166
+ export interface ThinkingWireProfile {
167
+ version: number
168
+ thinkingType: ThinkingTypeMode
169
+ effort: EffortMode
170
+ timestamp: number
171
+ }
172
+ export type ThinkingWireHint = Partial<Pick<ThinkingWireProfile, "thinkingType" | "effort">>
173
+ ```
174
+
175
+ - `thinkingWireCacheKey(host, modelId)`:都 `toLowerCase()`;host 空则只要 modelId
176
+ - `inferThinkingWire(modelId, host, providerType?)`:`minimax` 出现在 model 或 host(大小写不敏感)→ adaptive-disabled;`providerType === "openai"` → omit thinking;否则默认
177
+ - `resolveThinkingWireProfile(host, modelId, providerType?)`:有效学习缓存 → 否则启发式填 `timestamp: Date.now()`(启发式**不写盘**)
178
+ - `saveLearnedThinkingWire` 写内存 + `thinking-wire-cache.json`
179
+ - `clearThinkingWireCache()` 清内存(测试用);磁盘随 `MIN_AGENT_CONFIG_DIR` 隔离
180
+ - `parseThinkingWireHint(text)`:
181
+ - `/thinking\.type[\s\S]*?allowed:\s*([^)]+)/i`,列表按逗号拆、trim、去引号、小写;含 `adaptive` 且含 `disabled` → `adaptive-disabled`;含 `enabled` 且含 `disabled` → `enabled-disabled`
182
+ - `/unknown parameter[:\s]+['"`]?thinking['"`]?/i` → omit thinking(不要误匹配 `thinking.type` 那段里的 thinking)
183
+ - `/unknown parameter[:\s]+['"`]?reasoning_effort['"`]?/i` → omit effort
184
+ - 都不中 → `null`
185
+ - 未知 `version` 或过期 → 当未命中。损坏 JSON → `{}`
186
+
187
+ 本任务**不要**实现 fetch 包装。
188
+
189
+ - [ ] **Step 3: 测试通过并提交**
190
+
191
+ ```bash
192
+ cd packages/min-agent && bun test tests/thinking-wire.test.ts
193
+ git add src/thinking-wire.ts tests/thinking-wire.test.ts
194
+ git commit -m "feat: 增加思考线协议 profile 与错误 hint 解析"
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Task 2: applyThinkingToChatBody 按 profile 拼 body
200
+
201
+ **Files:**
202
+ - Modify: `src/thinking.ts`
203
+ - Modify: `tests/thinking.test.ts`
204
+
205
+ - [ ] **Step 1: 改测试**
206
+
207
+ 把现有「injects reasoning_effort and thinking.type」扩成显式 profile(缺省第三参 = `DEFAULT_THINKING_WIRE` 的 type/effort):
208
+
209
+ ```ts
210
+ import { DEFAULT_THINKING_WIRE } from "../src/thinking-wire.js"
211
+
212
+ test("applyThinkingToChatBody follows thinkingType and effort modes", () => {
213
+ const specOn = { reasoningEffort: "high", thinkingEnabled: true }
214
+ const specOff = { reasoningEffort: "none", thinkingEnabled: false }
215
+ expect(applyThinkingToChatBody({ model: "m" }, specOn)).toEqual({
216
+ model: "m",
217
+ reasoning_effort: "high",
218
+ thinking: { type: "enabled" },
219
+ })
220
+ expect(
221
+ applyThinkingToChatBody({ model: "m" }, specOn, { ...DEFAULT_THINKING_WIRE, thinkingType: "adaptive-disabled" }),
222
+ ).toEqual({
223
+ model: "m",
224
+ reasoning_effort: "high",
225
+ thinking: { type: "adaptive" },
226
+ })
227
+ expect(applyThinkingToChatBody({ model: "m" }, specOff, { ...DEFAULT_THINKING_WIRE, thinkingType: "adaptive-disabled" })).toEqual({
228
+ model: "m",
229
+ reasoning_effort: "none",
230
+ thinking: { type: "disabled" },
231
+ })
232
+ expect(applyThinkingToChatBody({ thinking: { type: "enabled" }, reasoning_effort: "x" }, specOn, {
233
+ ...DEFAULT_THINKING_WIRE,
234
+ thinkingType: "omit",
235
+ effort: "omit",
236
+ })).toEqual({})
237
+ })
238
+ ```
239
+
240
+ - [ ] **Step 2: 实现**
241
+
242
+ ```ts
243
+ export function applyThinkingToChatBody(
244
+ body: Record<string, unknown>,
245
+ spec: ThinkingRequestBody,
246
+ profile: Pick<ThinkingWireProfile, "thinkingType" | "effort"> = DEFAULT_THINKING_WIRE,
247
+ ): Record<string, unknown> {
248
+ const next: Record<string, unknown> = { ...body }
249
+ if (profile.effort === "omit") delete next.reasoning_effort
250
+ else next.reasoning_effort = spec.reasoningEffort
251
+ if (profile.thinkingType === "omit") delete next.thinking
252
+ else {
253
+ const on = profile.thinkingType === "adaptive-disabled" ? "adaptive" : "enabled"
254
+ next.thinking = { type: spec.thinkingEnabled ? on : "disabled" }
255
+ }
256
+ return next
257
+ }
258
+ ```
259
+
260
+ `ThinkingRequestBody` 不变。`resolveThinkingRequest` / `toWireEffort` 不变。
261
+
262
+ - [ ] **Step 3: 测试通过并提交**
263
+
264
+ ```bash
265
+ cd packages/min-agent && bun test tests/thinking.test.ts
266
+ git add src/thinking.ts tests/thinking.test.ts
267
+ git commit -m "feat: 按 wire profile 组装思考请求体"
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Task 3: fetch 400 改参并只重试一次
273
+
274
+ **Files:**
275
+ - Modify: `src/thinking-wire.ts`
276
+ - Modify: `tests/thinking-wire.test.ts`
277
+
278
+ - [ ] **Step 1: 写失败测试**
279
+
280
+ 在 `thinking-wire.test.ts` 增加(仍用隔离 `MIN_AGENT_CONFIG_DIR` + `thinkingRequestStore.run`):
281
+
282
+ ```ts
283
+ import { thinkingRequestStore } from "../src/thinking.js"
284
+ import { fetchWithThinkingWire } from "../src/thinking-wire.js"
285
+
286
+ test("fetchWithThinkingWire retries once when 400 names adaptive", async () => {
287
+ withIsolatedConfig(() => {
288
+ const bodies: string[] = []
289
+ const inner: FetchLike = async (_input, init) => {
290
+ bodies.push(String(init?.body ?? ""))
291
+ if (bodies.length === 1) {
292
+ return new Response(
293
+ 'invalid thinking.type: "enabled" (allowed: adaptive, disabled)',
294
+ { status: 400 },
295
+ )
296
+ }
297
+ return new Response("{}", { status: 200 })
298
+ }
299
+ return thinkingRequestStore.run({ reasoningEffort: "high", thinkingEnabled: true }, async () => {
300
+ const res = await fetchWithThinkingWire(inner, "https://gw.example/v1/chat/completions", {
301
+ method: "POST",
302
+ body: JSON.stringify({ model: "other-model", messages: [] }),
303
+ })
304
+ expect(res.status).toBe(200)
305
+ expect(bodies).toHaveLength(2)
306
+ expect(JSON.parse(bodies[0]!).thinking).toEqual({ type: "enabled" })
307
+ expect(JSON.parse(bodies[1]!).thinking).toEqual({ type: "adaptive" })
308
+ })
309
+ })
310
+ })
311
+
312
+ test("fetchWithThinkingWire does not retry unrelated 400", async () => {
313
+ withIsolatedConfig(() => {
314
+ let n = 0
315
+ const inner: FetchLike = async () => {
316
+ n++
317
+ return new Response("insufficient quota", { status: 400 })
318
+ }
319
+ return thinkingRequestStore.run({ reasoningEffort: "high", thinkingEnabled: true }, async () => {
320
+ const res = await fetchWithThinkingWire(inner, "https://gw.example/v1/chat/completions", {
321
+ method: "POST",
322
+ body: JSON.stringify({ model: "m" }),
323
+ })
324
+ expect(res.status).toBe(400)
325
+ expect(await res.text()).toBe("insufficient quota")
326
+ expect(n).toBe(1)
327
+ })
328
+ })
329
+ })
330
+ ```
331
+
332
+ `withIsolatedConfig` 若目前是同步的,改成支持 `fn: () => void | Promise<void>` 并 `await`。`FetchLike` 从 `fetch-timeout.js` 导入。
333
+
334
+ 再加一条:hint 相对当前 profile 无变化(已经是 adaptive,400 仍说 allowed adaptive)→ 不重试。可用 minimax 模型名让第一次就发 adaptive,inner 只 400 一次。
335
+
336
+ - [ ] **Step 2: 实现 `fetchWithThinkingWire`**
337
+
338
+ 签名:
339
+
340
+ ```ts
341
+ export async function fetchWithThinkingWire(
342
+ inner: FetchLike,
343
+ input: string | URL | Request,
344
+ init?: RequestInit,
345
+ opts?: { providerType?: string },
346
+ ): Promise<Response>
347
+ ```
348
+
349
+ 逻辑:
350
+
351
+ 1. `spec = thinkingRequestStore.getStore()`;无 spec 或 `init.body` 非字符串 → `return inner(input, init)`
352
+ 2. JSON.parse body;非对象 → `return inner(input, init)`
353
+ 3. `host` 从 `input` 解析 `URL.hostname`(`Request` 用 `.url`);失败则 `""`
354
+ 4. `modelId` = body.model 字符串,否则 `""`
355
+ 5. `providerType` = `opts?.providerType ?? getActiveProvider(getEffectiveConfig())?.type`
356
+ 6. `profile = resolveThinkingWireProfile(host, modelId, providerType)`
357
+ 7. `firstInit = { ...init, body: JSON.stringify(applyThinkingToChatBody(parsed, spec, profile)) }`
358
+ 8. `first = await inner(input, firstInit)`;`first.status !== 400` → `return first`
359
+ 9. `text = await first.text()`;`hint = parseThinkingWireHint(text)`
360
+ 10. 合并 hint:`next = { ...profile, ...hint, version: 1, timestamp: Date.now() }`;`thinkingType` 与 `effort` 都与当前相同 → `return rebuild400(first, text)`
361
+ 11. `saveLearnedThinkingWire(host, modelId, next)`;第二次 `inner(input, { ...init, body: JSON.stringify(applyThinkingToChatBody(parsed, spec, next)) })`,**不再**根据第二次状态重试
362
+ 12. `rebuild400`:`new Response(text, { status: 400, statusText: first.statusText, headers: first.headers })`
363
+
364
+ `getActiveProvider` 在 `thinking-wire.ts` 引用 `config.js` 可以;测试不依赖真实 config(默认走 enabled-disabled)。
365
+
366
+ - [ ] **Step 3: 测试通过并提交**
367
+
368
+ ```bash
369
+ cd packages/min-agent && bun test tests/thinking-wire.test.ts
370
+ git add src/thinking-wire.ts tests/thinking-wire.test.ts
371
+ git commit -m "feat: 思考字段 400 时改 profile 并只重试一次"
372
+ ```
373
+
374
+ ---
375
+
376
+ ## Task 4: 接入 modelFetch
377
+
378
+ **Files:**
379
+ - Modify: `src/provider.ts`
380
+
381
+ - [ ] **Step 1: 接线**
382
+
383
+ 删除 `withThinkingBody`。`modelFetch`:
384
+
385
+ ```ts
386
+ return (input, init) => fetchWithThinkingWire(inner, input, init).then(foldReasoningInChatResponse)
387
+ ```
388
+
389
+ 从 `thinking-wire.js` 导入 `fetchWithThinkingWire`;`thinking.ts` 不再被 `provider.ts` 直接用来拼 body(ALS 仍由 agent 设置,fetch 内部读)。
390
+
391
+ 现有 `tests/provider.test.ts` 应仍通过(不发真实请求)。
392
+
393
+ - [ ] **Step 2: 测试并提交**
394
+
395
+ ```bash
396
+ cd packages/min-agent && bun test tests/provider.test.ts tests/thinking-wire.test.ts tests/thinking.test.ts
397
+ git add src/provider.ts
398
+ git commit -m "feat: 模型请求走思考线协议适配"
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Task 5: self-config 文档
404
+
405
+ **Files:**
406
+ - Modify: `skills/self-config/SKILL.md`
407
+ - Modify: `skills/self-config/reference.md`
408
+
409
+ - [ ] **Step 1: 文案**
410
+
411
+ `SKILL.md` 思考段落里「The request sends `reasoning_effort` plus `thinking.type`」改为:请求会按当前模型和网关映射思考字段(例如 MiniMax 用 `adaptive`/`disabled`,官方 OpenAI 不发 `thinking` 对象);学到的映射缓存在 `~/.min-agent/thinking-wire-cache.json`(7 天)。官方 OpenAI 仍把 `max` 映射为 `xhigh`。不要写 400 重试、fetch 包装、profile 类型名。
412
+
413
+ `reference.md` 环境变量表 `MIN_AGENT_CONFIG_DIR` 一行改为同时提到目录缓存与思考映射缓存,例如:`Replaces ~/.min-agent`(含 `model-catalog-cache.json`、`thinking-wire-cache.json`)。
414
+
415
+ README / API.md / `/help` 不改。
416
+
417
+ - [ ] **Step 2: 提交**
418
+
419
+ ```bash
420
+ git add packages/min-agent/skills/self-config/SKILL.md packages/min-agent/skills/self-config/reference.md
421
+ git commit -m "docs: 说明思考请求按模型映射线协议"
422
+ ```
423
+
424
+ (在仓库根目录 add 时用上表路径;在 `packages/min-agent` 内则用 `skills/self-config/…`。)
425
+
426
+ ---
427
+
428
+ ## Task 6: 回归
429
+
430
+ - [ ] **Step 1: 全量相关测试与检查**
431
+
432
+ ```bash
433
+ cd packages/min-agent && bun test tests/thinking.test.ts tests/thinking-wire.test.ts tests/provider.test.ts
434
+ # 仓库根目录
435
+ bun run typecheck
436
+ bun run check
437
+ ```
438
+
439
+ - [ ] **Step 2: 若有格式问题则 `bun run format` 后再 check,不要 `--no-verify`**
440
+
441
+ ---
442
+
443
+ ## 手工验收
444
+
445
+ 在 `packages/min-agent` 下用 MiniMax-M3(思考非 off)发一句 `hello`:
446
+
447
+ 1. 不应再出现 `invalid thinking.type: "enabled"`
448
+ 2. 状态栏仍是「思考 high」这类用户档位,不出现 `adaptive`
449
+ 3. 换一个会 400 且错误带 `allowed: adaptive, disabled` 的网关:第一次失败后同一句应自动成功;`~/.min-agent/thinking-wire-cache.json` 出现对应 host+model 条目
450
+ 4. `/think off` 发送 `disabled`,对话仍可用