@bubblebrain-ai/bubble 0.0.25 → 0.0.27

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 (38) hide show
  1. package/README.md +4 -2
  2. package/dist/agent.js +1 -1
  3. package/dist/clipboard.d.ts +14 -0
  4. package/dist/clipboard.js +132 -0
  5. package/dist/model-catalog.d.ts +3 -1
  6. package/dist/model-catalog.js +17 -28
  7. package/dist/prompt/compose.js +1 -1
  8. package/dist/provider-anthropic.d.ts +4 -0
  9. package/dist/provider-anthropic.js +31 -0
  10. package/dist/provider-ark-responses.d.ts +17 -0
  11. package/dist/provider-ark-responses.js +462 -0
  12. package/dist/provider-transform.js +7 -0
  13. package/dist/provider.d.ts +7 -0
  14. package/dist/provider.js +150 -22
  15. package/dist/slash-commands/commands.js +22 -0
  16. package/dist/tools/todo.js +22 -38
  17. package/dist/tui-ink/app.js +82 -62
  18. package/dist/tui-ink/input-box.d.ts +1 -0
  19. package/dist/tui-ink/input-box.js +23 -17
  20. package/dist/tui-ink/message-list.d.ts +17 -1
  21. package/dist/tui-ink/message-list.js +74 -13
  22. package/dist/tui-ink/model-picker.d.ts +3 -2
  23. package/dist/tui-ink/model-picker.js +17 -4
  24. package/dist/tui-ink/question-dialog.js +36 -10
  25. package/dist/tui-ink/run.js +14 -22
  26. package/dist/tui-ink/terminal-mouse.d.ts +11 -0
  27. package/dist/tui-ink/terminal-mouse.js +13 -0
  28. package/dist/tui-ink/welcome.js +13 -3
  29. package/dist/variant/variant-resolver.js +4 -1
  30. package/package.json +1 -1
  31. package/dist/tui/transcript-scroll.d.ts +0 -25
  32. package/dist/tui/transcript-scroll.js +0 -20
  33. package/dist/tui-ink/transcript-input.d.ts +0 -8
  34. package/dist/tui-ink/transcript-input.js +0 -9
  35. package/dist/tui-ink/transcript-viewport-math.d.ts +0 -10
  36. package/dist/tui-ink/transcript-viewport-math.js +0 -16
  37. package/dist/tui-ink/transcript-viewport.d.ts +0 -24
  38. package/dist/tui-ink/transcript-viewport.js +0 -83
package/README.md CHANGED
@@ -69,7 +69,9 @@ Bubble ships with a catalog of built-in providers. Configure them inside the app
69
69
  | `/key <provider> <key>` | Set the API key for a provider. |
70
70
  | `/model` | Pick the active model and reasoning effort. |
71
71
 
72
- Built-in providers include OpenAI, Anthropic, Google, DeepSeek, Moonshot (CN and international), Kimi for Coding, Zhipu AI, Z.AI, Alibaba DashScope, MiniMax, StepFun, Groq, Together AI, Fireworks, and a `local` profile for any OpenAI-compatible endpoint (Ollama, vLLM, LM Studio, etc.).
72
+ Built-in providers include OpenAI, Anthropic, Google, DeepSeek, Moonshot (CN and international), Kimi for Coding, Zhipu AI, Z.AI, Alibaba DashScope, Doubao (Volcengine Ark), MiniMax, StepFun, Groq, Together AI, Fireworks, and a `local` profile for any OpenAI-compatible endpoint (Ollama, vLLM, LM Studio, etc.).
73
+
74
+ For Doubao Seed models on Volcengine Ark, run `/provider --add doubao` and paste your Ark API key. The built-in endpoint is `https://ark.cn-beijing.volces.com/api/v3` and uses Ark's Responses API. The model picker exposes `minimal`, `low`, `medium`, and `high`, defaulting to `high`; `minimal` disables Ark thinking, while the other levels enable it.
73
75
 
74
76
  ### Custom providers and models
75
77
 
