Compare commits

...
Author SHA1 Message Date
Saoud Rizwan 7e5b8be28c chore(cli): release v3.0.22 2026-06-09 12:08:17 -07:00
Saoud Rizwan 764e901693 test(core): update legacy migration default to claude-fable-5
The Fable 5 PR (#11385) made claude-fable-5 the newest anthropic model,
which sorts first in the generated catalog. Legacy provider migration
defaults to the first catalog model, so the migrated default changed from
claude-opus-4-8 to claude-fable-5. Update the test expectation to match.
2026-06-09 11:55:08 -07:00
Saoud Rizwan 2cabb2ddf6 chore(sdk): release v0.0.45 2026-06-09 11:43:56 -07:00
Saoud Rizwan c32789f697 chore: bump version and update changelog (v3.89.0) (#11386) 2026-06-09 11:36:08 -07:00
Saoud Rizwan 349a8da750 feat(sdk): add Claude Fable 5 model support (#11385) 2026-06-09 11:31:50 -07:00
Saoud Rizwan f09dab7a0b feat(vscode): add Claude Fable 5 support to VS Code extension (#11384) 2026-06-09 11:19:29 -07:00
Robin Newhouse 3a3ea6ee96 Fix MiniMax M3 thinking controls across gateways [ENG-2163] (#11371)
* fix(llms): route MiniMax M3 thinking controls

* test(llms): tighten MiniMax M3 routing scope

* fix(llms): preserve fetch preconnect in MiniMax shim
2026-06-09 10:53:59 -07:00
dependabot[bot] 1c1ea0bd53 chore(deps): bump shell-quote from 1.8.3 to 1.8.4 in /apps/vscode (#11383)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.8.3 to 1.8.4.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 19:11:24 +02:00
Mikołaj Kondratek 70303d8541 Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics (#11381)
* Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics

Rename the 'Plugin Type' dropdown to 'Cline Surface' since CLI is not a plugin; the option values keep each choice unambiguous.

Add an 'IDE / CLI Diagnostics' field with per-surface copy-paste steps for About info (VSCode Help/About, JetBrains Help/About Copy button) and a CLI exception using 'cline --version'. System Information is left as-is; minor overlap is acceptable.

* Update repo-label-issues workflow for renamed Cline Surface field

The auto-labeler matches the rendered '### Plugin Type' heading. Since the form label was renamed to 'Cline Surface', update the three regexes so JetBrains/VS Code/CLI labels keep applying.
2026-06-09 18:38:53 +02:00
Saoud Rizwan 8ba15dfca6 chore(cli): release v3.0.21 2026-06-08 21:44:14 -07:00
Saoud Rizwan 6ab6a1eabc chore(sdk): release v0.0.44 2026-06-08 21:27:22 -07:00
Bee 2ad4146de1 doc(sdk): add host logger support in plugin examples (#11363)
* doc(sdk): add host logger support in plugin examples

Add examples to use the exposed `ctx.logger` to plugins via the `setup` second argument for
diagnostics. Wire logging into the agents-squad example to record setup,
subagent starts, follow-ups, and async failures, with a `logPluginError`
helper that falls back to severity-tagged logs. Update README with
logger usage guidance and examples.

* patches
2026-06-08 16:29:38 -07:00
Ara cfc2250717 fix(sdk): support Vertex ADC tool-use inference (#10773)
* fix(sdk): replay Vertex thought signatures

* fix(sdk): route Gemini 2.5 thinking config

* fix(sdk): tighten Vertex thinking replay routing

* chore(sdk): keep Vertex PR scoped to signatures

* chore(sdk): remove defensive thought signature fallback

* test(sdk): cover legacy Google thought signatures

* refactor(sdk): move Gemini model facts
2026-06-08 15:38:54 -07:00
Bee 730bac7f59 fix: empty SDK message content replay for Bedrock CLINE-2373 (#11320)
* fix: empty SDK message content replay for Bedrock CLINE-2373

This fixes SDK message formatting when persisted conversation history contains an empty user or assistant message, such as after an interrupted task is resumed.

Instead of dropping the message turn, the SDK now preserves it and inserts a text content block:

ERROR: EMPTY CONTENT

This prevents providers like Amazon Bedrock from rejecting replayed history with empty content arrays while avoiding message removal that could affect provider turn ordering.

* Exported EMPTY_CONTENT_TEXT from @cline/shared so core/shared use one constant
2026-06-08 14:47:53 -07:00
BeeandSaoud Rizwan 797ea1f607 feat: global auto-update setting for CLI startup updates (#11326)
* feat: global auto-update setting for CLI startup updates

* patches

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-08 14:22:41 -07:00
Saoud Rizwan 9d59de4a4c test(llms): align ChatGPT subscription model expectations (#11348) 2026-06-07 23:44:51 -07:00
Saoud Rizwan ae67ca7a13 fix(cli): show Cline credits refill link (#11345)
* fix(cli): show Cline credits refill link

* fix(cli): simplify Cline credits error matcher

* fix(cli): keep credits handling in TUI

* fix(cli): rename credits error matcher

* fix(cli): render credits dashboard as link

* fix(cli): remove credits redirect param

* fix(cli): document temporary credits matcher
2026-06-07 19:58:34 -07:00
Tomás Barreiro 7e2583f40c Fix broken tests (#11344) 2026-06-07 18:22:43 -07:00
Tomás Barreiro ecca88bb98 Clean-up the Codex model list (#11342) 2026-06-07 17:16:12 -07:00
Saoud Rizwan 4bb93ee5b9 chore: bump version and update changelog (v3.88.1) (#11334) 2026-06-06 18:26:28 -07:00
Saoud Rizwan 4f2d7398ed fix(vscode): include walkthrough files in extension package (#11333) 2026-06-06 17:56:55 -07:00
Saoud Rizwan bc184f346d fix(cli): scroll inline ask question responses (#11293)
* fix(cli): scroll inline ask question responses

* fix(cli): address ask question review feedback
2026-06-06 17:31:16 -07:00
Bee 96aea0d34b fix(cli): connector thread session routing & stale hub session (#11325)
* fix(cli): connector thread session routing & stale hub session

Fix connector thread session routing and stale hub session recovery

**PR Description**

This fixes connector messages from separate chat threads being routed into the wrong active runtime session.

**Issue**

In Slack, if a user sent a message in a different thread while another thread was still processing, the new message could be treated as a steer message for the active task. Users could also see errors like:

```text
Slack bridge error: session not found: 1780596180501_ms45m
```

when a connector thread had a persisted session id that no longer existed in the hub, such as after a hub restart.

**Cause**

Connector conversation bindings and active turn queues were using participant identity as the primary key in several paths. That allowed messages from the same user in different chat threads to resolve to the same connector session/active turn.

Separately, persisted connector `sessionId` values were trusted without checking whether the hub still had that runtime session. After a hub restart, the connector could try to send input to a stale session id.

**Fix**

- Store connector conversation bindings by thread id instead of participant key.
- Key connector active turn queues by thread id across Slack, Discord, Telegram, Google Chat, Linear, and WhatsApp adapters.
- Only treat a follow-up as a steer message when the active turn belongs to the same thread.
- Keep participant key/label as metadata instead of using it as the conversation binding key.
- Validate a persisted session id with the hub before reusing it.
- If the persisted session is missing, clear it from thread state and start a fresh runtime session.
- Update schedule delivery metadata to target thread ids while preserving participant metadata.
- Add regression coverage for cross-thread active sessions and stale persisted session ids.

**Verification**

```bash
bun -F @cline/cli typecheck
bunx vitest run apps/cli/src/connectors/connector-host.test.ts apps/cli/src/connectors/thread-bindings.test.ts apps/cli/src/connectors/adapters/slack.test.ts apps/cli/src/connectors/adapters/telegram.test.ts apps/cli/src/connectors/adapters/discord.test.ts apps/cli/src/connectors/adapters/gchat.test.ts apps/cli/src/connectors/adapters/linear.test.ts apps/cli/src/connectors/adapters/whatsapp.test.ts
```

* patches
2026-06-06 10:17:01 -07:00
91 changed files with 4703 additions and 2245 deletions
+15 -3
View File
@@ -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:
+3 -3
View File
@@ -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,
+24
View File
@@ -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
+15
View File
@@ -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 -1
View File
@@ -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": {
+82 -1
View File
@@ -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
View File
@@ -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,
+16 -49
View File
@@ -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({
+4 -15
View File
@@ -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();
});
});
+13 -15
View File
@@ -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();
});
});
+13 -15
View File
@@ -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({
+1 -1
View File
@@ -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", () => {
+13 -13
View File
@@ -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();
});
});
+13 -15
View File
@@ -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();
});
});
+13 -15
View File
@@ -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({
+117 -5
View File
@@ -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" });
});
});
+7 -22
View File
@@ -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()) {
+40 -19
View File
@@ -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", () => {
+38 -58
View File
@@ -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,
>(
+12
View File
@@ -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>
);
}
+15
View File
@@ -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>
+5 -5
View File
@@ -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"
+1 -1
View File
@@ -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))
+101
View File
@@ -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]",
+97 -97
View File
@@ -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=="],
+12
View File
@@ -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
View File
@@ -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
+22 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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",
+2
View File
@@ -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 });
});
});
+2
View File
@@ -109,12 +109,14 @@ export {
filterDisabledTools,
filterExtensionToolRegistrations,
GlobalSettingsSchema,
isAutoUpdateEnabledGlobally,
isPluginDisabledGlobally,
isTelemetryOptedOutGlobally,
isToolDisabledGlobally,
readGlobalSettings,
resolveDisabledPluginPaths,
resolveDisabledToolNames,
setAutoUpdateEnabledGlobally,
setDisabledPlugin,
setDisabledTools,
setTelemetryOptOutGlobally,
+1 -1
View File
@@ -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
+12 -3
View File
@@ -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",
}),
],
},
},
},
});
});
});
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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",
+1
View File
@@ -86,6 +86,7 @@ export type {
AiSdkMessagePart,
} from "./llms/ai-sdk-format";
export {
EMPTY_CONTENT_TEXT,
formatMessagesForAiSdk,
sanitizeSurrogates,
toAiSdkToolResultOutput,
+1
View File
@@ -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":
+4 -1
View File
@@ -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" }
| {