aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-orchestrator.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-17 06:01:20 +0200
committerAgando <roduccomm@gmail.com>2026-06-17 06:01:20 +0200
commitcebacc27a48168e666679018f8e27156d12d4527 (patch)
treeb306d33b67b9d8c84bd9b9c16b1e91ee217628a6 /modules/core/module-orchestrator.js
parent6aa24b976952a698c673d350977721b27193183d (diff)
auto: 2026-06-17_06:01 changes
Diffstat (limited to 'modules/core/module-orchestrator.js')
-rw-r--r--modules/core/module-orchestrator.js41
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');
});
}