@defai.digital/automatosx 12.5.0 → 12.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  [![Ubuntu](https://img.shields.io/badge/Ubuntu-24.04-blue.svg)](https://ubuntu.com)
13
13
  [![License](https://img.shields.io/badge/license-Apache--2.0-yellow.svg)](LICENSE)
14
14
 
15
- **Status**: ✅ **Production Ready** | v12.5.0 | MCP Hybrid Framing & Multi-Protocol Support
15
+ **Status**: ✅ **Production Ready** | v12.5.1 | MCP Hybrid Framing & Multi-Protocol Support
16
16
 
17
17
  > 🎯 **What AutomatosX Does**: Adds 20+ specialized agents, persistent memory, workflow automation, and 80% cost savings to Claude Code/Codex - **without changing how you work**.
18
18
 
package/dist/index.js CHANGED
@@ -9431,7 +9431,7 @@ var PRECOMPILED_CONFIG = {
9431
9431
  "enableFreeTierPrioritization": true,
9432
9432
  "enableWorkloadAwareRouting": true
9433
9433
  },
9434
- "version": "12.5.0"
9434
+ "version": "12.5.1"
9435
9435
  };
9436
9436
 
9437
9437
  // src/core/config/schemas.ts
@@ -26006,6 +26006,232 @@ ${context.task}`;
26006
26006
  }
26007
26007
  };
26008
26008
 
26009
+ // src/agents/agent-selector.ts
26010
+ init_esm_shims();
26011
+ init_logger();
26012
+ function scoreAgent(task, profile) {
26013
+ let score = 0;
26014
+ const taskLower = task.toLowerCase();
26015
+ if (profile.selectionMetadata?.primaryIntents) {
26016
+ for (const intent of profile.selectionMetadata.primaryIntents) {
26017
+ const intentKeywords = intent.toLowerCase().split(/\s+/);
26018
+ const matchedKeywords = intentKeywords.filter(
26019
+ (keyword) => taskLower.includes(keyword) && keyword.length > 3
26020
+ );
26021
+ if (matchedKeywords.length >= 2) {
26022
+ score += 10;
26023
+ } else if (matchedKeywords.length === 1) {
26024
+ score += 5;
26025
+ }
26026
+ }
26027
+ }
26028
+ if (profile.selectionMetadata?.secondarySignals) {
26029
+ for (const signal of profile.selectionMetadata.secondarySignals) {
26030
+ if (taskLower.includes(signal.toLowerCase())) {
26031
+ score += 5;
26032
+ }
26033
+ }
26034
+ }
26035
+ if (profile.selectionMetadata?.negativeIntents) {
26036
+ for (const negative of profile.selectionMetadata.negativeIntents) {
26037
+ const keywords = negative.split("(")[0]?.toLowerCase() || "";
26038
+ const negativeKeywords = keywords.split(/\s+/).filter((k) => k.length > 3);
26039
+ const matchedNegative = negativeKeywords.filter(
26040
+ (keyword) => taskLower.includes(keyword)
26041
+ );
26042
+ if (matchedNegative.length >= 2) {
26043
+ score -= 20;
26044
+ }
26045
+ }
26046
+ }
26047
+ if (profile.selectionMetadata?.redirectWhen) {
26048
+ for (const rule of profile.selectionMetadata.redirectWhen) {
26049
+ try {
26050
+ const regex = new RegExp(rule.phrase, "i");
26051
+ if (regex.test(task)) {
26052
+ score -= 15;
26053
+ }
26054
+ } catch (error) {
26055
+ logger.debug("Invalid regex pattern in redirectWhen rule", {
26056
+ pattern: rule.phrase,
26057
+ error: error instanceof Error ? error.message : String(error)
26058
+ });
26059
+ }
26060
+ }
26061
+ }
26062
+ if (profile.abilitySelection?.taskBased) {
26063
+ for (const keyword of Object.keys(profile.abilitySelection.taskBased)) {
26064
+ if (taskLower.includes(keyword.toLowerCase())) {
26065
+ score += 3;
26066
+ }
26067
+ }
26068
+ }
26069
+ return Math.max(0, score);
26070
+ }
26071
+ function buildRationale(task, profile) {
26072
+ const rationale = [];
26073
+ const taskLower = task.toLowerCase();
26074
+ if (profile.selectionMetadata?.primaryIntents) {
26075
+ const matchedIntents = profile.selectionMetadata.primaryIntents.filter((intent) => {
26076
+ const keywords = intent.toLowerCase().split(/\s+/);
26077
+ return keywords.some((k) => taskLower.includes(k) && k.length > 3);
26078
+ });
26079
+ if (matchedIntents.length > 0) {
26080
+ rationale.push(`Matches: ${matchedIntents.slice(0, 2).join(", ")}`);
26081
+ }
26082
+ }
26083
+ if (profile.selectionMetadata?.secondarySignals) {
26084
+ const matchedSignals = profile.selectionMetadata.secondarySignals.filter(
26085
+ (signal) => taskLower.includes(signal.toLowerCase())
26086
+ );
26087
+ if (matchedSignals.length > 0) {
26088
+ rationale.push(`Keywords: ${matchedSignals.slice(0, 3).join(", ")}`);
26089
+ }
26090
+ }
26091
+ if (profile.abilitySelection?.taskBased) {
26092
+ const matchedAbilities = Object.keys(profile.abilitySelection.taskBased).filter(
26093
+ (keyword) => taskLower.includes(keyword.toLowerCase())
26094
+ );
26095
+ if (matchedAbilities.length > 0) {
26096
+ rationale.push(`Abilities: ${matchedAbilities.slice(0, 2).join(", ")}`);
26097
+ }
26098
+ }
26099
+ if (rationale.length === 0) {
26100
+ rationale.push("General capability match");
26101
+ }
26102
+ return rationale;
26103
+ }
26104
+ function getConfidence(score) {
26105
+ if (score >= 30) return "high";
26106
+ if (score >= 15) return "medium";
26107
+ return "low";
26108
+ }
26109
+ var MIN_SELECTION_SCORE = 5;
26110
+ var FALLBACK_AGENT = "standard";
26111
+ var AgentSelector = class {
26112
+ profileLoader;
26113
+ constructor(profileLoader) {
26114
+ this.profileLoader = profileLoader;
26115
+ }
26116
+ /**
26117
+ * Select the best agent for a task
26118
+ *
26119
+ * @param task - Task description
26120
+ * @returns Selection result with agent, score, confidence, and rationale
26121
+ */
26122
+ async selectAgent(task) {
26123
+ logger.debug("[AgentSelector] Selecting agent for task", {
26124
+ taskPreview: task.substring(0, 100)
26125
+ });
26126
+ const agentNames = await this.profileLoader.listProfiles();
26127
+ if (agentNames.length === 0) {
26128
+ logger.warn("[AgentSelector] No agents found, using fallback");
26129
+ return this.createFallbackResult(task);
26130
+ }
26131
+ const profileResults = await Promise.all(
26132
+ agentNames.map(async (name) => {
26133
+ try {
26134
+ const profile = await this.profileLoader.loadProfile(name);
26135
+ return { name, profile, score: scoreAgent(task, profile) };
26136
+ } catch (error) {
26137
+ logger.debug(`[AgentSelector] Failed to load profile: ${name}`, { error });
26138
+ return null;
26139
+ }
26140
+ })
26141
+ );
26142
+ const scoredAgents = profileResults.filter(
26143
+ (result) => result !== null
26144
+ );
26145
+ if (scoredAgents.length === 0) {
26146
+ logger.warn("[AgentSelector] Failed to load any profiles, using fallback");
26147
+ return this.createFallbackResult(task);
26148
+ }
26149
+ scoredAgents.sort((a, b) => b.score - a.score);
26150
+ const best = scoredAgents[0];
26151
+ const alternatives = scoredAgents.slice(1, 4).map((a) => ({
26152
+ agent: a.name,
26153
+ score: a.score
26154
+ }));
26155
+ if (best.score < MIN_SELECTION_SCORE) {
26156
+ logger.info("[AgentSelector] Best score below threshold, using fallback", {
26157
+ bestAgent: best.name,
26158
+ bestScore: best.score,
26159
+ threshold: MIN_SELECTION_SCORE
26160
+ });
26161
+ const standardAgent = scoredAgents.find((a) => a.name === FALLBACK_AGENT);
26162
+ if (standardAgent) {
26163
+ return {
26164
+ agent: standardAgent.name,
26165
+ displayName: standardAgent.profile.displayName || standardAgent.name,
26166
+ role: standardAgent.profile.role,
26167
+ score: standardAgent.score,
26168
+ confidence: "low",
26169
+ rationale: ["No strong match found, using general-purpose agent"],
26170
+ alternatives: scoredAgents.filter((a) => a.name !== FALLBACK_AGENT).slice(0, 3).map((a) => ({ agent: a.name, score: a.score })),
26171
+ usedFallback: true
26172
+ };
26173
+ }
26174
+ return {
26175
+ agent: best.name,
26176
+ displayName: best.profile.displayName || best.name,
26177
+ role: best.profile.role,
26178
+ score: best.score,
26179
+ confidence: "low",
26180
+ rationale: ["Low confidence match - consider specifying agent explicitly"],
26181
+ alternatives,
26182
+ usedFallback: false
26183
+ };
26184
+ }
26185
+ const rationale = buildRationale(task, best.profile);
26186
+ const confidence = getConfidence(best.score);
26187
+ logger.info("[AgentSelector] Agent selected", {
26188
+ agent: best.name,
26189
+ score: best.score,
26190
+ confidence,
26191
+ rationale
26192
+ });
26193
+ return {
26194
+ agent: best.name,
26195
+ displayName: best.profile.displayName || best.name,
26196
+ role: best.profile.role,
26197
+ score: best.score,
26198
+ confidence,
26199
+ rationale,
26200
+ alternatives,
26201
+ usedFallback: false
26202
+ };
26203
+ }
26204
+ /**
26205
+ * Create a fallback result when no agents are available
26206
+ */
26207
+ async createFallbackResult(_task) {
26208
+ try {
26209
+ const profile = await this.profileLoader.loadProfile(FALLBACK_AGENT);
26210
+ return {
26211
+ agent: FALLBACK_AGENT,
26212
+ displayName: profile.displayName || FALLBACK_AGENT,
26213
+ role: profile.role,
26214
+ score: 0,
26215
+ confidence: "low",
26216
+ rationale: ["No agents available, using fallback"],
26217
+ alternatives: [],
26218
+ usedFallback: true
26219
+ };
26220
+ } catch {
26221
+ return {
26222
+ agent: FALLBACK_AGENT,
26223
+ displayName: "Standard",
26224
+ role: "General-purpose agent",
26225
+ score: 0,
26226
+ confidence: "low",
26227
+ rationale: ["No agents available"],
26228
+ alternatives: [],
26229
+ usedFallback: true
26230
+ };
26231
+ }
26232
+ }
26233
+ };
26234
+
26009
26235
  // src/mcp/tools/run-agent.ts
26010
26236
  init_logger();
26011
26237
 
@@ -26305,22 +26531,41 @@ async function buildAgentContext(agentName, task, deps, callerProvider, bestProv
26305
26531
  }
26306
26532
  function createRunAgentHandler(deps) {
26307
26533
  return async (input, context) => {
26308
- const { agent, task, provider, no_memory, mode = "auto" } = input;
26534
+ const { task, provider, no_memory, mode = "auto" } = input;
26535
+ let { agent } = input;
26309
26536
  if (context?.signal?.aborted) {
26310
26537
  throw new Error("Request was cancelled");
26311
26538
  }
26312
- validateAgentName(agent);
26313
26539
  validateStringParameter(task, "task", {
26314
26540
  required: true,
26315
26541
  minLength: 1,
26316
26542
  maxLength: 1e4
26317
26543
  });
26544
+ let autoSelected = false;
26545
+ if (!agent && deps.profileLoader) {
26546
+ const selector = new AgentSelector(deps.profileLoader);
26547
+ const selection = await selector.selectAgent(task);
26548
+ agent = selection.agent;
26549
+ autoSelected = true;
26550
+ logger.info("[MCP] run_agent auto-selected agent", {
26551
+ task: task.substring(0, 100),
26552
+ selectedAgent: agent,
26553
+ confidence: selection.confidence,
26554
+ score: selection.score,
26555
+ rationale: selection.rationale
26556
+ });
26557
+ }
26558
+ if (!agent) {
26559
+ throw new Error("Agent name is required when profileLoader is not available");
26560
+ }
26561
+ validateAgentName(agent);
26318
26562
  const actualProvider = mapMcpProviderToActual(provider);
26319
26563
  const session = deps.getSession?.() || null;
26320
26564
  const callerProvider = session?.normalizedProvider || "unknown";
26321
26565
  const callerActual = mapNormalizedCallerToActual(callerProvider);
26322
26566
  logger.info("[MCP] run_agent called (Smart Routing v10.5.0)", {
26323
26567
  agent,
26568
+ autoSelected,
26324
26569
  task: task.substring(0, 100),
26325
26570
  mcpProvider: provider,
26326
26571
  actualProvider,
@@ -27133,20 +27378,35 @@ init_logger();
27133
27378
  function createGetAgentContextHandler(deps) {
27134
27379
  return async (input) => {
27135
27380
  const {
27136
- agent,
27137
27381
  task,
27138
27382
  includeMemory = true,
27139
27383
  maxMemoryResults = 5
27140
27384
  } = input;
27385
+ let { agent } = input;
27141
27386
  const startTime = Date.now();
27142
- validateAgentName(agent);
27143
27387
  validateStringParameter(task, "task", {
27144
27388
  required: true,
27145
27389
  minLength: 1,
27146
27390
  maxLength: 1e4
27147
27391
  });
27392
+ let autoSelected = false;
27393
+ if (!agent) {
27394
+ const selector = new AgentSelector(deps.profileLoader);
27395
+ const selection = await selector.selectAgent(task);
27396
+ agent = selection.agent;
27397
+ autoSelected = true;
27398
+ logger.info("[MCP] get_agent_context auto-selected agent", {
27399
+ task: task.substring(0, 100),
27400
+ selectedAgent: agent,
27401
+ confidence: selection.confidence,
27402
+ score: selection.score,
27403
+ rationale: selection.rationale
27404
+ });
27405
+ }
27406
+ validateAgentName(agent);
27148
27407
  logger.info("[MCP] get_agent_context called", {
27149
27408
  agent,
27409
+ autoSelected,
27150
27410
  task: task.substring(0, 100),
27151
27411
  includeMemory,
27152
27412
  maxMemoryResults
@@ -31697,17 +31957,24 @@ var McpServer = class _McpServer {
31697
31957
  return [
31698
31958
  {
31699
31959
  name: "run_agent",
31700
- description: "Execute an AutomatosX agent with a specific task. Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.",
31960
+ description: `Execute an AutomatosX agent with a specific task.
31961
+
31962
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.
31963
+ Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.
31964
+
31965
+ Examples:
31966
+ - With agent: run_agent({ agent: "backend", task: "implement API" })
31967
+ - Auto-select: run_agent({ task: "fix bugs in the codebase" }) \u2192 selects "quality" agent`,
31701
31968
  inputSchema: {
31702
31969
  type: "object",
31703
31970
  properties: {
31704
- agent: { type: "string", description: "The name of the agent to run (e.g., backend, Paris, Bob)" },
31971
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
31705
31972
  task: { type: "string", description: "The task for the agent to perform" },
31706
31973
  provider: { type: "string", description: "Optional: Override the AI provider", enum: ["claude", "gemini", "openai"] },
31707
31974
  no_memory: { type: "boolean", description: "Optional: Skip memory injection", default: false },
31708
31975
  mode: { type: "string", description: "Optional: Execution mode - auto (default), context (always return context), execute (always spawn)", enum: ["auto", "context", "execute"], default: "auto" }
31709
31976
  },
31710
- required: ["agent", "task"]
31977
+ required: ["task"]
31711
31978
  }
31712
31979
  },
31713
31980
  {
@@ -31819,16 +32086,18 @@ Use this tool first to understand what AutomatosX offers.`,
31819
32086
  // v10.5.0: Smart Routing - Explicit context retrieval
31820
32087
  {
31821
32088
  name: "get_agent_context",
31822
- description: "Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.",
32089
+ description: `Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.
32090
+
32091
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.`,
31823
32092
  inputSchema: {
31824
32093
  type: "object",
31825
32094
  properties: {
31826
- agent: { type: "string", description: "The name of the agent (e.g., backend, Paris, Bob)" },
32095
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
31827
32096
  task: { type: "string", description: "The task description for context building" },
31828
32097
  includeMemory: { type: "boolean", description: "Include relevant memory entries (default: true)", default: true },
31829
32098
  maxMemoryResults: { type: "number", description: "Maximum memory entries to return (default: 5)", default: 5 }
31830
32099
  },
31831
- required: ["agent", "task"]
32100
+ required: ["task"]
31832
32101
  }
31833
32102
  },
31834
32103
  // v11.3.5: Task Engine tools
@@ -35189,232 +35458,6 @@ var memoryCommand = {
35189
35458
  // src/cli/commands/run.ts
35190
35459
  init_esm_shims();
35191
35460
 
35192
- // src/agents/agent-selector.ts
35193
- init_esm_shims();
35194
- init_logger();
35195
- function scoreAgent(task, profile) {
35196
- let score = 0;
35197
- const taskLower = task.toLowerCase();
35198
- if (profile.selectionMetadata?.primaryIntents) {
35199
- for (const intent of profile.selectionMetadata.primaryIntents) {
35200
- const intentKeywords = intent.toLowerCase().split(/\s+/);
35201
- const matchedKeywords = intentKeywords.filter(
35202
- (keyword) => taskLower.includes(keyword) && keyword.length > 3
35203
- );
35204
- if (matchedKeywords.length >= 2) {
35205
- score += 10;
35206
- } else if (matchedKeywords.length === 1) {
35207
- score += 5;
35208
- }
35209
- }
35210
- }
35211
- if (profile.selectionMetadata?.secondarySignals) {
35212
- for (const signal of profile.selectionMetadata.secondarySignals) {
35213
- if (taskLower.includes(signal.toLowerCase())) {
35214
- score += 5;
35215
- }
35216
- }
35217
- }
35218
- if (profile.selectionMetadata?.negativeIntents) {
35219
- for (const negative of profile.selectionMetadata.negativeIntents) {
35220
- const keywords = negative.split("(")[0]?.toLowerCase() || "";
35221
- const negativeKeywords = keywords.split(/\s+/).filter((k) => k.length > 3);
35222
- const matchedNegative = negativeKeywords.filter(
35223
- (keyword) => taskLower.includes(keyword)
35224
- );
35225
- if (matchedNegative.length >= 2) {
35226
- score -= 20;
35227
- }
35228
- }
35229
- }
35230
- if (profile.selectionMetadata?.redirectWhen) {
35231
- for (const rule of profile.selectionMetadata.redirectWhen) {
35232
- try {
35233
- const regex = new RegExp(rule.phrase, "i");
35234
- if (regex.test(task)) {
35235
- score -= 15;
35236
- }
35237
- } catch (error) {
35238
- logger.debug("Invalid regex pattern in redirectWhen rule", {
35239
- pattern: rule.phrase,
35240
- error: error instanceof Error ? error.message : String(error)
35241
- });
35242
- }
35243
- }
35244
- }
35245
- if (profile.abilitySelection?.taskBased) {
35246
- for (const keyword of Object.keys(profile.abilitySelection.taskBased)) {
35247
- if (taskLower.includes(keyword.toLowerCase())) {
35248
- score += 3;
35249
- }
35250
- }
35251
- }
35252
- return Math.max(0, score);
35253
- }
35254
- function buildRationale(task, profile) {
35255
- const rationale = [];
35256
- const taskLower = task.toLowerCase();
35257
- if (profile.selectionMetadata?.primaryIntents) {
35258
- const matchedIntents = profile.selectionMetadata.primaryIntents.filter((intent) => {
35259
- const keywords = intent.toLowerCase().split(/\s+/);
35260
- return keywords.some((k) => taskLower.includes(k) && k.length > 3);
35261
- });
35262
- if (matchedIntents.length > 0) {
35263
- rationale.push(`Matches: ${matchedIntents.slice(0, 2).join(", ")}`);
35264
- }
35265
- }
35266
- if (profile.selectionMetadata?.secondarySignals) {
35267
- const matchedSignals = profile.selectionMetadata.secondarySignals.filter(
35268
- (signal) => taskLower.includes(signal.toLowerCase())
35269
- );
35270
- if (matchedSignals.length > 0) {
35271
- rationale.push(`Keywords: ${matchedSignals.slice(0, 3).join(", ")}`);
35272
- }
35273
- }
35274
- if (profile.abilitySelection?.taskBased) {
35275
- const matchedAbilities = Object.keys(profile.abilitySelection.taskBased).filter(
35276
- (keyword) => taskLower.includes(keyword.toLowerCase())
35277
- );
35278
- if (matchedAbilities.length > 0) {
35279
- rationale.push(`Abilities: ${matchedAbilities.slice(0, 2).join(", ")}`);
35280
- }
35281
- }
35282
- if (rationale.length === 0) {
35283
- rationale.push("General capability match");
35284
- }
35285
- return rationale;
35286
- }
35287
- function getConfidence(score) {
35288
- if (score >= 30) return "high";
35289
- if (score >= 15) return "medium";
35290
- return "low";
35291
- }
35292
- var MIN_SELECTION_SCORE = 5;
35293
- var FALLBACK_AGENT = "standard";
35294
- var AgentSelector = class {
35295
- profileLoader;
35296
- constructor(profileLoader) {
35297
- this.profileLoader = profileLoader;
35298
- }
35299
- /**
35300
- * Select the best agent for a task
35301
- *
35302
- * @param task - Task description
35303
- * @returns Selection result with agent, score, confidence, and rationale
35304
- */
35305
- async selectAgent(task) {
35306
- logger.debug("[AgentSelector] Selecting agent for task", {
35307
- taskPreview: task.substring(0, 100)
35308
- });
35309
- const agentNames = await this.profileLoader.listProfiles();
35310
- if (agentNames.length === 0) {
35311
- logger.warn("[AgentSelector] No agents found, using fallback");
35312
- return this.createFallbackResult(task);
35313
- }
35314
- const profileResults = await Promise.all(
35315
- agentNames.map(async (name) => {
35316
- try {
35317
- const profile = await this.profileLoader.loadProfile(name);
35318
- return { name, profile, score: scoreAgent(task, profile) };
35319
- } catch (error) {
35320
- logger.debug(`[AgentSelector] Failed to load profile: ${name}`, { error });
35321
- return null;
35322
- }
35323
- })
35324
- );
35325
- const scoredAgents = profileResults.filter(
35326
- (result) => result !== null
35327
- );
35328
- if (scoredAgents.length === 0) {
35329
- logger.warn("[AgentSelector] Failed to load any profiles, using fallback");
35330
- return this.createFallbackResult(task);
35331
- }
35332
- scoredAgents.sort((a, b) => b.score - a.score);
35333
- const best = scoredAgents[0];
35334
- const alternatives = scoredAgents.slice(1, 4).map((a) => ({
35335
- agent: a.name,
35336
- score: a.score
35337
- }));
35338
- if (best.score < MIN_SELECTION_SCORE) {
35339
- logger.info("[AgentSelector] Best score below threshold, using fallback", {
35340
- bestAgent: best.name,
35341
- bestScore: best.score,
35342
- threshold: MIN_SELECTION_SCORE
35343
- });
35344
- const standardAgent = scoredAgents.find((a) => a.name === FALLBACK_AGENT);
35345
- if (standardAgent) {
35346
- return {
35347
- agent: standardAgent.name,
35348
- displayName: standardAgent.profile.displayName || standardAgent.name,
35349
- role: standardAgent.profile.role,
35350
- score: standardAgent.score,
35351
- confidence: "low",
35352
- rationale: ["No strong match found, using general-purpose agent"],
35353
- alternatives: scoredAgents.filter((a) => a.name !== FALLBACK_AGENT).slice(0, 3).map((a) => ({ agent: a.name, score: a.score })),
35354
- usedFallback: true
35355
- };
35356
- }
35357
- return {
35358
- agent: best.name,
35359
- displayName: best.profile.displayName || best.name,
35360
- role: best.profile.role,
35361
- score: best.score,
35362
- confidence: "low",
35363
- rationale: ["Low confidence match - consider specifying agent explicitly"],
35364
- alternatives,
35365
- usedFallback: false
35366
- };
35367
- }
35368
- const rationale = buildRationale(task, best.profile);
35369
- const confidence = getConfidence(best.score);
35370
- logger.info("[AgentSelector] Agent selected", {
35371
- agent: best.name,
35372
- score: best.score,
35373
- confidence,
35374
- rationale
35375
- });
35376
- return {
35377
- agent: best.name,
35378
- displayName: best.profile.displayName || best.name,
35379
- role: best.profile.role,
35380
- score: best.score,
35381
- confidence,
35382
- rationale,
35383
- alternatives,
35384
- usedFallback: false
35385
- };
35386
- }
35387
- /**
35388
- * Create a fallback result when no agents are available
35389
- */
35390
- async createFallbackResult(_task) {
35391
- try {
35392
- const profile = await this.profileLoader.loadProfile(FALLBACK_AGENT);
35393
- return {
35394
- agent: FALLBACK_AGENT,
35395
- displayName: profile.displayName || FALLBACK_AGENT,
35396
- role: profile.role,
35397
- score: 0,
35398
- confidence: "low",
35399
- rationale: ["No agents available, using fallback"],
35400
- alternatives: [],
35401
- usedFallback: true
35402
- };
35403
- } catch {
35404
- return {
35405
- agent: FALLBACK_AGENT,
35406
- displayName: "Standard",
35407
- role: "General-purpose agent",
35408
- score: 0,
35409
- confidence: "low",
35410
- rationale: ["No agents available"],
35411
- alternatives: [],
35412
- usedFallback: true
35413
- };
35414
- }
35415
- }
35416
- };
35417
-
35418
35461
  // src/core/stage-execution-controller.ts
35419
35462
  init_esm_shims();
35420
35463
 
package/dist/mcp/index.js CHANGED
@@ -8727,7 +8727,7 @@ var PRECOMPILED_CONFIG = {
8727
8727
  "enableFreeTierPrioritization": true,
8728
8728
  "enableWorkloadAwareRouting": true
8729
8729
  },
8730
- "version": "12.5.0"
8730
+ "version": "12.5.1"
8731
8731
  };
8732
8732
 
8733
8733
  // src/core/config/schemas.ts
@@ -20348,6 +20348,232 @@ ${context.task}`;
20348
20348
  }
20349
20349
  };
20350
20350
 
20351
+ // src/agents/agent-selector.ts
20352
+ init_esm_shims();
20353
+ init_logger();
20354
+ function scoreAgent(task, profile) {
20355
+ let score = 0;
20356
+ const taskLower = task.toLowerCase();
20357
+ if (profile.selectionMetadata?.primaryIntents) {
20358
+ for (const intent of profile.selectionMetadata.primaryIntents) {
20359
+ const intentKeywords = intent.toLowerCase().split(/\s+/);
20360
+ const matchedKeywords = intentKeywords.filter(
20361
+ (keyword) => taskLower.includes(keyword) && keyword.length > 3
20362
+ );
20363
+ if (matchedKeywords.length >= 2) {
20364
+ score += 10;
20365
+ } else if (matchedKeywords.length === 1) {
20366
+ score += 5;
20367
+ }
20368
+ }
20369
+ }
20370
+ if (profile.selectionMetadata?.secondarySignals) {
20371
+ for (const signal of profile.selectionMetadata.secondarySignals) {
20372
+ if (taskLower.includes(signal.toLowerCase())) {
20373
+ score += 5;
20374
+ }
20375
+ }
20376
+ }
20377
+ if (profile.selectionMetadata?.negativeIntents) {
20378
+ for (const negative of profile.selectionMetadata.negativeIntents) {
20379
+ const keywords = negative.split("(")[0]?.toLowerCase() || "";
20380
+ const negativeKeywords = keywords.split(/\s+/).filter((k) => k.length > 3);
20381
+ const matchedNegative = negativeKeywords.filter(
20382
+ (keyword) => taskLower.includes(keyword)
20383
+ );
20384
+ if (matchedNegative.length >= 2) {
20385
+ score -= 20;
20386
+ }
20387
+ }
20388
+ }
20389
+ if (profile.selectionMetadata?.redirectWhen) {
20390
+ for (const rule of profile.selectionMetadata.redirectWhen) {
20391
+ try {
20392
+ const regex = new RegExp(rule.phrase, "i");
20393
+ if (regex.test(task)) {
20394
+ score -= 15;
20395
+ }
20396
+ } catch (error) {
20397
+ logger.debug("Invalid regex pattern in redirectWhen rule", {
20398
+ pattern: rule.phrase,
20399
+ error: error instanceof Error ? error.message : String(error)
20400
+ });
20401
+ }
20402
+ }
20403
+ }
20404
+ if (profile.abilitySelection?.taskBased) {
20405
+ for (const keyword of Object.keys(profile.abilitySelection.taskBased)) {
20406
+ if (taskLower.includes(keyword.toLowerCase())) {
20407
+ score += 3;
20408
+ }
20409
+ }
20410
+ }
20411
+ return Math.max(0, score);
20412
+ }
20413
+ function buildRationale(task, profile) {
20414
+ const rationale = [];
20415
+ const taskLower = task.toLowerCase();
20416
+ if (profile.selectionMetadata?.primaryIntents) {
20417
+ const matchedIntents = profile.selectionMetadata.primaryIntents.filter((intent) => {
20418
+ const keywords = intent.toLowerCase().split(/\s+/);
20419
+ return keywords.some((k) => taskLower.includes(k) && k.length > 3);
20420
+ });
20421
+ if (matchedIntents.length > 0) {
20422
+ rationale.push(`Matches: ${matchedIntents.slice(0, 2).join(", ")}`);
20423
+ }
20424
+ }
20425
+ if (profile.selectionMetadata?.secondarySignals) {
20426
+ const matchedSignals = profile.selectionMetadata.secondarySignals.filter(
20427
+ (signal) => taskLower.includes(signal.toLowerCase())
20428
+ );
20429
+ if (matchedSignals.length > 0) {
20430
+ rationale.push(`Keywords: ${matchedSignals.slice(0, 3).join(", ")}`);
20431
+ }
20432
+ }
20433
+ if (profile.abilitySelection?.taskBased) {
20434
+ const matchedAbilities = Object.keys(profile.abilitySelection.taskBased).filter(
20435
+ (keyword) => taskLower.includes(keyword.toLowerCase())
20436
+ );
20437
+ if (matchedAbilities.length > 0) {
20438
+ rationale.push(`Abilities: ${matchedAbilities.slice(0, 2).join(", ")}`);
20439
+ }
20440
+ }
20441
+ if (rationale.length === 0) {
20442
+ rationale.push("General capability match");
20443
+ }
20444
+ return rationale;
20445
+ }
20446
+ function getConfidence(score) {
20447
+ if (score >= 30) return "high";
20448
+ if (score >= 15) return "medium";
20449
+ return "low";
20450
+ }
20451
+ var MIN_SELECTION_SCORE = 5;
20452
+ var FALLBACK_AGENT = "standard";
20453
+ var AgentSelector = class {
20454
+ profileLoader;
20455
+ constructor(profileLoader) {
20456
+ this.profileLoader = profileLoader;
20457
+ }
20458
+ /**
20459
+ * Select the best agent for a task
20460
+ *
20461
+ * @param task - Task description
20462
+ * @returns Selection result with agent, score, confidence, and rationale
20463
+ */
20464
+ async selectAgent(task) {
20465
+ logger.debug("[AgentSelector] Selecting agent for task", {
20466
+ taskPreview: task.substring(0, 100)
20467
+ });
20468
+ const agentNames = await this.profileLoader.listProfiles();
20469
+ if (agentNames.length === 0) {
20470
+ logger.warn("[AgentSelector] No agents found, using fallback");
20471
+ return this.createFallbackResult(task);
20472
+ }
20473
+ const profileResults = await Promise.all(
20474
+ agentNames.map(async (name) => {
20475
+ try {
20476
+ const profile = await this.profileLoader.loadProfile(name);
20477
+ return { name, profile, score: scoreAgent(task, profile) };
20478
+ } catch (error) {
20479
+ logger.debug(`[AgentSelector] Failed to load profile: ${name}`, { error });
20480
+ return null;
20481
+ }
20482
+ })
20483
+ );
20484
+ const scoredAgents = profileResults.filter(
20485
+ (result) => result !== null
20486
+ );
20487
+ if (scoredAgents.length === 0) {
20488
+ logger.warn("[AgentSelector] Failed to load any profiles, using fallback");
20489
+ return this.createFallbackResult(task);
20490
+ }
20491
+ scoredAgents.sort((a, b) => b.score - a.score);
20492
+ const best = scoredAgents[0];
20493
+ const alternatives = scoredAgents.slice(1, 4).map((a) => ({
20494
+ agent: a.name,
20495
+ score: a.score
20496
+ }));
20497
+ if (best.score < MIN_SELECTION_SCORE) {
20498
+ logger.info("[AgentSelector] Best score below threshold, using fallback", {
20499
+ bestAgent: best.name,
20500
+ bestScore: best.score,
20501
+ threshold: MIN_SELECTION_SCORE
20502
+ });
20503
+ const standardAgent = scoredAgents.find((a) => a.name === FALLBACK_AGENT);
20504
+ if (standardAgent) {
20505
+ return {
20506
+ agent: standardAgent.name,
20507
+ displayName: standardAgent.profile.displayName || standardAgent.name,
20508
+ role: standardAgent.profile.role,
20509
+ score: standardAgent.score,
20510
+ confidence: "low",
20511
+ rationale: ["No strong match found, using general-purpose agent"],
20512
+ alternatives: scoredAgents.filter((a) => a.name !== FALLBACK_AGENT).slice(0, 3).map((a) => ({ agent: a.name, score: a.score })),
20513
+ usedFallback: true
20514
+ };
20515
+ }
20516
+ return {
20517
+ agent: best.name,
20518
+ displayName: best.profile.displayName || best.name,
20519
+ role: best.profile.role,
20520
+ score: best.score,
20521
+ confidence: "low",
20522
+ rationale: ["Low confidence match - consider specifying agent explicitly"],
20523
+ alternatives,
20524
+ usedFallback: false
20525
+ };
20526
+ }
20527
+ const rationale = buildRationale(task, best.profile);
20528
+ const confidence = getConfidence(best.score);
20529
+ logger.info("[AgentSelector] Agent selected", {
20530
+ agent: best.name,
20531
+ score: best.score,
20532
+ confidence,
20533
+ rationale
20534
+ });
20535
+ return {
20536
+ agent: best.name,
20537
+ displayName: best.profile.displayName || best.name,
20538
+ role: best.profile.role,
20539
+ score: best.score,
20540
+ confidence,
20541
+ rationale,
20542
+ alternatives,
20543
+ usedFallback: false
20544
+ };
20545
+ }
20546
+ /**
20547
+ * Create a fallback result when no agents are available
20548
+ */
20549
+ async createFallbackResult(_task) {
20550
+ try {
20551
+ const profile = await this.profileLoader.loadProfile(FALLBACK_AGENT);
20552
+ return {
20553
+ agent: FALLBACK_AGENT,
20554
+ displayName: profile.displayName || FALLBACK_AGENT,
20555
+ role: profile.role,
20556
+ score: 0,
20557
+ confidence: "low",
20558
+ rationale: ["No agents available, using fallback"],
20559
+ alternatives: [],
20560
+ usedFallback: true
20561
+ };
20562
+ } catch {
20563
+ return {
20564
+ agent: FALLBACK_AGENT,
20565
+ displayName: "Standard",
20566
+ role: "General-purpose agent",
20567
+ score: 0,
20568
+ confidence: "low",
20569
+ rationale: ["No agents available"],
20570
+ alternatives: [],
20571
+ usedFallback: true
20572
+ };
20573
+ }
20574
+ }
20575
+ };
20576
+
20351
20577
  // src/mcp/tools/run-agent.ts
20352
20578
  init_logger();
20353
20579
 
@@ -20647,22 +20873,41 @@ async function buildAgentContext(agentName, task, deps, callerProvider, bestProv
20647
20873
  }
20648
20874
  function createRunAgentHandler(deps) {
20649
20875
  return async (input, context) => {
20650
- const { agent, task, provider, no_memory, mode = "auto" } = input;
20876
+ const { task, provider, no_memory, mode = "auto" } = input;
20877
+ let { agent } = input;
20651
20878
  if (context?.signal?.aborted) {
20652
20879
  throw new Error("Request was cancelled");
20653
20880
  }
20654
- validateAgentName(agent);
20655
20881
  validateStringParameter(task, "task", {
20656
20882
  required: true,
20657
20883
  minLength: 1,
20658
20884
  maxLength: 1e4
20659
20885
  });
20886
+ let autoSelected = false;
20887
+ if (!agent && deps.profileLoader) {
20888
+ const selector = new AgentSelector(deps.profileLoader);
20889
+ const selection = await selector.selectAgent(task);
20890
+ agent = selection.agent;
20891
+ autoSelected = true;
20892
+ logger.info("[MCP] run_agent auto-selected agent", {
20893
+ task: task.substring(0, 100),
20894
+ selectedAgent: agent,
20895
+ confidence: selection.confidence,
20896
+ score: selection.score,
20897
+ rationale: selection.rationale
20898
+ });
20899
+ }
20900
+ if (!agent) {
20901
+ throw new Error("Agent name is required when profileLoader is not available");
20902
+ }
20903
+ validateAgentName(agent);
20660
20904
  const actualProvider = mapMcpProviderToActual(provider);
20661
20905
  const session = deps.getSession?.() || null;
20662
20906
  const callerProvider = session?.normalizedProvider || "unknown";
20663
20907
  const callerActual = mapNormalizedCallerToActual(callerProvider);
20664
20908
  logger.info("[MCP] run_agent called (Smart Routing v10.5.0)", {
20665
20909
  agent,
20910
+ autoSelected,
20666
20911
  task: task.substring(0, 100),
20667
20912
  mcpProvider: provider,
20668
20913
  actualProvider,
@@ -21475,20 +21720,35 @@ init_logger();
21475
21720
  function createGetAgentContextHandler(deps) {
21476
21721
  return async (input) => {
21477
21722
  const {
21478
- agent,
21479
21723
  task,
21480
21724
  includeMemory = true,
21481
21725
  maxMemoryResults = 5
21482
21726
  } = input;
21727
+ let { agent } = input;
21483
21728
  const startTime = Date.now();
21484
- validateAgentName(agent);
21485
21729
  validateStringParameter(task, "task", {
21486
21730
  required: true,
21487
21731
  minLength: 1,
21488
21732
  maxLength: 1e4
21489
21733
  });
21734
+ let autoSelected = false;
21735
+ if (!agent) {
21736
+ const selector = new AgentSelector(deps.profileLoader);
21737
+ const selection = await selector.selectAgent(task);
21738
+ agent = selection.agent;
21739
+ autoSelected = true;
21740
+ logger.info("[MCP] get_agent_context auto-selected agent", {
21741
+ task: task.substring(0, 100),
21742
+ selectedAgent: agent,
21743
+ confidence: selection.confidence,
21744
+ score: selection.score,
21745
+ rationale: selection.rationale
21746
+ });
21747
+ }
21748
+ validateAgentName(agent);
21490
21749
  logger.info("[MCP] get_agent_context called", {
21491
21750
  agent,
21751
+ autoSelected,
21492
21752
  task: task.substring(0, 100),
21493
21753
  includeMemory,
21494
21754
  maxMemoryResults
@@ -26039,17 +26299,24 @@ var McpServer = class _McpServer {
26039
26299
  return [
26040
26300
  {
26041
26301
  name: "run_agent",
26042
- description: "Execute an AutomatosX agent with a specific task. Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.",
26302
+ description: `Execute an AutomatosX agent with a specific task.
26303
+
26304
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.
26305
+ Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.
26306
+
26307
+ Examples:
26308
+ - With agent: run_agent({ agent: "backend", task: "implement API" })
26309
+ - Auto-select: run_agent({ task: "fix bugs in the codebase" }) \u2192 selects "quality" agent`,
26043
26310
  inputSchema: {
26044
26311
  type: "object",
26045
26312
  properties: {
26046
- agent: { type: "string", description: "The name of the agent to run (e.g., backend, Paris, Bob)" },
26313
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
26047
26314
  task: { type: "string", description: "The task for the agent to perform" },
26048
26315
  provider: { type: "string", description: "Optional: Override the AI provider", enum: ["claude", "gemini", "openai"] },
26049
26316
  no_memory: { type: "boolean", description: "Optional: Skip memory injection", default: false },
26050
26317
  mode: { type: "string", description: "Optional: Execution mode - auto (default), context (always return context), execute (always spawn)", enum: ["auto", "context", "execute"], default: "auto" }
26051
26318
  },
26052
- required: ["agent", "task"]
26319
+ required: ["task"]
26053
26320
  }
26054
26321
  },
26055
26322
  {
@@ -26161,16 +26428,18 @@ Use this tool first to understand what AutomatosX offers.`,
26161
26428
  // v10.5.0: Smart Routing - Explicit context retrieval
26162
26429
  {
26163
26430
  name: "get_agent_context",
26164
- description: "Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.",
26431
+ description: `Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.
26432
+
26433
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.`,
26165
26434
  inputSchema: {
26166
26435
  type: "object",
26167
26436
  properties: {
26168
- agent: { type: "string", description: "The name of the agent (e.g., backend, Paris, Bob)" },
26437
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
26169
26438
  task: { type: "string", description: "The task description for context building" },
26170
26439
  includeMemory: { type: "boolean", description: "Include relevant memory entries (default: true)", default: true },
26171
26440
  maxMemoryResults: { type: "number", description: "Maximum memory entries to return (default: 5)", default: 5 }
26172
26441
  },
26173
- required: ["agent", "task"]
26442
+ required: ["task"]
26174
26443
  }
26175
26444
  },
26176
26445
  // v11.3.5: Task Engine tools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defai.digital/automatosx",
3
- "version": "12.5.0",
3
+ "version": "12.5.1",
4
4
  "description": "Provider-agnostic AI orchestration platform with 20+ specialized agents, persistent memory, and multi-provider routing for Claude Code, Gemini CLI, Codex CLI, GLM, and Grok",
5
5
  "type": "module",
6
6
  "publishConfig": {