diff --git a/packages/core/package.json b/packages/core/package.json index 970b01e4d4..9dc4a7b2d7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "./state/*": "./src/state/*.ts" }, "scripts": { - "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", + "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/db/conversations.test.ts && bun test src/utils/path-validation.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", "type-check": "bun x tsc --noEmit", "build": "echo 'No build needed - Bun runs TypeScript directly'" }, diff --git a/packages/core/src/db/conversations.test.ts b/packages/core/src/db/conversations.test.ts index e63ae767c5..befbd3b489 100644 --- a/packages/core/src/db/conversations.test.ts +++ b/packages/core/src/db/conversations.test.ts @@ -11,6 +11,23 @@ mock.module('./connection', () => ({ getDialect: () => mockPostgresDialect, })); +// Module-level variable to control mock config assistant per test +let mockConfigAssistant = 'claude'; + +// Mock config-loader to control assistant type in tests without filesystem dependency +mock.module('../config/config-loader', () => ({ + loadConfig: async () => ({ + assistant: mockConfigAssistant, + assistants: { claude: {}, codex: {} }, + botName: 'Archon', + streaming: { telegram: 'stream', discord: 'batch', slack: 'batch' }, + paths: { workspaces: '/tmp', worktrees: '/tmp' }, + concurrency: { maxConversations: 10 }, + commands: { folder: undefined, autoLoad: true }, + defaults: { copyDefaults: true, loadDefaultCommands: true, loadDefaultWorkflows: true }, + }), +})); + import { getOrCreateConversation, updateConversation, @@ -31,6 +48,8 @@ describe('conversations', () => { // Save and clear env var to ensure test isolation originalDefaultAiAssistant = process.env.DEFAULT_AI_ASSISTANT; delete process.env.DEFAULT_AI_ASSISTANT; + // Reset mock config to default + mockConfigAssistant = 'claude'; }); afterEach(() => { @@ -40,6 +59,7 @@ describe('conversations', () => { } else { process.env.DEFAULT_AI_ASSISTANT = originalDefaultAiAssistant; } + // No need to reset mockConfigAssistant here — beforeEach resets it to 'claude' before each test }); const existingConversation: Conversation = { @@ -122,8 +142,10 @@ describe('conversations', () => { }); test('uses DEFAULT_AI_ASSISTANT env var when set', async () => { - // Set env var for this test (afterEach will restore original) + // The mock returns mockConfigAssistant directly (env var contract tested in config-loader.test.ts). + // Set both so the test documents that loadConfig() honours DEFAULT_AI_ASSISTANT. process.env.DEFAULT_AI_ASSISTANT = 'codex'; + mockConfigAssistant = 'codex'; const newConversation: Conversation = { ...existingConversation, @@ -144,7 +166,30 @@ describe('conversations', () => { ); }); - test('falls back to claude when codebase not found', async () => { + test('uses saved global config defaultAssistant when no env var', async () => { + // Simulate global config having defaultAssistant=codex (set via Settings page) + mockConfigAssistant = 'codex'; + + const newConversation: Conversation = { + ...existingConversation, + id: 'conv-new', + ai_assistant_type: 'codex', + }; + + mockQuery.mockResolvedValueOnce(createQueryResult([])); + mockQuery.mockResolvedValueOnce(createQueryResult([newConversation])); + + const result = await getOrCreateConversation('telegram', 'chat-new'); + + expect(result).toEqual(newConversation); + expect(mockQuery).toHaveBeenNthCalledWith( + 2, + 'INSERT INTO remote_agent_conversations (platform_type, platform_conversation_id, ai_assistant_type, codebase_id, cwd) VALUES ($1, $2, $3, $4, $5) RETURNING *', + ['telegram', 'chat-new', 'codex', null, null] + ); + }); + + test('uses config default assistant when codebase not found', async () => { const newConversation: Conversation = { ...existingConversation, id: 'conv-new', diff --git a/packages/core/src/db/conversations.ts b/packages/core/src/db/conversations.ts index 0a7a237da3..37ed4037ed 100644 --- a/packages/core/src/db/conversations.ts +++ b/packages/core/src/db/conversations.ts @@ -5,6 +5,7 @@ import { pool, getDialect } from './connection'; import type { Conversation } from '../types'; import { ConversationNotFoundError } from '../types'; import { createLogger } from '@archon/paths'; +import { loadConfig } from '../config/config-loader'; /** Lazy-initialized logger (deferred so test mocks can intercept createLogger) */ let cachedLog: ReturnType | undefined; @@ -72,7 +73,8 @@ export async function getOrCreateConversation( // Check if we should inherit from a parent conversation (e.g., Discord thread inheriting from parent channel) let inheritedCodebaseId: string | null = null; let inheritedCwd: string | null = null; - let assistantType = process.env.DEFAULT_AI_ASSISTANT ?? 'claude'; + const config = await loadConfig(); + let assistantType = config.assistant; if (parentConversationId) { const parent = await pool.query(