@bubblebrain-ai/bubble 0.0.58 → 0.0.60
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/README.md +56 -6
- package/dist/agent/child-runner.js +5 -1
- package/dist/agent/internal-reminder-sanitizer.d.ts +1 -1
- package/dist/agent/internal-reminder-sanitizer.js +7 -1
- package/dist/agent/profiles.js +4 -4
- package/dist/agent/routing-catalog.d.ts +19 -1
- package/dist/agent/routing-catalog.js +32 -13
- package/dist/agent/subagent/runtime.d.ts +28 -0
- package/dist/agent/subagent/runtime.js +34 -30
- package/dist/agent/subagent-control.d.ts +7 -0
- package/dist/agent/subagent-control.js +2 -0
- package/dist/agent/subagent-lifecycle-reminder.js +2 -1
- package/dist/agent/subagent-wait-guidance.d.ts +2 -0
- package/dist/agent/subagent-wait-guidance.js +8 -0
- package/dist/agent/subtask-policy.js +4 -4
- package/dist/agent.d.ts +20 -11
- package/dist/agent.js +200 -117
- package/dist/anthropic-thinking.d.ts +4 -0
- package/dist/anthropic-thinking.js +11 -0
- package/dist/approval/controller.d.ts +20 -6
- package/dist/approval/controller.js +67 -8
- package/dist/approval/protected-paths.d.ts +1 -0
- package/dist/approval/protected-paths.js +35 -0
- package/dist/approval/session-cache.d.ts +12 -26
- package/dist/approval/session-cache.js +20 -50
- package/dist/approval/types.d.ts +27 -1
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +25 -2
- package/dist/config.d.ts +3 -0
- package/dist/config.js +7 -0
- package/dist/context/checkpoint.d.ts +18 -0
- package/dist/context/checkpoint.js +117 -0
- package/dist/context/compact-llm.js +7 -0
- package/dist/context/compact.d.ts +8 -0
- package/dist/context/compact.js +40 -7
- package/dist/context/llm-compactor.d.ts +36 -2
- package/dist/context/llm-compactor.js +227 -44
- package/dist/context/session-write-lock.d.ts +20 -0
- package/dist/context/session-write-lock.js +162 -0
- package/dist/context/tool-images.d.ts +3 -0
- package/dist/context/tool-images.js +35 -0
- package/dist/context/usage.js +22 -5
- package/dist/feishu/agent-host/run-driver.js +56 -9
- package/dist/lsp/service.d.ts +13 -1
- package/dist/lsp/service.js +163 -43
- package/dist/main.js +82 -16
- package/dist/mcp/config.d.ts +4 -0
- package/dist/mcp/config.js +17 -0
- package/dist/mcp/manager.d.ts +13 -1
- package/dist/mcp/manager.js +32 -1
- package/dist/mcp/transports.js +11 -0
- package/dist/memory/db.d.ts +1 -0
- package/dist/memory/db.js +20 -4
- package/dist/memory/phase1.js +122 -16
- package/dist/memory/prompts.js +3 -0
- package/dist/model-catalog.js +19 -3
- package/dist/model-config.d.ts +1 -0
- package/dist/model-config.js +5 -5
- package/dist/model-pricing.js +26 -9
- package/dist/network/provider-transport-log.d.ts +12 -0
- package/dist/network/provider-transport-log.js +32 -0
- package/dist/network/provider-transport.js +6 -1
- package/dist/network/retry.d.ts +1 -1
- package/dist/network/retry.js +1 -1
- package/dist/oauth/grok.d.ts +1 -0
- package/dist/oauth/grok.js +20 -16
- package/dist/oauth/openai-codex.d.ts +1 -0
- package/dist/oauth/openai-codex.js +29 -25
- package/dist/oauth/refresh-control.d.ts +12 -0
- package/dist/oauth/refresh-control.js +42 -0
- package/dist/oauth/storage.d.ts +55 -1
- package/dist/oauth/storage.js +281 -16
- package/dist/permissions/rule.d.ts +6 -3
- package/dist/permissions/rule.js +71 -12
- package/dist/permissions/settings.d.ts +28 -1
- package/dist/permissions/settings.js +93 -3
- package/dist/permissions/shell-command.d.ts +36 -0
- package/dist/permissions/shell-command.js +226 -0
- package/dist/permissions/trust-prompt.d.ts +10 -0
- package/dist/permissions/trust-prompt.js +34 -0
- package/dist/permissions/trust.d.ts +26 -0
- package/dist/permissions/trust.js +113 -0
- package/dist/prompt/compose.d.ts +3 -0
- package/dist/prompt/compose.js +24 -7
- package/dist/prompt/environment.js +2 -2
- package/dist/prompt/reminders.js +1 -1
- package/dist/prompt/runtime.js +1 -1
- package/dist/provider-anthropic.d.ts +6 -2
- package/dist/provider-anthropic.js +98 -18
- package/dist/provider-error-record.d.ts +9 -0
- package/dist/provider-error-record.js +42 -14
- package/dist/provider-grok.js +5 -1
- package/dist/provider-openai-codex.d.ts +8 -0
- package/dist/provider-openai-codex.js +107 -24
- package/dist/provider-registry.d.ts +45 -2
- package/dist/provider-registry.js +300 -88
- package/dist/provider-transform.js +8 -0
- package/dist/provider-zen-errors.d.ts +9 -0
- package/dist/provider-zen-errors.js +36 -0
- package/dist/provider.js +36 -5
- package/dist/sdk/index.d.ts +69 -8
- package/dist/sdk/index.js +252 -39
- package/dist/sdk/replay-event-log.d.ts +7 -0
- package/dist/sdk/replay-event-log.js +66 -16
- package/dist/session-context-fence.d.ts +16 -0
- package/dist/session-context-fence.js +26 -0
- package/dist/session-log.d.ts +11 -2
- package/dist/session-log.js +187 -39
- package/dist/session-types.d.ts +5 -1
- package/dist/session.d.ts +39 -7
- package/dist/session.js +341 -93
- package/dist/skills/format.d.ts +31 -1
- package/dist/skills/format.js +45 -24
- package/dist/slash-commands/commands.d.ts +3 -0
- package/dist/slash-commands/commands.js +29 -14
- package/dist/system-prompt.d.ts +3 -1
- package/dist/tasks/manager.d.ts +7 -0
- package/dist/tasks/manager.js +97 -18
- package/dist/tools/agent-lifecycle.js +11 -7
- package/dist/tools/child-tools.d.ts +1 -1
- package/dist/tools/child-tools.js +4 -4
- package/dist/tools/edit-apply.d.ts +0 -3
- package/dist/tools/edit-apply.js +45 -249
- package/dist/tools/edit.js +3 -3
- package/dist/tools/exit-plan-mode.d.ts +5 -0
- package/dist/tools/exit-plan-mode.js +3 -2
- package/dist/tools/file-state.d.ts +4 -0
- package/dist/tools/file-state.js +12 -3
- package/dist/tools/grep.js +20 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools/ls.d.ts +3 -0
- package/dist/tools/ls.js +85 -0
- package/dist/tools/path-utils.d.ts +6 -0
- package/dist/tools/path-utils.js +38 -1
- package/dist/tools/sensitive-paths.d.ts +8 -0
- package/dist/tools/sensitive-paths.js +85 -3
- package/dist/tools/skill.d.ts +2 -0
- package/dist/tools/skill.js +34 -2
- package/dist/tools/task-tools.js +11 -7
- package/dist/tui/app.js +22 -12
- package/dist/tui/components/approval-dialog.d.ts +5 -5
- package/dist/tui/components/approval-dialog.js +16 -51
- package/dist/tui/components/skills-panel.d.ts +4 -0
- package/dist/tui/components/skills-panel.js +15 -1
- package/dist/tui/components/streaming-message.js +7 -2
- package/dist/tui/components/tasks-pane.d.ts +38 -0
- package/dist/tui/components/tasks-pane.js +182 -32
- package/dist/tui/components/transcript.d.ts +11 -4
- package/dist/tui/components/transcript.js +158 -71
- package/dist/tui/composer-autocomplete.d.ts +17 -2
- package/dist/tui/composer-autocomplete.js +79 -2
- package/dist/tui/controller/controller.d.ts +9 -4
- package/dist/tui/controller/controller.js +87 -95
- package/dist/tui/controller/goal-runtime-controller.js +1 -1
- package/dist/tui/controller/session-transition.js +1 -1
- package/dist/tui/model/subagent-view.d.ts +11 -19
- package/dist/tui/model/subagent-view.js +56 -85
- package/dist/tui/model/task-lifecycle.d.ts +57 -0
- package/dist/tui/model/task-lifecycle.js +124 -0
- package/dist/tui/model/trace-groups.js +10 -9
- package/dist/tui/trace-groups.js +10 -9
- package/dist/types.d.ts +20 -0
- package/dist/variant/variant-resolver.js +2 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -1
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +6 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -1
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +2 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -1
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +8 -4
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -1
- package/package.json +11 -3
- package/dist/prompt/skills.d.ts +0 -2
- package/dist/prompt/skills.js +0 -4
- package/dist/tools/glob.d.ts +0 -5
- package/dist/tools/glob.js +0 -199
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface PermissionTrustOptions {
|
|
2
|
+
bubbleHome?: string;
|
|
3
|
+
}
|
|
4
|
+
/** Parsed contents of the two repository settings files (null when absent). */
|
|
5
|
+
export interface RepoSettingsRaw {
|
|
6
|
+
project: unknown;
|
|
7
|
+
local: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface RepoCapabilities {
|
|
10
|
+
allow: string[];
|
|
11
|
+
mcpServers: string[];
|
|
12
|
+
/** LSP server ids whose definition carries more than an on/off switch. */
|
|
13
|
+
lspServers: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function getPermissionTrustPath(options?: PermissionTrustOptions): string;
|
|
16
|
+
export declare function repoCapabilities(raw: unknown): RepoCapabilities;
|
|
17
|
+
/** Everything the given repository settings would enable, both scopes merged. */
|
|
18
|
+
export declare function mergedRepoCapabilities(raw: RepoSettingsRaw): RepoCapabilities;
|
|
19
|
+
export declare function describeRepoCapabilities(caps: RepoCapabilities): string[];
|
|
20
|
+
export declare function hasRepoCapabilities(raw: RepoSettingsRaw): boolean;
|
|
21
|
+
export declare function repoConfigFingerprint(cwd: string, raw: RepoSettingsRaw): string;
|
|
22
|
+
/** True when the repository grants nothing, or the user trusted exactly this content. */
|
|
23
|
+
export declare function isRepoConfigTrusted(cwd: string, raw: RepoSettingsRaw, options?: PermissionTrustOptions): boolean;
|
|
24
|
+
export declare function trustRepoConfig(cwd: string, raw: RepoSettingsRaw, options?: PermissionTrustOptions): void;
|
|
25
|
+
/** Reads both repository settings files; unparsable files count as absent. */
|
|
26
|
+
export declare function readRepoSettings(cwd: string): RepoSettingsRaw;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { getBubbleHome } from "../bubble-home.js";
|
|
5
|
+
export function getPermissionTrustPath(options = {}) {
|
|
6
|
+
return join(options.bubbleHome ?? getBubbleHome(), "permissions-trust.json");
|
|
7
|
+
}
|
|
8
|
+
export function repoCapabilities(raw) {
|
|
9
|
+
const settings = isRecord(raw) ? raw : {};
|
|
10
|
+
const permissions = isRecord(settings.permissions) ? settings.permissions : {};
|
|
11
|
+
const allow = Array.isArray(permissions.allow)
|
|
12
|
+
? permissions.allow.filter((rule) => typeof rule === "string")
|
|
13
|
+
: [];
|
|
14
|
+
const mcpServers = isRecord(settings.mcpServers) ? Object.keys(settings.mcpServers) : [];
|
|
15
|
+
const lspServers = isRecord(settings.lsp)
|
|
16
|
+
? Object.entries(settings.lsp)
|
|
17
|
+
.filter(([, server]) => isRecord(server) && Object.keys(server).some((key) => key !== "disabled"))
|
|
18
|
+
.map(([id]) => id)
|
|
19
|
+
: [];
|
|
20
|
+
return { allow, mcpServers, lspServers };
|
|
21
|
+
}
|
|
22
|
+
/** Everything the given repository settings would enable, both scopes merged. */
|
|
23
|
+
export function mergedRepoCapabilities(raw) {
|
|
24
|
+
const project = repoCapabilities(raw.project);
|
|
25
|
+
const local = repoCapabilities(raw.local);
|
|
26
|
+
return {
|
|
27
|
+
allow: [...project.allow, ...local.allow],
|
|
28
|
+
mcpServers: [...new Set([...project.mcpServers, ...local.mcpServers])],
|
|
29
|
+
lspServers: [...new Set([...project.lspServers, ...local.lspServers])],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function describeRepoCapabilities(caps) {
|
|
33
|
+
return [
|
|
34
|
+
...caps.allow.map((rule) => `allow rule ${rule}`),
|
|
35
|
+
...caps.mcpServers.map((name) => `MCP server ${name} (Bubble starts it)`),
|
|
36
|
+
...caps.lspServers.map((id) => `LSP server ${id} (custom command, env or options)`),
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
export function hasRepoCapabilities(raw) {
|
|
40
|
+
return [raw.project, raw.local].some((scope) => {
|
|
41
|
+
const caps = repoCapabilities(scope);
|
|
42
|
+
return caps.allow.length + caps.mcpServers.length + caps.lspServers.length > 0;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function repoConfigFingerprint(cwd, raw) {
|
|
46
|
+
const pick = (scope) => {
|
|
47
|
+
const settings = isRecord(scope) ? scope : {};
|
|
48
|
+
const permissions = isRecord(settings.permissions) ? settings.permissions : {};
|
|
49
|
+
return { allow: permissions.allow ?? null, mcpServers: settings.mcpServers ?? null, lsp: settings.lsp ?? null };
|
|
50
|
+
};
|
|
51
|
+
return createHash("sha256")
|
|
52
|
+
.update(JSON.stringify({ cwd: safeRealpath(cwd), project: pick(raw.project), local: pick(raw.local) }))
|
|
53
|
+
.digest("hex");
|
|
54
|
+
}
|
|
55
|
+
/** True when the repository grants nothing, or the user trusted exactly this content. */
|
|
56
|
+
export function isRepoConfigTrusted(cwd, raw, options = {}) {
|
|
57
|
+
if (!hasRepoCapabilities(raw))
|
|
58
|
+
return true;
|
|
59
|
+
return readStore(options).projects[safeRealpath(cwd)]?.fingerprint === repoConfigFingerprint(cwd, raw);
|
|
60
|
+
}
|
|
61
|
+
export function trustRepoConfig(cwd, raw, options = {}) {
|
|
62
|
+
const store = readStore(options);
|
|
63
|
+
store.projects[safeRealpath(cwd)] = {
|
|
64
|
+
fingerprint: repoConfigFingerprint(cwd, raw),
|
|
65
|
+
trustedAt: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
const path = getPermissionTrustPath(options);
|
|
68
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
69
|
+
writeFileSync(path, JSON.stringify(store, null, 2) + "\n", "utf-8");
|
|
70
|
+
}
|
|
71
|
+
/** Reads both repository settings files; unparsable files count as absent. */
|
|
72
|
+
export function readRepoSettings(cwd) {
|
|
73
|
+
const read = (path) => {
|
|
74
|
+
if (!existsSync(path))
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
project: read(join(cwd, ".bubble", "settings.json")),
|
|
85
|
+
local: read(join(cwd, ".bubble", "settings.local.json")),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function readStore(options) {
|
|
89
|
+
const path = getPermissionTrustPath(options);
|
|
90
|
+
if (!existsSync(path))
|
|
91
|
+
return { version: 1, projects: {} };
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
94
|
+
if (parsed && typeof parsed.projects === "object" && parsed.projects !== null) {
|
|
95
|
+
return { version: 1, projects: parsed.projects };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// A corrupt store trusts nothing.
|
|
100
|
+
}
|
|
101
|
+
return { version: 1, projects: {} };
|
|
102
|
+
}
|
|
103
|
+
function isRecord(value) {
|
|
104
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
105
|
+
}
|
|
106
|
+
function safeRealpath(path) {
|
|
107
|
+
try {
|
|
108
|
+
return realpathSync(path);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return resolve(path);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/dist/prompt/compose.d.ts
CHANGED
|
@@ -6,7 +6,10 @@ export interface ComposeSystemPromptOptions extends EnvironmentPromptOptions {
|
|
|
6
6
|
guidelines?: string[];
|
|
7
7
|
thinkingLevel?: ThinkingLevel;
|
|
8
8
|
mode?: PermissionMode;
|
|
9
|
+
/** Prompt-visible skills; when given (and `skill` is a tool), listed as a catalog. */
|
|
9
10
|
skills?: SkillSummary[];
|
|
11
|
+
/** Catalog budget in characters (default SKILL_CATALOG_CHARS); 0 falls back to search only. */
|
|
12
|
+
skillCatalogChars?: number;
|
|
10
13
|
memoryPrompt?: string;
|
|
11
14
|
agentProfilePrompt?: string;
|
|
12
15
|
/** Rendered subagent model-routing menu (design §4). Placed right after the
|
package/dist/prompt/compose.js
CHANGED
|
@@ -9,6 +9,7 @@ import { buildKimiProviderPrompt } from "./provider-prompts/kimi.js";
|
|
|
9
9
|
import { buildDelegationPolicyPrompt } from "./delegation.js";
|
|
10
10
|
import { buildEnvironmentPrompt, defaultToolNames } from "./environment.js";
|
|
11
11
|
import { buildRuntimePrompt } from "./runtime.js";
|
|
12
|
+
import { buildSkillCatalog } from "../skills/format.js";
|
|
12
13
|
export function composeSystemPrompt(options = {}) {
|
|
13
14
|
const agentName = options.agentName ?? "Bubble";
|
|
14
15
|
const providerPrompt = buildProviderPrompt(agentName, options.configuredProvider, options.configuredModelId, options.configuredModel);
|
|
@@ -21,10 +22,20 @@ export function composeSystemPrompt(options = {}) {
|
|
|
21
22
|
tools: options.tools ?? defaultToolNames,
|
|
22
23
|
toolSnippets: options.toolSnippets,
|
|
23
24
|
});
|
|
25
|
+
const tools = options.tools ?? defaultToolNames;
|
|
26
|
+
// Level one of skill loading: names and one-line descriptions are always in
|
|
27
|
+
// context; bodies load on demand with `skill`. Last section, so the prefix
|
|
28
|
+
// above stays byte-identical while the skill set is unchanged.
|
|
29
|
+
const skillCatalog = options.skills && tools.includes("skill")
|
|
30
|
+
? buildSkillCatalog(options.skills, {
|
|
31
|
+
budgetChars: options.skillCatalogChars,
|
|
32
|
+
searchable: tools.includes("skill_search"),
|
|
33
|
+
})
|
|
34
|
+
: undefined;
|
|
24
35
|
const runtimePrompt = buildRuntimePrompt({
|
|
25
36
|
thinkingLevel: options.thinkingLevel,
|
|
26
37
|
mode: options.mode,
|
|
27
|
-
guidelines: buildGuidelines(
|
|
38
|
+
guidelines: buildGuidelines(tools, options.guidelines ?? [], skillCatalog),
|
|
28
39
|
});
|
|
29
40
|
const delegationPrompt = buildDelegationPolicyPrompt(options.tools ?? defaultToolNames);
|
|
30
41
|
// Same gate as the delegation policy: children (no spawn_agent) never see the menu.
|
|
@@ -37,6 +48,7 @@ export function composeSystemPrompt(options = {}) {
|
|
|
37
48
|
modelRoutingPrompt,
|
|
38
49
|
options.agentProfilePrompt,
|
|
39
50
|
options.memoryPrompt,
|
|
51
|
+
skillCatalog?.text,
|
|
40
52
|
].filter(Boolean).join("\n\n");
|
|
41
53
|
}
|
|
42
54
|
function buildProviderPrompt(agentName, providerId, modelId, modelName) {
|
|
@@ -67,23 +79,28 @@ function buildProviderPrompt(agentName, providerId, modelId, modelName) {
|
|
|
67
79
|
}
|
|
68
80
|
return buildDefaultProviderPrompt(agentName);
|
|
69
81
|
}
|
|
70
|
-
function buildGuidelines(tools, extraGuidelines) {
|
|
82
|
+
function buildGuidelines(tools, extraGuidelines, skillCatalog) {
|
|
71
83
|
const guidelines = [];
|
|
72
84
|
const add = (item) => {
|
|
73
85
|
if (!guidelines.includes(item)) {
|
|
74
86
|
guidelines.push(item);
|
|
75
87
|
}
|
|
76
88
|
};
|
|
77
|
-
if (tools.includes("
|
|
78
|
-
add("Use
|
|
89
|
+
if (tools.includes("ls")) {
|
|
90
|
+
add("Use ls to list directory contents, including files and subdirectories");
|
|
79
91
|
}
|
|
80
|
-
if (tools.includes("
|
|
81
|
-
add("Use grep
|
|
92
|
+
if (tools.includes("grep")) {
|
|
93
|
+
add("Use grep to search file contents");
|
|
94
|
+
}
|
|
95
|
+
if (tools.includes("bash")) {
|
|
96
|
+
add("Use bash for file operations like ls, find, and rg when appropriate");
|
|
82
97
|
}
|
|
83
98
|
if (tools.includes("question")) {
|
|
84
99
|
add("When the user is explicitly discussing, brainstorming, or shaping an approach instead of asking for immediate execution, use the question tool for targeted clarification or preference choices when it would materially improve the discussion; do not use it for generic permission-to-proceed questions");
|
|
85
100
|
}
|
|
86
|
-
|
|
101
|
+
// With a catalog the Skills section itself says how to load; search remains
|
|
102
|
+
// the only route when the host gave no catalog or turned it off.
|
|
103
|
+
if (!skillCatalog?.listed && tools.includes("skill_search") && tools.includes("skill")) {
|
|
87
104
|
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");
|
|
88
105
|
}
|
|
89
106
|
for (const item of extraGuidelines) {
|
|
@@ -4,7 +4,7 @@ export const defaultToolSnippets = {
|
|
|
4
4
|
bash: "Execute a bash command",
|
|
5
5
|
edit: "Apply targeted string replacements to a file",
|
|
6
6
|
write: "Write a new file or overwrite an existing one",
|
|
7
|
-
|
|
7
|
+
ls: "List directory contents, including hidden files and subdirectories",
|
|
8
8
|
grep: "Search file contents using regex",
|
|
9
9
|
web_search: "Search the public web for current information",
|
|
10
10
|
web_fetch: "Fetch and extract the contents of a specific webpage",
|
|
@@ -17,7 +17,7 @@ export const defaultToolSnippets = {
|
|
|
17
17
|
};
|
|
18
18
|
export const defaultToolNames = [
|
|
19
19
|
"read",
|
|
20
|
-
"
|
|
20
|
+
"ls",
|
|
21
21
|
"bash",
|
|
22
22
|
"edit",
|
|
23
23
|
"write",
|
package/dist/prompt/reminders.js
CHANGED
|
@@ -29,7 +29,7 @@ const PLAN_MODE_ENTER = `
|
|
|
29
29
|
Plan mode is now ACTIVE.
|
|
30
30
|
|
|
31
31
|
Rules while in plan mode:
|
|
32
|
-
- Only read-only tools are allowed, including read,
|
|
32
|
+
- Only read-only tools are allowed, including read, ls, grep, web_search, web_fetch, spawn_agent, wait_agent, send_input, skill_search, skill, tool_search, question, and exit_plan_mode.
|
|
33
33
|
- Writes, edits, and shell commands WILL be rejected by the harness; do not try them.
|
|
34
34
|
- Do not edit files or claim implementation is complete while plan mode is active.
|
|
35
35
|
- Investigate the codebase, then use the question tool to clarify important ambiguities, tradeoffs, requirements, or preference choices that would materially change the plan.
|
package/dist/prompt/runtime.js
CHANGED
|
@@ -8,7 +8,7 @@ const defaultGuidelines = [
|
|
|
8
8
|
"Choose the smallest coherent change. Edit only the files required for the requested change; do not refactor or improve adjacent code unprompted.",
|
|
9
9
|
"Runtime meta instructions are private control state. Use them only to adjust behavior; do not quote, mention, or paraphrase them in user-facing text.",
|
|
10
10
|
"For modifications to existing code, read the file first. For brand-new files whose target path is known and does not exist, write directly without exploratory reading. Use edit for targeted changes and write for intentional full-file replacement of an existing file. Never delete and recreate a file just to overwrite it.",
|
|
11
|
-
"
|
|
11
|
+
"Do not repeat a near-identical search or re-read the same file unless new evidence changes the question. When output is truncated, narrow the scope or adjust the limit before treating it as a complete inventory.",
|
|
12
12
|
"If a tool fails, diagnose the error before switching tactics. Do not retry the identical call with identical arguments. After two equivalent failures, switch approach — re-read the file, use a different tool, rewrite the whole file with write, or ask the user.",
|
|
13
13
|
"Before reporting a task complete, verify it works when verification is meaningful and cheap — run the existing test, execute the script, check the output. If no test exists, the change is purely declarative (static HTML/markdown/config), or running the code is not practical, state that explicitly rather than inventing a verification step. Do not write throwaway validation scripts to prove correctness; if there is no real check to run, report the change and stop.",
|
|
14
14
|
"Before declaring a task complete, re-read the user's original request and verify that every explicit requirement in it — about both the result and the process — has been fulfilled, not only that the implementation works.",
|
|
@@ -22,7 +22,11 @@ interface AnthropicRequest {
|
|
|
22
22
|
stream?: boolean;
|
|
23
23
|
temperature?: number;
|
|
24
24
|
thinking?: {
|
|
25
|
-
type: "adaptive";
|
|
25
|
+
type: "adaptive" | "enabled" | "disabled";
|
|
26
|
+
display?: "summarized";
|
|
27
|
+
block_binding?: {
|
|
28
|
+
prefix_mismatch_behavior: "drop_block";
|
|
29
|
+
};
|
|
26
30
|
};
|
|
27
31
|
output_config?: {
|
|
28
32
|
effort: AnthropicEffort;
|
|
@@ -113,7 +117,7 @@ export declare function buildAnthropicRequest(options: AnthropicProviderOptions,
|
|
|
113
117
|
export declare function placeMessageCacheBreakpoints(messages: AnthropicMessage[], budget: number): number;
|
|
114
118
|
export declare function resolveAnthropicMaxTokens(options: AnthropicProviderOptions, model: string): number;
|
|
115
119
|
export declare function supportsAnthropicPromptCache(options: AnthropicProviderOptions, model: string): boolean;
|
|
116
|
-
export declare function toAnthropicMessages(messages: ProviderMessage[], echoThinking?: boolean): {
|
|
120
|
+
export declare function toAnthropicMessages(messages: ProviderMessage[], echoThinking?: boolean | "all", allowUnsignedThinking?: boolean): {
|
|
117
121
|
system: string;
|
|
118
122
|
messages: AnthropicMessage[];
|
|
119
123
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { getAvailableThinkingLevels, normalizeThinkingLevel } from "./provider-transform.js";
|
|
2
|
+
import { getModelDefaultReasoningLevel } from "./model-catalog.js";
|
|
3
|
+
import { isClaudeWithBoundThinking } from "./anthropic-thinking.js";
|
|
2
4
|
import { RateLimitError } from "./network/errors.js";
|
|
3
5
|
import { isProviderTransportError, normalizeProviderNetworkError, providerFetch } from "./network/provider-transport.js";
|
|
4
6
|
import { computeRetryDelayMs, getProviderMaxRetries, isRetryableHttpStatus, ProviderStreamInterruptedError, retryAfterMsFromResponse, sleepBeforeRetry, } from "./network/retry.js";
|
|
5
7
|
const ANTHROPIC_VERSION = "2023-06-01";
|
|
8
|
+
const THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
|
|
6
9
|
const DEFAULT_MAX_TOKENS = 8192;
|
|
7
10
|
const ANTHROPIC_OPUS_LONG_OUTPUT_MAX_TOKENS = 128000;
|
|
8
11
|
const ANTHROPIC_LONG_OUTPUT_MAX_TOKENS = 64000;
|
|
@@ -11,6 +14,8 @@ const ANTHROPIC_LONG_OUTPUT_MAX_TOKENS = 64000;
|
|
|
11
14
|
// MiniMax's docs recommend M3 128K, M2.x 64K output.
|
|
12
15
|
const MINIMAX_M3_MAX_TOKENS = 128000;
|
|
13
16
|
const MINIMAX_M2_MAX_TOKENS = 64000;
|
|
17
|
+
// MiMo counts thinking and visible text together; match its documented 128K cap.
|
|
18
|
+
const MIMO_MAX_TOKENS = 131072;
|
|
14
19
|
const ANTHROPIC_PROMPT_CACHE_CONTROL = { type: "ephemeral" };
|
|
15
20
|
/** Hard API limit: a 5th cache_control breakpoint is a 400, not a warning. */
|
|
16
21
|
const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4;
|
|
@@ -55,6 +60,7 @@ export function createAnthropicMessagesProvider(options) {
|
|
|
55
60
|
});
|
|
56
61
|
const events = streamAnthropicEventsWithRetry(options, {
|
|
57
62
|
url: resolveAnthropicMessagesUrl(options.baseURL),
|
|
63
|
+
model: body.model,
|
|
58
64
|
stream: true,
|
|
59
65
|
method: "POST",
|
|
60
66
|
body: JSON.stringify(body),
|
|
@@ -73,20 +79,25 @@ export function createAnthropicMessagesProvider(options) {
|
|
|
73
79
|
});
|
|
74
80
|
const response = await fetchAnthropicResponseWithRetry(options, {
|
|
75
81
|
url: resolveAnthropicMessagesUrl(options.baseURL),
|
|
82
|
+
model: body.model,
|
|
76
83
|
stream: false,
|
|
77
84
|
method: "POST",
|
|
78
85
|
body: JSON.stringify(body),
|
|
79
86
|
signal: chatOptions?.abortSignal,
|
|
80
87
|
});
|
|
81
88
|
const data = await response.json();
|
|
89
|
+
reportThinkingPrefixDrops(data.input_transformations);
|
|
82
90
|
return extractAnthropicText(data.content).join("");
|
|
83
91
|
}
|
|
84
92
|
return { streamChat, complete };
|
|
85
93
|
}
|
|
86
94
|
export function buildAnthropicRequest(options, messages, chatOptions) {
|
|
87
|
-
const
|
|
95
|
+
const mimo = isMiMoProvider(options);
|
|
96
|
+
const modernClaude = isClaudeWithBoundThinking(chatOptions.model);
|
|
97
|
+
const { system, messages: anthropicMessages } = toAnthropicMessages(messages, modernClaude ? "all" : shouldEchoThinking(options), !modernClaude);
|
|
88
98
|
const enablePromptCache = supportsAnthropicPromptCache(options, chatOptions.model);
|
|
89
|
-
|
|
99
|
+
// MiMo only supports tool_choice=auto. Removing tools enforces "none".
|
|
100
|
+
const tools = (mimo && chatOptions.toolChoice === "none" ? undefined : chatOptions.tools)?.map((tool) => ({
|
|
90
101
|
name: tool.name,
|
|
91
102
|
description: tool.description,
|
|
92
103
|
input_schema: tool.parameters,
|
|
@@ -116,22 +127,35 @@ export function buildAnthropicRequest(options, messages, chatOptions) {
|
|
|
116
127
|
&& !messageCacheKillSwitchEnabled()) {
|
|
117
128
|
placeMessageCacheBreakpoints(anthropicMessages, ANTHROPIC_MAX_CACHE_BREAKPOINTS - staticBreakpoints);
|
|
118
129
|
}
|
|
119
|
-
const effectiveThinkingLevel = normalizeThinkingLevel(chatOptions.thinkingLevel ?? options.thinkingLevel
|
|
130
|
+
const effectiveThinkingLevel = normalizeThinkingLevel(chatOptions.thinkingLevel ?? options.thinkingLevel
|
|
131
|
+
?? (mimo ? "medium" : modernClaude || isClaude5WithOptionalThinking(chatOptions.model)
|
|
132
|
+
? getModelDefaultReasoningLevel(options.providerId || "", chatOptions.model) ?? "off" : "off"), mimo ? ["off", "medium"] : getAvailableThinkingLevels(options.providerId || "", chatOptions.model));
|
|
120
133
|
const body = {
|
|
121
134
|
model: chatOptions.model,
|
|
122
135
|
max_tokens: resolveAnthropicMaxTokens(options, chatOptions.model),
|
|
123
136
|
system: buildAnthropicSystem(system, enablePromptCache),
|
|
124
137
|
messages: anthropicMessages,
|
|
125
138
|
tools: tools && tools.length > 0 ? tools : undefined,
|
|
126
|
-
tool_choice: tools && tools.length > 0 ? { type: chatOptions.toolChoice ?? "auto" } : undefined,
|
|
139
|
+
tool_choice: tools && tools.length > 0 ? { type: mimo ? "auto" : chatOptions.toolChoice ?? "auto" } : undefined,
|
|
127
140
|
stream: chatOptions.stream || undefined,
|
|
128
141
|
};
|
|
129
142
|
if (typeof chatOptions.temperature === "number"
|
|
130
143
|
&& shouldSendTemperature(options, chatOptions.model, effectiveThinkingLevel)) {
|
|
131
144
|
body.temperature = chatOptions.temperature;
|
|
132
145
|
}
|
|
133
|
-
if (
|
|
134
|
-
|
|
146
|
+
if (mimo) {
|
|
147
|
+
// MiMo defaults to thinking on and rejects Claude's adaptive mode/effort.
|
|
148
|
+
body.thinking = { type: effectiveThinkingLevel === "off" ? "disabled" : "enabled" };
|
|
149
|
+
}
|
|
150
|
+
else if (effectiveThinkingLevel !== "off") {
|
|
151
|
+
body.thinking = modernClaude ? { type: "adaptive", display: "summarized" } : { type: "adaptive" };
|
|
152
|
+
if (modernClaude && isOfficialAnthropicBaseUrl(options.baseURL)) {
|
|
153
|
+
// Checkpoints proactively reset thinking. Runtime reminders, restored
|
|
154
|
+
// host context and tool changes can also change a prefix; let the API
|
|
155
|
+
// drop invalid blocks instead of failing the turn. Applied on every
|
|
156
|
+
// request so this behavior persists after restarts, per official guidance.
|
|
157
|
+
body.thinking.block_binding = { prefix_mismatch_behavior: "drop_block" };
|
|
158
|
+
}
|
|
135
159
|
// Apply the selected reasoning depth via output_config.effort. Without this
|
|
136
160
|
// every thinking request silently ran at Anthropic's default (high),
|
|
137
161
|
// ignoring the chosen level. effort is an official-API feature, so only
|
|
@@ -145,6 +169,10 @@ export function buildAnthropicRequest(options, messages, chatOptions) {
|
|
|
145
169
|
}
|
|
146
170
|
}
|
|
147
171
|
}
|
|
172
|
+
// Opus 5 and Sonnet 5 default to thinking on; omission cannot express Off.
|
|
173
|
+
if (!mimo && effectiveThinkingLevel === "off" && isClaude5WithOptionalThinking(chatOptions.model)) {
|
|
174
|
+
body.thinking = { type: "disabled" };
|
|
175
|
+
}
|
|
148
176
|
return body;
|
|
149
177
|
}
|
|
150
178
|
/**
|
|
@@ -237,6 +265,8 @@ function messageCacheKillSwitchEnabled() {
|
|
|
237
265
|
return raw === "0" || raw === "false";
|
|
238
266
|
}
|
|
239
267
|
export function resolveAnthropicMaxTokens(options, model) {
|
|
268
|
+
if (isMiMoProvider(options))
|
|
269
|
+
return MIMO_MAX_TOKENS;
|
|
240
270
|
// MiniMax needs a large output budget so thinking doesn't starve the answer.
|
|
241
271
|
if (isMiniMaxProvider(options)) {
|
|
242
272
|
return /m3/i.test(model) ? MINIMAX_M3_MAX_TOKENS : MINIMAX_M2_MAX_TOKENS;
|
|
@@ -244,7 +274,8 @@ export function resolveAnthropicMaxTokens(options, model) {
|
|
|
244
274
|
if (!isOfficialAnthropicBaseUrl(options.baseURL)) {
|
|
245
275
|
return DEFAULT_MAX_TOKENS;
|
|
246
276
|
}
|
|
247
|
-
if (isFableModelWith128kOutput(model) || isOpusModelWith128kOutput(model)
|
|
277
|
+
if (isFableModelWith128kOutput(model) || isOpusModelWith128kOutput(model)
|
|
278
|
+
|| isClaudeFamilyVersionAtLeast(model, "sonnet", 5, 0)) {
|
|
248
279
|
return ANTHROPIC_OPUS_LONG_OUTPUT_MAX_TOKENS;
|
|
249
280
|
}
|
|
250
281
|
if (isSonnetOrHaikuModelWith64kOutput(model)) {
|
|
@@ -269,7 +300,7 @@ export function supportsAnthropicPromptCache(options, model) {
|
|
|
269
300
|
}
|
|
270
301
|
return MINIMAX_PROMPT_CACHE_MODELS.has(model.toLowerCase());
|
|
271
302
|
}
|
|
272
|
-
export function toAnthropicMessages(messages, echoThinking = false) {
|
|
303
|
+
export function toAnthropicMessages(messages, echoThinking = false, allowUnsignedThinking = true) {
|
|
273
304
|
const system = [];
|
|
274
305
|
const out = [];
|
|
275
306
|
const thinkingReplayIndexes = getThinkingReplayIndexes(messages, echoThinking);
|
|
@@ -292,7 +323,7 @@ export function toAnthropicMessages(messages, echoThinking = false) {
|
|
|
292
323
|
continue;
|
|
293
324
|
}
|
|
294
325
|
if (message.role === "assistant") {
|
|
295
|
-
const content = buildAssistantAnthropicBlocks(message, thinkingReplayIndexes.has(index));
|
|
326
|
+
const content = buildAssistantAnthropicBlocks(message, thinkingReplayIndexes.has(index), allowUnsignedThinking);
|
|
296
327
|
if (content.length > 0) {
|
|
297
328
|
pushAnthropicMessage(out, { role: "assistant", content });
|
|
298
329
|
}
|
|
@@ -307,7 +338,7 @@ export function toAnthropicMessages(messages, echoThinking = false) {
|
|
|
307
338
|
}
|
|
308
339
|
return { system: system.join("\n\n"), messages: out };
|
|
309
340
|
}
|
|
310
|
-
function buildAssistantAnthropicBlocks(message, includeThinking) {
|
|
341
|
+
function buildAssistantAnthropicBlocks(message, includeThinking, allowUnsignedThinking) {
|
|
311
342
|
const rawBlocks = message.providerMetadata?.anthropic?.contentBlocks;
|
|
312
343
|
if (rawBlocks && rawBlocks.length > 0) {
|
|
313
344
|
const blocks = rawBlocks
|
|
@@ -319,7 +350,7 @@ function buildAssistantAnthropicBlocks(message, includeThinking) {
|
|
|
319
350
|
}
|
|
320
351
|
}
|
|
321
352
|
const content = [];
|
|
322
|
-
if (includeThinking && message.reasoning?.trim()) {
|
|
353
|
+
if (includeThinking && allowUnsignedThinking && message.reasoning?.trim()) {
|
|
323
354
|
content.push({ type: "thinking", thinking: message.reasoning });
|
|
324
355
|
}
|
|
325
356
|
if (message.content.trim()) {
|
|
@@ -339,6 +370,15 @@ function getThinkingReplayIndexes(messages, echoThinking) {
|
|
|
339
370
|
const indexes = new Set();
|
|
340
371
|
if (!echoThinking)
|
|
341
372
|
return indexes;
|
|
373
|
+
// MiMo and bound Claude thinking need intact historical blocks across user
|
|
374
|
+
// turns. Removing a middle block breaks the later Claude signature chain.
|
|
375
|
+
if (echoThinking === "all") {
|
|
376
|
+
messages.forEach((message, index) => {
|
|
377
|
+
if (message.role === "assistant")
|
|
378
|
+
indexes.add(index);
|
|
379
|
+
});
|
|
380
|
+
return indexes;
|
|
381
|
+
}
|
|
342
382
|
let lastUserIndex = -1;
|
|
343
383
|
for (let index = 0; index < messages.length; index++) {
|
|
344
384
|
if (messages[index].role === "user") {
|
|
@@ -384,6 +424,7 @@ export async function* translateAnthropicStream(events) {
|
|
|
384
424
|
for await (const event of events) {
|
|
385
425
|
const type = typeof event.type === "string" ? event.type : "";
|
|
386
426
|
if (type === "message_start") {
|
|
427
|
+
reportThinkingPrefixDrops(event.message?.input_transformations);
|
|
387
428
|
usage = mergeAnthropicUsage(usage, event.message?.usage);
|
|
388
429
|
if (usage)
|
|
389
430
|
yield { type: "usage", usage };
|
|
@@ -560,7 +601,7 @@ async function fetchAnthropicResponseWithRetry(options, request) {
|
|
|
560
601
|
try {
|
|
561
602
|
response = await providerFetch(request.url, {
|
|
562
603
|
method: request.method,
|
|
563
|
-
headers: buildAnthropicHeaders(options, request.stream),
|
|
604
|
+
headers: buildAnthropicHeaders(options, request.stream, request.model),
|
|
564
605
|
body: request.body,
|
|
565
606
|
signal: request.signal,
|
|
566
607
|
keepalive: false,
|
|
@@ -616,7 +657,7 @@ function resolveAnthropicMessagesUrl(baseURL) {
|
|
|
616
657
|
return `${normalized}/messages`;
|
|
617
658
|
return `${normalized || "https://api.anthropic.com"}/v1/messages`;
|
|
618
659
|
}
|
|
619
|
-
function buildAnthropicHeaders(options, stream) {
|
|
660
|
+
function buildAnthropicHeaders(options, stream, model) {
|
|
620
661
|
const headers = {
|
|
621
662
|
"content-type": "application/json",
|
|
622
663
|
"x-api-key": options.apiKey,
|
|
@@ -629,7 +670,25 @@ function buildAnthropicHeaders(options, stream) {
|
|
|
629
670
|
headers.accept = "text/event-stream";
|
|
630
671
|
// User-configured headers win last: anthropic-protocol coding plans gate on
|
|
631
672
|
// client identity (e.g. a specific User-Agent), configured per provider.
|
|
632
|
-
|
|
673
|
+
const merged = { ...headers, ...(options.headers ?? {}) };
|
|
674
|
+
if (isOfficialAnthropicBaseUrl(options.baseURL) && isClaudeWithBoundThinking(model)) {
|
|
675
|
+
// Merge case-insensitively without losing user-configured beta features.
|
|
676
|
+
const betaKeys = Object.keys(merged).filter(key => key.toLowerCase() === "anthropic-beta");
|
|
677
|
+
const betas = betaKeys.flatMap(key => merged[key].split(",").map(value => value.trim()).filter(Boolean));
|
|
678
|
+
for (const key of betaKeys)
|
|
679
|
+
delete merged[key];
|
|
680
|
+
merged["anthropic-beta"] = [...new Set([...betas, THINKING_BINDING_BETA])].join(",");
|
|
681
|
+
}
|
|
682
|
+
return merged;
|
|
683
|
+
}
|
|
684
|
+
function reportThinkingPrefixDrops(transformations) {
|
|
685
|
+
if (!Array.isArray(transformations))
|
|
686
|
+
return;
|
|
687
|
+
const count = transformations.filter(item => item?.type === "thinking_dropped" && item?.reason === "prefix_binding_mismatch").length;
|
|
688
|
+
if (count > 0) {
|
|
689
|
+
// Count only; raw transformations, signatures and prompt text are private.
|
|
690
|
+
console.warn(`[anthropic-thinking] API discarded ${count} thinking block(s) after a conversation prefix change.`);
|
|
691
|
+
}
|
|
633
692
|
}
|
|
634
693
|
async function readAnthropicErrorDetail(response) {
|
|
635
694
|
try {
|
|
@@ -788,18 +847,28 @@ function mergeAnthropicUsage(current, raw) {
|
|
|
788
847
|
totalTokens: promptTokens + outputTokens,
|
|
789
848
|
};
|
|
790
849
|
}
|
|
791
|
-
function shouldEchoThinking(
|
|
792
|
-
|
|
850
|
+
function shouldEchoThinking(options) {
|
|
851
|
+
if (isMiMoProvider(options))
|
|
852
|
+
return "all";
|
|
853
|
+
return options.providerId?.startsWith("minimax") ?? false;
|
|
793
854
|
}
|
|
794
855
|
function shouldSendBearerAuth(options) {
|
|
795
856
|
return !isOfficialAnthropicBaseUrl(options.baseURL) || options.providerId?.startsWith("minimax") === true;
|
|
796
857
|
}
|
|
797
858
|
function shouldSendTemperature(options, model, thinkingLevel) {
|
|
859
|
+
if (isMiMoProvider(options) && thinkingLevel !== "off")
|
|
860
|
+
return false;
|
|
798
861
|
if (!isOfficialAnthropicBaseUrl(options.baseURL))
|
|
799
862
|
return true;
|
|
800
863
|
if (thinkingLevel !== "off")
|
|
801
864
|
return false;
|
|
802
|
-
return !isOpusModelWithoutSamplingControls(model)
|
|
865
|
+
return !isOpusModelWithoutSamplingControls(model)
|
|
866
|
+
&& !isFableModelWith128kOutput(model)
|
|
867
|
+
&& !isClaudeFamilyVersionAtLeast(model, "sonnet", 5, 0);
|
|
868
|
+
}
|
|
869
|
+
function isClaude5WithOptionalThinking(model) {
|
|
870
|
+
return isClaudeFamilyVersionAtLeast(model, "sonnet", 5, 0)
|
|
871
|
+
|| (isClaudeFamilyVersionAtLeast(model, "opus", 5, 0) && !isClaudeWithBoundThinking(model));
|
|
803
872
|
}
|
|
804
873
|
function isOpusModelWith128kOutput(model) {
|
|
805
874
|
return isClaudeFamilyVersionAtLeast(model, "opus", 4, 6);
|
|
@@ -828,7 +897,7 @@ function isClaudeFamilyVersionAtLeast(model, family, minMajor, minMinor) {
|
|
|
828
897
|
if (major < minMajor)
|
|
829
898
|
return false;
|
|
830
899
|
if (!minorSegment || minorSegment.length > 2)
|
|
831
|
-
return
|
|
900
|
+
return minMinor === 0;
|
|
832
901
|
const minor = Number(minorSegment);
|
|
833
902
|
return Number.isFinite(minor) && minor >= minMinor;
|
|
834
903
|
}
|
|
@@ -858,3 +927,14 @@ function isMiniMaxProvider(options) {
|
|
|
858
927
|
return (options.providerId || "").toLowerCase().startsWith("minimax")
|
|
859
928
|
|| options.baseURL.toLowerCase().includes("minimaxi");
|
|
860
929
|
}
|
|
930
|
+
function isMiMoProvider(options) {
|
|
931
|
+
if (options.providerId === "mimo-token-plan")
|
|
932
|
+
return true;
|
|
933
|
+
try {
|
|
934
|
+
return ["api.xiaomimimo.com", "token-plan-cn.xiaomimimo.com", "token-plan-sgp.xiaomimimo.com", "token-plan-ams.xiaomimimo.com"]
|
|
935
|
+
.includes(new URL(options.baseURL).hostname);
|
|
936
|
+
}
|
|
937
|
+
catch {
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
@@ -14,6 +14,11 @@ export interface SanitizedProviderError {
|
|
|
14
14
|
bubbleVersion: string;
|
|
15
15
|
pid: number;
|
|
16
16
|
runtimeStartedAt: number;
|
|
17
|
+
retry?: {
|
|
18
|
+
attempt: number;
|
|
19
|
+
maxAttempts: number;
|
|
20
|
+
};
|
|
21
|
+
requestShape?: Record<string, number>;
|
|
17
22
|
}
|
|
18
23
|
export interface ProviderErrorContext {
|
|
19
24
|
providerId: string;
|
|
@@ -22,6 +27,10 @@ export interface ProviderErrorContext {
|
|
|
22
27
|
thinkingLevel: ThinkingLevel;
|
|
23
28
|
messageCount: number;
|
|
24
29
|
toolCount: number;
|
|
30
|
+
retry?: {
|
|
31
|
+
attempt: number;
|
|
32
|
+
maxAttempts: number;
|
|
33
|
+
};
|
|
25
34
|
}
|
|
26
35
|
/**
|
|
27
36
|
* Build the allowlisted diagnostic record written to a session log.
|