From 3d14627eb4665784113489d65b8f4c640401bda8 Mon Sep 17 00:00:00 2001 From: mehmet turac Date: Wed, 2 Sep 2026 11:09:46 +0300 Subject: [PATCH] fix(agent-runtime): stop run after local prompt config errors When assembleLocalAgentTemplates returns validation errors, callMainPrompt now returns early with an error result instead of continuing into mainPrompt. Previously the orphaned run kept executing (burning credits, running tools) after the prompt-error banner was already shown. Closes #1155 (sub-bug 1) --- .../src/__tests__/call-main-prompt.test.ts | 86 +++++++++++++++++++ packages/agent-runtime/src/main-prompt.ts | 21 ++++- 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 packages/agent-runtime/src/__tests__/call-main-prompt.test.ts diff --git a/packages/agent-runtime/src/__tests__/call-main-prompt.test.ts b/packages/agent-runtime/src/__tests__/call-main-prompt.test.ts new file mode 100644 index 0000000000..d99c4637af --- /dev/null +++ b/packages/agent-runtime/src/__tests__/call-main-prompt.test.ts @@ -0,0 +1,86 @@ +import { TEST_USER_ID } from '@codebuff/common/old-constants' +import { createTestAgentRuntimeParams } from '@codebuff/common/testing/fixtures/agent-runtime' +import { getInitialSessionState } from '@codebuff/common/types/session-state' +import { afterEach, describe, expect, it, mock, spyOn } from 'bun:test' + +import { callMainPrompt } from '../main-prompt' +import * as agentRegistry from '../templates/agent-registry' + +import type { ProjectFileContext } from '@codebuff/common/util/file' + +describe('callMainPrompt', () => { + afterEach(() => { + mock.restore() + }) + + const mockFileContext: ProjectFileContext = { + projectRoot: '/test', + cwd: '/test', + fileTree: [], + fileTokenScores: {}, + knowledgeFiles: {}, + gitChanges: { + status: '', + diff: '', + diffCached: '', + lastCommitMessages: '', + }, + changesSinceLastChat: {}, + shellConfigFiles: {}, + agentTemplates: {}, + customToolDefinitions: {}, + systemInfo: { + platform: 'test', + shell: 'test', + nodeVersion: 'test', + arch: 'test', + homedir: '/home/test', + cpus: 1, + chromeAvailable: false, + }, + } + + it('returns early without calling mainPrompt when agent config validation fails', async () => { + const sentActions: Array<{ type: string }> = [] + const sendAction = ({ action }: { action: { type: string } }) => { + sentActions.push(action) + } + + spyOn(agentRegistry, 'assembleLocalAgentTemplates').mockReturnValue({ + agentTemplates: {}, + validationErrors: [ + { message: 'bad agent config', agentId: 'test-agent' }, + ] as any, + }) + + const sessionState = getInitialSessionState(mockFileContext) + const baseParams = createTestAgentRuntimeParams() + + const result = await callMainPrompt({ + ...baseParams, + promptId: 'test-prompt', + sendAction, + logger: baseParams.logger, + signal: new AbortController().signal, + action: { + type: 'prompt' as const, + prompt: 'Hello', + sessionState, + fingerprintId: 'test', + costMode: 'normal' as const, + promptId: 'test-prompt', + toolResults: [], + }, + repoUrl: undefined, + repoId: undefined, + clientSessionId: 'test-session', + userId: TEST_USER_ID, + } as any) + + const actionTypes = sentActions.map((a) => a.type) + expect(actionTypes).toContain('prompt-error') + expect(actionTypes).toContain('prompt-response') + expect(actionTypes).not.toContain('response-chunk') + expect(result.output.type).toBe('error') + }) +}) diff --git a/packages/agent-runtime/src/main-prompt.ts b/packages/agent-runtime/src/main-prompt.ts index 4b81f0b368..518a434f1a 100644 --- a/packages/agent-runtime/src/main-prompt.ts +++ b/packages/agent-runtime/src/main-prompt.ts @@ -171,13 +171,32 @@ export async function callMainPrompt( assembleLocalAgentTemplates({ fileContext, logger }) if (validationErrors.length > 0) { + const errorMessage = `Invalid agent config: ${validationErrors.map((err) => err.message).join('\n')}` sendAction({ action: { type: 'prompt-error', - message: `Invalid agent config: ${validationErrors.map((err) => err.message).join('\n')}`, + message: errorMessage, userInputId: promptId, }, }) + + const errorResult = { + sessionState: action.sessionState, + output: { type: 'error' as const, message: errorMessage }, + } + + sendAction({ + action: { + type: 'prompt-response', + promptId, + sessionState: errorResult.sessionState, + toolCalls: [], + toolResults: [], + output: errorResult.output, + }, + }) + + return errorResult } sendAction({