Compare commits

...
Author SHA1 Message Date
Saoud Rizwan c10b417b78 chore(sdk): release v0.0.47 2026-06-11 14:02:45 -07:00
Saoud Rizwan 9958e3f354 feat(cli): allow plugin commands to submit prompts (#11479)
* feat(cli): allow plugin commands to submit prompts

* fix(cli): preserve plugin command output on abort

* revert(cli): drop ineffective clear view tweak
2026-06-11 13:49:50 -07:00
Tomás Barreiro ec75291d5b Allow overriding the API base url (#11440)
* Allow overriding the base url

* Override the mcpbaseurl

* update the api base url

* Fix tests
2026-06-11 22:29:17 +02:00
Tomás BarreiroandSaoud Rizwan a3a31da37d Add the FeatureFlagService to the SDK [NOOP] (#11444)
* Add the FeatureFlagService to the SDK

* Fix comments

* Dispose of the telemetry service

* Dispose of the feature flag service

* Address PR feedback

* Stop the polling early if a new one is triggered with another user id

* Address PR feedback

* Dispose of the feature flag service

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 22:11:35 +02:00
Tomás BarreiroandSaoud Rizwan a69d650838 Open URLs when starting device auth (#11393)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 21:27:44 +02:00
AraandClaude Fable 5 7934d367a9 fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder (#11465)
* test(sdk): add regression tests for structured ToolOperationResult truncation

MessageBuilder tests only covered string and {type:

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo"text"} tool-result
content, not the structured ToolOperationResult[] shape the default tools
(run_commands, read_files, search_codebase) actually emit. Those entries
are plain {query, result, success} objects with no type discriminator, so
the token-bloat path they create was unprotected by tests.

Adds regression tests using the real structured shape: huge result, huge
query, huge read_files payload, aggregate budget across multiple results,
mutation safety, and provider-formatted AI SDK payload size. Assertions
are on actual serialized payload sizes, not transcript shape.

The new tests fail at this commit by design; the following commit makes
them pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder

The runtime stores structured tool outputs (ToolOperationResult[] from
run_commands/read_files/search_codebase) directly as the tool_result
content array (agentPartToContentBlock casts the array straight through).
Those entries have no type discriminator, so MessageBuilder's per-result
truncation, aggregate byte counting, and budget truncation all skipped
them — multi-megabyte command outputs and file reads were JSON-serialized
in full into every subsequent provider request.

MessageBuilder now deep-truncates nested strings inside structured
entries (middle truncation, preserving head and tail), counts them
against the aggregate text budget, collects them as budget-truncation
candidates, and deep-clones them before mutation so the original
conversation history stays untouched. Image blocks are skipped so base64
payloads survive intact.

Real-inference A/B on openrouter:minimax/minimax-m2.7 with realistic
structured payloads: 58.7% overall input-token reduction (82.6% on a
single huge command output) with identical answer correctness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

* fix(sdk): include fetch_web_content in MessageBuilder truncation targets

Review feedback: fetch_web_content also returns ToolOperationResult[] and
its executor allows responses up to 5MB, but the tool was missing from
TARGET_TOOL_NAMES, so a single web fetch could still bloat every
subsequent provider request. Adds the tool to the truncation target set
with a regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:34:43 -07:00
AraandClaude Fable 5 7f9d5461f1 fix(sdk): stop echoing full command text in run_commands tool results (#11463)
The run_commands tool result's query field repeated the entire executed
command, which already exists verbatim in the assistant tool-call input.
For large generated-file commands (e.g. cat <<EOF heredocs) this
duplicated thousands of chars of source text into every subsequent
provider request.

Bound the provider-facing echo to a 200-char preview plus a truncation
note pointing at the tool call input. Short commands pass through
unchanged. Applies to both createBashTool and createWindowsShellTool,
on success and error paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/og840mbjqaog4zita8m0262m
2026-06-11 11:22:20 -07:00
41 changed files with 1737 additions and 460 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import {
type ChatCommandState,
createChatCommandHost,
chatCommandHost,
} from "../../utils/chat-commands";
import type { Config } from "../../utils/types";
@@ -162,4 +163,37 @@ describe("runInteractiveChatCommand", () => {
expect(state.autoApproveTools).toBe(true);
expect(setInteractiveAutoApprove).toHaveBeenCalledWith(true);
});
it("returns plugin command submit prompts as model input", async () => {
const config = makeConfig();
const runtime = makeRuntime();
const onCommandOutput = vi.fn();
const host = createChatCommandHost().register("command", {
names: ["/goal"],
run: async ({ args }, context) => {
await context.reply(`Goal guard set: ${args.join(" ")}`);
await context.submitPrompt?.(args.join(" "));
},
});
const result = await runInteractiveChatCommand({
prompt: "/goal fix tests",
enabled: true,
config,
host,
chatCommandState: makeState(config),
autoApproveAllRef: { current: false },
setInteractiveAutoApprove: () => {},
sessionRuntime: runtime,
stop: () => {},
onCommandOutput,
});
expect(result).toEqual({
handled: false,
input: "fix tests",
commandOutput: "Goal guard set: fix tests",
});
expect(onCommandOutput).toHaveBeenCalledWith("Goal guard set: fix tests");
});
});
@@ -26,7 +26,7 @@ export type InteractiveChatCommandRuntime = Pick<
export type InteractiveChatCommandResult =
| { handled: true; turnResult: InteractiveTurnResult }
| { handled: false; input: string };
| { handled: false; input: string; commandOutput?: string };
function commandTurnResult(commandOutput?: string): InteractiveTurnResult {
return {
@@ -46,6 +46,7 @@ export async function runInteractiveChatCommand(input: {
setInteractiveAutoApprove: (enabled: boolean) => void;
sessionRuntime: InteractiveChatCommandRuntime;
stop: () => void;
onCommandOutput?: (text: string) => void;
}): Promise<InteractiveChatCommandResult> {
let prompt = input.prompt;
const rewrittenTeamPrompt = rewriteTeamPrompt(prompt);
@@ -64,6 +65,7 @@ export async function runInteractiveChatCommand(input: {
}
let commandOutput: string | undefined;
let submitPrompt: string | undefined;
const handled = await maybeHandleChatCommand(prompt, {
enabled: input.enabled,
host: input.host,
@@ -80,6 +82,13 @@ export async function runInteractiveChatCommand(input: {
},
reply: async (text) => {
commandOutput = text;
input.onCommandOutput?.(text);
},
submitPrompt: async (text) => {
const trimmed = text.trim();
if (trimmed) {
submitPrompt = trimmed;
}
},
reset: async () => {
await input.sessionRuntime.resetForNewSession();
@@ -98,6 +107,13 @@ export async function runInteractiveChatCommand(input: {
fork: input.sessionRuntime.forkCurrentSession,
});
if (handled) {
if (submitPrompt) {
return {
handled: false,
input: submitPrompt,
...(commandOutput ? { commandOutput } : {}),
};
}
return {
handled: true,
turnResult: commandTurnResult(commandOutput),
+9 -1
View File
@@ -427,7 +427,8 @@ export async function runInteractive(
uiEvents.off("pending-prompt-submitted", onPendingPromptSubmitted);
};
},
onSubmit: async (input, mode, delivery, attachments) => {
onSubmit: async (input, mode, delivery, attachments, onCommandOutput) => {
let commandOutput: string | undefined;
try {
await sessionRuntime.ensureReady();
await waitForSubmittedMode(mode);
@@ -446,6 +447,7 @@ export async function runInteractive(
setInteractiveAutoApprove,
sessionRuntime,
stop: () => tuiApp?.destroy(),
onCommandOutput,
});
if (chatCommandResult.handled) {
return chatCommandResult.turnResult;
@@ -465,12 +467,14 @@ export async function runInteractive(
setInteractiveAutoApprove,
sessionRuntime,
stop: () => tuiApp?.destroy(),
onCommandOutput,
});
if (chatCommandResult.handled) {
return chatCommandResult.turnResult;
}
}
input = chatCommandResult.input;
commandOutput = chatCommandResult.commandOutput;
const {
prompt: userInput,
userImages,
@@ -507,6 +511,7 @@ export async function runInteractive(
iterations: 0,
finishReason: "queued",
queued: delivery === "queue" || delivery === "steer",
commandOutput,
};
}
if (result.finishReason !== "completed") {
@@ -519,6 +524,7 @@ export async function runInteractive(
currentContextSize: getCurrentContextSize(result.messages),
iterations: result.iterations,
finishReason: "aborted",
commandOutput,
};
}
const errorText = result.text.trim();
@@ -532,6 +538,7 @@ export async function runInteractive(
currentContextSize: getCurrentContextSize(result.messages),
iterations: result.iterations,
finishReason: result.finishReason,
commandOutput,
};
} catch (error) {
if (isAbortInProgress()) {
@@ -539,6 +546,7 @@ export async function runInteractive(
usage: { inputTokens: 0, outputTokens: 0 },
iterations: 0,
finishReason: "aborted",
commandOutput,
};
}
logCliError(config.logger, "Interactive turn failed", {
@@ -327,6 +327,14 @@ export function usePromptInputController(input: {
}
const startedAt = performance.now();
let commandOutputAppended = false;
const appendCommandOutput = (text: string) => {
commandOutputAppended = true;
session.appendEntry({
kind: "status",
text,
});
};
try {
const result = await onSubmit(
promptForSubmit,
@@ -335,8 +343,9 @@ export function usePromptInputController(input: {
activeUserImages.length > 0
? { userImages: activeUserImages }
: undefined,
appendCommandOutput,
);
if (result.commandOutput) {
if (result.commandOutput && !commandOutputAppended) {
session.appendEntry({
kind: "status",
text: result.commandOutput,
+1
View File
@@ -152,6 +152,7 @@ export interface TuiProps {
mode: AgentMode,
delivery?: "queue" | "steer",
attachments?: UserInputAttachments,
onCommandOutput?: (text: string) => void,
) => Promise<InteractiveTurnResult>;
onUpdatePendingPrompt: (input: {
promptId: string;
@@ -146,5 +146,50 @@ describe("onboarding auth telemetry forwarding", () => {
// emitted by completeClineDeviceAuth, so passing telemetry to the start
// helper would double-emit the event.
expect(hoisted.startClineDeviceAuth).toHaveBeenCalledWith();
expect(hoisted.openMock).toHaveBeenCalledWith(
"https://verify?user_code=uc",
{ wait: false },
);
});
it("falls back to displaying the device auth URL when browser open fails", async () => {
hoisted.openMock.mockRejectedValueOnce(new Error("no browser"));
hoisted.startClineDeviceAuth.mockResolvedValueOnce({
deviceCode: "dc",
userCode: "uc",
verificationUri: "https://verify",
verificationUriComplete: "https://verify?user_code=uc",
expiresInSeconds: 600,
pollIntervalSeconds: 5,
});
hoisted.completeClineDeviceAuth.mockResolvedValueOnce({
access: "a",
refresh: "r",
expires: 0,
});
const setStatus = vi.fn();
runDeviceCodeAuthFlow({
providerId: "cline",
providerSettingsManager: makeManager(),
isAborted: () => false,
setUserCode: vi.fn(),
setVerifyUrl: vi.fn(),
setStatus,
setError: vi.fn(),
onComplete: vi.fn(),
});
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
expect(hoisted.openMock).toHaveBeenCalledWith(
"https://verify?user_code=uc",
{ wait: false },
);
expect(setStatus).toHaveBeenCalledWith(
"Could not open browser. Visit the URL below.",
);
});
});
+10 -3
View File
@@ -89,11 +89,18 @@ export function runDeviceCodeAuthFlow(input: {
startClineDeviceAuth()
.then((result) => {
if (input.isAborted()) return;
const verifyUrl =
result.verificationUriComplete || result.verificationUri;
input.setUserCode(result.userCode);
input.setVerifyUrl(
result.verificationUriComplete || result.verificationUri,
);
input.setVerifyUrl(verifyUrl);
input.setStatus("Enter the code at the URL below");
try {
void open(verifyUrl, { wait: false }).catch(() => {
input.setStatus("Could not open browser. Visit the URL below.");
});
} catch {
input.setStatus("Could not open browser. Visit the URL below.");
}
completeClineDeviceAuth({
deviceCode: result.deviceCode,
+1
View File
@@ -28,6 +28,7 @@ export type ChatCommandContext = {
getState: () => Promise<ChatCommandState> | ChatCommandState;
setState: (next: ChatCommandState) => Promise<void> | void;
reply: (text: string) => Promise<void> | void;
submitPrompt?: (prompt: string) => Promise<void> | void;
reset?: () => Promise<void> | void;
abort?: () => Promise<void> | void;
stop?: () => Promise<void> | void;
@@ -65,4 +65,55 @@ describe("plugin chat commands", () => {
expect(reply).toHaveBeenCalledWith("echo:hello plugin");
await shutdown?.();
});
it("bridges plugin command submit prompts onto the chat command context", async () => {
const tempRoot = await mkdtemp(join(tmpdir(), "cli-plugin-commands-"));
tempRoots.push(tempRoot);
const pluginsDir = join(tempRoot, ".cline", "plugins");
await mkdir(pluginsDir, { recursive: true });
await writeFile(
join(pluginsDir, "submit.js"),
[
"export default {",
" name: 'submit-plugin',",
" manifest: { capabilities: ['commands'] },",
" setup(api) {",
" api.registerCommand({",
" name: 'goal',",
" description: 'Set a goal and submit it',",
" handler: async (input) => ({",
" reply: 'goal:' + input,",
" submitPrompt: input",
" })",
" });",
" },",
"};",
].join("\n"),
);
const { host, shutdown } = await createWorkspaceChatCommandHost({
cwd: tempRoot,
workspaceRoot: tempRoot,
});
const reply = vi.fn(async () => undefined);
const submitPrompt = vi.fn(async () => undefined);
const handled = await host.handle("/goal fix tests", {
enabled: true,
getState: async () => ({
enableTools: false,
autoApproveTools: false,
cwd: tempRoot,
workspaceRoot: tempRoot,
}),
setState: async () => undefined,
reply,
submitPrompt,
});
expect(handled).toBe(true);
expect(reply).toHaveBeenCalledWith("goal:fix tests");
expect(submitPrompt).toHaveBeenCalledWith("fix tests");
await shutdown?.();
});
});
+31 -2
View File
@@ -1,5 +1,6 @@
import {
type AgentExtensionCommand,
type AgentExtensionCommandResult,
type BasicLogger,
createContributionRegistry,
resolveAndLoadAgentPlugins,
@@ -45,13 +46,41 @@ function createPluginCommandDefinition(
names: [normalizedName.toLowerCase()],
run: async ({ args }, context) => {
const result = await command.handler?.(args.join(" "));
if (typeof result === "string" && result.trim()) {
await context.reply(result);
const { reply, submitPrompt } = normalizeCommandResult(result);
if (reply) {
await context.reply(reply);
}
if (submitPrompt) {
await context.submitPrompt?.(submitPrompt);
}
},
};
}
function normalizeCommandResult(
result: AgentExtensionCommandResult | undefined,
): { reply?: string; submitPrompt?: string } {
if (typeof result === "string") {
const reply = result.trim();
return reply ? { reply } : {};
}
if (!result || typeof result !== "object") {
return {};
}
const reply =
typeof result.reply === "string" && result.reply.trim()
? result.reply.trim()
: undefined;
const submitPrompt =
typeof result.submitPrompt === "string" && result.submitPrompt.trim()
? result.submitPrompt.trim()
: undefined;
return {
...(reply ? { reply } : {}),
...(submitPrompt ? { submitPrompt } : {}),
};
}
export async function createWorkspaceChatCommandHost(input: {
cwd: string;
workspaceRoot?: string;
+49 -49
View File
@@ -19,7 +19,7 @@
},
"apps/cli": {
"name": "@cline/cli",
"version": "3.0.22",
"version": "3.0.23",
"bin": {
"cline": "src/index.ts",
},
@@ -371,7 +371,7 @@
},
"sdk/packages/agents": {
"name": "@cline/agents",
"version": "0.0.46",
"version": "0.0.47",
"dependencies": {
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*",
@@ -380,7 +380,7 @@
},
"sdk/packages/core": {
"name": "@cline/core",
"version": "0.0.46",
"version": "0.0.47",
"dependencies": {
"@cline/agents": "workspace:*",
"@cline/llms": "workspace:*",
@@ -411,7 +411,7 @@
},
"sdk/packages/llms": {
"name": "@cline/llms",
"version": "0.0.46",
"version": "0.0.47",
"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.46",
"version": "0.0.47",
"dependencies": {
"@cline/core": "workspace:*",
},
},
"sdk/packages/shared": {
"name": "@cline/shared",
"version": "0.0.46",
"version": "0.0.47",
"dependencies": {
"aws4fetch": "^1.0.20",
"jsonrepair": "^3.13.2",
@@ -468,11 +468,11 @@
"@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.123", "", { "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-rL+3Sp9crOlfE7MwguFPS30qVp6HFcr9na0KYMb4CcQdxAIjBJec3EEdCjc94UyRVZWLmgQ6Yr605FmPlFIi0w=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.124", "", { "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-h8CrmbSG+8X0C+M/E1M4oiDHYevqwbzAPN+uLRHS0eJaatF2MZ+juNtOHXNOjk7Bsk9mD2RjYMjJO9dFkb9I7Q=="],
"@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=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.141", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/google": "3.0.80", "@ai-sdk/openai-compatible": "2.0.48", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-UGXQeV+z30Gk1/mhKZoXKbYO7Q0U7pg0Nlz44hb2B1FpYRFBu8+ziaI60BdetncaoxPocdNKhP41AD15IA6nJg=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.142", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/google": "3.0.80", "@ai-sdk/openai-compatible": "2.0.48", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bTWfj0ITBHjAVJHWCA0DB7PO+aDX8bWxTI9hpNAKH7e5uO74URKdi22zlQAOsROEufcLYiAw0LjrYmmXiksErw=="],
"@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=="],
@@ -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.197", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.195", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-sx5K0pvIWbgduMYGz++s28ldj+hu+GfDpXw9T2kL4n+bhRQpQvrH+jU0z3OqvjMrhD5oz9cGJ7bv/0JQEKXIbw=="],
"@ai-sdk/react": ["@ai-sdk/react@3.0.198", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.196", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-ozlxMidzvKXAefvnq95Y34rJ5MipXABIv1bg2RLEnWUBxGrKxNHrYl0fTfi6grTN88wSXMZYaMY2oHMCHDFuJw=="],
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
@@ -520,35 +520,35 @@
"@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.1059.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-node": "^3.972.49", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-HW3Oq2rL65tbuqkQzoRrjfF3jauRfra056Xv0K2YtBWt+LaeLrr95D8SlRmgGzXZHwy38FO/w0N1EKjsTYmDKw=="],
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1062.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.17", "@aws-sdk/credential-provider-node": "^3.972.51", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-QA5z/Pl3aTMR3+bmiHoC6MpKYa4FMk/9lNP7k104uKuUsjMqP4ysRa43IwdcbI9sH023T//kSJCLxrxa2CP/Tw=="],
"@aws-sdk/core": ["@aws-sdk/core@3.974.16", "", { "dependencies": { "@aws-sdk/types": "^3.973.10", "@aws-sdk/xml-builder": "^3.972.27", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.6", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-WXPvTfG7J2H4Ae6ewhd0285UC+8+9p/pKoibXXQlbXSqHexFLGM0oXHTwDfQEPmrNnvuWPpVjgoAUfW+cUFbXw=="],
"@aws-sdk/core": ["@aws-sdk/core@3.974.17", "", { "dependencies": { "@aws-sdk/types": "^3.973.10", "@aws-sdk/xml-builder": "^3.972.27", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.6", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-r8o4h2K7j6P9ngno+8ei0aK0U/4JwDb7A2fMMxGVoSqDN8AFlIzSDeZHME9LcVLR2codyhtr1WAAg+/nmkeeMA=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.39", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-fp7hiew245BbBiaDGjLDaMXqxbOWnqzuhczWrJq/6/3gDNHtZvkorxHQXYpHYiddetR8sBWe3S49HfZ2BQbYSg=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.41", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-byGPybEQe9ejeyUzhWjtjfh0ctv25HsRx2djF/Tl2j9+DAuAmhjq0NqSRqYZEoSe8vJObXz5RYDtJYAmdupBig=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.42", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-0+MCOYqeHyADFdeVr5/e1G8JJoWm7/szZAiKssqJS84E9+tO07509YNyQRJ5a7x5wO9YFAV324syrwm6yIcs5w=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.43", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-g0XVQKzaA/4cq1vz1IvCQwYM+1Pkv01J9yHDpCTXekVuGZRDEz0wqBQ1AuYTq7FM6uik4uBGH8Tb5d9YvgeA7g=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.44", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-auuhqlnv4PUdfqcdHLKGTdoCceXOuby6WNeCMoxtZmQGWNCibbz95/lSYzNWq9cExN17UlRFqo1nvTcC+zHfEg=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-w9PuOoKCt6+xoESvY+zlV0u3PKQ0mVL259PcsVR6a3S/uYJJHnIi4r1NxdJHEcNldUVRIciltWnFMGBR4YEm3g=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.47", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-login": "^3.972.46", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-G+8JuG0CfLcC2IQXFVqMR1+KDF1rksebr+YL6+HHYbNjs/hiwX53ye45sU3pJKljpS2uIXqOrOsicHIv02vWrw=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.49", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/credential-provider-env": "^3.972.43", "@aws-sdk/credential-provider-http": "^3.972.45", "@aws-sdk/credential-provider-login": "^3.972.48", "@aws-sdk/credential-provider-process": "^3.972.43", "@aws-sdk/credential-provider-sso": "^3.972.48", "@aws-sdk/credential-provider-web-identity": "^3.972.48", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-83r5MK+PERv9irzky1o5aNbXiLuaLfeB7N8MrktB9USpoebdNtuG0Ek9ieIxpGH1aZ9a0nIaDaLjEr3EmOV3Ng=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-+H6h2/1Q+iIJ4w+FPCKM//xwy4C9yADknDTR68+K3PbOtB/uLup3zIH8PUKn6QwnttME4VM4ftSTnbvoDf5wXg=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.48", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-amPGeF6fcvLInK4Pu2k2Y2jHFR6MpaIKrZrbaf0QUnV3tjzjWh442eifZ2+KcmzFdsqyvyjBqAhq2JNLt1C5gA=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.49", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-ini": "^3.972.47", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-hlyoc+2352BhC+HF91t9avIDJu1+EvQGGltxFB8ADCpAHGYNNLEQAZJIPzw3O/bRiiDSE2B8pdj/fFCXlDTEDg=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.51", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.43", "@aws-sdk/credential-provider-http": "^3.972.45", "@aws-sdk/credential-provider-ini": "^3.972.49", "@aws-sdk/credential-provider-process": "^3.972.43", "@aws-sdk/credential-provider-sso": "^3.972.48", "@aws-sdk/credential-provider-web-identity": "^3.972.48", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-mbhSY3ytXIGMuBoJsWCivk+63dtVlenT6wstUra07Lar4Ln2MVL8/j5zCTIOog+ig5/FlFJ8gcFU4nQZV+Jh4Q=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.42", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-r996IYVtQ7rWa5UfSs3fZLT/Dq/SSgH9wv9zahx9lcg6vvaPPQHFpTy45nZajZu/+OUdEQxEjTn9rOMons59mA=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.43", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-GPokLNyvTfCmuaHk+v3GKVs4ZT3cMu5kgS2a+NPkOMt96cq6fSIK0g+mZHpGS6Cd4QGrPKesANEaLUKgOskTzg=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/token-providers": "3.1059.0", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-vd+UqRbiYLvXXH3kTAuTxq+Vdb3rOgg29/FeB35ETsgdJTTB7x3YuqtpRckATsL5bDRXFVQo0uBj0/vxCJ8hHg=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.48", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/token-providers": "3.1062.0", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-tf0sD47SeTgCDfOWYssctzGgwAuk8/ECjb7bom4wZ7P1om0qE8i2yjniUdvysmANm5haARr35O8vZnTe/UEtpQ=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xuxBbMorygYsbDV6E/tUGQUgIDfhnzxz8uJYX8rByzehI6gLUu8RPsgOpLmh9YWerqeHZxJbqzgheBSB7tpooQ=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.48", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-YYsumc2oe09gl4l+fjfmR64JDn6+0o4Ql5HMBkMuhFazO1tZlE5NjSnZM3oXHwenPjh2qow0TFgSIVjfWfsojg=="],
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1059.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1059.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-cognito-identity": "^3.972.39", "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-ini": "^3.972.47", "@aws-sdk/credential-provider-login": "^3.972.46", "@aws-sdk/credential-provider-node": "^3.972.49", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-cSdDFb/O3cQHGg78VxPaNruyy9zGxcoeS7DlwYTdwxmYkE0GXmBRoej5N+q+Av9YWBayZ2n3QsSYhtnUXa/GXw=="],
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1062.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1062.0", "@aws-sdk/core": "^3.974.17", "@aws-sdk/credential-provider-cognito-identity": "^3.972.41", "@aws-sdk/credential-provider-env": "^3.972.43", "@aws-sdk/credential-provider-http": "^3.972.45", "@aws-sdk/credential-provider-ini": "^3.972.49", "@aws-sdk/credential-provider-login": "^3.972.48", "@aws-sdk/credential-provider-node": "^3.972.51", "@aws-sdk/credential-provider-process": "^3.972.43", "@aws-sdk/credential-provider-sso": "^3.972.48", "@aws-sdk/credential-provider-web-identity": "^3.972.48", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-QS2UT3srjNppZv6mq7V0igqK/ThYKqRWwDscxDsMEmmEE5JqCPPSqFW71aEpkvXaMdgmG8xEpt4RtNHpZ30cTA=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.14", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/signature-v4-multi-region": "^3.996.31", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-T5CS1r4P27FjkBYIWwVibWqEuq32BbCga2Z5m5OBSdSdi2wPfW2vl6zLWAB/5MeeyC4s2pY/MY3cj2Gd3rgSkg=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.16", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.17", "@aws-sdk/signature-v4-multi-region": "^3.996.31", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-bGvfDgC2KQePjEmZdltScPPLKFoyjPElAXeZcLfvZ58J1AO283//WGtvp9GdnryLHTi7gis0UoCezqh0vl/nig=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.31", "", { "dependencies": { "@aws-sdk/types": "^3.973.10", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-Kn2up9SlG1KC6wRtwf0d7waTGF6rvp9DxYqB54x6UCKdQ6kyaXCqHL4WGb5vUJga5kS8FxnjhY0LqM28aMvnNQ=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1059.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xql+7YBAE7WYb9xJfY0vcAXM8rJXfClmB2wkt+g/EoLUMog0pOb7o741fE96wFqha0uQTEgTo/5lGGguzavxmw=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1062.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.17", "@aws-sdk/nested-clients": "^3.997.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-fvHh53zSm2FoQPgkw9thH5D7sd13bC0nPyuZb+mQJ85l5v7lQnsZ97u6e6YkJJN/LU1Mxm1/DLGrIIRR2L7tZw=="],
"@aws-sdk/types": ["@aws-sdk/types@3.973.10", "", { "dependencies": { "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-992QrTO7G9qCvKD0fx1rMlqcL14plUcRAbwmqqYVsuF3GrqcvlAL9qxR+baMafarEZ+l7DUQ5lCMmt5mbMhF7g=="],
@@ -656,9 +656,9 @@
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
"@clack/core": ["@clack/core@1.4.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-7Wctjq6f7c1CPz8sPpkwUnz8yRgVANkpNupb81q432FjcJg4l+Sw7XANdNSdWfAKq0IHI0JTcUeK5dxs/HrGPw=="],
"@clack/core": ["@clack/core@1.4.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw=="],
"@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=="],
"@clack/prompts": ["@clack/prompts@1.5.1", "", { "dependencies": { "@clack/core": "1.4.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw=="],
"@cline/agents": ["@cline/agents@workspace:sdk/packages/agents"],
@@ -1302,19 +1302,19 @@
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@shikijs/core": ["@shikijs/core@4.1.0", "", { "dependencies": { "@shikijs/primitive": "4.1.0", "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ=="],
"@shikijs/core": ["@shikijs/core@4.2.0", "", { "dependencies": { "@shikijs/primitive": "4.2.0", "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.1.0", "", { "dependencies": { "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.2.0", "", { "dependencies": { "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.1.0", "", { "dependencies": { "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.2.0", "", { "dependencies": { "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g=="],
"@shikijs/langs": ["@shikijs/langs@4.1.0", "", { "dependencies": { "@shikijs/types": "4.1.0" } }, "sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg=="],
"@shikijs/langs": ["@shikijs/langs@4.2.0", "", { "dependencies": { "@shikijs/types": "4.2.0" } }, "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ=="],
"@shikijs/primitive": ["@shikijs/primitive@4.1.0", "", { "dependencies": { "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw=="],
"@shikijs/primitive": ["@shikijs/primitive@4.2.0", "", { "dependencies": { "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA=="],
"@shikijs/themes": ["@shikijs/themes@4.1.0", "", { "dependencies": { "@shikijs/types": "4.1.0" } }, "sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw=="],
"@shikijs/themes": ["@shikijs/themes@4.2.0", "", { "dependencies": { "@shikijs/types": "4.2.0" } }, "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w=="],
"@shikijs/types": ["@shikijs/types@4.1.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA=="],
"@shikijs/types": ["@shikijs/types@4.2.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
@@ -1336,7 +1336,7 @@
"@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.7", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xj8gq/bjFABAh6qWPSDCYcY3kzQIm4b561C+YnHH4zGq8rOgzQ3Shk+JGlpUxSd41UGiO6FkLdUCtNX1FAeHgg=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.8", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-5cAM+KZC02sTqDt6NaLXyu50M/GNMd1eTzDVR8Lb0BBsVtu7RWHo47VPPEEv1vt3Yub6uzr+M5FHC+GtoT0USg=="],
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "tslib": "^2.6.2" } }, "sha512-Ussyv240JxwQP8AmkYdm26wGP/1I8QmIv0ZosgDJDlSzD73FEdj1BOpXMc06VrxX5KxTKhadFNomT2SWutUnpg=="],
@@ -1344,7 +1344,7 @@
"@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.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-3fya8i7GrJilQouk4cZJKdy5k8MWQBpjfXrRNaXDedH8r779tr0jcxyH3+yoTmsluc2+vF4S343yFbnvu8ExDQ=="],
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.7", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-ZAFvHXrEk6K180EVhmZVg8GU5pUH5BSFqRs27JW3j1qEFx9YyYwWFx17x/MHcjALYimGAji7qEOlF1++be+G5A=="],
"@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=="],
@@ -1662,7 +1662,7 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"ai": ["ai@6.0.195", "", { "dependencies": { "@ai-sdk/gateway": "3.0.123", "@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-IYZpuVz0boWbpIQYyinfWFrvQ1N0dG+EVB63it45B2YAU/MxxCnwz4zBswjYnPtHnJBedpMPNrwVbeczbl2GKg=="],
"ai": ["ai@6.0.196", "", { "dependencies": { "@ai-sdk/gateway": "3.0.124", "@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-2T45UeqKL4a11KQ14I5i1YYHOvCFrMF478E1k6PVjlQSGUvXSv4xrxIaQbUL4qgv91DADSbddwv3oR49pPAK3g=="],
"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=="],
@@ -1714,7 +1714,7 @@
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
"axios": ["axios@1.16.1", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A=="],
"axios": ["axios@1.17.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw=="],
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
@@ -1982,7 +1982,7 @@
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
"dompurify": ["dompurify@3.4.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA=="],
"dompurify": ["dompurify@3.4.8", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ=="],
"dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
@@ -1996,7 +1996,7 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"electron-to-chromium": ["electron-to-chromium@1.5.366", "", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="],
"electron-to-chromium": ["electron-to-chromium@1.5.367", "", {}, "sha512-4Mk/mrynCNQ+atY40D3UpmhLWB6AHMbYMlIrPhHcMF6x0L7O0b052FCAsxw1LlaR++UFuNg3D/A6XCuGDa0guQ=="],
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
@@ -2010,7 +2010,7 @@
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
"enhanced-resolve": ["enhanced-resolve@5.22.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww=="],
"enhanced-resolve": ["enhanced-resolve@5.22.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-0rxICaFZ7NQho/sHely2bvOPRP0Eu2B0NZ9zM54YvRvWMn7jfz3DmnOZDR9LlXDdDcqntAVc6Hfy4gr/tdH/Ag=="],
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
@@ -2676,7 +2676,7 @@
"object-treeify": ["object-treeify@1.1.33", "", {}, "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A=="],
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
"obug": ["obug@2.1.2", "", {}, "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg=="],
"omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
@@ -2962,7 +2962,7 @@
"shell-quote": ["shell-quote@1.8.4", "", {}, "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ=="],
"shiki": ["shiki@4.1.0", "", { "dependencies": { "@shikijs/core": "4.1.0", "@shikijs/engine-javascript": "4.1.0", "@shikijs/engine-oniguruma": "4.1.0", "@shikijs/langs": "4.1.0", "@shikijs/themes": "4.1.0", "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q=="],
"shiki": ["shiki@4.2.0", "", { "dependencies": { "@shikijs/core": "4.2.0", "@shikijs/engine-javascript": "4.2.0", "@shikijs/engine-oniguruma": "4.2.0", "@shikijs/langs": "4.2.0", "@shikijs/themes": "4.2.0", "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
@@ -3168,7 +3168,7 @@
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
"use-stick-to-bottom": ["use-stick-to-bottom@1.1.4", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2w/lydkrwhWMv1vCaEhYbzMDhgbwIodHpAHPV0/xKJErRkbjDEUe1EWmvr6Fwb+qhiERjc1EWgAEZaSaF69CpA=="],
"use-stick-to-bottom": ["use-stick-to-bottom@1.1.5", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-A6vhRIbuQqqkwR9CbbMEP9oZcNaAVknjYL/GR9BnmpSUxwR8ncPx7k4O2CrJriObORKIYgvAsmVWcE+moJDmVg=="],
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
@@ -3272,7 +3272,7 @@
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"@cline/cline-hub-webview/@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="],
"@cline/cline-hub-webview/@types/node": ["@types/node@24.13.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-5vtOqGQr4NJKeEzV441FcOi2MeG9UTWq9LqVLGneDdu4vlX17H8kQ2PA2UmNwCUGPVDj4oBjNhS7ReVEIWJJrg=="],
"@cline/code/lucide-react": ["lucide-react@0.564.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg=="],
@@ -3500,7 +3500,7 @@
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"@typescript-eslint/typescript-estree/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"@typescript-eslint/typescript-estree/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
@@ -3576,7 +3576,7 @@
"jest-util/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"jsonwebtoken/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"jsonwebtoken/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="],
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
@@ -3646,7 +3646,7 @@
"shadcn/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"sharp/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"sharp/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="],
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
@@ -3666,7 +3666,7 @@
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
"webview/@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="],
"webview/@types/node": ["@types/node@24.13.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-5vtOqGQr4NJKeEzV441FcOi2MeG9UTWq9LqVLGneDdu4vlX17H8kQ2PA2UmNwCUGPVDj4oBjNhS7ReVEIWJJrg=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
@@ -3676,7 +3676,7 @@
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"@cline/cline-hub-webview/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"@cline/cline-hub-webview/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@dotenvx/dotenvx/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
@@ -3798,7 +3798,7 @@
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"webview/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"webview/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+8
View File
@@ -1,5 +1,13 @@
# Cline SDK Changelog
## 0.0.47
- Added support for overriding the API base URL
- Enforced a production singleton Cline Hub so only one hub daemon runs, and a stale hub is respawned after an upgrade
- Allowed plugin chat commands to submit prompts to the agent
- Fixed truncation of structured tool operation result strings so oversized tool output stays within limits
- Stopped echoing the full command text in run_commands tool results
## 0.0.46
- Added support for configured agents as subagent tools
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/agents",
"version": "0.0.46",
"version": "0.0.47",
"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.46",
"version": "0.0.47",
"repository": {
"type": "git",
"url": "https://github.com/cline/cline",
+53
View File
@@ -18,6 +18,7 @@ vi.mock("./runtime/host/host", () => ({
import type { AgentResult } from "@cline/shared";
import { ClineCore } from "./ClineCore";
import { NoOpFeatureFlagsProvider } from "./services/feature-flags";
function createStartInput(): ClineCoreStartInput {
return {
@@ -331,6 +332,58 @@ describe("ClineCore", () => {
expect(coreTelemetry.capture).not.toHaveBeenCalled();
});
it("wraps an injected feature flags provider", async () => {
const host = {
runtimeAddress: undefined,
startSession: vi.fn(),
runTurn: vi.fn(),
getAccumulatedUsage: vi.fn(),
abort: vi.fn(),
stopSession: vi.fn(),
dispose: vi.fn(),
getSession: vi.fn(async () => undefined),
listSessions: vi.fn(),
deleteSession: vi.fn(),
readSessionMessages: vi.fn(),
subscribe: vi.fn(() => () => {}),
updateSessionModel: vi.fn(),
};
createRuntimeHostMock.mockResolvedValue(host);
const provider = new NoOpFeatureFlagsProvider();
const core = await ClineCore.create({ featureFlags: provider });
expect(core.featureFlags.getProvider()).toBe(provider);
await core.dispose();
});
it("uses a no-op feature flags provider by default", async () => {
const host = {
runtimeAddress: undefined,
startSession: vi.fn(),
runTurn: vi.fn(),
getAccumulatedUsage: vi.fn(),
abort: vi.fn(),
stopSession: vi.fn(),
dispose: vi.fn(),
getSession: vi.fn(async () => undefined),
listSessions: vi.fn(),
deleteSession: vi.fn(),
readSessionMessages: vi.fn(),
subscribe: vi.fn(() => () => {}),
updateSessionModel: vi.fn(),
};
createRuntimeHostMock.mockResolvedValue(host);
const core = await ClineCore.create();
expect(core.featureFlags.getProvider()).toBeInstanceOf(
NoOpFeatureFlagsProvider,
);
await core.dispose();
expect(host.dispose).toHaveBeenCalledTimes(1);
});
it("hydrates list rows through the core API", async () => {
const host = {
runtimeAddress: undefined,
+27 -2
View File
@@ -42,6 +42,11 @@ import type {
StartSessionInput,
StartSessionResult,
} from "./runtime/host/runtime-host";
import {
FeatureFlagsService,
NoOpFeatureFlagsProvider,
} from "./services/feature-flags";
import { resolveCoreDistinctId } from "./services/telemetry/distinct-id";
import type { CoreSessionEvent } from "./types/events";
import type { SessionHistoryRecord } from "./types/sessions";
@@ -86,6 +91,7 @@ export class ClineCore {
readonly runtimeAddress: string | undefined;
readonly automation: ClineCoreAutomationApi;
readonly settings: ClineCoreSettingsApi;
readonly featureFlags: FeatureFlagsService;
readonly pendingPrompts: PendingPromptsServiceApi;
private readonly host: RuntimeHost;
private readonly prepare: ClineCoreOptions["prepare"] | undefined;
@@ -109,6 +115,7 @@ export class ClineCore {
logger: BasicLogger | undefined,
telemetry: ITelemetryService | undefined,
distinctId: string | undefined,
featureFlags: FeatureFlagsService,
automationOptions:
| (ClineCoreAutomationOptions & { logger?: BasicLogger })
| undefined,
@@ -121,6 +128,7 @@ export class ClineCore {
this.logger = logger;
this.telemetry = telemetry;
this.distinctId = distinctId;
this.featureFlags = featureFlags;
this.settings = createClineCoreSettingsApi(host);
this.pendingPrompts = createClineCorePendingPromptsApi(host);
this.automation = new ClineCoreAutomationController(() => {
@@ -187,9 +195,20 @@ export class ClineCore {
* ```
*/
static async create(options: ClineCoreOptions = {}): Promise<ClineCore> {
const distinctId = resolveCoreDistinctId(options.distinctId);
const capabilities = normalizeRuntimeCapabilities(options.capabilities);
const host = await createRuntimeHost({ ...options, capabilities });
const normalizedOptions = { ...options, capabilities, distinctId };
const host = await createRuntimeHost(normalizedOptions);
const automationOptions = normalizeAutomationOptions(options.automation);
const featureFlags = new FeatureFlagsService({
provider: options.featureFlags ?? new NoOpFeatureFlagsProvider(),
telemetry: options.telemetry,
logger: options.logger,
context: {
distinctId,
clientName: options.clientName,
},
});
const core = new ClineCore(
host,
options.clientName,
@@ -198,7 +217,8 @@ export class ClineCore {
capabilities,
options.logger,
options.telemetry,
options.distinctId,
distinctId,
featureFlags,
automationOptions
? { ...automationOptions, logger: options.logger }
: undefined,
@@ -376,6 +396,11 @@ export class ClineCore {
await this.automationService?.dispose();
await this.host.dispose(...args);
} finally {
await this.featureFlags.dispose().catch((error) => {
this.logger?.error?.("Error disposing feature flags provider", {
error,
});
});
this.unsubscribeBootstrapCleanup();
const sessionIds = [...this.activeSessionBootstraps.keys()];
await Promise.allSettled(
@@ -3,6 +3,7 @@ import type {
AgentConfig,
AutomationEventEnvelope,
BasicLogger,
IFeatureFlagsProvider,
ITelemetryService,
} from "@cline/shared";
import type { CronEventSuppression } from "../cron/events/cron-event-ingress";
@@ -205,6 +206,12 @@ export interface ClineCoreOptions {
* If omitted, telemetry is a no-op.
*/
telemetry?: ITelemetryService;
/**
* Feature flags provider for this ClineCore instance. Core wraps the provider
* in a cached FeatureFlagsService and exposes it as `cline.featureFlags`.
* If omitted, Core uses a no-op provider with default flag values.
*/
featureFlags?: IFeatureFlagsProvider;
/**
* Optional structured logger for core-side operational diagnostics such as
* runtime-host selection and fallback decisions.
@@ -37,9 +37,20 @@ interface PluginTool {
interface PluginCommand {
name: string;
description?: string;
handler?: (input: string) => Promise<string>;
handler?: (
input: string,
) => Promise<PluginCommandResult> | PluginCommandResult;
}
// Keep this local mirror in sync with AgentExtensionCommandResult from @cline/shared.
// The sandbox bootstrap runs in an isolated process and avoids host package imports.
type PluginCommandResult =
| string
| {
reply?: string;
submitPrompt?: string;
};
interface PluginRule {
id: string;
content: string | (() => string | Promise<string>);
@@ -706,7 +717,7 @@ async function executeCommand(args: {
pluginId: string;
contributionId: string;
input: string;
}): Promise<string> {
}): Promise<PluginCommandResult> {
const state = getPlugin(args.pluginId);
const handler = state.handlers.commands.get(args.contributionId);
if (typeof handler !== "function") {
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type {
AgentConfig,
AgentExtensionCommandResult,
AgentExtensionAutomationEventType,
AgentExtensionRule,
AgentRuntimeHooks,
@@ -434,7 +435,7 @@ function registerCommands(
description: cd.description,
handler: async (input: string) => {
try {
return await sandbox.call<string>(
return await sandbox.call<AgentExtensionCommandResult>(
"executeCommand",
{
pluginId: descriptor.pluginId,
@@ -448,7 +449,7 @@ function registerCommands(
throw error;
}
await reinitialize();
return await sandbox.call<string>(
return await sandbox.call<AgentExtensionCommandResult>(
"executeCommand",
{
pluginId: descriptor.pluginId,
@@ -11,7 +11,7 @@ import {
createSkillsTool,
createWindowsShellTool,
} from "./definitions";
import { TimeoutError } from "./helpers";
import { RUN_COMMAND_QUERY_PREVIEW_LIMIT, TimeoutError } from "./helpers";
import { INPUT_ARG_CHAR_LIMIT } from "./schemas";
import type { SkillsExecutorWithMetadata } from "./types";
@@ -594,6 +594,108 @@ describe("default run_commands tool", () => {
);
});
it("keeps short command echoes unchanged in tool results", async () => {
const execute = vi.fn(
async (command: string | { command: string }) =>
`ran:${typeof command === "string" ? command : command.command}`,
);
const tool = createBashTool(execute);
const result = await tool.execute(
{ commands: ["git status --short"] },
{
agentId: "agent-1",
conversationId: "conv-1",
iteration: 1,
},
);
expect(result).toEqual([
{
query: "git status --short",
result: "ran:git status --short",
success: true,
},
]);
});
it("truncates long command echoes in tool results without affecting execution", async () => {
const execute = vi.fn(
async (command: string | { command: string }) =>
`ran:${typeof command === "string" ? command.length : command.command.length}`,
);
const tool = createBashTool(execute);
const largeSource = "x".repeat(14000);
const command = `cat > /app/eval.scm << 'EOF'\n${largeSource}\nEOF`;
const result = (await tool.execute(
{ commands: [command] },
{
agentId: "agent-1",
conversationId: "conv-1",
iteration: 1,
},
)) as Array<{ query: string; result: string; success: boolean }>;
// The executor still receives the full command
expect(execute).toHaveBeenCalledWith(
command,
process.cwd(),
expect.anything(),
);
expect(result[0].success).toBe(true);
expect(result[0].result).toBe(`ran:${command.length}`);
// The provider-facing echo is bounded and self-describing
expect(result[0].query.length).toBeLessThan(
RUN_COMMAND_QUERY_PREVIEW_LIMIT + 100,
);
expect(result[0].query).toContain("cat > /app/eval.scm << 'EOF'");
expect(result[0].query).toContain("command truncated");
expect(result[0].query).toContain("full command is in the tool call input");
});
it("truncates long command echoes on the error path too", async () => {
const execute = vi.fn(async () => {
throw new Error("boom");
});
const tool = createBashTool(execute);
const command = `cat > /app/big.txt << 'EOF'\n${"y".repeat(10000)}\nEOF`;
const result = (await tool.execute(
{ commands: [command] },
{
agentId: "agent-1",
conversationId: "conv-1",
iteration: 1,
},
)) as Array<{ query: string; success: boolean; error?: string }>;
expect(result[0].success).toBe(false);
expect(result[0].error).toContain("boom");
expect(result[0].query.length).toBeLessThan(
RUN_COMMAND_QUERY_PREVIEW_LIMIT + 100,
);
expect(result[0].query).toContain("command truncated");
});
it("truncates long command echoes for the structured windows shell tool", async () => {
const execute = vi.fn(async () => "ok");
const tool = createWindowsShellTool(execute);
const command = `powershell -Command "${"z".repeat(9000)}"`;
const result = (await tool.execute({ commands: [command] } as never, {
agentId: "agent-1",
conversationId: "conv-1",
iteration: 1,
})) as Array<{ query: string; success: boolean }>;
expect(result[0].success).toBe(true);
expect(result[0].query.length).toBeLessThan(
RUN_COMMAND_QUERY_PREVIEW_LIMIT + 100,
);
expect(result[0].query).toContain("command truncated");
});
it("emits timeout telemetry without leaking raw command data", async () => {
const execute = vi.fn(
async (): Promise<string> =>
@@ -16,7 +16,7 @@ import { getToolContextTelemetry } from "../../services/telemetry/tool-context";
import {
formatError,
formatReadFileQuery,
formatRunCommandQuery,
formatRunCommandQueryPreview,
getEditorSizeError,
getReadFileRangeError,
normalizeRunCommandsInput,
@@ -310,6 +310,7 @@ export function createBashTool(
return Promise.all(
commands.map(async (command: string): Promise<ToolOperationResult> => {
const startedAt = Date.now();
const query = formatRunCommandQueryPreview(command);
try {
const output = await withTimeout(
executor(command, cwd, context),
@@ -317,7 +318,7 @@ export function createBashTool(
`Command timed out after ${timeoutMs}ms`,
);
return {
query: command,
query,
result: output,
success: true,
};
@@ -332,7 +333,7 @@ export function createBashTool(
}
const msg = formatError(error);
return {
query: command,
query,
result: "",
error: `Command failed: ${msg}`,
success: false,
@@ -376,6 +377,7 @@ export function createWindowsShellTool(
return Promise.all(
commands.map(async (command): Promise<ToolOperationResult> => {
const startedAt = Date.now();
const query = formatRunCommandQueryPreview(command);
try {
const output = await withTimeout(
executor(command, cwd, context),
@@ -383,7 +385,7 @@ export function createWindowsShellTool(
`Command timed out after ${timeoutMs}ms`,
);
return {
query: formatRunCommandQuery(command),
query,
result: output,
success: true,
};
@@ -398,7 +400,7 @@ export function createWindowsShellTool(
}
const msg = formatError(error);
return {
query: formatRunCommandQuery(command),
query,
result: "",
error: `Command failed: ${msg}`,
success: false,
@@ -124,3 +124,27 @@ export function formatRunCommandQuery(
);
return `${command.command} ${renderedArgs.join(" ")}`;
}
/**
* Max characters of the executed command echoed back in the tool result's
* `query` field. The full command already exists in the assistant tool-call
* input, so repeating it in the result only duplicates tokens in the
* provider request (expensive for large heredoc/file-generation commands).
*/
export const RUN_COMMAND_QUERY_PREVIEW_LIMIT = 200;
/**
* Bound the command echo placed in a provider-facing tool result.
* Short commands pass through unchanged; long commands keep a short
* prefix plus a truncation note so the result is still identifiable.
*/
export function formatRunCommandQueryPreview(
command: string | StructuredCommandInput,
): string {
const rendered = formatRunCommandQuery(command);
if (rendered.length <= RUN_COMMAND_QUERY_PREVIEW_LIMIT) {
return rendered;
}
const truncatedChars = rendered.length - RUN_COMMAND_QUERY_PREVIEW_LIMIT;
return `${rendered.slice(0, RUN_COMMAND_QUERY_PREVIEW_LIMIT)} ... [command truncated: ${truncatedChars} more chars; full command is in the tool call input]`;
}
+14
View File
@@ -12,6 +12,7 @@ export type {
AgentEvent,
AgentExtension as AgentPlugin, // Public-facing alias for extensions
AgentExtensionCommand,
AgentExtensionCommandResult,
AgentExtensionCommand as AgentPluginCommand,
AgentHooks,
AgentMode,
@@ -32,9 +33,15 @@ export type {
ClineAccountActionRequest,
ConnectorHookEvent,
ContentBlock,
FeatureFlag,
FeatureFlagPayload,
FeatureFlagsAndPayloads,
FeatureFlagsContext,
FeatureFlagsSettings,
FileContent,
GetProviderModelsActionRequest,
HookSessionContext,
IFeatureFlagsProvider,
ImageContent,
ITelemetryService,
ListProvidersActionRequest,
@@ -81,6 +88,8 @@ export {
createContributionRegistry,
createTool,
emptyWorkspaceManifest,
FEATURE_FLAGS,
FeatureFlagDefaultValue,
formatDisplayUserInput,
noopBasicLogger,
normalizeSdkError,
@@ -434,6 +443,11 @@ export {
type DesktopToolApprovalOptions,
requestDesktopToolApproval,
} from "./runtime/tools/tool-approval";
export {
FeatureFlagsService,
type FeatureFlagsServiceOptions,
NoOpFeatureFlagsProvider,
} from "./services/feature-flags";
export type { GlobalSettings } from "./services/global-settings";
export {
filterDisabledPluginPaths,
@@ -0,0 +1,115 @@
import type { IFeatureFlagsProvider } from "@cline/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { FeatureFlagsService } from "./FeatureFlagsService";
const TEST_BOOLEAN_FLAG = "test_boolean_flag";
const TEST_PAYLOAD_FLAG = "test_payload_flag";
function createProvider(
overrides: Partial<IFeatureFlagsProvider> = {},
): IFeatureFlagsProvider {
return {
getAllFlagsAndPayloads: vi.fn(async () => ({
featureFlags: {
[TEST_BOOLEAN_FLAG]: true,
},
featureFlagPayloads: {
[TEST_PAYLOAD_FLAG]: 1234,
},
})),
enabled: true,
getSettings: vi.fn(() => ({ enabled: true, timeoutMs: 1000 })),
dispose: vi.fn(async () => {}),
...overrides,
};
}
describe("FeatureFlagsService", () => {
beforeEach(() => {
vi.useRealTimers();
});
it("polls provider values into the cache", async () => {
const provider = createProvider();
const telemetry = { capture: vi.fn() };
const service = new FeatureFlagsService({
provider,
telemetry: telemetry as never,
context: { distinctId: "machine-1", clientName: "unit-test" },
});
await service.poll("user-1");
expect(provider.getAllFlagsAndPayloads).toHaveBeenCalledWith({
flagKeys: undefined,
context: {
distinctId: "machine-1",
clientName: "unit-test",
userId: "user-1",
},
});
expect(service.getBooleanFlagEnabled(TEST_BOOLEAN_FLAG)).toBe(true);
expect(service.getFlagPayload(TEST_PAYLOAD_FLAG)).toBe(1234);
expect(telemetry.capture).toHaveBeenCalledWith({
event: "$feature_flag_called",
properties: {
$feature_flag: TEST_BOOLEAN_FLAG,
$feature_flag_response: true,
},
});
});
it("skips polling while the cache is fresh and user context is unchanged", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-10T10:00:00Z"));
const provider = createProvider();
const service = new FeatureFlagsService({ provider });
await service.poll("user-1");
expect(provider.getAllFlagsAndPayloads).toHaveBeenCalledTimes(1);
await service.poll("user-1");
expect(provider.getAllFlagsAndPayloads).toHaveBeenCalledTimes(1);
});
it("polls only once if two calls are made simultaneously with the same user context", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-10T10:00:00Z"));
const provider = createProvider();
const service = new FeatureFlagsService({ provider });
await Promise.all([service.poll("user-1"), service.poll("user-1")]);
expect(provider.getAllFlagsAndPayloads).toHaveBeenCalledTimes(1);
});
it("re-polls when the user context changes within the cache ttl", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-10T10:00:00Z"));
const provider = createProvider();
const service = new FeatureFlagsService({ provider });
await service.poll("user-1");
await service.poll("user-2");
expect(provider.getAllFlagsAndPayloads).toHaveBeenCalledTimes(2);
});
it("returns false or undefined before polling", () => {
const service = new FeatureFlagsService({ provider: createProvider() });
expect(service.getBooleanFlagEnabled(TEST_BOOLEAN_FLAG)).toBe(false);
expect(service.getFlagPayload(TEST_PAYLOAD_FLAG)).toBeUndefined();
});
it("disposes the provider", async () => {
const provider = createProvider();
const service = new FeatureFlagsService({ provider });
await service.dispose();
expect(provider.dispose).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,167 @@
import type {
BasicLogger,
FeatureFlagPayload,
FeatureFlagsAndPayloads,
FeatureFlagsContext,
IFeatureFlagsProvider,
ITelemetryService,
} from "@cline/shared";
import {
FEATURE_FLAGS,
type FeatureFlag,
FeatureFlagDefaultValue,
} from "@cline/shared";
import { CORE_TELEMETRY_EVENTS } from "../..";
const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000;
type CacheInfo = {
updateTime: number;
userId: string | null;
flagsPayload?: FeatureFlagsAndPayloads;
};
export interface FeatureFlagsServiceOptions {
provider: IFeatureFlagsProvider;
telemetry?: ITelemetryService;
logger?: BasicLogger;
cacheTtlMs?: number;
context?: FeatureFlagsContext;
}
export class FeatureFlagsService {
private readonly provider: IFeatureFlagsProvider;
private readonly telemetry?: ITelemetryService;
private readonly logger?: BasicLogger;
private readonly cacheTtlMs: number;
private context: FeatureFlagsContext;
private cache: Map<FeatureFlag, FeatureFlagPayload | undefined> = new Map();
private cacheInfo: CacheInfo = { updateTime: 0, userId: null };
constructor(options: FeatureFlagsServiceOptions) {
this.provider = options.provider;
this.telemetry = options.telemetry;
this.logger = options.logger;
this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
this.context = { ...(options.context ?? {}) };
}
setContext(context: FeatureFlagsContext): void {
this.context = { ...context };
}
async poll(
userId: string | null = this.context.userId ?? null,
): Promise<void> {
const timeNow = Date.now();
if (timeNow - this.cacheInfo.updateTime < this.cacheTtlMs) {
if (this.cacheInfo.userId === userId) {
return;
}
}
const previousCacheInfo = this.cacheInfo;
this.cacheInfo = { updateTime: timeNow, userId: userId || null };
try {
const values = await this.provider.getAllFlagsAndPayloads({
flagKeys: FEATURE_FLAGS.length > 0 ? FEATURE_FLAGS : undefined,
context: { ...this.context, userId },
});
if (this.cacheInfo.userId !== userId) {
// A new poll has started with a different userId, so we should not update the cache with the results of this poll
return;
}
this.cacheInfo.flagsPayload = values;
const nextCache = new Map<FeatureFlag, FeatureFlagPayload | undefined>();
for (const flag of this.getReturnedFlagKeys(values)) {
const payload = this.getFeatureFlag(flag);
nextCache.set(flag, payload ?? false);
}
this.cache = nextCache;
} catch (error) {
if (this.cacheInfo.userId !== userId) {
// A new poll has started with a different userId, so we should not update the cache with the results of this poll
return;
}
this.cacheInfo = previousCacheInfo.updateTime
? previousCacheInfo
: { updateTime: 0, userId: null };
this.logger?.error?.("Error polling SDK feature flags", { error });
throw error;
}
}
private getReturnedFlagKeys(
values: FeatureFlagsAndPayloads | undefined,
): FeatureFlag[] {
return [
...new Set([
...Object.keys(values?.featureFlags ?? {}),
...Object.keys(values?.featureFlagPayloads ?? {}),
]),
];
}
private getFeatureFlag(
flagName: FeatureFlag,
): FeatureFlagPayload | undefined {
try {
const payload =
this.cacheInfo.flagsPayload?.featureFlagPayloads?.[flagName];
const flagValue = this.cacheInfo.flagsPayload?.featureFlags?.[flagName];
const value =
payload ?? flagValue ?? FeatureFlagDefaultValue[flagName] ?? undefined;
if (!this.cache.has(flagName) || this.cache.get(flagName) !== value) {
this.telemetry?.capture({
event: CORE_TELEMETRY_EVENTS.FEATURE_FLAGS.FLAG_CALLED,
properties: {
$feature_flag: flagName,
$feature_flag_response: flagValue,
},
});
}
return value;
} catch (error) {
this.logger?.error?.(`Error checking SDK feature flag ${flagName}`, {
error,
});
return FeatureFlagDefaultValue[flagName] ?? false;
}
}
getBooleanFlagEnabled(flagName: FeatureFlag): boolean {
return this.cache.get(flagName) === true;
}
getFlagPayload(flagName: FeatureFlag): FeatureFlagPayload | undefined {
return this.cache.get(flagName) ?? FeatureFlagDefaultValue[flagName];
}
getProvider(): IFeatureFlagsProvider {
return this.provider;
}
get enabled(): boolean {
return this.provider.enabled;
}
getSettings() {
return this.provider.getSettings();
}
test(flagName: FeatureFlag, value: boolean): void {
if (process.env.NODE_ENV === "test" || process.env.IS_TEST === "true") {
this.cache.set(flagName, value);
}
}
async dispose(): Promise<void> {
await this.provider.dispose();
}
}
@@ -0,0 +1,5 @@
export {
FeatureFlagsService,
type FeatureFlagsServiceOptions,
} from "./FeatureFlagsService";
export { NoOpFeatureFlagsProvider } from "./providers";
@@ -0,0 +1,19 @@
import type {
FeatureFlagsAndPayloads,
FeatureFlagsSettings,
IFeatureFlagsProvider,
} from "@cline/shared";
export class NoOpFeatureFlagsProvider implements IFeatureFlagsProvider {
async getAllFlagsAndPayloads(): Promise<FeatureFlagsAndPayloads> {
return {};
}
enabled = false;
getSettings(): FeatureFlagsSettings {
return { enabled: false, timeoutMs: 1000 };
}
async dispose(): Promise<void> {}
}
@@ -74,6 +74,9 @@ export const CORE_TELEMETRY_EVENTS = {
ERROR: SDK_ERROR_TELEMETRY_EVENT,
TOOL_TIMEOUT: "sdk.tool_timeout",
},
FEATURE_FLAGS: {
FLAG_CALLED: "$feature_flag_called",
},
} as const;
export interface RunCommandsTimeoutTelemetryProperties {
@@ -1,5 +1,11 @@
import type { Message } from "@cline/shared";
import {
type AiSdkFormatterMessage,
formatMessagesForAiSdk,
type Message,
type ToolResultContent,
} from "@cline/shared";
import { describe, expect, it } from "vitest";
import { messagesToAgentMessages } from "../../runtime/config/agent-message-codec";
import { MessageBuilder } from "./message-builder";
describe("MessageBuilder", () => {
@@ -373,3 +379,296 @@ describe("MessageBuilder", () => {
]);
});
});
/**
* Regression coverage for the real `ToolOperationResult[]` shape emitted by
* the default Cline tools (run_commands, read_files, search_codebase).
*
* The runtime stores these structured results directly as the tool_result
* `content` array (see `agentPartToContentBlock` in
* `runtime/config/agent-message-codec.ts`): the entries are plain
* `{query, result, success, ...}` objects with no `type` discriminator, so
* they bypass the text/file-entry truncation paths unless MessageBuilder
* handles them explicitly.
*/
describe("MessageBuilder with structured ToolOperationResult content", () => {
const MIDDLE_SENTINEL = "__MIDDLE_SENTINEL_MUST_BE_TRUNCATED__";
const HEAD_MARKER = "__HEAD_MARKER__";
const TAIL_MARKER = "__TAIL_MARKER__";
interface ToolOperationResultLike {
query: string;
result: unknown;
error?: string;
success: boolean;
duration?: number;
}
function hugeText(size = 400_000): string {
const fillerLength = Math.floor(
(size -
MIDDLE_SENTINEL.length -
HEAD_MARKER.length -
TAIL_MARKER.length) /
2,
);
const filler = "x".repeat(fillerLength);
return `${HEAD_MARKER}${filler}${MIDDLE_SENTINEL}${filler}${TAIL_MARKER}`;
}
function toolUseMessage(
id: string,
name: string,
input: Record<string, unknown>,
): Message {
return {
role: "assistant",
content: [{ type: "tool_use", id, name, input }],
};
}
function structuredToolResultMessage(
toolUseId: string,
name: string,
operations: ToolOperationResultLike[],
): Message {
return {
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolUseId,
name,
// The runtime casts ToolOperationResult[] straight into the
// content array; mirror that here.
content: operations as unknown as ToolResultContent["content"],
},
],
};
}
function sumStringBytes(value: unknown): number {
if (typeof value === "string") {
return Buffer.byteLength(value, "utf8");
}
if (Array.isArray(value)) {
return value.reduce<number>((sum, item) => sum + sumStringBytes(item), 0);
}
if (value !== null && typeof value === "object") {
return Object.values(value).reduce<number>(
(sum, item) => sum + sumStringBytes(item),
0,
);
}
return 0;
}
it("truncates a huge nested `result` string in run_commands structured output", () => {
const builder = new MessageBuilder();
const messages: Message[] = [
toolUseMessage("call_1", "run_commands", {
commands: ["cat big.log"],
}),
structuredToolResultMessage("call_1", "run_commands", [
{
query: "cat big.log",
result: hugeText(),
success: true,
duration: 1234,
},
]),
];
const rawSerializedLength = JSON.stringify(messages).length;
const result = builder.buildForApi(messages);
const serialized = JSON.stringify(result);
expect(rawSerializedLength).toBeGreaterThan(390_000);
expect(serialized.length).toBeLessThan(120_000);
expect(serialized).not.toContain(MIDDLE_SENTINEL);
// Middle truncation must preserve the head and tail of the output.
expect(serialized).toContain(HEAD_MARKER);
expect(serialized).toContain(TAIL_MARKER);
});
it("truncates a huge nested `query` string in run_commands structured output", () => {
const builder = new MessageBuilder();
const messages: Message[] = [
toolUseMessage("call_1", "run_commands", {
commands: ["bash -c '...giant heredoc...'"],
}),
structuredToolResultMessage("call_1", "run_commands", [
{
query: hugeText(),
result: "ok",
success: true,
},
]),
];
const result = builder.buildForApi(messages);
const serialized = JSON.stringify(result);
expect(serialized.length).toBeLessThan(120_000);
expect(serialized).not.toContain(MIDDLE_SENTINEL);
expect(serialized).toContain(HEAD_MARKER);
expect(serialized).toContain(TAIL_MARKER);
});
it("truncates a huge file payload in read_files structured output", () => {
const builder = new MessageBuilder();
const messages: Message[] = [
toolUseMessage("call_1", "read_files", {
files: [{ path: "/tmp/big.txt" }],
}),
structuredToolResultMessage("call_1", "read_files", [
{
query: "/tmp/big.txt",
result: hugeText(),
success: true,
},
]),
];
const result = builder.buildForApi(messages);
const serialized = JSON.stringify(result);
expect(serialized.length).toBeLessThan(120_000);
expect(serialized).not.toContain(MIDDLE_SENTINEL);
expect(serialized).toContain(HEAD_MARKER);
expect(serialized).toContain(TAIL_MARKER);
// The latest read of a file must not be rewritten as outdated.
expect(serialized).not.toContain("[outdated");
});
it("truncates a huge fetch_web_content structured result", () => {
// The web-fetch executor allows responses up to 5MB, so this tool must
// be covered by the truncation targets like the other bulk-output tools.
const builder = new MessageBuilder();
const messages: Message[] = [
toolUseMessage("call_1", "fetch_web_content", {
requests: [{ url: "https://example.com/huge-page" }],
}),
structuredToolResultMessage("call_1", "fetch_web_content", [
{
query: "https://example.com/huge-page",
result: hugeText(),
success: true,
},
]),
];
const result = builder.buildForApi(messages);
const serialized = JSON.stringify(result);
expect(serialized.length).toBeLessThan(120_000);
expect(serialized).not.toContain(MIDDLE_SENTINEL);
expect(serialized).toContain(HEAD_MARKER);
expect(serialized).toContain(TAIL_MARKER);
});
it("applies the aggregate text budget to nested structured strings", () => {
const builder = new MessageBuilder(
50_000,
new Set(["run_commands", "read_files"]),
100_000,
);
// Five results of ~40k chars each: every nested string is below the
// per-result limit, but the aggregate (~200k) exceeds the budget.
const messages: Message[] = [];
for (let i = 0; i < 5; i++) {
const name = i % 2 === 0 ? "run_commands" : "read_files";
messages.push(
toolUseMessage(`call_${i}`, name, { commands: [`cmd ${i}`] }),
structuredToolResultMessage(`call_${i}`, name, [
{
query: `cmd ${i}`,
result: `chunk_${i}_`.repeat(4_000),
success: true,
},
]),
);
}
const result = builder.buildForApi(messages);
let toolResultStringBytes = 0;
for (const message of result) {
if (!Array.isArray(message.content)) {
continue;
}
for (const block of message.content) {
if (block.type === "tool_result") {
toolResultStringBytes += sumStringBytes(block.content);
}
}
}
expect(toolResultStringBytes).toBeLessThanOrEqual(100_000);
expect(JSON.stringify(result)).toContain("provider request budget");
});
it("does not mutate the original structured tool results", () => {
const builder = new MessageBuilder(
10_000,
new Set(["run_commands"]),
20_000,
);
const messages: Message[] = [
toolUseMessage("call_1", "run_commands", { commands: ["cat a.log"] }),
structuredToolResultMessage("call_1", "run_commands", [
{
query: "cat a.log",
result: hugeText(50_000),
success: true,
},
]),
toolUseMessage("call_2", "run_commands", { commands: ["cat b.log"] }),
structuredToolResultMessage("call_2", "run_commands", [
{
query: "cat b.log",
result: hugeText(50_000),
success: true,
},
]),
];
const snapshot = structuredClone(messages);
const result = builder.buildForApi(messages);
expect(messages).toEqual(snapshot);
expect(JSON.stringify(result)).not.toContain(MIDDLE_SENTINEL);
});
it("keeps huge nested strings out of provider-formatted AI SDK messages", () => {
const builder = new MessageBuilder();
const messages: Message[] = [
toolUseMessage("call_1", "run_commands", {
commands: ["cat big.log"],
}),
structuredToolResultMessage("call_1", "run_commands", [
{
query: "cat big.log",
result: hugeText(),
success: true,
},
]),
];
const built = builder.buildForApi(messages);
const agentMessages = messagesToAgentMessages(built);
const aiSdkMessages = formatMessagesForAiSdk(
undefined,
agentMessages.map(({ role, content }) => ({
role,
content,
})) as unknown as AiSdkFormatterMessage[],
);
const serialized = JSON.stringify(aiSdkMessages);
expect(serialized.length).toBeLessThan(130_000);
expect(serialized).not.toContain(MIDDLE_SENTINEL);
expect(serialized).toContain(HEAD_MARKER);
expect(serialized).toContain(TAIL_MARKER);
});
});
@@ -27,6 +27,7 @@ const TARGET_TOOL_NAMES = new Set([
"search_codebase",
"bash",
"run_commands",
"fetch_web_content",
]);
const READ_TOOL_NAMES = new Set(["read", "read_files"]);
const OUTDATED_FILE_CONTENT = "[outdated - see the latest file content]";
@@ -765,14 +766,56 @@ export class MessageBuilder {
const next = this.truncateMiddle(entry.content);
return next === entry.content ? entry : { ...entry, content: next };
}
if (entry.type !== "text") {
return entry;
if (entry.type === "text") {
const next = this.truncateMiddle(entry.text);
return next === entry.text ? entry : { ...entry, text: next };
}
const next = this.truncateMiddle(entry.text);
return next === entry.text ? entry : { ...entry, text: next };
if (isStructuredToolResultEntry(entry)) {
return this.truncateNestedStrings(entry) as typeof entry;
}
return entry;
});
}
/**
* Deep-truncates string values inside structured tool outputs (e.g.
* `ToolOperationResult[]` from run_commands/read_files), which carry the
* payload in untyped `{query, result, ...}` fields rather than text
* blocks. Image blocks are left intact so base64 payloads survive.
*/
private truncateNestedStrings(value: unknown): unknown {
if (typeof value === "string") {
return this.truncateMiddle(value);
}
if (Array.isArray(value)) {
let changed = false;
const next = value.map((item) => {
const out = this.truncateNestedStrings(item);
if (out !== item) {
changed = true;
}
return out;
});
return changed ? next : value;
}
if (value !== null && typeof value === "object") {
if (isImageContentLike(value)) {
return value;
}
let changed = false;
const next: Record<string, unknown> = {};
for (const [key, item] of Object.entries(value)) {
const out = this.truncateNestedStrings(item);
if (out !== item) {
changed = true;
}
next[key] = out;
}
return changed ? next : value;
}
return value;
}
private truncateMiddle(text: string): string {
return truncateMiddleByChars(
text,
@@ -852,6 +895,8 @@ export class MessageBuilder {
total += utf8ByteLength(entry.text);
} else if (entry.type === "file") {
total += utf8ByteLength(entry.content);
} else if (isStructuredToolResultEntry(entry)) {
total += countNestedStringBytes(entry);
}
}
}
@@ -904,6 +949,8 @@ export class MessageBuilder {
entry.content = value;
},
});
} else if (isStructuredToolResultEntry(entry)) {
collectNestedStringCandidates(entry, candidates);
}
}
}
@@ -971,6 +1018,110 @@ function cloneContentBlockForMutation(block: ContentBlock): ContentBlock {
}
return {
...block,
content: block.content.map((entry) => ({ ...entry })),
// Structured entries can nest the payload strings arbitrarily deep, so
// a shallow copy would leak budget-truncation mutations back into the
// original conversation history.
content: block.content.map((entry) =>
isStructuredToolResultEntry(entry)
? (deepCloneJsonLike(entry) as typeof entry)
: { ...entry },
),
};
}
/**
* True for tool_result content entries that are not the typed text/image/file
* blocks — i.e. structured tool outputs such as `ToolOperationResult[]`
* entries that the runtime stores directly in the content array.
*/
function isStructuredToolResultEntry(entry: unknown): boolean {
if (entry === null || typeof entry !== "object") {
return false;
}
const type = (entry as { type?: unknown }).type;
return type !== "text" && type !== "image" && type !== "file";
}
function isImageContentLike(value: object): boolean {
return (value as { type?: unknown }).type === "image";
}
function countNestedStringBytes(value: unknown): number {
if (typeof value === "string") {
return utf8ByteLength(value);
}
if (Array.isArray(value)) {
let total = 0;
for (const item of value) {
total += countNestedStringBytes(item);
}
return total;
}
if (value !== null && typeof value === "object") {
if (isImageContentLike(value)) {
return 0;
}
let total = 0;
for (const item of Object.values(value)) {
total += countNestedStringBytes(item);
}
return total;
}
return 0;
}
function collectNestedStringCandidates(
container: unknown,
candidates: TruncationCandidate[],
): void {
if (Array.isArray(container)) {
container.forEach((item, index) => {
if (typeof item === "string") {
candidates.push({
byteLength: utf8ByteLength(item),
get: () => container[index] as string,
set: (value) => {
container[index] = value;
},
});
} else {
collectNestedStringCandidates(item, candidates);
}
});
return;
}
if (container !== null && typeof container === "object") {
if (isImageContentLike(container)) {
return;
}
const record = container as Record<string, unknown>;
for (const key of Object.keys(record)) {
const item = record[key];
if (typeof item === "string") {
candidates.push({
byteLength: utf8ByteLength(item),
get: () => record[key] as string,
set: (value) => {
record[key] = value;
},
});
} else {
collectNestedStringCandidates(item, candidates);
}
}
}
}
function deepCloneJsonLike(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(deepCloneJsonLike);
}
if (value !== null && typeof value === "object") {
const out: Record<string, unknown> = {};
for (const [key, item] of Object.entries(value)) {
out[key] = deepCloneJsonLike(item);
}
return out;
}
return value;
}
+15
View File
@@ -1,9 +1,19 @@
export type {
AgentRunResult,
AgentRunStatus,
FeatureFlag,
FeatureFlagPayload,
FeatureFlagsAndPayloads,
FeatureFlagsContext,
FeatureFlagsSettings,
IFeatureFlagsProvider,
WorkspaceInfo,
WorkspaceManifest,
} from "@cline/shared";
export {
FEATURE_FLAGS,
FeatureFlagDefaultValue,
} from "@cline/shared";
export { ClineCore } from "./ClineCore";
export type {
ClineCoreListHistoryOptions,
@@ -103,6 +113,11 @@ export type {
SubprocessSandboxOptions,
} from "./runtime/tools/subprocess-sandbox";
export { SubprocessSandbox } from "./runtime/tools/subprocess-sandbox";
export {
FeatureFlagsService,
type FeatureFlagsServiceOptions,
NoOpFeatureFlagsProvider,
} from "./services/feature-flags";
export type { GlobalSettings } from "./services/global-settings";
export {
filterDisabledPluginPaths,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/llms",
"version": "0.0.46",
"version": "0.0.47",
"description": "Config-driven SDK for selecting, extending, and instantiating LLM providers and models",
"repository": {
"type": "git",
+234 -320
View File
@@ -14,7 +14,7 @@ export const GENERATED_PROVIDER_MODELS: {
version: number;
providers: Record<string, Record<string, ModelInfo>>;
} = {
version: 1781051263433,
version: 1781211653622,
providers: {
aihubmix: {
"glm-5v-turbo": {
@@ -3597,26 +3597,36 @@ export const GENERATED_PROVIDER_MODELS: {
name: "Z.AI GLM-4.7",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 40000,
capabilities: ["tools", "temperature"],
maxTokens: 40960,
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 2.25,
output: 2.75,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2026-01-10",
releaseDate: "2026-01-07",
},
"gpt-oss-120b": {
id: "gpt-oss-120b",
name: "GPT OSS 120B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 32768,
capabilities: ["tools", "reasoning", "temperature"],
maxTokens: 40960,
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.25,
output: 0.69,
input: 0.35,
output: 0.75,
cacheRead: 0,
cacheWrite: 0,
},
@@ -4241,58 +4251,27 @@ export const GENERATED_PROVIDER_MODELS: {
},
},
groq: {
"moonshotai/kimi-k2-instruct-0905": {
id: "moonshotai/kimi-k2-instruct-0905",
name: "Kimi K2 Instruct 0905",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 16384,
"openai/gpt-oss-safeguard-20b": {
id: "openai/gpt-oss-safeguard-20b",
name: "Safety GPT OSS 20B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 65536,
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
"prompt-cache",
],
pricing: {
input: 1,
output: 3,
cacheRead: 0.5,
input: 0.075,
output: 0.3,
cacheRead: 0.037,
cacheWrite: 0,
},
releaseDate: "2025-09-05",
family: "kimi",
},
"groq/compound": {
id: "groq/compound",
name: "Compound",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 8192,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-09-04",
family: "groq",
},
"groq/compound-mini": {
id: "groq/compound-mini",
name: "Compound Mini",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 8192,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-09-04",
family: "groq",
releaseDate: "2025-10-29",
family: "gpt-oss",
},
"openai/gpt-oss-120b": {
id: "openai/gpt-oss-120b",
@@ -4338,9 +4317,25 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-08-05",
family: "gpt-oss",
},
"qwen/qwen3-32b": {
id: "qwen/qwen3-32b",
name: "Qwen3-32B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 40960,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0.29,
output: 0.59,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-06-11",
family: "qwen",
},
"meta-llama/llama-4-scout-17b-16e-instruct": {
id: "meta-llama/llama-4-scout-17b-16e-instruct",
name: "Llama 4 Scout 17B",
name: "Llama 4 Scout 17B 16E",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 8192,
@@ -4354,41 +4349,9 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-04-05",
family: "llama",
},
"openai/gpt-oss-safeguard-20b": {
id: "openai/gpt-oss-safeguard-20b",
name: "Safety GPT OSS 20B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 65536,
capabilities: ["tools", "reasoning", "temperature", "prompt-cache"],
pricing: {
input: 0.075,
output: 0.3,
cacheRead: 0.037,
cacheWrite: 0,
},
releaseDate: "2025-03-05",
family: "gpt-oss",
},
"qwen/qwen3-32b": {
id: "qwen/qwen3-32b",
name: "Qwen3 32B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 40960,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0.29,
output: 0.59,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2024-12-23",
family: "qwen",
},
"llama-3.3-70b-versatile": {
id: "llama-3.3-70b-versatile",
name: "Llama 3.3 70B Versatile",
name: "Llama 3.3 70B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 32768,
@@ -4404,7 +4367,7 @@ export const GENERATED_PROVIDER_MODELS: {
},
"llama-3.1-8b-instant": {
id: "llama-3.1-8b-instant",
name: "Llama 3.1 8B Instant",
name: "Llama 3.1 8B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 131072,
@@ -11251,6 +11214,52 @@ export const GENERATED_PROVIDER_MODELS: {
},
},
openrouter: {
"~anthropic/claude-fable-latest": {
id: "~anthropic/claude-fable-latest",
name: "Claude Fable Latest",
contextWindow: 1000000,
maxInputTokens: 1000000,
maxTokens: 128000,
capabilities: [
"images",
"files",
"tools",
"reasoning",
"structured_output",
"prompt-cache",
],
pricing: {
input: 10,
output: 50,
cacheRead: 1,
cacheWrite: 12.5,
},
releaseDate: "2026-06-09",
family: "claude-fable",
},
"anthropic/claude-fable-5": {
id: "anthropic/claude-fable-5",
name: "Claude Fable 5",
contextWindow: 1000000,
maxInputTokens: 1000000,
maxTokens: 128000,
capabilities: [
"images",
"files",
"tools",
"reasoning",
"structured_output",
"prompt-cache",
],
pricing: {
input: 10,
output: 50,
cacheRead: 1,
cacheWrite: 12.5,
},
releaseDate: "2026-06-09",
family: "claude-fable",
},
"nex-agi/nex-n2-pro:free": {
id: "nex-agi/nex-n2-pro:free",
name: "Nex-N2-Pro (free)",
@@ -11325,10 +11334,10 @@ export const GENERATED_PROVIDER_MODELS: {
"prompt-cache",
],
pricing: {
input: 0.4,
output: 1.6,
cacheRead: 0.08,
cacheWrite: 0.5,
input: 0.32,
output: 1.28,
cacheRead: 0.064,
cacheWrite: 0.4,
},
releaseDate: "2026-06-02",
family: "qwen",
@@ -11377,29 +11386,6 @@ export const GENERATED_PROVIDER_MODELS: {
},
releaseDate: "2026-05-29",
},
"anthropic/claude-fable-5": {
id: "anthropic/claude-fable-5",
name: "Claude Fable 5",
contextWindow: 1000000,
maxInputTokens: 1000000,
maxTokens: 128000,
capabilities: [
"images",
"files",
"tools",
"reasoning",
"structured_output",
"prompt-cache",
],
pricing: {
input: 10,
output: 50,
cacheRead: 1,
cacheWrite: 12.5,
},
releaseDate: "2026-06-09",
family: "claude",
},
"anthropic/claude-opus-4.8": {
id: "anthropic/claude-opus-4.8",
name: "Claude Opus 4.8",
@@ -12137,22 +12123,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2026-04-21",
family: "kimi-k2.6",
},
"moonshotai/kimi-k2.6:free": {
id: "moonshotai/kimi-k2.6:free",
name: "Kimi K2.6 (free)",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["images", "tools", "reasoning"],
pricing: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2026-04-21",
family: "kimi-k2.6",
},
"qwen/qwen3.6-max-preview": {
id: "qwen/qwen3.6-max-preview",
name: "Qwen3.6 Max Preview",
@@ -12194,20 +12164,21 @@ export const GENERATED_PROVIDER_MODELS: {
"qwen/qwen3.6-35b-a3b": {
id: "qwen/qwen3.6-35b-a3b",
name: "Qwen3.6 35B-A3B",
contextWindow: 262140,
maxInputTokens: 262140,
maxTokens: 262140,
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: [
"images",
"tools",
"reasoning",
"structured_output",
"temperature",
"prompt-cache",
],
pricing: {
input: 0.14,
input: 0.15,
output: 1,
cacheRead: 0,
cacheRead: 0.05,
cacheWrite: 0,
},
releaseDate: "2026-04-17",
@@ -12347,9 +12318,9 @@ export const GENERATED_PROVIDER_MODELS: {
"google/gemma-4-31b-it": {
id: "google/gemma-4-31b-it",
name: "Gemma 4 31B IT",
contextWindow: 256000,
maxInputTokens: 256000,
maxTokens: 8192,
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: [
"images",
"tools",
@@ -12360,7 +12331,7 @@ export const GENERATED_PROVIDER_MODELS: {
],
pricing: {
input: 0.12,
output: 0.36,
output: 0.35,
cacheRead: 0.09,
cacheWrite: 0,
},
@@ -12428,28 +12399,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2026-04-01",
family: "trinity",
},
"z-ai/glm-5v-turbo": {
id: "z-ai/glm-5v-turbo",
name: "GLM-5V-Turbo",
contextWindow: 202752,
maxInputTokens: 202752,
maxTokens: 131072,
capabilities: [
"images",
"tools",
"reasoning",
"temperature",
"prompt-cache",
],
pricing: {
input: 1.2,
output: 4,
cacheRead: 0.24,
cacheWrite: 0,
},
releaseDate: "2026-04-01",
family: "glm",
},
"x-ai/grok-4.20": {
id: "x-ai/grok-4.20",
name: "Grok 4.20",
@@ -12536,19 +12485,20 @@ export const GENERATED_PROVIDER_MODELS: {
"minimax/minimax-m2.7": {
id: "minimax/minimax-m2.7",
name: "MiniMax-M2.7",
contextWindow: 196608,
maxInputTokens: 196608,
maxTokens: 196608,
contextWindow: 204800,
maxInputTokens: 204800,
maxTokens: 131072,
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
"prompt-cache",
],
pricing: {
input: 0.279,
output: 1.2,
cacheRead: 0,
input: 0.27,
output: 1.08,
cacheRead: 0.054,
cacheWrite: 0,
},
releaseDate: "2026-03-18",
@@ -12626,8 +12576,8 @@ export const GENERATED_PROVIDER_MODELS: {
"z-ai/glm-5-turbo": {
id: "z-ai/glm-5-turbo",
name: "GLM-5-Turbo",
contextWindow: 202752,
maxInputTokens: 202752,
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 131072,
capabilities: ["tools", "reasoning", "temperature", "prompt-cache"],
pricing: {
@@ -12645,7 +12595,12 @@ export const GENERATED_PROVIDER_MODELS: {
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["tools", "reasoning", "temperature"],
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.09,
output: 0.45,
@@ -13071,11 +13026,12 @@ export const GENERATED_PROVIDER_MODELS: {
"reasoning",
"structured_output",
"temperature",
"prompt-cache",
],
pricing: {
input: 0.15,
output: 1.15,
cacheRead: 0,
output: 0.9,
cacheRead: 0.05,
cacheWrite: 0,
},
releaseDate: "2026-02-12",
@@ -13596,7 +13552,7 @@ export const GENERATED_PROVIDER_MODELS: {
name: "GLM-4.6V",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 24000,
maxTokens: 32768,
capabilities: [
"images",
"tools",
@@ -13607,7 +13563,7 @@ export const GENERATED_PROVIDER_MODELS: {
pricing: {
input: 0.3,
output: 0.9,
cacheRead: 0.05,
cacheRead: 0.055,
cacheWrite: 0,
},
releaseDate: "2025-12-08",
@@ -14516,22 +14472,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-08-21",
family: "deepseek",
},
"nvidia/nemotron-nano-9b-v2": {
id: "nvidia/nemotron-nano-9b-v2",
name: "Nemotron Nano 9B v2",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 16384,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0.04,
output: 0.16,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-08-18",
family: "nemotron",
},
"nvidia/nemotron-nano-9b-v2:free": {
id: "nvidia/nemotron-nano-9b-v2:free",
name: "Nemotron Nano 9B V2 (free)",
@@ -14867,22 +14807,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-07-28",
family: "glm-air",
},
"z-ai/glm-4.5-air:free": {
id: "z-ai/glm-4.5-air:free",
name: "GLM 4.5 Air (free)",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 96000,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-07-28",
family: "glm-air",
},
"nvidia/llama-3.3-nemotron-super-49b-v1.5": {
id: "nvidia/llama-3.3-nemotron-super-49b-v1.5",
name: "Llama 3.3 Nemotron Super 49B v1.5",
@@ -14921,22 +14845,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-07-25",
family: "qwen",
},
"z-ai/glm-4-32b": {
id: "z-ai/glm-4-32b",
name: "GLM 4 32B ",
contextWindow: 128000,
maxInputTokens: 128000,
maxTokens: 128000,
capabilities: ["tools", "temperature"],
pricing: {
input: 0.1,
output: 0.1,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-07-24",
family: "glm",
},
"qwen/qwen3-coder": {
id: "qwen/qwen3-coder",
name: "Qwen3 Coder 480B A35B",
@@ -15544,8 +15452,8 @@ export const GENERATED_PROVIDER_MODELS: {
"deepseek/deepseek-chat-v3-0324": {
id: "deepseek/deepseek-chat-v3-0324",
name: "DeepSeek V3 0324",
contextWindow: 163840,
maxInputTokens: 163840,
contextWindow: 32768,
maxInputTokens: 32768,
maxTokens: 16384,
capabilities: [
"tools",
@@ -17744,7 +17652,7 @@ export const GENERATED_PROVIDER_MODELS: {
contextWindow: 1000000,
maxInputTokens: 1000000,
maxTokens: 500000,
capabilities: ["tools", "reasoning", "temperature"],
capabilities: ["tools", "temperature"],
pricing: {
input: 2.5,
output: 7.5,
@@ -17784,8 +17692,8 @@ export const GENERATED_PROVIDER_MODELS: {
"prompt-cache",
],
pricing: {
input: 2.1,
output: 4.4,
input: 1.74,
output: 3.48,
cacheRead: 0.2,
cacheWrite: 0,
},
@@ -17821,10 +17729,16 @@ export const GENERATED_PROVIDER_MODELS: {
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 131072,
capabilities: ["images", "tools", "reasoning", "temperature"],
capabilities: [
"images",
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.2,
output: 0.5,
input: 0.39,
output: 0.97,
cacheRead: 0,
cacheWrite: 0,
},
@@ -17874,6 +17788,28 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2026-03-18",
family: "minimax",
},
"Qwen/Qwen3.5-9B": {
id: "Qwen/Qwen3.5-9B",
name: "Qwen3.5 9B",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 65536,
capabilities: [
"images",
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.17,
output: 0.25,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2026-03-03",
family: "qwen",
},
"Qwen/Qwen3.5-397B-A17B": {
id: "Qwen/Qwen3.5-397B-A17B",
name: "Qwen3.5 397B A17B",
@@ -17890,53 +17826,26 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2026-02-16",
family: "qwen",
},
"MiniMaxAI/MiniMax-M2.5": {
id: "MiniMaxAI/MiniMax-M2.5",
name: "MiniMax-M2.5",
contextWindow: 204800,
maxInputTokens: 204800,
"zai-org/GLM-5": {
id: "zai-org/GLM-5",
name: "GLM-5",
contextWindow: 202752,
maxInputTokens: 202752,
maxTokens: 131072,
capabilities: ["tools", "reasoning", "temperature", "prompt-cache"],
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.3,
output: 1.2,
cacheRead: 0.06,
cacheWrite: 0,
},
releaseDate: "2026-02-12",
family: "minimax",
},
"Qwen/Qwen3-Coder-Next-FP8": {
id: "Qwen/Qwen3-Coder-Next-FP8",
name: "Qwen3 Coder Next FP8",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0.5,
output: 1.2,
input: 1,
output: 3.2,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2026-02-03",
family: "qwen",
},
"moonshotai/Kimi-K2.5": {
id: "moonshotai/Kimi-K2.5",
name: "Kimi K2.5",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["images", "tools", "reasoning", "temperature"],
pricing: {
input: 0.5,
output: 2.8,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2026-01-27",
family: "kimi",
releaseDate: "2026-02-11",
family: "glm",
},
"essentialai/Rnj-1-Instruct": {
id: "essentialai/Rnj-1-Instruct",
@@ -17954,22 +17863,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-12-05",
family: "rnj",
},
"deepseek-ai/DeepSeek-V3-1": {
id: "deepseek-ai/DeepSeek-V3-1",
name: "DeepSeek V3.1",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 131072,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 0.6,
output: 1.7,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-08-21",
family: "deepseek",
},
"openai/gpt-oss-120b": {
id: "openai/gpt-oss-120b",
name: "GPT OSS 120B",
@@ -17986,13 +17879,34 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-08-05",
family: "gpt-oss",
},
"openai/gpt-oss-20b": {
id: "openai/gpt-oss-20b",
name: "GPT OSS 20B",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 131072,
capabilities: [
"tools",
"reasoning",
"structured_output",
"temperature",
],
pricing: {
input: 0.05,
output: 0.2,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-08-05",
family: "gpt-oss",
},
"Qwen/Qwen3-235B-A22B-Instruct-2507-tput": {
id: "Qwen/Qwen3-235B-A22B-Instruct-2507-tput",
name: "Qwen3 235B A22B Instruct 2507 FP8",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["tools", "reasoning", "temperature"],
capabilities: ["tools", "temperature"],
pricing: {
input: 0.2,
output: 0.6,
@@ -18002,38 +17916,6 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-07-25",
family: "qwen",
},
"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": {
id: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8",
name: "Qwen3 Coder 480B A35B Instruct",
contextWindow: 262144,
maxInputTokens: 262144,
maxTokens: 262144,
capabilities: ["tools", "temperature"],
pricing: {
input: 2,
output: 2,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-07-23",
family: "qwen",
},
"deepseek-ai/DeepSeek-V3": {
id: "deepseek-ai/DeepSeek-V3",
name: "DeepSeek-V3",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 131072,
capabilities: ["tools", "reasoning", "temperature"],
pricing: {
input: 1.25,
output: 1.25,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2024-12-26",
family: "deepseek",
},
"meta-llama/Llama-3.3-70B-Instruct-Turbo": {
id: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
name: "Llama 3.3 70B",
@@ -18050,6 +17932,22 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2024-12-06",
family: "llama",
},
"Qwen/Qwen2.5-7B-Instruct-Turbo": {
id: "Qwen/Qwen2.5-7B-Instruct-Turbo",
name: "Qwen 2.5 7B Instruct Turbo",
contextWindow: 32768,
maxInputTokens: 32768,
maxTokens: 32768,
capabilities: ["tools", "structured_output", "temperature"],
pricing: {
input: 0.3,
output: 0.3,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2024-09-19",
family: "qwen",
},
},
v0: {
"v0-1.5-lg": {
@@ -20009,6 +19907,22 @@ export const GENERATED_PROVIDER_MODELS: {
releaseDate: "2025-09-12",
family: "qwen",
},
"moonshotai/kimi-k2": {
id: "moonshotai/kimi-k2",
name: "Kimi K2 Instruct",
contextWindow: 131072,
maxInputTokens: 131072,
maxTokens: 131072,
capabilities: ["tools", "temperature"],
pricing: {
input: 0.57,
output: 2.3,
cacheRead: 0,
cacheWrite: 0,
},
releaseDate: "2025-09-05",
family: "kimi",
},
"moonshotai/kimi-k2-turbo": {
id: "moonshotai/kimi-k2-turbo",
name: "Kimi K2 Turbo",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@cline/sdk",
"description": "Cline SDK - user-facing alias for @cline/core",
"version": "0.0.46",
"version": "0.0.47",
"repository": {
"type": "git",
"url": "https://github.com/cline/cline",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/shared",
"version": "0.0.46",
"version": "0.0.47",
"description": "Shared utilities, types, and schemas for Cline packages",
"repository": {
"type": "git",
@@ -8,9 +8,18 @@ import type { ClientContext, UserContext } from "./context";
export interface AgentExtensionCommand {
name: string;
description?: string;
handler?: (input: string) => Promise<string> | string;
handler?: (
input: string,
) => Promise<AgentExtensionCommandResult> | AgentExtensionCommandResult;
}
export type AgentExtensionCommandResult =
| string
| {
reply?: string;
submitPrompt?: string;
};
export interface AgentExtensionRule {
id: string;
content: string | (() => string | Promise<string>);
+61
View File
@@ -0,0 +1,61 @@
export type FeatureFlag = string;
export type FeatureFlagJsonValue =
| string
| number
| boolean
| null
| { [key: string]: FeatureFlagJsonValue }
| FeatureFlagJsonValue[];
export type FeatureFlagPayload = FeatureFlagJsonValue;
export type FeatureFlagsAndPayloads = {
featureFlags?: Record<string, FeatureFlagPayload>;
featureFlagPayloads?: Record<string, FeatureFlagPayload>;
};
export interface FeatureFlagsContext {
/** Stable SDK/client/user identifier used by providers that evaluate per identity. */
distinctId?: string;
/** Authenticated Cline account/user ID, when available. */
userId?: string | null;
/** Optional SDK consumer name, e.g. `my-production-app`. */
clientName?: string;
}
type AssertTrue<T extends true> = T;
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
type HasNonPrimitiveFieldNames<T> = {
[K in keyof T]-?: Exclude<T[K], Primitive> extends never ? never : K;
}[keyof T];
type HasOnlyPrimitiveFields<T> =
HasNonPrimitiveFieldNames<T> extends never ? true : false;
export type FeatureFlagsContextPrimitiveValued = AssertTrue<
HasOnlyPrimitiveFields<FeatureFlagsContext>
>;
export interface FeatureFlagsSettings {
/** Whether the provider is enabled. */
enabled: boolean;
/** Optional timeout in ms for feature flag requests. */
timeoutMs?: number;
}
export interface IFeatureFlagsProvider {
getAllFlagsAndPayloads(options: {
flagKeys?: readonly string[];
context?: FeatureFlagsContext;
}): Promise<FeatureFlagsAndPayloads | undefined>;
readonly enabled: boolean;
getSettings(): FeatureFlagsSettings;
dispose(): Promise<void>;
}
export const FeatureFlagDefaultValue: Partial<
Record<FeatureFlag, FeatureFlagPayload | undefined>
> = {};
export const FEATURE_FLAGS: readonly FeatureFlag[] = Object.keys(
FeatureFlagDefaultValue,
);
+11
View File
@@ -31,6 +31,7 @@ export type {
AgentExtensionAutomationEventType,
AgentExtensionCapability,
AgentExtensionCommand,
AgentExtensionCommandResult,
AgentExtensionHooks,
AgentExtensionMessageBuilder,
AgentExtensionProvider,
@@ -48,6 +49,16 @@ export {
normalizePluginManifest,
} from "./extensions/contribution-registry";
export { PLUGIN_FILE_EXTENSIONS } from "./extensions/plugin";
export {
FEATURE_FLAGS,
type FeatureFlag,
FeatureFlagDefaultValue,
type FeatureFlagPayload,
type FeatureFlagsAndPayloads,
type FeatureFlagsContext,
type FeatureFlagsSettings,
type IFeatureFlagsProvider,
} from "./feature-flags";
export type { HookControl } from "./hooks/contracts";
export type {
AgentAbortHookPayload,
+11
View File
@@ -45,6 +45,7 @@ export type {
AgentExtensionAutomationEventType,
AgentExtensionCapability,
AgentExtensionCommand,
AgentExtensionCommandResult,
AgentExtensionHooks,
AgentExtensionMessageBuilder,
AgentExtensionProvider,
@@ -62,6 +63,16 @@ export {
normalizePluginManifest,
} from "./extensions/contribution-registry";
export { PLUGIN_FILE_EXTENSIONS } from "./extensions/plugin";
export {
FEATURE_FLAGS,
type FeatureFlag,
FeatureFlagDefaultValue,
type FeatureFlagPayload,
type FeatureFlagsAndPayloads,
type FeatureFlagsContext,
type FeatureFlagsSettings,
type IFeatureFlagsProvider,
} from "./feature-flags";
export type { HookControl } from "./hooks/contracts";
export type {
AgentAbortHookPayload,
@@ -1,4 +1,4 @@
import { describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
CLINE_ENVIRONMENT_ENV,
CLINE_ENVIRONMENT_OVERRIDE_ENV,
@@ -8,58 +8,75 @@ import {
resolveClineEnvironment,
} from "./cline-environment";
const ENV_KEYS = [
CLINE_ENVIRONMENT_ENV,
CLINE_ENVIRONMENT_OVERRIDE_ENV,
"CLINE_API_BASE_URL",
] as const;
const originalEnvValues = Object.fromEntries(
ENV_KEYS.map((key) => [key, process.env[key]]),
);
beforeEach(() => {
vi.unstubAllGlobals();
for (const key of ENV_KEYS) {
delete process.env[key];
}
});
afterEach(() => {
vi.unstubAllGlobals();
for (const key of ENV_KEYS) {
const value = originalEnvValues[key];
if (typeof value === "string") {
process.env[key] = value;
} else {
delete process.env[key];
}
}
});
describe("resolveClineEnvironment", () => {
it("defaults to production when no env var is set", () => {
expect(resolveClineEnvironment({ env: {} })).toBe("production");
expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT);
});
it("reads CLINE_ENVIRONMENT", () => {
expect(
resolveClineEnvironment({
env: { [CLINE_ENVIRONMENT_ENV]: "staging" },
}),
).toBe("staging");
expect(
resolveClineEnvironment({
env: { [CLINE_ENVIRONMENT_ENV]: "local" },
}),
).toBe("local");
it("reads CLINE_ENVIRONMENT from process.env", () => {
process.env[CLINE_ENVIRONMENT_ENV] = "staging";
expect(resolveClineEnvironment()).toBe("staging");
process.env[CLINE_ENVIRONMENT_ENV] = "local";
expect(resolveClineEnvironment()).toBe("local");
});
it("prefers CLINE_ENVIRONMENT_OVERRIDE over CLINE_ENVIRONMENT", () => {
expect(
resolveClineEnvironment({
env: {
[CLINE_ENVIRONMENT_OVERRIDE_ENV]: "local",
[CLINE_ENVIRONMENT_ENV]: "staging",
},
}),
).toBe("local");
process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV] = "local";
process.env[CLINE_ENVIRONMENT_ENV] = "staging";
expect(resolveClineEnvironment()).toBe("local");
});
it("normalizes case and surrounding whitespace", () => {
expect(
resolveClineEnvironment({
env: { [CLINE_ENVIRONMENT_ENV]: " STAGING " },
}),
).toBe("staging");
process.env[CLINE_ENVIRONMENT_ENV] = " STAGING ";
expect(resolveClineEnvironment()).toBe("staging");
});
it("ignores unknown values and falls through to the next source", () => {
expect(
resolveClineEnvironment({
env: {
[CLINE_ENVIRONMENT_OVERRIDE_ENV]: "qa",
[CLINE_ENVIRONMENT_ENV]: "staging",
},
}),
).toBe("staging");
process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV] = "qa";
process.env[CLINE_ENVIRONMENT_ENV] = "staging";
expect(resolveClineEnvironment()).toBe("staging");
expect(
resolveClineEnvironment({
env: { [CLINE_ENVIRONMENT_ENV]: "qa" },
}),
).toBe(DEFAULT_CLINE_ENVIRONMENT);
delete process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV];
process.env[CLINE_ENVIRONMENT_ENV] = "qa";
expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT);
});
it("defaults to production when process is unavailable", () => {
vi.stubGlobal("process", undefined);
expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT);
});
});
@@ -74,18 +91,31 @@ describe("getClineEnvironmentConfig", () => {
);
});
it("resolves from env when no explicit environment is passed", () => {
expect(
getClineEnvironmentConfig({
env: { [CLINE_ENVIRONMENT_ENV]: "staging" },
}),
).toBe(CLINE_ENVIRONMENTS.staging);
it("falls back to production by default", () => {
expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.production);
});
it("falls back to production by default", () => {
expect(getClineEnvironmentConfig({ env: {} })).toBe(
CLINE_ENVIRONMENTS.production,
);
it("uses the resolved process.env environment when no explicit environment is provided", () => {
process.env[CLINE_ENVIRONMENT_ENV] = "staging";
expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.staging);
});
it("applies CLINE_API_BASE_URL without mutating the catalog config", () => {
process.env.CLINE_API_BASE_URL = "http://127.0.0.1:3000";
expect(getClineEnvironmentConfig("local")).toEqual({
...CLINE_ENVIRONMENTS.local,
apiBaseUrl: "http://127.0.0.1:3000",
mcpBaseUrl: "http://127.0.0.1:3000/v1/mcp",
});
expect(CLINE_ENVIRONMENTS.local.apiBaseUrl).toBe("http://localhost:7777");
});
it("defaults to production when process is unavailable", () => {
vi.stubGlobal("process", undefined);
expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.production);
});
});
@@ -67,10 +67,8 @@ function readProcessEnv(): NodeJS.ProcessEnv {
return process.env;
}
export function resolveClineEnvironment(
options: ResolveClineEnvironmentOptions = {},
): ClineEnvironment {
const env = options.env ?? readProcessEnv();
export function resolveClineEnvironment(): ClineEnvironment {
const env = readProcessEnv();
return (
normalizeClineEnvironment(env[CLINE_ENVIRONMENT_OVERRIDE_ENV]) ??
normalizeClineEnvironment(env[CLINE_ENVIRONMENT_ENV]) ??
@@ -78,11 +76,32 @@ export function resolveClineEnvironment(
);
}
export function getClineEnvironmentConfig(
environmentOrOptions?: ClineEnvironment | ResolveClineEnvironmentOptions,
): ClineEnvironmentConfig {
if (typeof environmentOrOptions === "string") {
return CLINE_ENVIRONMENTS[environmentOrOptions];
function getEnvConfig(env?: ClineEnvironment) {
if (typeof env === "string") {
return CLINE_ENVIRONMENTS[env];
}
return CLINE_ENVIRONMENTS[resolveClineEnvironment(environmentOrOptions)];
return CLINE_ENVIRONMENTS[resolveClineEnvironment()];
}
function applyConfigOverrides(
config: ClineEnvironmentConfig,
env: NodeJS.ProcessEnv,
): ClineEnvironmentConfig {
if (env.CLINE_API_BASE_URL) {
config = {
...config,
apiBaseUrl: env.CLINE_API_BASE_URL,
mcpBaseUrl: `${env.CLINE_API_BASE_URL}/v1/mcp`,
};
}
return config;
}
export function getClineEnvironmentConfig(
env?: ClineEnvironment,
): ClineEnvironmentConfig {
const config = getEnvConfig(env);
return applyConfigOverrides(config, readProcessEnv());
}