ft-scout 9.0.6 → 9.0.7
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 +9 -9
- package/README.md +18 -8
- package/bin/src/commands/agent.d.ts.map +1 -1
- package/bin/src/commands/agent.js +43 -2
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/engine/agentEngine.d.ts.map +1 -1
- package/bin/src/engine/agentEngine.js +161 -82
- package/bin/src/engine/agentEngine.js.map +1 -1
- package/bin/src/engine/planner.d.ts.map +1 -1
- package/bin/src/engine/planner.js +81 -2
- package/bin/src/engine/planner.js.map +1 -1
- package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -1
- package/bin/src/engine/runtime/agentRuntime.js +39 -2
- package/bin/src/engine/runtime/agentRuntime.js.map +1 -1
- package/bin/src/engine/scoutAudioRecorder.cs +215 -0
- package/bin/src/engine/scout_audio_recorder.exe +0 -0
- package/bin/src/engine/voiceEngine.d.ts.map +1 -1
- package/bin/src/engine/voiceEngine.js +333 -13
- package/bin/src/engine/voiceEngine.js.map +1 -1
- package/bin/src/index.js +4 -4
- package/bin/src/utils/branding.js +1 -1
- package/package.json +1 -1
- package/web/app.js +2 -2
- package/web/styles.css +2 -2
|
@@ -14,7 +14,7 @@ import { TaskStateManager } from './agentState.js';
|
|
|
14
14
|
import { SemanticBrowserController } from './browser/index.js';
|
|
15
15
|
import { ActionVerifier } from './actionVerifier.js';
|
|
16
16
|
import { ApprovalManager, RiskClassifier } from './safety.js';
|
|
17
|
-
import { TaskPlanner } from './planner.js';
|
|
17
|
+
import { TaskPlanner, checkSimpleAppAction } from './planner.js';
|
|
18
18
|
import { UnifiedToolRegistry } from './toolRegistry.js';
|
|
19
19
|
import { SystemComputerController, MockComputerController } from './computer/index.js';
|
|
20
20
|
import { AgentWorkspace, inferExecutionIntent, resolveDesktopPath } from './workspace/index.js';
|
|
@@ -615,8 +615,10 @@ CORE ARCHITECTURAL PRINCIPLES:
|
|
|
615
615
|
6. MULTILINGUAL & INTENT COMPREHENSION: Understand user intent in any language (English, Hinglish like "likho", "bnao", "code karo", "fix karo", "samjha do", Hindi, Spanish, etc.).
|
|
616
616
|
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).
|
|
617
617
|
8. RUN & VERIFY COMMANDS: Execute shell commands, tests, builds, and formatters using run_command.
|
|
618
|
-
9. CRITICAL TERMINATION 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
|
|
618
|
+
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.
|
|
619
|
+
- 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!
|
|
619
620
|
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.
|
|
621
|
+
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!
|
|
620
622
|
|
|
621
623
|
When returning tool calls, use standard OpenAI function calling format or JSON tool call payload format:
|
|
622
624
|
\`\`\`json
|
|
@@ -657,97 +659,100 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
|
|
|
657
659
|
content: recentSessionsMemory,
|
|
658
660
|
});
|
|
659
661
|
}
|
|
660
|
-
// Robust Target File Discovery:
|
|
662
|
+
// Robust Target File Discovery: Only run file discovery if the goal is NOT a simple desktop application launch/action
|
|
663
|
+
const simpleAppCheck = checkSimpleAppAction(userGoal);
|
|
661
664
|
const filesList = getDirectoryFiles(this.cwd);
|
|
662
665
|
const matchedFiles = [];
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
norm.includes('
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
matchedFiles.
|
|
666
|
+
if (!simpleAppCheck.isSimple) {
|
|
667
|
+
const isIgnoredFile = (filePath) => {
|
|
668
|
+
const norm = filePath.replace(/\\/g, '/').toLowerCase();
|
|
669
|
+
const ext = path.extname(norm);
|
|
670
|
+
const ignoredExts = ['.pyc', '.pyo', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.exe', '.bin', '.dll', '.so', '.dylib', '.zip', '.tar', '.gz'];
|
|
671
|
+
return (norm.includes('__pycache__/') ||
|
|
672
|
+
norm.includes('.git/') ||
|
|
673
|
+
norm.includes('node_modules/') ||
|
|
674
|
+
norm.includes('.ft/') ||
|
|
675
|
+
ignoredExts.includes(ext));
|
|
676
|
+
};
|
|
677
|
+
// Match potential paths
|
|
678
|
+
const pathMatches = userGoal.match(/(?:[a-zA-Z]:[\\/][^\s"':;,]+|\.?[\\/][^\s"':;,]+\.[a-zA-Z0-9]+|[a-zA-Z0-9_\-./\\]+\.[a-zA-Z0-9]+)/g) || [];
|
|
679
|
+
for (const rawPath of pathMatches) {
|
|
680
|
+
const cleanP = rawPath.replace(/^["'()]+|["'()]+$/g, '').trim();
|
|
681
|
+
if (!cleanP || cleanP.length < 3)
|
|
682
|
+
continue;
|
|
683
|
+
const fullP = path.isAbsolute(cleanP) ? cleanP : path.resolve(this.cwd, cleanP);
|
|
684
|
+
if (fs.existsSync(fullP) && fs.statSync(fullP).isFile()) {
|
|
685
|
+
const relP = path.relative(this.cwd, fullP).replace(/\\/g, '/');
|
|
686
|
+
if (!isIgnoredFile(relP) && !matchedFiles.includes(relP))
|
|
687
|
+
matchedFiles.push(relP);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
// Check if any file in filesList matches or ends with the extracted relative path or filename
|
|
691
|
+
const normToken = cleanP.replace(/\\/g, '/').toLowerCase();
|
|
692
|
+
const baseName = path.basename(normToken);
|
|
693
|
+
for (const f of filesList) {
|
|
694
|
+
if (isIgnoredFile(f))
|
|
695
|
+
continue;
|
|
696
|
+
const normF = f.replace(/\\/g, '/').toLowerCase();
|
|
697
|
+
if (normF === normToken || normF.endsWith('/' + normToken) || (baseName.includes('.') && normF.endsWith('/' + baseName))) {
|
|
698
|
+
if (!matchedFiles.includes(f))
|
|
699
|
+
matchedFiles.push(f);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
684
703
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
704
|
+
// Keyword fallback if no direct path matched
|
|
705
|
+
if (matchedFiles.length === 0) {
|
|
706
|
+
const stopWords = new Set([
|
|
707
|
+
'the', 'a', 'an', 'in', 'is', 'for', 'to', 'of', 'and', 'or', 'on', 'at', 'by', 'from',
|
|
708
|
+
'with', 'this', 'that', 'it', 'code', 'file', 'files', 'fix', 'add', 'make', 'create',
|
|
709
|
+
'update', 'change', 'modify', 'edit', 'write', 'implement', 'bro', 'please', 'scout',
|
|
710
|
+
'agent', 'bug', 'error', 'issue', 'help', 'app', 'project', 'script', 'function',
|
|
711
|
+
'folder', 'dir', 'directory', 'testing', 'inside', 'open', 'vs', 'vscode', 'py', 'python',
|
|
712
|
+
'new', 'build', 'run', 'test', 'suite'
|
|
713
|
+
]);
|
|
714
|
+
const words = userGoal.split(/[\s,;:\/\\]+/).map((w) => w.replace(/["'(),;:!]/g, '').trim());
|
|
715
|
+
const keywords = words.filter((w) => w.length >= 3 && !stopWords.has(w.toLowerCase()));
|
|
689
716
|
for (const f of filesList) {
|
|
690
717
|
if (isIgnoredFile(f))
|
|
691
718
|
continue;
|
|
719
|
+
if (matchedFiles.length >= 3)
|
|
720
|
+
break;
|
|
692
721
|
const normF = f.replace(/\\/g, '/').toLowerCase();
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
722
|
+
const baseName = path.basename(normF, path.extname(normF));
|
|
723
|
+
for (const kw of keywords) {
|
|
724
|
+
const kwLower = kw.toLowerCase();
|
|
725
|
+
if (baseName === kwLower || normF.endsWith('/' + kwLower + path.extname(normF))) {
|
|
726
|
+
if (!matchedFiles.includes(f))
|
|
727
|
+
matchedFiles.push(f);
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
696
730
|
}
|
|
697
731
|
}
|
|
698
732
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
if (baseName === kwLower || normF.endsWith('/' + kwLower + path.extname(normF))) {
|
|
722
|
-
if (!matchedFiles.includes(f))
|
|
723
|
-
matchedFiles.push(f);
|
|
724
|
-
break;
|
|
733
|
+
// Safely inject high-confidence existing file previews without prompt-hijacking
|
|
734
|
+
for (const file of matchedFiles.slice(0, 3)) {
|
|
735
|
+
const fullP = path.resolve(this.cwd, file);
|
|
736
|
+
if (fs.existsSync(fullP) && fs.statSync(fullP).isFile()) {
|
|
737
|
+
const analyzeSpin = spinner();
|
|
738
|
+
analyzeSpin.start(chalk.cyan(` Auto-discovered target file: ${file}. Analyzing...`));
|
|
739
|
+
try {
|
|
740
|
+
const content = fs.readFileSync(fullP, 'utf-8');
|
|
741
|
+
const lines = content.split('\n');
|
|
742
|
+
analyzeSpin.stop(chalk.green(` Auto-discovered & analyzed ${file} (${lines.length} lines)`));
|
|
743
|
+
const previewSnippet = lines.length <= 250
|
|
744
|
+
? lines.map((l, i) => `${i + 1} | ${l}`).join('\n')
|
|
745
|
+
: lines.slice(0, 100).map((l, i) => `${i + 1} | ${l}`).join('\n') + `\n... [truncated, file has ${lines.length} lines. Call read_file to view remaining lines]`;
|
|
746
|
+
this.readFilesHistory.add(file);
|
|
747
|
+
this.readFilesHistory.add(file.replace(/\\/g, '/').toLowerCase());
|
|
748
|
+
this.historyMessages.push({
|
|
749
|
+
role: 'user',
|
|
750
|
+
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.`,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
catch {
|
|
754
|
+
analyzeSpin.stop(chalk.yellow(`Could not read file ${file}`));
|
|
725
755
|
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
// Safely inject high-confidence existing file previews without prompt-hijacking
|
|
730
|
-
for (const file of matchedFiles.slice(0, 3)) {
|
|
731
|
-
const fullP = path.resolve(this.cwd, file);
|
|
732
|
-
if (fs.existsSync(fullP) && fs.statSync(fullP).isFile()) {
|
|
733
|
-
const analyzeSpin = spinner();
|
|
734
|
-
analyzeSpin.start(chalk.cyan(` Auto-discovered target file: ${file}. Analyzing...`));
|
|
735
|
-
try {
|
|
736
|
-
const content = fs.readFileSync(fullP, 'utf-8');
|
|
737
|
-
const lines = content.split('\n');
|
|
738
|
-
analyzeSpin.stop(chalk.green(` Auto-discovered & analyzed ${file} (${lines.length} lines)`));
|
|
739
|
-
const previewSnippet = lines.length <= 250
|
|
740
|
-
? lines.map((l, i) => `${i + 1} | ${l}`).join('\n')
|
|
741
|
-
: lines.slice(0, 100).map((l, i) => `${i + 1} | ${l}`).join('\n') + `\n... [truncated, file has ${lines.length} lines. Call read_file to view remaining lines]`;
|
|
742
|
-
this.readFilesHistory.add(file);
|
|
743
|
-
this.readFilesHistory.add(file.replace(/\\/g, '/').toLowerCase());
|
|
744
|
-
this.historyMessages.push({
|
|
745
|
-
role: 'user',
|
|
746
|
-
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.`,
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
catch {
|
|
750
|
-
analyzeSpin.stop(chalk.yellow(`Could not read file ${file}`));
|
|
751
756
|
}
|
|
752
757
|
}
|
|
753
758
|
}
|
|
@@ -858,6 +863,29 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
858
863
|
content: `Tool Execution Result (${fnName}):\n${toolResult.result}`,
|
|
859
864
|
});
|
|
860
865
|
this.handleToolConsecutiveTracking(fnName, args, step);
|
|
866
|
+
// Automatic immediate completion for simple app actions (open/close/switch window)
|
|
867
|
+
if (simpleAppCheck.isSimple && (fnName === 'computer_open_app' || fnName === 'computer_close_app' || fnName === 'computer_switch_window')) {
|
|
868
|
+
let callSucceeded = false;
|
|
869
|
+
try {
|
|
870
|
+
const parsed = JSON.parse(toolResult.result);
|
|
871
|
+
callSucceeded = parsed.success !== false;
|
|
872
|
+
}
|
|
873
|
+
catch {
|
|
874
|
+
callSucceeded = !toolResult.result.toLowerCase().includes('failed') && !toolResult.result.toLowerCase().includes('error');
|
|
875
|
+
}
|
|
876
|
+
if (callSucceeded) {
|
|
877
|
+
const appName = simpleAppCheck.appName || 'application';
|
|
878
|
+
const actionVerb = simpleAppCheck.action === 'close' ? 'closed' : (simpleAppCheck.action === 'switch' ? 'focused' : 'launched');
|
|
879
|
+
finalSummary = `I've ${actionVerb} ${appName} for you. It's ready on your screen!`;
|
|
880
|
+
safeNote(chalk.green(` ${finalSummary}`), 'Task Completed');
|
|
881
|
+
saveAgentSession({
|
|
882
|
+
goal: userGoal,
|
|
883
|
+
summary: finalSummary,
|
|
884
|
+
modifiedFiles: [],
|
|
885
|
+
});
|
|
886
|
+
return { success: true, summary: finalSummary };
|
|
887
|
+
}
|
|
888
|
+
}
|
|
861
889
|
if (fnName === 'task_completed') {
|
|
862
890
|
finalSummary = args.summary || toolResult.result;
|
|
863
891
|
if (this.modifiedFiles.size > 0) {
|
|
@@ -891,6 +919,29 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
891
919
|
content: `Tool Execution Result (${parsedJsonTool.tool}):\n${toolResult.result}`,
|
|
892
920
|
});
|
|
893
921
|
this.handleToolConsecutiveTracking(parsedJsonTool.tool, parsedJsonTool.args, step);
|
|
922
|
+
// Automatic immediate completion for simple app actions (open/close/switch window)
|
|
923
|
+
if (simpleAppCheck.isSimple && (parsedJsonTool.tool === 'computer_open_app' || parsedJsonTool.tool === 'computer_close_app' || parsedJsonTool.tool === 'computer_switch_window')) {
|
|
924
|
+
let callSucceeded = false;
|
|
925
|
+
try {
|
|
926
|
+
const parsed = JSON.parse(toolResult.result);
|
|
927
|
+
callSucceeded = parsed.success !== false;
|
|
928
|
+
}
|
|
929
|
+
catch {
|
|
930
|
+
callSucceeded = !toolResult.result.toLowerCase().includes('failed') && !toolResult.result.toLowerCase().includes('error');
|
|
931
|
+
}
|
|
932
|
+
if (callSucceeded) {
|
|
933
|
+
const appName = simpleAppCheck.appName || 'application';
|
|
934
|
+
const actionVerb = simpleAppCheck.action === 'close' ? 'closed' : (simpleAppCheck.action === 'switch' ? 'focused' : 'launched');
|
|
935
|
+
finalSummary = `I've ${actionVerb} ${appName} for you. It's ready on your screen!`;
|
|
936
|
+
safeNote(chalk.green(` ${finalSummary}`), 'Task Completed');
|
|
937
|
+
saveAgentSession({
|
|
938
|
+
goal: userGoal,
|
|
939
|
+
summary: finalSummary,
|
|
940
|
+
modifiedFiles: [],
|
|
941
|
+
});
|
|
942
|
+
return { success: true, summary: finalSummary };
|
|
943
|
+
}
|
|
944
|
+
}
|
|
894
945
|
if (parsedJsonTool.tool === 'task_completed') {
|
|
895
946
|
finalSummary = parsedJsonTool.args?.summary || toolResult.result;
|
|
896
947
|
saveAgentSession({
|
|
@@ -1178,6 +1229,22 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1178
1229
|
}
|
|
1179
1230
|
}
|
|
1180
1231
|
// Validate against execution intent (USER INTENT > TOOL CONVENIENCE)
|
|
1232
|
+
const simpleCheckInDispatch = checkSimpleAppAction(this.taskStateManager.getState()?.objective || '');
|
|
1233
|
+
if (simpleCheckInDispatch.isSimple) {
|
|
1234
|
+
if (['list_dir', 'read_file', 'grep_search', 'glob_search', 'file_info', 'tree_view', 'write_file', 'edit_file', 'multi_edit_file'].includes(name)) {
|
|
1235
|
+
const blockedExplorationMsg = `[UNNECESSARY WORKSPACE EXPLORATION BLOCKED]: The user only requested to ${simpleCheckInDispatch.action || 'launch'} ${simpleCheckInDispatch.appName || 'the application'}. Reading repository code or listing files (${name}) is forbidden for this task. You must call task_completed immediately.`;
|
|
1236
|
+
this.taskStateManager.addObservation(name, blockedExplorationMsg, false);
|
|
1237
|
+
return {
|
|
1238
|
+
result: JSON.stringify({
|
|
1239
|
+
success: false,
|
|
1240
|
+
action: name,
|
|
1241
|
+
error: 'UnnecessaryWorkspaceExplorationBlocked',
|
|
1242
|
+
message: blockedExplorationMsg,
|
|
1243
|
+
directive: "Call 'task_completed' NOW with your completion summary."
|
|
1244
|
+
}, null, 2)
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1181
1248
|
if (this.executionIntent && !this.executionIntent.allowAlternativeTools) {
|
|
1182
1249
|
if (this.executionIntent.requiredInteraction === 'computer') {
|
|
1183
1250
|
if (['write_file', 'edit_file', 'multi_edit_file'].includes(name)) {
|
|
@@ -1983,6 +2050,9 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1983
2050
|
const res = await this.computerController.openApplication(appName, args.args);
|
|
1984
2051
|
this.taskStateManager.setActiveApplication(appName);
|
|
1985
2052
|
this.taskStateManager.addObservation('computer_open_app', res.output, res.success);
|
|
2053
|
+
const currentObj = this.taskStateManager.getState()?.objective || '';
|
|
2054
|
+
const simpleCheck = checkSimpleAppAction(currentObj);
|
|
2055
|
+
const isOnlyOpening = simpleCheck.isSimple && simpleCheck.action !== 'close';
|
|
1986
2056
|
const structuredRes = {
|
|
1987
2057
|
success: res.success,
|
|
1988
2058
|
action: 'open_app',
|
|
@@ -1991,6 +2061,9 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1991
2061
|
recoverable: res.recoverable ?? true,
|
|
1992
2062
|
activeWindow: res.activeWindow || appName,
|
|
1993
2063
|
expectedWindow: res.expectedWindow || appName,
|
|
2064
|
+
...(isOnlyOpening && res.success ? {
|
|
2065
|
+
directive: "CRITICAL: The requested application is now opened. Your objective is 100% satisfied. You MUST call 'task_completed' immediately on your next turn. DO NOT call list_dir, read_file, or explore the repository."
|
|
2066
|
+
} : {}),
|
|
1994
2067
|
};
|
|
1995
2068
|
return { result: JSON.stringify(structuredRes, null, 2) };
|
|
1996
2069
|
}
|
|
@@ -1998,6 +2071,12 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1998
2071
|
const appName = String(args.appName || args.app || '');
|
|
1999
2072
|
const res = await this.computerController.closeApplication(appName);
|
|
2000
2073
|
this.taskStateManager.addObservation('computer_close_app', res.output, res.success);
|
|
2074
|
+
const currentObj = this.taskStateManager.getState()?.objective || '';
|
|
2075
|
+
const simpleCheck = checkSimpleAppAction(currentObj);
|
|
2076
|
+
const isOnlyClosing = simpleCheck.isSimple && simpleCheck.action === 'close';
|
|
2077
|
+
if (isOnlyClosing && res.success) {
|
|
2078
|
+
return { result: `${res.output}\nCRITICAL: Application closed. Objective complete. Call task_completed now.` };
|
|
2079
|
+
}
|
|
2001
2080
|
return { result: res.output };
|
|
2002
2081
|
}
|
|
2003
2082
|
case 'computer_switch_window': {
|