@@ -90,7 +92,7 @@ For full control — custom base URLs, self-hosted gateways, extra models, or pi
90
92
  }
91
93
  ```
92
94
 
93
- `protocol` accepts `openai-chat` (default) or `anthropic-messages`. Entries in `models.json` take precedence over the built-in catalog.
95
+ `protocol` accepts `openai-chat` (default), `anthropic-messages`, or `ark-responses`. Entries in `models.json` take precedence over the built-in catalog.
94
96
 
95
97
  ### Reasoning effort
96
98
 
package/dist/agent.js CHANGED
@@ -2047,7 +2047,7 @@ export class Agent {
2047
2047
  }
2048
2048
  if (toolCall.argsCorrupt) {
2049
2049
  return {
2050
- content: `Error: The arguments for "${toolCall.name}" failed to parse as JSON, indicating the tool call was truncated or malformed mid-stream. ` +
2050
+ content: `Error: The arguments for "${toolCall.name}" failed to parse as JSON, indicating the provider returned truncated or malformed tool arguments. ` +
2051
2051
  `Re-issue the call with valid JSON arguments; do not assume the previous attempt ran.`,
2052
2052
  isError: true,
2053
2053
  status: "blocked",
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build the OSC 52 escape sequence for the given text. Returns `null` when the
3
+ * base64 payload exceeds {@link MAX_OSC52_ENCODED_LENGTH}.
4
+ *
5
+ * Format: ESC ] 52 ; c ; <base64> BEL => `\x1b]52;c;<base64>\x07`
6
+ *
7
+ * Exported for unit testing the encoding/cap logic in isolation.
8
+ */
9
+ export declare function encodeOsc52(text: string): string | null;
10
+ /**
11
+ * Copy `text` to the system clipboard. Resolves once a copy path succeeds and
12
+ * throws only when no path (native tools nor OSC 52) could place the text.
13
+ */
14
+ export declare function copyToClipboard(text: string): Promise<void>;
@@ -0,0 +1,132 @@
1
+ import { execSync, spawn } from "node:child_process";
2
+ import { platform } from "node:os";
3
+ /**
4
+ * Maximum length of the base64-encoded payload we are willing to emit via
5
+ * OSC 52. Very large payloads can desynchronize terminal rendering and many
6
+ * terminals silently drop sequences past ~100k, so we cap and skip instead.
7
+ */
8
+ const MAX_OSC52_ENCODED_LENGTH = 100_000;
9
+ function copyToX11Clipboard(options) {
10
+ try {
11
+ execSync("xclip -selection clipboard", options);
12
+ }
13
+ catch {
14
+ execSync("xsel --clipboard --input", options);
15
+ }
16
+ }
17
+ /**
18
+ * True when we appear to be running over SSH/Mosh, i.e. the host running this
19
+ * process is not the machine whose clipboard the user is looking at.
20
+ */
21
+ function isRemoteSession(env = process.env) {
22
+ return Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.MOSH_CONNECTION);
23
+ }
24
+ /**
25
+ * True when running inside a tmux session. tmux intercepts/relays terminal
26
+ * clipboard escapes, so OSC 52 is the reliable path to reach the outer
27
+ * terminal's clipboard (especially tmux-over-ssh). Native pbcopy still works
28
+ * locally, so we treat this as "also emit OSC 52", never as a replacement.
29
+ */
30
+ function isTmuxSession(env = process.env) {
31
+ return Boolean(env.TMUX);
32
+ }
33
+ /**
34
+ * Build the OSC 52 escape sequence for the given text. Returns `null` when the
35
+ * base64 payload exceeds {@link MAX_OSC52_ENCODED_LENGTH}.
36
+ *
37
+ * Format: ESC ] 52 ; c ; <base64> BEL => `\x1b]52;c;<base64>\x07`
38
+ *
39
+ * Exported for unit testing the encoding/cap logic in isolation.
40
+ */
41
+ export function encodeOsc52(text) {
42
+ const encoded = Buffer.from(text).toString("base64");
43
+ if (encoded.length > MAX_OSC52_ENCODED_LENGTH) {
44
+ return null;
45
+ }
46
+ return `\x1b]52;c;${encoded}\x07`;
47
+ }
48
+ /**
49
+ * Write the OSC 52 sequence to stdout. Returns false (without writing) when the
50
+ * payload is too large to emit safely.
51
+ */
52
+ function emitOsc52(text) {
53
+ const sequence = encodeOsc52(text);
54
+ if (sequence === null) {
55
+ return false;
56
+ }
57
+ process.stdout.write(sequence);
58
+ return true;
59
+ }
60
+ /**
61
+ * Copy `text` to the system clipboard. Resolves once a copy path succeeds and
62
+ * throws only when no path (native tools nor OSC 52) could place the text.
63
+ */
64
+ export async function copyToClipboard(text) {
65
+ let copied = false;
66
+ const p = platform();
67
+ const options = { input: text, timeout: 5000, stdio: ["pipe", "ignore", "ignore"] };
68
+ try {
69
+ if (p === "darwin") {
70
+ execSync("pbcopy", options);
71
+ copied = true;
72
+ }
73
+ else if (p === "win32") {
74
+ execSync("clip", options);
75
+ copied = true;
76
+ }
77
+ else {
78
+ // Linux/other. Try Termux, Wayland, then X11 clipboard tools.
79
+ if (process.env.TERMUX_VERSION) {
80
+ try {
81
+ execSync("termux-clipboard-set", options);
82
+ copied = true;
83
+ }
84
+ catch {
85
+ // Fall back to Wayland or X11 tools.
86
+ }
87
+ }
88
+ if (!copied) {
89
+ const hasWaylandDisplay = Boolean(process.env.WAYLAND_DISPLAY);
90
+ const hasX11Display = Boolean(process.env.DISPLAY);
91
+ if (hasWaylandDisplay) {
92
+ try {
93
+ // Verify wl-copy exists (spawn errors are async and won't be caught).
94
+ execSync("which wl-copy", { stdio: "ignore" });
95
+ // wl-copy with execSync hangs due to fork behavior; use spawn instead.
96
+ const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] });
97
+ proc.stdin.on("error", () => {
98
+ // Ignore EPIPE errors if wl-copy exits early.
99
+ });
100
+ proc.stdin.write(text);
101
+ proc.stdin.end();
102
+ proc.unref();
103
+ copied = true;
104
+ }
105
+ catch {
106
+ if (hasX11Display) {
107
+ copyToX11Clipboard(options);
108
+ copied = true;
109
+ }
110
+ }
111
+ }
112
+ else if (hasX11Display) {
113
+ copyToX11Clipboard(options);
114
+ copied = true;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ catch {
120
+ // Fall through to the OSC 52 fallback.
121
+ }
122
+ // Emit OSC 52 when the native clipboard may be unreachable from this host
123
+ // (remote sessions, tmux relaying to an outer terminal) or when no native
124
+ // tool succeeded. This is additive — never throw if a native copy worked.
125
+ if (isRemoteSession() || isTmuxSession() || !copied) {
126
+ const osc52Copied = emitOsc52(text);
127
+ copied = copied || osc52Copied;
128
+ }
129
+ if (!copied) {
130
+ throw new Error("Failed to copy to clipboard");
131
+ }
132
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ReasoningEffort } from "./types.js";
2
- export type ProviderProtocol = "openai-chat" | "anthropic-messages";
2
+ export type ProviderProtocol = "openai-chat" | "anthropic-messages" | "ark-responses";
3
3
  export interface BuiltinProviderDefinition {
4
4
  id: string;
5
5
  name: string;
@@ -13,6 +13,7 @@ export interface BuiltinModelDefinition {
13
13
  name: string;
14
14
  providerId: string;
15
15
  reasoningLevels: ReasoningEffort[];
16
+ defaultReasoningLevel?: ReasoningEffort;
16
17
  contextWindow?: number;
17
18
  /**
18
19
  * Server-declared cap on per-tool-output tokens. When set, the agent must
@@ -27,6 +28,7 @@ export declare const BUILTIN_MODELS: BuiltinModelDefinition[];
27
28
  export declare function listBuiltinModels(providerId: string): BuiltinModelDefinition[];
28
29
  export declare function registerDynamicModelMetadata(model: BuiltinModelDefinition): void;
29
30
  export declare function getBuiltinModel(providerId: string, modelId: string): BuiltinModelDefinition | undefined;
31
+ export declare function getModelDefaultReasoningLevel(providerId: string, modelId: string): ReasoningEffort | undefined;
30
32
  export declare function getBuiltinProvider(providerId: string): BuiltinProviderDefinition | undefined;
31
33
  export declare function getModelContextWindow(providerId: string, modelId: string): number | undefined;
32
34
  export declare function getToolOutputTokenLimit(providerId: string, modelId: string): number | undefined;
@@ -10,9 +10,9 @@ export const BUILTIN_PROVIDERS = [
10
10
  { id: "zai", name: "Z.AI", baseURL: "https://api.z.ai/api/paas/v4" },
11
11
  { id: "zai-coding-plan", name: "Z.AI Coding Plan", baseURL: "https://api.z.ai/api/coding/paas/v4" },
12
12
  { id: "alibaba", name: "Alibaba DashScope", baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1" },
13
+ { id: "doubao", name: "Doubao (Volcengine Ark)", baseURL: "https://ark.cn-beijing.volces.com/api/v3", protocol: "ark-responses" },
13
14
  { id: "minimax", name: "MiniMax Token Plan", baseURL: "https://api.minimaxi.com/anthropic", protocol: "anthropic-messages" },
14
- { id: "minimax-openai", name: "MiniMax (OpenAI Compatible)", baseURL: "https://api.minimaxi.com/v1" },
15
- { id: "minimax-anthropic", name: "MiniMax (Anthropic Compatible)", baseURL: "https://api.minimaxi.com/anthropic", protocol: "anthropic-messages", hidden: true },
15
+ { id: "minimax-anthropic", name: "MiniMax API", baseURL: "https://api.minimaxi.com/anthropic", protocol: "anthropic-messages" },
16
16
  { id: "stepfun", name: "StepFun Step Plan", baseURL: "https://api.stepfun.com/step_plan/v1" },
17
17
  { id: "moonshot-cn", name: "Moonshot (国内 platform.moonshot.cn)", baseURL: "https://api.moonshot.cn/v1" },
18
18
  { id: "moonshot-intl", name: "Moonshot (海外 platform.moonshot.ai)", baseURL: "https://api.moonshot.ai/v1" },
@@ -40,10 +40,16 @@ const GLM_5_2_LEVELS = ["high", "max", "off"];
40
40
  const KIMI_THINKING_ONLY_LEVELS = ["medium"];
41
41
  const DEEPSEEK_V4_LEVELS = ["high", "max"];
42
42
  const STEPFUN_REASONING_LEVELS = ["off", "low", "medium", "high"];
43
+ const DOUBAO_SEED_REASONING_LEVELS = ["minimal", "low", "medium", "high"];
43
44
  const MINIMAX_M3_REASONING_LEVELS = ["off", "medium"];
44
45
  const MINIMAX_REASONING_LEVELS = ["medium"];
45
- const ANTHROPIC_ALWAYS_ADAPTIVE_LEVELS = ["medium"];
46
- const ANTHROPIC_ADAPTIVE_LEVELS = ["off", "medium"];
46
+ // Anthropic exposes reasoning depth through output_config.effort (low | medium
47
+ // | high | xhigh | max), not a token budget. xhigh is Opus 4.7+ only; max is
48
+ // Opus 4.6+/Sonnet 4.6/Fable 5; Haiku 4.5 does not accept the effort param.
49
+ // Fable 5 has thinking always on, so it has no "off". "off" disables thinking.
50
+ const ANTHROPIC_OPUS_EFFORT_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"];
51
+ const ANTHROPIC_SONNET_EFFORT_LEVELS = ["off", "low", "medium", "high", "max"];
52
+ const ANTHROPIC_FABLE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
47
53
  const ANTHROPIC_CHAT_LEVELS = ["off"];
48
54
  export const BUILTIN_MODELS = [
49
55
  { id: "gpt-5.5", name: "gpt-5.5", providerId: "openai-codex", reasoningLevels: ALL_OPENAI_LEVELS, contextWindow: 272000, toolOutputTokenLimit: 10000 },
@@ -61,9 +67,9 @@ export const BUILTIN_MODELS = [
61
67
  { id: "o1-preview", name: "o1-preview", providerId: "openai", reasoningLevels: ["off", "low", "medium", "high"], contextWindow: 128000 },
62
68
  { id: "o1-mini", name: "o1-mini", providerId: "openai", reasoningLevels: ["off", "low", "medium", "high"], contextWindow: 128000 },
63
69
  { id: "gpt-4-turbo", name: "gpt-4-turbo", providerId: "openai", reasoningLevels: OPENAI_CHAT_LEVELS, contextWindow: 128000 },
64
- { id: "claude-fable-5", name: "Claude Fable 5", providerId: "anthropic", reasoningLevels: ANTHROPIC_ALWAYS_ADAPTIVE_LEVELS, contextWindow: 1000000 },
65
- { id: "claude-opus-4-8", name: "Claude Opus 4.8", providerId: "anthropic", reasoningLevels: ANTHROPIC_ADAPTIVE_LEVELS, contextWindow: 1000000 },
66
- { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providerId: "anthropic", reasoningLevels: ANTHROPIC_ADAPTIVE_LEVELS, contextWindow: 1000000 },
70
+ { id: "claude-fable-5", name: "Claude Fable 5", providerId: "anthropic", reasoningLevels: ANTHROPIC_FABLE_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
71
+ { id: "claude-opus-4-8", name: "Claude Opus 4.8", providerId: "anthropic", reasoningLevels: ANTHROPIC_OPUS_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
72
+ { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providerId: "anthropic", reasoningLevels: ANTHROPIC_SONNET_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
67
73
  { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providerId: "anthropic", reasoningLevels: ANTHROPIC_CHAT_LEVELS, contextWindow: 200000 },
68
74
  { id: "deepseek-v4-flash", name: "deepseek-v4-flash", providerId: "deepseek", reasoningLevels: DEEPSEEK_V4_LEVELS, contextWindow: 1048576 },
69
75
  { id: "deepseek-v4-pro", name: "deepseek-v4-pro", providerId: "deepseek", reasoningLevels: DEEPSEEK_V4_LEVELS, contextWindow: 1048576 },
@@ -88,33 +94,13 @@ export const BUILTIN_MODELS = [
88
94
  { id: "glm-4.6", name: "GLM-4.6", providerId: "zai-coding-plan", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 200000 },
89
95
  { id: "qwen3.6-plus", name: "Qwen3.6 Plus", providerId: "alibaba", reasoningLevels: ["off"], contextWindow: 1048576 },
90
96
  { id: "qwen3.7-max", name: "Qwen3.7 Max", providerId: "alibaba", reasoningLevels: ["off"], contextWindow: 1048576 },
97
+ { id: "doubao-seed-2-1-pro-260628", name: "Doubao Seed 2.1 Pro", providerId: "doubao", reasoningLevels: DOUBAO_SEED_REASONING_LEVELS, defaultReasoningLevel: "high" },
91
98
  { id: "MiniMax-M3", name: "MiniMax M3", providerId: "minimax", reasoningLevels: MINIMAX_M3_REASONING_LEVELS, contextWindow: 1000000 },
92
99
  { id: "MiniMax-M2.7", name: "MiniMax M2.7", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
93
100
  { id: "MiniMax-M2.7-highspeed", name: "MiniMax M2.7 Highspeed", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
94
- { id: "MiniMax-M2.5", name: "MiniMax M2.5", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
95
- { id: "MiniMax-M2.5-highspeed", name: "MiniMax M2.5 Highspeed", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
96
- { id: "MiniMax-M2.1", name: "MiniMax M2.1", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
97
- { id: "MiniMax-M2.1-highspeed", name: "MiniMax M2.1 Highspeed", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
98
- { id: "MiniMax-M2", name: "MiniMax M2", providerId: "minimax", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
99
- { id: "M2-her", name: "M2-her", providerId: "minimax", reasoningLevels: ["off"], contextWindow: 64000 },
100
- { id: "MiniMax-M3", name: "MiniMax M3", providerId: "minimax-openai", reasoningLevels: MINIMAX_M3_REASONING_LEVELS, contextWindow: 1000000 },
101
- { id: "MiniMax-M2.7", name: "MiniMax M2.7", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
102
- { id: "MiniMax-M2.7-highspeed", name: "MiniMax M2.7 Highspeed", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
103
- { id: "MiniMax-M2.5", name: "MiniMax M2.5", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
104
- { id: "MiniMax-M2.5-highspeed", name: "MiniMax M2.5 Highspeed", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
105
- { id: "MiniMax-M2.1", name: "MiniMax M2.1", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
106
- { id: "MiniMax-M2.1-highspeed", name: "MiniMax M2.1 Highspeed", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
107
- { id: "MiniMax-M2", name: "MiniMax M2", providerId: "minimax-openai", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
108
- { id: "M2-her", name: "M2-her", providerId: "minimax-openai", reasoningLevels: ["off"], contextWindow: 64000 },
109
101
  { id: "MiniMax-M3", name: "MiniMax M3", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_M3_REASONING_LEVELS, contextWindow: 1000000 },
110
102
  { id: "MiniMax-M2.7", name: "MiniMax M2.7", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
111
103
  { id: "MiniMax-M2.7-highspeed", name: "MiniMax M2.7 Highspeed", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
112
- { id: "MiniMax-M2.5", name: "MiniMax M2.5", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
113
- { id: "MiniMax-M2.5-highspeed", name: "MiniMax M2.5 Highspeed", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
114
- { id: "MiniMax-M2.1", name: "MiniMax M2.1", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
115
- { id: "MiniMax-M2.1-highspeed", name: "MiniMax M2.1 Highspeed", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
116
- { id: "MiniMax-M2", name: "MiniMax M2", providerId: "minimax-anthropic", reasoningLevels: MINIMAX_REASONING_LEVELS, contextWindow: 204800 },
117
- { id: "M2-her", name: "M2-her", providerId: "minimax-anthropic", reasoningLevels: ["off"], contextWindow: 64000 },
118
104
  { id: "step-3.7-flash", name: "Step 3.7 Flash", providerId: "stepfun", reasoningLevels: STEPFUN_REASONING_LEVELS, contextWindow: 256000 },
119
105
  { id: "step-3.5-flash-2603", name: "Step 3.5 Flash 2603", providerId: "stepfun", reasoningLevels: STEPFUN_REASONING_LEVELS },
120
106
  { id: "step-3.5-flash", name: "Step 3.5 Flash", providerId: "stepfun", reasoningLevels: STEPFUN_REASONING_LEVELS },
@@ -171,6 +157,9 @@ export function getBuiltinModel(providerId, modelId) {
171
157
  ? BUILTIN_MODELS.find((model) => model.providerId === "openai-codex" && model.id === modelId)
172
158
  : undefined);
173
159
  }
160
+ export function getModelDefaultReasoningLevel(providerId, modelId) {
161
+ return getBuiltinModel(providerId, modelId)?.defaultReasoningLevel;
162
+ }
174
163
  export function getBuiltinProvider(providerId) {
175
164
  return BUILTIN_PROVIDERS.find((provider) => provider.id === providerId);
176
165
  }
@@ -84,7 +84,7 @@ function buildGuidelines(tools, extraGuidelines) {
84
84
  add("Skills may provide specialized workflows. When a task appears to match a specialized workflow, call skill_search to find relevant skills, then call skill with the exact name to load the selected skill before applying it");
85
85
  }
86
86
  if (tools.includes("todo_write")) {
87
- add("Use todo_write to plan any task that needs three or more concrete steps before you start. Mark each item completed as soon as it is done; do not batch updates");
87
+ add("Default to just doing the work. Reach for todo_write only when tracking progress would genuinely help — multi-phase work with real dependencies, a long task spanning several areas, an explicit user request, or steps you discover mid-task. Don't make a list to pad a task you could just do; when in doubt, skip it. Mark each item completed as soon as it is done; do not batch updates");
88
88
  }
89
89
  for (const item of extraGuidelines) {
90
90
  add(item);
@@ -22,7 +22,11 @@ interface AnthropicRequest {
22
22
  thinking?: {
23
23
  type: "adaptive";
24
24
  };
25
+ output_config?: {
26
+ effort: AnthropicEffort;
27
+ };
25
28
  }
29
+ type AnthropicEffort = "low" | "medium" | "high" | "xhigh" | "max";
26
30
  type AnthropicCacheControl = typeof ANTHROPIC_PROMPT_CACHE_CONTROL;
27
31
  interface AnthropicSystemBlock {
28
32
  type: "text";
@@ -17,6 +17,25 @@ const MINIMAX_PROMPT_CACHE_MODELS = new Set([
17
17
  "minimax-m2",
18
18
  "m2-her",
19
19
  ]);
20
+ function anthropicEffortForLevel(level) {
21
+ switch (level) {
22
+ case "off":
23
+ return undefined;
24
+ case "minimal":
25
+ case "low":
26
+ return "low";
27
+ case "medium":
28
+ return "medium";
29
+ case "high":
30
+ return "high";
31
+ case "xhigh":
32
+ return "xhigh";
33
+ case "max":
34
+ return "max";
35
+ default:
36
+ return undefined;
37
+ }
38
+ }
20
39
  export function createAnthropicMessagesProvider(options) {
21
40
  async function* streamChat(messages, chatOptions) {
22
41
  const body = buildAnthropicRequest(options, messages, {
@@ -87,6 +106,18 @@ export function buildAnthropicRequest(options, messages, chatOptions) {
87
106
  }
88
107
  if (effectiveThinkingLevel !== "off") {
89
108
  body.thinking = { type: "adaptive" };
109
+ // Apply the selected reasoning depth via output_config.effort. Without this
110
+ // every thinking request silently ran at Anthropic's default (high),
111
+ // ignoring the chosen level. effort is an official-API feature, so only
112
+ // send it to the official endpoint — anthropic-compatible third parties
113
+ // (e.g. MiniMax) reject it. Levels are already clamped to the model's
114
+ // supported set, so the value is always a valid effort for this model.
115
+ if (isOfficialAnthropicBaseUrl(options.baseURL)) {
116
+ const effort = anthropicEffortForLevel(effectiveThinkingLevel);
117
+ if (effort) {
118
+ body.output_config = { effort };
119
+ }
120
+ }
90
121
  }
91
122
  return body;
92
123
  }
@@ -0,0 +1,17 @@
1
+ import type { Provider, ProviderMessage, StreamChunk, ThinkingLevel, TokenUsage, ToolChoiceMode, ToolDefinition } from "./types.js";
2
+ export interface ArkResponsesProviderOptions {
3
+ providerId?: string;
4
+ apiKey: string;
5
+ baseURL: string;
6
+ thinkingLevel?: ThinkingLevel;
7
+ }
8
+ export declare function createArkResponsesProvider(options: ArkResponsesProviderOptions): Provider;
9
+ export declare function buildArkResponsesBody(messages: ProviderMessage[], options: {
10
+ model: string;
11
+ tools?: ToolDefinition[];
12
+ toolChoice?: ToolChoiceMode;
13
+ thinkingLevel?: ThinkingLevel;
14
+ stream: boolean;
15
+ }): Record<string, unknown>;
16
+ export declare function translateArkResponsesStream(response: Response): AsyncIterable<StreamChunk>;
17
+ export declare function normalizeArkResponsesUsage(usage: any): TokenUsage;