kanna-code 0.41.5 → 0.41.7
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/dist/client/assets/{index-CTT6qioa.js → index-BWrXLfF8.js} +197 -197
- package/dist/client/index.html +1 -1
- package/package.json +5 -3
- package/src/server/agent.ts +19 -0
- package/src/server/app-settings.test.ts +1 -1
- package/src/server/app-settings.ts +1 -1
- package/src/server/diff-store.test.ts +47 -0
- package/src/server/diff-store.ts +2 -5
- package/src/server/provider-catalog.test.ts +25 -4
- package/src/server/provider-catalog.ts +78 -9
- package/src/server/ws-router.test.ts +1 -1
- package/src/server/ws-router.ts +1 -1
- package/src/shared/types.test.ts +11 -2
- package/src/shared/types.ts +20 -5
package/dist/client/index.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
})()
|
|
26
26
|
</script>
|
|
27
27
|
<title>Kanna</title>
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-BWrXLfF8.js"></script>
|
|
29
29
|
<link rel="stylesheet" crossorigin href="/assets/index-BujIAIt0.css">
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kanna-code",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.41.
|
|
4
|
+
"version": "0.41.7",
|
|
5
5
|
"description": "A beautiful web UI for Claude Code",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"prepublishOnly": "bun run build"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
51
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.170",
|
|
52
|
+
"@anthropic-ai/sdk": "^0.104.0",
|
|
52
53
|
"@legendapp/list": "3.0.0-beta.44",
|
|
53
54
|
"@pierre/diffs": "^1.1.12",
|
|
54
55
|
"@radix-ui/react-context-menu": "^2.2.16",
|
|
@@ -63,7 +64,8 @@
|
|
|
63
64
|
"file-type": "^22.0.0",
|
|
64
65
|
"openai": "^6.34.0",
|
|
65
66
|
"react-resizable-panels": "^4.7.3",
|
|
66
|
-
"uqr": "^0.1.3"
|
|
67
|
+
"uqr": "^0.1.3",
|
|
68
|
+
"zod": "^4.4.3"
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
71
|
"@dnd-kit/core": "^6.3.1",
|
package/src/server/agent.ts
CHANGED
|
@@ -20,6 +20,8 @@ import { CodexAppServerManager } from "./codex-app-server"
|
|
|
20
20
|
import { type GenerateChatTitleResult, generateTitleForChatDetailed } from "./generate-title"
|
|
21
21
|
import type { HarnessEvent, HarnessToolRequest, HarnessTurn } from "./harness-types"
|
|
22
22
|
import {
|
|
23
|
+
applyClaudeSdkModels,
|
|
24
|
+
type ClaudeSdkModelInfo,
|
|
23
25
|
codexServiceTierFromModelOptions,
|
|
24
26
|
getServerProviderCatalog,
|
|
25
27
|
normalizeClaudeModelOptions,
|
|
@@ -82,6 +84,7 @@ interface ClaudeSessionHandle {
|
|
|
82
84
|
sendPrompt: (content: string) => Promise<void>
|
|
83
85
|
setModel: (model: string) => Promise<void>
|
|
84
86
|
setPermissionMode: (planMode: boolean) => Promise<void>
|
|
87
|
+
supportedModels?: () => Promise<ClaudeSdkModelInfo[]>
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
interface ClaudeSessionState {
|
|
@@ -664,6 +667,7 @@ async function startClaudeSession(args: {
|
|
|
664
667
|
setPermissionMode: async (planMode: boolean) => {
|
|
665
668
|
await q.setPermissionMode(planMode ? "plan" : "acceptEdits")
|
|
666
669
|
},
|
|
670
|
+
supportedModels: async () => await q.supportedModels(),
|
|
667
671
|
close: () => {
|
|
668
672
|
promptQueue.close()
|
|
669
673
|
q.close()
|
|
@@ -718,6 +722,20 @@ export class AgentCoordinator {
|
|
|
718
722
|
this.onStateChange(chatId, options)
|
|
719
723
|
}
|
|
720
724
|
|
|
725
|
+
private refreshClaudeModelCatalog(session: ClaudeSessionHandle) {
|
|
726
|
+
if (!session.supportedModels) return
|
|
727
|
+
void session.supportedModels()
|
|
728
|
+
.then((models) => {
|
|
729
|
+
if (applyClaudeSdkModels(models)) {
|
|
730
|
+
this.emitStateChange(undefined, { immediate: true })
|
|
731
|
+
}
|
|
732
|
+
})
|
|
733
|
+
.catch((error) => {
|
|
734
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
735
|
+
this.reportBackgroundError?.(`[claude-models] failed to refresh Claude model catalog: ${message}`)
|
|
736
|
+
})
|
|
737
|
+
}
|
|
738
|
+
|
|
721
739
|
getActiveTurnProfile(chatId: string): SendToStartingProfile | null {
|
|
722
740
|
const active = this.activeTurns.get(chatId)
|
|
723
741
|
if (!active?.clientTraceId || active.profilingStartedAt === undefined) {
|
|
@@ -1078,6 +1096,7 @@ export class AgentCoordinator {
|
|
|
1078
1096
|
forkSession: args.forkSession,
|
|
1079
1097
|
onToolRequest: args.onToolRequest,
|
|
1080
1098
|
})
|
|
1099
|
+
this.refreshClaudeModelCatalog(started)
|
|
1081
1100
|
|
|
1082
1101
|
session = {
|
|
1083
1102
|
id: crypto.randomUUID(),
|
|
@@ -36,7 +36,7 @@ function expectedSettingsSnapshot(filePath: string, overrides: Partial<AppSettin
|
|
|
36
36
|
defaultProvider: "last_used",
|
|
37
37
|
providerDefaults: {
|
|
38
38
|
claude: {
|
|
39
|
-
model: "claude-opus-4-
|
|
39
|
+
model: "claude-opus-4-8",
|
|
40
40
|
modelOptions: {
|
|
41
41
|
reasoningEffort: "high",
|
|
42
42
|
contextWindow: "200k",
|
|
@@ -99,7 +99,7 @@ function getDefaultEditorCommandTemplate(preset: EditorPreset) {
|
|
|
99
99
|
function createDefaultProviderDefaults(): ChatProviderPreferences {
|
|
100
100
|
return {
|
|
101
101
|
claude: {
|
|
102
|
-
model: "claude-opus-4-
|
|
102
|
+
model: "claude-opus-4-8",
|
|
103
103
|
modelOptions: { ...DEFAULT_CLAUDE_MODEL_OPTIONS },
|
|
104
104
|
planMode: false,
|
|
105
105
|
},
|
|
@@ -633,6 +633,53 @@ describe("DiffStore", () => {
|
|
|
633
633
|
expect((await run(["git", "branch", "--show-current"], repoRoot)).trim()).toBe("feature/new")
|
|
634
634
|
})
|
|
635
635
|
|
|
636
|
+
test("syncBranch pull rebases divergent local commits onto the upstream branch", async () => {
|
|
637
|
+
const repoRoot = await createRepo()
|
|
638
|
+
const remoteRoot = await createBareRemote()
|
|
639
|
+
const remoteWorktree = await mkdtemp(path.join(tmpdir(), "kanna-diff-remote-worktree-"))
|
|
640
|
+
tempDirs.push(repoRoot, remoteRoot, remoteWorktree)
|
|
641
|
+
|
|
642
|
+
await writeFile(path.join(repoRoot, "app.txt"), "base\n", "utf8")
|
|
643
|
+
await run(["git", "add", "."], repoRoot)
|
|
644
|
+
await run(["git", "commit", "-m", "base"], repoRoot)
|
|
645
|
+
await run(["git", "branch", "-M", "main"], repoRoot)
|
|
646
|
+
await run(["git", "remote", "add", "origin", remoteRoot], repoRoot)
|
|
647
|
+
await run(["git", "push", "-u", "origin", "main"], repoRoot)
|
|
648
|
+
|
|
649
|
+
await run(["git", "clone", "-b", "main", remoteRoot, remoteWorktree], tmpdir())
|
|
650
|
+
await run(["git", "config", "user.email", "kanna@example.com"], remoteWorktree)
|
|
651
|
+
await run(["git", "config", "user.name", "Kanna"], remoteWorktree)
|
|
652
|
+
await writeFile(path.join(remoteWorktree, "remote.txt"), "remote\n", "utf8")
|
|
653
|
+
await run(["git", "add", "."], remoteWorktree)
|
|
654
|
+
await run(["git", "commit", "-m", "remote change"], remoteWorktree)
|
|
655
|
+
await run(["git", "push"], remoteWorktree)
|
|
656
|
+
|
|
657
|
+
await writeFile(path.join(repoRoot, "local.txt"), "local\n", "utf8")
|
|
658
|
+
await run(["git", "add", "."], repoRoot)
|
|
659
|
+
await run(["git", "commit", "-m", "local change"], repoRoot)
|
|
660
|
+
|
|
661
|
+
const store = new DiffStore(repoRoot)
|
|
662
|
+
await store.initialize()
|
|
663
|
+
const result = await store.syncBranch({
|
|
664
|
+
projectId: "project-1",
|
|
665
|
+
projectPath: repoRoot,
|
|
666
|
+
action: "pull",
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
expect(result).toMatchObject({
|
|
670
|
+
ok: true,
|
|
671
|
+
action: "pull",
|
|
672
|
+
branchName: "main",
|
|
673
|
+
aheadCount: 1,
|
|
674
|
+
behindCount: 0,
|
|
675
|
+
snapshotChanged: true,
|
|
676
|
+
})
|
|
677
|
+
expect((await run(["git", "log", "--format=%s", "-2"], repoRoot)).trim().split("\n")).toEqual([
|
|
678
|
+
"local change",
|
|
679
|
+
"remote change",
|
|
680
|
+
])
|
|
681
|
+
})
|
|
682
|
+
|
|
636
683
|
test("previewMergeBranch reports up-to-date and mergeable states", async () => {
|
|
637
684
|
const repoRoot = await createRepo()
|
|
638
685
|
tempDirs.push(repoRoot)
|
package/src/server/diff-store.ts
CHANGED
|
@@ -1903,7 +1903,7 @@ export class DiffStore {
|
|
|
1903
1903
|
}
|
|
1904
1904
|
|
|
1905
1905
|
const syncResult = args.action === "pull"
|
|
1906
|
-
? await runGit(["pull", "--
|
|
1906
|
+
? await runGit(["pull", "--rebase", "--autostash"], repo.repoRoot)
|
|
1907
1907
|
: await runGit(["fetch", "--all", "--prune"], repo.repoRoot)
|
|
1908
1908
|
|
|
1909
1909
|
if (syncResult.exitCode !== 0) {
|
|
@@ -1912,10 +1912,7 @@ export class DiffStore {
|
|
|
1912
1912
|
let title = args.action === "pull" ? "Pull failed" : "Fetch failed"
|
|
1913
1913
|
let message = summarizeGitFailure(detail, args.action === "pull" ? "Git could not pull the latest changes." : "Git could not fetch the latest changes.")
|
|
1914
1914
|
|
|
1915
|
-
if (
|
|
1916
|
-
title = "Pull requires merge or rebase"
|
|
1917
|
-
message = "Your branch cannot be fast-forwarded. Rebase or merge manually, then try again."
|
|
1918
|
-
} else if (normalized.includes("could not read from remote repository") || normalized.includes("authentication failed") || normalized.includes("permission denied")) {
|
|
1915
|
+
if (normalized.includes("could not read from remote repository") || normalized.includes("authentication failed") || normalized.includes("permission denied")) {
|
|
1919
1916
|
title = "Remote authentication failed"
|
|
1920
1917
|
message = "Git could not authenticate with the remote repository."
|
|
1921
1918
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
2
|
import {
|
|
3
|
+
SERVER_PROVIDERS,
|
|
4
|
+
applyClaudeSdkModels,
|
|
3
5
|
codexServiceTierFromModelOptions,
|
|
4
6
|
normalizeClaudeModelOptions,
|
|
5
7
|
normalizeCodexModelOptions,
|
|
6
8
|
normalizeServerModel,
|
|
9
|
+
resetServerProvidersForTests,
|
|
7
10
|
} from "./provider-catalog"
|
|
8
11
|
import { resolveClaudeApiModelId } from "../shared/types"
|
|
9
12
|
|
|
10
13
|
describe("provider catalog normalization", () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
resetServerProvidersForTests()
|
|
16
|
+
})
|
|
17
|
+
|
|
11
18
|
test("maps legacy Claude effort into shared model options", () => {
|
|
12
|
-
expect(normalizeClaudeModelOptions("claude-opus-4-
|
|
19
|
+
expect(normalizeClaudeModelOptions("claude-opus-4-8", undefined, "max")).toEqual({
|
|
13
20
|
reasoningEffort: "max",
|
|
14
21
|
contextWindow: "200k",
|
|
15
22
|
})
|
|
@@ -58,12 +65,26 @@ describe("provider catalog normalization", () => {
|
|
|
58
65
|
|
|
59
66
|
test("normalizes server model ids through the shared alias catalog", () => {
|
|
60
67
|
expect(normalizeServerModel("codex")).toBe("gpt-5.5")
|
|
61
|
-
expect(normalizeServerModel("claude", "
|
|
68
|
+
expect(normalizeServerModel("claude", "fable")).toBe("fable")
|
|
69
|
+
expect(normalizeServerModel("claude", "opus")).toBe("claude-opus-4-8")
|
|
62
70
|
expect(normalizeServerModel("codex", "gpt-5-codex")).toBe("gpt-5.3-codex")
|
|
63
71
|
})
|
|
64
72
|
|
|
65
73
|
test("resolves Claude API model ids for 1m context window", () => {
|
|
66
|
-
expect(resolveClaudeApiModelId("claude-opus-4-
|
|
74
|
+
expect(resolveClaudeApiModelId("claude-opus-4-8", "1m")).toBe("claude-opus-4-8[1m]")
|
|
75
|
+
expect(resolveClaudeApiModelId("fable", "200k")).toBe("fable")
|
|
67
76
|
expect(resolveClaudeApiModelId("claude-sonnet-4-6", "200k")).toBe("claude-sonnet-4-6")
|
|
68
77
|
})
|
|
78
|
+
|
|
79
|
+
test("overlays Claude model labels from the Agent SDK model catalog", () => {
|
|
80
|
+
expect(applyClaudeSdkModels([
|
|
81
|
+
{ value: "claude-fable-5[1m]", displayName: "Fable from SDK", supportsEffort: true },
|
|
82
|
+
{ value: "claude-opus-4-7", displayName: "Opus 4.7", supportsEffort: true },
|
|
83
|
+
{ value: "claude-opus-4-8", displayName: "Opus from SDK", supportsEffort: true },
|
|
84
|
+
])).toBe(true)
|
|
85
|
+
|
|
86
|
+
const claude = SERVER_PROVIDERS.find((provider) => provider.id === "claude")
|
|
87
|
+
expect(claude?.models.find((model) => model.id === "fable")?.label).toBe("Fable from SDK")
|
|
88
|
+
expect(claude?.models.find((model) => model.id === "claude-opus-4-8")?.label).toBe("Opus from SDK")
|
|
89
|
+
})
|
|
69
90
|
})
|
|
@@ -25,15 +25,84 @@ const HARD_CODED_CODEX_MODELS: ProviderModelOption[] = [
|
|
|
25
25
|
{ id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark", supportsEffort: false },
|
|
26
26
|
]
|
|
27
27
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
export interface ClaudeSdkModelInfo {
|
|
29
|
+
value: string
|
|
30
|
+
displayName?: string
|
|
31
|
+
description?: string
|
|
32
|
+
supportsEffort?: boolean
|
|
33
|
+
supportedEffortLevels?: readonly string[]
|
|
34
|
+
supportsAdaptiveThinking?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createServerProviders(): ProviderCatalogEntry[] {
|
|
38
|
+
return PROVIDERS.map((provider) =>
|
|
39
|
+
provider.id === "codex"
|
|
40
|
+
? {
|
|
41
|
+
...provider,
|
|
42
|
+
defaultModel: "gpt-5.5",
|
|
43
|
+
models: HARD_CODED_CODEX_MODELS,
|
|
44
|
+
}
|
|
45
|
+
: provider
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const SERVER_PROVIDERS: ProviderCatalogEntry[] = createServerProviders()
|
|
50
|
+
|
|
51
|
+
export function resetServerProvidersForTests() {
|
|
52
|
+
SERVER_PROVIDERS.splice(0, SERVER_PROVIDERS.length, ...createServerProviders())
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function modelFamily(value: string) {
|
|
56
|
+
const match = value.match(/^(?:claude-)?([a-z]+)(?:-|$)/i)
|
|
57
|
+
return match?.[1]?.toLowerCase() ?? value.toLowerCase()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function sdkModelMatchScore(model: ClaudeSdkModelInfo, option: ProviderModelOption) {
|
|
61
|
+
const modelValue = model.value.toLowerCase()
|
|
62
|
+
if (modelValue === option.id.toLowerCase()) return 3
|
|
63
|
+
if (option.aliases?.some((alias) => alias.toLowerCase() === modelValue)) return 2
|
|
64
|
+
const optionKeys = [option.id, ...(option.aliases ?? [])].map(modelFamily)
|
|
65
|
+
return optionKeys.includes(modelFamily(model.value)) ? 1 : 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function findSdkModelForOption(models: readonly ClaudeSdkModelInfo[], option: ProviderModelOption) {
|
|
69
|
+
let bestModel: ClaudeSdkModelInfo | undefined
|
|
70
|
+
let bestScore = 0
|
|
71
|
+
for (const model of models) {
|
|
72
|
+
const score = sdkModelMatchScore(model, option)
|
|
73
|
+
if (score > bestScore) {
|
|
74
|
+
bestModel = model
|
|
75
|
+
bestScore = score
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return bestModel
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function applyClaudeSdkModels(models: readonly ClaudeSdkModelInfo[]) {
|
|
82
|
+
const claudeIndex = SERVER_PROVIDERS.findIndex((provider) => provider.id === "claude")
|
|
83
|
+
const claudeProvider = SERVER_PROVIDERS[claudeIndex]
|
|
84
|
+
if (!claudeProvider) return false
|
|
85
|
+
|
|
86
|
+
const nextModels = claudeProvider.models.map((option) => {
|
|
87
|
+
const sdkModel = findSdkModelForOption(models, option)
|
|
88
|
+
if (!sdkModel) return option
|
|
89
|
+
return {
|
|
90
|
+
...option,
|
|
91
|
+
label: sdkModel.displayName?.trim() || option.label,
|
|
92
|
+
supportsEffort: sdkModel.supportsEffort ?? option.supportsEffort,
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
if (JSON.stringify(nextModels) === JSON.stringify(claudeProvider.models)) {
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
SERVER_PROVIDERS.splice(claudeIndex, 1, {
|
|
101
|
+
...claudeProvider,
|
|
102
|
+
models: nextModels,
|
|
103
|
+
})
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
37
106
|
|
|
38
107
|
export function getServerProviderCatalog(provider: AgentProvider): ProviderCatalogEntry {
|
|
39
108
|
const entry = SERVER_PROVIDERS.find((candidate) => candidate.id === provider)
|
|
@@ -87,7 +87,7 @@ const DEFAULT_APP_SETTINGS_SNAPSHOT: AppSettingsSnapshot = {
|
|
|
87
87
|
defaultProvider: "last_used",
|
|
88
88
|
providerDefaults: {
|
|
89
89
|
claude: {
|
|
90
|
-
model: "claude-opus-4-
|
|
90
|
+
model: "claude-opus-4-8",
|
|
91
91
|
modelOptions: {
|
|
92
92
|
reasoningEffort: "high",
|
|
93
93
|
contextWindow: "200k",
|
package/src/server/ws-router.ts
CHANGED
package/src/shared/types.test.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
2
|
import {
|
|
3
|
+
deriveClaudeModelLabel,
|
|
3
4
|
normalizeClaudeModelId,
|
|
4
5
|
normalizeCodexModelId,
|
|
5
6
|
supportsClaudeMaxReasoningEffort,
|
|
6
7
|
} from "./types"
|
|
7
8
|
|
|
8
9
|
describe("shared model normalization", () => {
|
|
10
|
+
test("derives fallback Claude model labels from model ids", () => {
|
|
11
|
+
expect(deriveClaudeModelLabel("fable")).toBe("Fable")
|
|
12
|
+
expect(deriveClaudeModelLabel("claude-opus-4-8")).toBe("Opus")
|
|
13
|
+
expect(deriveClaudeModelLabel("claude-haiku-4-5-20251001")).toBe("Haiku")
|
|
14
|
+
})
|
|
15
|
+
|
|
9
16
|
test("normalizes Claude aliases via the provider catalog", () => {
|
|
10
|
-
expect(normalizeClaudeModelId("
|
|
17
|
+
expect(normalizeClaudeModelId("fable")).toBe("fable")
|
|
18
|
+
expect(normalizeClaudeModelId("opus")).toBe("claude-opus-4-8")
|
|
11
19
|
expect(normalizeClaudeModelId("sonnet")).toBe("claude-sonnet-4-6")
|
|
12
20
|
expect(normalizeClaudeModelId("haiku")).toBe("claude-haiku-4-5-20251001")
|
|
13
21
|
})
|
|
@@ -18,8 +26,9 @@ describe("shared model normalization", () => {
|
|
|
18
26
|
})
|
|
19
27
|
|
|
20
28
|
test("uses declarative metadata for Claude max-effort support", () => {
|
|
21
|
-
expect(supportsClaudeMaxReasoningEffort("claude-opus-4-
|
|
29
|
+
expect(supportsClaudeMaxReasoningEffort("claude-opus-4-8")).toBe(true)
|
|
22
30
|
expect(supportsClaudeMaxReasoningEffort("opus")).toBe(true)
|
|
31
|
+
expect(supportsClaudeMaxReasoningEffort("fable")).toBe(false)
|
|
23
32
|
expect(supportsClaudeMaxReasoningEffort("claude-sonnet-4-6")).toBe(false)
|
|
24
33
|
})
|
|
25
34
|
})
|
package/src/shared/types.ts
CHANGED
|
@@ -228,6 +228,16 @@ export function isClaudeContextWindow(value: unknown): value is ClaudeContextWin
|
|
|
228
228
|
return CLAUDE_CONTEXT_WINDOW_OPTIONS.some((option) => option.id === value)
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
function titleCaseWord(value: string) {
|
|
232
|
+
return value.length === 0 ? value : `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}`
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function deriveClaudeModelLabel(modelId: string): string {
|
|
236
|
+
const parts = modelId.replace(/^claude-/, "").split("-").filter(Boolean)
|
|
237
|
+
if (parts.length === 0) return modelId
|
|
238
|
+
return titleCaseWord(parts[0] ?? modelId)
|
|
239
|
+
}
|
|
240
|
+
|
|
231
241
|
export interface ProviderCatalogEntry {
|
|
232
242
|
id: AgentProvider
|
|
233
243
|
label: string
|
|
@@ -247,8 +257,13 @@ export const PROVIDERS: ProviderCatalogEntry[] = [
|
|
|
247
257
|
supportsPlanMode: true,
|
|
248
258
|
models: [
|
|
249
259
|
{
|
|
250
|
-
id: "
|
|
251
|
-
label: "
|
|
260
|
+
id: "fable",
|
|
261
|
+
label: deriveClaudeModelLabel("fable"),
|
|
262
|
+
supportsEffort: true,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: "claude-opus-4-8",
|
|
266
|
+
label: deriveClaudeModelLabel("claude-opus-4-8"),
|
|
252
267
|
supportsEffort: true,
|
|
253
268
|
aliases: ["opus"],
|
|
254
269
|
contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
|
|
@@ -256,14 +271,14 @@ export const PROVIDERS: ProviderCatalogEntry[] = [
|
|
|
256
271
|
},
|
|
257
272
|
{
|
|
258
273
|
id: "claude-sonnet-4-6",
|
|
259
|
-
label: "
|
|
274
|
+
label: deriveClaudeModelLabel("claude-sonnet-4-6"),
|
|
260
275
|
supportsEffort: true,
|
|
261
276
|
aliases: ["sonnet"],
|
|
262
277
|
contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
|
|
263
278
|
},
|
|
264
279
|
{
|
|
265
280
|
id: "claude-haiku-4-5-20251001",
|
|
266
|
-
label: "
|
|
281
|
+
label: deriveClaudeModelLabel("claude-haiku-4-5-20251001"),
|
|
267
282
|
supportsEffort: true,
|
|
268
283
|
aliases: ["haiku"],
|
|
269
284
|
},
|
|
@@ -311,7 +326,7 @@ export function normalizeProviderModelId(
|
|
|
311
326
|
?? getProviderCatalog(provider).defaultModel
|
|
312
327
|
}
|
|
313
328
|
|
|
314
|
-
export function normalizeClaudeModelId(modelId?: string, fallbackModelId = "claude-opus-4-
|
|
329
|
+
export function normalizeClaudeModelId(modelId?: string, fallbackModelId = "claude-opus-4-8"): string {
|
|
315
330
|
return normalizeProviderModelId("claude", modelId, fallbackModelId)
|
|
316
331
|
}
|
|
317
332
|
|