@xtruder/opencode-claude-max-plugin 0.3.0 → 0.4.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 +14 -17
- package/build/index.d.ts +13 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +207 -38
- package/build/model.d.ts +25 -0
- package/build/model.d.ts.map +1 -1
- package/build/server.js +206 -37
- package/build/stream.d.ts +31 -1
- package/build/stream.d.ts.map +1 -1
- package/build/tui.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/tui.tsx +196 -17
package/build/server.js
CHANGED
|
@@ -48,8 +48,8 @@ function hasCchPlaceholder(body) {
|
|
|
48
48
|
return block?.text?.includes("cch=00000") ?? false;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// src/claudecode-system-
|
|
52
|
-
var
|
|
51
|
+
// src/claudecode-system-new.txt
|
|
52
|
+
var claudecode_system_new_default = `
|
|
53
53
|
You are an interactive agent that helps users with software engineering tasks.
|
|
54
54
|
|
|
55
55
|
# Harness
|
|
@@ -382,6 +382,14 @@ function convertUserMessage(message) {
|
|
|
382
382
|
function convertAssistantMessage(message) {
|
|
383
383
|
const content = [];
|
|
384
384
|
for (const part of message.content) {
|
|
385
|
+
const fallback = part.providerMetadata?.anthropic?.fallback ?? part.providerOptions?.anthropic?.fallback;
|
|
386
|
+
if (fallback?.from?.model && fallback?.to?.model) {
|
|
387
|
+
content.push({
|
|
388
|
+
type: "fallback",
|
|
389
|
+
from: { model: fallback.from.model },
|
|
390
|
+
to: { model: fallback.to.model }
|
|
391
|
+
});
|
|
392
|
+
}
|
|
385
393
|
switch (part.type) {
|
|
386
394
|
case "text":
|
|
387
395
|
if (part.text.length > 0) {
|
|
@@ -460,6 +468,14 @@ var idCounter = 0;
|
|
|
460
468
|
function generateId() {
|
|
461
469
|
return `block-${Date.now()}-${idCounter++}`;
|
|
462
470
|
}
|
|
471
|
+
function buildRefusalError(stopDetails, fallbackModel) {
|
|
472
|
+
const category = stopDetails?.category ?? null;
|
|
473
|
+
const explanation = stopDetails?.explanation ?? null;
|
|
474
|
+
const categoryNote = category ? ` (category: ${category})` : "";
|
|
475
|
+
const detail = explanation ? explanation : "Claude Fable 5's safety classifiers declined this request.";
|
|
476
|
+
const hint = fallbackModel ? `The configured fallback model (${fallbackModel}) also refused.` : `Retry with a fallback model such as anthropic-sdk/claude-opus-4-8.`;
|
|
477
|
+
return new Error(`Claude Fable 5 refused this request${categoryNote}. ${detail} ${hint}`);
|
|
478
|
+
}
|
|
463
479
|
function mapFinishReason(stopReason) {
|
|
464
480
|
const unified = (() => {
|
|
465
481
|
switch (stopReason) {
|
|
@@ -476,8 +492,13 @@ function mapFinishReason(stopReason) {
|
|
|
476
492
|
})();
|
|
477
493
|
return { unified, raw: stopReason ?? undefined };
|
|
478
494
|
}
|
|
479
|
-
function convertStream(anthropicStream, modelId) {
|
|
495
|
+
function convertStream(anthropicStream, modelId, context) {
|
|
480
496
|
const blockStates = new Map;
|
|
497
|
+
const ctx = {
|
|
498
|
+
apiModelId: context?.apiModelId ?? modelId,
|
|
499
|
+
fallbacksEnabled: context?.fallbacksEnabled ?? false,
|
|
500
|
+
fallbackModel: context?.fallbackModel
|
|
501
|
+
};
|
|
481
502
|
let inputTokens;
|
|
482
503
|
let outputTokens;
|
|
483
504
|
let cachedInputTokens;
|
|
@@ -486,7 +507,7 @@ function convertStream(anthropicStream, modelId) {
|
|
|
486
507
|
async start(controller) {
|
|
487
508
|
try {
|
|
488
509
|
for await (const event of anthropicStream) {
|
|
489
|
-
const parts = processEvent(event, blockStates, modelId);
|
|
510
|
+
const parts = processEvent(event, blockStates, modelId, ctx);
|
|
490
511
|
if (event.type === "message_start" && event.message.usage) {
|
|
491
512
|
const u = event.message.usage;
|
|
492
513
|
inputTokens = u.input_tokens;
|
|
@@ -496,6 +517,12 @@ function convertStream(anthropicStream, modelId) {
|
|
|
496
517
|
if (event.type === "message_delta") {
|
|
497
518
|
const delta = event;
|
|
498
519
|
outputTokens = delta.usage?.output_tokens;
|
|
520
|
+
if (delta.delta?.stop_reason === "refusal") {
|
|
521
|
+
controller.enqueue({
|
|
522
|
+
type: "error",
|
|
523
|
+
error: buildRefusalError(delta.delta?.stop_details, ctx.fallbacksEnabled ? ctx.fallbackModel : undefined)
|
|
524
|
+
});
|
|
525
|
+
}
|
|
499
526
|
const finishReason = mapFinishReason(delta.delta?.stop_reason);
|
|
500
527
|
controller.enqueue({
|
|
501
528
|
type: "finish",
|
|
@@ -532,10 +559,16 @@ function convertStream(anthropicStream, modelId) {
|
|
|
532
559
|
}
|
|
533
560
|
});
|
|
534
561
|
}
|
|
535
|
-
function processEvent(event, blockStates, modelId) {
|
|
562
|
+
function processEvent(event, blockStates, modelId, ctx) {
|
|
536
563
|
const parts = [];
|
|
537
564
|
switch (event.type) {
|
|
538
565
|
case "message_start": {
|
|
566
|
+
const servedModel = event.message.model;
|
|
567
|
+
if (ctx.fallbacksEnabled && servedModel && servedModel !== ctx.apiModelId) {
|
|
568
|
+
ctx.pendingMeta = {
|
|
569
|
+
servedBy: { from: ctx.apiModelId, to: servedModel, kind: "sticky" }
|
|
570
|
+
};
|
|
571
|
+
}
|
|
539
572
|
parts.push({
|
|
540
573
|
type: "response-metadata",
|
|
541
574
|
id: event.message.id,
|
|
@@ -547,14 +580,38 @@ function processEvent(event, blockStates, modelId) {
|
|
|
547
580
|
case "content_block_start": {
|
|
548
581
|
const index = event.index;
|
|
549
582
|
const block = event.content_block;
|
|
550
|
-
|
|
583
|
+
const takeMeta = () => {
|
|
584
|
+
const meta = ctx.pendingMeta;
|
|
585
|
+
ctx.pendingMeta = undefined;
|
|
586
|
+
return meta;
|
|
587
|
+
};
|
|
588
|
+
if (block.type === "fallback") {
|
|
589
|
+
ctx.pendingMeta = {
|
|
590
|
+
fallback: { from: block.from, to: block.to },
|
|
591
|
+
servedBy: {
|
|
592
|
+
from: block.from?.model ?? ctx.apiModelId,
|
|
593
|
+
to: block.to?.model ?? "unknown",
|
|
594
|
+
kind: "fallback"
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
} else if (block.type === "text") {
|
|
551
598
|
const id = generateId();
|
|
552
|
-
|
|
553
|
-
|
|
599
|
+
const meta = takeMeta();
|
|
600
|
+
blockStates.set(index, { type: "text", id, fallbackMeta: meta });
|
|
601
|
+
parts.push({
|
|
602
|
+
type: "text-start",
|
|
603
|
+
id,
|
|
604
|
+
...meta ? { providerMetadata: { anthropic: meta } } : {}
|
|
605
|
+
});
|
|
554
606
|
} else if (block.type === "thinking") {
|
|
555
607
|
const id = generateId();
|
|
556
|
-
|
|
557
|
-
|
|
608
|
+
const meta = takeMeta();
|
|
609
|
+
blockStates.set(index, { type: "thinking", id, fallbackMeta: meta });
|
|
610
|
+
parts.push({
|
|
611
|
+
type: "reasoning-start",
|
|
612
|
+
id,
|
|
613
|
+
...meta ? { providerMetadata: { anthropic: meta } } : {}
|
|
614
|
+
});
|
|
558
615
|
} else if (block.type === "tool_use") {
|
|
559
616
|
const toolId = block.id;
|
|
560
617
|
const toolName = toOpencodeToolName(block.name);
|
|
@@ -563,7 +620,8 @@ function processEvent(event, blockStates, modelId) {
|
|
|
563
620
|
id: toolId,
|
|
564
621
|
toolUseId: toolId,
|
|
565
622
|
toolName,
|
|
566
|
-
argsText: ""
|
|
623
|
+
argsText: "",
|
|
624
|
+
fallbackMeta: takeMeta()
|
|
567
625
|
});
|
|
568
626
|
parts.push({
|
|
569
627
|
type: "tool-input-start",
|
|
@@ -611,12 +669,14 @@ function processEvent(event, blockStates, modelId) {
|
|
|
611
669
|
if (state.type === "text") {
|
|
612
670
|
parts.push({ type: "text-end", id: state.id });
|
|
613
671
|
} else if (state.type === "thinking") {
|
|
672
|
+
const meta = {
|
|
673
|
+
...state.signature ? { signature: state.signature } : {},
|
|
674
|
+
...state.fallbackMeta
|
|
675
|
+
};
|
|
614
676
|
parts.push({
|
|
615
677
|
type: "reasoning-end",
|
|
616
678
|
id: state.id,
|
|
617
|
-
...
|
|
618
|
-
providerMetadata: { anthropic: { signature: state.signature } }
|
|
619
|
-
} : {}
|
|
679
|
+
...Object.keys(meta).length > 0 ? { providerMetadata: { anthropic: meta } } : {}
|
|
620
680
|
});
|
|
621
681
|
} else if (state.type === "tool_use") {
|
|
622
682
|
parts.push({ type: "tool-input-end", id: state.id });
|
|
@@ -624,7 +684,8 @@ function processEvent(event, blockStates, modelId) {
|
|
|
624
684
|
type: "tool-call",
|
|
625
685
|
toolCallId: state.toolUseId,
|
|
626
686
|
toolName: state.toolName,
|
|
627
|
-
input: state.argsText || "{}"
|
|
687
|
+
input: state.argsText || "{}",
|
|
688
|
+
...state.fallbackMeta ? { providerMetadata: { anthropic: state.fallbackMeta } } : {}
|
|
628
689
|
});
|
|
629
690
|
}
|
|
630
691
|
blockStates.delete(index);
|
|
@@ -695,6 +756,17 @@ function handleApiError(error) {
|
|
|
695
756
|
}
|
|
696
757
|
throw error;
|
|
697
758
|
}
|
|
759
|
+
function isRefusal(stopReason) {
|
|
760
|
+
return stopReason === "refusal";
|
|
761
|
+
}
|
|
762
|
+
function refusalError(stopDetails, fallbackModel) {
|
|
763
|
+
const category = stopDetails?.category ?? null;
|
|
764
|
+
const explanation = stopDetails?.explanation ?? null;
|
|
765
|
+
const categoryNote = category ? ` (category: ${category})` : "";
|
|
766
|
+
const detail = explanation ? explanation : "Claude Fable 5's safety classifiers declined this request.";
|
|
767
|
+
const hint = fallbackModel ? `The configured fallback model (${fallbackModel}) also refused.` : `Retry with a fallback model such as anthropic-sdk/claude-opus-4-8.`;
|
|
768
|
+
return new Error(`Claude Fable 5 refused this request${categoryNote}. ${detail} ${hint}`);
|
|
769
|
+
}
|
|
698
770
|
function mapFinishReason2(stopReason) {
|
|
699
771
|
const unified = (() => {
|
|
700
772
|
switch (stopReason) {
|
|
@@ -711,9 +783,10 @@ function mapFinishReason2(stopReason) {
|
|
|
711
783
|
})();
|
|
712
784
|
return { unified, raw: stopReason ?? undefined };
|
|
713
785
|
}
|
|
786
|
+
var FALLBACK_BETAS_HEADER = "x-anthropic-sdk-fallback-betas";
|
|
714
787
|
var BILLING_SYSTEM_BLOCK = {
|
|
715
788
|
type: "text",
|
|
716
|
-
text: "x-anthropic-billing-header: cc_version=2.1.
|
|
789
|
+
text: "x-anthropic-billing-header: cc_version=2.1.173.d11; cc_entrypoint=sdk-cli; cch=00000;"
|
|
717
790
|
};
|
|
718
791
|
var IDENTITY_SYSTEM_BLOCK = {
|
|
719
792
|
type: "text",
|
|
@@ -847,6 +920,9 @@ class AnthropicSDKModel {
|
|
|
847
920
|
}
|
|
848
921
|
if (anthropicOptions.metadata)
|
|
849
922
|
params.metadata = anthropicOptions.metadata;
|
|
923
|
+
if (typeof anthropicOptions.refusalFallback === "string" && anthropicOptions.refusalFallback.length > 0) {
|
|
924
|
+
params.fallbacks = [{ model: anthropicOptions.refusalFallback }];
|
|
925
|
+
}
|
|
850
926
|
}
|
|
851
927
|
if (this.isOAuth && supportsContextManagement(this.apiModelId)) {
|
|
852
928
|
const edits = [];
|
|
@@ -861,16 +937,19 @@ class AnthropicSDKModel {
|
|
|
861
937
|
params.context_management = { edits };
|
|
862
938
|
}
|
|
863
939
|
}
|
|
864
|
-
|
|
940
|
+
const needsFallbackBetas = params.fallbacks != null || messages.some((msg) => Array.isArray(msg.content) && msg.content.some((block) => block?.type === "fallback"));
|
|
941
|
+
return { params, warnings, needsFallbackBetas };
|
|
865
942
|
}
|
|
866
|
-
buildRequestOptions(signal) {
|
|
943
|
+
buildRequestOptions(signal, needsFallbackBetas) {
|
|
867
944
|
const opts = {};
|
|
868
945
|
if (signal)
|
|
869
946
|
opts.signal = signal;
|
|
947
|
+
if (needsFallbackBetas)
|
|
948
|
+
opts.headers = { [FALLBACK_BETAS_HEADER]: "1" };
|
|
870
949
|
return opts;
|
|
871
950
|
}
|
|
872
951
|
async doGenerate(options) {
|
|
873
|
-
const { params, warnings } = this.buildParams(options);
|
|
952
|
+
const { params, warnings, needsFallbackBetas } = this.buildParams(options);
|
|
874
953
|
if (!options.maxOutputTokens && params.max_tokens > 4096) {
|
|
875
954
|
params.max_tokens = 4096;
|
|
876
955
|
}
|
|
@@ -879,31 +958,74 @@ class AnthropicSDKModel {
|
|
|
879
958
|
response = await this.client.messages.create({
|
|
880
959
|
...params,
|
|
881
960
|
stream: false
|
|
882
|
-
}, this.buildRequestOptions(options.abortSignal));
|
|
961
|
+
}, this.buildRequestOptions(options.abortSignal, needsFallbackBetas));
|
|
883
962
|
} catch (error) {
|
|
884
963
|
handleApiError(error);
|
|
885
964
|
}
|
|
965
|
+
const fallbacksSent = Array.isArray(params.fallbacks) && params.fallbacks.length > 0;
|
|
966
|
+
if (isRefusal(response.stop_reason)) {
|
|
967
|
+
throw refusalError(response.stop_details, fallbacksSent ? params.fallbacks[0].model : undefined);
|
|
968
|
+
}
|
|
969
|
+
const fallbackBlock = response.content.find((b) => b.type === "fallback");
|
|
970
|
+
const sticky = !fallbackBlock && fallbacksSent && response.model && response.model !== this.apiModelId;
|
|
971
|
+
let pendingMeta;
|
|
972
|
+
if (fallbackBlock) {
|
|
973
|
+
pendingMeta = {
|
|
974
|
+
fallback: { from: fallbackBlock.from, to: fallbackBlock.to },
|
|
975
|
+
servedBy: {
|
|
976
|
+
from: fallbackBlock.from?.model ?? this.apiModelId,
|
|
977
|
+
to: fallbackBlock.to?.model ?? "unknown",
|
|
978
|
+
kind: "fallback"
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
} else if (sticky) {
|
|
982
|
+
pendingMeta = {
|
|
983
|
+
servedBy: { from: this.apiModelId, to: response.model, kind: "sticky" }
|
|
984
|
+
};
|
|
985
|
+
}
|
|
886
986
|
const content = [];
|
|
987
|
+
const takeMeta = () => {
|
|
988
|
+
const meta = pendingMeta;
|
|
989
|
+
pendingMeta = undefined;
|
|
990
|
+
return meta;
|
|
991
|
+
};
|
|
992
|
+
let seenFallbackBlock = false;
|
|
887
993
|
for (const block of response.content) {
|
|
888
994
|
switch (block.type) {
|
|
889
|
-
case "text":
|
|
890
|
-
|
|
995
|
+
case "text": {
|
|
996
|
+
const meta = seenFallbackBlock || sticky ? takeMeta() : undefined;
|
|
997
|
+
content.push({
|
|
998
|
+
type: "text",
|
|
999
|
+
text: block.text,
|
|
1000
|
+
...meta ? { providerMetadata: { anthropic: meta } } : {}
|
|
1001
|
+
});
|
|
891
1002
|
break;
|
|
892
|
-
|
|
1003
|
+
}
|
|
1004
|
+
case "tool_use": {
|
|
1005
|
+
const meta = seenFallbackBlock || sticky ? takeMeta() : undefined;
|
|
893
1006
|
content.push({
|
|
894
1007
|
type: "tool-call",
|
|
895
1008
|
toolCallId: block.id,
|
|
896
1009
|
toolName: toOpencodeToolName(block.name),
|
|
897
|
-
input: JSON.stringify(block.input)
|
|
1010
|
+
input: JSON.stringify(block.input),
|
|
1011
|
+
...meta ? { providerMetadata: { anthropic: meta } } : {}
|
|
898
1012
|
});
|
|
899
1013
|
break;
|
|
1014
|
+
}
|
|
900
1015
|
default: {
|
|
901
1016
|
const anyBlock = block;
|
|
902
|
-
if (anyBlock.type === "
|
|
1017
|
+
if (anyBlock.type === "fallback") {
|
|
1018
|
+
seenFallbackBlock = true;
|
|
1019
|
+
} else if (anyBlock.type === "thinking" && anyBlock.thinking) {
|
|
1020
|
+
const extra = seenFallbackBlock || sticky ? takeMeta() : undefined;
|
|
1021
|
+
const meta = {
|
|
1022
|
+
...anyBlock.signature ? { signature: anyBlock.signature } : {},
|
|
1023
|
+
...extra
|
|
1024
|
+
};
|
|
903
1025
|
content.push({
|
|
904
1026
|
type: "reasoning",
|
|
905
1027
|
text: anyBlock.thinking,
|
|
906
|
-
providerMetadata:
|
|
1028
|
+
providerMetadata: Object.keys(meta).length > 0 ? { anthropic: meta } : undefined
|
|
907
1029
|
});
|
|
908
1030
|
}
|
|
909
1031
|
break;
|
|
@@ -942,13 +1064,13 @@ class AnthropicSDKModel {
|
|
|
942
1064
|
};
|
|
943
1065
|
}
|
|
944
1066
|
async doStream(options) {
|
|
945
|
-
const { params, warnings } = this.buildParams(options);
|
|
1067
|
+
const { params, warnings, needsFallbackBetas } = this.buildParams(options);
|
|
946
1068
|
let anthropicStream;
|
|
947
1069
|
try {
|
|
948
1070
|
anthropicStream = await this.client.messages.create({
|
|
949
1071
|
...params,
|
|
950
1072
|
stream: true
|
|
951
|
-
}, this.buildRequestOptions(options.abortSignal));
|
|
1073
|
+
}, this.buildRequestOptions(options.abortSignal, needsFallbackBetas));
|
|
952
1074
|
} catch (error) {
|
|
953
1075
|
if (error instanceof APIError && error.status === 400 && error.message?.includes("must end with a user message")) {
|
|
954
1076
|
return {
|
|
@@ -976,7 +1098,12 @@ class AnthropicSDKModel {
|
|
|
976
1098
|
}
|
|
977
1099
|
handleApiError(error);
|
|
978
1100
|
}
|
|
979
|
-
const
|
|
1101
|
+
const fallbacksSent = Array.isArray(params.fallbacks) && params.fallbacks.length > 0;
|
|
1102
|
+
const stream = convertStream(anthropicStream, this.modelId, {
|
|
1103
|
+
apiModelId: this.apiModelId,
|
|
1104
|
+
fallbacksEnabled: fallbacksSent,
|
|
1105
|
+
fallbackModel: fallbacksSent ? params.fallbacks[0].model : undefined
|
|
1106
|
+
});
|
|
980
1107
|
const wrappedStream = new ReadableStream({
|
|
981
1108
|
async start(controller) {
|
|
982
1109
|
controller.enqueue({
|
|
@@ -1099,13 +1226,14 @@ var API_TTL_MS = 5 * 60000;
|
|
|
1099
1226
|
|
|
1100
1227
|
// src/index.ts
|
|
1101
1228
|
var CLAUDE_CODE_SYSTEM_PROMPT = claudecode_system_default;
|
|
1102
|
-
var
|
|
1229
|
+
var CLAUDE_CODE_NEW_SYSTEM_PROMPT = claudecode_system_new_default;
|
|
1103
1230
|
function selectClaudePromptForModel(modelId) {
|
|
1104
|
-
if (modelId.includes("opus-4-8"))
|
|
1105
|
-
return
|
|
1231
|
+
if (modelId.includes("opus-4-8") || modelId.includes("fable-5")) {
|
|
1232
|
+
return claudecode_system_new_default;
|
|
1233
|
+
}
|
|
1106
1234
|
return claudecode_system_default;
|
|
1107
1235
|
}
|
|
1108
|
-
var CLAUDE_CODE_VERSION = "2.1.
|
|
1236
|
+
var CLAUDE_CODE_VERSION = "2.1.173";
|
|
1109
1237
|
var OAUTH_BETAS = [
|
|
1110
1238
|
"claude-code-20250219",
|
|
1111
1239
|
"oauth-2025-04-20",
|
|
@@ -1122,7 +1250,8 @@ var PROVIDER_ID = "anthropic-sdk";
|
|
|
1122
1250
|
var API_KEY_COSTS = {
|
|
1123
1251
|
haiku: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 },
|
|
1124
1252
|
sonnet: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
1125
|
-
opus: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }
|
|
1253
|
+
opus: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 },
|
|
1254
|
+
fable: { input: 10, output: 50, cache_read: 1, cache_write: 12.5 }
|
|
1126
1255
|
};
|
|
1127
1256
|
var ZERO_COST = { input: 0, output: 0, cache_read: 0, cache_write: 0 };
|
|
1128
1257
|
function buildPluginModels(isOAuth) {
|
|
@@ -1236,6 +1365,21 @@ function buildPluginModels(isOAuth) {
|
|
|
1236
1365
|
},
|
|
1237
1366
|
options: { effort: "medium" },
|
|
1238
1367
|
...opus47Variants
|
|
1368
|
+
},
|
|
1369
|
+
"claude-fable-5": {
|
|
1370
|
+
name: "Claude Fable 5",
|
|
1371
|
+
reasoning: true,
|
|
1372
|
+
tool_call: true,
|
|
1373
|
+
attachment: true,
|
|
1374
|
+
temperature: true,
|
|
1375
|
+
limit: { context: 1e6, output: 128000 },
|
|
1376
|
+
cost: cost("fable"),
|
|
1377
|
+
modalities: {
|
|
1378
|
+
input: ["text", "image", "pdf"],
|
|
1379
|
+
output: ["text"]
|
|
1380
|
+
},
|
|
1381
|
+
options: { effort: "medium", refusalFallback: "claude-opus-4-8" },
|
|
1382
|
+
...opus47Variants
|
|
1239
1383
|
}
|
|
1240
1384
|
};
|
|
1241
1385
|
}
|
|
@@ -1300,7 +1444,7 @@ function createAnthropicSDK(options = {}) {
|
|
|
1300
1444
|
if (!betas.includes("effort-2025-11-24"))
|
|
1301
1445
|
betas.push("effort-2025-11-24");
|
|
1302
1446
|
}
|
|
1303
|
-
if (model.includes("opus-4-8")) {
|
|
1447
|
+
if (model.includes("opus-4-8") || model.includes("fable-5")) {
|
|
1304
1448
|
if (!betas.includes("mid-conversation-system-2026-04-07")) {
|
|
1305
1449
|
betas.push("mid-conversation-system-2026-04-07");
|
|
1306
1450
|
}
|
|
@@ -1308,6 +1452,19 @@ function createAnthropicSDK(options = {}) {
|
|
|
1308
1452
|
reqHeaders.set("anthropic-beta", betas.join(","));
|
|
1309
1453
|
init = { ...init, headers: reqHeaders };
|
|
1310
1454
|
}
|
|
1455
|
+
{
|
|
1456
|
+
const reqHeaders = new Headers(init?.headers);
|
|
1457
|
+
if (reqHeaders.has(FALLBACK_BETAS_HEADER)) {
|
|
1458
|
+
reqHeaders.delete(FALLBACK_BETAS_HEADER);
|
|
1459
|
+
const betas = (reqHeaders.get("anthropic-beta") ?? "").split(",").map((beta) => beta.trim()).filter(Boolean);
|
|
1460
|
+
for (const beta of ["server-side-fallback-2026-06-01", "fallback-credit-2026-06-01"]) {
|
|
1461
|
+
if (!betas.includes(beta))
|
|
1462
|
+
betas.push(beta);
|
|
1463
|
+
}
|
|
1464
|
+
reqHeaders.set("anthropic-beta", betas.join(","));
|
|
1465
|
+
init = { ...init, headers: reqHeaders };
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1311
1468
|
const resp = await baseFetch(url, init);
|
|
1312
1469
|
const h5 = resp.headers.get("anthropic-ratelimit-unified-5h-utilization");
|
|
1313
1470
|
if (h5 != null) {
|
|
@@ -1356,10 +1513,22 @@ var anthropicSDKPlugin = async () => {
|
|
|
1356
1513
|
if (!cfg.provider) {
|
|
1357
1514
|
cfg.provider = {};
|
|
1358
1515
|
}
|
|
1516
|
+
const userProvider = cfg.provider[PROVIDER_ID] ?? {};
|
|
1517
|
+
const defaultModels = buildPluginModels(isOAuth);
|
|
1518
|
+
const models = { ...defaultModels };
|
|
1519
|
+
for (const [modelID, userModel] of Object.entries(userProvider.models ?? {})) {
|
|
1520
|
+
const base = defaultModels[modelID];
|
|
1521
|
+
models[modelID] = base ? {
|
|
1522
|
+
...base,
|
|
1523
|
+
...userModel,
|
|
1524
|
+
options: { ...base.options, ...userModel?.options },
|
|
1525
|
+
variants: { ...base.variants, ...userModel?.variants }
|
|
1526
|
+
} : userModel;
|
|
1527
|
+
}
|
|
1359
1528
|
cfg.provider[PROVIDER_ID] = {
|
|
1360
1529
|
npm: PACKAGE_NAME,
|
|
1361
|
-
|
|
1362
|
-
|
|
1530
|
+
...userProvider,
|
|
1531
|
+
models
|
|
1363
1532
|
};
|
|
1364
1533
|
},
|
|
1365
1534
|
"experimental.chat.system.transform": async (input, output) => {
|
package/build/stream.d.ts
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
import type { LanguageModelV3StreamPart } from "@ai-sdk/provider";
|
|
2
2
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
3
|
type MessageStreamEvent = Anthropic.MessageStreamEvent;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Options and mutable state for one streamed response.
|
|
6
|
+
*
|
|
7
|
+
* Server-side fallback (Fable 5 safety refusals → Opus 4.8) surfaces in two
|
|
8
|
+
* ways that we translate into part metadata:
|
|
9
|
+
*
|
|
10
|
+
* - A `fallback` content block (content_block_start/stop, no deltas) marks
|
|
11
|
+
* the hop where the declining model's output gives way to the fallback
|
|
12
|
+
* model. We don't emit a part for it; instead we carry two keys on the
|
|
13
|
+
* NEXT block's part metadata (OpenCode persists part metadata and syncs
|
|
14
|
+
* it to the TUI — the same round trip thinking signatures use):
|
|
15
|
+
* anthropic.fallback — {from, to}; prompt.ts re-inserts the block into
|
|
16
|
+
* history verbatim. Position is load-bearing for thinking
|
|
17
|
+
* verification, so it must precede exactly this block.
|
|
18
|
+
* anthropic.servedBy — {from, to, kind} display-only notice for the
|
|
19
|
+
* TUI plugin (toast + sidebar). Never echoed back.
|
|
20
|
+
* - Sticky-routed follow-up turns carry no block; message_start announcing
|
|
21
|
+
* a different model than requested is the signal. servedBy (kind
|
|
22
|
+
* "sticky") attaches to the first content block.
|
|
23
|
+
*/
|
|
24
|
+
interface StreamContext {
|
|
25
|
+
/** Model ID requested by the caller (without -1m suffix). */
|
|
26
|
+
apiModelId: string;
|
|
27
|
+
/** Whether the request carried a `fallbacks` chain. */
|
|
28
|
+
fallbacksEnabled: boolean;
|
|
29
|
+
/** First fallback model — used for error wording when the chain refuses. */
|
|
30
|
+
fallbackModel?: string;
|
|
31
|
+
/** Metadata waiting to be attached to the next content block. */
|
|
32
|
+
pendingMeta?: Record<string, any>;
|
|
33
|
+
}
|
|
34
|
+
export declare function convertStream(anthropicStream: AsyncIterable<MessageStreamEvent>, modelId: string, context?: Partial<StreamContext>): ReadableStream<LanguageModelV3StreamPart>;
|
|
5
35
|
export {};
|
|
6
36
|
//# sourceMappingURL=stream.d.ts.map
|
package/build/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAC9F,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,kBAAkB,GAAG,SAAS,CAAC,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAC9F,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,kBAAkB,GAAG,SAAS,CAAC,kBAAkB,CAAA;AAetD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,UAAU,aAAa;IACrB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAA;IACzB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAClC;AA0CD,wBAAgB,aAAa,CAC3B,eAAe,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAClD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAC/B,cAAc,CAAC,yBAAyB,CAAC,CA0E3C"}
|
package/build/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAA;AAujBhC,QAAA,MAAM,MAAM,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAG3C,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xtruder/opencode-claude-max-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "OpenCode plugin for Claude Max/Pro subscription via @anthropic-ai/sdk - uses Claude Code OAuth credentials",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"prepare": "simple-git-hooks"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@anthropic-ai/sdk": "0.
|
|
52
|
+
"@anthropic-ai/sdk": "0.104.1",
|
|
53
53
|
"xxhash-wasm": "^1.1.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|