Config split: agent.json (connection) + config.json (LLM)
- loadAgentConfig() reads both files with priority: env > config.json > agent.json - Remote config merge from auth.ok (model, provider, prompt) - Local config.json overrides remote values - Transport default changed to 'ws'
This commit is contained in:
parent
1c7fdf8d77
commit
6871fdb443
@ -116,7 +116,7 @@ export function hasAgentConfig(): boolean {
|
|||||||
export function loadAgentConfig(): AgentConfig {
|
export function loadAgentConfig(): AgentConfig {
|
||||||
const { configPath, agentHome } = resolveConfig();
|
const { configPath, agentHome } = resolveConfig();
|
||||||
|
|
||||||
// Load file if available, otherwise empty object
|
// Load agent.json (connection config)
|
||||||
let file: Record<string, unknown> = {};
|
let file: Record<string, unknown> = {};
|
||||||
if (configPath) {
|
if (configPath) {
|
||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath)) {
|
||||||
@ -125,6 +125,17 @@ export function loadAgentConfig(): AgentConfig {
|
|||||||
file = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
file = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load config.json (LLM config) — separate file, optional
|
||||||
|
let llm: Record<string, unknown> = {};
|
||||||
|
const llmConfigPath = configPath
|
||||||
|
? path.join(path.dirname(configPath), 'config.json')
|
||||||
|
: agentHome
|
||||||
|
? path.join(agentHome, 'config.json')
|
||||||
|
: null;
|
||||||
|
if (llmConfigPath && fs.existsSync(llmConfigPath)) {
|
||||||
|
llm = JSON.parse(fs.readFileSync(llmConfigPath, 'utf-8'));
|
||||||
|
}
|
||||||
|
|
||||||
const trackerUrl = process.env.TRACKER_URL || (file.tracker_url as string) || '';
|
const trackerUrl = process.env.TRACKER_URL || (file.tracker_url as string) || '';
|
||||||
const token = process.env.AGENT_TOKEN || (file.token as string) || '';
|
const token = process.env.AGENT_TOKEN || (file.token as string) || '';
|
||||||
|
|
||||||
@ -144,22 +155,23 @@ export function loadAgentConfig(): AgentConfig {
|
|||||||
|
|
||||||
const wsUrl = process.env.AGENT_WS_URL || (file.ws_url as string) || '';
|
const wsUrl = process.env.AGENT_WS_URL || (file.ws_url as string) || '';
|
||||||
|
|
||||||
|
// Priority: env > config.json (LLM) > agent.json (legacy compat) > defaults
|
||||||
return {
|
return {
|
||||||
name: (file.name as string) || process.env.AGENT_NAME || 'Agent',
|
name: (file.name as string) || process.env.AGENT_NAME || 'Agent',
|
||||||
slug: (file.slug as string) || process.env.AGENT_SLUG || 'agent',
|
slug: (file.slug as string) || process.env.AGENT_SLUG || 'agent',
|
||||||
prompt: (file.prompt as string) || process.env.AGENT_PROMPT || '',
|
prompt: (llm.prompt as string) || (file.prompt as string) || process.env.AGENT_PROMPT || '',
|
||||||
trackerUrl,
|
trackerUrl,
|
||||||
wsUrl,
|
wsUrl,
|
||||||
token,
|
token,
|
||||||
transport: (process.env.AGENT_TRANSPORT || (file.transport as string) || 'http') as 'http' | 'ws',
|
transport: (process.env.AGENT_TRANSPORT || (file.transport as string) || 'ws') as 'http' | 'ws',
|
||||||
listenPort: parseInt(process.env.AGENT_PORT || String(file.listen_port || '3200'), 10),
|
listenPort: parseInt(process.env.AGENT_PORT || String(file.listen_port || '3200'), 10),
|
||||||
workDir,
|
workDir,
|
||||||
agentHome: resolvedHome || workDir,
|
agentHome: resolvedHome || workDir,
|
||||||
capabilities: (file.capabilities as string[]) || ['coding'],
|
capabilities: (file.capabilities as string[]) || ['coding'],
|
||||||
maxConcurrentTasks: (file.max_concurrent_tasks as number) || 2,
|
maxConcurrentTasks: (llm.max_concurrent_tasks as number) || (file.max_concurrent_tasks as number) || 2,
|
||||||
model: process.env.PICOGENT_MODEL || (file.model as string) || 'sonnet',
|
model: process.env.PICOGENT_MODEL || (llm.model as string) || (file.model as string) || 'sonnet',
|
||||||
provider: process.env.PICOGENT_PROVIDER || (file.provider as string) || 'anthropic',
|
provider: process.env.PICOGENT_PROVIDER || (llm.provider as string) || (file.provider as string) || 'anthropic',
|
||||||
apiKey: process.env.PICOGENT_API_KEY || process.env.ANTHROPIC_API_KEY || (file.api_key as string) || '',
|
apiKey: process.env.PICOGENT_API_KEY || process.env.ANTHROPIC_API_KEY || (llm.api_key as string) || (file.api_key as string) || '',
|
||||||
heartbeatIntervalSec: (file.heartbeat_interval_sec as number) || 30,
|
heartbeatIntervalSec: (file.heartbeat_interval_sec as number) || 30,
|
||||||
allowedPaths: (file.allowed_paths as string[]) || [],
|
allowedPaths: (file.allowed_paths as string[]) || [],
|
||||||
sessionId: ensureSessionId(file, configPath),
|
sessionId: ensureSessionId(file, configPath),
|
||||||
|
|||||||
19
src/index.ts
19
src/index.ts
@ -51,6 +51,25 @@ async function startAgentWs(config: AgentConfig, client: TrackerClient): Promise
|
|||||||
router.setProjectMappings(wsTransport.projects);
|
router.setProjectMappings(wsTransport.projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge remote config (Tracker is source of truth, local config.json overrides)
|
||||||
|
if (wsTransport.remoteConfig) {
|
||||||
|
const rc = wsTransport.remoteConfig;
|
||||||
|
// Only apply remote values if local config.json didn't set them
|
||||||
|
// (loadAgentConfig already prioritized config.json > agent.json)
|
||||||
|
if (rc.model && !process.env.PICOGENT_MODEL && config.model === 'sonnet') {
|
||||||
|
config.model = rc.model;
|
||||||
|
logger.info('Applied remote model: %s', rc.model);
|
||||||
|
}
|
||||||
|
if (rc.provider && !process.env.PICOGENT_PROVIDER && config.provider === 'anthropic') {
|
||||||
|
config.provider = rc.provider;
|
||||||
|
logger.info('Applied remote provider: %s', rc.provider);
|
||||||
|
}
|
||||||
|
if (rc.prompt && !config.prompt) {
|
||||||
|
config.prompt = rc.prompt;
|
||||||
|
logger.info('Applied remote prompt (%d chars)', rc.prompt.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const shutdown = () => {
|
const shutdown = () => {
|
||||||
logger.info('Shutting down agent (ws)...');
|
logger.info('Shutting down agent (ws)...');
|
||||||
wsTransport.stop().then(() => {
|
wsTransport.stop().then(() => {
|
||||||
|
|||||||
@ -36,6 +36,14 @@ export class WsClientTransport implements TaskTracker {
|
|||||||
projects: Array<{ id: string; slug: string; name: string; chat_id?: string }> = [];
|
projects: Array<{ id: string; slug: string; name: string; chat_id?: string }> = [];
|
||||||
/** Online members from auth.ok */
|
/** Online members from auth.ok */
|
||||||
online: string[] = [];
|
online: string[] = [];
|
||||||
|
/** Remote agent config from auth.ok (if agent) */
|
||||||
|
remoteConfig: {
|
||||||
|
model?: string;
|
||||||
|
provider?: string;
|
||||||
|
prompt?: string;
|
||||||
|
max_concurrent_tasks?: number;
|
||||||
|
capabilities?: string[];
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
constructor(private config: AgentConfig) {}
|
constructor(private config: AgentConfig) {}
|
||||||
|
|
||||||
@ -205,11 +213,25 @@ export class WsClientTransport implements TaskTracker {
|
|||||||
this.projects = (data.projects as Array<{ id: string; slug: string; name: string; chat_id?: string }>) || [];
|
this.projects = (data.projects as Array<{ id: string; slug: string; name: string; chat_id?: string }>) || [];
|
||||||
this.online = (data.online as string[]) || (data.agents_online as string[]) || [];
|
this.online = (data.online as string[]) || (data.agents_online as string[]) || [];
|
||||||
|
|
||||||
|
// Remote agent config (if present)
|
||||||
|
const agentConfig = data.agent_config as Record<string, unknown> | undefined;
|
||||||
|
if (agentConfig) {
|
||||||
|
this.remoteConfig = {
|
||||||
|
model: agentConfig.model as string | undefined,
|
||||||
|
provider: agentConfig.provider as string | undefined,
|
||||||
|
prompt: agentConfig.prompt as string | undefined,
|
||||||
|
max_concurrent_tasks: agentConfig.max_concurrent_tasks as number | undefined,
|
||||||
|
capabilities: agentConfig.capabilities as string[] | undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.log.info('━━━ AUTH OK ━━━');
|
this.log.info('━━━ AUTH OK ━━━');
|
||||||
this.log.info(' Lobby chat: %s', this.lobbyChatId || '(none)');
|
this.log.info(' Lobby chat: %s', this.lobbyChatId || '(none)');
|
||||||
this.log.info(' Projects: %s', this.projects.map(p => `${p.slug}(${p.id})`).join(', ') || '(none)');
|
this.log.info(' Projects: %s', this.projects.map(p => `${p.slug}(${p.id})`).join(', ') || '(none)');
|
||||||
this.log.info(' Online: %s', this.online.join(', ') || '(nobody)');
|
this.log.info(' Online: %s', this.online.join(', ') || '(nobody)');
|
||||||
this.log.info(' Full auth data: %s', JSON.stringify(data, null, 2));
|
if (this.remoteConfig) {
|
||||||
|
this.log.info(' Remote config: model=%s provider=%s', this.remoteConfig.model || '-', this.remoteConfig.provider || '-');
|
||||||
|
}
|
||||||
|
|
||||||
this.authenticated = true;
|
this.authenticated = true;
|
||||||
this.reconnectDelay = 1000;
|
this.reconnectDelay = 1000;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user