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.
- package/.firebase/hosting.d2Vi.cache +8 -8
- package/AUTONOMOUS_FEATURES.md +1 -1
- package/README.md +113 -16
- package/bin/src/commands/agent.js +2 -2
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/commands/audit.js +1 -1
- package/bin/src/commands/audit.js.map +1 -1
- package/bin/src/commands/dashboard.js +1 -1
- package/bin/src/commands/dashboard.js.map +1 -1
- package/bin/src/commands/mcp.d.ts.map +1 -0
- package/bin/src/commands/mcp.js +393 -0
- package/bin/src/commands/mcp.js.map +1 -0
- package/bin/src/commands/owners.js +1 -1
- package/bin/src/commands/owners.js.map +1 -1
- package/bin/src/discovery/messages.js +2 -2
- package/bin/src/discovery/messages.js.map +1 -1
- package/bin/src/discovery/signals.js +3 -3
- package/bin/src/discovery/signals.js.map +1 -1
- package/bin/src/engine/agentEngine.d.ts.map +1 -1
- package/bin/src/engine/agentEngine.js +53 -11
- package/bin/src/engine/agentEngine.js.map +1 -1
- package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -1
- package/bin/src/engine/runtime/agentRuntime.js +40 -3
- package/bin/src/engine/runtime/agentRuntime.js.map +1 -1
- package/bin/src/engine/toolRegistry.d.ts.map +1 -1
- package/bin/src/engine/toolRegistry.js +13 -0
- package/bin/src/engine/toolRegistry.js.map +1 -1
- package/bin/src/engine/verifier.js +2 -2
- package/bin/src/engine/verifier.js.map +1 -1
- package/bin/src/index.js +85 -7
- package/bin/src/index.js.map +1 -1
- package/bin/src/mcp/client.d.ts.map +1 -0
- package/bin/src/mcp/client.js +127 -0
- package/bin/src/mcp/client.js.map +1 -0
- package/bin/src/mcp/discovery.d.ts.map +1 -0
- package/bin/src/mcp/discovery.js +91 -0
- package/bin/src/mcp/discovery.js.map +1 -0
- package/bin/src/mcp/health.d.ts.map +1 -0
- package/bin/src/mcp/health.js +44 -0
- package/bin/src/mcp/health.js.map +1 -0
- package/bin/src/mcp/index.d.ts.map +1 -0
- package/bin/src/mcp/index.js +15 -0
- package/bin/src/mcp/index.js.map +1 -0
- package/bin/src/mcp/manager.d.ts.map +1 -0
- package/bin/src/mcp/manager.js +535 -0
- package/bin/src/mcp/manager.js.map +1 -0
- package/bin/src/mcp/permissions.d.ts.map +1 -0
- package/bin/src/mcp/permissions.js +50 -0
- package/bin/src/mcp/permissions.js.map +1 -0
- package/bin/src/mcp/persistence.d.ts.map +1 -0
- package/bin/src/mcp/persistence.js +156 -0
- package/bin/src/mcp/persistence.js.map +1 -0
- package/bin/src/mcp/recovery.d.ts.map +1 -0
- package/bin/src/mcp/recovery.js +75 -0
- package/bin/src/mcp/recovery.js.map +1 -0
- package/bin/src/mcp/telemetry.d.ts.map +1 -0
- package/bin/src/mcp/telemetry.js +82 -0
- package/bin/src/mcp/telemetry.js.map +1 -0
- package/bin/src/mcp/toolRouter.d.ts.map +1 -0
- package/bin/src/mcp/toolRouter.js +127 -0
- package/bin/src/mcp/toolRouter.js.map +1 -0
- package/bin/src/mcp/transport.d.ts.map +1 -0
- package/bin/src/mcp/transport.js +328 -0
- package/bin/src/mcp/transport.js.map +1 -0
- package/bin/src/mcp/types.d.ts.map +1 -0
- package/bin/src/mcp/types.js +6 -0
- package/bin/src/mcp/types.js.map +1 -0
- package/bin/src/server/server.d.ts.map +1 -1
- package/bin/src/server/server.js +207 -0
- package/bin/src/server/server.js.map +1 -1
- package/bin/src/utils/branding.js +1 -1
- package/bin/src/utils/email.js +4 -4
- package/package.json +1 -1
- package/web/app.js +1255 -59
- 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 >)
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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
|
}
|