ft-scout 9.0.7 → 9.0.8

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.
Files changed (75) hide show
  1. package/.firebase/hosting.d2Vi.cache +8 -8
  2. package/AUTONOMOUS_FEATURES.md +1 -1
  3. package/README.md +113 -16
  4. package/bin/src/commands/agent.js +2 -2
  5. package/bin/src/commands/agent.js.map +1 -1
  6. package/bin/src/commands/audit.js +1 -1
  7. package/bin/src/commands/audit.js.map +1 -1
  8. package/bin/src/commands/dashboard.js +1 -1
  9. package/bin/src/commands/dashboard.js.map +1 -1
  10. package/bin/src/commands/mcp.d.ts.map +1 -0
  11. package/bin/src/commands/mcp.js +393 -0
  12. package/bin/src/commands/mcp.js.map +1 -0
  13. package/bin/src/commands/owners.js +1 -1
  14. package/bin/src/commands/owners.js.map +1 -1
  15. package/bin/src/discovery/messages.js +2 -2
  16. package/bin/src/discovery/messages.js.map +1 -1
  17. package/bin/src/discovery/signals.js +3 -3
  18. package/bin/src/discovery/signals.js.map +1 -1
  19. package/bin/src/engine/agentEngine.d.ts.map +1 -1
  20. package/bin/src/engine/agentEngine.js +53 -11
  21. package/bin/src/engine/agentEngine.js.map +1 -1
  22. package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -1
  23. package/bin/src/engine/runtime/agentRuntime.js +40 -3
  24. package/bin/src/engine/runtime/agentRuntime.js.map +1 -1
  25. package/bin/src/engine/toolRegistry.d.ts.map +1 -1
  26. package/bin/src/engine/toolRegistry.js +13 -0
  27. package/bin/src/engine/toolRegistry.js.map +1 -1
  28. package/bin/src/engine/verifier.js +2 -2
  29. package/bin/src/engine/verifier.js.map +1 -1
  30. package/bin/src/index.js +85 -7
  31. package/bin/src/index.js.map +1 -1
  32. package/bin/src/mcp/client.d.ts.map +1 -0
  33. package/bin/src/mcp/client.js +127 -0
  34. package/bin/src/mcp/client.js.map +1 -0
  35. package/bin/src/mcp/discovery.d.ts.map +1 -0
  36. package/bin/src/mcp/discovery.js +91 -0
  37. package/bin/src/mcp/discovery.js.map +1 -0
  38. package/bin/src/mcp/health.d.ts.map +1 -0
  39. package/bin/src/mcp/health.js +44 -0
  40. package/bin/src/mcp/health.js.map +1 -0
  41. package/bin/src/mcp/index.d.ts.map +1 -0
  42. package/bin/src/mcp/index.js +15 -0
  43. package/bin/src/mcp/index.js.map +1 -0
  44. package/bin/src/mcp/manager.d.ts.map +1 -0
  45. package/bin/src/mcp/manager.js +535 -0
  46. package/bin/src/mcp/manager.js.map +1 -0
  47. package/bin/src/mcp/permissions.d.ts.map +1 -0
  48. package/bin/src/mcp/permissions.js +50 -0
  49. package/bin/src/mcp/permissions.js.map +1 -0
  50. package/bin/src/mcp/persistence.d.ts.map +1 -0
  51. package/bin/src/mcp/persistence.js +156 -0
  52. package/bin/src/mcp/persistence.js.map +1 -0
  53. package/bin/src/mcp/recovery.d.ts.map +1 -0
  54. package/bin/src/mcp/recovery.js +75 -0
  55. package/bin/src/mcp/recovery.js.map +1 -0
  56. package/bin/src/mcp/telemetry.d.ts.map +1 -0
  57. package/bin/src/mcp/telemetry.js +82 -0
  58. package/bin/src/mcp/telemetry.js.map +1 -0
  59. package/bin/src/mcp/toolRouter.d.ts.map +1 -0
  60. package/bin/src/mcp/toolRouter.js +127 -0
  61. package/bin/src/mcp/toolRouter.js.map +1 -0
  62. package/bin/src/mcp/transport.d.ts.map +1 -0
  63. package/bin/src/mcp/transport.js +328 -0
  64. package/bin/src/mcp/transport.js.map +1 -0
  65. package/bin/src/mcp/types.d.ts.map +1 -0
  66. package/bin/src/mcp/types.js +6 -0
  67. package/bin/src/mcp/types.js.map +1 -0
  68. package/bin/src/server/server.d.ts.map +1 -1
  69. package/bin/src/server/server.js +207 -0
  70. package/bin/src/server/server.js.map +1 -1
  71. package/bin/src/utils/branding.js +1 -1
  72. package/bin/src/utils/email.js +4 -4
  73. package/package.json +1 -1
  74. package/web/app.js +1255 -59
  75. package/web/styles.css +1474 -15
