ubume 0.1.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 +88 -0
- package/bin/codexa-local-harness-bridge.js +3 -0
- package/bin/codexa.js +28 -0
- package/bin/ubume-local-harness-bridge.js +325 -0
- package/bin/ubume.js +398 -0
- package/package.json +66 -0
- package/src/app.tsx +5759 -0
- package/src/commands/handler.ts +889 -0
- package/src/config/appVersion.ts +69 -0
- package/src/config/buildInfo.ts +3 -0
- package/src/config/launchArgs.ts +196 -0
- package/src/config/layeredConfig.ts +853 -0
- package/src/config/legacyEnv.ts +16 -0
- package/src/config/persistence.ts +377 -0
- package/src/config/runtimeConfig.ts +558 -0
- package/src/config/settings.ts +405 -0
- package/src/config/toml-serialize.ts +109 -0
- package/src/config/trustStore.ts +84 -0
- package/src/config/updateCheckCache.ts +85 -0
- package/src/core/README.md +55 -0
- package/src/core/agent/loop.ts +464 -0
- package/src/core/agent/protocol.ts +345 -0
- package/src/core/agent/tools.ts +423 -0
- package/src/core/auth/codexAuth.ts +359 -0
- package/src/core/codex/codexExecArgs.ts +166 -0
- package/src/core/codex/codexLaunch.ts +163 -0
- package/src/core/codex/codexPrompt.ts +429 -0
- package/src/core/debug/inputDebug.ts +51 -0
- package/src/core/debug/localStreamDebug.ts +50 -0
- package/src/core/debug/modelStateDebug.ts +35 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/claudeExecutable.ts +63 -0
- package/src/core/executables/codexExecutable.ts +160 -0
- package/src/core/executables/executableResolver.ts +164 -0
- package/src/core/executables/geminiExecutable.ts +78 -0
- package/src/core/models/codexCapabilities.ts +97 -0
- package/src/core/models/codexModelCapabilities.ts +624 -0
- package/src/core/models/codexModelsCacheSeed.ts +153 -0
- package/src/core/models/modelSpecs.ts +303 -0
- package/src/core/models/providerModelCache.ts +94 -0
- package/src/core/perf/profiler.ts +125 -0
- package/src/core/perf/renderDebug.ts +398 -0
- package/src/core/process/CommandRunner.ts +280 -0
- package/src/core/process/processValidation.ts +111 -0
- package/src/core/providerLauncher/launcher.ts +220 -0
- package/src/core/providerLauncher/registry.ts +354 -0
- package/src/core/providerLauncher/types.ts +95 -0
- package/src/core/providerLauncher/workspaceConfig.ts +487 -0
- package/src/core/providerRuntime/anthropic.ts +580 -0
- package/src/core/providerRuntime/antigravity.ts +500 -0
- package/src/core/providerRuntime/capabilityProfile.ts +383 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -0
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/codexaCupy.ts +97 -0
- package/src/core/providerRuntime/codexaNative.ts +425 -0
- package/src/core/providerRuntime/contextMetadata.ts +397 -0
- package/src/core/providerRuntime/gemini.ts +789 -0
- package/src/core/providerRuntime/lmstudio.ts +118 -0
- package/src/core/providerRuntime/local.ts +770 -0
- package/src/core/providerRuntime/localHarness/runtime.ts +1090 -0
- package/src/core/providerRuntime/localOutputBudget.ts +17 -0
- package/src/core/providerRuntime/mistralVibe.ts +667 -0
- package/src/core/providerRuntime/models.ts +175 -0
- package/src/core/providerRuntime/reasoning.ts +20 -0
- package/src/core/providerRuntime/registry.ts +286 -0
- package/src/core/providerRuntime/types.ts +142 -0
- package/src/core/providerRuntime/unsloth.ts +216 -0
- package/src/core/providers/codexJsonStream.ts +305 -0
- package/src/core/providers/codexSubprocess.ts +378 -0
- package/src/core/providers/codexTranscript.ts +695 -0
- package/src/core/providers/openaiNative.ts +13 -0
- package/src/core/providers/registry.ts +21 -0
- package/src/core/providers/types.ts +91 -0
- package/src/core/shared/attachments.ts +101 -0
- package/src/core/shared/cleanupFastFail.ts +67 -0
- package/src/core/shared/clipboard.ts +24 -0
- package/src/core/shared/clipboardImage.ts +111 -0
- package/src/core/shared/githubDiagnostics.ts +222 -0
- package/src/core/shared/hollowResponseFormat.ts +39 -0
- package/src/core/terminal/clearFrameBoundary.ts +852 -0
- package/src/core/terminal/frameLock.ts +110 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/startupClear.ts +20 -0
- package/src/core/terminal/terminalCapabilities.ts +100 -0
- package/src/core/terminal/terminalControl.ts +169 -0
- package/src/core/terminal/terminalSanitize.ts +147 -0
- package/src/core/terminal/terminalTitle.ts +400 -0
- package/src/core/version/channel.ts +27 -0
- package/src/core/version/packageManager.ts +119 -0
- package/src/core/version/updateCheck.ts +203 -0
- package/src/core/workspace/appData.ts +107 -0
- package/src/core/workspace/conversationStore.ts +335 -0
- package/src/core/workspace/launchContext.ts +259 -0
- package/src/core/workspace/planStorage.ts +135 -0
- package/src/core/workspace/projectInstructions.ts +54 -0
- package/src/core/workspace/scratchDir.ts +64 -0
- package/src/core/workspace/workspaceActivity.ts +384 -0
- package/src/core/workspace/workspaceGuard.ts +377 -0
- package/src/core/workspace/workspaceRoot.ts +47 -0
- package/src/exec.ts +73 -0
- package/src/headless/execArgs.ts +296 -0
- package/src/headless/execRunner.ts +304 -0
- package/src/index.tsx +270 -0
- package/src/legacyEnvBootstrap.ts +5 -0
- package/src/session/appSession.ts +771 -0
- package/src/session/chatLifecycle.ts +994 -0
- package/src/session/conversation.ts +107 -0
- package/src/session/liveRenderScheduler.ts +214 -0
- package/src/session/persistedResponse.ts +93 -0
- package/src/session/planFlow.ts +159 -0
- package/src/session/planTranscript.ts +19 -0
- package/src/session/promptRunSchedule.ts +26 -0
- package/src/session/types.ts +234 -0
- package/src/test/runtimeTestUtils.ts +14 -0
- package/src/types/react-dom.d.ts +3 -0
- package/src/ui/chrome/ActivityBars.tsx +68 -0
- package/src/ui/chrome/ActivityIndicator.tsx +58 -0
- package/src/ui/chrome/AnimatedStatusText.tsx +69 -0
- package/src/ui/chrome/AppShell.tsx +474 -0
- package/src/ui/chrome/BottomComposer.tsx +1135 -0
- package/src/ui/chrome/DashCard.tsx +82 -0
- package/src/ui/chrome/RunFooter.tsx +65 -0
- package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
- package/src/ui/chrome/Spinner.tsx +25 -0
- package/src/ui/chrome/TopHeader.tsx +439 -0
- package/src/ui/chrome/UpdateAvailableCard.tsx +42 -0
- package/src/ui/chrome/busyStatusAnimation.ts +11 -0
- package/src/ui/input/commandNormalize.ts +66 -0
- package/src/ui/input/focus.ts +73 -0
- package/src/ui/input/imageAttachments.ts +18 -0
- package/src/ui/input/inputBuffer.ts +203 -0
- package/src/ui/input/pastedContent.ts +75 -0
- package/src/ui/input/rawArrowKeys.ts +28 -0
- package/src/ui/input/slashCommands.ts +43 -0
- package/src/ui/input/useStdinRawModeLease.ts +23 -0
- package/src/ui/layout.ts +560 -0
- package/src/ui/panels/AttachmentImportPanel.tsx +131 -0
- package/src/ui/panels/AuthPanel.tsx +149 -0
- package/src/ui/panels/BackendPicker.tsx +28 -0
- package/src/ui/panels/ModePicker.tsx +31 -0
- package/src/ui/panels/ModelPicker.tsx +31 -0
- package/src/ui/panels/ModelPickerScreen.tsx +761 -0
- package/src/ui/panels/ModelReasoningPicker.tsx +458 -0
- package/src/ui/panels/Panel.tsx +51 -0
- package/src/ui/panels/PermissionsPanel.tsx +78 -0
- package/src/ui/panels/PlanActionPicker.tsx +187 -0
- package/src/ui/panels/ProviderPicker.tsx +753 -0
- package/src/ui/panels/ProviderSetupPrompt.tsx +52 -0
- package/src/ui/panels/ReasoningPicker.tsx +46 -0
- package/src/ui/panels/ResumePicker.tsx +90 -0
- package/src/ui/panels/SelectionPanel.tsx +138 -0
- package/src/ui/panels/SettingsPanel.tsx +156 -0
- package/src/ui/panels/TextEntryPanel.tsx +139 -0
- package/src/ui/panels/ThemePicker.tsx +32 -0
- package/src/ui/panels/ToolApprovalPanel.tsx +46 -0
- package/src/ui/panels/UpdatePromptPanel.tsx +236 -0
- package/src/ui/panels/responsivePickerViewport.ts +64 -0
- package/src/ui/render/Markdown.tsx +331 -0
- package/src/ui/render/diffRenderer.ts +116 -0
- package/src/ui/render/logoVariants.ts +113 -0
- package/src/ui/render/modeDisplay.ts +52 -0
- package/src/ui/render/outputPipeline.ts +64 -0
- package/src/ui/render/runtimeDisplay.ts +128 -0
- package/src/ui/render/terminalAnswerFormat.ts +128 -0
- package/src/ui/render/textLayout.ts +392 -0
- package/src/ui/theme.tsx +274 -0
- package/src/ui/themeFlow.ts +41 -0
- package/src/ui/timeline/ActionRequiredBlock.tsx +38 -0
- package/src/ui/timeline/AgentBlock.tsx +130 -0
- package/src/ui/timeline/StaticIntroItem.tsx +54 -0
- package/src/ui/timeline/ThinkingBlock.tsx +100 -0
- package/src/ui/timeline/Timeline.tsx +1410 -0
- package/src/ui/timeline/TranscriptShell.tsx +302 -0
- package/src/ui/timeline/TurnGroup.tsx +673 -0
- package/src/ui/timeline/layoutListWindow.ts +145 -0
- package/src/ui/timeline/liveViewportWindow.ts +68 -0
- package/src/ui/timeline/progressEntries.ts +156 -0
- package/src/ui/timeline/runActivityView.ts +37 -0
- package/src/ui/timeline/staticTranscriptCache.ts +174 -0
- package/src/ui/timeline/streamCoalesce.ts +53 -0
- package/src/ui/timeline/timelineMeasure.ts +3273 -0
- package/src/ui/useThrottledValue.ts +31 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { CodexModelCapability, CodexModelCapabilities } from "../models/codexModelCapabilities.js";
|
|
2
|
+
import type { ProviderModel } from "./types.js";
|
|
3
|
+
import { getClaudeCodeEffortLevels } from "./reasoning.js";
|
|
4
|
+
|
|
5
|
+
export const GEMINI_DEFAULT_MODEL_ID = "gemini-3-flash-preview";
|
|
6
|
+
export const GEMINI_VERIFIED_MODEL_IDS = [
|
|
7
|
+
"gemini-3.1-pro-preview",
|
|
8
|
+
"gemini-3-flash-preview",
|
|
9
|
+
"gemini-3.1-flash-lite-preview",
|
|
10
|
+
"gemini-2.5-pro",
|
|
11
|
+
"gemini-2.5-flash",
|
|
12
|
+
"gemini-2.5-flash-lite",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export function isVerifiedGeminiModelId(modelId: string | null | undefined): modelId is typeof GEMINI_VERIFIED_MODEL_IDS[number] {
|
|
16
|
+
return typeof modelId === "string" && GEMINI_VERIFIED_MODEL_IDS.includes(modelId as typeof GEMINI_VERIFIED_MODEL_IDS[number]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeGeminiModelId(modelId: string | null | undefined): string {
|
|
20
|
+
// "gemini-3-flash" is an older shorthand; remap it to the canonical preview ID.
|
|
21
|
+
if (modelId === "gemini-3-flash") return "gemini-3-flash-preview";
|
|
22
|
+
return isVerifiedGeminiModelId(modelId) ? modelId : GEMINI_DEFAULT_MODEL_ID;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const GEMINI_FALLBACK_MODELS: readonly ProviderModel[] = [
|
|
26
|
+
{
|
|
27
|
+
id: "gemini-3-flash-preview",
|
|
28
|
+
modelId: "gemini-3-flash-preview",
|
|
29
|
+
label: "Gemini 3 Flash Preview",
|
|
30
|
+
description: "Verified Gemini CLI Flash Preview route.",
|
|
31
|
+
defaultReasoningLevel: "medium",
|
|
32
|
+
supportedReasoningLevels: null,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "gemini-3.1-pro-preview",
|
|
36
|
+
modelId: "gemini-3.1-pro-preview",
|
|
37
|
+
label: "Gemini 3.1 Pro Preview",
|
|
38
|
+
description: "Verified Gemini CLI Pro Preview route.",
|
|
39
|
+
defaultReasoningLevel: "high",
|
|
40
|
+
supportedReasoningLevels: null,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "gemini-3.1-flash-lite-preview",
|
|
44
|
+
modelId: "gemini-3.1-flash-lite-preview",
|
|
45
|
+
label: "Gemini 3.1 Flash Lite Preview",
|
|
46
|
+
description: "Verified Gemini CLI Flash Lite Preview route.",
|
|
47
|
+
defaultReasoningLevel: "medium",
|
|
48
|
+
supportedReasoningLevels: null,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "gemini-2.5-pro",
|
|
52
|
+
modelId: "gemini-2.5-pro",
|
|
53
|
+
label: "Gemini 2.5 Pro",
|
|
54
|
+
description: "Verified Gemini CLI Pro route.",
|
|
55
|
+
defaultReasoningLevel: "high",
|
|
56
|
+
supportedReasoningLevels: null,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "gemini-2.5-flash",
|
|
60
|
+
modelId: "gemini-2.5-flash",
|
|
61
|
+
label: "Gemini 2.5 Flash",
|
|
62
|
+
description: "Verified Gemini CLI Flash route.",
|
|
63
|
+
defaultReasoningLevel: "medium",
|
|
64
|
+
supportedReasoningLevels: null,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "gemini-2.5-flash-lite",
|
|
68
|
+
modelId: "gemini-2.5-flash-lite",
|
|
69
|
+
label: "Gemini 2.5 Flash Lite",
|
|
70
|
+
description: "Verified Gemini CLI Flash Lite route.",
|
|
71
|
+
defaultReasoningLevel: "medium",
|
|
72
|
+
supportedReasoningLevels: null,
|
|
73
|
+
},
|
|
74
|
+
] as const;
|
|
75
|
+
|
|
76
|
+
function fallbackModelDescription(family: string): string {
|
|
77
|
+
return `Claude ${family} alias — version unknown because model discovery is unavailable`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const ANTHROPIC_FALLBACK_MODELS: readonly ProviderModel[] = [
|
|
81
|
+
{
|
|
82
|
+
id: "fable",
|
|
83
|
+
modelId: "fable",
|
|
84
|
+
label: "Claude Fable (version unknown)",
|
|
85
|
+
description: fallbackModelDescription("Fable"),
|
|
86
|
+
defaultReasoningLevel: "xhigh",
|
|
87
|
+
supportedReasoningLevels: getClaudeCodeEffortLevels(["low", "medium", "high", "xhigh", "max"]),
|
|
88
|
+
source: "fallback",
|
|
89
|
+
canonicalId: "fable",
|
|
90
|
+
family: "fable",
|
|
91
|
+
effortSource: "fallback",
|
|
92
|
+
effortVerified: false,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "opus",
|
|
96
|
+
modelId: "opus",
|
|
97
|
+
label: "Claude Opus (version unknown)",
|
|
98
|
+
description: fallbackModelDescription("Opus"),
|
|
99
|
+
defaultReasoningLevel: "xhigh",
|
|
100
|
+
supportedReasoningLevels: getClaudeCodeEffortLevels(["low", "medium", "high", "xhigh", "max"]),
|
|
101
|
+
source: "fallback",
|
|
102
|
+
canonicalId: "opus",
|
|
103
|
+
family: "opus",
|
|
104
|
+
effortSource: "fallback",
|
|
105
|
+
effortVerified: false,
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
id: "sonnet",
|
|
110
|
+
modelId: "sonnet",
|
|
111
|
+
label: "Claude Sonnet (version unknown)",
|
|
112
|
+
description: fallbackModelDescription("Sonnet"),
|
|
113
|
+
defaultReasoningLevel: "high",
|
|
114
|
+
supportedReasoningLevels: getClaudeCodeEffortLevels(["low", "medium", "high", "xhigh", "max"]),
|
|
115
|
+
source: "fallback",
|
|
116
|
+
canonicalId: "sonnet",
|
|
117
|
+
family: "sonnet",
|
|
118
|
+
effortSource: "fallback",
|
|
119
|
+
effortVerified: false,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "haiku",
|
|
123
|
+
modelId: "haiku",
|
|
124
|
+
label: "Claude Haiku (version unknown)",
|
|
125
|
+
description: fallbackModelDescription("Haiku"),
|
|
126
|
+
defaultReasoningLevel: "medium",
|
|
127
|
+
supportedReasoningLevels: getClaudeCodeEffortLevels(["low", "medium", "high", "xhigh", "max"]),
|
|
128
|
+
source: "fallback",
|
|
129
|
+
canonicalId: "haiku",
|
|
130
|
+
family: "haiku",
|
|
131
|
+
effortSource: "fallback",
|
|
132
|
+
effortVerified: false,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
function isRuntimeSource(source: ProviderModel["source"]): boolean {
|
|
137
|
+
return source === "discovered"
|
|
138
|
+
|| source === "claude-code"
|
|
139
|
+
|| source === "claude-code-command"
|
|
140
|
+
|| source === "claude-code-package"
|
|
141
|
+
|| source === "claude-code-cache"
|
|
142
|
+
|| source === "claude-code-config"
|
|
143
|
+
|| source === "settings"
|
|
144
|
+
|| source === "config";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function providerModelsToCodexCapabilities(
|
|
148
|
+
models: readonly ProviderModel[],
|
|
149
|
+
currentModel: string,
|
|
150
|
+
): CodexModelCapabilities {
|
|
151
|
+
const capabilities: readonly CodexModelCapability[] = models.map((model, index) => ({
|
|
152
|
+
id: model.id,
|
|
153
|
+
model: model.modelId,
|
|
154
|
+
label: model.label,
|
|
155
|
+
description: model.description,
|
|
156
|
+
available: true,
|
|
157
|
+
hidden: false,
|
|
158
|
+
isDefault: model.modelId === currentModel || (!currentModel && index === 0),
|
|
159
|
+
defaultReasoningLevel: model.defaultReasoningLevel,
|
|
160
|
+
supportedReasoningLevels: model.supportedReasoningLevels,
|
|
161
|
+
reasoningLevelCount: model.supportedReasoningLevels ? model.supportedReasoningLevels.length : null,
|
|
162
|
+
source: isRuntimeSource(model.source) ? "runtime" : "fallback",
|
|
163
|
+
raw: model,
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
const anyDiscovered = models.some((m) => isRuntimeSource(m.source));
|
|
167
|
+
return {
|
|
168
|
+
status: "ready",
|
|
169
|
+
source: anyDiscovered ? "runtime" : "fallback",
|
|
170
|
+
models: capabilities,
|
|
171
|
+
discoveredAt: Date.now(),
|
|
172
|
+
executable: null,
|
|
173
|
+
error: null,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReasoningEffortCapability } from "../models/codexModelCapabilities.js";
|
|
2
|
+
import { formatReasoningLabel } from "../../config/settings.js";
|
|
3
|
+
|
|
4
|
+
export const CLAUDE_CODE_EFFORT_LEVELS: readonly ReasoningEffortCapability[] = [
|
|
5
|
+
{ id: "low", label: "Low", description: "Claude Code low effort." },
|
|
6
|
+
{ id: "medium", label: "Medium", description: "Claude Code medium effort." },
|
|
7
|
+
{ id: "high", label: "High", description: "Claude Code high effort." },
|
|
8
|
+
{ id: "xhigh", label: "XHigh", description: "Claude Code extra-high effort." },
|
|
9
|
+
{ id: "max", label: "Max", description: "Claude Code maximum effort." },
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export const CLAUDE_CODE_EFFORT_IDS = new Set(CLAUDE_CODE_EFFORT_LEVELS.map((level) => level.id));
|
|
13
|
+
|
|
14
|
+
export function getClaudeCodeEffortLevels(ids: readonly string[]): readonly ReasoningEffortCapability[] {
|
|
15
|
+
return ids.map((id) => CLAUDE_CODE_EFFORT_LEVELS.find((level) => level.id === id) ?? {
|
|
16
|
+
id,
|
|
17
|
+
label: formatReasoningLabel(id),
|
|
18
|
+
description: null,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { codexSubprocessProvider } from "../providers/codexSubprocess.js";
|
|
2
|
+
import { loadSeededOpenAiModels } from "../models/codexModelsCacheSeed.js";
|
|
3
|
+
import { loadCachedProviderModels, saveCachedProviderModels } from "../models/providerModelCache.js";
|
|
4
|
+
import type { BackendRunHandlers } from "../providers/types.js";
|
|
5
|
+
import type { ProviderId, ProviderActiveRoute, ProviderWorkspaceOverride } from "../providerLauncher/types.js";
|
|
6
|
+
import { isLocalDevChannel } from "../version/channel.js";
|
|
7
|
+
import { anthropicRuntime } from "./anthropic.js";
|
|
8
|
+
import { geminiRuntime } from "./gemini.js";
|
|
9
|
+
import { localRuntime } from "./local.js";
|
|
10
|
+
import { codexaNativeRuntime, CODEXA_NATIVE_MODEL_ID } from "./codexaNative.js";
|
|
11
|
+
import { codexaCupyRuntime } from "./codexaCupy.js";
|
|
12
|
+
import { antigravityRuntime, ANTIGRAVITY_DEFAULT_MODEL_ID, migrateAntigravityLegacyModelId } from "./antigravity.js";
|
|
13
|
+
import { mistralVibeRuntime } from "./mistralVibe.js";
|
|
14
|
+
import {
|
|
15
|
+
ANTHROPIC_FALLBACK_MODELS,
|
|
16
|
+
GEMINI_DEFAULT_MODEL_ID,
|
|
17
|
+
GEMINI_FALLBACK_MODELS,
|
|
18
|
+
normalizeGeminiModelId,
|
|
19
|
+
} from "./models.js";
|
|
20
|
+
import type {
|
|
21
|
+
ActiveProviderRoute,
|
|
22
|
+
GeminiModelSelection,
|
|
23
|
+
ProviderChatRequest,
|
|
24
|
+
ProviderModelDiscoveryResult,
|
|
25
|
+
ProviderRoute,
|
|
26
|
+
ProviderRouteValidationResult,
|
|
27
|
+
ProviderRuntime,
|
|
28
|
+
} from "./types.js";
|
|
29
|
+
|
|
30
|
+
const openAiRuntime: ProviderRuntime = {
|
|
31
|
+
providerId: "openai",
|
|
32
|
+
label: "OpenAI/Codex",
|
|
33
|
+
backendKind: "codex-cli-auth",
|
|
34
|
+
routeAvailable: true,
|
|
35
|
+
routeStatus: "Uses the configured Codex/OpenAI backend inside Ubume.",
|
|
36
|
+
launchAvailable: true,
|
|
37
|
+
discoverModels: () => ({
|
|
38
|
+
status: "ready",
|
|
39
|
+
providerId: "openai",
|
|
40
|
+
backendKind: "codex-cli-auth",
|
|
41
|
+
models: loadSeededOpenAiModels()?.models ?? [],
|
|
42
|
+
}),
|
|
43
|
+
run: (request: ProviderChatRequest, handlers: BackendRunHandlers) => {
|
|
44
|
+
handlers.onProgress?.({
|
|
45
|
+
id: "openai-route",
|
|
46
|
+
source: "stdout",
|
|
47
|
+
text: "Starting Codex CLI",
|
|
48
|
+
});
|
|
49
|
+
return codexSubprocessProvider.run!(
|
|
50
|
+
request.prompt,
|
|
51
|
+
{
|
|
52
|
+
runtime: request.runtime,
|
|
53
|
+
workspaceRoot: request.workspaceRoot,
|
|
54
|
+
projectInstructions: request.projectInstructions,
|
|
55
|
+
conversationHistory: request.conversationHistory,
|
|
56
|
+
imageAttachments: request.imageAttachments,
|
|
57
|
+
},
|
|
58
|
+
handlers,
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function unavailableRuntime(providerId: ProviderId, label: string): ProviderRuntime {
|
|
64
|
+
return {
|
|
65
|
+
providerId,
|
|
66
|
+
label,
|
|
67
|
+
backendKind: "unavailable",
|
|
68
|
+
routeAvailable: false,
|
|
69
|
+
routeStatus: `${label} is available as a launcher, but in-Ubume routing is not configured yet.`,
|
|
70
|
+
launchAvailable: providerId !== "local",
|
|
71
|
+
discoverModels: (): ProviderModelDiscoveryResult => ({
|
|
72
|
+
status: "not-configured",
|
|
73
|
+
providerId,
|
|
74
|
+
backendKind: "unavailable",
|
|
75
|
+
models: [],
|
|
76
|
+
message: `${label} is available as a launcher, but in-Ubume routing is not configured yet.`,
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const PROVIDER_RUNTIMES: Record<ProviderId, ProviderRuntime> = {
|
|
82
|
+
openai: openAiRuntime,
|
|
83
|
+
anthropic: anthropicRuntime,
|
|
84
|
+
google: geminiRuntime,
|
|
85
|
+
mistral: mistralVibeRuntime,
|
|
86
|
+
local: localRuntime,
|
|
87
|
+
"codexa-native": codexaNativeRuntime,
|
|
88
|
+
"codexa-cupy": codexaCupyRuntime,
|
|
89
|
+
antigravity: antigravityRuntime,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export function getProviderRuntime(providerId: ProviderId): ProviderRuntime {
|
|
93
|
+
return PROVIDER_RUNTIMES[providerId];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function isProviderRoutableInUbume(
|
|
97
|
+
providerId: ProviderId,
|
|
98
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
99
|
+
): boolean {
|
|
100
|
+
if ((providerId === "codexa-native" || providerId === "codexa-cupy") && !isLocalDevChannel(env)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return getProviderRuntime(providerId).routeAvailable;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isProviderRouteConfigured(
|
|
107
|
+
providerId: ProviderId,
|
|
108
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
109
|
+
): boolean {
|
|
110
|
+
const runtime = getProviderRuntime(providerId);
|
|
111
|
+
return isProviderRoutableInUbume(providerId, env) && (runtime.isRouteConfigured?.() ?? true);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getProviderRouteSetupMessage(providerId: ProviderId): string {
|
|
115
|
+
const runtime = getProviderRuntime(providerId);
|
|
116
|
+
return runtime.routeSetupMessage ?? runtime.routeStatus;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function discoverProviderModels(providerId: ProviderId): ProviderModelDiscoveryResult {
|
|
120
|
+
const result = getProviderRuntime(providerId).discoverModels();
|
|
121
|
+
const hasRuntimeModels = result.models.some((model) => model.source && model.source !== "fallback");
|
|
122
|
+
if (result.status === "ready" && !hasRuntimeModels) {
|
|
123
|
+
const cached = loadCachedProviderModels(providerId);
|
|
124
|
+
if (cached) {
|
|
125
|
+
return { ...result, models: cached.models };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function persistProviderDiscovery(discovery: ProviderModelDiscoveryResult): void {
|
|
132
|
+
const runtimeModels = discovery.models.filter((model) => model.source && model.source !== "fallback");
|
|
133
|
+
if (discovery.status !== "ready" || runtimeModels.length === 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
saveCachedProviderModels(discovery.providerId, {
|
|
137
|
+
discoveredAt: Date.now(),
|
|
138
|
+
models: runtimeModels,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function validateProviderRouteActivation(options: {
|
|
143
|
+
route: ProviderRoute;
|
|
144
|
+
workspaceRoot: string;
|
|
145
|
+
geminiCommandPath?: string | null;
|
|
146
|
+
claudeCommandPath?: string | null;
|
|
147
|
+
antigravityCommandPath?: string | null;
|
|
148
|
+
localConfig?: ProviderWorkspaceOverride | null;
|
|
149
|
+
}): Promise<ProviderRouteValidationResult> {
|
|
150
|
+
const runtime = getProviderRuntime(options.route.providerId);
|
|
151
|
+
if (!runtime.routeAvailable) {
|
|
152
|
+
return {
|
|
153
|
+
status: "not-configured",
|
|
154
|
+
providerId: options.route.providerId,
|
|
155
|
+
backendKind: "unavailable",
|
|
156
|
+
message: getProviderRouteSetupMessage(options.route.providerId),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (runtime.validateRoute) {
|
|
161
|
+
return runtime.validateRoute(options);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!isProviderRouteConfigured(options.route.providerId)) {
|
|
165
|
+
return {
|
|
166
|
+
status: "not-configured",
|
|
167
|
+
providerId: options.route.providerId,
|
|
168
|
+
backendKind: "unavailable",
|
|
169
|
+
message: getProviderRouteSetupMessage(options.route.providerId),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
status: "ready",
|
|
175
|
+
providerId: options.route.providerId,
|
|
176
|
+
backendKind: runtime.backendKind,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function resolveGeminiModelId(selection: GeminiModelSelection): string {
|
|
181
|
+
if (selection.kind === "manual") {
|
|
182
|
+
return normalizeGeminiModelId(selection.modelId);
|
|
183
|
+
}
|
|
184
|
+
if (selection.family === "gemini-3") {
|
|
185
|
+
return "gemini-3-flash-preview";
|
|
186
|
+
}
|
|
187
|
+
if (selection.family === "gemini-2.5") {
|
|
188
|
+
return "gemini-2.5-pro";
|
|
189
|
+
}
|
|
190
|
+
return GEMINI_DEFAULT_MODEL_ID;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function resolveActiveProviderRoute(options: {
|
|
194
|
+
workspaceConfigActiveRoute?: ProviderActiveRoute;
|
|
195
|
+
currentModel: string;
|
|
196
|
+
currentReasoning: string;
|
|
197
|
+
}): ActiveProviderRoute {
|
|
198
|
+
const configuredRoute = options.workspaceConfigActiveRoute;
|
|
199
|
+
if (configuredRoute && configuredRoute.providerId !== "google" && isProviderRoutableInUbume(configuredRoute.providerId)) {
|
|
200
|
+
const route: ActiveProviderRoute = {
|
|
201
|
+
providerId: configuredRoute.providerId,
|
|
202
|
+
modelId: configuredRoute.modelId,
|
|
203
|
+
backendKind: configuredRoute.backendKind ?? getProviderRuntime(configuredRoute.providerId).backendKind,
|
|
204
|
+
...(configuredRoute.reasoning ? { reasoning: configuredRoute.reasoning } : {}),
|
|
205
|
+
...(configuredRoute.modelSelection ? { modelSelection: configuredRoute.modelSelection } : {}),
|
|
206
|
+
...(configuredRoute.providerId === "local"
|
|
207
|
+
? { localBackend: configuredRoute.localBackend ?? "lm-studio" }
|
|
208
|
+
: {}),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (route.providerId === "google" && route.modelSelection) {
|
|
212
|
+
route.modelId = resolveGeminiModelId(route.modelSelection);
|
|
213
|
+
} else if (route.providerId === "google") {
|
|
214
|
+
route.modelId = normalizeGeminiModelId(route.modelId);
|
|
215
|
+
} else if (route.providerId === "anthropic") {
|
|
216
|
+
const discovery = discoverProviderModels("anthropic");
|
|
217
|
+
const stillAvailable = discovery.models.some((model) =>
|
|
218
|
+
model.modelId === route.modelId ||
|
|
219
|
+
model.id === route.modelId ||
|
|
220
|
+
model.canonicalId === route.modelId
|
|
221
|
+
);
|
|
222
|
+
const hasNonFallbackModels = discovery.models.some((model) => model.source !== "fallback");
|
|
223
|
+
const isKnownShortAlias = ANTHROPIC_FALLBACK_MODELS.some((model) => model.modelId === route.modelId);
|
|
224
|
+
if (discovery.status === "ready" && hasNonFallbackModels && discovery.models.length > 0 && !stillAvailable && isKnownShortAlias) {
|
|
225
|
+
route.modelId = discovery.models[0]!.modelId;
|
|
226
|
+
}
|
|
227
|
+
} else if (route.providerId === "antigravity") {
|
|
228
|
+
const migrated = migrateAntigravityLegacyModelId(route.modelId);
|
|
229
|
+
route.modelId = migrated.modelId;
|
|
230
|
+
if (!route.reasoning && migrated.reasoning) {
|
|
231
|
+
route.reasoning = migrated.reasoning;
|
|
232
|
+
}
|
|
233
|
+
const discovery = discoverProviderModels("antigravity");
|
|
234
|
+
if (discovery.status === "ready" && discovery.models.length > 0) {
|
|
235
|
+
let model = discovery.models.find((item) => item.modelId === route.modelId || item.id === route.modelId);
|
|
236
|
+
if (!model) {
|
|
237
|
+
model = discovery.models[0];
|
|
238
|
+
route.modelId = model.modelId;
|
|
239
|
+
}
|
|
240
|
+
const levels = model.supportedReasoningLevels;
|
|
241
|
+
if (levels?.length && (!route.reasoning || !levels.some((level) => level.id === route.reasoning))) {
|
|
242
|
+
route.reasoning = model.defaultReasoningLevel ?? levels[0]?.id;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return route;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
providerId: "openai",
|
|
252
|
+
modelId: options.currentModel,
|
|
253
|
+
backendKind: "codex-cli-auth",
|
|
254
|
+
reasoning: options.currentReasoning,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function getDefaultRouteModel(providerId: ProviderId, currentOpenAiModel: string): string {
|
|
259
|
+
if (providerId === "anthropic") {
|
|
260
|
+
const discovered = discoverProviderModels("anthropic");
|
|
261
|
+
if (discovered.status === "ready" && discovered.models.length > 0) {
|
|
262
|
+
return discovered.models[0].modelId;
|
|
263
|
+
}
|
|
264
|
+
return ANTHROPIC_FALLBACK_MODELS[0]?.modelId ?? "sonnet";
|
|
265
|
+
}
|
|
266
|
+
if (providerId === "google") {
|
|
267
|
+
return GEMINI_FALLBACK_MODELS[0]?.modelId ?? GEMINI_DEFAULT_MODEL_ID;
|
|
268
|
+
}
|
|
269
|
+
if (providerId === "local") {
|
|
270
|
+
const discovery = discoverProviderModels("local");
|
|
271
|
+
return discovery.models[0]?.modelId ?? "Local default";
|
|
272
|
+
}
|
|
273
|
+
if (providerId === "codexa-native") {
|
|
274
|
+
return CODEXA_NATIVE_MODEL_ID;
|
|
275
|
+
}
|
|
276
|
+
if (providerId === "mistral") {
|
|
277
|
+
const discovery = discoverProviderModels("mistral");
|
|
278
|
+
return discovery.models[0]?.modelId ?? "Vibe default";
|
|
279
|
+
}
|
|
280
|
+
if (providerId === "antigravity") {
|
|
281
|
+
return discoverProviderModels("antigravity").models[0]?.modelId ?? ANTIGRAVITY_DEFAULT_MODEL_ID;
|
|
282
|
+
}
|
|
283
|
+
return currentOpenAiModel;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const isProviderRoutableInCodexa = isProviderRoutableInUbume;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { ReasoningEffortCapability } from "../models/codexModelCapabilities.js";
|
|
2
|
+
import type { ProjectInstructions } from "../workspace/projectInstructions.js";
|
|
3
|
+
import type { BackendRunHandlers } from "../providers/types.js";
|
|
4
|
+
import type { ResolvedRuntimeConfig } from "../../config/runtimeConfig.js";
|
|
5
|
+
export type { ResolvedRuntimeConfig };
|
|
6
|
+
import type { LocalBackendId, ProviderId } from "../providerLauncher/types.js";
|
|
7
|
+
import type { ProviderWorkspaceOverride } from "../providerLauncher/types.js";
|
|
8
|
+
import type { ConversationContextCheckpoint, ConversationMessage, LocalHarnessSessionMetadata } from "../workspace/conversationStore.js";
|
|
9
|
+
|
|
10
|
+
export type ProviderBackendKind =
|
|
11
|
+
| "codex-cli-auth"
|
|
12
|
+
| "gemini-cli-auth"
|
|
13
|
+
| "claude-code-auth"
|
|
14
|
+
| "mistral-vibe-cli-auth"
|
|
15
|
+
| "antigravity-cli-auth"
|
|
16
|
+
| "openai-api-key"
|
|
17
|
+
| "gemini-api-key"
|
|
18
|
+
| "anthropic-api-key"
|
|
19
|
+
| "local-openai-compatible"
|
|
20
|
+
| "codexa-native-pytorch"
|
|
21
|
+
| "codexa-cupy"
|
|
22
|
+
| "unavailable";
|
|
23
|
+
|
|
24
|
+
export interface ProviderModel {
|
|
25
|
+
id: string;
|
|
26
|
+
modelId: string;
|
|
27
|
+
label: string;
|
|
28
|
+
description: string | null;
|
|
29
|
+
defaultReasoningLevel: string | null;
|
|
30
|
+
supportedReasoningLevels: readonly ReasoningEffortCapability[] | null;
|
|
31
|
+
source?: "discovered" | "claude-code" | "claude-code-command" | "claude-code-package" | "claude-code-cache" | "claude-code-config" | "settings" | "config" | "fallback";
|
|
32
|
+
canonicalId?: string;
|
|
33
|
+
family?: string;
|
|
34
|
+
version?: string;
|
|
35
|
+
isFallback?: boolean;
|
|
36
|
+
discoveryKind?: "models" | "aliases";
|
|
37
|
+
effortSource?: "claude-code" | "claude-code-command" | "claude-code-package" | "claude-code-cache" | "claude-code-config" | "settings" | "config" | "fallback";
|
|
38
|
+
effortVerified?: boolean;
|
|
39
|
+
raw?: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ProviderModelDiscoveryResult {
|
|
43
|
+
status: "ready" | "not-configured";
|
|
44
|
+
providerId: ProviderId;
|
|
45
|
+
backendKind: ProviderBackendKind;
|
|
46
|
+
models: readonly ProviderModel[];
|
|
47
|
+
message?: string;
|
|
48
|
+
diagnostics?: Record<string, string | number | boolean | null>;
|
|
49
|
+
localBackend?: LocalBackendId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type GeminiModelFamily = "gemini-3" | "gemini-2.5";
|
|
53
|
+
|
|
54
|
+
export type GeminiModelSelection =
|
|
55
|
+
| { kind: "auto"; family: GeminiModelFamily }
|
|
56
|
+
| { kind: "manual"; modelId: string };
|
|
57
|
+
|
|
58
|
+
export interface ProviderRoute {
|
|
59
|
+
providerId: ProviderId;
|
|
60
|
+
modelId: string;
|
|
61
|
+
backendKind: ProviderBackendKind;
|
|
62
|
+
reasoning?: string;
|
|
63
|
+
modelSelection?: GeminiModelSelection;
|
|
64
|
+
localBackend?: LocalBackendId;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type ActiveProviderRoute = ProviderRoute;
|
|
68
|
+
|
|
69
|
+
/** Ephemeral, request-scoped Local connection. Secrets are never persisted. */
|
|
70
|
+
export interface ResolvedLocalAgentConfig {
|
|
71
|
+
localBackend: LocalBackendId;
|
|
72
|
+
baseUrl: string;
|
|
73
|
+
apiKey: string;
|
|
74
|
+
modelId: string;
|
|
75
|
+
contextWindow: number;
|
|
76
|
+
maxTokens: number;
|
|
77
|
+
supportsStreaming: boolean | null;
|
|
78
|
+
supportsToolCalls: boolean | null;
|
|
79
|
+
supportsSystemPrompt: boolean | null;
|
|
80
|
+
supportsVision: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ProviderRouteValidationRequest {
|
|
84
|
+
route: ProviderRoute;
|
|
85
|
+
workspaceRoot: string;
|
|
86
|
+
geminiCommandPath?: string | null;
|
|
87
|
+
claudeCommandPath?: string | null;
|
|
88
|
+
antigravityCommandPath?: string | null;
|
|
89
|
+
localConfig?: ProviderWorkspaceOverride | null;
|
|
90
|
+
localBackend?: LocalBackendId;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ProviderRouteValidationResult {
|
|
94
|
+
status: "ready" | "not-configured";
|
|
95
|
+
providerId: ProviderId;
|
|
96
|
+
backendKind: ProviderBackendKind;
|
|
97
|
+
message?: string;
|
|
98
|
+
diagnostics?: Record<string, string | number | boolean | null>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ProviderChatRequest {
|
|
102
|
+
prompt: string;
|
|
103
|
+
imageAttachments?: readonly ProviderImageAttachment[];
|
|
104
|
+
route: ProviderRoute;
|
|
105
|
+
runtime: ResolvedRuntimeConfig;
|
|
106
|
+
workspaceRoot: string;
|
|
107
|
+
projectInstructions?: ProjectInstructions | null;
|
|
108
|
+
localConfig?: ProviderWorkspaceOverride | null;
|
|
109
|
+
resolvedLocalAgentConfig?: ResolvedLocalAgentConfig;
|
|
110
|
+
runIntent?: "normal" | "plan" | "approved-execution";
|
|
111
|
+
conversationHistory?: readonly ConversationMessage[];
|
|
112
|
+
localContextCheckpoint?: ConversationContextCheckpoint;
|
|
113
|
+
localHarnessSession?: LocalHarnessSessionMetadata;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ProviderImageAttachment {
|
|
117
|
+
path: string;
|
|
118
|
+
mediaType: "image/png" | "image/jpeg" | "image/webp" | "image/gif";
|
|
119
|
+
name: string;
|
|
120
|
+
bytes: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ProviderChatResponse {
|
|
124
|
+
text: string;
|
|
125
|
+
rawOutput?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ProviderRuntime {
|
|
129
|
+
providerId: ProviderId;
|
|
130
|
+
label: string;
|
|
131
|
+
modelPickerLabel?: string;
|
|
132
|
+
backendKind: ProviderBackendKind;
|
|
133
|
+
routeAvailable: boolean;
|
|
134
|
+
routeStatus: string;
|
|
135
|
+
routeSetupMessage?: string;
|
|
136
|
+
launchAvailable: boolean;
|
|
137
|
+
isRouteConfigured?: () => boolean;
|
|
138
|
+
validateRoute?: (request: ProviderRouteValidationRequest) => Promise<ProviderRouteValidationResult>;
|
|
139
|
+
discoverModels: () => ProviderModelDiscoveryResult;
|
|
140
|
+
refreshModels?: (options: { cwd: string; localConfig?: ProviderWorkspaceOverride | null; localBackend?: LocalBackendId }) => Promise<ProviderModelDiscoveryResult>;
|
|
141
|
+
run?: (request: ProviderChatRequest, handlers: BackendRunHandlers) => () => void;
|
|
142
|
+
}
|