Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions src/agent/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,40 @@ export async function getAvailableAgents(): Promise<AgentInfo[]> {

const DEFAULT_AGENT = "build";

function pickFallbackAgent(agents: AgentInfo[]): string {
const defaultAgent = agents.find((agent) => agent.name === DEFAULT_AGENT);
if (defaultAgent) {
return defaultAgent.name;
}

return agents[0]?.name ?? DEFAULT_AGENT;
}

export async function resolveProjectAgent(preferredAgent?: string): Promise<string> {
const requestedAgent = preferredAgent ?? getCurrentAgent() ?? DEFAULT_AGENT;
const project = getCurrentProject();

if (!project) {
return requestedAgent;
}

const agents = await getAvailableAgents();
if (agents.length === 0) {
return requestedAgent;
}

if (agents.some((agent) => agent.name === requestedAgent)) {
return requestedAgent;
}

const fallbackAgent = pickFallbackAgent(agents);
logger.warn(
`[AgentManager] Agent "${requestedAgent}" is not available for project ${project.worktree}. Falling back to "${fallbackAgent}".`,
);
setCurrentAgent(fallbackAgent);
return fallbackAgent;
}

/**
* Get current agent from last session message or settings.
* Falls back to "build" if nothing is stored.
Expand All @@ -50,11 +84,15 @@ export async function fetchCurrentAgent(): Promise<string> {
const session = getCurrentSession();
const project = getCurrentProject();

if (!session || !project) {
// No active session, return stored agent from settings
if (!project) {
// No active project, return stored agent from settings
return storedAgent ?? DEFAULT_AGENT;
}

if (!session) {
return resolveProjectAgent(storedAgent ?? DEFAULT_AGENT);
}

try {
const { data: messages, error } = await opencodeClient.session.messages({
sessionID: session.id,
Expand All @@ -64,7 +102,7 @@ export async function fetchCurrentAgent(): Promise<string> {

if (error || !messages || messages.length === 0) {
logger.debug("[AgentManager] No messages found, using stored agent");
return storedAgent ?? DEFAULT_AGENT;
return resolveProjectAgent(storedAgent ?? DEFAULT_AGENT);
}

const lastAgent = messages[0].info.agent;
Expand All @@ -76,18 +114,18 @@ export async function fetchCurrentAgent(): Promise<string> {
logger.debug(
`[AgentManager] Using stored agent "${storedAgent}" instead of session agent "${lastAgent}"`,
);
return storedAgent;
return resolveProjectAgent(storedAgent);
}

// No stored agent yet: sync from session history
if (lastAgent && lastAgent !== storedAgent) {
setCurrentAgent(lastAgent);
}

return lastAgent || storedAgent || DEFAULT_AGENT;
return resolveProjectAgent(lastAgent || storedAgent || DEFAULT_AGENT);
} catch (err) {
logger.error("[AgentManager] Error fetching current agent:", err);
return storedAgent ?? DEFAULT_AGENT;
return resolveProjectAgent(storedAgent ?? DEFAULT_AGENT);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/bot/commands/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ingestSessionInfoForCache } from "../../session/cache-manager.js";
import { interactionManager } from "../../interaction/manager.js";
import type { InteractionState } from "../../interaction/types.js";
import { summaryAggregator } from "../../summary/aggregator.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { getStoredModel } from "../../model/manager.js";
import { safeBackgroundTask } from "../../utils/safe-background-task.js";
import { logger } from "../../utils/logger.js";
Expand Down Expand Up @@ -426,7 +426,7 @@ async function executeCommand(
return;
}

const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const storedModel = getStoredModel();
const model =
storedModel.providerID && storedModel.modelID
Expand Down
5 changes: 3 additions & 2 deletions src/bot/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { clearAllInteractionState } from "../../interaction/cleanup.js";
import { summaryAggregator } from "../../summary/aggregator.js";
import { pinnedMessageManager } from "../../pinned/manager.js";
import { keyboardManager } from "../../keyboard/manager.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { getStoredModel } from "../../model/manager.js";
import { formatVariantForButton } from "../../variant/manager.js";
import { createMainKeyboard } from "../utils/keyboard.js";
Expand Down Expand Up @@ -68,10 +68,11 @@ export async function newCommand(ctx: CommandContext<Context>) {
}

// Get current state for keyboard
const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const currentModel = getStoredModel();
const contextInfo = pinnedMessageManager.getContextInfo();
const variantName = formatVariantForButton(currentModel.variant || "default");
keyboardManager.updateAgent(currentAgent);
const keyboard = createMainKeyboard(
currentAgent,
currentModel,
Expand Down
5 changes: 3 additions & 2 deletions src/bot/commands/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { clearSession } from "../../session/manager.js";
import { summaryAggregator } from "../../summary/aggregator.js";
import { pinnedMessageManager } from "../../pinned/manager.js";
import { keyboardManager } from "../../keyboard/manager.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { getStoredModel } from "../../model/manager.js";
import { formatVariantForButton } from "../../variant/manager.js";
import { clearAllInteractionState } from "../../interaction/cleanup.js";
Expand Down Expand Up @@ -286,10 +286,11 @@ export async function handleProjectSelect(ctx: Context): Promise<boolean> {
keyboardManager.updateContext(0, contextLimit);

// Get current state for keyboard (with context = 0)
const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const currentModel = getStoredModel();
const contextInfo = { tokensUsed: 0, tokensLimit: contextLimit };
const variantName = formatVariantForButton(currentModel.variant || "default");
keyboardManager.updateAgent(currentAgent);
const keyboard = createMainKeyboard(currentAgent, currentModel, contextInfo, variantName);

const projectName = selectedProject.name || selectedProject.worktree;
Expand Down
6 changes: 4 additions & 2 deletions src/bot/handlers/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { FavoriteModel, ModelInfo, ModelSelectionLists } from "../../model/
import { formatVariantForButton } from "../../variant/manager.js";
import { logger } from "../../utils/logger.js";
import { createMainKeyboard } from "../utils/keyboard.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { pinnedMessageManager } from "../../pinned/manager.js";
import { keyboardManager } from "../../keyboard/manager.js";
import {
Expand Down Expand Up @@ -83,13 +83,15 @@ export async function handleModelSelect(ctx: Context): Promise<boolean> {
await pinnedMessageManager.refreshContextLimit();

// Update Reply Keyboard with new model and context
const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const contextInfo =
pinnedMessageManager.getContextInfo() ??
(pinnedMessageManager.getContextLimit() > 0
? { tokensUsed: 0, tokensLimit: pinnedMessageManager.getContextLimit() }
: null);

keyboardManager.updateAgent(currentAgent);

if (contextInfo) {
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
}
Expand Down
7 changes: 4 additions & 3 deletions src/bot/handlers/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { opencodeClient } from "../../opencode/client.js";
import { clearSession, getCurrentSession, setCurrentSession } from "../../session/manager.js";
import { ingestSessionInfoForCache } from "../../session/cache-manager.js";
import { getCurrentProject } from "../../settings/manager.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { getStoredModel } from "../../model/manager.js";
import { formatVariantForButton } from "../../variant/manager.js";
import { createMainKeyboard } from "../utils/keyboard.js";
Expand Down Expand Up @@ -156,10 +156,11 @@ export async function processUserPrompt(
logger.error("[Bot] Error creating pinned message for new session:", err);
}

const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const currentModel = getStoredModel();
const contextInfo = pinnedMessageManager.getContextInfo();
const variantName = formatVariantForButton(currentModel.variant || "default");
keyboardManager.updateAgent(currentAgent);
const keyboard = createMainKeyboard(
currentAgent,
currentModel,
Expand Down Expand Up @@ -198,7 +199,7 @@ export async function processUserPrompt(
}

try {
const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const storedModel = getStoredModel();

// Build parts array with text and files
Expand Down
6 changes: 4 additions & 2 deletions src/bot/handlers/variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
formatVariantForButton,
} from "../../variant/manager.js";
import { getStoredModel } from "../../model/manager.js";
import { getStoredAgent } from "../../agent/manager.js";
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
import { logger } from "../../utils/logger.js";
import { keyboardManager } from "../../keyboard/manager.js";
import { pinnedMessageManager } from "../../pinned/manager.js";
Expand Down Expand Up @@ -70,13 +70,15 @@ export async function handleVariantSelect(ctx: Context): Promise<boolean> {
keyboardManager.updateVariant(variantId);

// Build keyboard with correct context info
const currentAgent = getStoredAgent();
const currentAgent = await resolveProjectAgent(getStoredAgent());
const contextInfo =
pinnedMessageManager.getContextInfo() ??
(pinnedMessageManager.getContextLimit() > 0
? { tokensUsed: 0, tokensLimit: pinnedMessageManager.getContextLimit() }
: null);

keyboardManager.updateAgent(currentAgent);

if (contextInfo) {
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
}
Expand Down
Loading
Loading