diff options
| author | Agando <roduccomm@gmail.com> | 2026-06-17 06:01:20 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-06-17 06:01:20 +0200 |
| commit | cebacc27a48168e666679018f8e27156d12d4527 (patch) | |
| tree | b306d33b67b9d8c84bd9b9c16b1e91ee217628a6 /modules/core/module-orchestrator.js | |
| parent | 6aa24b976952a698c673d350977721b27193183d (diff) | |
auto: 2026-06-17_06:01 changes
Diffstat (limited to 'modules/core/module-orchestrator.js')
| -rw-r--r-- | modules/core/module-orchestrator.js | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/modules/core/module-orchestrator.js b/modules/core/module-orchestrator.js index bcbd00d..ba43dfd 100644 --- a/modules/core/module-orchestrator.js +++ b/modules/core/module-orchestrator.js @@ -12,6 +12,7 @@ class OrchestratorModule { this._HEARTBEAT_TIMEOUT = 15000; this._IDLE_TIMEOUT = 120000; this._RESTART_POLICY = 'never'; + this._AGENT_DEFAULT_MODEL = null; this._A2A_POLLING = true; this._A2A_POLL_INTERVAL = 500; this._HEARTBEAT_CHECK = 3000; @@ -34,7 +35,7 @@ class OrchestratorModule { this._loadConfig(); } _loadConfig() { - try { const c = localStorage.getItem('webx11_orchestrator_config'); if (c) { const p = JSON.parse(c); if (p.spawnLimit) this._SPAWN_LIMIT = p.spawnLimit; if (p.heartbeatTimeout) this._HEARTBEAT_TIMEOUT = p.heartbeatTimeout; if (p.idleTimeout) this._IDLE_TIMEOUT = p.idleTimeout; if (p.restartPolicy) this._RESTART_POLICY = p.restartPolicy; if (p.a2aPollInterval) this._A2A_POLL_INTERVAL = p.a2aPollInterval; if (p.heartbeatCheck) this._HEARTBEAT_CHECK = p.heartbeatCheck; if (p.idleSweepInterval) this._IDLE_SWEEP_INTERVAL = p.idleSweepInterval; if (p.deadPurgeTimeout) this._DEAD_PURGE_TIMEOUT = p.deadPurgeTimeout; if (p.consensusSize) this._CONSENSUS_SIZE = p.consensusSize; if (p.consensusStrategy) this._CONSENSUS_STRATEGY = p.consensusStrategy; if (p.consensusTimeout) this._CONSENSUS_TIMEOUT = p.consensusTimeout; if (p.pipelineAutoFix !== undefined) this._PIPELINE_CONFIG.autoFix = p.pipelineAutoFix; if (p.pipelineStageTimeout) this._PIPELINE_CONFIG.stageTimeout = p.pipelineStageTimeout; } } catch (e) {} + try { const c = localStorage.getItem('webx11_orchestrator_config'); if (c) { const p = JSON.parse(c); if (p.spawnLimit) this._SPAWN_LIMIT = p.spawnLimit; if (p.heartbeatTimeout) this._HEARTBEAT_TIMEOUT = p.heartbeatTimeout; if (p.idleTimeout) this._IDLE_TIMEOUT = p.idleTimeout; if (p.restartPolicy) this._RESTART_POLICY = p.restartPolicy; if (p.agentDefaultModel !== undefined) this._AGENT_DEFAULT_MODEL = p.agentDefaultModel; if (p.a2aPollInterval) this._A2A_POLL_INTERVAL = p.a2aPollInterval; if (p.heartbeatCheck) this._HEARTBEAT_CHECK = p.heartbeatCheck; if (p.idleSweepInterval) this._IDLE_SWEEP_INTERVAL = p.idleSweepInterval; if (p.deadPurgeTimeout) this._DEAD_PURGE_TIMEOUT = p.deadPurgeTimeout; if (p.consensusSize) this._CONSENSUS_SIZE = p.consensusSize; if (p.consensusStrategy) this._CONSENSUS_STRATEGY = p.consensusStrategy; if (p.consensusTimeout) this._CONSENSUS_TIMEOUT = p.consensusTimeout; if (p.pipelineAutoFix !== undefined) this._PIPELINE_CONFIG.autoFix = p.pipelineAutoFix; if (p.pipelineStageTimeout) this._PIPELINE_CONFIG.stageTimeout = p.pipelineStageTimeout; } } catch (e) {} } _saveConfig() { localStorage.setItem('webx11_orchestrator_config', JSON.stringify({ @@ -42,6 +43,7 @@ class OrchestratorModule { heartbeatTimeout: this._HEARTBEAT_TIMEOUT, idleTimeout: this._IDLE_TIMEOUT, restartPolicy: this._RESTART_POLICY, + agentDefaultModel: this._AGENT_DEFAULT_MODEL, a2aPollInterval: this._A2A_POLL_INTERVAL, heartbeatCheck: this._HEARTBEAT_CHECK, idleSweepInterval: this._IDLE_SWEEP_INTERVAL, @@ -56,7 +58,16 @@ class OrchestratorModule { registerCommands() { this.commands['orchestrate'] = (args) => this.delegateTask(args.join(' ')); this.commands['orchestrator-status'] = () => this.showStatus(); - this.commands['orchestrator-spawn'] = (args) => this.spawnAgent(args[0], args.slice(1)); + this.commands['orchestrator-spawn'] = (args) => { + // Parse --model flag before passing remaining as capabilities + const modelIdx = args.indexOf('--model'); + let modelName = null; + if (modelIdx !== -1 && args[modelIdx + 1]) { + modelName = args[modelIdx + 1]; + args.splice(modelIdx, 2); + } + this.spawnAgent(args[0], args.slice(1), modelName); + }; this.commands['orchestrator-kill'] = (args) => this.killAgent(args[0]); this.commands['orchestrator-purge'] = () => this.purgeDeadAgents(); this.commands['orchestrator-config'] = (args) => this.setConfig(args); @@ -311,7 +322,7 @@ class OrchestratorModule { _sanitizeId(name) { return (name || 'worker').replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').substring(0, 32) || 'worker'; } - spawnAgent(role, capabilities) { + spawnAgent(role, capabilities, modelName = null) { if (this._spawnLock) { this.terminal.print('⚠️ Spawn in progress — queued', 'warning'); return null; @@ -327,6 +338,7 @@ class OrchestratorModule { const agent = { id, role: role || 'worker', + model: modelName || this._AGENT_DEFAULT_MODEL, capabilities: capabilities.length ? capabilities : ['list_files', 'read_file', 'web_search'], createdAt: new Date().toISOString(), lastHeartbeat: Date.now(), @@ -344,7 +356,13 @@ class OrchestratorModule { this.terminal.fs.writeFile(`agents/${id}/status.json`, JSON.stringify(agent)); // Register in agent registry const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; - if (registry) registry.registerAgent(['register', id, ...agent.capabilities]); + if (registry) { + registry.registerAgent(['register', id, ...agent.capabilities]); + // A2A Scratchpad registration + if (typeof registry.a2aRegister === 'function') { + registry.a2aRegister(id, agent.capabilities); + } + } this.terminal.print(`🤖 Agent '${id}' spawned (role: ${role || 'worker'}, ${agent.capabilities.length} caps)`, 'success'); return agent; } finally { @@ -363,7 +381,13 @@ class OrchestratorModule { } // Unregister from registry const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; - if (registry) registry.unregisterAgent(id); + if (registry) { + registry.unregisterAgent(id); + // A2A Scratchpad unregister + if (typeof registry.a2aUnregister === 'function') { + registry.a2aUnregister(id); + } + } // Clean up VFS this.terminal.fs.deleteFile('agents/' + id); delete this._agents[id]; @@ -552,7 +576,8 @@ class OrchestratorModule { 'consensus-strategy': () => { if (!['majority', 'unanimous', 'weighted'].includes(val)) return 'Must be majority, unanimous, or weighted'; this._CONSENSUS_STRATEGY = val; return `consensus-strategy set to ${val}`; }, 'consensus-timeout': () => { const n = parseInt(val); if (isNaN(n) || n < 5000 || n > 300000) return 'Must be 5000-300000ms'; this._CONSENSUS_TIMEOUT = n; return `consensus-timeout set to ${n}ms`; }, 'pipeline-auto-fix': () => { this._PIPELINE_CONFIG.autoFix = val === 'on' || val === 'true'; return `auto-fix ${this._PIPELINE_CONFIG.autoFix ? 'on' : 'off'}`; }, - 'pipeline-stage-timeout': () => { const n = parseInt(val); if (isNaN(n) || n < 5000 || n > 120000) return 'Must be 5000-120000ms'; this._PIPELINE_CONFIG.stageTimeout = n; return `stage-timeout set to ${n}ms`; } + 'pipeline-stage-timeout': () => { const n = parseInt(val); if (isNaN(n) || n < 5000 || n > 120000) return 'Must be 5000-120000ms'; this._PIPELINE_CONFIG.stageTimeout = n; return `stage-timeout set to ${n}ms`; }, + 'agent-default-model': () => { this._AGENT_DEFAULT_MODEL = val; this._saveConfig(); return `agent-default-model set to ${val}`; } }; if (!configMap[key]) { @@ -1207,11 +1232,13 @@ class OrchestratorModule { this.terminal.print(` Agents: ${total} (${busy} busy, ${idle} idle, ${sleeping} sleeping, ${failed} failed)`, 'info'); this.terminal.print(` Spawn limit: ${this._SPAWN_LIMIT} (strategy: ${this._SPAWN_STRATEGY})`, 'info'); this.terminal.print(` Default restart: ${this._RESTART_POLICY}`, 'info'); + this.terminal.print(` Default agent model: ${this._AGENT_DEFAULT_MODEL || 'none (uses main AI model)'}`, 'info'); this.terminal.print(` A2A Messages: ${a2aTotal} (${a2aPending} pending)`, 'info'); Object.values(this._agents).forEach(a => { const task = a.currentTask ? ` (task: ${a.currentTask.substring(0, 40)})` : ''; + const modelInfo = a.model ? ` [model: ${a.model}]` : ''; const backoff = a.nextRestartAt && a.nextRestartAt > Date.now() ? ` [restart in ${Math.round((a.nextRestartAt - Date.now()) / 1000)}s]` : ''; - this.terminal.print(` ${a.id} [${a.role}] — ${a.status}${task}${backoff}`, 'info'); + this.terminal.print(` ${a.id} [${a.role}] — ${a.status}${modelInfo}${task}${backoff}`, 'info'); }); } |