@@ -20,6 +20,7 @@ import { SystemComputerController, MockComputerController } from './computer/ind
20
20
  import { AgentWorkspace, inferExecutionIntent, resolveDesktopPath } from './workspace/index.js';
21
21
  import { AgentRuntime } from './runtime/index.js';
22
22
  import { loadAuthConfig, isProOrHigher } from '../utils/auth.js';
23
+ import { getMCPManager } from '../mcp/index.js';
23
24
  export { AgentRuntime, AgentWorkspace, inferExecutionIntent, resolveDesktopPath, SystemComputerController, MockComputerController };
24
25
  const execAsync = promisify(exec);
25
26
  export function robustSnippetReplace(origContent, target, replacement) {
@@ -157,7 +158,7 @@ export const AGENT_TOOLS = [
157
158
  },
158
159
  {
159
160
  name: 'run_command',
160
- description: 'Execute a terminal/shell command in the workspace (e.g. npm test, npm run build, git status). WARNING: Do NOT use run_command to create files or folders (e.g. mkdir, touch, echo >)—call write_file directly instead!',
161
+ description: 'Execute a terminal/shell command in the workspace (e.g. npm test, npm run build, git status). WARNING: Do NOT use run_command to create files or folders (e.g. mkdir, touch, echo >)call write_file directly instead!',
161
162
  parameters: {
162
163
  type: 'object',
163
164
  properties: {
@@ -270,7 +271,7 @@ export const AGENT_TOOLS = [
270
271
  },
271
272
  {
272
273
  name: 'ask_user',
273
- description: 'ONLY use for missing API keys or secret credentials. DO NOT use to ask the user what code to write or to re-confirm their prompt—take immediate autonomous tool action instead.',
274
+ description: 'ONLY use for missing API keys or secret credentials. DO NOT use to ask the user what code to write or to re-confirm their prompttake immediate autonomous tool action instead.',
274
275
  parameters: {
275
276
  type: 'object',
276
277
  properties: {
@@ -374,6 +375,7 @@ export class AgentExecutionLoop {
374
375
  cancelReason;
375
376
  executionIntent;
376
377
  userTier;
378
+ currentGoal = '';
377
379
  constructor(options) {
378
380
  this.cwd = process.cwd();
379
381
  this.autoApprove = Boolean(options?.autoApprove);
@@ -582,6 +584,8 @@ PLAN TIER & CAPABILITIES: DEVELOPER FREE (BASIC DEVELOPER OPS ONLY)
582
584
  4. Reading & exploring: use read_file, list_dir, grep_search to inspect files.
583
585
  • Do NOT attempt to use scratchpad, computer, or browser tools. Execute the requested file edit, file creation, or command directly and call task_completed immediately.
584
586
  `;
587
+ const mcpRouting = getMCPManager().routeTools(this.currentGoal);
588
+ const mcpCapabilitiesSection = mcpRouting.capabilitiesSummary ? `\n${mcpRouting.capabilitiesSummary}\n` : '';
585
589
  return `You are Scout Agent, an elite AI Principal Software Engineer & Autonomous Computer-Use Operator (developed by FrontTerrain).
586
590
 
587
591
  Working Directory: "${this.cwd}"
@@ -589,6 +593,7 @@ Host OS Platform: "${process.platform}"
589
593
  Current Date & Time: "${new Date().toISOString()}" (Current Year: ${new Date().getFullYear()})
590
594
  ${tierDirective}
591
595
  ${intentInfo}
596
+ ${mcpCapabilitiesSection}
592
597
  Workspace Overview (first 100 files):
593
598
  ${filesList.join('\n')}
594
599
 
@@ -631,6 +636,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
631
636
  }
632
637
  async executeGoal(userGoal) {
633
638
  this.resetContext();
639
+ this.currentGoal = userGoal;
634
640
  this.executionIntent = inferExecutionIntent(userGoal);
635
641
  this.taskStateManager.setObjective(userGoal);
636
642
  if (this.computerController.setExpectedApplication && this.executionIntent.requiredApplications.length > 0) {
@@ -638,7 +644,9 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
638
644
  }
639
645
  const structuredPlan = this.taskPlanner.createPlan(userGoal);
640
646
  this.taskStateManager.setPlan(structuredPlan.steps);
641
- this.taskStateManager.setAvailableTools(this.toolRegistry.getToolsForTier(this.userTier).map((t) => t.name));
647
+ const mcpRouted = getMCPManager().routeTools(userGoal);
648
+ const mcpNames = mcpRouted.tools.map((t) => t.originalName);
649
+ this.taskStateManager.setAvailableTools([...this.toolRegistry.getToolsForTier(this.userTier).map((t) => t.name), ...mcpNames]);
642
650
  if (this.mockMode) {
643
651
  for (let i = 1; i <= structuredPlan.steps.length; i++) {
644
652
  this.taskStateManager.startStep(i);
@@ -791,12 +799,26 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
791
799
  stepSpinner.start(chalk.cyan(`Scout is Working (Step ${step}/${this.maxSteps} • Est. completion: ~${estLeftStr} left)`));
792
800
  try {
793
801
  let response;
802
+ const mcpRouting = getMCPManager().routeTools(userGoal);
803
+ const mcpToolDefs = mcpRouting.tools.map((mt) => ({
804
+ name: mt.originalName,
805
+ description: `[MCP: ${mt.serverName}] ${mt.description}`,
806
+ parameters: {
807
+ type: 'object',
808
+ properties: mt.inputSchema.properties || {},
809
+ required: mt.inputSchema.required || [],
810
+ },
811
+ source: 'mcp',
812
+ mcpServerId: mt.serverId,
813
+ mcpServerName: mt.serverName,
814
+ }));
815
+ const activeLoopTools = [...this.toolRegistry.getToolsForTier(this.userTier), ...mcpToolDefs];
794
816
  try {
795
817
  response = await callOpenAIWithRetry(async (model) => {
796
818
  return await openai.chat.completions.create({
797
819
  model,
798
820
  messages: sanitizeMessages(this.historyMessages),
799
- tools: this.toolRegistry.getToolsForTier(this.userTier).map((t) => ({ type: 'function', function: t })),
821
+ tools: activeLoopTools.map((t) => ({ type: 'function', function: t })),
800
822
  tool_choice: 'auto',
801
823
  temperature: 0.1,
802
824
  presence_penalty: 0.1,
@@ -807,7 +829,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
807
829
  catch (err) {
808
830
  const errStr = String(err?.message || err?.error || err || '').toLowerCase();
809
831
  if (errStr.includes('tool') || errStr.includes('400') || errStr.includes('not supported') || errStr.includes('reasoning')) {
810
- // Model doesn't support native function calling — inject tool-call formatting hint
832
+ // Model doesn't support native function calling inject tool-call formatting hint
811
833
  // so extractJsonToolCall can parse the response as a structured tool invocation
812
834
  const toolHintMsg = {
813
835
  role: 'user',
@@ -816,7 +838,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
816
838
  { "tool": "tool_name", "args": { ... } }
817
839
  \`\`\`
818
840
  Available tools: read_file, write_file, edit_file, run_command, list_dir, grep_search, glob_search, tree_view, file_info, multi_edit_file, fetch_url, git_diff, ask_user, speak_text, task_completed.
819
- You MUST output exactly ONE JSON code block per tool call. Do NOT describe what you would do in plain text—output the JSON tool call directly!`,
841
+ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what you would do in plain textoutput the JSON tool call directly!`,
820
842
  };
821
843
  const fallbackMessages = [...this.historyMessages, toolHintMsg];
822
844
  response = await callOpenAIWithRetry(async (model) => {
@@ -1423,13 +1445,13 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
1423
1445
  checkSpin.start(chalk.cyan(` Checking for error: edited ${targetPath}...`));
1424
1446
  const syntaxCheck = await checkFileSyntax(targetPath, this.cwd);
1425
1447
  if (!syntaxCheck.valid) {
1426
- checkSpin.stop(chalk.red(`️ Checking for error: edited ${targetPath} — Compiler/Syntax Error!`));
1448
+ checkSpin.stop(chalk.red(`️ Checking for error: edited ${targetPath} Compiler/Syntax Error!`));
1427
1449
  this.taskStateManager.recordError(`Syntax error in "${targetPath}": ${syntaxCheck.errors.join('; ')}`, 'write_file');
1428
1450
  return {
1429
1451
  result: `Successfully wrote ${content.length} characters (${numberOfLinesChanged} lines changed) to "${targetPath}".\n\n️ AUTOMATED SYNTAX / COMPILER ERROR DETECTED IN "${targetPath}":\n${syntaxCheck.errors.join('\n')}\n\nPLEASE IMMEDIATELY FIX THIS SYNTAX ERROR IN YOUR NEXT TOOL CALL.`
1430
1452
  };
1431
1453
  }
1432
- checkSpin.stop(chalk.green(` Checking for error: edited ${targetPath} — Syntax & build clean.`));
1454
+ checkSpin.stop(chalk.green(` Checking for error: edited ${targetPath} Syntax & build clean.`));
1433
1455
  this.taskStateManager.recordArtifact({
1434
1456
  filename: path.basename(targetPath),
1435
1457
  path: targetPath,
@@ -1489,12 +1511,12 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
1489
1511
  checkSpin.start(chalk.cyan(` Checking for error: edited ${targetPath}...`));
1490
1512
  const syntaxCheck = await checkFileSyntax(targetPath, this.cwd);
1491
1513
  if (!syntaxCheck.valid) {
1492
- checkSpin.stop(chalk.red(`️ Checking for error: edited ${targetPath} — Compiler/Syntax Error!`));
1514
+ checkSpin.stop(chalk.red(`️ Checking for error: edited ${targetPath} Compiler/Syntax Error!`));
1493
1515
  return {
1494
1516
  result: `Successfully replaced snippet (${numberOfLinesChanged} lines changed) in "${targetPath}".\n\n️ AUTOMATED SYNTAX / COMPILER ERROR DETECTED IN "${targetPath}":\n${syntaxCheck.errors.join('\n')}\n\nPLEASE IMMEDIATELY FIX THIS SYNTAX ERROR IN YOUR NEXT TOOL CALL.`
1495
1517
  };
1496
1518
  }
1497
- checkSpin.stop(chalk.green(` Checking for error: edited ${targetPath} — Syntax & build clean.`));
1519
+ checkSpin.stop(chalk.green(` Checking for error: edited ${targetPath} Syntax & build clean.`));
1498
1520
  return { result: `Successfully replaced target snippet (${numberOfLinesChanged} lines changed) in "${targetPath}". Rationale: ${reason}. Syntax check passed cleanly.` };
1499
1521
  }
1500
1522
  case 'run_command': {
@@ -2132,8 +2154,28 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
2132
2154
  this.taskStateManager.completeStep(this.currentStep, summaryStr);
2133
2155
  return { result: summaryStr };
2134
2156
  }
2135
- default:
2157
+ default: {
2158
+ // Check if this tool is provided by a connected MCP server
2159
+ const mcpManager = getMCPManager();
2160
+ const mcpTarget = mcpManager.findTool(name);
2161
+ if (mcpTarget) {
2162
+ const mcpRes = await mcpManager.executeTool(name, args);
2163
+ const isSuccess = mcpRes.status === 'success';
2164
+ const outputStr = typeof mcpRes.result === 'string'
2165
+ ? mcpRes.result
2166
+ : (mcpRes.result?.text ? String(mcpRes.result.text) : JSON.stringify(mcpRes.result ?? { status: mcpRes.status, error: mcpRes.error }));
2167
+ this.taskStateManager.addObservation(name, mcpRes.outputSummary || outputStr, isSuccess);
2168
+ // Check if MCP result generated or referenced local files/artifacts
2169
+ if (isSuccess && typeof mcpRes.result === 'object' && mcpRes.result !== null) {
2170
+ const possiblePath = mcpRes.result.path || mcpRes.result.filePath || mcpRes.result.file || args.destPath || args.path || args.outputPath;
2171
+ if (typeof possiblePath === 'string' && fs.existsSync(path.resolve(this.cwd, possiblePath))) {
2172
+ this.modifiedFiles.add(path.relative(this.cwd, path.resolve(this.cwd, possiblePath)).replace(/\\/g, '/'));
2173
+ }
2174
+ }
2175
+ return { result: outputStr };
2176
+ }
2136
2177
  return { result: `Unknown tool call name: "${name}"` };
2178
+ }
2137
2179
  }
2138
2180
  }
2139
2181
  }