@khalilgharbaoui/opencode-claude-code-plugin 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +369 -221
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -731,6 +731,251 @@ Now continuing with the current message:
|
|
|
731
731
|
});
|
|
732
732
|
}
|
|
733
733
|
|
|
734
|
+
// src/models.ts
|
|
735
|
+
var PROVIDER_ID = "claude-code";
|
|
736
|
+
var NPM = "@khalilgharbaoui/opencode-claude-code-plugin";
|
|
737
|
+
var reasoningVariants = {
|
|
738
|
+
low: { reasoningEffort: "low" },
|
|
739
|
+
medium: { reasoningEffort: "medium" },
|
|
740
|
+
high: { reasoningEffort: "high" },
|
|
741
|
+
xhigh: { reasoningEffort: "xhigh" },
|
|
742
|
+
max: { reasoningEffort: "max" }
|
|
743
|
+
};
|
|
744
|
+
var baseCapabilities = {
|
|
745
|
+
temperature: false,
|
|
746
|
+
attachment: true,
|
|
747
|
+
toolcall: true,
|
|
748
|
+
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
|
749
|
+
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
750
|
+
interleaved: false
|
|
751
|
+
};
|
|
752
|
+
function defineModel(opts) {
|
|
753
|
+
return {
|
|
754
|
+
id: opts.id,
|
|
755
|
+
providerID: PROVIDER_ID,
|
|
756
|
+
api: { id: opts.id, url: "", npm: NPM },
|
|
757
|
+
name: `${opts.name} (${opts.multiplier}\xD7)`,
|
|
758
|
+
family: opts.family,
|
|
759
|
+
capabilities: { ...baseCapabilities, reasoning: opts.reasoning },
|
|
760
|
+
cost: {
|
|
761
|
+
input: opts.cost.input,
|
|
762
|
+
output: opts.cost.output,
|
|
763
|
+
cache: { read: opts.cost.cacheRead, write: opts.cost.cacheWrite }
|
|
764
|
+
},
|
|
765
|
+
limit: { context: opts.context, output: opts.output },
|
|
766
|
+
status: opts.status ?? "active",
|
|
767
|
+
options: {},
|
|
768
|
+
headers: {},
|
|
769
|
+
release_date: opts.releaseDate,
|
|
770
|
+
variants: opts.reasoning ? reasoningVariants : void 0
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
var haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 };
|
|
774
|
+
var sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
775
|
+
var sonnet5Cost = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 };
|
|
776
|
+
var opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 };
|
|
777
|
+
var fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
|
|
778
|
+
var opusFastCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
|
|
779
|
+
function toConfigModel(model) {
|
|
780
|
+
const inputMods = [];
|
|
781
|
+
const outputMods = [];
|
|
782
|
+
for (const [k, v] of Object.entries(model.capabilities.input)) {
|
|
783
|
+
if (v) inputMods.push(k);
|
|
784
|
+
}
|
|
785
|
+
for (const [k, v] of Object.entries(model.capabilities.output)) {
|
|
786
|
+
if (v) outputMods.push(k);
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
id: model.api.id,
|
|
790
|
+
name: model.name,
|
|
791
|
+
status: model.status,
|
|
792
|
+
family: model.family ?? "",
|
|
793
|
+
release_date: model.release_date,
|
|
794
|
+
temperature: model.capabilities.temperature,
|
|
795
|
+
reasoning: model.capabilities.reasoning,
|
|
796
|
+
attachment: model.capabilities.attachment,
|
|
797
|
+
tool_call: model.capabilities.toolcall,
|
|
798
|
+
modalities: { input: inputMods, output: outputMods },
|
|
799
|
+
cost: {
|
|
800
|
+
input: model.cost.input,
|
|
801
|
+
output: model.cost.output,
|
|
802
|
+
cache_read: model.cost.cache.read,
|
|
803
|
+
cache_write: model.cost.cache.write
|
|
804
|
+
},
|
|
805
|
+
limit: model.limit,
|
|
806
|
+
options: model.options,
|
|
807
|
+
headers: model.headers,
|
|
808
|
+
variants: model.variants
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
var defaultModels = {
|
|
812
|
+
"claude-haiku-4-5": defineModel({
|
|
813
|
+
id: "claude-haiku-4-5",
|
|
814
|
+
name: "Claude Haiku 4.5",
|
|
815
|
+
family: "haiku",
|
|
816
|
+
reasoning: false,
|
|
817
|
+
context: 2e5,
|
|
818
|
+
output: 64e3,
|
|
819
|
+
cost: haikuCost,
|
|
820
|
+
multiplier: 1,
|
|
821
|
+
releaseDate: "2025-10-01"
|
|
822
|
+
}),
|
|
823
|
+
"claude-sonnet-4-5": defineModel({
|
|
824
|
+
id: "claude-sonnet-4-5",
|
|
825
|
+
name: "Claude Sonnet 4.5",
|
|
826
|
+
family: "sonnet",
|
|
827
|
+
reasoning: true,
|
|
828
|
+
context: 2e5,
|
|
829
|
+
output: 64e3,
|
|
830
|
+
cost: sonnetCost,
|
|
831
|
+
multiplier: 3,
|
|
832
|
+
releaseDate: "2025-09-29"
|
|
833
|
+
}),
|
|
834
|
+
"claude-sonnet-4-6": defineModel({
|
|
835
|
+
id: "claude-sonnet-4-6",
|
|
836
|
+
name: "Claude Sonnet 4.6",
|
|
837
|
+
family: "sonnet",
|
|
838
|
+
reasoning: true,
|
|
839
|
+
context: 1e6,
|
|
840
|
+
output: 128e3,
|
|
841
|
+
cost: sonnetCost,
|
|
842
|
+
multiplier: 3,
|
|
843
|
+
releaseDate: "2025-06-19"
|
|
844
|
+
}),
|
|
845
|
+
"claude-sonnet-5": defineModel({
|
|
846
|
+
id: "claude-sonnet-5",
|
|
847
|
+
name: "Claude Sonnet 5",
|
|
848
|
+
family: "sonnet",
|
|
849
|
+
reasoning: true,
|
|
850
|
+
context: 1e6,
|
|
851
|
+
output: 128e3,
|
|
852
|
+
cost: sonnet5Cost,
|
|
853
|
+
multiplier: 2,
|
|
854
|
+
releaseDate: "2026-06-30"
|
|
855
|
+
}),
|
|
856
|
+
"claude-opus-4-5": defineModel({
|
|
857
|
+
id: "claude-opus-4-5",
|
|
858
|
+
name: "Claude Opus 4.5",
|
|
859
|
+
family: "opus",
|
|
860
|
+
reasoning: true,
|
|
861
|
+
context: 2e5,
|
|
862
|
+
output: 64e3,
|
|
863
|
+
cost: opusCost,
|
|
864
|
+
multiplier: 5,
|
|
865
|
+
releaseDate: "2025-11-01"
|
|
866
|
+
}),
|
|
867
|
+
"claude-opus-4-6": defineModel({
|
|
868
|
+
id: "claude-opus-4-6",
|
|
869
|
+
name: "Claude Opus 4.6",
|
|
870
|
+
family: "opus",
|
|
871
|
+
reasoning: true,
|
|
872
|
+
context: 1e6,
|
|
873
|
+
output: 128e3,
|
|
874
|
+
cost: opusCost,
|
|
875
|
+
multiplier: 5,
|
|
876
|
+
releaseDate: "2025-06-19"
|
|
877
|
+
}),
|
|
878
|
+
"claude-opus-4-7": defineModel({
|
|
879
|
+
id: "claude-opus-4-7",
|
|
880
|
+
name: "Claude Opus 4.7",
|
|
881
|
+
family: "opus",
|
|
882
|
+
reasoning: true,
|
|
883
|
+
context: 1e6,
|
|
884
|
+
output: 128e3,
|
|
885
|
+
cost: opusCost,
|
|
886
|
+
multiplier: 5,
|
|
887
|
+
releaseDate: "2025-07-16"
|
|
888
|
+
}),
|
|
889
|
+
"claude-opus-4-8": defineModel({
|
|
890
|
+
id: "claude-opus-4-8",
|
|
891
|
+
name: "Claude Opus 4.8",
|
|
892
|
+
family: "opus",
|
|
893
|
+
reasoning: true,
|
|
894
|
+
context: 1e6,
|
|
895
|
+
output: 128e3,
|
|
896
|
+
cost: opusCost,
|
|
897
|
+
multiplier: 5,
|
|
898
|
+
releaseDate: "2026-05-28"
|
|
899
|
+
}),
|
|
900
|
+
// Fast mode. The `-fast` suffix is OUR marker, not a model name Anthropic
|
|
901
|
+
// serves: `parseModelId` strips it before `--model` and turns it into
|
|
902
|
+
// `--settings {"fastMode":true}` on the spawn. Retired `-fast` model strings
|
|
903
|
+
// (`claude-opus-4-6-fast`) are a different thing and are not registered here.
|
|
904
|
+
//
|
|
905
|
+
// Only Opus 4.8 and Opus 5 qualify: the CLI gates fast mode on the resolved
|
|
906
|
+
// model name containing `opus-4-8` or `opus-5`, so registering a fast entry
|
|
907
|
+
// for any other model would produce a picker option that silently runs at
|
|
908
|
+
// standard speed while displaying the 10x price.
|
|
909
|
+
"claude-opus-4-8-fast": defineModel({
|
|
910
|
+
id: "claude-opus-4-8-fast",
|
|
911
|
+
name: "Claude Opus 4.8 Fast",
|
|
912
|
+
family: "opus",
|
|
913
|
+
reasoning: true,
|
|
914
|
+
context: 1e6,
|
|
915
|
+
output: 128e3,
|
|
916
|
+
cost: opusFastCost,
|
|
917
|
+
multiplier: 10,
|
|
918
|
+
releaseDate: "2026-05-28"
|
|
919
|
+
}),
|
|
920
|
+
"claude-opus-5": defineModel({
|
|
921
|
+
id: "claude-opus-5",
|
|
922
|
+
name: "Claude Opus 5",
|
|
923
|
+
family: "opus",
|
|
924
|
+
reasoning: true,
|
|
925
|
+
context: 1e6,
|
|
926
|
+
output: 128e3,
|
|
927
|
+
cost: opusCost,
|
|
928
|
+
multiplier: 5,
|
|
929
|
+
releaseDate: "2026-07-24"
|
|
930
|
+
}),
|
|
931
|
+
"claude-opus-5-fast": defineModel({
|
|
932
|
+
id: "claude-opus-5-fast",
|
|
933
|
+
name: "Claude Opus 5 Fast",
|
|
934
|
+
family: "opus",
|
|
935
|
+
reasoning: true,
|
|
936
|
+
context: 1e6,
|
|
937
|
+
output: 128e3,
|
|
938
|
+
cost: opusFastCost,
|
|
939
|
+
multiplier: 10,
|
|
940
|
+
releaseDate: "2026-07-24"
|
|
941
|
+
}),
|
|
942
|
+
"claude-fable-5": defineModel({
|
|
943
|
+
id: "claude-fable-5",
|
|
944
|
+
name: "Claude Fable 5",
|
|
945
|
+
family: "fable",
|
|
946
|
+
reasoning: true,
|
|
947
|
+
context: 1e6,
|
|
948
|
+
output: 128e3,
|
|
949
|
+
cost: fableCost,
|
|
950
|
+
multiplier: 10,
|
|
951
|
+
releaseDate: "2026-06-09"
|
|
952
|
+
}),
|
|
953
|
+
// Mythos 5 shares Fable 5's capabilities and pricing without the safety
|
|
954
|
+
// classifiers; limited availability via Project Glasswing. `claude --model
|
|
955
|
+
// claude-mythos-5` simply errors for accounts without access, so it's safe to
|
|
956
|
+
// register unconditionally.
|
|
957
|
+
"claude-mythos-5": defineModel({
|
|
958
|
+
id: "claude-mythos-5",
|
|
959
|
+
name: "Claude Mythos 5",
|
|
960
|
+
family: "mythos",
|
|
961
|
+
reasoning: true,
|
|
962
|
+
context: 1e6,
|
|
963
|
+
output: 128e3,
|
|
964
|
+
cost: fableCost,
|
|
965
|
+
multiplier: 10,
|
|
966
|
+
releaseDate: "2026-06-09"
|
|
967
|
+
})
|
|
968
|
+
};
|
|
969
|
+
var FAST_SUFFIX = "-fast";
|
|
970
|
+
function parseModelId(modelId) {
|
|
971
|
+
const at = modelId.indexOf("@");
|
|
972
|
+
const base = at === -1 ? modelId : modelId.slice(0, at);
|
|
973
|
+
const account = at === -1 ? "" : modelId.slice(at);
|
|
974
|
+
if (!base.endsWith(FAST_SUFFIX)) return { model: modelId, fast: false };
|
|
975
|
+
if (!Object.hasOwn(defaultModels, base)) return { model: modelId, fast: false };
|
|
976
|
+
return { model: base.slice(0, -FAST_SUFFIX.length) + account, fast: true };
|
|
977
|
+
}
|
|
978
|
+
|
|
734
979
|
// src/plan-mode-question.ts
|
|
735
980
|
var QUESTION_TOOL_NAME = "question";
|
|
736
981
|
var APPROVED_EXIT_PLAN_MODE_MESSAGE = "User has approved your plan. You can now start coding. Start with updating your todo list if applicable.";
|
|
@@ -1386,6 +1631,10 @@ function cliSupportsThinkingDisplay(v) {
|
|
|
1386
1631
|
if (!v) return false;
|
|
1387
1632
|
return gte(v, { major: 2, minor: 1, patch: 142 });
|
|
1388
1633
|
}
|
|
1634
|
+
function cliSupportsFastMode(v) {
|
|
1635
|
+
if (!v) return false;
|
|
1636
|
+
return gte(v, { major: 2, minor: 1, patch: 220 });
|
|
1637
|
+
}
|
|
1389
1638
|
function cliSupportsThinking(v) {
|
|
1390
1639
|
if (!v) return false;
|
|
1391
1640
|
return gte(v, { major: 2, minor: 0, patch: 0 });
|
|
@@ -1609,6 +1858,7 @@ function buildCliArgs(opts) {
|
|
|
1609
1858
|
appendSystemPromptFile,
|
|
1610
1859
|
thinking,
|
|
1611
1860
|
thinkingDisplay,
|
|
1861
|
+
fastMode,
|
|
1612
1862
|
cliVersion
|
|
1613
1863
|
} = opts;
|
|
1614
1864
|
const args = [
|
|
@@ -1654,6 +1904,9 @@ function buildCliArgs(opts) {
|
|
|
1654
1904
|
if (appendSystemPromptFile) {
|
|
1655
1905
|
args.push("--append-system-prompt-file", appendSystemPromptFile);
|
|
1656
1906
|
}
|
|
1907
|
+
if (fastMode && cliSupportsFastMode(cliVersion ?? null)) {
|
|
1908
|
+
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
1909
|
+
}
|
|
1657
1910
|
if (skipPermissions) {
|
|
1658
1911
|
args.push("--dangerously-skip-permissions");
|
|
1659
1912
|
}
|
|
@@ -2081,11 +2334,15 @@ function spawnInteractiveProcess(opts) {
|
|
|
2081
2334
|
"--strict-mcp-config"
|
|
2082
2335
|
);
|
|
2083
2336
|
}
|
|
2337
|
+
const flagSettings = {};
|
|
2084
2338
|
if (opts.permissionsAllow && opts.permissionsAllow.length > 0) {
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2339
|
+
flagSettings.permissions = { allow: opts.permissionsAllow };
|
|
2340
|
+
}
|
|
2341
|
+
if (opts.fastMode) {
|
|
2342
|
+
flagSettings.fastMode = true;
|
|
2343
|
+
}
|
|
2344
|
+
if (Object.keys(flagSettings).length > 0) {
|
|
2345
|
+
extraArgs.push("--settings", JSON.stringify(flagSettings));
|
|
2089
2346
|
}
|
|
2090
2347
|
if (opts.permissionMode === "bypassPermissions") {
|
|
2091
2348
|
log.warn(
|
|
@@ -2541,10 +2798,50 @@ var DEFAULT_PROXY_TOOLS = [
|
|
|
2541
2798
|
async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverrides, interceptors) {
|
|
2542
2799
|
const calls = new EventEmitter3();
|
|
2543
2800
|
const pending = /* @__PURE__ */ new Map();
|
|
2801
|
+
const authToken = crypto2.randomBytes(32).toString("hex");
|
|
2802
|
+
const expectedAuth = Buffer.from(`Bearer ${authToken}`);
|
|
2803
|
+
let boundAuthority = "";
|
|
2804
|
+
function authOk(req) {
|
|
2805
|
+
const got = req.headers.authorization;
|
|
2806
|
+
if (typeof got !== "string") return false;
|
|
2807
|
+
const candidate = Buffer.from(got);
|
|
2808
|
+
if (candidate.length !== expectedAuth.length) return false;
|
|
2809
|
+
return crypto2.timingSafeEqual(candidate, expectedAuth);
|
|
2810
|
+
}
|
|
2811
|
+
function reject(req, res, statusCode, reason) {
|
|
2812
|
+
log.notice("proxy-mcp rejected a request", {
|
|
2813
|
+
statusCode,
|
|
2814
|
+
reason,
|
|
2815
|
+
method: req.method,
|
|
2816
|
+
hasAuthorization: typeof req.headers.authorization === "string"
|
|
2817
|
+
});
|
|
2818
|
+
res.statusCode = statusCode;
|
|
2819
|
+
res.setHeader("Connection", "close");
|
|
2820
|
+
res.on("finish", () => {
|
|
2821
|
+
req.socket?.destroy();
|
|
2822
|
+
});
|
|
2823
|
+
res.end();
|
|
2824
|
+
}
|
|
2544
2825
|
const server2 = createServer(async (req, res) => {
|
|
2545
2826
|
if (req.method !== "POST" || !req.url?.startsWith("/mcp")) {
|
|
2546
|
-
res
|
|
2547
|
-
|
|
2827
|
+
reject(req, res, 404, "not a POST to /mcp");
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
if (req.headers.host !== boundAuthority) {
|
|
2831
|
+
reject(req, res, 403, "host header is not the bound authority");
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (req.headers.origin !== void 0) {
|
|
2835
|
+
reject(req, res, 403, "origin header present");
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
const contentType = String(req.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
|
|
2839
|
+
if (contentType !== "application/json") {
|
|
2840
|
+
reject(req, res, 415, "content-type is not application/json");
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
if (!authOk(req)) {
|
|
2844
|
+
reject(req, res, 401, "missing or invalid bearer token");
|
|
2548
2845
|
return;
|
|
2549
2846
|
}
|
|
2550
2847
|
let requestId = null;
|
|
@@ -2636,13 +2933,13 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2636
2933
|
});
|
|
2637
2934
|
let timer = null;
|
|
2638
2935
|
const result = await new Promise(
|
|
2639
|
-
(resolve4,
|
|
2936
|
+
(resolve4, reject2) => {
|
|
2640
2937
|
const entry = {
|
|
2641
2938
|
id: callId,
|
|
2642
2939
|
toolName,
|
|
2643
2940
|
input,
|
|
2644
2941
|
resolve: resolve4,
|
|
2645
|
-
reject
|
|
2942
|
+
reject: reject2
|
|
2646
2943
|
};
|
|
2647
2944
|
pending.set(callId, entry);
|
|
2648
2945
|
const deadlineMs = resolveProxyCallTimeoutMs(
|
|
@@ -2658,7 +2955,7 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2658
2955
|
toolName,
|
|
2659
2956
|
deadlineMs
|
|
2660
2957
|
});
|
|
2661
|
-
|
|
2958
|
+
reject2(buildProxyTimeoutError(toolName, deadlineMs));
|
|
2662
2959
|
}, deadlineMs);
|
|
2663
2960
|
calls.emit("call", entry);
|
|
2664
2961
|
}
|
|
@@ -2717,10 +3014,10 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2717
3014
|
}
|
|
2718
3015
|
}
|
|
2719
3016
|
});
|
|
2720
|
-
await new Promise((resolve4,
|
|
2721
|
-
server2.once("error",
|
|
3017
|
+
await new Promise((resolve4, reject2) => {
|
|
3018
|
+
server2.once("error", reject2);
|
|
2722
3019
|
server2.listen(0, "127.0.0.1", () => {
|
|
2723
|
-
server2.off("error",
|
|
3020
|
+
server2.off("error", reject2);
|
|
2724
3021
|
resolve4();
|
|
2725
3022
|
});
|
|
2726
3023
|
});
|
|
@@ -2729,7 +3026,8 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2729
3026
|
server2.close();
|
|
2730
3027
|
throw new Error("Failed to bind proxy MCP server");
|
|
2731
3028
|
}
|
|
2732
|
-
|
|
3029
|
+
boundAuthority = `127.0.0.1:${addr.port}`;
|
|
3030
|
+
const url = `http://${boundAuthority}/mcp`;
|
|
2733
3031
|
log.info("proxy-mcp server started", {
|
|
2734
3032
|
url,
|
|
2735
3033
|
tools: tools.map((t) => t.name)
|
|
@@ -2739,6 +3037,7 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2739
3037
|
url,
|
|
2740
3038
|
serverName: SERVER_NAME,
|
|
2741
3039
|
tools,
|
|
3040
|
+
authToken,
|
|
2742
3041
|
calls,
|
|
2743
3042
|
configPath() {
|
|
2744
3043
|
if (configFilePath) return configFilePath;
|
|
@@ -2748,6 +3047,10 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2748
3047
|
[SERVER_NAME]: {
|
|
2749
3048
|
type: "http",
|
|
2750
3049
|
url,
|
|
3050
|
+
// Claude CLI replays these headers on every request to this
|
|
3051
|
+
// server, which is what lets the handler above reject anyone
|
|
3052
|
+
// who did not read this 0600 file.
|
|
3053
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
2751
3054
|
timeout: resolveProxyClientCeilingMs(timeoutOverrides)
|
|
2752
3055
|
}
|
|
2753
3056
|
}
|
|
@@ -3290,6 +3593,47 @@ ${options.compressionSummary.trim()}`
|
|
|
3290
3593
|
return void 0;
|
|
3291
3594
|
}
|
|
3292
3595
|
}
|
|
3596
|
+
var FAST_MODE_REASONS = {
|
|
3597
|
+
sdk_opt_in_required: "the CLI did not receive the headless opt-in (--settings). This is a plugin bug, please report it",
|
|
3598
|
+
extra_usage_disabled: "your account has usage credits turned off. Run /usage-credits in an interactive `claude` session to enable them",
|
|
3599
|
+
free: "fast mode requires a paid subscription or purchased credits",
|
|
3600
|
+
preference: "fast mode is turned off for your organization",
|
|
3601
|
+
model_not_allowed: "this model is not in your organization's allowed models",
|
|
3602
|
+
not_first_party: "fast mode only works against the Anthropic API directly, not Bedrock / Vertex / Foundry",
|
|
3603
|
+
network_error: "the CLI could not reach Anthropic to check availability",
|
|
3604
|
+
disabled_by_env: "CLAUDE_CODE_DISABLE_FAST_MODE is set in the environment",
|
|
3605
|
+
pending: "the CLI is still checking availability"
|
|
3606
|
+
};
|
|
3607
|
+
var warnedFastModeReasons = /* @__PURE__ */ new Set();
|
|
3608
|
+
function reportFastModeState(msg, requested) {
|
|
3609
|
+
const state = msg.fast_mode_state;
|
|
3610
|
+
if (!state) return;
|
|
3611
|
+
if (!requested) {
|
|
3612
|
+
log.debug("fast mode state", { state });
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3615
|
+
if (state === "on") {
|
|
3616
|
+
log.info("fast mode active", { state });
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
const reason = msg.fast_mode_disabled_reason;
|
|
3620
|
+
if (state === "cooldown") {
|
|
3621
|
+
log.notice(
|
|
3622
|
+
"fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the 10x shown in the model picker.",
|
|
3623
|
+
{ state, reason: reason ?? null }
|
|
3624
|
+
);
|
|
3625
|
+
return;
|
|
3626
|
+
}
|
|
3627
|
+
const key = reason ?? "unknown";
|
|
3628
|
+
const explanation = reason ? FAST_MODE_REASONS[reason] : void 0;
|
|
3629
|
+
const message = `fast mode was requested but is off${explanation ? `: ${explanation}` : reason ? ` (${reason})` : ""}. Turns run at standard speed and are billed at standard Opus rates, not the 10x shown in the model picker. Switch to the non-fast model id to make the picker's price accurate.`;
|
|
3630
|
+
if (warnedFastModeReasons.has(key)) {
|
|
3631
|
+
log.debug(message, { state, reason: reason ?? null });
|
|
3632
|
+
return;
|
|
3633
|
+
}
|
|
3634
|
+
warnedFastModeReasons.add(key);
|
|
3635
|
+
log.warn(message, { state, reason: reason ?? null });
|
|
3636
|
+
}
|
|
3293
3637
|
var ClaudeCodeLanguageModel = class {
|
|
3294
3638
|
specificationVersion = "v3";
|
|
3295
3639
|
modelId;
|
|
@@ -3899,17 +4243,19 @@ var ClaudeCodeLanguageModel = class {
|
|
|
3899
4243
|
// An existing summary still carries: it is this key's prior context.
|
|
3900
4244
|
{ compressEnabled: false, compressionSummary: getCompressionSummary(sk) }
|
|
3901
4245
|
);
|
|
4246
|
+
const { model: spawnModelId, fast: fastMode } = parseModelId(this.modelId);
|
|
3902
4247
|
const cliArgs = buildCliArgs({
|
|
3903
4248
|
sessionKey: sk,
|
|
3904
4249
|
skipPermissions: this.config.skipPermissions !== false,
|
|
3905
4250
|
includeSessionId: false,
|
|
3906
|
-
model:
|
|
4251
|
+
model: spawnModelId,
|
|
3907
4252
|
permissionMode: this.config.permissionMode,
|
|
3908
4253
|
mcpConfig: this.effectiveMcpConfig(cwd, void 0, runtimeStatus).paths,
|
|
3909
4254
|
strictMcpConfig: this.config.strictMcpConfig,
|
|
3910
4255
|
disallowedTools: this.config.webSearch === "disabled" ? ["WebSearch"] : void 0,
|
|
3911
4256
|
appendSystemPromptFile: systemPromptFile,
|
|
3912
4257
|
...this.thinkingCliOptions(),
|
|
4258
|
+
fastMode,
|
|
3913
4259
|
cliVersion
|
|
3914
4260
|
});
|
|
3915
4261
|
log.info("doGenerate starting", {
|
|
@@ -3963,6 +4309,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
3963
4309
|
if (msg.session_id) {
|
|
3964
4310
|
setClaudeSessionId(sk, msg.session_id);
|
|
3965
4311
|
}
|
|
4312
|
+
reportFastModeState(msg, fastMode);
|
|
3966
4313
|
}
|
|
3967
4314
|
if (msg.type === "assistant" && msg.message?.content && !gotPartialEvents) {
|
|
3968
4315
|
for (const block of msg.message.content) {
|
|
@@ -4200,6 +4547,7 @@ ${plan}
|
|
|
4200
4547
|
const affinity = this.sessionAffinity(options);
|
|
4201
4548
|
const compactionMode = this.isCompactionCall(options);
|
|
4202
4549
|
const effectiveModelId = compactionMode ? this.resolveCompactionModel() : this.modelId;
|
|
4550
|
+
const { model: spawnModelId, fast: fastMode } = parseModelId(effectiveModelId);
|
|
4203
4551
|
const sk = compactionMode ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`) : sessionKey(cwd, `${this.modelId}::${scope}::${affinity}`);
|
|
4204
4552
|
const toUsage = this.toUsage.bind(this);
|
|
4205
4553
|
const toFinishReason = this.toFinishReason.bind(this);
|
|
@@ -4395,7 +4743,8 @@ ${plan}
|
|
|
4395
4743
|
cwd,
|
|
4396
4744
|
cliPath,
|
|
4397
4745
|
configDir: self.config.configDir,
|
|
4398
|
-
model:
|
|
4746
|
+
model: spawnModelId,
|
|
4747
|
+
fastMode,
|
|
4399
4748
|
mcpConfigPaths: mcp.paths,
|
|
4400
4749
|
permissionsAllow: allow,
|
|
4401
4750
|
systemPromptFile,
|
|
@@ -4422,8 +4771,9 @@ ${plan}
|
|
|
4422
4771
|
sessionKey: sk,
|
|
4423
4772
|
skipPermissions,
|
|
4424
4773
|
includeSessionId: false,
|
|
4425
|
-
model:
|
|
4774
|
+
model: spawnModelId,
|
|
4426
4775
|
permissionMode: self.config.permissionMode,
|
|
4776
|
+
fastMode,
|
|
4427
4777
|
cliVersion
|
|
4428
4778
|
});
|
|
4429
4779
|
} else {
|
|
@@ -4510,13 +4860,14 @@ ${plan}
|
|
|
4510
4860
|
cliArgs = buildCliArgs({
|
|
4511
4861
|
sessionKey: sk,
|
|
4512
4862
|
skipPermissions,
|
|
4513
|
-
model:
|
|
4863
|
+
model: spawnModelId,
|
|
4514
4864
|
permissionMode: self.config.permissionMode,
|
|
4515
4865
|
mcpConfig: mcp.paths,
|
|
4516
4866
|
strictMcpConfig: self.config.strictMcpConfig,
|
|
4517
4867
|
disallowedTools: allDisallowed.length > 0 ? allDisallowed : void 0,
|
|
4518
4868
|
appendSystemPromptFile: systemPromptFile,
|
|
4519
4869
|
...self.thinkingCliOptions(),
|
|
4870
|
+
fastMode,
|
|
4520
4871
|
cliVersion
|
|
4521
4872
|
});
|
|
4522
4873
|
spawnSystemPromptFile = systemPromptFile;
|
|
@@ -4948,6 +5299,7 @@ ${plan}
|
|
|
4948
5299
|
claudeSessionId: msg.session_id
|
|
4949
5300
|
});
|
|
4950
5301
|
}
|
|
5302
|
+
reportFastModeState(msg, fastMode);
|
|
4951
5303
|
}
|
|
4952
5304
|
if (msg.type === "content_block_start" && msg.content_block && msg.index !== void 0) {
|
|
4953
5305
|
const block = msg.content_block;
|
|
@@ -5699,210 +6051,6 @@ ${plan}
|
|
|
5699
6051
|
}
|
|
5700
6052
|
};
|
|
5701
6053
|
|
|
5702
|
-
// src/models.ts
|
|
5703
|
-
var PROVIDER_ID = "claude-code";
|
|
5704
|
-
var NPM = "@khalilgharbaoui/opencode-claude-code-plugin";
|
|
5705
|
-
var reasoningVariants = {
|
|
5706
|
-
low: { reasoningEffort: "low" },
|
|
5707
|
-
medium: { reasoningEffort: "medium" },
|
|
5708
|
-
high: { reasoningEffort: "high" },
|
|
5709
|
-
xhigh: { reasoningEffort: "xhigh" },
|
|
5710
|
-
max: { reasoningEffort: "max" }
|
|
5711
|
-
};
|
|
5712
|
-
var baseCapabilities = {
|
|
5713
|
-
temperature: false,
|
|
5714
|
-
attachment: true,
|
|
5715
|
-
toolcall: true,
|
|
5716
|
-
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
|
5717
|
-
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
5718
|
-
interleaved: false
|
|
5719
|
-
};
|
|
5720
|
-
function defineModel(opts) {
|
|
5721
|
-
return {
|
|
5722
|
-
id: opts.id,
|
|
5723
|
-
providerID: PROVIDER_ID,
|
|
5724
|
-
api: { id: opts.id, url: "", npm: NPM },
|
|
5725
|
-
name: `${opts.name} (${opts.multiplier}\xD7)`,
|
|
5726
|
-
family: opts.family,
|
|
5727
|
-
capabilities: { ...baseCapabilities, reasoning: opts.reasoning },
|
|
5728
|
-
cost: {
|
|
5729
|
-
input: opts.cost.input,
|
|
5730
|
-
output: opts.cost.output,
|
|
5731
|
-
cache: { read: opts.cost.cacheRead, write: opts.cost.cacheWrite }
|
|
5732
|
-
},
|
|
5733
|
-
limit: { context: opts.context, output: opts.output },
|
|
5734
|
-
status: opts.status ?? "active",
|
|
5735
|
-
options: {},
|
|
5736
|
-
headers: {},
|
|
5737
|
-
release_date: opts.releaseDate,
|
|
5738
|
-
variants: opts.reasoning ? reasoningVariants : void 0
|
|
5739
|
-
};
|
|
5740
|
-
}
|
|
5741
|
-
var haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 };
|
|
5742
|
-
var sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
5743
|
-
var sonnet5Cost = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 };
|
|
5744
|
-
var opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 };
|
|
5745
|
-
var fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
|
|
5746
|
-
function toConfigModel(model) {
|
|
5747
|
-
const inputMods = [];
|
|
5748
|
-
const outputMods = [];
|
|
5749
|
-
for (const [k, v] of Object.entries(model.capabilities.input)) {
|
|
5750
|
-
if (v) inputMods.push(k);
|
|
5751
|
-
}
|
|
5752
|
-
for (const [k, v] of Object.entries(model.capabilities.output)) {
|
|
5753
|
-
if (v) outputMods.push(k);
|
|
5754
|
-
}
|
|
5755
|
-
return {
|
|
5756
|
-
id: model.api.id,
|
|
5757
|
-
name: model.name,
|
|
5758
|
-
status: model.status,
|
|
5759
|
-
family: model.family ?? "",
|
|
5760
|
-
release_date: model.release_date,
|
|
5761
|
-
temperature: model.capabilities.temperature,
|
|
5762
|
-
reasoning: model.capabilities.reasoning,
|
|
5763
|
-
attachment: model.capabilities.attachment,
|
|
5764
|
-
tool_call: model.capabilities.toolcall,
|
|
5765
|
-
modalities: { input: inputMods, output: outputMods },
|
|
5766
|
-
cost: {
|
|
5767
|
-
input: model.cost.input,
|
|
5768
|
-
output: model.cost.output,
|
|
5769
|
-
cache_read: model.cost.cache.read,
|
|
5770
|
-
cache_write: model.cost.cache.write
|
|
5771
|
-
},
|
|
5772
|
-
limit: model.limit,
|
|
5773
|
-
options: model.options,
|
|
5774
|
-
headers: model.headers,
|
|
5775
|
-
variants: model.variants
|
|
5776
|
-
};
|
|
5777
|
-
}
|
|
5778
|
-
var defaultModels = {
|
|
5779
|
-
"claude-haiku-4-5": defineModel({
|
|
5780
|
-
id: "claude-haiku-4-5",
|
|
5781
|
-
name: "Claude Haiku 4.5",
|
|
5782
|
-
family: "haiku",
|
|
5783
|
-
reasoning: false,
|
|
5784
|
-
context: 2e5,
|
|
5785
|
-
output: 64e3,
|
|
5786
|
-
cost: haikuCost,
|
|
5787
|
-
multiplier: 1,
|
|
5788
|
-
releaseDate: "2025-10-01"
|
|
5789
|
-
}),
|
|
5790
|
-
"claude-sonnet-4-5": defineModel({
|
|
5791
|
-
id: "claude-sonnet-4-5",
|
|
5792
|
-
name: "Claude Sonnet 4.5",
|
|
5793
|
-
family: "sonnet",
|
|
5794
|
-
reasoning: true,
|
|
5795
|
-
context: 2e5,
|
|
5796
|
-
output: 64e3,
|
|
5797
|
-
cost: sonnetCost,
|
|
5798
|
-
multiplier: 3,
|
|
5799
|
-
releaseDate: "2025-09-29"
|
|
5800
|
-
}),
|
|
5801
|
-
"claude-sonnet-4-6": defineModel({
|
|
5802
|
-
id: "claude-sonnet-4-6",
|
|
5803
|
-
name: "Claude Sonnet 4.6",
|
|
5804
|
-
family: "sonnet",
|
|
5805
|
-
reasoning: true,
|
|
5806
|
-
context: 1e6,
|
|
5807
|
-
output: 128e3,
|
|
5808
|
-
cost: sonnetCost,
|
|
5809
|
-
multiplier: 3,
|
|
5810
|
-
releaseDate: "2025-06-19"
|
|
5811
|
-
}),
|
|
5812
|
-
"claude-sonnet-5": defineModel({
|
|
5813
|
-
id: "claude-sonnet-5",
|
|
5814
|
-
name: "Claude Sonnet 5",
|
|
5815
|
-
family: "sonnet",
|
|
5816
|
-
reasoning: true,
|
|
5817
|
-
context: 1e6,
|
|
5818
|
-
output: 128e3,
|
|
5819
|
-
cost: sonnet5Cost,
|
|
5820
|
-
multiplier: 2,
|
|
5821
|
-
releaseDate: "2026-06-30"
|
|
5822
|
-
}),
|
|
5823
|
-
"claude-opus-4-5": defineModel({
|
|
5824
|
-
id: "claude-opus-4-5",
|
|
5825
|
-
name: "Claude Opus 4.5",
|
|
5826
|
-
family: "opus",
|
|
5827
|
-
reasoning: true,
|
|
5828
|
-
context: 2e5,
|
|
5829
|
-
output: 64e3,
|
|
5830
|
-
cost: opusCost,
|
|
5831
|
-
multiplier: 5,
|
|
5832
|
-
releaseDate: "2025-11-01"
|
|
5833
|
-
}),
|
|
5834
|
-
"claude-opus-4-6": defineModel({
|
|
5835
|
-
id: "claude-opus-4-6",
|
|
5836
|
-
name: "Claude Opus 4.6",
|
|
5837
|
-
family: "opus",
|
|
5838
|
-
reasoning: true,
|
|
5839
|
-
context: 1e6,
|
|
5840
|
-
output: 128e3,
|
|
5841
|
-
cost: opusCost,
|
|
5842
|
-
multiplier: 5,
|
|
5843
|
-
releaseDate: "2025-06-19"
|
|
5844
|
-
}),
|
|
5845
|
-
"claude-opus-4-7": defineModel({
|
|
5846
|
-
id: "claude-opus-4-7",
|
|
5847
|
-
name: "Claude Opus 4.7",
|
|
5848
|
-
family: "opus",
|
|
5849
|
-
reasoning: true,
|
|
5850
|
-
context: 1e6,
|
|
5851
|
-
output: 128e3,
|
|
5852
|
-
cost: opusCost,
|
|
5853
|
-
multiplier: 5,
|
|
5854
|
-
releaseDate: "2025-07-16"
|
|
5855
|
-
}),
|
|
5856
|
-
"claude-opus-4-8": defineModel({
|
|
5857
|
-
id: "claude-opus-4-8",
|
|
5858
|
-
name: "Claude Opus 4.8",
|
|
5859
|
-
family: "opus",
|
|
5860
|
-
reasoning: true,
|
|
5861
|
-
context: 1e6,
|
|
5862
|
-
output: 128e3,
|
|
5863
|
-
cost: opusCost,
|
|
5864
|
-
multiplier: 5,
|
|
5865
|
-
releaseDate: "2026-05-28"
|
|
5866
|
-
}),
|
|
5867
|
-
"claude-opus-5": defineModel({
|
|
5868
|
-
id: "claude-opus-5",
|
|
5869
|
-
name: "Claude Opus 5",
|
|
5870
|
-
family: "opus",
|
|
5871
|
-
reasoning: true,
|
|
5872
|
-
context: 1e6,
|
|
5873
|
-
output: 128e3,
|
|
5874
|
-
cost: opusCost,
|
|
5875
|
-
multiplier: 5,
|
|
5876
|
-
releaseDate: "2026-07-24"
|
|
5877
|
-
}),
|
|
5878
|
-
"claude-fable-5": defineModel({
|
|
5879
|
-
id: "claude-fable-5",
|
|
5880
|
-
name: "Claude Fable 5",
|
|
5881
|
-
family: "fable",
|
|
5882
|
-
reasoning: true,
|
|
5883
|
-
context: 1e6,
|
|
5884
|
-
output: 128e3,
|
|
5885
|
-
cost: fableCost,
|
|
5886
|
-
multiplier: 10,
|
|
5887
|
-
releaseDate: "2026-06-09"
|
|
5888
|
-
}),
|
|
5889
|
-
// Mythos 5 shares Fable 5's capabilities and pricing without the safety
|
|
5890
|
-
// classifiers; limited availability via Project Glasswing. `claude --model
|
|
5891
|
-
// claude-mythos-5` simply errors for accounts without access, so it's safe to
|
|
5892
|
-
// register unconditionally.
|
|
5893
|
-
"claude-mythos-5": defineModel({
|
|
5894
|
-
id: "claude-mythos-5",
|
|
5895
|
-
name: "Claude Mythos 5",
|
|
5896
|
-
family: "mythos",
|
|
5897
|
-
reasoning: true,
|
|
5898
|
-
context: 1e6,
|
|
5899
|
-
output: 128e3,
|
|
5900
|
-
cost: fableCost,
|
|
5901
|
-
multiplier: 10,
|
|
5902
|
-
releaseDate: "2026-06-09"
|
|
5903
|
-
})
|
|
5904
|
-
};
|
|
5905
|
-
|
|
5906
6054
|
// src/accounts.ts
|
|
5907
6055
|
import { chmod, lstat, mkdir, readlink, symlink, writeFile } from "fs/promises";
|
|
5908
6056
|
import path5 from "path";
|