This commit is contained in:
2026-07-26 14:02:37 +07:00
parent bc56546b49
commit 367ebc1c7f
171 changed files with 4617 additions and 10402 deletions
@@ -76,7 +76,12 @@ import { FooterDataProvider, type ReadonlyFooterDataProvider } from "../../core/
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.ts";
import { type AppKeybinding, KeybindingsManager } from "../../core/keybindings.ts";
import { createCompactionSummaryMessage } from "../../core/messages.ts";
import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.ts";
import {
defaultModelPerProvider,
findExactModelReferenceMatch,
resolveModelScope,
resolveModelScopeWithDiagnostics,
} from "../../core/model-resolver.ts";
import { DefaultPackageManager } from "../../core/package-manager.ts";
import type { ResourceDiagnostic } from "../../core/resource-loader.ts";
import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.ts";
@@ -2985,6 +2990,10 @@ export class InteractiveMode {
this.ui.requestRender();
break;
case "bash_execution_update":
// The bash execution callback handles TUI output rendering.
break;
case "tool_execution_start": {
let component = this.pendingTools.get(event.toolCallId);
if (!component) {
@@ -3233,7 +3242,12 @@ export class InteractiveMode {
case "custom": {
if (message.display) {
const renderer = this.session.extensionRunner.getMessageRenderer(message.customType);
const component = new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings());
const component = new CustomMessageComponent(
message,
renderer,
this.getMarkdownThemeWithSettings(),
this.outputPad,
);
component.setExpanded(this.toolOutputExpanded);
this.chatContainer.addChild(component);
}
@@ -4248,7 +4262,11 @@ export class InteractiveMode {
this.outputPad = padding;
if (this.streamingComponent || this.session.isStreaming) {
for (const child of this.chatContainer.children) {
if (child instanceof AssistantMessageComponent || child instanceof UserMessageComponent) {
if (
child instanceof AssistantMessageComponent ||
child instanceof CustomMessageComponent ||
child instanceof UserMessageComponent
) {
child.setOutputPad(padding);
}
}
@@ -4459,14 +4477,20 @@ export class InteractiveMode {
// Get all available models
await this.session.modelRuntime.refresh();
const allModels = [...(await this.session.modelRuntime.getAvailable())];
const allModelIds = new Set(allModels.map((model) => `${model.provider}/${model.id}`));
const configuredPatterns = this.settingsManager.getEnabledModels();
const sessionScopedModels = this.session.scopedModels;
if (allModels.length === 0) {
if (allModels.length === 0 && !configuredPatterns?.length && sessionScopedModels.length === 0) {
this.showStatus("No models available");
return;
}
const configuredScope = configuredPatterns?.length
? await resolveModelScopeWithDiagnostics(configuredPatterns, this.session.modelRuntime)
: undefined;
// Check if session has scoped models (from previous session-only changes or CLI --models)
const sessionScopedModels = this.session.scopedModels;
const hasSessionScope = sessionScopedModels.length > 0;
// Build enabled model IDs from session state or settings
@@ -4475,19 +4499,25 @@ export class InteractiveMode {
if (hasSessionScope) {
// Use current session's scoped models
currentEnabledIds = sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
} else {
// Fall back to settings
const patterns = this.settingsManager.getEnabledModels();
if (patterns !== undefined && patterns.length > 0) {
const scopedModels = await resolveModelScope(patterns, this.session.modelRuntime);
currentEnabledIds = scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
}
} else if (configuredScope) {
currentEnabledIds = configuredScope.scopedModels.map(
(scoped) => `${scoped.model.provider}/${scoped.model.id}`,
);
}
for (const diagnostic of configuredScope?.diagnostics ?? []) {
if (diagnostic.code !== "no-match") continue;
currentEnabledIds ??= [];
if (!currentEnabledIds.includes(diagnostic.pattern)) currentEnabledIds.push(diagnostic.pattern);
}
// Helper to update session's scoped models (session-only, no persist)
const updateSessionModels = async (enabledIds: string[] | null) => {
currentEnabledIds = enabledIds === null ? null : [...enabledIds];
if (enabledIds && enabledIds.length > 0 && enabledIds.length < allModels.length) {
const hasEnabledAvailableModel = enabledIds?.some((id) => allModelIds.has(id)) ?? false;
const allAvailableModelsEnabled =
enabledIds !== null && [...allModelIds].every((id) => enabledIds.includes(id));
if (enabledIds && hasEnabledAvailableModel && !allAvailableModelsEnabled) {
const newScopedModels = await resolveModelScope(enabledIds, this.session.modelRuntime);
this.session.setScopedModels(
newScopedModels.map((sm) => ({
@@ -4515,10 +4545,11 @@ export class InteractiveMode {
},
onPersist: (enabledIds) => {
// Persist to settings
const newPatterns =
enabledIds === null || enabledIds.length === allModels.length
? undefined // All enabled = clear filter
: enabledIds;
const allEnabled =
enabledIds !== null &&
enabledIds.length === allModels.length &&
enabledIds.every((id) => allModelIds.has(id));
const newPatterns = enabledIds === null || allEnabled ? undefined : enabledIds;
this.settingsManager.setEnabledModels(newPatterns ? [...newPatterns] : undefined);
this.showStatus("Model selection saved to settings");
},