mirror of
https://github.com/cline/cline.git
synced 2026-09-08 22:13:11 +08:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e5b8be28c | ||
|
|
764e901693 | ||
|
|
2cabb2ddf6 | ||
|
|
c32789f697 | ||
|
|
349a8da750 | ||
|
|
f09dab7a0b | ||
|
|
3a3ea6ee96 | ||
|
|
1c1ea0bd53 | ||
|
|
70303d8541 | ||
|
|
8ba15dfca6 | ||
|
|
6ab6a1eabc | ||
|
|
2ad4146de1 | ||
|
|
cfc2250717 | ||
|
|
730bac7f59 | ||
|
|
797ea1f607 | ||
|
|
9d59de4a4c | ||
|
|
ae67ca7a13 | ||
|
|
7e2583f40c | ||
|
|
ecca88bb98 | ||
|
|
4bb93ee5b9 | ||
|
|
4f2d7398ed | ||
|
|
bc184f346d | ||
|
|
96aea0d34b |
@@ -7,10 +7,10 @@ body:
|
||||
value: |
|
||||
**Important:** All bug reports must be reproducible using Claude Sonnet 4.5. Cline uses complex prompts so less capable models may not work as expected.
|
||||
- type: dropdown
|
||||
id: plugin-type
|
||||
id: cline-surface
|
||||
attributes:
|
||||
label: Plugin Type
|
||||
description: Which plugin are you reporting a bug for?
|
||||
label: Cline Surface
|
||||
description: Which Cline surface are you reporting a bug for?
|
||||
options:
|
||||
- VSCode Extension
|
||||
- JetBrains Plugin
|
||||
@@ -59,6 +59,18 @@ body:
|
||||
placeholder: 'e.g., cline:anthropic/claude-sonnet-4.5, gemini:gemini-2.5-pro-exp-03-25'
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: ide-diagnostics
|
||||
attributes:
|
||||
label: IDE / CLI Diagnostics
|
||||
description: |
|
||||
Paste the "About" diagnostics for your Cline surface. This captures the IDE build, runtime, and host details we need.
|
||||
- VSCode Extension: open `Help → About` (Windows/Linux) or `Code → About Visual Studio Code` (macOS), then copy the info.
|
||||
- JetBrains Plugin: open `Help → About` (Windows/Linux) or `<IDE name> → About` (macOS), then click `Copy` to grab build, runtime, OS, memory, and cores.
|
||||
- CLI: there is no About dialog. Run `cline --version` and paste the output.
|
||||
placeholder: Paste the copied About info or `cline --version` output here.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
const labels = context.payload.issue.labels.map(l => l.name);
|
||||
|
||||
// Check if JetBrains Plugin is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
|
||||
if (body.match(/###\s*Cline Surface\s*\n+JetBrains Plugin/i)) {
|
||||
if (!labels.includes('JetBrains')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
}
|
||||
|
||||
// Check if VSCode Extension is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+VSCode Extension/i)) {
|
||||
if (body.match(/###\s*Cline Surface\s*\n+VSCode Extension/i)) {
|
||||
if (!labels.includes('VS Code')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
}
|
||||
|
||||
// Check if CLI is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+CLI/i)) {
|
||||
if (body.match(/###\s*Cline Surface\s*\n+CLI/i)) {
|
||||
if (!labels.includes('CLI')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## [3.89.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add Claude Fable 5 model support.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix MiniMax M3 thinking controls across gateways.
|
||||
|
||||
### Changed
|
||||
|
||||
- Clean up the Codex model list.
|
||||
|
||||
## [3.88.1]
|
||||
|
||||
### Added
|
||||
|
||||
- Add a debug section in settings for Cline testers.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Include the walkthrough markdown files in the VS Code extension package so the first-run walkthrough steps load correctly.
|
||||
|
||||
## [3.88.0]
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Cline CLI Changelog
|
||||
|
||||
## 3.0.22
|
||||
|
||||
- Added support for the Claude Fable 5 model
|
||||
- Fixed MiniMax M3 thinking controls so they route correctly across gateways
|
||||
|
||||
## 3.0.21
|
||||
|
||||
- Added a global auto-update setting that controls automatic updates on CLI startup
|
||||
- Added a Cline credits refill link
|
||||
- Fixed scrolling for inline ask-question responses
|
||||
- Fixed connector thread session routing and stale hub session handling
|
||||
- Added support for Vertex AI Application Default Credentials (ADC) with tool use
|
||||
- Fixed empty message content replay for Bedrock
|
||||
- Cleaned up the OpenAI Codex model list
|
||||
|
||||
## 3.0.20
|
||||
|
||||
- Installed plugin wrappers are now named from their source (npm package name, git repo, remote filename, official slug, or local directory) instead of an opaque hash, making installed plugins easier to identify.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/cli",
|
||||
"displayName": "cline",
|
||||
"version": "3.0.20",
|
||||
"version": "3.0.22",
|
||||
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
autoUpdateOnStartup,
|
||||
checkForUpdates,
|
||||
getInstallationInfo,
|
||||
PackageManager,
|
||||
withMinimumReleaseAgeBypass,
|
||||
@@ -10,6 +12,9 @@ import {
|
||||
|
||||
const originalArgv = [...process.argv];
|
||||
const originalWrapperPath = process.env.CLINE_WRAPPER_PATH;
|
||||
const originalGlobalSettingsPath = process.env.CLINE_GLOBAL_SETTINGS_PATH;
|
||||
const originalIsDev = process.env.IS_DEV;
|
||||
const originalNoAutoUpdate = process.env.CLINE_NO_AUTO_UPDATE;
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
function createFile(path: string): string {
|
||||
@@ -32,6 +37,22 @@ describe("getInstallationInfo", () => {
|
||||
} else {
|
||||
process.env.CLINE_WRAPPER_PATH = originalWrapperPath;
|
||||
}
|
||||
if (originalGlobalSettingsPath === undefined) {
|
||||
delete process.env.CLINE_GLOBAL_SETTINGS_PATH;
|
||||
} else {
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = originalGlobalSettingsPath;
|
||||
}
|
||||
if (originalIsDev === undefined) {
|
||||
delete process.env.IS_DEV;
|
||||
} else {
|
||||
process.env.IS_DEV = originalIsDev;
|
||||
}
|
||||
if (originalNoAutoUpdate === undefined) {
|
||||
delete process.env.CLINE_NO_AUTO_UPDATE;
|
||||
} else {
|
||||
process.env.CLINE_NO_AUTO_UPDATE = originalNoAutoUpdate;
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -72,6 +93,66 @@ describe("getInstallationInfo", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("auto update settings", () => {
|
||||
afterEach(() => {
|
||||
process.argv = [...originalArgv];
|
||||
if (originalWrapperPath === undefined) {
|
||||
delete process.env.CLINE_WRAPPER_PATH;
|
||||
} else {
|
||||
process.env.CLINE_WRAPPER_PATH = originalWrapperPath;
|
||||
}
|
||||
if (originalGlobalSettingsPath === undefined) {
|
||||
delete process.env.CLINE_GLOBAL_SETTINGS_PATH;
|
||||
} else {
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = originalGlobalSettingsPath;
|
||||
}
|
||||
if (originalIsDev === undefined) {
|
||||
delete process.env.IS_DEV;
|
||||
} else {
|
||||
process.env.IS_DEV = originalIsDev;
|
||||
}
|
||||
if (originalNoAutoUpdate === undefined) {
|
||||
delete process.env.CLINE_NO_AUTO_UPDATE;
|
||||
} else {
|
||||
process.env.CLINE_NO_AUTO_UPDATE = originalNoAutoUpdate;
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("skips startup auto update when disabled globally", () => {
|
||||
const settingsPath = createTempFile("data/global-settings.json");
|
||||
writeFileSync(settingsPath, JSON.stringify({ autoUpdateEnabled: false }));
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
|
||||
delete process.env.IS_DEV;
|
||||
delete process.env.CLINE_NO_AUTO_UPDATE;
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockRejectedValue(new Error("should not fetch"));
|
||||
|
||||
autoUpdateOnStartup();
|
||||
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("still lets manual update checks run when startup auto update is disabled", async () => {
|
||||
const settingsPath = createTempFile("data/global-settings.json");
|
||||
writeFileSync(settingsPath, JSON.stringify({ autoUpdateEnabled: false }));
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
|
||||
delete process.env.CLINE_NO_AUTO_UPDATE;
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ version: "0.0.0" }),
|
||||
} as Response);
|
||||
|
||||
await checkForUpdates({ includeKanban: false });
|
||||
|
||||
expect(fetchSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("withMinimumReleaseAgeBypass", () => {
|
||||
it("adds the package-manager-specific cooldown bypass", () => {
|
||||
expect(
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type ChildProcess, spawn } from "node:child_process";
|
||||
import { realpathSync } from "node:fs";
|
||||
import {
|
||||
clearHubDiscovery,
|
||||
isAutoUpdateEnabledGlobally,
|
||||
probeHubServer,
|
||||
readHubDiscovery,
|
||||
resolveSharedHubOwnerContext,
|
||||
@@ -340,6 +341,7 @@ async function restartHubServerIfRunning(): Promise<void> {
|
||||
export function autoUpdateOnStartup(): void {
|
||||
if (process.env.IS_DEV === "true") return;
|
||||
if (process.env.CLINE_NO_AUTO_UPDATE === "1") return;
|
||||
if (!isAutoUpdateEnabledGlobally()) return;
|
||||
|
||||
const { packageName, packageManager, updateCommand } =
|
||||
getInstallationInfo(version);
|
||||
|
||||
@@ -228,7 +228,7 @@ describe("discordConnector", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("switches Discord thread state to the incoming participant without reusing the previous participant session", async () => {
|
||||
it("updates Discord participant metadata without changing the thread session", async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), "discord-participants-"));
|
||||
const bindingsPath = join(dir, "threads.json");
|
||||
const thread = createThread({
|
||||
@@ -278,11 +278,13 @@ describe("discordConnector", () => {
|
||||
errorLabel: "Discord",
|
||||
});
|
||||
|
||||
const bob =
|
||||
readBindings<TestDiscordState>(bindingsPath)["discord:user:bob"];
|
||||
expect(bob?.state?.participantKey).toBe("discord:user:bob");
|
||||
expect(bob?.state?.participantLabel).toBe("Bob");
|
||||
expect(bob?.state?.sessionId).toBeUndefined();
|
||||
const binding =
|
||||
readBindings<TestDiscordState>(bindingsPath)[
|
||||
"discord:guild:channel:thread"
|
||||
];
|
||||
expect(binding?.state?.participantKey).toBe("discord:user:bob");
|
||||
expect(binding?.state?.participantLabel).toBe("Bob");
|
||||
expect(binding?.state?.sessionId).toBe("session-alice");
|
||||
expect(
|
||||
readBindings<TestDiscordState>(bindingsPath)["discord:user:alice"]?.state
|
||||
?.sessionId,
|
||||
|
||||
@@ -50,10 +50,9 @@ import {
|
||||
type ConnectorMuteTarget,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
mergeThreadState,
|
||||
persistMergedThreadState,
|
||||
readBindings,
|
||||
} from "../thread-bindings";
|
||||
@@ -564,45 +563,17 @@ async function postDiscordResolvedText(input: {
|
||||
});
|
||||
}
|
||||
|
||||
function resolveParticipantState(input: {
|
||||
bindingsPath: string;
|
||||
baseStartRequest: ChatStartSessionRequest;
|
||||
function resolveCurrentStateWithParticipant(input: {
|
||||
currentState: DiscordThreadState;
|
||||
participant: DiscordParticipant;
|
||||
}): DiscordThreadState {
|
||||
const existing = findBindingForParticipantKey(
|
||||
readBindings<DiscordThreadState>(input.bindingsPath),
|
||||
input.participant.key,
|
||||
)?.binding.state;
|
||||
return {
|
||||
...mergeThreadState<DiscordThreadState>(
|
||||
undefined,
|
||||
existing,
|
||||
input.baseStartRequest,
|
||||
),
|
||||
...input.currentState,
|
||||
participantKey: input.participant.key,
|
||||
participantLabel: input.participant.label,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveCurrentStateWithParticipant(input: {
|
||||
currentState: DiscordThreadState;
|
||||
bindingsPath: string;
|
||||
baseStartRequest: ChatStartSessionRequest;
|
||||
participant: DiscordParticipant;
|
||||
}): DiscordThreadState {
|
||||
if (input.currentState.participantKey === input.participant.key) {
|
||||
return {
|
||||
...input.currentState,
|
||||
participantLabel: input.participant.label,
|
||||
};
|
||||
}
|
||||
return resolveParticipantState({
|
||||
bindingsPath: input.bindingsPath,
|
||||
baseStartRequest: input.baseStartRequest,
|
||||
participant: input.participant,
|
||||
});
|
||||
}
|
||||
|
||||
async function persistDiscordThreadContext(input: {
|
||||
thread: Thread<DiscordThreadState>;
|
||||
bindingsPath: string;
|
||||
@@ -624,8 +595,6 @@ async function persistDiscordThreadContext(input: {
|
||||
);
|
||||
const nextState = resolveCurrentStateWithParticipant({
|
||||
currentState,
|
||||
bindingsPath: input.bindingsPath,
|
||||
baseStartRequest: input.baseStartRequest,
|
||||
participant,
|
||||
});
|
||||
if (
|
||||
@@ -669,20 +638,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<DiscordThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
if (!binding?.serializedThread) {
|
||||
return;
|
||||
@@ -1133,9 +1102,7 @@ class DiscordConnector extends ConnectorBase<
|
||||
isSubscribedThreadMessage?: boolean;
|
||||
},
|
||||
) => {
|
||||
const queueKey =
|
||||
(await loadThreadState(thread, bindingsPath, startRequest))
|
||||
.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await handleConnectorUserTurn({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { __test__ } from "./gchat";
|
||||
|
||||
describe("gchat binding lookup", () => {
|
||||
it("falls back to channel identity when a restarted connector gets a new thread id", () => {
|
||||
it("does not fall back to channel identity for a different space thread id", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
legacy_thread_id: {
|
||||
@@ -21,17 +21,7 @@ describe("gchat binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
key: "legacy_thread_id",
|
||||
binding: {
|
||||
channelId: "space-123",
|
||||
isDM: false,
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-1",
|
||||
state: { sessionId: "sess-1", cwd: "/tmp/work" },
|
||||
updatedAt: "2026-03-17T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("prefers an exact thread id match over a channel fallback", () => {
|
||||
@@ -65,7 +55,7 @@ describe("gchat binding lookup", () => {
|
||||
expect(result?.binding.sessionId).toBe("sess-2");
|
||||
});
|
||||
|
||||
it("reuses a binding by participant key across different spaces", () => {
|
||||
it("does not reuse a binding by participant key across different spaces", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
"gchat:email:alice@example.com": {
|
||||
@@ -91,7 +81,6 @@ describe("gchat binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.key).toBe("gchat:email:alice@example.com");
|
||||
expect(result?.binding.sessionId).toBe("sess-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
type ConnectorBindingStore,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
persistMergedThreadState,
|
||||
@@ -191,20 +191,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<GoogleChatThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
if (!binding?.serializedThread) {
|
||||
return;
|
||||
@@ -590,9 +590,7 @@ class GoogleChatConnector extends ConnectorBase<
|
||||
thread: Thread<GoogleChatThreadState>,
|
||||
text: string,
|
||||
) => {
|
||||
const queueKey =
|
||||
(await loadThreadState(thread, bindingsPath, startRequest))
|
||||
.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await handleConnectorUserTurn({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { __test__ } from "./linear";
|
||||
|
||||
describe("linear binding lookup", () => {
|
||||
it("falls back to channel identity when a restarted connector gets a new thread id", () => {
|
||||
it("does not fall back to channel identity for a different issue thread id", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
legacy_thread_id: {
|
||||
@@ -21,17 +21,7 @@ describe("linear binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
key: "legacy_thread_id",
|
||||
binding: {
|
||||
channelId: "linear:issue:ISS-123",
|
||||
isDM: false,
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-1",
|
||||
state: { sessionId: "sess-1", cwd: "/tmp/work" },
|
||||
updatedAt: "2026-03-17T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("prefers an exact thread id match over a channel fallback", () => {
|
||||
@@ -65,7 +55,7 @@ describe("linear binding lookup", () => {
|
||||
expect(result?.binding.sessionId).toBe("sess-2");
|
||||
});
|
||||
|
||||
it("reuses a binding by participant key across different issue threads", () => {
|
||||
it("does not reuse a binding by participant key across different issue threads", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
"linear:user:user_123": {
|
||||
@@ -91,7 +81,6 @@ describe("linear binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.key).toBe("linear:user:user_123");
|
||||
expect(result?.binding.sessionId).toBe("sess-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
type ConnectorBindingStore,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
persistMergedThreadState,
|
||||
@@ -229,20 +229,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<LinearThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
if (!binding?.serializedThread) {
|
||||
return;
|
||||
@@ -625,9 +625,7 @@ class LinearConnector extends ConnectorBase<
|
||||
thread: Thread<LinearThreadState>,
|
||||
text: string,
|
||||
) => {
|
||||
const queueKey =
|
||||
(await loadThreadState(thread, bindingsPath, startRequest))
|
||||
.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await handleConnectorUserTurn({
|
||||
|
||||
@@ -29,7 +29,7 @@ export function getConnectorSystemRules(
|
||||
}
|
||||
|
||||
const CONNECTOR_FIRST_CONTACT_MESSAGE = [
|
||||
"Connected.",
|
||||
"Connected to Cline.",
|
||||
"Your chat history is kept separately for your account.",
|
||||
"Send /new to start a fresh session or /whereami for thread details.",
|
||||
].join("\n");
|
||||
|
||||
@@ -63,12 +63,12 @@ describe("slack binding lookup", () => {
|
||||
expect(options.appToken).toBe("xapp-token");
|
||||
});
|
||||
|
||||
it("falls back to channel identity when a restarted connector gets a new thread id", () => {
|
||||
it("falls back to DM channel identity when a restarted connector gets a new thread id", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
legacy_thread_id: {
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-1",
|
||||
state: { sessionId: "sess-1", cwd: "/tmp/work", teamId: "T123" },
|
||||
@@ -78,7 +78,7 @@ describe("slack binding lookup", () => {
|
||||
{
|
||||
id: "new_thread_id",
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ describe("slack binding lookup", () => {
|
||||
key: "legacy_thread_id",
|
||||
binding: {
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-1",
|
||||
state: { sessionId: "sess-1", cwd: "/tmp/work", teamId: "T123" },
|
||||
@@ -126,7 +126,7 @@ describe("slack binding lookup", () => {
|
||||
expect(result?.binding.sessionId).toBe("sess-2");
|
||||
});
|
||||
|
||||
it("reuses a binding by participant key across different threads", () => {
|
||||
it("does not reuse a binding by participant key across different threads", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
[participantKey]: {
|
||||
@@ -153,8 +153,7 @@ describe("slack binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.key).toBe(participantKey);
|
||||
expect(result?.binding.sessionId).toBe("sess-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("builds Slack participant keys with a team scope", () => {
|
||||
|
||||
@@ -51,7 +51,7 @@ import {
|
||||
type ConnectorThreadBinding,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
persistMergedThreadState,
|
||||
@@ -376,20 +376,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<SlackThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
const deliveryThreadId = match?.key || threadId || bindingKey;
|
||||
if (!binding?.serializedThread) {
|
||||
@@ -826,7 +826,7 @@ class SlackConnector extends ConnectorBase<
|
||||
bindingsPath,
|
||||
startRequest,
|
||||
);
|
||||
const queueKey = currentState.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await withSlackTeamBotToken({
|
||||
|
||||
@@ -363,7 +363,7 @@ describe("telegram binding lookup", () => {
|
||||
expect(result?.binding.sessionId).toBe("sess-2");
|
||||
});
|
||||
|
||||
it("reuses a binding by participant key across different chats", () => {
|
||||
it("does not reuse a binding by participant key across different chats", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
"telegram:user:alice": {
|
||||
@@ -389,7 +389,6 @@ describe("telegram binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.key).toBe("telegram:user:alice");
|
||||
expect(result?.binding.sessionId).toBe("sess-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
type ConnectorBindingStore,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
persistMergedThreadState,
|
||||
@@ -293,20 +293,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<TelegramThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
const deliveryThreadId = match?.key || threadId;
|
||||
if (!binding?.serializedThread) {
|
||||
@@ -788,9 +788,7 @@ class TelegramConnector extends ConnectorBase<
|
||||
thread: Thread<TelegramThreadState>,
|
||||
text: string,
|
||||
) => {
|
||||
const queueKey =
|
||||
(await loadThreadState(thread, bindingsPath, startRequest))
|
||||
.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await handleConnectorUserTurn({
|
||||
|
||||
@@ -65,7 +65,7 @@ describe("whatsapp binding lookup", () => {
|
||||
expect(result?.binding.sessionId).toBe("sess-2");
|
||||
});
|
||||
|
||||
it("reuses a binding by participant key across different threads", () => {
|
||||
it("does not reuse a binding by participant key across different threads", () => {
|
||||
const result = __test__.findBindingForThread(
|
||||
{
|
||||
"whatsapp:user:15551234567": {
|
||||
@@ -91,7 +91,6 @@ describe("whatsapp binding lookup", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.key).toBe("whatsapp:user:15551234567");
|
||||
expect(result?.binding.sessionId).toBe("sess-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
type ConnectorBindingStore,
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForParticipantKey,
|
||||
findBindingForDeliveryTarget,
|
||||
findBindingForThread,
|
||||
loadThreadState,
|
||||
persistMergedThreadState,
|
||||
@@ -226,20 +226,20 @@ async function deliverScheduledResult(input: {
|
||||
const threadId =
|
||||
typeof delivery.threadId === "string" ? delivery.threadId.trim() : "";
|
||||
const bindingKey =
|
||||
typeof delivery.bindingKey === "string"
|
||||
? delivery.bindingKey.trim()
|
||||
: typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey) {
|
||||
typeof delivery.bindingKey === "string" ? delivery.bindingKey.trim() : "";
|
||||
const participantKey =
|
||||
typeof delivery.participantKey === "string"
|
||||
? delivery.participantKey.trim()
|
||||
: "";
|
||||
if (!threadId && !bindingKey && !participantKey) {
|
||||
return;
|
||||
}
|
||||
const bindings = readBindings<WhatsAppThreadState>(input.bindingsPath);
|
||||
const match = bindingKey
|
||||
? findBindingForParticipantKey(bindings, bindingKey)
|
||||
: threadId
|
||||
? { key: threadId, binding: bindings[threadId] }
|
||||
: undefined;
|
||||
const match = findBindingForDeliveryTarget(bindings, {
|
||||
bindingKey,
|
||||
threadId,
|
||||
participantKey,
|
||||
});
|
||||
const binding = match?.binding;
|
||||
if (!binding?.serializedThread) {
|
||||
return;
|
||||
@@ -597,9 +597,7 @@ class WhatsAppConnector extends ConnectorBase<
|
||||
thread: Thread<WhatsAppThreadState>,
|
||||
text: string,
|
||||
) => {
|
||||
const queueKey =
|
||||
(await loadThreadState(thread, bindingsPath, startRequest))
|
||||
.participantKey || thread.id;
|
||||
const queueKey = thread.id;
|
||||
const runTurn = async () => {
|
||||
try {
|
||||
await handleConnectorUserTurn({
|
||||
|
||||
@@ -92,6 +92,11 @@ function createRuntimeClient(
|
||||
) {
|
||||
const startRuntimeSession = vi.fn(async () => ({ sessionId: "session-1" }));
|
||||
const updateSession = vi.fn(async () => undefined);
|
||||
const getSession = vi.fn(
|
||||
async (sessionId: string): Promise<{ sessionId: string } | undefined> => ({
|
||||
sessionId,
|
||||
}),
|
||||
);
|
||||
const abortRuntimeSession = vi.fn(async () => undefined);
|
||||
const deleteSession = vi.fn(async () => undefined);
|
||||
const sendRuntimeSession = vi.fn(async () => ({
|
||||
@@ -106,6 +111,7 @@ function createRuntimeClient(
|
||||
client: {
|
||||
startRuntimeSession,
|
||||
updateSession,
|
||||
getSession,
|
||||
abortRuntimeSession,
|
||||
stopRuntimeSession: abortRuntimeSession,
|
||||
deleteSession,
|
||||
@@ -115,6 +121,7 @@ function createRuntimeClient(
|
||||
},
|
||||
startRuntimeSession,
|
||||
updateSession,
|
||||
getSession,
|
||||
sendRuntimeSession,
|
||||
readMessages,
|
||||
};
|
||||
@@ -593,7 +600,8 @@ describe("handleConnectorUserTurn", () => {
|
||||
metadata: expect.objectContaining({
|
||||
delivery: expect.objectContaining({
|
||||
adapter: "telegram",
|
||||
bindingKey: "telegram:user:alice",
|
||||
bindingKey: "thread-1",
|
||||
participantKey: "telegram:user:alice",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
@@ -627,7 +635,8 @@ describe("handleConnectorUserTurn", () => {
|
||||
metadata: {
|
||||
delivery: {
|
||||
adapter: "telegram",
|
||||
bindingKey: "telegram:user:alice",
|
||||
bindingKey: "thread-1",
|
||||
participantKey: "telegram:user:alice",
|
||||
threadId: "thread-1",
|
||||
},
|
||||
},
|
||||
@@ -640,7 +649,8 @@ describe("handleConnectorUserTurn", () => {
|
||||
metadata: {
|
||||
delivery: {
|
||||
adapter: "telegram",
|
||||
bindingKey: "telegram:user:bob",
|
||||
bindingKey: "thread-2",
|
||||
participantKey: "telegram:user:bob",
|
||||
threadId: "thread-2",
|
||||
},
|
||||
},
|
||||
@@ -699,7 +709,8 @@ describe("handleConnectorUserTurn", () => {
|
||||
delivery: expect.objectContaining({
|
||||
adapter: "telegram",
|
||||
threadId: "thread-1",
|
||||
bindingKey: "telegram:user:alice",
|
||||
bindingKey: "thread-1",
|
||||
participantKey: "telegram:user:alice",
|
||||
userName: "ClineAdapterBot",
|
||||
}),
|
||||
}),
|
||||
@@ -1442,7 +1453,7 @@ describe("handleConnectorUserTurn", () => {
|
||||
});
|
||||
const runtime = createRuntimeClient("unused");
|
||||
const activeTurns = new Map([
|
||||
["other-turn-key", { sessionId: "session-1" }],
|
||||
["other-turn-key", { sessionId: "session-1", threadId: "thread-1" }],
|
||||
]);
|
||||
|
||||
await handleConnectorUserTurn({
|
||||
@@ -1478,4 +1489,105 @@ describe("handleConnectorUserTurn", () => {
|
||||
);
|
||||
expect(posts.at(-1)).toEqual({ raw: "Steering current task." });
|
||||
});
|
||||
|
||||
it("starts a normal turn when the active session is in a different thread", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "connector-host-test-"));
|
||||
tempDirs.push(dir);
|
||||
const bindingsPath = join(dir, "threads.json");
|
||||
const { thread, posts } = createThread({
|
||||
enableTools: true,
|
||||
autoApproveTools: true,
|
||||
cwd: "/tmp/work",
|
||||
workspaceRoot: "/tmp/work",
|
||||
welcomeSentAt: new Date().toISOString(),
|
||||
});
|
||||
const runtime = createRuntimeClient("normal reply");
|
||||
const activeTurns = new Map([
|
||||
["other-thread", { sessionId: "session-1", threadId: "other-thread" }],
|
||||
]);
|
||||
|
||||
await handleConnectorUserTurn({
|
||||
thread: thread as never,
|
||||
text: "start work in this thread",
|
||||
client: runtime.client as never,
|
||||
pendingApprovals: new Map(),
|
||||
baseStartRequest: baseStartRequest() as never,
|
||||
explicitSystemPrompt: undefined,
|
||||
clientId: "client-1",
|
||||
logger: {
|
||||
core: { debug: vi.fn(), log: vi.fn(), error: vi.fn() },
|
||||
} as never,
|
||||
transport: "telegram",
|
||||
botUserName: "ClineAdapterBot",
|
||||
requestStop: vi.fn(),
|
||||
bindingsPath,
|
||||
systemRules: "rules",
|
||||
errorLabel: "Telegram",
|
||||
getSessionMetadata: () => ({}),
|
||||
reusedLogMessage: "reused",
|
||||
activeTurns,
|
||||
turnKey: "thread-1",
|
||||
});
|
||||
|
||||
expect(runtime.startRuntimeSession).toHaveBeenCalled();
|
||||
expect(runtime.sendRuntimeSession).toHaveBeenCalledWith(
|
||||
"session-1",
|
||||
expect.not.objectContaining({
|
||||
delivery: "steer",
|
||||
}),
|
||||
{ timeoutMs: null },
|
||||
);
|
||||
expect(posts.at(-1)).toEqual({ raw: "normal reply" });
|
||||
});
|
||||
|
||||
it("starts a fresh session when persisted thread session is missing from the hub", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "connector-host-test-"));
|
||||
tempDirs.push(dir);
|
||||
const bindingsPath = join(dir, "threads.json");
|
||||
const { thread, posts, getState } = createThread({
|
||||
sessionId: "stale-session",
|
||||
enableTools: true,
|
||||
autoApproveTools: true,
|
||||
cwd: "/tmp/work",
|
||||
workspaceRoot: "/tmp/work",
|
||||
welcomeSentAt: new Date().toISOString(),
|
||||
});
|
||||
const runtime = createRuntimeClient("fresh reply");
|
||||
runtime.getSession.mockResolvedValueOnce(undefined);
|
||||
|
||||
await handleConnectorUserTurn({
|
||||
thread: thread as never,
|
||||
text: "continue after hub restart",
|
||||
client: runtime.client as never,
|
||||
pendingApprovals: new Map(),
|
||||
baseStartRequest: baseStartRequest() as never,
|
||||
explicitSystemPrompt: undefined,
|
||||
clientId: "client-1",
|
||||
logger: {
|
||||
core: { debug: vi.fn(), log: vi.fn(), error: vi.fn() },
|
||||
} as never,
|
||||
transport: "telegram",
|
||||
botUserName: "ClineAdapterBot",
|
||||
requestStop: vi.fn(),
|
||||
bindingsPath,
|
||||
systemRules: "rules",
|
||||
errorLabel: "Telegram",
|
||||
getSessionMetadata: () => ({}),
|
||||
reusedLogMessage: "reused",
|
||||
startedLogMessage: "started",
|
||||
turnKey: "thread-1",
|
||||
});
|
||||
|
||||
expect(runtime.getSession).toHaveBeenCalledWith("stale-session");
|
||||
expect(runtime.startRuntimeSession).toHaveBeenCalled();
|
||||
expect(runtime.sendRuntimeSession).toHaveBeenCalledWith(
|
||||
"session-1",
|
||||
expect.not.objectContaining({
|
||||
delivery: "steer",
|
||||
}),
|
||||
{ timeoutMs: null },
|
||||
);
|
||||
expect(getState().sessionId).toBe("session-1");
|
||||
expect(posts.at(-1)).toEqual({ raw: "fresh reply" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -749,9 +749,7 @@ export async function handleConnectorUserTurn<
|
||||
`channelId=${input.thread.channelId}`,
|
||||
`deliveryAdapter=${input.transport}`,
|
||||
`deliveryThread=${input.thread.id}`,
|
||||
...(effectiveCurrent.participantKey
|
||||
? [`deliveryBindingKey=${effectiveCurrent.participantKey}`]
|
||||
: []),
|
||||
`deliveryBindingKey=${input.thread.id}`,
|
||||
`deliveryChannel=${input.thread.channelId}`,
|
||||
...(input.botUserName
|
||||
? [`deliveryUserName=${input.botUserName}`]
|
||||
@@ -789,11 +787,9 @@ export async function handleConnectorUserTurn<
|
||||
delivery: {
|
||||
adapter: input.transport,
|
||||
threadId: input.thread.id,
|
||||
bindingKey: input.thread.id,
|
||||
...(current.participantKey
|
||||
? {
|
||||
bindingKey: current.participantKey,
|
||||
participantKey: current.participantKey,
|
||||
}
|
||||
? { participantKey: current.participantKey }
|
||||
: {}),
|
||||
...(current.participantLabel
|
||||
? { participantLabel: current.participantLabel }
|
||||
@@ -832,11 +828,6 @@ export async function handleConnectorUserTurn<
|
||||
].join("\n");
|
||||
},
|
||||
list: async () => {
|
||||
const current = await loadThreadState(
|
||||
input.thread,
|
||||
input.bindingsPath,
|
||||
input.baseStartRequest,
|
||||
);
|
||||
const schedules = await input.client.listSchedules({ limit: 200 });
|
||||
const matching = schedules.filter((schedule) => {
|
||||
const delivery = schedule.metadata?.delivery;
|
||||
@@ -846,17 +837,9 @@ export async function handleConnectorUserTurn<
|
||||
!Array.isArray(delivery)
|
||||
? (delivery as Record<string, unknown>)
|
||||
: undefined;
|
||||
const deliveryBindingKey =
|
||||
typeof deliveryRecord?.bindingKey === "string"
|
||||
? deliveryRecord.bindingKey
|
||||
: typeof deliveryRecord?.participantKey === "string"
|
||||
? deliveryRecord.participantKey
|
||||
: undefined;
|
||||
return (
|
||||
deliveryRecord?.adapter === input.transport &&
|
||||
(current.participantKey
|
||||
? deliveryBindingKey === current.participantKey
|
||||
: deliveryRecord.threadId === input.thread.id)
|
||||
deliveryRecord.threadId === input.thread.id
|
||||
);
|
||||
});
|
||||
if (matching.length === 0) {
|
||||
@@ -913,7 +896,9 @@ export async function handleConnectorUserTurn<
|
||||
input.activeTurns?.get(turnKey) ??
|
||||
(input.activeTurns && currentState.sessionId?.trim()
|
||||
? Array.from(input.activeTurns.values()).find(
|
||||
(turn) => turn.sessionId === currentState.sessionId?.trim(),
|
||||
(turn) =>
|
||||
turn.sessionId === currentState.sessionId?.trim() &&
|
||||
turn.threadId === input.thread.id,
|
||||
)
|
||||
: undefined);
|
||||
if (activeTurn?.sessionId?.trim()) {
|
||||
|
||||
@@ -159,36 +159,57 @@ export async function getOrCreateSessionId<
|
||||
);
|
||||
const existing = threadState.sessionId?.trim();
|
||||
if (existing) {
|
||||
const existingSession = await input.client.getSession(existing);
|
||||
if (existingSession) {
|
||||
await persistMergedThreadState(
|
||||
input.thread,
|
||||
input.bindingsPath,
|
||||
{
|
||||
...threadState,
|
||||
sessionId: existing,
|
||||
},
|
||||
input.errorLabel,
|
||||
);
|
||||
input.logger.core.log(input.reusedLogMessage, {
|
||||
transport: input.transport,
|
||||
threadId: input.thread.id,
|
||||
sessionId: existing,
|
||||
});
|
||||
await dispatchConnectorHook(
|
||||
input.hookCommand,
|
||||
{
|
||||
adapter: input.transport,
|
||||
botUserName: input.hookBotUserName,
|
||||
event: "session.reused",
|
||||
payload: {
|
||||
threadId: input.thread.id,
|
||||
channelId: input.thread.channelId,
|
||||
sessionId: existing,
|
||||
},
|
||||
ts: new Date().toISOString(),
|
||||
},
|
||||
input.logger,
|
||||
);
|
||||
return existing;
|
||||
}
|
||||
await persistMergedThreadState(
|
||||
input.thread,
|
||||
input.bindingsPath,
|
||||
{
|
||||
...threadState,
|
||||
sessionId: existing,
|
||||
sessionId: undefined,
|
||||
},
|
||||
input.errorLabel,
|
||||
);
|
||||
input.logger.core.log(input.reusedLogMessage, {
|
||||
transport: input.transport,
|
||||
threadId: input.thread.id,
|
||||
sessionId: existing,
|
||||
});
|
||||
await dispatchConnectorHook(
|
||||
input.hookCommand,
|
||||
input.logger.core.log(
|
||||
"Connector thread session missing; starting a new session",
|
||||
{
|
||||
adapter: input.transport,
|
||||
botUserName: input.hookBotUserName,
|
||||
event: "session.reused",
|
||||
payload: {
|
||||
threadId: input.thread.id,
|
||||
channelId: input.thread.channelId,
|
||||
sessionId: existing,
|
||||
},
|
||||
ts: new Date().toISOString(),
|
||||
severity: "warn",
|
||||
transport: input.transport,
|
||||
threadId: input.thread.id,
|
||||
sessionId: existing,
|
||||
},
|
||||
input.logger,
|
||||
);
|
||||
return existing;
|
||||
}
|
||||
|
||||
const started = await input.client.startRuntimeSession(input.startRequest);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
type ConnectorThreadState,
|
||||
clearBindingSessionIds,
|
||||
findBindingForDeliveryTarget,
|
||||
isParticipantMuted,
|
||||
isThreadMuted,
|
||||
readBindingForThread,
|
||||
@@ -52,16 +53,16 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe("thread binding refresh", () => {
|
||||
it("refreshes the serialized thread immediately when channel fallback rebinds a thread id", () => {
|
||||
it("refreshes the serialized thread immediately when DM channel fallback rebinds a thread id", () => {
|
||||
const path = createBindingsPath();
|
||||
writeBindings<TestState>(path, {
|
||||
legacy_thread_id: {
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
serializedThread: JSON.stringify({
|
||||
id: "legacy_thread_id",
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
}),
|
||||
sessionId: "sess-1",
|
||||
state: { sessionId: "sess-1", teamId: "T123" },
|
||||
@@ -74,7 +75,7 @@ describe("thread binding refresh", () => {
|
||||
createThread({
|
||||
id: "new_thread_id",
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
isDM: true,
|
||||
}),
|
||||
"Slack",
|
||||
);
|
||||
@@ -85,7 +86,7 @@ describe("thread binding refresh", () => {
|
||||
expect(bindings.new_thread_id?.serializedThread).toContain("new_thread_id");
|
||||
});
|
||||
|
||||
it("refreshes the serialized thread when a participant-key binding matches a new thread id", () => {
|
||||
it("does not rebind a different thread by participant key", () => {
|
||||
const path = createBindingsPath();
|
||||
const participantKey = "slack:team:T123:user:U123";
|
||||
writeBindings<TestState>(path, {
|
||||
@@ -119,10 +120,69 @@ describe("thread binding refresh", () => {
|
||||
participantKey,
|
||||
);
|
||||
|
||||
expect(binding?.serializedThread).toContain("new_thread_id");
|
||||
expect(binding).toBeUndefined();
|
||||
expect(
|
||||
readBindings<TestState>(path)[participantKey]?.serializedThread,
|
||||
).toContain("new_thread_id");
|
||||
).toContain("legacy_thread_id");
|
||||
});
|
||||
|
||||
it("resolves schedule delivery targets by exact binding key before participant metadata", () => {
|
||||
const path = createBindingsPath();
|
||||
writeBindings<TestState>(path, {
|
||||
"slack:C123:111.222": {
|
||||
kind: "conversation",
|
||||
channelId: "slack:C123",
|
||||
isDM: false,
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-thread",
|
||||
state: {
|
||||
sessionId: "sess-thread",
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
},
|
||||
updatedAt: "2026-03-17T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
|
||||
const match = findBindingForDeliveryTarget<TestState>(
|
||||
readBindings<TestState>(path),
|
||||
{
|
||||
bindingKey: "slack:C123:111.222",
|
||||
threadId: "slack:C123:111.222",
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
},
|
||||
);
|
||||
|
||||
expect(match?.key).toBe("slack:C123:111.222");
|
||||
expect(match?.binding.sessionId).toBe("sess-thread");
|
||||
});
|
||||
|
||||
it("resolves schedule delivery targets by participant key when no exact thread binding exists", () => {
|
||||
const path = createBindingsPath();
|
||||
writeBindings<TestState>(path, {
|
||||
"slack:team:T123:user:U123": {
|
||||
channelId: "slack:C123",
|
||||
isDM: true,
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
serializedThread: "{}",
|
||||
sessionId: "sess-participant",
|
||||
state: {
|
||||
sessionId: "sess-participant",
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
},
|
||||
updatedAt: "2026-03-17T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
|
||||
const match = findBindingForDeliveryTarget<TestState>(
|
||||
readBindings<TestState>(path),
|
||||
{
|
||||
participantKey: "slack:team:T123:user:U123",
|
||||
},
|
||||
);
|
||||
|
||||
expect(match?.key).toBe("slack:team:T123:user:U123");
|
||||
expect(match?.binding.sessionId).toBe("sess-participant");
|
||||
});
|
||||
|
||||
it("stores mute state at thread scope instead of participant scope", () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ export type ConnectorThreadState = {
|
||||
};
|
||||
|
||||
export type ConnectorThreadBinding<TState extends ConnectorThreadState> = {
|
||||
kind?: "participant" | "thread" | "thread-participant-mute";
|
||||
kind?: "conversation" | "participant" | "thread" | "thread-participant-mute";
|
||||
channelId: string;
|
||||
isDM: boolean;
|
||||
participantKey?: string;
|
||||
@@ -134,12 +134,9 @@ function clearSerializedThreadSessionId(serializedThread: string | undefined): {
|
||||
|
||||
export function resolveThreadBindingKey(
|
||||
thread: ConnectorBindingThreadIdentity,
|
||||
state?: ConnectorThreadState | null,
|
||||
_state?: ConnectorThreadState | null,
|
||||
): string {
|
||||
return (
|
||||
normalizeParticipantKey(state?.participantKey ?? thread.participantKey) ??
|
||||
thread.id
|
||||
);
|
||||
return thread.id;
|
||||
}
|
||||
|
||||
export function readBindings<TState extends ConnectorThreadState>(
|
||||
@@ -160,40 +157,13 @@ export function findBindingForThread<TState extends ConnectorThreadState>(
|
||||
bindings: ConnectorBindingStore<TState>,
|
||||
thread: ConnectorBindingThreadIdentity,
|
||||
): { binding: ConnectorThreadBinding<TState>; key: string } | undefined {
|
||||
const participantKey = normalizeParticipantKey(thread.participantKey);
|
||||
if (participantKey) {
|
||||
const exactThread = bindings[thread.id];
|
||||
const exactThreadParticipantKey = normalizeParticipantKey(
|
||||
exactThread?.participantKey ?? exactThread?.state?.participantKey,
|
||||
);
|
||||
if (
|
||||
exactThread &&
|
||||
!isControlBinding(exactThread) &&
|
||||
exactThreadParticipantKey === participantKey
|
||||
) {
|
||||
return { key: thread.id, binding: exactThread };
|
||||
}
|
||||
const exactParticipant = bindings[participantKey];
|
||||
if (exactParticipant && !isControlBinding(exactParticipant)) {
|
||||
return { key: participantKey, binding: exactParticipant };
|
||||
}
|
||||
for (const [key, binding] of Object.entries(bindings)) {
|
||||
if (isControlBinding(binding)) {
|
||||
continue;
|
||||
}
|
||||
const bindingParticipantKey = normalizeParticipantKey(
|
||||
binding.participantKey ?? binding.state?.participantKey,
|
||||
);
|
||||
if (bindingParticipantKey === participantKey) {
|
||||
return { key, binding };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
const exact = bindings[thread.id];
|
||||
if (exact && !isControlBinding(exact)) {
|
||||
return { key: thread.id, binding: exact };
|
||||
}
|
||||
if (!thread.isDM) {
|
||||
return undefined;
|
||||
}
|
||||
for (const [key, binding] of Object.entries(bindings)) {
|
||||
if (isControlBinding(binding)) {
|
||||
continue;
|
||||
@@ -282,29 +252,8 @@ export function persistThreadBinding<TState extends ConnectorThreadState>(
|
||||
thread as ConnectorBindingThreadIdentity,
|
||||
state,
|
||||
);
|
||||
for (const [key, binding] of Object.entries(bindings)) {
|
||||
if (isControlBinding(binding)) {
|
||||
continue;
|
||||
}
|
||||
const bindingParticipantKey = normalizeParticipantKey(
|
||||
binding.participantKey ?? binding.state?.participantKey,
|
||||
);
|
||||
const matchesParticipant =
|
||||
participantKey && bindingParticipantKey === participantKey;
|
||||
const matchesLegacyKey = participantKey && key === thread.id;
|
||||
const matchesLegacyThread =
|
||||
!participantKey &&
|
||||
binding.channelId === thread.channelId &&
|
||||
binding.isDM === thread.isDM;
|
||||
if (
|
||||
key !== bindingKey &&
|
||||
(matchesParticipant || matchesLegacyKey || matchesLegacyThread)
|
||||
) {
|
||||
delete bindings[key];
|
||||
}
|
||||
}
|
||||
bindings[bindingKey] = {
|
||||
kind: "participant",
|
||||
kind: "conversation",
|
||||
channelId: thread.channelId,
|
||||
isDM: thread.isDM,
|
||||
participantKey,
|
||||
@@ -531,6 +480,37 @@ export function findBindingForParticipantKey<
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function findBindingForDeliveryTarget<
|
||||
TState extends ConnectorThreadState,
|
||||
>(
|
||||
bindings: ConnectorBindingStore<TState>,
|
||||
input: {
|
||||
bindingKey?: string;
|
||||
threadId?: string;
|
||||
participantKey?: string;
|
||||
},
|
||||
): { binding: ConnectorThreadBinding<TState>; key: string } | undefined {
|
||||
const bindingKey = normalizeParticipantKey(input.bindingKey);
|
||||
if (bindingKey) {
|
||||
const exact = bindings[bindingKey];
|
||||
if (exact && !isControlBinding(exact)) {
|
||||
return { key: bindingKey, binding: exact };
|
||||
}
|
||||
const participantMatch = findBindingForParticipantKey(bindings, bindingKey);
|
||||
if (participantMatch) {
|
||||
return participantMatch;
|
||||
}
|
||||
}
|
||||
const threadId = input.threadId?.trim();
|
||||
if (threadId) {
|
||||
const exact = bindings[threadId];
|
||||
if (exact && !isControlBinding(exact)) {
|
||||
return { key: threadId, binding: exact };
|
||||
}
|
||||
}
|
||||
return findBindingForParticipantKey(bindings, input.participantKey);
|
||||
}
|
||||
|
||||
export async function persistMergedThreadState<
|
||||
TState extends ConnectorThreadState,
|
||||
>(
|
||||
|
||||
@@ -14,6 +14,8 @@ import { toProviderApiKey } from "../utils/provider-auth";
|
||||
import type { Config } from "../utils/types";
|
||||
|
||||
const WORKOS_TOKEN_PREFIX = "workos:";
|
||||
export const CLINE_CREDITS_DASHBOARD_URL =
|
||||
"https://app.cline.bot/dashboard/account?tab=credits";
|
||||
|
||||
type ClineAccountConfig = Pick<Config, "apiKey" | "providerId">;
|
||||
|
||||
@@ -30,6 +32,8 @@ export function formatClineCredits(value: number): string {
|
||||
return formatCreditBalance(normalizeCreditBalance(value));
|
||||
}
|
||||
|
||||
// FIXME: These message checks are temporary until structured error types are
|
||||
// passed through to the CLI instead of plain error strings.
|
||||
export function isClineAccountAuthErrorMessage(message: string): boolean {
|
||||
const normalized = message.trim().toLowerCase();
|
||||
return (
|
||||
@@ -38,6 +42,14 @@ export function isClineAccountAuthErrorMessage(message: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function isClineAccountCreditsErrorMessage(message: string): boolean {
|
||||
const normalized = message.trim().toLowerCase();
|
||||
return (
|
||||
normalized.includes("insufficient balance") &&
|
||||
normalized.includes("cline credits balance")
|
||||
);
|
||||
}
|
||||
|
||||
function resolveAccountApiBaseUrl(input: {
|
||||
clineApiBaseUrl?: string;
|
||||
clineProviderSettings?: ProviderSettings;
|
||||
|
||||
@@ -2,6 +2,10 @@ import { useTerminalDimensions } from "@opentui/react";
|
||||
import type React from "react";
|
||||
import { useState } from "react";
|
||||
import "opentui-spinner/react";
|
||||
import {
|
||||
CLINE_CREDITS_DASHBOARD_URL,
|
||||
isClineAccountCreditsErrorMessage,
|
||||
} from "../cline-account";
|
||||
import { useTerminalBackground } from "../hooks/use-terminal-background";
|
||||
import {
|
||||
getDefaultForeground,
|
||||
@@ -256,6 +260,36 @@ function ToolCallView(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function ClineCreditsErrorView(props: { defaultFg?: string }) {
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg="red" content="* " />
|
||||
<box
|
||||
flexDirection="column"
|
||||
border
|
||||
borderStyle="rounded"
|
||||
borderColor="red"
|
||||
paddingX={1}
|
||||
>
|
||||
<text fg="red">Cline Credits depleted</text>
|
||||
<text
|
||||
fg={props.defaultFg}
|
||||
selectable
|
||||
content="You have run out of Cline credits. Add credits in the dashboard to continue."
|
||||
/>
|
||||
<box flexDirection="row">
|
||||
<text fg="gray">Dashboard: </text>
|
||||
<text fg="cyan" selectable>
|
||||
<a href={CLINE_CREDITS_DASHBOARD_URL}>
|
||||
{CLINE_CREDITS_DASHBOARD_URL}
|
||||
</a>
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatEntryView(props: {
|
||||
entry: ChatEntry;
|
||||
accent?: string;
|
||||
@@ -351,6 +385,9 @@ export function ChatEntryView(props: {
|
||||
);
|
||||
|
||||
case "error":
|
||||
if (isClineAccountCreditsErrorMessage(entry.text)) {
|
||||
return <ClineCreditsErrorView defaultFg={defaultFg} />;
|
||||
}
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg="red" content="* " />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ScrollBoxRenderable } from "@opentui/core";
|
||||
import { useKeyboard, useTerminalDimensions } from "@opentui/react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { palette } from "../palette";
|
||||
import type { RuntimeToolInteraction } from "../types";
|
||||
import { formatApprovalParams } from "./dialogs/tool-approval";
|
||||
@@ -22,23 +23,129 @@ function keyToText(name: string): string {
|
||||
return name === "space" ? " " : name;
|
||||
}
|
||||
|
||||
function getToolShellMaxHeight(terminalHeight: number): number {
|
||||
return Math.max(7, Math.min(14, Math.floor(terminalHeight * 0.38)));
|
||||
}
|
||||
|
||||
function getAskQuestionShellMaxHeight(terminalHeight: number): number {
|
||||
const preferredHeight = Math.max(11, Math.floor(terminalHeight * 0.58));
|
||||
const availableHeight = Math.max(7, terminalHeight - 3);
|
||||
return Math.min(18, preferredHeight, availableHeight);
|
||||
}
|
||||
|
||||
function getAskQuestionBodyHeight(shellMaxHeight: number): number {
|
||||
return Math.max(1, shellMaxHeight - 4);
|
||||
}
|
||||
|
||||
function addWrappedWidth(input: {
|
||||
rows: number;
|
||||
lineWidth: number;
|
||||
width: number;
|
||||
maxWidth: number;
|
||||
}): { rows: number; lineWidth: number } {
|
||||
if (input.width <= 0) {
|
||||
return { rows: input.rows, lineWidth: input.lineWidth };
|
||||
}
|
||||
|
||||
let rows = input.rows;
|
||||
let remainingWidth = input.width;
|
||||
let lineWidth = input.lineWidth;
|
||||
|
||||
if (lineWidth > 0) {
|
||||
const availableWidth = input.maxWidth - lineWidth;
|
||||
if (remainingWidth <= availableWidth) {
|
||||
return { rows, lineWidth: lineWidth + remainingWidth };
|
||||
}
|
||||
|
||||
remainingWidth -= Math.max(0, availableWidth);
|
||||
rows += 1;
|
||||
lineWidth = 0;
|
||||
}
|
||||
|
||||
rows += Math.max(0, Math.ceil(remainingWidth / input.maxWidth) - 1);
|
||||
lineWidth = remainingWidth % input.maxWidth || input.maxWidth;
|
||||
|
||||
return { rows, lineWidth };
|
||||
}
|
||||
|
||||
function countWrappedRows(text: string, width: number): number {
|
||||
const safeWidth = Math.max(1, width);
|
||||
const paragraphs = text.split("\n");
|
||||
let rows = 0;
|
||||
|
||||
for (const paragraph of paragraphs) {
|
||||
rows += 1;
|
||||
let lineWidth = 0;
|
||||
const tokens = paragraph.match(/\s+|\S+/g) ?? [];
|
||||
|
||||
for (const token of tokens) {
|
||||
const tokenWidth = Bun.stringWidth(token);
|
||||
const isWhitespace = /^\s+$/.test(token);
|
||||
|
||||
if (
|
||||
!isWhitespace &&
|
||||
lineWidth > 0 &&
|
||||
lineWidth + tokenWidth > safeWidth
|
||||
) {
|
||||
rows += 1;
|
||||
lineWidth = 0;
|
||||
}
|
||||
|
||||
const next = addWrappedWidth({
|
||||
rows,
|
||||
lineWidth,
|
||||
width: tokenWidth,
|
||||
maxWidth: safeWidth,
|
||||
});
|
||||
rows = next.rows;
|
||||
lineWidth = next.lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function getAskQuestionContentHeight(input: {
|
||||
terminalWidth: number;
|
||||
question: string;
|
||||
options: string[];
|
||||
customText: string;
|
||||
}): number {
|
||||
const questionWidth = Math.max(1, input.terminalWidth - 3);
|
||||
const optionTextWidth = Math.max(1, input.terminalWidth - 7);
|
||||
const questionRows = countWrappedRows(input.question, questionWidth);
|
||||
const optionRows = input.options.reduce(
|
||||
(rows, option) => rows + countWrappedRows(option, optionTextWidth),
|
||||
0,
|
||||
);
|
||||
const customRows = countWrappedRows(input.customText, optionTextWidth);
|
||||
return questionRows + 1 + optionRows + customRows;
|
||||
}
|
||||
|
||||
function getAskQuestionChoiceId(interactionId: number, index: number): string {
|
||||
return `ask-question-${interactionId.toString()}-choice-${index.toString()}`;
|
||||
}
|
||||
|
||||
function Shell(
|
||||
props: Pick<
|
||||
InlineToolResponseProps,
|
||||
"accent" | "inputBackground" | "inputForeground"
|
||||
> & {
|
||||
title: string;
|
||||
maxHeight?: number;
|
||||
overflow?: "hidden";
|
||||
children: React.ReactNode;
|
||||
},
|
||||
) {
|
||||
const { height } = useTerminalDimensions();
|
||||
const maxHeight = Math.max(7, Math.min(14, Math.floor(height * 0.38)));
|
||||
const maxHeight = props.maxHeight ?? getToolShellMaxHeight(height);
|
||||
|
||||
return (
|
||||
<box
|
||||
flexDirection="column"
|
||||
width="100%"
|
||||
maxHeight={maxHeight}
|
||||
overflow={props.overflow}
|
||||
backgroundColor={props.inputBackground}
|
||||
paddingX={1}
|
||||
paddingY={1}
|
||||
@@ -59,6 +166,7 @@ function ChoiceButton(props: {
|
||||
onPress: () => void;
|
||||
}) {
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: OpenTUI boxes handle terminal mouse input.
|
||||
<box
|
||||
paddingX={1}
|
||||
backgroundColor={props.selected ? palette.selection : undefined}
|
||||
@@ -155,9 +263,11 @@ function AskQuestionResponse(
|
||||
},
|
||||
) {
|
||||
const { interaction } = props;
|
||||
const { height, width } = useTerminalDimensions();
|
||||
const [selected, setSelected] = useState(0);
|
||||
const [customValue, setCustomValue] = useState("");
|
||||
const [customEmptyAttempted, setCustomEmptyAttempted] = useState(false);
|
||||
const scrollRef = useRef<ScrollBoxRenderable | null>(null);
|
||||
const selectedRef = useRef(0);
|
||||
const customValueRef = useRef("");
|
||||
const interactionId = interaction.id;
|
||||
@@ -165,6 +275,24 @@ function AskQuestionResponse(
|
||||
const customIndex = interaction.options.length;
|
||||
const isTyping = selected === customIndex;
|
||||
const totalChoices = interaction.options.length + 1;
|
||||
const shellMaxHeight = getAskQuestionShellMaxHeight(height);
|
||||
const maxBodyHeight = getAskQuestionBodyHeight(shellMaxHeight);
|
||||
const customText = isTyping
|
||||
? customValue
|
||||
? `${customValue}|`
|
||||
: customEmptyAttempted
|
||||
? "Type a response first..."
|
||||
: "Type a response..."
|
||||
: "Type a response...";
|
||||
const bodyHeight = Math.min(
|
||||
maxBodyHeight,
|
||||
getAskQuestionContentHeight({
|
||||
terminalWidth: width,
|
||||
question: interaction.question,
|
||||
options: interaction.options,
|
||||
customText,
|
||||
}),
|
||||
);
|
||||
|
||||
const selectIndex = useCallback(
|
||||
(index: number) => {
|
||||
@@ -192,6 +320,26 @@ function AskQuestionResponse(
|
||||
[interactionId, onResolveAskQuestion],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const choiceId = getAskQuestionChoiceId(interactionId, selected);
|
||||
let canceled = false;
|
||||
const scrollSelectedChoiceIntoView = () => {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollRef.current?.scrollChildIntoView(choiceId);
|
||||
};
|
||||
|
||||
scrollSelectedChoiceIntoView();
|
||||
queueMicrotask(scrollSelectedChoiceIntoView);
|
||||
const timeout = setTimeout(scrollSelectedChoiceIntoView, 0);
|
||||
return () => {
|
||||
canceled = true;
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [interactionId, selected]);
|
||||
|
||||
useKeyboard((key) => {
|
||||
const typing = selectedRef.current === customIndex;
|
||||
if (key.name === "escape") {
|
||||
@@ -261,64 +409,91 @@ function AskQuestionResponse(
|
||||
accent={props.accent}
|
||||
inputBackground={props.inputBackground}
|
||||
inputForeground={props.inputForeground}
|
||||
maxHeight={shellMaxHeight}
|
||||
overflow="hidden"
|
||||
>
|
||||
<text fg={props.inputForeground} selectable>
|
||||
{interaction.question}
|
||||
</text>
|
||||
<scrollbox
|
||||
ref={scrollRef}
|
||||
height={bodyHeight}
|
||||
width="100%"
|
||||
scrollY
|
||||
scrollX={false}
|
||||
viewportOptions={{ overflow: "hidden" }}
|
||||
contentOptions={{ flexDirection: "column" }}
|
||||
>
|
||||
<box flexDirection="column" gap={1} flexShrink={0} width="100%">
|
||||
<text fg={props.inputForeground} selectable flexShrink={0}>
|
||||
{interaction.question}
|
||||
</text>
|
||||
|
||||
<box flexDirection="column">
|
||||
{interaction.options.map((option, index) => {
|
||||
const optionSelected = !isTyping && selected === index;
|
||||
return (
|
||||
<box flexDirection="column" flexShrink={0} width="100%">
|
||||
{interaction.options.map((option, index) => {
|
||||
const optionSelected = !isTyping && selected === index;
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: OpenTUI boxes handle terminal mouse input.
|
||||
<box
|
||||
id={getAskQuestionChoiceId(interactionId, index)}
|
||||
key={`${index.toString()}:${option}`}
|
||||
paddingX={1}
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
flexShrink={0}
|
||||
width="100%"
|
||||
backgroundColor={
|
||||
optionSelected ? palette.selection : undefined
|
||||
}
|
||||
onMouseDown={() => resolveAnswer(option)}
|
||||
>
|
||||
<text
|
||||
fg={optionSelected ? palette.textOnSelection : "gray"}
|
||||
flexShrink={0}
|
||||
>
|
||||
{optionSelected ? ">" : " "}
|
||||
</text>
|
||||
<text
|
||||
fg={
|
||||
optionSelected
|
||||
? palette.textOnSelection
|
||||
: props.inputForeground
|
||||
}
|
||||
flexGrow={1}
|
||||
flexShrink={1}
|
||||
>
|
||||
{option}
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
})}
|
||||
{/* biome-ignore lint/a11y/noStaticElementInteractions: OpenTUI boxes handle terminal mouse input. */}
|
||||
<box
|
||||
key={`${index.toString()}:${option}`}
|
||||
id={getAskQuestionChoiceId(interactionId, customIndex)}
|
||||
paddingX={1}
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
backgroundColor={optionSelected ? palette.selection : undefined}
|
||||
onMouseDown={() => resolveAnswer(option)}
|
||||
flexShrink={0}
|
||||
width="100%"
|
||||
backgroundColor={isTyping ? palette.selection : undefined}
|
||||
onMouseDown={() => selectIndex(customIndex)}
|
||||
>
|
||||
<text
|
||||
fg={optionSelected ? palette.textOnSelection : "gray"}
|
||||
fg={isTyping ? palette.textOnSelection : "gray"}
|
||||
flexShrink={0}
|
||||
>
|
||||
{optionSelected ? ">" : " "}
|
||||
</text>
|
||||
<text
|
||||
fg={
|
||||
optionSelected
|
||||
? palette.textOnSelection
|
||||
: props.inputForeground
|
||||
}
|
||||
>
|
||||
{option}
|
||||
{isTyping ? ">" : " "}
|
||||
</text>
|
||||
{isTyping ? (
|
||||
<text fg={palette.textOnSelection} flexGrow={1} flexShrink={1}>
|
||||
{customText}
|
||||
</text>
|
||||
) : (
|
||||
<text fg={props.inputPlaceholder} flexGrow={1} flexShrink={1}>
|
||||
Type a response...
|
||||
</text>
|
||||
)}
|
||||
</box>
|
||||
);
|
||||
})}
|
||||
<box
|
||||
paddingX={1}
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
backgroundColor={isTyping ? palette.selection : undefined}
|
||||
onMouseDown={() => selectIndex(customIndex)}
|
||||
>
|
||||
<text fg={isTyping ? palette.textOnSelection : "gray"} flexShrink={0}>
|
||||
{isTyping ? ">" : " "}
|
||||
</text>
|
||||
{isTyping ? (
|
||||
<text fg={palette.textOnSelection} flexGrow={1}>
|
||||
{customValue
|
||||
? `${customValue}|`
|
||||
: customEmptyAttempted
|
||||
? "Type a response first..."
|
||||
: "Type a response..."}
|
||||
</text>
|
||||
) : (
|
||||
<text fg={props.inputPlaceholder}>Type a response...</text>
|
||||
)}
|
||||
</box>
|
||||
</box>
|
||||
</box>
|
||||
</scrollbox>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { readGlobalSettings, setAutoUpdateEnabledGlobally } from "@cline/core";
|
||||
import { useTerminalDimensions } from "@opentui/react";
|
||||
import type { ChoiceContext } from "@opentui-ui/dialog";
|
||||
import { useDialogKeyboard } from "@opentui-ui/dialog/react";
|
||||
@@ -368,6 +369,9 @@ export function ConfigPanelContent(props: ConfigPanelProps) {
|
||||
const [autoApprove, setAutoApprove] = useState(
|
||||
config.toolPolicies["*"]?.autoApprove !== false,
|
||||
);
|
||||
const [autoUpdateEnabled, setAutoUpdateEnabled] = useState(
|
||||
() => readGlobalSettings().autoUpdateEnabled,
|
||||
);
|
||||
const [verbose, setVerbose] = useState(config.verbose);
|
||||
const [compactionMode, setCompactionMode] = useState(
|
||||
props.currentCompactionMode,
|
||||
@@ -445,6 +449,7 @@ export function ConfigPanelContent(props: ConfigPanelProps) {
|
||||
id: "auto-approve",
|
||||
label: "Auto-approve all",
|
||||
});
|
||||
r.push({ kind: "toggle", id: "auto-update", label: "Auto update" });
|
||||
r.push({ kind: "toggle", id: "verbose", label: "Verbose" });
|
||||
} else {
|
||||
const activeItems = resolveActiveConfigItems(configData, activeTab);
|
||||
@@ -584,6 +589,13 @@ export function ConfigPanelContent(props: ConfigPanelProps) {
|
||||
setAutoApprove(!autoApprove);
|
||||
props.onToggleAutoApprove();
|
||||
break;
|
||||
case "auto-update":
|
||||
setAutoUpdateEnabled((previous) => {
|
||||
const next = !previous;
|
||||
setAutoUpdateEnabledGlobally(next);
|
||||
return next;
|
||||
});
|
||||
break;
|
||||
case "compaction": {
|
||||
const nextMode = getNextCliCompactionMode(compactionMode);
|
||||
setCompactionMode(nextMode);
|
||||
@@ -789,6 +801,9 @@ export function ConfigPanelContent(props: ConfigPanelProps) {
|
||||
} else if (row.id === "auto-approve") {
|
||||
value = autoApprove ? "● on" : "○ off";
|
||||
valueColor = autoApprove ? palette.success : "gray";
|
||||
} else if (row.id === "auto-update") {
|
||||
value = autoUpdateEnabled ? "● on" : "○ off";
|
||||
valueColor = autoUpdateEnabled ? palette.success : "gray";
|
||||
} else if (row.id === "compaction") {
|
||||
value = formatCliCompactionMode(compactionMode);
|
||||
valueColor = COMPACTION_MODE_COLORS[compactionMode];
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
resolveLocalClineAuthToken,
|
||||
saveLocalProviderOAuthCredentials,
|
||||
saveLocalProviderSettings,
|
||||
setAutoUpdateEnabledGlobally,
|
||||
setDisabledPlugin,
|
||||
setDisabledTools,
|
||||
setTelemetryOptOutGlobally,
|
||||
@@ -155,6 +156,13 @@ export async function handleDesktopCommand(
|
||||
setTelemetryOptOutGlobally(args.telemetry_opt_out);
|
||||
return readGlobalSettings();
|
||||
}
|
||||
if (command === "set_auto_update_enabled") {
|
||||
if (typeof args?.auto_update_enabled !== "boolean") {
|
||||
throw new Error("auto_update_enabled must be a boolean");
|
||||
}
|
||||
setAutoUpdateEnabledGlobally(args.auto_update_enabled);
|
||||
return readGlobalSettings();
|
||||
}
|
||||
if (command === "list_connector_channels") {
|
||||
return connectorChannelsPayload();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export type SettingsSection = (typeof navCategories)[number];
|
||||
type Theme = "dark" | "light";
|
||||
type GlobalSettingsResponse = {
|
||||
telemetryOptOut: boolean;
|
||||
autoUpdateEnabled: boolean;
|
||||
};
|
||||
|
||||
const PROVIDER_CATALOG_CACHE_TTL_MS = 60_000;
|
||||
@@ -525,20 +526,29 @@ function GeneralSettingsContent({
|
||||
const [telemetryLoading, setTelemetryLoading] = useState(true);
|
||||
const [telemetrySaving, setTelemetrySaving] = useState(false);
|
||||
const [telemetryError, setTelemetryError] = useState<string | null>(null);
|
||||
const [autoUpdateEnabled, setAutoUpdateEnabled] = useState(true);
|
||||
const [autoUpdateLoading, setAutoUpdateLoading] = useState(true);
|
||||
const [autoUpdateSaving, setAutoUpdateSaving] = useState(false);
|
||||
const [autoUpdateError, setAutoUpdateError] = useState<string | null>(null);
|
||||
|
||||
const loadGlobalSettings = useCallback(async () => {
|
||||
setTelemetryLoading(true);
|
||||
setTelemetryError(null);
|
||||
setAutoUpdateLoading(true);
|
||||
setAutoUpdateError(null);
|
||||
try {
|
||||
const settings = await desktopClient.invoke<GlobalSettingsResponse>(
|
||||
"get_global_settings",
|
||||
);
|
||||
setTelemetryOptOut(settings.telemetryOptOut);
|
||||
setAutoUpdateEnabled(settings.autoUpdateEnabled);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
setTelemetryError(message);
|
||||
setAutoUpdateError(message);
|
||||
} finally {
|
||||
setTelemetryLoading(false);
|
||||
setAutoUpdateLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -571,6 +581,28 @@ function GeneralSettingsContent({
|
||||
}
|
||||
};
|
||||
|
||||
const updateAutoUpdateEnabled = async (nextValue: boolean) => {
|
||||
const previousValue = autoUpdateEnabled;
|
||||
setAutoUpdateEnabled(nextValue);
|
||||
setAutoUpdateSaving(true);
|
||||
setAutoUpdateError(null);
|
||||
try {
|
||||
const settings = await desktopClient.invoke<GlobalSettingsResponse>(
|
||||
"set_auto_update_enabled",
|
||||
{
|
||||
auto_update_enabled: nextValue,
|
||||
},
|
||||
);
|
||||
setAutoUpdateEnabled(settings.autoUpdateEnabled);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
setAutoUpdateEnabled(previousValue);
|
||||
setAutoUpdateError(message);
|
||||
} finally {
|
||||
setAutoUpdateSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full">
|
||||
<div className="mx-auto max-w-3xl px-8 py-6">
|
||||
@@ -605,6 +637,29 @@ function GeneralSettingsContent({
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-4 rounded-lg border border-border p-5">
|
||||
<div className="flex items-center justify-between gap-5 max-[720px]:flex-col max-[720px]:items-stretch">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-foreground">Auto update</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Automatically install CLI updates on startup.
|
||||
</p>
|
||||
{autoUpdateError ? (
|
||||
<p className="mt-2 text-xs text-destructive">
|
||||
Failed to update auto update setting: {autoUpdateError}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<Switch
|
||||
aria-label="Auto update"
|
||||
checked={autoUpdateEnabled}
|
||||
disabled={autoUpdateLoading || autoUpdateSaving}
|
||||
onCheckedChange={(checked) =>
|
||||
void updateAutoUpdateEnabled(checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-4 rounded-lg border border-border p-5">
|
||||
<div className="flex items-center justify-between gap-5 max-[720px]:flex-col max-[720px]:items-stretch">
|
||||
<div>
|
||||
|
||||
Generated
+5
-5
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.88.0",
|
||||
"version": "3.89.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.88.0",
|
||||
"version": "3.89.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.37.0",
|
||||
@@ -18543,9 +18543,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
|
||||
"version": "1.8.4",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
|
||||
"integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.88.0",
|
||||
"version": "3.89.0",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.84.0"
|
||||
|
||||
@@ -87,6 +87,30 @@ describe("AnthropicHandler", () => {
|
||||
result.id.should.equal("claude-opus-4-8:1m")
|
||||
result.info.should.deepEqual(anthropicModels["claude-opus-4-8:1m"])
|
||||
})
|
||||
|
||||
it("should return the Fable 5 model when configured", () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-fable-5",
|
||||
})
|
||||
|
||||
const result = handler.getModel()
|
||||
|
||||
result.id.should.equal("claude-fable-5")
|
||||
result.info.should.deepEqual(anthropicModels["claude-fable-5"])
|
||||
})
|
||||
|
||||
it("should return the Fable 5 1m model when configured", () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-fable-5:1m",
|
||||
})
|
||||
|
||||
const result = handler.getModel()
|
||||
|
||||
result.id.should.equal("claude-fable-5:1m")
|
||||
result.info.should.deepEqual(anthropicModels["claude-fable-5:1m"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("createMessage", () => {
|
||||
|
||||
@@ -215,6 +215,11 @@ describe("AwsBedrockHandler", () => {
|
||||
bedrockModels["anthropic.claude-opus-4-8:1m"].supportsGlobalEndpoint.should.equal(true)
|
||||
})
|
||||
|
||||
it("should mark Bedrock Fable 5 variants as global-endpoint capable", () => {
|
||||
bedrockModels["anthropic.claude-fable-5"].supportsGlobalEndpoint.should.equal(true)
|
||||
bedrockModels["anthropic.claude-fable-5:1m"].supportsGlobalEndpoint.should.equal(true)
|
||||
})
|
||||
|
||||
it("should include Vertex Opus 4.7 variants in the derived global model list", () => {
|
||||
vertexModels["claude-opus-4-7"].supportsGlobalEndpoint.should.equal(true)
|
||||
vertexModels["claude-opus-4-7:1m"].supportsGlobalEndpoint.should.equal(true)
|
||||
@@ -228,6 +233,13 @@ describe("AwsBedrockHandler", () => {
|
||||
vertexGlobalModels.should.have.property("claude-opus-4-8")
|
||||
vertexGlobalModels.should.have.property("claude-opus-4-8:1m")
|
||||
})
|
||||
|
||||
it("should include Vertex Fable 5 variants in the derived global model list", () => {
|
||||
vertexModels["claude-fable-5"].supportsGlobalEndpoint.should.equal(true)
|
||||
vertexModels["claude-fable-5:1m"].supportsGlobalEndpoint.should.equal(true)
|
||||
vertexGlobalModels.should.have.property("claude-fable-5")
|
||||
vertexGlobalModels.should.have.property("claude-fable-5:1m")
|
||||
})
|
||||
})
|
||||
|
||||
const mockOptions: AwsBedrockHandlerOptions = {
|
||||
|
||||
@@ -416,6 +416,26 @@ describe("ClaudeCodeHandler", () => {
|
||||
model.info.contextWindow.should.equal(1_000_000)
|
||||
})
|
||||
|
||||
it("should support Fable 5 model id", () => {
|
||||
const handler = new ClaudeCodeHandler({
|
||||
apiModelId: "claude-fable-5",
|
||||
})
|
||||
|
||||
const model = handler.getModel()
|
||||
model.id.should.equal("claude-fable-5")
|
||||
model.info.contextWindow.should.equal(200_000)
|
||||
})
|
||||
|
||||
it("should support Fable 5 1m model id", () => {
|
||||
const handler = new ClaudeCodeHandler({
|
||||
apiModelId: "claude-fable-5[1m]",
|
||||
})
|
||||
|
||||
const model = handler.getModel()
|
||||
model.id.should.equal("claude-fable-5[1m]")
|
||||
model.info.contextWindow.should.equal(1_000_000)
|
||||
})
|
||||
|
||||
it("should support Opus 1m alias model id", () => {
|
||||
const handler = new ClaudeCodeHandler({
|
||||
apiModelId: "opus[1m]",
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
CLAUDE_SONNET_1M_SUFFIX,
|
||||
ModelInfo,
|
||||
OPENROUTER_PROVIDER_PREFERENCES,
|
||||
openRouterClaudeFable51mModelId,
|
||||
openRouterClaudeOpus461mModelId,
|
||||
openRouterClaudeOpus471mModelId,
|
||||
openRouterClaudeOpus481mModelId,
|
||||
@@ -63,7 +64,8 @@ export async function createOpenRouterStream(
|
||||
model.id === openRouterClaudeSonnet461mModelId ||
|
||||
model.id === openRouterClaudeOpus461mModelId ||
|
||||
model.id === openRouterClaudeOpus471mModelId ||
|
||||
model.id === openRouterClaudeOpus481mModelId
|
||||
model.id === openRouterClaudeOpus481mModelId ||
|
||||
model.id === openRouterClaudeFable51mModelId
|
||||
if (isClaude1m) {
|
||||
// remove the custom :1m suffix, to create the model id openrouter API expects
|
||||
model.id = model.id.slice(0, -CLAUDE_SONNET_1M_SUFFIX.length)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import {
|
||||
CLAUDE_SONNET_1M_SUFFIX,
|
||||
ModelInfo,
|
||||
openRouterClaudeFable51mModelId,
|
||||
openRouterClaudeOpus461mModelId,
|
||||
openRouterClaudeOpus471mModelId,
|
||||
openRouterClaudeOpus481mModelId,
|
||||
@@ -39,7 +40,8 @@ export async function createVercelAIGatewayStream(
|
||||
model.id === openRouterClaudeSonnet461mModelId ||
|
||||
model.id === openRouterClaudeOpus461mModelId ||
|
||||
model.id === openRouterClaudeOpus471mModelId ||
|
||||
model.id === openRouterClaudeOpus481mModelId
|
||||
model.id === openRouterClaudeOpus481mModelId ||
|
||||
model.id === openRouterClaudeFable51mModelId
|
||||
if (isClaude1m) {
|
||||
// remove the custom :1m suffix, to create the model id the API expects
|
||||
model.id = model.id.slice(0, -CLAUDE_SONNET_1M_SUFFIX.length)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as disk from "@core/storage/disk"
|
||||
import { openRouterClaudeFable51mModelId } from "@shared/api"
|
||||
import axios from "axios"
|
||||
import { expect } from "chai"
|
||||
import fs from "fs/promises"
|
||||
import { afterEach, beforeEach, describe, it } from "mocha"
|
||||
import sinon from "sinon"
|
||||
import { ClineEnv, Environment } from "@/config"
|
||||
import type { Controller } from "@/core/controller"
|
||||
import { StateManager } from "@/core/storage/StateManager"
|
||||
import { getFeatureFlagsService } from "@/services/feature-flags"
|
||||
import { FeatureFlag } from "@/shared/services/feature-flags/feature-flags"
|
||||
@@ -37,7 +39,7 @@ describe("refreshClineModels", () => {
|
||||
sandbox.stub(StateManager, "get").returns({
|
||||
getModelsCache: () => null,
|
||||
setModelsCache: () => {},
|
||||
} as any)
|
||||
} as unknown as StateManager)
|
||||
sandbox.stub(disk, "ensureCacheDirectoryExists").resolves("/tmp")
|
||||
sandbox.stub(fs, "writeFile").resolves()
|
||||
sandbox.stub(axios, "get").resolves({
|
||||
@@ -67,11 +69,70 @@ describe("refreshClineModels", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const models = await refreshClineModels({} as any)
|
||||
const models = await refreshClineModels({} as Controller)
|
||||
const qwen37 = models["qwen/qwen3.7-max"]
|
||||
|
||||
expect(qwen37.supportsPromptCache).to.equal(true)
|
||||
expect(qwen37.cacheReadsPrice).to.equal(0.25)
|
||||
expect(qwen37.cacheWritesPrice).to.equal(undefined)
|
||||
})
|
||||
|
||||
it("adds Claude Fable 5 context variants to the Cline model list", async () => {
|
||||
sandbox.stub(getFeatureFlagsService(), "getBooleanFlagEnabled").callsFake((flag) => {
|
||||
return flag === FeatureFlag.EXTENSION_CLINE_MODELS_ENDPOINT
|
||||
})
|
||||
sandbox.stub(ClineEnv, "config").returns({
|
||||
environment: Environment.production,
|
||||
appBaseUrl: "https://app.cline-mock.bot",
|
||||
apiBaseUrl: "https://api.cline-mock.bot",
|
||||
mcpBaseUrl: "https://api.cline-mock.bot/v1/mcp",
|
||||
})
|
||||
sandbox.stub(StateManager, "get").returns({
|
||||
getModelsCache: () => null,
|
||||
setModelsCache: () => {},
|
||||
} as unknown as StateManager)
|
||||
sandbox.stub(disk, "ensureCacheDirectoryExists").resolves("/tmp")
|
||||
sandbox.stub(fs, "writeFile").resolves()
|
||||
sandbox.stub(axios, "get").resolves({
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
id: "anthropic/claude-fable-5",
|
||||
name: "Claude Fable 5",
|
||||
description: "Fetched description",
|
||||
context_length: 1_000_000,
|
||||
top_provider: {
|
||||
max_completion_tokens: 128_000,
|
||||
context_length: 1_000_000,
|
||||
is_moderated: false,
|
||||
},
|
||||
architecture: {
|
||||
modality: ["text", "image"],
|
||||
},
|
||||
pricing: {
|
||||
prompt: "0.00001",
|
||||
completion: "0.00005",
|
||||
input_cache_read: "0.000001",
|
||||
input_cache_write: "0.0000125",
|
||||
},
|
||||
supported_parameters: ["include_reasoning", "reasoning"],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const models = await refreshClineModels({} as Controller)
|
||||
const fable = models["anthropic/claude-fable-5"]
|
||||
const fable1m = models[openRouterClaudeFable51mModelId]
|
||||
|
||||
expect(fable.contextWindow).to.equal(200_000)
|
||||
expect(fable.maxTokens).to.equal(128_000)
|
||||
expect(fable.supportsPromptCache).to.equal(true)
|
||||
expect(fable.inputPrice).to.equal(10)
|
||||
expect(fable.outputPrice).to.equal(50)
|
||||
expect(fable.cacheWritesPrice).to.equal(12.5)
|
||||
expect(fable.cacheReadsPrice).to.equal(1)
|
||||
expect(fable1m.contextWindow).to.equal(1_000_000)
|
||||
expect(fable1m.tiers).to.not.equal(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,8 +11,10 @@ import { StateManager } from "@/core/storage/StateManager"
|
||||
import { featureFlagsService } from "@/services/feature-flags"
|
||||
import {
|
||||
ANTHROPIC_MAX_THINKING_BUDGET,
|
||||
CLAUDE_FABLE_1M_TIERS,
|
||||
CLAUDE_OPUS_1M_TIERS,
|
||||
CLAUDE_SONNET_1M_TIERS,
|
||||
openRouterClaudeFable51mModelId,
|
||||
openRouterClaudeOpus461mModelId,
|
||||
openRouterClaudeOpus471mModelId,
|
||||
openRouterClaudeOpus481mModelId,
|
||||
@@ -78,7 +80,7 @@ interface ClineRawModelInfo {
|
||||
input_cache_write?: string
|
||||
} | null
|
||||
supports_global_endpoint?: boolean | null
|
||||
tiers?: any[] | null
|
||||
tiers?: ModelInfo["tiers"] | null
|
||||
supported_parameters?: ClineSupportedParams[] | null
|
||||
}
|
||||
|
||||
@@ -138,7 +140,7 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
|
||||
let models: Record<string, ModelInfo> = {}
|
||||
try {
|
||||
const rawModels = await fetchRawClineModels()
|
||||
const parsePrice = (price: any) => {
|
||||
const parsePrice = (price: unknown) => {
|
||||
if (price === undefined || price === null || price === "") {
|
||||
return undefined
|
||||
}
|
||||
@@ -204,6 +206,14 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
|
||||
modelInfo.cacheWritesPrice = 6.25
|
||||
modelInfo.cacheReadsPrice = 0.5
|
||||
break
|
||||
case "anthropic/claude-fable-5":
|
||||
modelInfo.contextWindow = 200_000
|
||||
modelInfo.supportsPromptCache = true
|
||||
modelInfo.inputPrice = 10
|
||||
modelInfo.outputPrice = 50
|
||||
modelInfo.cacheWritesPrice = 12.5
|
||||
modelInfo.cacheReadsPrice = 1
|
||||
break
|
||||
case "anthropic/claude-opus-4.5":
|
||||
modelInfo.supportsPromptCache = true
|
||||
modelInfo.cacheWritesPrice = 6.25
|
||||
@@ -299,6 +309,12 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
|
||||
models[openRouterClaudeOpus481mModelId] = claudeOpus1mModelInfo
|
||||
}
|
||||
}
|
||||
if (rawModel.id === "anthropic/claude-fable-5") {
|
||||
const claudeFable1mModelInfo = cloneDeep(modelInfo)
|
||||
claudeFable1mModelInfo.contextWindow = 1_000_000
|
||||
claudeFable1mModelInfo.tiers = CLAUDE_FABLE_1M_TIERS
|
||||
models[openRouterClaudeFable51mModelId] = claudeFable1mModelInfo
|
||||
}
|
||||
}
|
||||
if (Object.keys(models).length === 0) {
|
||||
throw new Error("No Cline models returned from API")
|
||||
|
||||
@@ -8,8 +8,10 @@ import path from "path"
|
||||
import { StateManager } from "@/core/storage/StateManager"
|
||||
import {
|
||||
ANTHROPIC_MAX_THINKING_BUDGET,
|
||||
CLAUDE_FABLE_1M_TIERS,
|
||||
CLAUDE_OPUS_1M_TIERS,
|
||||
CLAUDE_SONNET_1M_TIERS,
|
||||
openRouterClaudeFable51mModelId,
|
||||
openRouterClaudeOpus461mModelId,
|
||||
openRouterClaudeOpus471mModelId,
|
||||
openRouterClaudeOpus481mModelId,
|
||||
@@ -179,6 +181,14 @@ async function fetchAndCacheModels(controller: Controller): Promise<Record<strin
|
||||
modelInfo.cacheWritesPrice = 6.25
|
||||
modelInfo.cacheReadsPrice = 0.5
|
||||
break
|
||||
case "anthropic/claude-fable-5":
|
||||
modelInfo.contextWindow = 200_000 // restrict to 200k, 1m variant created below
|
||||
modelInfo.supportsPromptCache = true
|
||||
modelInfo.inputPrice = 10
|
||||
modelInfo.outputPrice = 50
|
||||
modelInfo.cacheWritesPrice = 12.5
|
||||
modelInfo.cacheReadsPrice = 1
|
||||
break
|
||||
case "anthropic/claude-opus-4.5":
|
||||
modelInfo.supportsPromptCache = true
|
||||
modelInfo.cacheWritesPrice = 6.25
|
||||
@@ -322,6 +332,12 @@ async function fetchAndCacheModels(controller: Controller): Promise<Record<strin
|
||||
models[openRouterClaudeOpus481mModelId] = claudeOpus1mModelInfo
|
||||
}
|
||||
}
|
||||
if (rawModel.id === "anthropic/claude-fable-5") {
|
||||
const claudeFable1mModelInfo = cloneDeep(modelInfo)
|
||||
claudeFable1mModelInfo.contextWindow = 1_000_000
|
||||
claudeFable1mModelInfo.tiers = CLAUDE_FABLE_1M_TIERS
|
||||
models[openRouterClaudeFable51mModelId] = claudeFable1mModelInfo
|
||||
}
|
||||
}
|
||||
// Save models and cache them in memory
|
||||
await fs.writeFile(openRouterModelsFilePath, JSON.stringify(models))
|
||||
|
||||
@@ -144,6 +144,22 @@ export const CLAUDE_OPUS_1M_TIERS = [
|
||||
cacheReadsPrice: 1.0,
|
||||
},
|
||||
]
|
||||
export const CLAUDE_FABLE_1M_TIERS = [
|
||||
{
|
||||
contextWindow: 200000,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
},
|
||||
{
|
||||
contextWindow: Number.MAX_SAFE_INTEGER,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
},
|
||||
]
|
||||
|
||||
export interface HicapCompatibleModelInfo extends ModelInfo {
|
||||
temperature?: number
|
||||
@@ -318,6 +334,29 @@ export const anthropicModels = {
|
||||
cacheReadsPrice: 0.5,
|
||||
tiers: CLAUDE_OPUS_1M_TIERS,
|
||||
},
|
||||
"claude-fable-5": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsReasoning: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
},
|
||||
"claude-fable-5:1m": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 1_000_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsReasoning: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
tiers: CLAUDE_FABLE_1M_TIERS,
|
||||
},
|
||||
"claude-opus-4-7": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
@@ -505,6 +544,17 @@ export const claudeCodeModels = {
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
"claude-fable-5": {
|
||||
...anthropicModels["claude-fable-5"],
|
||||
contextWindow: 200_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
"claude-fable-5[1m]": {
|
||||
...anthropicModels["claude-fable-5:1m"],
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
"claude-opus-4-7": {
|
||||
...anthropicModels["claude-opus-4-7"],
|
||||
contextWindow: 200_000,
|
||||
@@ -685,6 +735,31 @@ export const bedrockModels = {
|
||||
cacheReadsPrice: 0.5,
|
||||
tiers: CLAUDE_OPUS_1M_TIERS,
|
||||
},
|
||||
"anthropic.claude-fable-5": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsReasoning: true,
|
||||
supportsGlobalEndpoint: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
},
|
||||
"anthropic.claude-fable-5:1m": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 1_000_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsReasoning: true,
|
||||
supportsGlobalEndpoint: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
tiers: CLAUDE_FABLE_1M_TIERS,
|
||||
},
|
||||
"anthropic.claude-opus-4-7": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
@@ -922,6 +997,7 @@ export const openRouterClaudeSonnet461mModelId = `anthropic/claude-sonnet-4.6${C
|
||||
export const openRouterClaudeOpus461mModelId = `anthropic/claude-opus-4.6${CLAUDE_SONNET_1M_SUFFIX}`
|
||||
export const openRouterClaudeOpus471mModelId = `anthropic/claude-opus-4.7${CLAUDE_SONNET_1M_SUFFIX}`
|
||||
export const openRouterClaudeOpus481mModelId = `anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`
|
||||
export const openRouterClaudeFable51mModelId = `anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`
|
||||
export const openRouterDefaultModelInfo: ModelInfo = {
|
||||
maxTokens: 64_000,
|
||||
contextWindow: 200_000,
|
||||
@@ -1207,6 +1283,31 @@ export const vertexModels = {
|
||||
supportsReasoning: true,
|
||||
tiers: CLAUDE_OPUS_1M_TIERS,
|
||||
},
|
||||
"claude-fable-5": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsGlobalEndpoint: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
supportsReasoning: true,
|
||||
},
|
||||
"claude-fable-5:1m": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 1_000_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsGlobalEndpoint: true,
|
||||
inputPrice: 10,
|
||||
outputPrice: 50,
|
||||
cacheWritesPrice: 12.5,
|
||||
cacheReadsPrice: 1,
|
||||
supportsReasoning: true,
|
||||
tiers: CLAUDE_FABLE_1M_TIERS,
|
||||
},
|
||||
"claude-opus-4-7": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 200_000,
|
||||
|
||||
@@ -12,7 +12,10 @@ export function isClaudeOpusAdaptiveThinkingModel(modelId?: string): boolean {
|
||||
|
||||
const id = modelId.toLowerCase()
|
||||
const adaptiveVersions = ["4-6", "4.6", "4-7", "4.7", "4-8", "4.8"]
|
||||
return adaptiveVersions.some((version) => id.includes(`claude-opus-${version}`) || id.includes(`claude-${version}-opus`))
|
||||
return (
|
||||
id.includes("claude-fable-5") ||
|
||||
adaptiveVersions.some((version) => id.includes(`claude-opus-${version}`) || id.includes(`claude-${version}-opus`))
|
||||
)
|
||||
}
|
||||
|
||||
export function resolveClaudeOpusAdaptiveThinking(
|
||||
|
||||
@@ -512,6 +512,14 @@ const ClineModelPicker: React.FC<ClineModelPickerProps> = ({ isPopup, currentMod
|
||||
)}
|
||||
</DropdownWrapper>
|
||||
|
||||
{/* Context window switcher for Claude Fable 5 */}
|
||||
<ContextWindowSwitcher
|
||||
base1mModelId={`anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`}
|
||||
base200kModelId="anthropic/claude-fable-5"
|
||||
onModelChange={handleModelChange}
|
||||
selectedModelId={selectedModelId}
|
||||
/>
|
||||
|
||||
{/* Context window switcher for Claude Opus 4.8 */}
|
||||
<ContextWindowSwitcher
|
||||
base1mModelId={`anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`}
|
||||
|
||||
@@ -323,6 +323,14 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ isPopup,
|
||||
)}
|
||||
</DropdownWrapper>
|
||||
|
||||
{/* Context window switcher for Claude Fable 5 */}
|
||||
<ContextWindowSwitcher
|
||||
base1mModelId={`anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`}
|
||||
base200kModelId="anthropic/claude-fable-5"
|
||||
onModelChange={handleModelChange}
|
||||
selectedModelId={selectedModelId}
|
||||
/>
|
||||
|
||||
{/* Context window switcher for Claude Opus 4.8 */}
|
||||
<ContextWindowSwitcher
|
||||
base1mModelId={`anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`}
|
||||
|
||||
@@ -13,6 +13,7 @@ const SUPPORTED_CLAUDE_CODE_THINKING_MODELS = [
|
||||
...SUPPORTED_ANTHROPIC_THINKING_MODELS,
|
||||
"sonnet",
|
||||
"sonnet[1m]",
|
||||
"claude-fable-5[1m]",
|
||||
"claude-opus-4-8[1m]",
|
||||
"claude-opus-4-7[1m]",
|
||||
"claude-sonnet-4-6[1m]",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"apps/cli": {
|
||||
"name": "@cline/cli",
|
||||
"version": "3.0.17",
|
||||
"version": "3.0.21",
|
||||
"bin": {
|
||||
"cline": "src/index.ts",
|
||||
},
|
||||
@@ -371,7 +371,7 @@
|
||||
},
|
||||
"sdk/packages/agents": {
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"dependencies": {
|
||||
"@cline/llms": "workspace:*",
|
||||
"@cline/shared": "workspace:*",
|
||||
@@ -380,7 +380,7 @@
|
||||
},
|
||||
"sdk/packages/core": {
|
||||
"name": "@cline/core",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"dependencies": {
|
||||
"@cline/agents": "workspace:*",
|
||||
"@cline/llms": "workspace:*",
|
||||
@@ -411,7 +411,7 @@
|
||||
},
|
||||
"sdk/packages/llms": {
|
||||
"name": "@cline/llms",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^4.0.89",
|
||||
"@ai-sdk/anthropic": "^3.0.68",
|
||||
@@ -445,14 +445,14 @@
|
||||
},
|
||||
"sdk/packages/sdk": {
|
||||
"name": "@cline/sdk",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"dependencies": {
|
||||
"@cline/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"sdk/packages/shared": {
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"dependencies": {
|
||||
"aws4fetch": "^1.0.20",
|
||||
"jsonrepair": "^3.13.2",
|
||||
@@ -464,11 +464,11 @@
|
||||
"packages": {
|
||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.16.1", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-1ad+Sc/0sCtZGHthxxvgEUo5Wsbw16I+aF+YwdiLnPwkZG8KAGUEAPK6LM6Pf69lCyJPt1Aomk1d+8oE3C4ZEw=="],
|
||||
|
||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.111", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cH71k96tcnLuq1x3xi0KP384Jxio8qM6VQzHDUfU4OuX2P83FC/pBvksR5YVRm17GdQliAfR1t5o6z1iJRtfpA=="],
|
||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.112", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/openai": "3.0.67", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-PsSh7a6qW+3kQXPs1kD4wDwuZby0t1PIaB6j/1aMKmPFJ5LxcIcULLMF/bjITLt5o/8lc0t6TXIwG0zlhH7uZw=="],
|
||||
|
||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.81", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-B1JDd9Ugq9R5AgIaW3674lhGCMMYJcPUxnrZh8fzbGojgg4QvHFRv6eZahGQAUsmGHbcf74G9bdSBDLWQGY2GA=="],
|
||||
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.121", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-uY248djJRxa5W68MHiyqO8WLdOeKQoRClGg7PVX/VPhVW8SJNM7/l5DcrA5WAM3YfQrLyNkgZa2VOu8T0t8LUw=="],
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.122", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-U1k2fk7cSH/tS5CZ3ujROiUCOLFwkzb792OqR/Org8Mfm27dKSIdRZG4ZuJUifT8alUWa61IoaRu4foXKlP5TQ=="],
|
||||
|
||||
"@ai-sdk/google": ["@ai-sdk/google@3.0.80", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5ORbm/yFUPO0MEvZsxBMN0cdKw2+lwU/wVn5KN3KF8Dmk1LughuDuUohMh/7iU/XFTiyB0OvmTW/tdV/J7O9zg=="],
|
||||
|
||||
@@ -476,7 +476,7 @@
|
||||
|
||||
"@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.37", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KkdaMjs4C2y+vrZWJE990E3ZxBFiOTHQ94ZlquuIttpphcqJTMxNoIpnKT/4UzMVWXL0BUEE2vs+1UEVXkN8Kg=="],
|
||||
|
||||
"@ai-sdk/openai": ["@ai-sdk/openai@3.0.66", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-n9mZ7PbU7O2zN8UMcx495Gfx7sE/rL4KS+o5JzBOUbYJCuwEIxKO6yJaUkxa4r6IyiLxyGib0jegZw91Hh0diA=="],
|
||||
"@ai-sdk/openai": ["@ai-sdk/openai@3.0.67", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-oAiGC9eWG7IgtdsdS74bOCnAAHarAfTJhWN9x5INwnWPekL802AvF+0I5DvLzIF1MIRmNw4N8mPSL/GUVbX9Mw=="],
|
||||
|
||||
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.48", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z9MC6M4Oh/yUY/F/eszOtO8wc2nMz99XmZQKd2gWTtyIfe716xTfrKe3aYZKg20NZDtyjqPPKPSR+wqz7q1T7Q=="],
|
||||
|
||||
@@ -484,7 +484,7 @@
|
||||
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.194", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.192", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-ctoMYL8Yc5iWzwUp5LLCGqMPOELpHAqygyzNpTvaXaLXPhtSGzNEXVSVvy4NyxwwLH/nF42b92tFcnrWlRRVYA=="],
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.196", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.194", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-e/XI6e5cY/FYAvd7ThQzA/zadmVLukwUPvLS9+i1ZoSjwcgTB5LyUnvljMY/+8w++aCp0EKEgrJ+jiL81UHpXQ=="],
|
||||
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
@@ -520,7 +520,7 @@
|
||||
|
||||
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
|
||||
|
||||
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1056.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-node": "^3.972.46", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Fywg6+B39uGiYZRYFEsOXbIeHQ8wvtMqlt6FUwWev8N2H+V0pVdgCKn32pSOzud1i17wnm5gpB2VXZEoyVHc2A=="],
|
||||
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1058.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-node": "^3.972.48", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-5D3cnn3h72xc7oXLfqMIP81Z7H2Y+FXUq+YGSHQRfGTEvbH+m+5IuA1SwmEKIktobXwjpSuQW34eUKSnTgK1ng=="],
|
||||
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.974.15", "", { "dependencies": { "@aws-sdk/types": "^3.973.9", "@aws-sdk/xml-builder": "^3.972.26", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.5", "@smithy/signature-v4": "^5.4.5", "@smithy/types": "^4.14.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw=="],
|
||||
|
||||
@@ -530,11 +530,11 @@
|
||||
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.43", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-TT76RN1NkI9WoyZqCNxOw6/WBMF7pYOTJcXbMokNFU+euSG40Kaf/t/FhDACVZWP+43wEM6ZynIPIkzS1wR1iA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-sJe5ZWibO4s7RWjFQ8Zol76KxoJcIYyEZH1/wxQSBMSIAAxzaJ8cS/ITAaIHWUQvDKQdt18+cJAHKWB7n1Jmrg=="],
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.6", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-hvcgcwOiS0nb2XFb5Op1Pz/vYaWz5K8kKullziGpdNRuG0NwzRXseuPt2CoBqknHGaSPVesu1aOn2OcctEYdCA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-MZQv4SNjByk1iOKmrqmzcUF/uCB05wjvEHyXKxmGQTUANTIVayX6HPUF0bzkWLvtnkH7sAn9kUCfkXbSpj9sDA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.46", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.45", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-cS4w0jzDRb1jOlkiJS3y80OxddHzkky/MN9k3NYs5jganNKVLjF0lpvjlwS118oGMr3cdAfOlVdo8gLurTSE7w=="],
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.48", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.46", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.6", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-QIbtJP0olSLZ2ImEu636pP+7JJbPfaL3xSJIFXhu472CWuondCc4bGOa8OeyhOFet8z4H1D/ZFKXc39FboWwYA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-7I/n1zkysouLOWvkEhjNEP4vMnD2v4kzzr3/3QBdrripEpn7ap1/I5DF3Hou1SUqkKWo1f3oPGMyFAA1FAMvsQ=="],
|
||||
|
||||
@@ -542,7 +542,7 @@
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ=="],
|
||||
|
||||
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1056.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1056.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-cognito-identity": "^3.972.38", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.45", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-node": "^3.972.46", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Qp7ndCG+dZldiaURze6BM/dLkHQJxwi6WNRR1sR9lhX9jS9QG5ZIOiY3jm6T668vgGqHuNQS7r/P9pimxnHyyg=="],
|
||||
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1058.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1058.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-cognito-identity": "^3.972.38", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.46", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-node": "^3.972.48", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.6", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/5Yuf7w8GRz2lic2cg0gRvZzJA2OgiX8HYoMNWLbs3b1vJelz222w5VlKsxPP9D376Zv38E+VHJenvwFgKjK+Q=="],
|
||||
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.13", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/signature-v4-multi-region": "^3.996.30", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg=="],
|
||||
|
||||
@@ -640,25 +640,25 @@
|
||||
|
||||
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
|
||||
|
||||
"@chat-adapter/discord": ["@chat-adapter/discord@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0", "discord-api-types": "^0.37.119", "discord-interactions": "^4.4.0", "discord.js": "^14.25.1" } }, "sha512-C6pDVKn7s0PhvgeqldYxoShWXJzthwtlo+sCesqenh56M03eHsioinqxeJttmtK8y1ZD8/qiP2lAZufDwuEQhA=="],
|
||||
"@chat-adapter/discord": ["@chat-adapter/discord@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0", "discord-api-types": "^0.37.119", "discord-interactions": "^4.4.0", "discord.js": "^14.25.1" } }, "sha512-hWMwCwVgMeQWB5F2JL91GLTWcF8rlE4eewzXzfPSoAsM7Y71yBDCfQ9QfL5VODwPKtYnVPU5Go2pGsuauaZ/yw=="],
|
||||
|
||||
"@chat-adapter/gchat": ["@chat-adapter/gchat@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@googleapis/chat": "^44.6.0", "@googleapis/workspaceevents": "^9.1.0", "chat": "4.29.0" } }, "sha512-SZ5ZgzFmZ4oRj0AAISbjPztPiICTpw+RdPiPywNwqGWt2LItUOL+8+dl8zZMk+F86E/r0sqRjdbc8+9GqUe2pA=="],
|
||||
"@chat-adapter/gchat": ["@chat-adapter/gchat@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@googleapis/chat": "^44.6.0", "@googleapis/workspaceevents": "^9.1.0", "chat": "4.30.0" } }, "sha512-s3rqHJqW17RE1drBhS9H+YYDpiasfd4YBaXdLrNqao5AS5sNWR/ekWiDGPeF/zlzDkOUNdnyDq3NbA7/2HsHsQ=="],
|
||||
|
||||
"@chat-adapter/linear": ["@chat-adapter/linear@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@linear/sdk": "^76.0.0", "chat": "4.29.0" } }, "sha512-4cq7pBv0CICka8V0uuae7bNfb1rTdmYjsNjOs7Gkr4vChlhDxRUEGUoiBqvOEYKqxFtFOkRk9Wod0tIQmbPySw=="],
|
||||
"@chat-adapter/linear": ["@chat-adapter/linear@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@linear/sdk": "^76.0.0", "chat": "4.30.0" } }, "sha512-L0/B71Sdx8XuMbhaw0YShzR18DwhBS+Yfna61SnTpyoS+BzIRKxP1xEQU6LYGCiOiS+587GZOUm2vSWPakMAWw=="],
|
||||
|
||||
"@chat-adapter/shared": ["@chat-adapter/shared@4.29.0", "", { "dependencies": { "chat": "4.29.0" } }, "sha512-ARqTDoHJHKN9rpytbFPJbNmqqx3fOg5xwsTZdlingQPAssOSeHDBdqrFJkgDhyCRGbmDtG09cuS0FkVzeoh2qg=="],
|
||||
"@chat-adapter/shared": ["@chat-adapter/shared@4.30.0", "", { "dependencies": { "chat": "4.30.0" } }, "sha512-IuYtbn/p1FBXvp7JYGEMLCt07GHOMlyjx7OlZXPJwLTravcyJuP7Q6N31r6c1yubMhM8PLb8eT8l/YnjwYjs9Q=="],
|
||||
|
||||
"@chat-adapter/slack": ["@chat-adapter/slack@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@slack/socket-mode": "^2.0.5", "@slack/web-api": "^7.14.0", "chat": "4.29.0" } }, "sha512-s2DXAwkTpmiIKSATXgrO879s1pqFwS70Y0JPd+TRGRzDeh6nfqt5dnKt5Bug0P1zwkB6DoPurhnYS9nqhSmD/w=="],
|
||||
"@chat-adapter/slack": ["@chat-adapter/slack@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@slack/socket-mode": "^2.0.5", "@slack/web-api": "^7.14.0", "chat": "4.30.0" } }, "sha512-ZB+G/JBKmaXzvl+DuUQPBb/gCwXP3fOtUK5Cyj6wLbbeeDYzi3NQPvpvieVum/GI4iuR8OUVcNAnVQiH6P6DOQ=="],
|
||||
|
||||
"@chat-adapter/telegram": ["@chat-adapter/telegram@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0" } }, "sha512-015tU3HEjFQWw7DgebXWkDeQA6lTdTVEO4btAV3f6U1kEnOfjLVJq98latcsM1WkEvv+3LIFKpsz9pG53aamBw=="],
|
||||
"@chat-adapter/telegram": ["@chat-adapter/telegram@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0" } }, "sha512-OX98fYMorz3gRvNoCVidYKOnb89Rf9UZQaUDQMhtk5IsgHX0k2qiUpEb2VOquMHyH8Pb7xkqrG52NFLnzGpspQ=="],
|
||||
|
||||
"@chat-adapter/whatsapp": ["@chat-adapter/whatsapp@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0" } }, "sha512-CDKmlDHmiiJ2xtca0wZ2DIYnySUFuiKn6f3ZQvschFVa6pSaDmaldmJc3iul+ELi10+RE7x5+immfc//+TTzFg=="],
|
||||
"@chat-adapter/whatsapp": ["@chat-adapter/whatsapp@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0" } }, "sha512-4iXroN/FYRsWdhgVFUNzf4VXX67a9u6xcYoBGgX2eqTwlq5THywYSe7FSA5GP7Hg0SK0mRExdpR/Hvk9+tP9xw=="],
|
||||
|
||||
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
|
||||
|
||||
"@clack/core": ["@clack/core@1.3.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA=="],
|
||||
"@clack/core": ["@clack/core@1.4.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-7Wctjq6f7c1CPz8sPpkwUnz8yRgVANkpNupb81q432FjcJg4l+Sw7XANdNSdWfAKq0IHI0JTcUeK5dxs/HrGPw=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@1.4.0", "", { "dependencies": { "@clack/core": "1.3.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA=="],
|
||||
"@clack/prompts": ["@clack/prompts@1.5.0", "", { "dependencies": { "@clack/core": "1.4.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-wKh+wTjmrUoUdkZg8KpJO5X+p9PWV+KE9mePseq9UYWkukgTKsGS47RRL2HstwVcvDQH+PenrPJWII8+MfiiyA=="],
|
||||
|
||||
"@cline/agents": ["@cline/agents@workspace:sdk/packages/agents"],
|
||||
|
||||
@@ -712,7 +712,7 @@
|
||||
|
||||
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||
|
||||
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.69.1", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.4", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-kwQB5KcAegxw/+NGUgXAo5ovyOSjlMhoXSSnSEpDhoHJwzMcMO0HE1U0VCYZ7jbAeCMGamed9XdWzOA5ixtTNg=="],
|
||||
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.71.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.4", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-KEUw/mGu+EDRhYWRTNGHIimVCs9NvMFaIXOGrHSXoCteKLE5EsJnmPjOPpYorjXVg/0xG0fbdVw720azw1z4ag=="],
|
||||
|
||||
"@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="],
|
||||
|
||||
@@ -880,15 +880,15 @@
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||
|
||||
"@inquirer/ansi": ["@inquirer/ansi@2.0.6", "", {}, "sha512-I/INw4sHGlVZ/afZOckpLiDP9SmbMl1g/GCqeHjLw1Afw/0PlRs2tRFgTGWmdI0hoNuWZn3y2iHNmG1vyECyQQ=="],
|
||||
"@inquirer/ansi": ["@inquirer/ansi@2.0.7", "", {}, "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q=="],
|
||||
|
||||
"@inquirer/confirm": ["@inquirer/confirm@6.1.0", "", { "dependencies": { "@inquirer/core": "^11.2.0", "@inquirer/type": "^4.0.6" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-USpeB76eqK7yGricDlGAupxWlp4a59qpeZOoNWaxO/nJln7agpJveyNkQ1d5u8YXG6TOqxZtQpKPORQQDrdVsA=="],
|
||||
"@inquirer/confirm": ["@inquirer/confirm@6.1.1", "", { "dependencies": { "@inquirer/core": "^11.2.1", "@inquirer/type": "^4.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ=="],
|
||||
|
||||
"@inquirer/core": ["@inquirer/core@11.2.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.6", "@inquirer/figures": "^2.0.6", "@inquirer/type": "^4.0.6", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^4.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-joR1YS2sI0us+9d0I8ViqFbrRLONO8CFTuyvBX4ZVBSch+VsZiugUABdrhBXXJR1VyEzvpz5SQCix3keETQ58g=="],
|
||||
"@inquirer/core": ["@inquirer/core@11.2.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.7", "@inquirer/figures": "^2.0.7", "@inquirer/type": "^4.0.7", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA=="],
|
||||
|
||||
"@inquirer/figures": ["@inquirer/figures@2.0.6", "", {}, "sha512-dsZgQtH2t5Q6ah3aPbZbeEZAxsD9qQu0DXf01AltuEfRTm+NoLN6+rLVbr+4edeEbNCp/wBNM6mALRWtsQpfkw=="],
|
||||
"@inquirer/figures": ["@inquirer/figures@2.0.7", "", {}, "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw=="],
|
||||
|
||||
"@inquirer/type": ["@inquirer/type@4.0.6", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-J+9tdxOskuYuGjsvGaq00AamhDgjR7anhEW2dP4QdQpFCMPngCeC/bCYWQ5NsMWZRdsy53is7kAHb/+7cwDk2g=="],
|
||||
"@inquirer/type": ["@inquirer/type@4.0.7", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g=="],
|
||||
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
@@ -1038,7 +1038,7 @@
|
||||
|
||||
"@openai/codex-win32-x64": ["@openai/codex@0.130.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-FzMznm7fr5/nbjZgOujZ9Y9AbdGm7ji1FOoWiY3U+srqauvZaTgn6o6aCheSL7kuymu7nTLOO/cAyWV6NuesqQ=="],
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.15.12", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-lOaBNX93dkakZe6C42ttX1bkSx3K2c6+Yv+w8Qv02v5rPlu1vCXbmdfYDh9/bw+oq+NKPSaBm9d6kPA19hA5Lg=="],
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.15.13", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-4TwojIoQ8EG6/mVBuUVYZXiFcwNmiiytEnjnvyuvSJjGwFIlw2YIBFxtSVC3FbwwbwHT63teh1RHiQUUC4U5xw=="],
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
|
||||
|
||||
@@ -1088,7 +1088,7 @@
|
||||
|
||||
"@opentui/react": ["@opentui/react@0.1.102", "", { "dependencies": { "@opentui/core": "0.1.102", "react-reconciler": "^0.32.0" }, "peerDependencies": { "react": ">=19.0.0", "react-devtools-core": "^7.0.1", "ws": "^8.18.0" } }, "sha512-c7EiK30xlvaHc6WBOLAH7TJ8xC4QbiD5ZA6tb4zZ74XM4SH5Tb+uBee8b4TztTLseq+hGtW85qrIUJM3OrdtNw=="],
|
||||
|
||||
"@oxc-project/types": ["@oxc-project/types@0.132.0", "", {}, "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ=="],
|
||||
"@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="],
|
||||
|
||||
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
||||
|
||||
@@ -1238,35 +1238,35 @@
|
||||
|
||||
"@rive-app/webgl2": ["@rive-app/webgl2@2.37.8", "", {}, "sha512-Y2nXPwAeQtZrADNIzY7v3Lk7XZRMy4Gd+bpGFyzkaQ/EQ91Hp/6sCdd8yTCyjH4b71N/T6kU0MHIYA0IDmpjyQ=="],
|
||||
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.2", "", { "os": "android", "cpu": "arm64" }, "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ=="],
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.3", "", { "os": "android", "cpu": "arm64" }, "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw=="],
|
||||
|
||||
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w=="],
|
||||
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA=="],
|
||||
|
||||
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA=="],
|
||||
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg=="],
|
||||
|
||||
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA=="],
|
||||
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g=="],
|
||||
|
||||
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w=="],
|
||||
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw=="],
|
||||
|
||||
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig=="],
|
||||
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw=="],
|
||||
|
||||
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw=="],
|
||||
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q=="],
|
||||
|
||||
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA=="],
|
||||
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg=="],
|
||||
|
||||
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ=="],
|
||||
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg=="],
|
||||
|
||||
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ=="],
|
||||
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg=="],
|
||||
|
||||
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw=="],
|
||||
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow=="],
|
||||
|
||||
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w=="],
|
||||
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg=="],
|
||||
|
||||
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.2", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ=="],
|
||||
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.3", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg=="],
|
||||
|
||||
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A=="],
|
||||
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g=="],
|
||||
|
||||
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ=="],
|
||||
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
|
||||
|
||||
@@ -1334,25 +1334,25 @@
|
||||
|
||||
"@slack/web-api": ["@slack/web-api@7.16.0", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/types": "^2.21.0", "@types/node": ">=18", "@types/retry": "0.12.0", "axios": "^1.16.0", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-68SAV77uuGKuhyyaRytX8UijVnqSLsTSKslGXw17cjQYXn+jtNl7gbaEjHgC5x2rhCuFdahBrEC2VCLppbzReg=="],
|
||||
|
||||
"@smithy/core": ["@smithy/core@3.24.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA=="],
|
||||
"@smithy/core": ["@smithy/core@3.24.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug=="],
|
||||
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-yiF8xHpdkaTfzLVqFzsP6WvNghEK+qZzLYWFD13L2SsFhbXwBGlxdocKF95qjr7s5lE5NRage+EJFK4mAsx88Q=="],
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.7", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xj8gq/bjFABAh6qWPSDCYcY3kzQIm4b561C+YnHH4zGq8rOgzQ3Shk+JGlpUxSd41UGiO6FkLdUCtNX1FAeHgg=="],
|
||||
|
||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.3.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "tslib": "^2.6.2" } }, "sha512-sM98Snchk/k9dFKwf3F2pyDUaiKilgOU/I+EAQin8Y1XXYusIP5EVRMpjKFVeIHXDnwijxg+6RmIM6HCDvYQoA=="],
|
||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "tslib": "^2.6.2" } }, "sha512-Ussyv240JxwQP8AmkYdm26wGP/1I8QmIv0ZosgDJDlSzD73FEdj1BOpXMc06VrxX5KxTKhadFNomT2SWutUnpg=="],
|
||||
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-SK3VMeH0fibgdTg2QeB+O4p7Yy/2E5HBOHJeC58FshkDdeuX8lOgO7PfjYfLyPLP1ch55j91cQqKBzDS0mRjSQ=="],
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-FEwEYJ1jlBKdhe9TPzfghEi1bP55ZeEImlDkEa62bBBYzUcnB6RUCyuiS2mqKt6ZVjUbBgcNhzfIctH+Hevx9g=="],
|
||||
|
||||
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw=="],
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-3fya8i7GrJilQouk4cZJKdy5k8MWQBpjfXrRNaXDedH8r779tr0jcxyH3+yoTmsluc2+vF4S343yFbnvu8ExDQ=="],
|
||||
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.4.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA=="],
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.4.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ=="],
|
||||
|
||||
"@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
||||
"@smithy/types": ["@smithy/types@4.14.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ=="],
|
||||
|
||||
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.3.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "tslib": "^2.6.2" } }, "sha512-l1d7I7YP2LjXjAZDC7eXqkzuEB75KfCANwhNj/knmT6+0a9XG3QasvI8kEn8WAI3tx/q8PdmSuuXcM+MTkk/7Q=="],
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "tslib": "^2.6.2" } }, "sha512-tAa4sePYB7mlJzdYbdBqdv37KwFKWixmM/r3ihcI0HFOVjf+a5oGvtcLXcGm4S1bY4DFsLAIOHgjubtp+oRufw=="],
|
||||
|
||||
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
|
||||
|
||||
@@ -1596,25 +1596,25 @@
|
||||
|
||||
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.60.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/type-utils": "8.60.0", "@typescript-eslint/utils": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.60.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw=="],
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.60.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/type-utils": "8.60.1", "@typescript-eslint/utils": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.60.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.60.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg=="],
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.60.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.60.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.60.0", "@typescript-eslint/types": "^8.60.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg=="],
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.60.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.60.1", "@typescript-eslint/types": "^8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0" } }, "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw=="],
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1" } }, "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.60.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.60.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/utils": "8.60.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg=="],
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.60.0", "", {}, "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.60.1", "", {}, "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.60.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.60.0", "@typescript-eslint/tsconfig-utils": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.60.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.60.1", "@typescript-eslint/tsconfig-utils": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.60.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA=="],
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.60.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg=="],
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
|
||||
|
||||
@@ -1626,19 +1626,19 @@
|
||||
|
||||
"@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@4.3.1", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0", "@swc/core": "^1.15.11" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7 || ^8" } }, "sha512-PaeokKjAGraNN+s5SIApgsktnJprIyt3zgEIu7awnEdfn29QiB2crTcCzyi2XGpX9rUnTc0cKU07Wm0N0g7H2w=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@4.1.7", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w=="],
|
||||
"@vitest/expect": ["@vitest/expect@4.1.8", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@4.1.7", "", { "dependencies": { "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA=="],
|
||||
"@vitest/mocker": ["@vitest/mocker@4.1.8", "", { "dependencies": { "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw=="],
|
||||
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.7", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw=="],
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="],
|
||||
|
||||
"@vitest/runner": ["@vitest/runner@4.1.7", "", { "dependencies": { "@vitest/utils": "4.1.7", "pathe": "^2.0.3" } }, "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw=="],
|
||||
"@vitest/runner": ["@vitest/runner@4.1.8", "", { "dependencies": { "@vitest/utils": "4.1.8", "pathe": "^2.0.3" } }, "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg=="],
|
||||
|
||||
"@vitest/snapshot": ["@vitest/snapshot@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "@vitest/utils": "4.1.7", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw=="],
|
||||
"@vitest/snapshot": ["@vitest/snapshot@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ=="],
|
||||
|
||||
"@vitest/spy": ["@vitest/spy@4.1.7", "", {}, "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q=="],
|
||||
"@vitest/spy": ["@vitest/spy@4.1.8", "", {}, "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA=="],
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw=="],
|
||||
"@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="],
|
||||
|
||||
"@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
|
||||
|
||||
@@ -1648,9 +1648,9 @@
|
||||
|
||||
"@xterm/headless": ["@xterm/headless@5.5.0", "", {}, "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g=="],
|
||||
|
||||
"@xyflow/react": ["@xyflow/react@12.10.2", "", { "dependencies": { "@xyflow/system": "0.0.76", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ=="],
|
||||
"@xyflow/react": ["@xyflow/react@12.11.0", "", { "dependencies": { "@xyflow/system": "0.0.77", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "@types/react": ">=17", "@types/react-dom": ">=17", "react": ">=17", "react-dom": ">=17" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA=="],
|
||||
|
||||
"@xyflow/system": ["@xyflow/system@0.0.76", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA=="],
|
||||
"@xyflow/system": ["@xyflow/system@0.0.77", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
|
||||
@@ -1662,7 +1662,7 @@
|
||||
|
||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"ai": ["ai@6.0.192", "", { "dependencies": { "@ai-sdk/gateway": "3.0.121", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-13j2MqJzFS5NqILJO6Gdc7ShXk39KERFOgUpnpMYsASXE9l97vvcfUG/RVkEuR0J9ILxMS67kGy+msGtfeJoOQ=="],
|
||||
"ai": ["ai@6.0.194", "", { "dependencies": { "@ai-sdk/gateway": "3.0.122", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-0MkYqrSZZuC1zTECppcaUT0i54aocXpYaUMVue3V8z/weBHCytfO5/CcwZCU80msZpfkbBUKYSSrkZFotEO5wQ=="],
|
||||
|
||||
"ai-sdk-provider-claude-code": ["ai-sdk-provider-claude-code@3.4.4", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.1", "@anthropic-ai/claude-agent-sdk": "^0.2.63" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-iHcup5SHh4Tul1RIi9J+bnpngen8WX66yC3lsz1YlbtwAmRhUEzZUuGKzmFGIN8Pmx9uQrerGfLJdbFxIxKkyw=="],
|
||||
|
||||
@@ -1722,7 +1722,7 @@
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.33", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw=="],
|
||||
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
@@ -1784,7 +1784,7 @@
|
||||
|
||||
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
||||
|
||||
"chat": ["chat@4.29.0", "", { "dependencies": { "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" }, "peerDependencies": { "ai": "^6.0.182", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai", "zod"] }, "sha512-KdPfzaie5ivYytyRICTERg5xT+LeCbYefokvNAqTHe92eqkFaoTMXXkSitikxJVWhZIb2YoXF1b9UZHyzSzKzw=="],
|
||||
"chat": ["chat@4.30.0", "", { "dependencies": { "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" }, "peerDependencies": { "ai": "^6.0.182", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai", "zod"] }, "sha512-8LXrauKckMmR83FcYC/R8nNEda5VJDDdIhZwUUu+hzaSbk4lqsro0IWm7rB1GGYXONRrUOG2XJlkNr4C15vgMA=="],
|
||||
|
||||
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
|
||||
|
||||
@@ -1856,7 +1856,7 @@
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"cytoscape": ["cytoscape@3.33.4", "", {}, "sha512-HIN5Pmd9MrX9BkV7tDwnOcEJCSFvCpc8X97h3f508J6I5FsqAY65wKOCvgH2CuP42CaahWaz4tuh32SOOIH7ww=="],
|
||||
"cytoscape": ["cytoscape@3.34.0", "", {}, "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg=="],
|
||||
|
||||
"cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
|
||||
|
||||
@@ -1996,7 +1996,7 @@
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.363", "", {}, "sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.366", "", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="],
|
||||
|
||||
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
|
||||
|
||||
@@ -2212,7 +2212,7 @@
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"graphql": ["graphql@16.14.0", "", {}, "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q=="],
|
||||
"graphql": ["graphql@16.14.1", "", {}, "sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg=="],
|
||||
|
||||
"hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
|
||||
|
||||
@@ -2368,7 +2368,7 @@
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
"js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
@@ -2532,7 +2532,7 @@
|
||||
|
||||
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||
|
||||
"media-chrome": ["media-chrome@4.19.0", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-HWhDTwts+BSbdPkkB1VsJXp5kvL0IxY7xFT5tBwliM2+89kTPVTnHnev+9it2f9PweANjT/C8/C/S0PW9oyZbA=="],
|
||||
"media-chrome": ["media-chrome@4.19.1", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-1+x2l0mNulHKZN0lBxGJwJ+TV2W/KzLjaAd//UCGZz8GE5O5YNafFskWTcv/D6Ty0d9drX9SSfimOzGwob8eVQ=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
|
||||
@@ -2636,7 +2636,7 @@
|
||||
|
||||
"msw": ["msw@2.14.6", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.11", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg=="],
|
||||
|
||||
"mute-stream": ["mute-stream@4.0.0", "", {}, "sha512-gSrprq0fJ3EiOErzjdIZrjysVVmJ4uu1QWfCDss5LypA5OXvrMje5Ym5z6V6RLyJ2eF87lasX7t6a0AnFvZblg=="],
|
||||
"mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="],
|
||||
|
||||
"nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="],
|
||||
|
||||
@@ -2664,7 +2664,7 @@
|
||||
|
||||
"node-pty": ["node-pty@1.2.0-beta.11", "", { "dependencies": { "node-addon-api": "^7.1.0" } }, "sha512-THcUyu1WwdgoIyUvgXOZ70EOMXzheGa0q3tbEb5kUIfKgcpBJ+AJ9Q1kq0bKtYmQzr77usXiTORZTLmAUQlnoQ=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.46", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="],
|
||||
"node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="],
|
||||
|
||||
"node-rsa": ["node-rsa@1.1.1", "", { "dependencies": { "asn1": "^0.2.4" } }, "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw=="],
|
||||
|
||||
@@ -2806,9 +2806,9 @@
|
||||
|
||||
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
|
||||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
"property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="],
|
||||
|
||||
"protobufjs": ["protobufjs@7.6.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg=="],
|
||||
"protobufjs": ["protobufjs@7.6.2", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
@@ -2836,7 +2836,7 @@
|
||||
|
||||
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
||||
|
||||
"react-hook-form": ["react-hook-form@7.76.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ=="],
|
||||
"react-hook-form": ["react-hook-form@7.77.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-Sslh9YDYc0GDlWT/lxasnIduNo4v3yyvqRGvmGKUre5AFjDs/HV9/OafHGD8d+sB2yoL4UIL9L8X9i0WlZZebg=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
@@ -2920,7 +2920,7 @@
|
||||
|
||||
"robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
|
||||
|
||||
"rolldown": ["rolldown@1.0.2", "", { "dependencies": { "@oxc-project/types": "=0.132.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.2", "@rolldown/binding-darwin-arm64": "1.0.2", "@rolldown/binding-darwin-x64": "1.0.2", "@rolldown/binding-freebsd-x64": "1.0.2", "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", "@rolldown/binding-linux-arm64-gnu": "1.0.2", "@rolldown/binding-linux-arm64-musl": "1.0.2", "@rolldown/binding-linux-ppc64-gnu": "1.0.2", "@rolldown/binding-linux-s390x-gnu": "1.0.2", "@rolldown/binding-linux-x64-gnu": "1.0.2", "@rolldown/binding-linux-x64-musl": "1.0.2", "@rolldown/binding-openharmony-arm64": "1.0.2", "@rolldown/binding-wasm32-wasi": "1.0.2", "@rolldown/binding-win32-arm64-msvc": "1.0.2", "@rolldown/binding-win32-x64-msvc": "1.0.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g=="],
|
||||
"rolldown": ["rolldown@1.0.3", "", { "dependencies": { "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.3", "@rolldown/binding-darwin-arm64": "1.0.3", "@rolldown/binding-darwin-x64": "1.0.3", "@rolldown/binding-freebsd-x64": "1.0.3", "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", "@rolldown/binding-linux-arm64-gnu": "1.0.3", "@rolldown/binding-linux-arm64-musl": "1.0.3", "@rolldown/binding-linux-ppc64-gnu": "1.0.3", "@rolldown/binding-linux-s390x-gnu": "1.0.3", "@rolldown/binding-linux-x64-gnu": "1.0.3", "@rolldown/binding-linux-x64-musl": "1.0.3", "@rolldown/binding-openharmony-arm64": "1.0.3", "@rolldown/binding-wasm32-wasi": "1.0.3", "@rolldown/binding-win32-arm64-msvc": "1.0.3", "@rolldown/binding-win32-x64-msvc": "1.0.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g=="],
|
||||
|
||||
"roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
|
||||
|
||||
@@ -2952,7 +2952,7 @@
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shadcn": ["shadcn@4.8.2", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-pt3KneOg6LGYKNAdoTVf/lpVcf7t2MlV+Ll2Xc3lIIYN3ph4ajrjU+CcG6OVSgO5ubbLZj+9j5oMA9Lqg7o8KA=="],
|
||||
"shadcn": ["shadcn@4.10.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-84IJhUsK0xqSCRJx3QxyZe2NpUXj2Nwk8Vc8Ow/tCOND3yz4CT6uU4655vqicNXhzG9Q1cyUt+TBl2SiCJwNgg=="],
|
||||
|
||||
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
|
||||
|
||||
@@ -3076,15 +3076,15 @@
|
||||
|
||||
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="],
|
||||
"tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
"tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||
|
||||
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
|
||||
|
||||
"tldts": ["tldts@7.4.0", "", { "dependencies": { "tldts-core": "^7.4.0" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg=="],
|
||||
"tldts": ["tldts@7.4.2", "", { "dependencies": { "tldts-core": "^7.4.2" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw=="],
|
||||
|
||||
"tldts-core": ["tldts-core@7.4.1", "", {}, "sha512-sc2nGvGbixlJRHwTh/qQdPXTxJU1UDJboGPQm4d/01YUJ9r/u6aeIulQvEaxUlvKDN7hb1qCLjax+jhVAPLa/g=="],
|
||||
"tldts-core": ["tldts-core@7.4.2", "", {}, "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
@@ -3120,13 +3120,13 @@
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
|
||||
"type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="],
|
||||
|
||||
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.60.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.0", "@typescript-eslint/parser": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/utils": "8.60.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw=="],
|
||||
"typescript-eslint": ["typescript-eslint@8.60.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.1", "@typescript-eslint/parser": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA=="],
|
||||
|
||||
"uc.micro": ["uc.micro@1.0.6", "", {}, "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="],
|
||||
|
||||
@@ -3194,9 +3194,9 @@
|
||||
|
||||
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
|
||||
|
||||
"vite": ["vite@8.0.14", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.2", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw=="],
|
||||
"vite": ["vite@8.0.16", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="],
|
||||
|
||||
"vitest": ["vitest@4.1.7", "", { "dependencies": { "@vitest/expect": "4.1.7", "@vitest/mocker": "4.1.7", "@vitest/pretty-format": "4.1.7", "@vitest/runner": "4.1.7", "@vitest/snapshot": "4.1.7", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.7", "@vitest/browser-preview": "4.1.7", "@vitest/browser-webdriverio": "4.1.7", "@vitest/coverage-istanbul": "4.1.7", "@vitest/coverage-v8": "4.1.7", "@vitest/ui": "4.1.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA=="],
|
||||
"vitest": ["vitest@4.1.8", "", { "dependencies": { "@vitest/expect": "4.1.8", "@vitest/mocker": "4.1.8", "@vitest/pretty-format": "4.1.8", "@vitest/runner": "4.1.8", "@vitest/snapshot": "4.1.8", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.8", "@vitest/browser-preview": "4.1.8", "@vitest/browser-webdriverio": "4.1.8", "@vitest/coverage-istanbul": "4.1.8", "@vitest/coverage-v8": "4.1.8", "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig=="],
|
||||
|
||||
"voca": ["voca@1.4.1", "", {}, "sha512-NJC/BzESaHT1p4B5k4JykxedeltmNbau4cummStd4RjFojgq/kLew5TzYge9N2geeWyI2w8T30wUET5v+F7ZHA=="],
|
||||
|
||||
@@ -3630,7 +3630,7 @@
|
||||
|
||||
"react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"react-jsx-parser/@types/react": ["@types/react@18.3.29", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg=="],
|
||||
"react-jsx-parser/@types/react": ["@types/react@18.3.30", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-3ek6mwJL5/VBewBcY4S66cqlCtK3qi4WIq37Z0m/NHw1hjhI7274Mx1qz/+ggSzyBCOEf7eHjBN6INjPAWYfYw=="],
|
||||
|
||||
"react-jsx-parser/@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Cline SDK Changelog
|
||||
|
||||
## 0.0.45
|
||||
|
||||
- Added support for the Claude Fable 5 model
|
||||
- Fixed MiniMax M3 thinking controls so they route correctly across gateways
|
||||
|
||||
## 0.0.44
|
||||
|
||||
- Added support for Vertex AI Application Default Credentials (ADC) with tool use
|
||||
- Added a global auto-update setting for CLI startup updates
|
||||
- Fixed empty message content replay for Bedrock
|
||||
- Cleaned up the OpenAI Codex model list
|
||||
|
||||
## 0.0.43
|
||||
|
||||
- Added the Cline Hub web app for managing and monitoring agent sessions
|
||||
|
||||
+30
-1
@@ -19,13 +19,41 @@ Examples include:
|
||||
- `mac-notify.ts` - macOS Notification Center alerts
|
||||
- `custom-compaction.ts` - Custom context compaction
|
||||
- `automation-events.ts` - Plugin event emission
|
||||
- `background-terminal.ts` - Background shell jobs with logging
|
||||
- `background-terminal.ts` - Background shell jobs with setup and job logging
|
||||
|
||||
```bash
|
||||
cline plugin install https://github.com/cline/cline/blob/main/sdk/examples/plugins/weather-metrics.ts
|
||||
cline -i "What's the weather like in Tokyo and Paris?"
|
||||
```
|
||||
|
||||
Plugin setup receives a host logger through the second `setup` argument. Use
|
||||
`ctx.logger` for setup-time diagnostics and logs emitted during tool calls:
|
||||
|
||||
```ts
|
||||
setup(api, ctx) {
|
||||
ctx.logger?.log("my-plugin setup", {
|
||||
sessionId: ctx.session?.sessionId,
|
||||
workspaceRoot: ctx.workspaceInfo?.rootPath,
|
||||
});
|
||||
|
||||
try {
|
||||
// Register tools or perform plugin setup work.
|
||||
} catch (error) {
|
||||
if (ctx.logger?.error) {
|
||||
ctx.logger.error("my-plugin setup failed", { error });
|
||||
} else {
|
||||
ctx.logger?.log("my-plugin setup failed", { error, severity: "error" });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ctx.logger` is session-scoped. For detached work that can outlive the session,
|
||||
such as background processes, persist status to plugin-owned storage or report
|
||||
completion through the host event channel instead of calling the captured logger
|
||||
from long-lived callbacks.
|
||||
|
||||
### [`./plugins/typescript-lsp/`](./plugins/typescript-lsp)
|
||||
|
||||
TypeScript LSP plugin that gives the agent a `goto_definition` tool powered by the TypeScript Language Service API. Resolves through imports, re-exports, and type aliases -- much more precise than text search.
|
||||
@@ -47,6 +75,7 @@ cline -i "Find where createTool is defined"
|
||||
- Export a reusable plugin module for `.cline/plugins`
|
||||
- Start background subagents from the main session
|
||||
- Load bundled or custom agent presets and skills
|
||||
- Log setup, subagent starts, and follow-ups through `ctx.logger`
|
||||
|
||||
Includes pre-configured agents:
|
||||
- **Anvil** - Build and compile
|
||||
|
||||
@@ -506,7 +506,15 @@ const plugin: AgentPlugin = {
|
||||
name: "portable-subagents",
|
||||
manifest: { capabilities: ["tools"] },
|
||||
|
||||
setup(api) {
|
||||
setup(api, ctx) {
|
||||
const logger = ctx.logger;
|
||||
logger?.log("portable-subagents plugin setup", {
|
||||
sessionId: ctx.session?.sessionId,
|
||||
defaultPreset: DEFAULT_AGENT_PRESET,
|
||||
backendMode: DEFAULT_BACKEND_MODE,
|
||||
workspaceRoot: ctx.workspaceInfo?.rootPath,
|
||||
});
|
||||
|
||||
// -- start_subagent: Start a new subagent session --
|
||||
api.registerTool(
|
||||
toRegisteredTool(
|
||||
@@ -571,6 +579,14 @@ const plugin: AgentPlugin = {
|
||||
status: "running",
|
||||
};
|
||||
subagents.set(sessionId, subagent);
|
||||
logger?.log("Started subagent", {
|
||||
sessionId,
|
||||
toolName: "start_subagent",
|
||||
label: input.label,
|
||||
preset: def?.name ?? input.preset,
|
||||
providerId,
|
||||
modelId,
|
||||
});
|
||||
void runSubagentTurn(
|
||||
subagent,
|
||||
input.task,
|
||||
@@ -655,6 +671,11 @@ const plugin: AgentPlugin = {
|
||||
subagent.error = undefined;
|
||||
subagents.set(subagent.sessionId, subagent);
|
||||
|
||||
logger?.log("Queued subagent follow-up", {
|
||||
sessionId: subagent.sessionId,
|
||||
toolName: "message_subagent",
|
||||
label: subagent.name,
|
||||
});
|
||||
void runSubagentTurn(
|
||||
subagent,
|
||||
input.prompt,
|
||||
|
||||
@@ -266,6 +266,11 @@ const plugin: AgentPlugin = {
|
||||
workspaceContext?.rootPath?.trim() ||
|
||||
sessionDefaultCwd;
|
||||
setupSessionId = ctx.session?.sessionId?.trim() || undefined;
|
||||
ctx.logger?.log("background-terminal plugin setup", {
|
||||
sessionId: setupSessionId,
|
||||
defaultCwd: sessionDefaultCwd,
|
||||
jobsDir: JOBS_DIR,
|
||||
});
|
||||
|
||||
api.registerTool(
|
||||
createTool<unknown, Record<string, unknown>>({
|
||||
@@ -311,6 +316,14 @@ const plugin: AgentPlugin = {
|
||||
notifyParent,
|
||||
resolveToolSessionId(context),
|
||||
);
|
||||
ctx.logger?.log("Started background command", {
|
||||
sessionId: record.sessionId,
|
||||
toolName: "start_background_command",
|
||||
jobId: record.jobId,
|
||||
cwd: record.cwd,
|
||||
shell: record.shell,
|
||||
pid: record.pid,
|
||||
});
|
||||
|
||||
return {
|
||||
jobId: record.jobId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/core",
|
||||
"description": "Cline Core SDK for Node Runtime",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -421,12 +421,14 @@ export {
|
||||
filterDisabledTools,
|
||||
filterExtensionToolRegistrations,
|
||||
GlobalSettingsSchema,
|
||||
isAutoUpdateEnabledGlobally,
|
||||
isPluginDisabledGlobally,
|
||||
isTelemetryOptedOutGlobally,
|
||||
isToolDisabledGlobally,
|
||||
readGlobalSettings,
|
||||
resolveDisabledPluginPaths,
|
||||
resolveDisabledToolNames,
|
||||
setAutoUpdateEnabledGlobally,
|
||||
setDisabledPlugin,
|
||||
setDisabledTools,
|
||||
setTelemetryOptOutGlobally,
|
||||
|
||||
@@ -1,7 +1,69 @@
|
||||
import { EMPTY_CONTENT_TEXT } from "@cline/shared";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { messageToAgentMessages } from "./agent-message-codec";
|
||||
import {
|
||||
agentMessageToMessageWithMetadata,
|
||||
messageToAgentMessages,
|
||||
messagesToAgentMessages,
|
||||
} from "./agent-message-codec";
|
||||
|
||||
describe("agent message codec", () => {
|
||||
it("replaces empty persisted messages with an explicit error text part", () => {
|
||||
expect(
|
||||
messageToAgentMessages({
|
||||
id: "empty",
|
||||
role: "assistant",
|
||||
ts: 1,
|
||||
content: [],
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
id: "empty",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
createdAt: 1,
|
||||
metadata: undefined,
|
||||
modelInfo: undefined,
|
||||
metrics: undefined,
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
messageToAgentMessages({
|
||||
id: "blank",
|
||||
role: "user",
|
||||
ts: 1,
|
||||
content: "",
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
id: "blank",
|
||||
role: "user",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
createdAt: 1,
|
||||
metadata: undefined,
|
||||
modelInfo: undefined,
|
||||
metrics: undefined,
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
messageToAgentMessages({
|
||||
id: "whitespace",
|
||||
role: "assistant",
|
||||
ts: 1,
|
||||
content: " \n\t ",
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
id: "whitespace",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
createdAt: 1,
|
||||
metadata: undefined,
|
||||
modelInfo: undefined,
|
||||
metrics: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves mixed tool result and user text order", () => {
|
||||
const messages = messageToAgentMessages({
|
||||
id: "msg_mixed",
|
||||
@@ -68,4 +130,42 @@ describe("agent message codec", () => {
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("round-trips Gemini tool call thought signatures", () => {
|
||||
const persisted = agentMessageToMessageWithMetadata({
|
||||
id: "msg_tool_call",
|
||||
role: "assistant",
|
||||
createdAt: 1,
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "toolu_4",
|
||||
toolName: "editor",
|
||||
input: { path: "/tmp/out.txt" },
|
||||
metadata: {
|
||||
thoughtSignature: "sig_4",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(persisted.content).toEqual([
|
||||
expect.objectContaining({
|
||||
type: "tool_use",
|
||||
id: "toolu_4",
|
||||
name: "editor",
|
||||
signature: "sig_4",
|
||||
}),
|
||||
]);
|
||||
|
||||
const [restored] = messagesToAgentMessages([persisted]);
|
||||
expect(restored?.content[0]).toMatchObject({
|
||||
type: "tool-call",
|
||||
toolCallId: "toolu_4",
|
||||
toolName: "editor",
|
||||
metadata: {
|
||||
signature: "sig_4",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
ToolResultContent,
|
||||
ToolUseContent,
|
||||
} from "@cline/shared";
|
||||
import { EMPTY_CONTENT_TEXT } from "@cline/shared";
|
||||
|
||||
export function messageToAgentMessages(
|
||||
message: MessageWithMetadata,
|
||||
@@ -48,7 +49,7 @@ export function messageToAgentMessages(
|
||||
out.push({
|
||||
id: baseId,
|
||||
role: message.role,
|
||||
content: [],
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
createdAt: message.ts ?? Date.now(),
|
||||
metadata: message.metadata,
|
||||
modelInfo: message.modelInfo,
|
||||
@@ -133,7 +134,7 @@ export function agentMessagesToMessages(
|
||||
|
||||
function normalizeContentBlocks(content: Message["content"]): ContentBlock[] {
|
||||
if (typeof content === "string") {
|
||||
return content.length > 0
|
||||
return content.trim().length > 0
|
||||
? [{ type: "text", text: content } as TextContent]
|
||||
: [];
|
||||
}
|
||||
@@ -171,7 +172,11 @@ function contentBlockToAgentPart(block: ContentBlock): AgentMessagePart {
|
||||
toolCallId: block.id,
|
||||
toolName: block.name,
|
||||
input: block.input,
|
||||
metadata: block.signature ? { signature: block.signature } : undefined,
|
||||
metadata: block.signature
|
||||
? {
|
||||
signature: block.signature,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
case "tool_result":
|
||||
return toolResultContentToAgentPart(block);
|
||||
@@ -229,15 +234,21 @@ function agentPartToContentBlock(
|
||||
path: part.path,
|
||||
content: part.content,
|
||||
} satisfies FileContent;
|
||||
case "tool-call":
|
||||
case "tool-call": {
|
||||
const metadata = part.metadata as
|
||||
| {
|
||||
signature?: string;
|
||||
thoughtSignature?: string;
|
||||
}
|
||||
| undefined;
|
||||
return {
|
||||
type: "tool_use",
|
||||
id: part.toolCallId,
|
||||
name: part.toolName,
|
||||
input: (part.input as Record<string, unknown>) ?? {},
|
||||
signature: (part.metadata as { signature?: string } | undefined)
|
||||
?.signature,
|
||||
signature: metadata?.thoughtSignature ?? metadata?.signature,
|
||||
} satisfies ToolUseContent;
|
||||
}
|
||||
case "tool-result": {
|
||||
const output = part.output;
|
||||
const content =
|
||||
|
||||
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
GlobalSettingsSchema,
|
||||
readGlobalSettings,
|
||||
setAutoUpdateEnabledGlobally,
|
||||
setDisabledPlugin,
|
||||
setDisabledTools,
|
||||
setTelemetryOptOutGlobally,
|
||||
@@ -26,6 +27,7 @@ describe("global-settings", () => {
|
||||
disabledPlugins: ["/plugins/example.js", "/plugins/example.js"],
|
||||
}),
|
||||
).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledPlugins: ["/plugins/example.js"],
|
||||
disabledTools: ["editor", "read_files"],
|
||||
telemetryOptOut: false,
|
||||
@@ -35,8 +37,9 @@ describe("global-settings", () => {
|
||||
disabledTools: [],
|
||||
telemetryOptOut: true,
|
||||
}),
|
||||
).toEqual({ telemetryOptOut: true });
|
||||
).toEqual({ autoUpdateEnabled: true, telemetryOptOut: true });
|
||||
expect(GlobalSettingsSchema.parse({ disabledTools: [] })).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
expect(
|
||||
@@ -45,6 +48,7 @@ describe("global-settings", () => {
|
||||
extra: true,
|
||||
}),
|
||||
).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -55,8 +59,17 @@ describe("global-settings", () => {
|
||||
telemetryOptOut: true,
|
||||
}),
|
||||
).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: true,
|
||||
});
|
||||
expect(
|
||||
GlobalSettingsSchema.parse({
|
||||
autoUpdateEnabled: false,
|
||||
}),
|
||||
).toEqual({
|
||||
autoUpdateEnabled: false,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the schema when reading and writing settings", async () => {
|
||||
@@ -71,10 +84,12 @@ describe("global-settings", () => {
|
||||
});
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["editor", "read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
expect(JSON.parse(await readFile(settingsPath, "utf8"))).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["editor", "read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -88,6 +103,7 @@ describe("global-settings", () => {
|
||||
}),
|
||||
);
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: true,
|
||||
});
|
||||
@@ -100,7 +116,10 @@ describe("global-settings", () => {
|
||||
telemetryOptOut: true,
|
||||
}),
|
||||
);
|
||||
expect(readGlobalSettings()).toEqual({ telemetryOptOut: true });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: true,
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
@@ -117,11 +136,13 @@ describe("global-settings", () => {
|
||||
setDisabledTools(["editor"], false);
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledPlugins: ["/plugins/example.js"],
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
expect(JSON.parse(await readFile(settingsPath, "utf8"))).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledPlugins: ["/plugins/example.js"],
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
@@ -147,7 +168,32 @@ describe("global-settings", () => {
|
||||
|
||||
expect(captureRequired).toHaveBeenCalledTimes(1);
|
||||
expect(captureRequired).toHaveBeenCalledWith("user.opt_out", undefined);
|
||||
expect(readGlobalSettings()).toEqual({ telemetryOptOut: false });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves other settings when auto update is changed", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "core-global-settings-"));
|
||||
try {
|
||||
const settingsPath = join(root, "global-settings.json");
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
|
||||
|
||||
writeGlobalSettings({
|
||||
disabledTools: ["editor"],
|
||||
telemetryOptOut: true,
|
||||
});
|
||||
setAutoUpdateEnabledGlobally(false);
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: false,
|
||||
disabledTools: ["editor"],
|
||||
telemetryOptOut: true,
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
@@ -165,6 +211,7 @@ describe("global-settings", () => {
|
||||
writeGlobalSettings({ disabledTools: ["read_files"] });
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -187,6 +234,7 @@ describe("global-settings", () => {
|
||||
);
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -205,6 +253,7 @@ describe("global-settings", () => {
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = pathA;
|
||||
writeGlobalSettings({ disabledTools: ["editor"] });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["editor"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -212,12 +261,14 @@ describe("global-settings", () => {
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = pathB;
|
||||
writeGlobalSettings({ disabledTools: ["read_files"] });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["read_files"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = pathA;
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["editor"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -233,8 +284,14 @@ describe("global-settings", () => {
|
||||
const settingsPath = join(root, "missing-global-settings.json");
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
|
||||
|
||||
expect(readGlobalSettings()).toEqual({ telemetryOptOut: false });
|
||||
expect(readGlobalSettings()).toEqual({ telemetryOptOut: false });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
@@ -272,7 +329,10 @@ describe("global-settings", () => {
|
||||
const settingsPath = join(root, "global-settings.json");
|
||||
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
|
||||
|
||||
expect(readGlobalSettings()).toEqual({ telemetryOptOut: false });
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
|
||||
await writeFile(
|
||||
settingsPath,
|
||||
@@ -280,6 +340,7 @@ describe("global-settings", () => {
|
||||
);
|
||||
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["editor"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ const GlobalSettingsStringListSchema = z
|
||||
export const GlobalSettingsSchema = z
|
||||
.object({
|
||||
telemetryOptOut: z.boolean().default(false).catch(false),
|
||||
autoUpdateEnabled: z.boolean().default(true).catch(true),
|
||||
disabledTools: GlobalSettingsStringListSchema.optional(),
|
||||
disabledPlugins: GlobalSettingsStringListSchema.optional(),
|
||||
})
|
||||
@@ -39,9 +40,11 @@ export const GlobalSettingsSchema = z
|
||||
.transform((settings) => {
|
||||
const normalized: {
|
||||
telemetryOptOut: boolean;
|
||||
autoUpdateEnabled: boolean;
|
||||
disabledTools?: string[];
|
||||
disabledPlugins?: string[];
|
||||
} = {
|
||||
autoUpdateEnabled: settings.autoUpdateEnabled,
|
||||
telemetryOptOut: settings.telemetryOptOut,
|
||||
};
|
||||
if (settings.disabledTools?.length) {
|
||||
@@ -160,6 +163,23 @@ export function setTelemetryOptOutGlobally(
|
||||
);
|
||||
}
|
||||
|
||||
export function isAutoUpdateEnabledGlobally(): boolean {
|
||||
return readGlobalSettings().autoUpdateEnabled;
|
||||
}
|
||||
|
||||
export function setAutoUpdateEnabledGlobally(
|
||||
autoUpdateEnabled: boolean,
|
||||
options: WriteGlobalSettingsOptions = {},
|
||||
): void {
|
||||
writeGlobalSettings(
|
||||
{
|
||||
...readGlobalSettings(),
|
||||
autoUpdateEnabled,
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveDisabledToolNames(
|
||||
disabledToolNames?: ReadonlyArray<string>,
|
||||
): Set<string> {
|
||||
|
||||
@@ -217,15 +217,7 @@ describe("resolveProviderConfig", () => {
|
||||
const modelIds = Object.keys(resolved?.knownModels ?? {});
|
||||
|
||||
expect(modelIds).toEqual(
|
||||
expect.arrayContaining([
|
||||
"gpt-5.5",
|
||||
"gpt-5.5-pro",
|
||||
"gpt-5.2",
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.3-codex-spark",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
]),
|
||||
expect.arrayContaining(["gpt-5.5", "gpt-5.5-pro", "gpt-5.4"]),
|
||||
);
|
||||
expect(modelIds).not.toContain("gpt-5.1-codex-max");
|
||||
expect(modelIds).not.toContain("gpt-5.2-codex");
|
||||
@@ -254,14 +246,7 @@ describe("resolveProviderConfig", () => {
|
||||
);
|
||||
|
||||
expect(Object.keys(resolved?.knownModels ?? {})).toEqual(
|
||||
expect.arrayContaining([
|
||||
"gpt-5.2",
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.3-codex-spark",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
"gpt-5.5",
|
||||
]),
|
||||
expect.arrayContaining(["gpt-5.4", "gpt-5.4-mini", "gpt-5.5"]),
|
||||
);
|
||||
expect(resolved?.knownModels?.["gpt-5.4-mini"]).toEqual(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -106,6 +106,7 @@ describe("plugin uninstall service", () => {
|
||||
expect(result.installPath).toBe(installPath);
|
||||
expect(existsSync(installPath)).toBe(false);
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledPlugins: ["/tmp/other-plugin.ts"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -146,6 +147,7 @@ describe("plugin uninstall service", () => {
|
||||
await expect(uninstallPlugin({ path: pluginPath })).rejects.toThrow();
|
||||
expect(existsSync(pluginPath)).toBe(true);
|
||||
expect(readGlobalSettings()).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledPlugins: [pluginPath],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
|
||||
@@ -111,7 +111,7 @@ describe("migrateLegacyProviderSettings", () => {
|
||||
);
|
||||
expect(manager.getProviderSettings("anthropic")).toEqual({
|
||||
provider: "anthropic",
|
||||
model: "claude-opus-4-8",
|
||||
model: "claude-fable-5",
|
||||
apiKey: "legacy-key",
|
||||
});
|
||||
expect(manager.read().providers.openai?.tokenSource).toBe("manual");
|
||||
|
||||
@@ -287,6 +287,7 @@ Use this skill.`,
|
||||
await readFile(process.env.CLINE_GLOBAL_SETTINGS_PATH, "utf8"),
|
||||
),
|
||||
).toEqual({
|
||||
autoUpdateEnabled: true,
|
||||
disabledTools: ["plugin-tool"],
|
||||
telemetryOptOut: false,
|
||||
});
|
||||
@@ -297,6 +298,6 @@ Use this skill.`,
|
||||
JSON.parse(
|
||||
await readFile(process.env.CLINE_GLOBAL_SETTINGS_PATH, "utf8"),
|
||||
),
|
||||
).toEqual({ telemetryOptOut: false });
|
||||
).toEqual({ autoUpdateEnabled: true, telemetryOptOut: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,12 +109,14 @@ export {
|
||||
filterDisabledTools,
|
||||
filterExtensionToolRegistrations,
|
||||
GlobalSettingsSchema,
|
||||
isAutoUpdateEnabledGlobally,
|
||||
isPluginDisabledGlobally,
|
||||
isTelemetryOptedOutGlobally,
|
||||
isToolDisabledGlobally,
|
||||
readGlobalSettings,
|
||||
resolveDisabledPluginPaths,
|
||||
resolveDisabledToolNames,
|
||||
setAutoUpdateEnabledGlobally,
|
||||
setDisabledPlugin,
|
||||
setDisabledTools,
|
||||
setTelemetryOptOutGlobally,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/llms",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"description": "Config-driven SDK for selecting, extending, and instantiating LLM providers and models",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -140,7 +140,9 @@ function toAiSdkMessages(
|
||||
if (part.type === "tool-call") {
|
||||
const metadata = part.metadata as Record<string, unknown> | undefined;
|
||||
const thoughtSignature =
|
||||
metadata?.thoughtSignature ?? metadata?.thought_signature;
|
||||
metadata?.thoughtSignature ??
|
||||
metadata?.signature ??
|
||||
metadata?.thought_signature;
|
||||
content.push({
|
||||
type: "tool-call",
|
||||
toolCallId: part.toolCallId,
|
||||
@@ -604,12 +606,19 @@ function extractGoogleThoughtMetadata(
|
||||
providerMetadata?.google && typeof providerMetadata.google === "object"
|
||||
? (providerMetadata.google as Record<string, unknown>)
|
||||
: undefined;
|
||||
const vertexMetadata =
|
||||
providerMetadata?.vertex && typeof providerMetadata.vertex === "object"
|
||||
? (providerMetadata.vertex as Record<string, unknown>)
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
typeof metadata.thoughtSignature !== "string" &&
|
||||
typeof googleMetadata?.thoughtSignature === "string"
|
||||
typeof (
|
||||
googleMetadata?.thoughtSignature ?? vertexMetadata?.thoughtSignature
|
||||
) === "string"
|
||||
) {
|
||||
metadata.thoughtSignature = googleMetadata.thoughtSignature;
|
||||
metadata.thoughtSignature =
|
||||
googleMetadata?.thoughtSignature ?? vertexMetadata?.thoughtSignature;
|
||||
}
|
||||
if (
|
||||
typeof metadata.thought_signature !== "string" &&
|
||||
|
||||
@@ -70,15 +70,15 @@ describe("built-in provider metadata", () => {
|
||||
expect.arrayContaining([
|
||||
"gpt-5.5",
|
||||
"gpt-5.5-pro",
|
||||
"gpt-5.2",
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.3-codex-spark",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
]),
|
||||
);
|
||||
expect(modelIds).not.toContain("gpt-5.1-codex-max");
|
||||
expect(modelIds).not.toContain("gpt-5.2");
|
||||
expect(modelIds).not.toContain("gpt-5.2-codex");
|
||||
expect(modelIds).not.toContain("gpt-5.3-codex");
|
||||
expect(modelIds).not.toContain("gpt-5.3-codex-spark");
|
||||
expect(modelIds).not.toContain("gpt-5.4-nano");
|
||||
expect(modelIds).not.toContain("o3");
|
||||
expect(chatGptModels["gpt-5.5"]).toEqual(
|
||||
@@ -95,13 +95,51 @@ describe("built-in provider metadata", () => {
|
||||
contextWindow: expect.any(Number),
|
||||
}),
|
||||
);
|
||||
expect(chatGptModels["gpt-5.3-codex"]).toEqual(
|
||||
});
|
||||
|
||||
it("includes Claude Fable 5 in Anthropic, OpenRouter, Vercel AI Gateway, and Cline model lists", async () => {
|
||||
const anthropicModels = await getModelsForProvider("anthropic");
|
||||
const openRouterModels = await getModelsForProvider("openrouter");
|
||||
const vercelModels = await getModelsForProvider("vercel-ai-gateway");
|
||||
const clineModels = await getModelsForProvider("cline");
|
||||
|
||||
expect(anthropicModels["claude-fable-5"]).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "GPT-5.3 Codex",
|
||||
maxInputTokens: expect.any(Number),
|
||||
contextWindow: expect.any(Number),
|
||||
name: "Claude Fable 5",
|
||||
contextWindow: 1_000_000,
|
||||
maxTokens: 128_000,
|
||||
capabilities: expect.arrayContaining([
|
||||
"tools",
|
||||
"reasoning",
|
||||
"prompt-cache",
|
||||
]),
|
||||
pricing: expect.objectContaining({
|
||||
input: 10,
|
||||
output: 50,
|
||||
cacheRead: 1,
|
||||
cacheWrite: 12.5,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(openRouterModels["anthropic/claude-fable-5"]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "anthropic/claude-fable-5",
|
||||
name: "Claude Fable 5",
|
||||
contextWindow: 1_000_000,
|
||||
maxTokens: 128_000,
|
||||
}),
|
||||
);
|
||||
expect(vercelModels["anthropic/claude-fable-5"]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "anthropic/claude-fable-5",
|
||||
name: "Claude Fable 5",
|
||||
contextWindow: 1_000_000,
|
||||
}),
|
||||
);
|
||||
expect(clineModels["anthropic/claude-fable-5"]).toEqual(
|
||||
openRouterModels["anthropic/claude-fable-5"],
|
||||
);
|
||||
expect(clineModels["anthropic/claude-opus-4.8"]).toBeDefined();
|
||||
});
|
||||
|
||||
it("routes native Z.AI providers through GLM thinking metadata", async () => {
|
||||
@@ -123,4 +161,22 @@ describe("built-in provider metadata", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("routes direct MiniMax M3 through MiniMax thinking metadata", async () => {
|
||||
await expect(getProvider("minimax")).resolves.toMatchObject({
|
||||
metadata: {
|
||||
routing: {
|
||||
reasoning: {
|
||||
format: "minimax-thinking",
|
||||
routes: [
|
||||
expect.objectContaining({
|
||||
matcher: "model-id",
|
||||
modelId: "MiniMax-M3",
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
QWEN_CACHE_ROUTING_METADATA,
|
||||
} from "./routing/anthropic-compatible";
|
||||
import { GLM_THINKING_ROUTING_METADATA } from "./routing/glm-thinking";
|
||||
import { MINIMAX_THINKING_ROUTING_METADATA } from "./routing/minimax-thinking";
|
||||
|
||||
export const DEFAULT_INTERNAL_OCA_BASE_URL =
|
||||
"https://code-internal.aiservice.us-chicago-1.oci.oraclecloud.com/20250206/app/litellm";
|
||||
@@ -953,7 +954,7 @@ export const BUILTIN_SPECS: BuiltinSpec[] = [
|
||||
apiKeyEnv: ["MINIMAX_API_KEY"],
|
||||
modelsProviderId: "minimax",
|
||||
defaults: { baseUrl: "https://api.minimax.io/anthropic/v1" },
|
||||
metadata: ANTHROPIC_ROUTING_METADATA,
|
||||
metadata: MINIMAX_THINKING_ROUTING_METADATA,
|
||||
},
|
||||
{
|
||||
id: "opencode",
|
||||
|
||||
@@ -1568,6 +1568,286 @@ describe("sdk-gateway", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves Vertex thought signatures on tool calls and replays them", async () => {
|
||||
streamTextSpy.mockReturnValueOnce({
|
||||
fullStream: makeStreamParts([
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_1",
|
||||
toolName: "editor",
|
||||
input: { path: "/tmp/out.txt" },
|
||||
providerMetadata: {
|
||||
vertex: {
|
||||
thoughtSignature: "sig_1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ type: "finish", usage: { inputTokens: 1, outputTokens: 1 } },
|
||||
]),
|
||||
});
|
||||
|
||||
const gateway = createGateway({
|
||||
providerConfigs: [
|
||||
{
|
||||
providerId: "vertex",
|
||||
options: {
|
||||
project: "test-project",
|
||||
location: "global",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const events = await collect(
|
||||
await gateway.stream({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
messages: baseMessages,
|
||||
tools: [
|
||||
{
|
||||
name: "editor",
|
||||
description: "Edits files",
|
||||
inputSchema: { type: "object" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(events).toContainEqual(
|
||||
expect.objectContaining({
|
||||
type: "tool-call-delta",
|
||||
toolCallId: "call_1",
|
||||
toolName: "editor",
|
||||
metadata: expect.objectContaining({
|
||||
thoughtSignature: "sig_1",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
const toolCallEvent = events.find(
|
||||
(event): event is Extract<AgentModelEvent, { type: "tool-call-delta" }> =>
|
||||
event.type === "tool-call-delta",
|
||||
);
|
||||
expect(toolCallEvent).toBeDefined();
|
||||
|
||||
streamTextSpy.mockReset();
|
||||
|
||||
const replayCases = [
|
||||
{
|
||||
name: "fresh event metadata",
|
||||
metadata: toolCallEvent?.metadata,
|
||||
},
|
||||
{
|
||||
name: "persisted history metadata",
|
||||
metadata: { signature: "sig_1" },
|
||||
},
|
||||
];
|
||||
|
||||
for (const replayCase of replayCases) {
|
||||
streamTextSpy.mockReset();
|
||||
streamTextSpy.mockReturnValueOnce({
|
||||
fullStream: makeStreamParts([
|
||||
{ type: "text-delta", textDelta: `done ${replayCase.name}` },
|
||||
{ type: "finish", usage: { inputTokens: 1, outputTokens: 1 } },
|
||||
]),
|
||||
});
|
||||
|
||||
await collect(
|
||||
await gateway.stream({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
messages: [
|
||||
{
|
||||
id: `assistant_${replayCase.name}`,
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_1",
|
||||
toolName: "editor",
|
||||
input: { path: "/tmp/out.txt" },
|
||||
metadata: replayCase.metadata,
|
||||
},
|
||||
],
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: `tool_${replayCase.name}`,
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call_1",
|
||||
toolName: "editor",
|
||||
output: { ok: true },
|
||||
},
|
||||
],
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
name: "editor",
|
||||
description: "Edits files",
|
||||
inputSchema: { type: "object" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(streamTextSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
role: "assistant",
|
||||
content: [
|
||||
expect.objectContaining({
|
||||
type: "tool-call",
|
||||
toolCallId: "call_1",
|
||||
toolName: "editor",
|
||||
providerOptions: {
|
||||
google: {
|
||||
thoughtSignature: "sig_1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves legacy Google snake_case thought signatures", async () => {
|
||||
streamTextSpy.mockReturnValueOnce({
|
||||
fullStream: makeStreamParts([
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_legacy",
|
||||
toolName: "editor",
|
||||
input: { path: "/tmp/out.txt" },
|
||||
providerMetadata: {
|
||||
google: {
|
||||
thought_signature: "sig_legacy",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ type: "finish", usage: { inputTokens: 1, outputTokens: 1 } },
|
||||
]),
|
||||
});
|
||||
|
||||
const gateway = createGateway({
|
||||
providerConfigs: [
|
||||
{
|
||||
providerId: "gemini",
|
||||
apiKey: "google-key",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const events = await collect(
|
||||
await gateway.stream({
|
||||
providerId: "gemini",
|
||||
modelId: "gemini-2.5-flash",
|
||||
messages: baseMessages,
|
||||
tools: [
|
||||
{
|
||||
name: "editor",
|
||||
description: "Edits files",
|
||||
inputSchema: { type: "object" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(events).toContainEqual(
|
||||
expect.objectContaining({
|
||||
type: "tool-call-delta",
|
||||
toolCallId: "call_legacy",
|
||||
toolName: "editor",
|
||||
metadata: expect.objectContaining({
|
||||
thought_signature: "sig_legacy",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
streamTextSpy.mockReset();
|
||||
streamTextSpy.mockReturnValueOnce({
|
||||
fullStream: makeStreamParts([
|
||||
{ type: "text-delta", textDelta: "done" },
|
||||
{ type: "finish", usage: { inputTokens: 1, outputTokens: 1 } },
|
||||
]),
|
||||
});
|
||||
|
||||
await collect(
|
||||
await gateway.stream({
|
||||
providerId: "gemini",
|
||||
modelId: "gemini-2.5-flash",
|
||||
messages: [
|
||||
{
|
||||
id: "assistant_legacy",
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_legacy",
|
||||
toolName: "editor",
|
||||
input: { path: "/tmp/out.txt" },
|
||||
metadata: {
|
||||
thought_signature: "sig_legacy",
|
||||
},
|
||||
},
|
||||
],
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: "tool_legacy",
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call_legacy",
|
||||
toolName: "editor",
|
||||
output: { ok: true },
|
||||
},
|
||||
],
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
name: "editor",
|
||||
description: "Edits files",
|
||||
inputSchema: { type: "object" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(streamTextSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
role: "assistant",
|
||||
content: [
|
||||
expect.objectContaining({
|
||||
type: "tool-call",
|
||||
toolCallId: "call_legacy",
|
||||
toolName: "editor",
|
||||
providerOptions: {
|
||||
google: {
|
||||
thoughtSignature: "sig_legacy",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not pass extra tools to providers that disable external tool execution", async () => {
|
||||
streamTextSpy.mockReturnValue({
|
||||
fullStream: makeStreamParts([
|
||||
|
||||
@@ -27,6 +27,21 @@ function normalizedModelId(
|
||||
return normalizeRoutingValue(request.modelId) ?? "";
|
||||
}
|
||||
|
||||
function geminiModelDescriptor(input: {
|
||||
request: Pick<GatewayStreamRequest, "modelId">;
|
||||
context: GatewayProviderContext;
|
||||
}): string {
|
||||
return [
|
||||
input.request.modelId,
|
||||
input.context.model.id,
|
||||
input.context.model.name,
|
||||
input.context.model.metadata?.family,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function isAnthropicLineageValue(value: string | undefined): boolean {
|
||||
const normalized = normalizeRoutingValue(value);
|
||||
return normalized
|
||||
@@ -87,6 +102,44 @@ export function isQwenModel(options: {
|
||||
return isQwenLineageValue(options.modelId);
|
||||
}
|
||||
|
||||
export function isGemini3Model(input: {
|
||||
request: Pick<GatewayStreamRequest, "modelId">;
|
||||
context: GatewayProviderContext;
|
||||
}): boolean {
|
||||
return /(^|[/\s])gemini-3([.-]|$)/.test(geminiModelDescriptor(input));
|
||||
}
|
||||
|
||||
export function isGeminiProModel(input: {
|
||||
request: Pick<GatewayStreamRequest, "modelId">;
|
||||
context: GatewayProviderContext;
|
||||
}): boolean {
|
||||
return /(^|[/\s])gemini-2\.5-pro([-\s]|$)/.test(geminiModelDescriptor(input));
|
||||
}
|
||||
|
||||
export function isGeminiFlashModel(input: {
|
||||
request: Pick<GatewayStreamRequest, "modelId">;
|
||||
context: GatewayProviderContext;
|
||||
}): boolean {
|
||||
const descriptor = geminiModelDescriptor(input);
|
||||
return (
|
||||
/(^|[/\s])gemini-(?:\d(?:\.\d)?-)?flash(?:-lite|-image)?([-\s]|$)/.test(
|
||||
descriptor,
|
||||
) || descriptor.includes("gemini-flash")
|
||||
);
|
||||
}
|
||||
|
||||
export function supportsGeminiThinking(input: {
|
||||
request: Pick<GatewayStreamRequest, "modelId">;
|
||||
context: GatewayProviderContext;
|
||||
}): boolean {
|
||||
const descriptor = geminiModelDescriptor(input);
|
||||
return (
|
||||
isGemini3Model(input) ||
|
||||
/(^|[/\s])gemini-2\.5([-\s]|$)/.test(descriptor) ||
|
||||
descriptor.includes("gemini-flash-latest")
|
||||
);
|
||||
}
|
||||
|
||||
function modelFamilyMatches(
|
||||
family: string | undefined,
|
||||
routeFamily: string | undefined,
|
||||
@@ -162,6 +215,15 @@ export function isGlmModel(
|
||||
return family.includes("glm") || normalizedModelId(request).includes("glm");
|
||||
}
|
||||
|
||||
export function isMiniMaxM3Model(
|
||||
request: Pick<GatewayStreamRequest, "modelId">,
|
||||
_context: GatewayProviderContext,
|
||||
): boolean {
|
||||
const modelId = normalizedModelId(request);
|
||||
|
||||
return modelId === "minimax-m3" || modelId === "minimax/minimax-m3";
|
||||
}
|
||||
|
||||
export function isKimiK26Family(context: GatewayProviderContext): boolean {
|
||||
return normalizedFamily(context) === "kimi-k2.6";
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ import type { ModelInfo } from "../catalog/types";
|
||||
|
||||
const OPENAI_CODEX_ALLOWED_MODELS = new Set([
|
||||
"gpt-5.5",
|
||||
"gpt-5.2",
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.3-codex-spark",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import type {
|
||||
GatewayProviderMetadata,
|
||||
GatewayStreamRequest,
|
||||
} from "@cline/shared";
|
||||
import { buildProviderAndAliasPatch, type ProviderOptionsPatch } from "./utils";
|
||||
|
||||
export const MINIMAX_THINKING_ROUTING_METADATA: GatewayProviderMetadata = {
|
||||
routing: {
|
||||
promptCache: {
|
||||
format: "anthropic-cache-control",
|
||||
routes: [{ matcher: "anthropic-compatible" }],
|
||||
},
|
||||
reasoning: {
|
||||
format: "minimax-thinking",
|
||||
routes: [
|
||||
{
|
||||
matcher: "model-id",
|
||||
modelId: "MiniMax-M3",
|
||||
requiredCapability: "reasoning",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function buildMiniMaxThinkingOptions(request: GatewayStreamRequest) {
|
||||
if (request.reasoning?.enabled === true) {
|
||||
return { thinking: { type: "adaptive" } };
|
||||
}
|
||||
if (request.reasoning?.enabled === false) {
|
||||
return { thinking: { type: "disabled" } };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function buildMiniMaxGatewayReasoningOptions(request: GatewayStreamRequest) {
|
||||
if (request.reasoning?.enabled === true) {
|
||||
return { reasoning: { enabled: true } };
|
||||
}
|
||||
if (request.reasoning?.enabled === false) {
|
||||
return { reasoning: { exclude: true } };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function buildMiniMaxThinkingProviderOptionsPatch(
|
||||
request: GatewayStreamRequest,
|
||||
providerOptionsKey: string,
|
||||
): ProviderOptionsPatch | undefined {
|
||||
const thinking = buildMiniMaxThinkingOptions(request);
|
||||
if (!thinking) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
openaiCompatible: thinking,
|
||||
[request.providerId]: thinking,
|
||||
...(providerOptionsKey !== request.providerId
|
||||
? { [providerOptionsKey]: thinking }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMiniMaxGatewayReasoningProviderOptionsPatch(
|
||||
request: GatewayStreamRequest,
|
||||
providerOptionsKey: string,
|
||||
): ProviderOptionsPatch | undefined {
|
||||
const reasoning = buildMiniMaxGatewayReasoningOptions(request);
|
||||
return reasoning
|
||||
? buildProviderAndAliasPatch({
|
||||
providerId: request.providerId,
|
||||
providerOptionsKey,
|
||||
bucketOptions: reasoning,
|
||||
})
|
||||
: undefined;
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
import {
|
||||
isDeepSeekFamily,
|
||||
isGemini3Model,
|
||||
isGeminiFlashModel,
|
||||
isGeminiProModel,
|
||||
isGlmModel,
|
||||
isKimiK26Family as isKimiK26FamilyFact,
|
||||
isMiniMaxM3Model,
|
||||
isMoonshotKimiModelIdFallback,
|
||||
modelReasoningDefaultsOn,
|
||||
providerReasoningRouteMatches,
|
||||
supportsGeminiThinking,
|
||||
} from "../model-facts";
|
||||
import { buildGatewayReasoningOptions } from "./anthropic-compatible";
|
||||
import { buildOpenAINativeProviderOptions } from "./generic-compatible";
|
||||
@@ -12,6 +17,10 @@ import {
|
||||
buildNativeGlmThinkingProviderOptionsPatch,
|
||||
buildRoutedGlmReasoningProviderOptionsPatch,
|
||||
} from "./glm-thinking";
|
||||
import {
|
||||
buildMiniMaxGatewayReasoningProviderOptionsPatch,
|
||||
buildMiniMaxThinkingProviderOptionsPatch,
|
||||
} from "./minimax-thinking";
|
||||
import type {
|
||||
MatchedProviderOptionRule,
|
||||
ProviderOptionBuildInput,
|
||||
@@ -42,6 +51,10 @@ function isDeepSeekModelOrProviderDefault(
|
||||
);
|
||||
}
|
||||
|
||||
function isMiniMaxM3(input: ProviderOptionMatchInput): boolean {
|
||||
return isMiniMaxM3Model(input.request, input.context);
|
||||
}
|
||||
|
||||
function isOllamaReasoningDefaultOnDisable(
|
||||
input: ProviderOptionMatchInput,
|
||||
): boolean {
|
||||
@@ -74,6 +87,16 @@ function hasGlmThinkingProviderRouting(
|
||||
);
|
||||
}
|
||||
|
||||
function usesMiniMaxThinkingProviderRouting(
|
||||
input: ProviderOptionMatchInput,
|
||||
): boolean {
|
||||
return providerReasoningRouteMatches(
|
||||
"minimax-thinking",
|
||||
input.request,
|
||||
input.context,
|
||||
);
|
||||
}
|
||||
|
||||
function resolveFamilyThinkingType(
|
||||
input: ProviderOptionMatchInput,
|
||||
defaultWhenUnset: "enabled" | "disabled" | undefined,
|
||||
@@ -102,6 +125,64 @@ function buildReasoningPatchForProvider(
|
||||
});
|
||||
}
|
||||
|
||||
const GEMINI_25_THINKING_BUDGET_BY_EFFORT = {
|
||||
low: 1_024,
|
||||
medium: 8_192,
|
||||
high: 24_576,
|
||||
} as const;
|
||||
|
||||
function buildGeminiThinkingConfig(input: ProviderOptionBuildInput):
|
||||
| {
|
||||
thinkingLevel?: "minimal" | "low" | "medium" | "high";
|
||||
thinkingBudget?: number;
|
||||
includeThoughts: boolean;
|
||||
}
|
||||
| undefined {
|
||||
const reasoning = input.request.reasoning;
|
||||
if (!reasoning) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isGemini3Model(input)) {
|
||||
if (reasoning.enabled === false) {
|
||||
return {
|
||||
thinkingLevel: isGeminiFlashModel(input) ? "minimal" : "low",
|
||||
includeThoughts: false,
|
||||
};
|
||||
}
|
||||
if (!reasoning.effort) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
thinkingLevel: reasoning.effort,
|
||||
includeThoughts: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoning.enabled === false) {
|
||||
return {
|
||||
thinkingBudget: isGeminiProModel(input) ? 128 : 0,
|
||||
includeThoughts: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof reasoning.budgetTokens === "number") {
|
||||
return {
|
||||
thinkingBudget: reasoning.budgetTokens,
|
||||
includeThoughts: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (!reasoning.effort) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
thinkingBudget: GEMINI_25_THINKING_BUDGET_BY_EFFORT[reasoning.effort],
|
||||
includeThoughts: true,
|
||||
};
|
||||
}
|
||||
|
||||
const directAnthropicProviderRule: ProviderOptionRule = {
|
||||
id: "provider.anthropic.direct",
|
||||
phase: "provider",
|
||||
@@ -205,22 +286,58 @@ const openRouterReasoningRule: ProviderOptionRule = {
|
||||
),
|
||||
};
|
||||
|
||||
const clineMiniMaxM3GatewayReasoningRule: ProviderOptionRule = {
|
||||
id: "provider.cline.minimax-m3.gateway-reasoning",
|
||||
phase: "provider-reasoning",
|
||||
description:
|
||||
"Cline-routed MiniMax M3 keeps the gateway reasoning shape instead of leaking generic thinking.",
|
||||
applies: (input) =>
|
||||
input.request.providerId === "cline" && isMiniMaxM3(input),
|
||||
suppresses: { genericThinking: true, genericEffort: true },
|
||||
build: () => undefined,
|
||||
};
|
||||
|
||||
const vercelMiniMaxM3GatewayReasoningRule: ProviderOptionRule = {
|
||||
id: "provider.vercel-ai-gateway.minimax-m3.gateway-reasoning",
|
||||
phase: "provider-reasoning",
|
||||
description:
|
||||
"Vercel-routed MiniMax M3 uses the gateway reasoning include/exclude shape.",
|
||||
applies: (input) =>
|
||||
input.request.providerId === "vercel-ai-gateway" && isMiniMaxM3(input),
|
||||
suppresses: { genericThinking: true, genericEffort: true },
|
||||
build: (input) =>
|
||||
buildMiniMaxGatewayReasoningProviderOptionsPatch(
|
||||
input.request,
|
||||
input.providerOptionsKey,
|
||||
),
|
||||
};
|
||||
|
||||
const geminiThinkingRule: ProviderOptionRule = {
|
||||
id: "provider.google-gemini.thinking-config",
|
||||
phase: "provider",
|
||||
description: "Google/Gemini maps reasoning effort to google.thinkingConfig.",
|
||||
description: "Google/Gemini/Vertex maps reasoning to thinkingConfig.",
|
||||
suppresses: { genericThinking: true, genericEffort: true },
|
||||
applies: (input) =>
|
||||
(input.request.providerId === "google" ||
|
||||
input.request.providerId === "gemini") &&
|
||||
!!input.request.reasoning?.effort,
|
||||
build: (input) => ({
|
||||
google: {
|
||||
thinkingConfig: {
|
||||
thinkingLevel: input.request.reasoning?.effort,
|
||||
includeThoughts: true,
|
||||
input.request.providerId === "gemini" ||
|
||||
input.request.providerId === "vertex") &&
|
||||
supportsGeminiThinking(input) &&
|
||||
(!!input.request.reasoning?.effort ||
|
||||
typeof input.request.reasoning?.budgetTokens === "number" ||
|
||||
input.request.reasoning?.enabled === false),
|
||||
build: (input) => {
|
||||
const providerOptionsName =
|
||||
input.request.providerId === "vertex" ? "vertex" : "google";
|
||||
const thinkingConfig = buildGeminiThinkingConfig(input);
|
||||
if (!thinkingConfig) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
[providerOptionsName]: {
|
||||
thinkingConfig,
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const clineReasoningDisabledThinkingRule: ProviderOptionRule = {
|
||||
@@ -331,6 +448,19 @@ const nativeZaiGlmThinkingRule: ProviderOptionRule = {
|
||||
),
|
||||
};
|
||||
|
||||
const miniMaxThinkingRule: ProviderOptionRule = {
|
||||
id: "provider.routing.minimax-thinking",
|
||||
phase: "model-overlay",
|
||||
description: "Direct MiniMax M3 uses thinking.type adaptive/disabled.",
|
||||
applies: usesMiniMaxThinkingProviderRouting,
|
||||
suppresses: { genericThinking: true, genericEffort: true },
|
||||
build: (input) =>
|
||||
buildMiniMaxThinkingProviderOptionsPatch(
|
||||
input.request,
|
||||
input.providerOptionsKey,
|
||||
),
|
||||
};
|
||||
|
||||
const routedGlmReasoningRule: ProviderOptionRule = {
|
||||
id: "family.glm.routed-reasoning",
|
||||
phase: "model-overlay",
|
||||
@@ -365,6 +495,8 @@ export const PROVIDER_OPTION_RULES: ReadonlyArray<ProviderOptionRule> = [
|
||||
genericProviderFanoutRule,
|
||||
clineGatewayReasoningRule,
|
||||
openRouterReasoningRule,
|
||||
clineMiniMaxM3GatewayReasoningRule,
|
||||
vercelMiniMaxM3GatewayReasoningRule,
|
||||
geminiThinkingRule,
|
||||
clineReasoningDisabledThinkingRule,
|
||||
kimiK26ThinkingRule,
|
||||
@@ -372,6 +504,7 @@ export const PROVIDER_OPTION_RULES: ReadonlyArray<ProviderOptionRule> = [
|
||||
ollamaReasoningDefaultOnDisableRule,
|
||||
nonGlmProviderRoutingSuppressionRule,
|
||||
nativeZaiGlmThinkingRule,
|
||||
miniMaxThinkingRule,
|
||||
routedGlmReasoningRule,
|
||||
];
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
} from "@cline/shared";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { GLM_THINKING_ROUTING_METADATA } from "./glm-thinking";
|
||||
import { MINIMAX_THINKING_ROUTING_METADATA } from "./minimax-thinking";
|
||||
import {
|
||||
composeAiSdkProviderOptions,
|
||||
mergeProviderOptionPatches,
|
||||
@@ -1227,6 +1228,263 @@ describe("composeAiSdkProviderOptions: family/provider thinking patches", () =>
|
||||
{ bucket: "openaiCompatible", lacks: ["thinking"] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "openrouter MiniMax M3 reasoning enabled -> OpenRouter reasoning shape",
|
||||
request: {
|
||||
providerId: "openrouter",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: true },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "openrouter",
|
||||
has: { reasoning: { enabled: true } },
|
||||
lacks: ["thinking", "effort", "reasoningEffort"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
lacks: ["thinking", "reasoning", "effort", "reasoningEffort"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "openrouter MiniMax M3 reasoning disabled -> OpenRouter reasoning.exclude",
|
||||
request: {
|
||||
providerId: "openrouter",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: false },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "openrouter",
|
||||
has: { reasoning: { exclude: true } },
|
||||
lacks: ["thinking"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
lacks: ["thinking", "reasoning"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "vercel MiniMax M3 reasoning enabled -> gateway reasoning shape",
|
||||
request: {
|
||||
providerId: "vercel-ai-gateway",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "vercel-ai-gateway",
|
||||
has: { reasoning: { enabled: true } },
|
||||
lacks: ["thinking", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
{
|
||||
bucket: "vercelAiGateway",
|
||||
has: { reasoning: { enabled: true } },
|
||||
lacks: ["thinking", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "vercel MiniMax M3 reasoning disabled -> gateway reasoning.exclude",
|
||||
request: {
|
||||
providerId: "vercel-ai-gateway",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: false },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "vercel-ai-gateway",
|
||||
has: { reasoning: { exclude: true } },
|
||||
lacks: ["thinking"],
|
||||
},
|
||||
{
|
||||
bucket: "vercelAiGateway",
|
||||
has: { reasoning: { exclude: true } },
|
||||
lacks: ["thinking"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "vercel MiniMax M3 sibling id -> no MiniMax M3 gateway exception",
|
||||
request: {
|
||||
providerId: "vercel-ai-gateway",
|
||||
modelId: "minimax/minimax-m3-pro",
|
||||
reasoning: { enabled: true },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "vercel-ai-gateway",
|
||||
has: { thinking: { type: "adaptive" } },
|
||||
lacks: ["reasoning"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "cline MiniMax M3 reasoning enabled -> gateway reasoning without thinking leak",
|
||||
request: {
|
||||
providerId: "cline",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "cline",
|
||||
has: { reasoning: { enabled: true, effort: "high" } },
|
||||
lacks: ["thinking", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
lacks: ["thinking", "reasoning", "effort", "reasoningEffort"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "cline MiniMax M3 reasoning disabled -> gateway reasoning disabled",
|
||||
request: {
|
||||
providerId: "cline",
|
||||
modelId: "minimax/minimax-m3",
|
||||
reasoning: { enabled: false },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "cline",
|
||||
has: { reasoning: { enabled: false } },
|
||||
lacks: ["thinking"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
lacks: ["thinking", "reasoning"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "direct MiniMax M3 reasoning disabled -> thinking.type=disabled",
|
||||
request: {
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M3",
|
||||
reasoning: { enabled: false },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
metadata: MINIMAX_THINKING_ROUTING_METADATA,
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "minimax",
|
||||
has: { thinking: { type: "disabled" } },
|
||||
lacks: ["reasoning", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
has: { thinking: { type: "disabled" } },
|
||||
lacks: ["reasoning", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "direct MiniMax M3 reasoning enabled -> thinking.type=adaptive",
|
||||
request: {
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M3",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
metadata: MINIMAX_THINKING_ROUTING_METADATA,
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "minimax",
|
||||
has: { thinking: { type: "adaptive" } },
|
||||
lacks: ["reasoning", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
has: { thinking: { type: "adaptive" } },
|
||||
lacks: ["reasoning", "effort", "reasoningEffort", "reasoningSummary"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "direct MiniMax M2.5 reasoning enabled -> existing generic adaptive thinking",
|
||||
request: {
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M2.5",
|
||||
reasoning: { enabled: true },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
metadata: MINIMAX_THINKING_ROUTING_METADATA,
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "minimax",
|
||||
has: { thinking: { type: "adaptive" } },
|
||||
lacks: ["reasoning"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
has: { thinking: { type: "adaptive" } },
|
||||
lacks: ["reasoning"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "direct MiniMax M2.7 reasoning disabled -> no MiniMax M3 disabled exception",
|
||||
request: {
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M2.7",
|
||||
reasoning: { enabled: false },
|
||||
},
|
||||
context: {
|
||||
family: "minimax",
|
||||
capabilities: ["reasoning"],
|
||||
metadata: MINIMAX_THINKING_ROUTING_METADATA,
|
||||
},
|
||||
expect: [
|
||||
{
|
||||
bucket: "minimax",
|
||||
lacks: ["thinking", "reasoning", "effort", "reasoningEffort"],
|
||||
},
|
||||
{
|
||||
bucket: "openaiCompatible",
|
||||
lacks: ["thinking", "reasoning", "effort", "reasoningEffort"],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Ollama Qwen3: model behavior fact first, documented dynamic fallback second.
|
||||
{
|
||||
name: "ollama metadata reasoningDefaultOn disabled -> reasoningEffort none",
|
||||
@@ -1416,7 +1674,7 @@ describe("composeAiSdkProviderOptions: provider-specific overlays", () => {
|
||||
expect(result.openaiCodex).not.toHaveProperty("truncation");
|
||||
});
|
||||
|
||||
it("emits the gemini google.thinkingConfig only when reasoning effort is set", () => {
|
||||
it("emits Gemini 2.5 google.thinkingConfig budget only when reasoning effort is set", () => {
|
||||
const withEffort = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "gemini",
|
||||
@@ -1426,7 +1684,7 @@ describe("composeAiSdkProviderOptions: provider-specific overlays", () => {
|
||||
makeContext({ providerId: "gemini", modelId: "gemini-2.5-flash" }),
|
||||
);
|
||||
expect(withEffort.google).toEqual({
|
||||
thinkingConfig: { thinkingLevel: "medium", includeThoughts: true },
|
||||
thinkingConfig: { thinkingBudget: 8192, includeThoughts: true },
|
||||
});
|
||||
|
||||
const withoutEffort = composeAiSdkProviderOptions(
|
||||
@@ -1447,11 +1705,168 @@ describe("composeAiSdkProviderOptions: provider-specific overlays", () => {
|
||||
);
|
||||
|
||||
expect(result.google).toEqual({
|
||||
thinkingConfig: { thinkingLevel: "high", includeThoughts: true },
|
||||
thinkingConfig: { thinkingBudget: 24576, includeThoughts: true },
|
||||
});
|
||||
expect(result.google).not.toHaveProperty("thinking");
|
||||
expect(result.google).not.toHaveProperty("effort");
|
||||
expect(result.google).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.google).not.toHaveProperty("reasoningSummary");
|
||||
});
|
||||
|
||||
it("emits Gemini thinkingConfig in the vertex bucket for Vertex providers", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
thinkingConfig: { thinkingLevel: "high", includeThoughts: true },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinking");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
expect(result.google).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps Vertex Claude reasoning on the Anthropic-compatible route", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "claude-sonnet-4-5",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "claude-sonnet-4-5",
|
||||
family: "claude-sonnet",
|
||||
capabilities: ["text", "reasoning"],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
reasoning: { enabled: true, max_tokens: 1024 },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinkingConfig");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
});
|
||||
|
||||
it("maps disabled Vertex thinking to minimal thinkingConfig", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
reasoning: { enabled: false },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
thinkingConfig: { thinkingLevel: "minimal", includeThoughts: false },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinking");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
expect(result.google).toBeUndefined();
|
||||
});
|
||||
|
||||
it("maps Vertex Gemini Flash Latest disabled thinking to a zero thinking budget", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-flash-latest",
|
||||
reasoning: { enabled: false },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-flash-latest",
|
||||
family: "gemini-flash",
|
||||
capabilities: ["reasoning"],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
thinkingConfig: { thinkingBudget: 0, includeThoughts: false },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinking");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
expect(result.google).toBeUndefined();
|
||||
});
|
||||
|
||||
it("maps Vertex Gemini Flash Latest effort to a Gemini 2.5 thinking budget", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-flash-latest",
|
||||
reasoning: { enabled: true, effort: "high" },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-flash-latest",
|
||||
family: "gemini-flash",
|
||||
capabilities: ["reasoning"],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
thinkingConfig: { thinkingBudget: 24576, includeThoughts: true },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinking");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
expect(result.google).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps disabled Vertex Gemini 2.5 Pro on a valid minimum thinking budget", () => {
|
||||
const result = composeAiSdkProviderOptions(
|
||||
makeRequest({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-2.5-pro",
|
||||
reasoning: { enabled: false },
|
||||
}),
|
||||
makeContext({
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-2.5-pro",
|
||||
family: "gemini-pro",
|
||||
capabilities: ["reasoning"],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.vertex).toEqual(
|
||||
expect.objectContaining({
|
||||
thinkingConfig: { thinkingBudget: 128, includeThoughts: false },
|
||||
}),
|
||||
);
|
||||
expect(result.vertex).not.toHaveProperty("thinking");
|
||||
expect(result.vertex).not.toHaveProperty("effort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningEffort");
|
||||
expect(result.vertex).not.toHaveProperty("reasoningSummary");
|
||||
expect(result.google).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
+21
-1
@@ -41,6 +41,26 @@ describe("createAnthropicProviderModule", () => {
|
||||
);
|
||||
expect(anthropicModelMock).toHaveBeenCalledWith("MiniMax-M2.5");
|
||||
});
|
||||
|
||||
it("does not wrap fetch for non-MiniMax Anthropic-compatible providers", async () => {
|
||||
const customFetch = vi.fn<typeof fetch>();
|
||||
|
||||
await createAnthropicProviderModule(
|
||||
config({
|
||||
providerId: "anthropic",
|
||||
apiKey: "anthropic-api-key",
|
||||
fetch: customFetch,
|
||||
}),
|
||||
context("anthropic"),
|
||||
);
|
||||
|
||||
expect(createAnthropicMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "anthropic",
|
||||
fetch: customFetch,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function config(
|
||||
@@ -65,6 +85,6 @@ function context(providerId: string): GatewayProviderContext {
|
||||
id: "MiniMax-M2.5",
|
||||
name: "MiniMax-M2.5",
|
||||
},
|
||||
config: config({}),
|
||||
config: config({ providerId }),
|
||||
};
|
||||
}
|
||||
|
||||
+17
-2
@@ -1,9 +1,15 @@
|
||||
import { createAnthropic } from "@ai-sdk/anthropic";
|
||||
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
||||
import type {
|
||||
GatewayProviderContext,
|
||||
GatewayResolvedProviderConfig,
|
||||
} from "@cline/shared";
|
||||
import { wrapLanguageModel } from "ai";
|
||||
import { resolveApiKey } from "../http";
|
||||
import {
|
||||
createMiniMaxThinkingFetch,
|
||||
miniMaxThinkingDisabledMiddleware,
|
||||
} from "./minimax-thinking";
|
||||
import type { ProviderFactoryResult } from "./types";
|
||||
|
||||
export async function createAnthropicProviderModule(
|
||||
@@ -11,14 +17,23 @@ export async function createAnthropicProviderModule(
|
||||
context: GatewayProviderContext,
|
||||
): Promise<ProviderFactoryResult> {
|
||||
const apiKey = await resolveApiKey(config);
|
||||
const isMiniMax = context.provider.id === "minimax";
|
||||
const provider = createAnthropic({
|
||||
apiKey,
|
||||
baseURL: config.baseUrl,
|
||||
headers: config.headers,
|
||||
fetch: config.fetch,
|
||||
fetch: isMiniMax ? createMiniMaxThinkingFetch(config.fetch) : config.fetch,
|
||||
name: context.provider.id,
|
||||
});
|
||||
return {
|
||||
model: (modelId) => provider(modelId),
|
||||
model: (modelId) => {
|
||||
const model = provider(modelId);
|
||||
return isMiniMax
|
||||
? wrapLanguageModel({
|
||||
model: model as LanguageModelV3,
|
||||
middleware: miniMaxThinkingDisabledMiddleware,
|
||||
})
|
||||
: model;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createMiniMaxThinkingFetch,
|
||||
MINIMAX_THINKING_DISABLED_HEADER,
|
||||
miniMaxThinkingDisabledMiddleware,
|
||||
} from "./minimax-thinking";
|
||||
|
||||
describe("MiniMax thinking shim", () => {
|
||||
it("preserves fetch preconnect when the runtime exposes it", () => {
|
||||
const preconnect = vi.fn();
|
||||
const baseFetch = Object.assign(
|
||||
vi.fn(async () => new Response("{}")),
|
||||
{
|
||||
preconnect,
|
||||
},
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const wrappedFetch = createMiniMaxThinkingFetch(
|
||||
baseFetch,
|
||||
) as typeof fetch & {
|
||||
preconnect?: (url: string) => void;
|
||||
};
|
||||
wrappedFetch.preconnect?.("https://api.minimax.io");
|
||||
|
||||
expect(preconnect).toHaveBeenCalledWith("https://api.minimax.io");
|
||||
});
|
||||
|
||||
it("marks MiniMax requests whose provider options disable thinking", async () => {
|
||||
const result = await miniMaxThinkingDisabledMiddleware.transformParams?.({
|
||||
type: "stream",
|
||||
model: {} as never,
|
||||
params: {
|
||||
providerOptions: {
|
||||
minimax: { thinking: { type: "disabled" } },
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
[MINIMAX_THINKING_DISABLED_HEADER]: "1",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("injects explicit disabled thinking and strips the private marker header", async () => {
|
||||
const baseFetch = vi.fn(async () => new Response("{}"));
|
||||
const wrappedFetch = createMiniMaxThinkingFetch(baseFetch);
|
||||
|
||||
await wrappedFetch("https://api.minimax.io/anthropic/v1/messages", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
[MINIMAX_THINKING_DISABLED_HEADER]: "1",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "MiniMax-M3",
|
||||
messages: [],
|
||||
}),
|
||||
});
|
||||
|
||||
const init = baseFetch.mock.calls[0]?.[1];
|
||||
expect(
|
||||
new Headers(init?.headers).has(MINIMAX_THINKING_DISABLED_HEADER),
|
||||
).toBe(false);
|
||||
expect(JSON.parse(String(init?.body))).toEqual(
|
||||
expect.objectContaining({
|
||||
thinking: { type: "disabled" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import type {
|
||||
LanguageModelV3CallOptions,
|
||||
LanguageModelV3Middleware,
|
||||
} from "@ai-sdk/provider";
|
||||
|
||||
export const MINIMAX_THINKING_DISABLED_HEADER =
|
||||
"x-cline-minimax-thinking-disabled";
|
||||
|
||||
type FetchWithOptionalPreconnect = typeof fetch & {
|
||||
preconnect?: (...args: unknown[]) => unknown;
|
||||
};
|
||||
|
||||
function getMiniMaxThinkingType(providerOptions: unknown): string | undefined {
|
||||
const options =
|
||||
providerOptions && typeof providerOptions === "object"
|
||||
? (providerOptions as Record<string, unknown>)
|
||||
: {};
|
||||
const minimax =
|
||||
options.minimax && typeof options.minimax === "object"
|
||||
? (options.minimax as Record<string, unknown>)
|
||||
: undefined;
|
||||
const thinking =
|
||||
minimax?.thinking && typeof minimax.thinking === "object"
|
||||
? (minimax.thinking as Record<string, unknown>)
|
||||
: undefined;
|
||||
return typeof thinking?.type === "string" ? thinking.type : undefined;
|
||||
}
|
||||
|
||||
export const miniMaxThinkingDisabledMiddleware: LanguageModelV3Middleware = {
|
||||
specificationVersion: "v3",
|
||||
transformParams: async ({ params }) => {
|
||||
if (getMiniMaxThinkingType(params.providerOptions) !== "disabled") {
|
||||
return params;
|
||||
}
|
||||
|
||||
return {
|
||||
...params,
|
||||
headers: {
|
||||
...params.headers,
|
||||
[MINIMAX_THINKING_DISABLED_HEADER]: "1",
|
||||
},
|
||||
} satisfies LanguageModelV3CallOptions;
|
||||
},
|
||||
};
|
||||
|
||||
async function readBody(
|
||||
body: NonNullable<Parameters<typeof fetch>[1]>["body"],
|
||||
): Promise<unknown> {
|
||||
if (typeof body === "string") {
|
||||
return JSON.parse(body) as unknown;
|
||||
}
|
||||
if (body instanceof Uint8Array) {
|
||||
return JSON.parse(new TextDecoder().decode(body)) as unknown;
|
||||
}
|
||||
if (body instanceof ArrayBuffer) {
|
||||
return JSON.parse(new TextDecoder().decode(body)) as unknown;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function writeBody(body: unknown): string {
|
||||
return JSON.stringify(body);
|
||||
}
|
||||
|
||||
export function createMiniMaxThinkingFetch(baseFetch: typeof fetch = fetch) {
|
||||
const wrappedFetch = (async (
|
||||
input: Parameters<typeof fetch>[0],
|
||||
init?: Parameters<typeof fetch>[1],
|
||||
) => {
|
||||
const headers = new Headers(init?.headers);
|
||||
const shouldInject = headers.get(MINIMAX_THINKING_DISABLED_HEADER) === "1";
|
||||
if (!shouldInject) {
|
||||
return baseFetch(input, init);
|
||||
}
|
||||
|
||||
headers.delete(MINIMAX_THINKING_DISABLED_HEADER);
|
||||
const body = await readBody(init?.body);
|
||||
const bodyRecord =
|
||||
body && typeof body === "object" && !Array.isArray(body)
|
||||
? (body as Record<string, unknown>)
|
||||
: undefined;
|
||||
const nextBody =
|
||||
bodyRecord && bodyRecord.thinking === undefined
|
||||
? writeBody({
|
||||
...bodyRecord,
|
||||
thinking: { type: "disabled" },
|
||||
})
|
||||
: init?.body;
|
||||
|
||||
return baseFetch(input, {
|
||||
...init,
|
||||
headers,
|
||||
body: nextBody,
|
||||
});
|
||||
}) as typeof fetch;
|
||||
const sourceFetch = baseFetch as FetchWithOptionalPreconnect;
|
||||
(wrappedFetch as FetchWithOptionalPreconnect).preconnect =
|
||||
typeof sourceFetch.preconnect === "function"
|
||||
? sourceFetch.preconnect.bind(baseFetch)
|
||||
: () => {};
|
||||
|
||||
return wrappedFetch;
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createHandlerAsync, type ProviderConfig } from "../providers";
|
||||
|
||||
type ReasoningControl = { enabled: boolean };
|
||||
|
||||
type LiveCase = {
|
||||
label: string;
|
||||
config: ProviderConfig;
|
||||
expectedBody: Record<string, unknown>;
|
||||
unexpectedBodyKeys?: string[];
|
||||
};
|
||||
|
||||
type LiveMetrics = {
|
||||
usageSeen: boolean;
|
||||
reasoningChunks: number;
|
||||
};
|
||||
|
||||
type CapturedRequest = {
|
||||
url: string;
|
||||
body: unknown;
|
||||
};
|
||||
|
||||
const LIVE_TEST_ENABLED = process.env.LLMS_LIVE_MINIMAX_ROUTING_TESTS === "1";
|
||||
const PROVIDER_TIMEOUT_MS = Number(
|
||||
process.env.LLMS_LIVE_PROVIDER_TIMEOUT_MS ?? "120000",
|
||||
);
|
||||
const DEFAULT_PROMPT =
|
||||
"What is 12*13? Answer with only the number and no explanation.";
|
||||
|
||||
function readRequiredEnv(name: string): string {
|
||||
const value = process.env[name]?.trim();
|
||||
if (!value) {
|
||||
throw new Error(`Set ${name} to run MiniMax M3 live routing tests.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function makeConfig(options: {
|
||||
providerId: string;
|
||||
modelId: string;
|
||||
apiKeyEnv: string;
|
||||
reasoning: ReasoningControl;
|
||||
fetch: typeof fetch;
|
||||
}): ProviderConfig {
|
||||
return {
|
||||
providerId: options.providerId,
|
||||
modelId: options.modelId,
|
||||
apiKey: readRequiredEnv(options.apiKeyEnv),
|
||||
fetch: options.fetch,
|
||||
thinking: options.reasoning.enabled,
|
||||
maxOutputTokens: 64,
|
||||
};
|
||||
}
|
||||
|
||||
async function readRequestBody(
|
||||
body: unknown,
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (typeof body === "string") {
|
||||
return JSON.parse(body) as Record<string, unknown>;
|
||||
}
|
||||
if (body instanceof Uint8Array) {
|
||||
return JSON.parse(new TextDecoder().decode(body)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
}
|
||||
if (body instanceof ArrayBuffer) {
|
||||
return JSON.parse(new TextDecoder().decode(body)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
}
|
||||
return body && typeof body === "object"
|
||||
? (body as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
async function runLiveRequest(config: ProviderConfig): Promise<LiveMetrics> {
|
||||
const handler = await createHandlerAsync(config);
|
||||
const metrics: LiveMetrics = { usageSeen: false, reasoningChunks: 0 };
|
||||
const stream = handler.createMessage("You are concise.", [
|
||||
{ role: "user", content: DEFAULT_PROMPT },
|
||||
]);
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.type === "usage") {
|
||||
metrics.usageSeen = true;
|
||||
}
|
||||
if (chunk.type === "reasoning") {
|
||||
metrics.reasoningChunks += 1;
|
||||
}
|
||||
if (chunk.type === "done" && !chunk.success) {
|
||||
throw new Error(chunk.error ?? "done chunk reported success=false");
|
||||
}
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
function buildCases(): LiveCase[] {
|
||||
const cases: LiveCase[] = [];
|
||||
const addCase = (options: {
|
||||
label: string;
|
||||
providerId: string;
|
||||
modelId: string;
|
||||
apiKeyEnv: string;
|
||||
reasoning: ReasoningControl;
|
||||
expectedBody: Record<string, unknown>;
|
||||
unexpectedBodyKeys?: string[];
|
||||
}) => {
|
||||
const captured: CapturedRequest[] = [];
|
||||
const captureFetch: typeof fetch = async (input, init) => {
|
||||
captured.push({ url: String(input), body: init?.body });
|
||||
return fetch(input, init);
|
||||
};
|
||||
const config = makeConfig({ ...options, fetch: captureFetch });
|
||||
cases.push({
|
||||
label: options.label,
|
||||
config: {
|
||||
...config,
|
||||
metadata: {
|
||||
...(config.metadata ?? {}),
|
||||
get capturedRequests() {
|
||||
return captured;
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBody: options.expectedBody,
|
||||
unexpectedBodyKeys: options.unexpectedBodyKeys,
|
||||
});
|
||||
};
|
||||
|
||||
addCase({
|
||||
label: "direct MiniMax M3 reasoning enabled",
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M3",
|
||||
apiKeyEnv: "MINIMAX_API_KEY",
|
||||
reasoning: { enabled: true },
|
||||
expectedBody: { thinking: { type: "adaptive" } },
|
||||
unexpectedBodyKeys: [
|
||||
"reasoning",
|
||||
"effort",
|
||||
"reasoningEffort",
|
||||
"reasoningSummary",
|
||||
],
|
||||
});
|
||||
addCase({
|
||||
label: "direct MiniMax M3 reasoning disabled",
|
||||
providerId: "minimax",
|
||||
modelId: "MiniMax-M3",
|
||||
apiKeyEnv: "MINIMAX_API_KEY",
|
||||
reasoning: { enabled: false },
|
||||
expectedBody: { thinking: { type: "disabled" } },
|
||||
unexpectedBodyKeys: [
|
||||
"reasoning",
|
||||
"effort",
|
||||
"reasoningEffort",
|
||||
"reasoningSummary",
|
||||
],
|
||||
});
|
||||
addCase({
|
||||
label: "OpenRouter MiniMax M3 reasoning enabled",
|
||||
providerId: "openrouter",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "OPENROUTER_API_KEY",
|
||||
reasoning: { enabled: true },
|
||||
expectedBody: { reasoning: { enabled: true } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
addCase({
|
||||
label: "OpenRouter MiniMax M3 reasoning disabled",
|
||||
providerId: "openrouter",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "OPENROUTER_API_KEY",
|
||||
reasoning: { enabled: false },
|
||||
expectedBody: { reasoning: { exclude: true } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
addCase({
|
||||
label: "Vercel AI Gateway MiniMax M3 reasoning enabled",
|
||||
providerId: "vercel-ai-gateway",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "AI_GATEWAY_API_KEY",
|
||||
reasoning: { enabled: true },
|
||||
expectedBody: { reasoning: { enabled: true } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
addCase({
|
||||
label: "Vercel AI Gateway MiniMax M3 reasoning disabled",
|
||||
providerId: "vercel-ai-gateway",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "AI_GATEWAY_API_KEY",
|
||||
reasoning: { enabled: false },
|
||||
expectedBody: { reasoning: { exclude: true } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
addCase({
|
||||
label: "Cline Gateway MiniMax M3 reasoning enabled",
|
||||
providerId: "cline",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "CLINE_API_KEY",
|
||||
reasoning: { enabled: true },
|
||||
expectedBody: { reasoning: { enabled: true } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
addCase({
|
||||
label: "Cline Gateway MiniMax M3 reasoning disabled",
|
||||
providerId: "cline",
|
||||
modelId: "minimax/minimax-m3",
|
||||
apiKeyEnv: "CLINE_API_KEY",
|
||||
reasoning: { enabled: false },
|
||||
expectedBody: { reasoning: { enabled: false } },
|
||||
unexpectedBodyKeys: ["thinking"],
|
||||
});
|
||||
|
||||
return cases;
|
||||
}
|
||||
|
||||
function getCapturedRequests(config: ProviderConfig): CapturedRequest[] {
|
||||
const metadata = config.metadata as
|
||||
| { capturedRequests?: CapturedRequest[] }
|
||||
| undefined;
|
||||
return metadata?.capturedRequests ?? [];
|
||||
}
|
||||
|
||||
describe("live MiniMax M3 provider routing mechanics", () => {
|
||||
const runLive = LIVE_TEST_ENABLED ? it : it.skip;
|
||||
|
||||
runLive(
|
||||
"sends explicit reasoning controls for every MiniMax M3 route",
|
||||
async () => {
|
||||
const failures: string[] = [];
|
||||
|
||||
for (const liveCase of buildCases()) {
|
||||
try {
|
||||
const metrics = await runLiveRequest(liveCase.config);
|
||||
expect(metrics.usageSeen, liveCase.label).toBe(true);
|
||||
const request = getCapturedRequests(liveCase.config).at(-1);
|
||||
expect(request, liveCase.label).toBeDefined();
|
||||
const body = await readRequestBody(request?.body);
|
||||
expect(body, liveCase.label).toEqual(
|
||||
expect.objectContaining(liveCase.expectedBody),
|
||||
);
|
||||
for (const key of liveCase.unexpectedBodyKeys ?? []) {
|
||||
expect(body, liveCase.label).not.toHaveProperty(key);
|
||||
}
|
||||
} catch (error) {
|
||||
failures.push(
|
||||
`${liveCase.label}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
throw new Error(
|
||||
`MiniMax M3 routing failures (${failures.length}):\n${failures.join(
|
||||
"\n",
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
PROVIDER_TIMEOUT_MS,
|
||||
);
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/sdk",
|
||||
"description": "Cline SDK - user-facing alias for @cline/core",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"description": "Shared utilities, types, and schemas for Cline packages",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -86,6 +86,7 @@ export type {
|
||||
AiSdkMessagePart,
|
||||
} from "./llms/ai-sdk-format";
|
||||
export {
|
||||
EMPTY_CONTENT_TEXT,
|
||||
formatMessagesForAiSdk,
|
||||
sanitizeSurrogates,
|
||||
toAiSdkToolResultOutput,
|
||||
|
||||
@@ -100,6 +100,7 @@ export type {
|
||||
AiSdkMessagePart,
|
||||
} from "./llms/ai-sdk-format";
|
||||
export {
|
||||
EMPTY_CONTENT_TEXT,
|
||||
formatMessagesForAiSdk,
|
||||
sanitizeSurrogates,
|
||||
toAiSdkToolResultOutput,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
EMPTY_CONTENT_TEXT,
|
||||
formatMessagesForAiSdk,
|
||||
sanitizeSurrogates,
|
||||
toAiSdkToolResultOutput,
|
||||
@@ -38,6 +39,42 @@ describe("formatMessagesForAiSdk", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("replaces empty string user and assistant messages with explicit error text", () => {
|
||||
const messages = formatMessagesForAiSdk(undefined, [
|
||||
{ role: "user", content: "" },
|
||||
{ role: "assistant", content: " \n\t " },
|
||||
{ role: "user", content: [{ type: "text", text: "continue" }] },
|
||||
]);
|
||||
|
||||
expect(messages).toEqual([
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "continue" }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("replaces empty content arrays with explicit error text", () => {
|
||||
const messages = formatMessagesForAiSdk(undefined, [
|
||||
{ role: "assistant", content: [] },
|
||||
]);
|
||||
|
||||
expect(messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves providerOptions on text parts", () => {
|
||||
const messages = formatMessagesForAiSdk(undefined, [
|
||||
{
|
||||
|
||||
@@ -61,6 +61,8 @@ export interface AiSdkFormatterMessage {
|
||||
content: string | AiSdkFormatterPart[];
|
||||
}
|
||||
|
||||
export const EMPTY_CONTENT_TEXT = "ERROR: EMPTY CONTENT";
|
||||
|
||||
export type AiSdkMessagePart = Record<string, unknown>;
|
||||
export type AiSdkMessage = {
|
||||
role: "system" | "user" | "assistant" | "tool";
|
||||
@@ -290,6 +292,13 @@ export function formatMessagesForAiSdk(
|
||||
const contentParts = message.content;
|
||||
|
||||
if (typeof contentParts === "string") {
|
||||
if (contentParts.trim().length === 0) {
|
||||
result.push({
|
||||
role: message.role,
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
result.push({
|
||||
role: message.role,
|
||||
content: sanitizeSurrogates(contentParts),
|
||||
@@ -299,6 +308,14 @@ export function formatMessagesForAiSdk(
|
||||
|
||||
const messageParts: AiSdkMessagePart[] = [];
|
||||
const toolResultParts: AiSdkMessagePart[] = [];
|
||||
if (contentParts.length === 0) {
|
||||
result.push({
|
||||
role: message.role,
|
||||
content: [{ type: "text", text: EMPTY_CONTENT_TEXT }],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const part of contentParts) {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
|
||||
@@ -35,7 +35,10 @@ export type GatewayModelCapability =
|
||||
export type GatewayPromptCacheStrategy = "anthropic-automatic";
|
||||
export type GatewayUsageCostDisplay = "show" | "hide";
|
||||
export type GatewayPromptCacheFormat = "anthropic-cache-control";
|
||||
export type GatewayReasoningFormat = "anthropic-thinking" | "glm-thinking";
|
||||
export type GatewayReasoningFormat =
|
||||
| "anthropic-thinking"
|
||||
| "glm-thinking"
|
||||
| "minimax-thinking";
|
||||
export type GatewayModelRoute =
|
||||
| { matcher: "anthropic-compatible" }
|
||||
| {
|
||||
|
||||
Reference in New Issue
Block a user