ft-scout 9.0.8 → 9.0.9

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.
@@ -9,7 +9,7 @@ import { saveAgentSession, getRecentSessionsSummary } from '../utils/session.js'
9
9
  import { safeNote, renderMarkdown } from '../utils/markdown.js';
10
10
  import { openai, callOpenAIWithRetry, isQuotaExceededError, sanitizeMessage, sanitizeMessages } from './llm.js';
11
11
  import { checkFileSyntax, verifyAndSelfHealFiles, executeSmartCommand, extractErrorDiagnostics } from './verifier.js';
12
- import { speakText, listenSpeechToText } from './voiceEngine.js';
12
+ import { speakText, listenSpeechToText, cleanTextForSpeech } from './voiceEngine.js';
13
13
  import { TaskStateManager } from './agentState.js';
14
14
  import { SemanticBrowserController } from './browser/index.js';
15
15
  import { ActionVerifier } from './actionVerifier.js';
@@ -293,11 +293,14 @@ export const AGENT_TOOLS = [
293
293
  },
294
294
  {
295
295
  name: 'task_completed',
296
- description: 'Signal that the user goal has been fully completed with a final teardown summary.',
296
+ description: 'Signal that the user goal has been fully completed with the full answer, results, and summary.',
297
297
  parameters: {
298
298
  type: 'object',
299
299
  properties: {
300
- summary: { type: 'string', description: 'Comprehensive teardown report of completed changes' },
300
+ summary: {
301
+ type: 'string',
302
+ description: 'The complete answer, findings, and results delivered directly to the user. For lookups or questions, this MUST include all retrieved details, accounts, URLs, or data.',
303
+ },
301
304
  },
302
305
  required: ['summary'],
303
306
  },
@@ -376,10 +379,12 @@ export class AgentExecutionLoop {
376
379
  executionIntent;
377
380
  userTier;
378
381
  currentGoal = '';
382
+ isAudioMode = false;
379
383
  constructor(options) {
380
384
  this.cwd = process.cwd();
381
385
  this.autoApprove = Boolean(options?.autoApprove);
382
386
  this.mockMode = Boolean(options?.mockMode);
387
+ this.isAudioMode = Boolean(options?.isAudioMode);
383
388
  this.userTier = (options?.tier || loadAuthConfig().currentTier || 'free').toLowerCase();
384
389
  // Free tier is bounded to basic direct actions (max 5 steps). Full autonomous multi-step loops require Pro/Enterprise.
385
390
  const isPaid = this.mockMode || this.userTier === 'pro' || this.userTier === 'enterprise';
@@ -586,6 +591,8 @@ PLAN TIER & CAPABILITIES: DEVELOPER FREE (BASIC DEVELOPER OPS ONLY)
586
591
  `;
587
592
  const mcpRouting = getMCPManager().routeTools(this.currentGoal);
588
593
  const mcpCapabilitiesSection = mcpRouting.capabilitiesSummary ? `\n${mcpRouting.capabilitiesSummary}\n` : '';
594
+ const recentSessionsMemory = getRecentSessionsSummary(3);
595
+ const sessionHistorySection = recentSessionsMemory ? `\n${recentSessionsMemory}\n` : '';
589
596
  return `You are Scout Agent, an elite AI Principal Software Engineer & Autonomous Computer-Use Operator (developed by FrontTerrain).
590
597
 
591
598
  Working Directory: "${this.cwd}"
@@ -594,6 +601,7 @@ Current Date & Time: "${new Date().toISOString()}" (Current Year: ${new Date().g
594
601
  ${tierDirective}
595
602
  ${intentInfo}
596
603
  ${mcpCapabilitiesSection}
604
+ ${sessionHistorySection}
597
605
  Workspace Overview (first 100 files):
598
606
  ${filesList.join('\n')}
599
607
 
@@ -620,10 +628,18 @@ CORE ARCHITECTURAL PRINCIPLES:
620
628
  6. MULTILINGUAL & INTENT COMPREHENSION: Understand user intent in any language (English, Hinglish like "likho", "bnao", "code karo", "fix karo", "samjha do", Hindi, Spanish, etc.).
621
629
  7. FOR CODE & REPO TASKS: When the task is a coding, project refactoring, or repository maintenance task, use direct code modification tools (write_file, edit_file, run_command, read_file, grep_search).
622
630
  8. RUN & VERIFY COMMANDS: Execute shell commands, tests, builds, and formatters using run_command.
623
- 9. CRITICAL TERMINATION & CONVERSATIONAL VOICE RULE: As soon as you have finished creating/modifying the necessary files, running verification checks, or completing the desktop application workflow, YOU MUST CALL task_completed tool immediately.
624
- - CONVERSATIONAL CO-PILOT TONE: In your 'summary' parameter for task_completed, speak directly and warmly to the developer like an elite human co-pilot (e.g. "I've launched Chrome for you and it's active on your screen. What should we tackle next?" or "I've created the authentication controller and all unit tests pass with zero regressions!"). Never output dry mechanical robot logs or third-person phrases like "Objective achieved with 1 tool call". Talk directly to the user!
631
+ 9. CRITICAL TERMINATION, RESULTS DELIVERY & CONVERSATIONAL VOICE RULE:
632
+ - For ANY lookup, question, status check, external data retrieval, or inquiry (e.g. "Lookup up my github account", "What is X", "Check my PRs"):
633
+ Your 'summary' argument in task_completed (or your direct response) MUST contain the COMPLETE, ACCURATE, AND DETAILED ANSWER with all specific data found (usernames, repository names, statistics, URLs, findings, or explanations).
634
+ NEVER output a vacuous meta-summary like "Task completed" or "Looked up account" without providing the actual information the user asked for!
635
+ - For coding/modification tasks: Detail what was changed, created, and tested.
636
+ - CONVERSATIONAL CO-PILOT TONE: In your 'summary' parameter for task_completed, speak directly and warmly to the developer like an elite human co-pilot (e.g. "Here are your GitHub account details: your username is ..."). Never output dry mechanical robot logs or third-person phrases like "Objective achieved with 1 tool call". Talk directly to the user!
625
637
  10. THOUGHT TRANSPARENCY PROTOCOL: In EVERY step before calling any tools or ending a turn, you MUST provide a clear 1-2 sentence explanation in your text response describing your current reasoning, what file/action you are taking, and why you are taking it.
626
638
  11. APPLICATION LAUNCH & SIMPLE ACTION TERMINATION: When the user's objective is simply to open, launch, close, or focus an application (e.g. "Open Chrome", "Launch Notepad", "Open Calculator"): Immediately after calling computer_open_app or computer_close_app, your goal is 100% complete! You MUST call task_completed on the very next step with your friendly conversational summary. NEVER explore the repository or call list_dir, read_file, grep_search, or edit_file for app launching tasks!
639
+ 12. STRICT ACTIVE GOAL ISOLATION & ACCURACY:
640
+ - You must strictly execute ONLY the current active goal provided in the user prompt.
641
+ - Historical workspace sessions are provided for background context ONLY. NEVER re-run, repeat, or resume past session tasks unless the user explicitly requests it in the current prompt.
642
+ - For lookups, questions, status checks, or external service queries (e.g. GitHub account, MCP tools, web): deliver the concrete results directly and do NOT attempt to edit or rewrite unrelated workspace files.
627
643
 
628
644
  When returning tool calls, use standard OpenAI function calling format or JSON tool call payload format:
629
645
  \`\`\`json
@@ -637,6 +653,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
637
653
  async executeGoal(userGoal) {
638
654
  this.resetContext();
639
655
  this.currentGoal = userGoal;
656
+ this.taskStateManager.createTaskState(userGoal);
640
657
  this.executionIntent = inferExecutionIntent(userGoal);
641
658
  this.taskStateManager.setObjective(userGoal);
642
659
  if (this.computerController.setExpectedApplication && this.executionIntent.requiredApplications.length > 0) {
@@ -659,19 +676,14 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
659
676
  };
660
677
  }
661
678
  this.historyMessages.push({ role: 'system', content: this.systemPrompt() });
662
- // Load persistent session history memory (minimum 3 sessions Context)
663
- const recentSessionsMemory = getRecentSessionsSummary(3);
664
- if (recentSessionsMemory) {
665
- this.historyMessages.push({
666
- role: 'user',
667
- content: recentSessionsMemory,
668
- });
669
- }
670
- // Robust Target File Discovery: Only run file discovery if the goal is NOT a simple desktop application launch/action
679
+ // Robust Target File Discovery: Only run file discovery if the goal has explicit code editing/modification intent
671
680
  const simpleAppCheck = checkSimpleAppAction(userGoal);
681
+ const lowerGoal = userGoal.toLowerCase().trim();
682
+ const hasCodeEditIntent = /\b(edit|modify|fix|update|change|refactor|write|create|implement|patch|delete|remove|add\s+code|add\s+function|debug)\b/i.test(lowerGoal) &&
683
+ !/\b(lookup|look\s*up|who|what|why|where|how|explain|describe|tell|status|check|github|mcp)\b/i.test(lowerGoal);
672
684
  const filesList = getDirectoryFiles(this.cwd);
673
685
  const matchedFiles = [];
674
- if (!simpleAppCheck.isSimple) {
686
+ if (hasCodeEditIntent && !simpleAppCheck.isSimple) {
675
687
  const isIgnoredFile = (filePath) => {
676
688
  const norm = filePath.replace(/\\/g, '/').toLowerCase();
677
689
  const ext = path.extname(norm);
@@ -709,7 +721,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
709
721
  }
710
722
  }
711
723
  }
712
- // Keyword fallback if no direct path matched
724
+ // Keyword fallback only if explicit path wasn't matched and code edit intent exists
713
725
  if (matchedFiles.length === 0) {
714
726
  const stopWords = new Set([
715
727
  'the', 'a', 'an', 'in', 'is', 'for', 'to', 'of', 'and', 'or', 'on', 'at', 'by', 'from',
@@ -755,7 +767,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
755
767
  this.readFilesHistory.add(file.replace(/\\/g, '/').toLowerCase());
756
768
  this.historyMessages.push({
757
769
  role: 'user',
758
- content: `[Reference Workspace Context - Pre-loaded target file "${file}" (${lines.length} lines)]:\n\`\`\`\n${previewSnippet}\n\`\`\`\nCRITICAL DIRECTIVE: The target file "${file}" is ALREADY fully pre-loaded into your reference context above. DO NOT call read_file or list_dir for this file again! Immediately begin calling write_file, edit_file, or run_command to satisfy the user goal.`,
770
+ content: `[Reference Workspace Context - Pre-loaded target file "${file}" (${lines.length} lines)]:\n\`\`\`\n${previewSnippet}\n\`\`\`\n(Note: "${file}" is pre-loaded above for your reference. Use this context if relevant to completing the user goal.)`,
759
771
  });
760
772
  }
761
773
  catch {
@@ -767,7 +779,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
767
779
  if (userGoal && userGoal.trim()) {
768
780
  this.historyMessages.push({
769
781
  role: 'user',
770
- content: userGoal,
782
+ content: `USER GOAL: "${userGoal.trim()}"\n\nPlease execute the necessary steps and tools to fulfill this exact goal now.`,
771
783
  });
772
784
  }
773
785
  const estTotalSecs = Math.round(this.maxSteps * 12);
@@ -866,6 +878,12 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
866
878
  const textContent = msg.content || '';
867
879
  if (textContent.trim()) {
868
880
  safeNote(renderMarkdown(textContent), `Scout Thought (Step ${step})`);
881
+ if (this.isAudioMode) {
882
+ const speechText = cleanTextForSpeech(textContent);
883
+ if (speechText) {
884
+ speakText(speechText.length > 200 ? speechText.slice(0, 200) + '...' : speechText, { async: true });
885
+ }
886
+ }
869
887
  }
870
888
  // Check native tool calls
871
889
  if (msg.tool_calls && msg.tool_calls.length > 0) {
@@ -878,6 +896,21 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
878
896
  args = JSON.parse(tc.function.arguments || '{}');
879
897
  }
880
898
  catch { }
899
+ if (this.isAudioMode && fnName !== 'task_completed') {
900
+ let actionPhrase = '';
901
+ if (fnName === 'run_command')
902
+ actionPhrase = `Running command ${args.command ? args.command.slice(0, 40) : ''}`;
903
+ else if (fnName === 'read_file')
904
+ actionPhrase = `Reading ${path.basename(args.filePath || '')}`;
905
+ else if (fnName === 'write_file')
906
+ actionPhrase = `Creating ${path.basename(args.filePath || '')}`;
907
+ else if (fnName === 'edit_file')
908
+ actionPhrase = `Editing ${path.basename(args.filePath || '')}`;
909
+ else if (fnName.startsWith('mcp_') || fnName.includes('github'))
910
+ actionPhrase = `Querying ${fnName}`;
911
+ if (actionPhrase)
912
+ speakText(actionPhrase, { async: true });
913
+ }
881
914
  const toolResult = await this.dispatchToolCall(fnName, args);
882
915
  this.historyMessages.push({
883
916
  role: 'user',
@@ -998,8 +1031,13 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
998
1031
  lowerText.includes('file created') ||
999
1032
  lowerText.includes('implementation complete') ||
1000
1033
  lowerText.includes('work is complete');
1001
- // If explicit completion phrase found, OR files have already been modified and assistant returned a final summary without calling tools
1002
- if (isExplicitCompletion || (this.modifiedFiles.size > 0 && !lowerText.includes('?') && textContent.length > 50)) {
1034
+ const isInformationalGoal = /^(?:lookup|look\s*up|check|find|who|what|why|where|how|tell|show|explain|display|list|search|query|status|info|get|fetch|describe)\b/i.test(userGoal.trim()) ||
1035
+ !/\b(create|make|write|edit|modify|fix|update|refactor|build|delete|remove|patch|implement)\b/i.test(userGoal.trim());
1036
+ const isAnsweringInformational = isInformationalGoal && textContent.trim().length > 20 && !lowerText.includes('error occurred') && !lowerText.includes('cannot complete');
1037
+ const isSummaryAfterModifications = this.modifiedFiles.size > 0 && !lowerText.includes('?') && textContent.trim().length > 30;
1038
+ const isSummaryAfterToolExecution = this.totalToolCalls > 0 && textContent.trim().length > 30 && !lowerText.includes('calling tool') && !lowerText.includes('next step');
1039
+ // If explicit completion phrase found, OR informational answer provided, OR files were modified, OR tools were executed and assistant is delivering the summary
1040
+ if (isExplicitCompletion || isAnsweringInformational || isSummaryAfterModifications || isSummaryAfterToolExecution) {
1003
1041
  saveAgentSession({
1004
1042
  goal: userGoal,
1005
1043
  summary: textContent,
@@ -2149,7 +2187,6 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
2149
2187
  };
2150
2188
  }
2151
2189
  }
2152
- speakText(summaryStr, { async: true });
2153
2190
  this.taskStateManager.setStatus('completed');
2154
2191
  this.taskStateManager.completeStep(this.currentStep, summaryStr);
2155
2192
  return { result: summaryStr };