ft-scout 9.0.1 → 9.0.3
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 +7 -7
- package/README.md +93 -13
- package/bin/src/commands/agent.d.ts.map +1 -1
- package/bin/src/commands/agent.js +46 -3
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/engine/actionVerifier.d.ts.map +1 -0
- package/bin/src/engine/actionVerifier.js +223 -0
- package/bin/src/engine/actionVerifier.js.map +1 -0
- package/bin/src/engine/agentEngine.d.ts.map +1 -1
- package/bin/src/engine/agentEngine.js +237 -1
- package/bin/src/engine/agentEngine.js.map +1 -1
- package/bin/src/engine/agentState.d.ts.map +1 -0
- package/bin/src/engine/agentState.js +445 -0
- package/bin/src/engine/agentState.js.map +1 -0
- package/bin/src/engine/appControl.d.ts.map +1 -1
- package/bin/src/engine/appControl.js +165 -59
- package/bin/src/engine/appControl.js.map +1 -1
- package/bin/src/engine/browser/browserController.d.ts.map +1 -0
- package/bin/src/engine/browser/browserController.js +689 -0
- package/bin/src/engine/browser/browserController.js.map +1 -0
- package/bin/src/engine/browser/index.d.ts.map +1 -0
- package/bin/src/engine/browser/index.js +2 -0
- package/bin/src/engine/browser/index.js.map +1 -0
- package/bin/src/engine/computer/computerController.d.ts.map +1 -0
- package/bin/src/engine/computer/computerController.js +295 -0
- package/bin/src/engine/computer/computerController.js.map +1 -0
- package/bin/src/engine/computer/index.d.ts.map +1 -0
- package/bin/src/engine/computer/index.js +2 -0
- package/bin/src/engine/computer/index.js.map +1 -0
- package/bin/src/engine/liveScreenEngine.js +2 -2
- package/bin/src/engine/liveScreenEngine.js.map +1 -1
- package/bin/src/engine/planner.d.ts.map +1 -0
- package/bin/src/engine/planner.js +242 -0
- package/bin/src/engine/planner.js.map +1 -0
- package/bin/src/engine/safety.d.ts.map +1 -0
- package/bin/src/engine/safety.js +281 -0
- package/bin/src/engine/safety.js.map +1 -0
- package/bin/src/engine/toolRegistry.d.ts.map +1 -0
- package/bin/src/engine/toolRegistry.js +218 -0
- package/bin/src/engine/toolRegistry.js.map +1 -0
- package/bin/src/index.js +6 -4
- package/bin/src/index.js.map +1 -1
- package/bin/src/server/server.d.ts.map +1 -1
- package/bin/src/server/server.js +116 -0
- package/bin/src/server/server.js.map +1 -1
- package/bin/src/utils/branding.js +1 -1
- package/downloads/INVOICE_2026_ACME.pdf +1 -0
- package/package.json +1 -1
- package/web/app.js +277 -46
- package/web/styles.css +79 -2
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { checkFileSyntax } from './verifier.js';
|
|
4
|
+
export class ActionVerifier {
|
|
5
|
+
workspaceDir;
|
|
6
|
+
failureCounts = new Map();
|
|
7
|
+
constructor(workspaceDir = process.cwd()) {
|
|
8
|
+
this.workspaceDir = workspaceDir;
|
|
9
|
+
}
|
|
10
|
+
determineRecoveryStrategy(result, attempt = 1, maxRetries = 3) {
|
|
11
|
+
if (attempt >= maxRetries) {
|
|
12
|
+
return 'RE_PLAN';
|
|
13
|
+
}
|
|
14
|
+
const ruleType = result.ruleType || '';
|
|
15
|
+
const msg = (result.message || '').toLowerCase();
|
|
16
|
+
if (ruleType.includes('dom') || msg.includes('selector') || msg.includes('element') || msg.includes('dom')) {
|
|
17
|
+
return 'ALTERNATIVE_ACTION';
|
|
18
|
+
}
|
|
19
|
+
return 'RETRY';
|
|
20
|
+
}
|
|
21
|
+
async verify(target) {
|
|
22
|
+
if (target.type === 'file_exists') {
|
|
23
|
+
const res = await this.verifyFileCreated(target.expected, target.options?.minSizeBytes || (target.options?.requireNonEmpty ? 1 : 0));
|
|
24
|
+
return { success: res.verified, actual: res.verified, verified: res.verified, ruleType: 'file_exists', message: res.message };
|
|
25
|
+
}
|
|
26
|
+
if (target.type === 'file_syntax') {
|
|
27
|
+
const res = await this.verifyFileSyntax(target.expected);
|
|
28
|
+
return { success: res.verified, actual: res.verified, verified: res.verified, ruleType: 'file_syntax', message: res.message };
|
|
29
|
+
}
|
|
30
|
+
if (target.type === 'dom_element_present') {
|
|
31
|
+
let elements = [];
|
|
32
|
+
if (target.browser?.getDOM) {
|
|
33
|
+
const dom = await target.browser.getDOM();
|
|
34
|
+
elements = dom.elements || [];
|
|
35
|
+
}
|
|
36
|
+
else if (target.browser?.getDom) {
|
|
37
|
+
const dom = await target.browser.getDom();
|
|
38
|
+
elements = dom.elements || [];
|
|
39
|
+
}
|
|
40
|
+
else if (target.browser?.inspect_page) {
|
|
41
|
+
const dom = await target.browser.inspect_page();
|
|
42
|
+
elements = dom.elements || [];
|
|
43
|
+
}
|
|
44
|
+
const res = this.verifyDomElement(elements, target.expected);
|
|
45
|
+
return { success: res.verified, actual: res.verified, verified: res.verified, ruleType: 'dom_element_present', message: res.message };
|
|
46
|
+
}
|
|
47
|
+
if (target.type === 'url_matches') {
|
|
48
|
+
const actual = target.browser?.currentUrl || '';
|
|
49
|
+
const res = this.verifyUrl(actual, target.expected);
|
|
50
|
+
return { success: res.verified, actual, verified: res.verified, ruleType: 'url_matches', message: res.message };
|
|
51
|
+
}
|
|
52
|
+
if (target.type === 'file_content_matches') {
|
|
53
|
+
const fullPath = path.isAbsolute(target.expected) ? target.expected : path.resolve(this.workspaceDir, target.expected);
|
|
54
|
+
if (!fs.existsSync(fullPath))
|
|
55
|
+
return { success: false, actual: false, verified: false, ruleType: 'file_content_matches', message: 'File not found' };
|
|
56
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
57
|
+
const pattern = target.options?.pattern;
|
|
58
|
+
const matches = pattern instanceof RegExp ? pattern.test(content) : content.includes(String(pattern));
|
|
59
|
+
return { success: matches, actual: matches, verified: matches, ruleType: 'file_content_matches', message: matches ? 'Content matched' : 'Content did not match' };
|
|
60
|
+
}
|
|
61
|
+
return { success: false, actual: false, verified: false, ruleType: target.type || 'unknown', message: `Unknown verification target: ${target.type}` };
|
|
62
|
+
}
|
|
63
|
+
async verifyFileCreated(filePath, minSizeBytes = 1) {
|
|
64
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(this.workspaceDir, filePath);
|
|
65
|
+
if (!fs.existsSync(fullPath)) {
|
|
66
|
+
return {
|
|
67
|
+
verified: false,
|
|
68
|
+
ruleType: 'file_exists',
|
|
69
|
+
message: `Verification failed: File "${filePath}" does not exist on disk.`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const stat = fs.statSync(fullPath);
|
|
74
|
+
if (stat.size < minSizeBytes) {
|
|
75
|
+
return {
|
|
76
|
+
verified: false,
|
|
77
|
+
ruleType: 'file_exists',
|
|
78
|
+
message: `Verification failed: File "${filePath}" exists but is empty (0 bytes).`,
|
|
79
|
+
details: { size: stat.size },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
verified: true,
|
|
84
|
+
ruleType: 'file_exists',
|
|
85
|
+
message: `Verification confirmed: File "${filePath}" exists (${stat.size} bytes).`,
|
|
86
|
+
details: { size: stat.size, mtime: stat.mtime },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
verified: false,
|
|
92
|
+
ruleType: 'file_exists',
|
|
93
|
+
message: `Verification error accessing file "${filePath}": ${err.message}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async verifyFileSyntax(filePath) {
|
|
98
|
+
const res = await checkFileSyntax(filePath, this.workspaceDir);
|
|
99
|
+
if (!res.valid) {
|
|
100
|
+
return {
|
|
101
|
+
verified: false,
|
|
102
|
+
ruleType: 'file_syntax',
|
|
103
|
+
message: `Syntax/build verification failed for "${filePath}":\n${res.errors.join('\n')}`,
|
|
104
|
+
details: { errors: res.errors },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
verified: true,
|
|
109
|
+
ruleType: 'file_syntax',
|
|
110
|
+
message: `Syntax/build verification confirmed: "${filePath}" is clean.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
verifyDomElement(elements, targetSelectorOrText) {
|
|
114
|
+
const search = targetSelectorOrText.toLowerCase().trim();
|
|
115
|
+
const found = elements.some((el) => {
|
|
116
|
+
if (el.ref !== undefined && String(el.ref).toLowerCase() === search)
|
|
117
|
+
return true;
|
|
118
|
+
if (el.selector && el.selector.toLowerCase() === search)
|
|
119
|
+
return true;
|
|
120
|
+
if (el.id && (el.id.toLowerCase() === search || `#${el.id.toLowerCase()}` === search))
|
|
121
|
+
return true;
|
|
122
|
+
if (el.tag && el.tag.toLowerCase() === search)
|
|
123
|
+
return true;
|
|
124
|
+
if (el.text && el.text.toLowerCase().includes(search))
|
|
125
|
+
return true;
|
|
126
|
+
return false;
|
|
127
|
+
});
|
|
128
|
+
if (found) {
|
|
129
|
+
return {
|
|
130
|
+
verified: true,
|
|
131
|
+
ruleType: 'dom_element',
|
|
132
|
+
message: `Verified DOM element matching "${targetSelectorOrText}" is present and interactive.`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
verified: false,
|
|
137
|
+
ruleType: 'dom_element',
|
|
138
|
+
message: `Verification failed: DOM element matching "${targetSelectorOrText}" was not found in current page.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
verifyUrl(currentUrl, expectedPattern) {
|
|
142
|
+
if (!currentUrl) {
|
|
143
|
+
return {
|
|
144
|
+
verified: false,
|
|
145
|
+
ruleType: 'url_match',
|
|
146
|
+
message: `Verification failed: No active URL detected.`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const match = currentUrl.toLowerCase().includes(expectedPattern.toLowerCase());
|
|
150
|
+
if (match) {
|
|
151
|
+
return {
|
|
152
|
+
verified: true,
|
|
153
|
+
ruleType: 'url_match',
|
|
154
|
+
message: `URL verification confirmed: Current URL "${currentUrl}" matches pattern "${expectedPattern}".`,
|
|
155
|
+
details: { currentUrl },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
verified: false,
|
|
160
|
+
ruleType: 'url_match',
|
|
161
|
+
message: `URL verification failed: Current URL "${currentUrl}" does not match pattern "${expectedPattern}".`,
|
|
162
|
+
details: { currentUrl, expected: expectedPattern },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
determineRecovery(tool, error, attemptKey = tool, maxRetries = 3) {
|
|
166
|
+
const errLower = (error || '').toLowerCase();
|
|
167
|
+
const currentCount = (this.failureCounts.get(attemptKey) || 0) + 1;
|
|
168
|
+
this.failureCounts.set(attemptKey, currentCount);
|
|
169
|
+
// 1. If exceeded retry budget, abort or re-plan
|
|
170
|
+
if (currentCount > maxRetries) {
|
|
171
|
+
return {
|
|
172
|
+
strategy: 'RE_PLAN',
|
|
173
|
+
reason: `Action "${tool}" failed ${currentCount} times (exceeded max retries of ${maxRetries}). Switching strategy or re-planning.`,
|
|
174
|
+
maxRetriesExceeded: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// 2. Element not found or stale selector
|
|
178
|
+
if (errLower.includes('element not found') || errLower.includes('stale selector') || errLower.includes('not visible') || errLower.includes('not found')) {
|
|
179
|
+
return {
|
|
180
|
+
strategy: 'RE_OBSERVE',
|
|
181
|
+
reason: `Target element was not found in current view. Re-inspecting page/screen structure to locate updated selector.`,
|
|
182
|
+
suggestedAction: {
|
|
183
|
+
tool: 'browser_inspect',
|
|
184
|
+
args: {},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// 3. Login or credentials required
|
|
189
|
+
if (errLower.includes('login required') || errLower.includes('unauthorized') || errLower.includes('authentication') || errLower.includes('401') || errLower.includes('403')) {
|
|
190
|
+
return {
|
|
191
|
+
strategy: 'ASK_USER',
|
|
192
|
+
reason: `Application or page requires user credentials or authentication to proceed.`,
|
|
193
|
+
suggestedAction: {
|
|
194
|
+
tool: 'ask_user',
|
|
195
|
+
args: { question: 'This action requires authentication or access permission. Please provide required access or log in.' },
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// 4. Temporary timeout or network glitch
|
|
200
|
+
if (errLower.includes('timeout') || errLower.includes('econnreset') || errLower.includes('network') || errLower.includes('rate limit')) {
|
|
201
|
+
return {
|
|
202
|
+
strategy: 'RETRY',
|
|
203
|
+
reason: `Temporary network/timeout error encountered. Retrying attempt ${currentCount}/${maxRetries}...`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// 5. App or browser disconnected
|
|
207
|
+
if (errLower.includes('disconnected') || errLower.includes('target closed') || errLower.includes('process exited')) {
|
|
208
|
+
return {
|
|
209
|
+
strategy: 'ALTERNATIVE_ACTION',
|
|
210
|
+
reason: `Connection to application lost. Re-launching application or utilizing workspace API alternative.`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Default recovery
|
|
214
|
+
return {
|
|
215
|
+
strategy: 'RE_OBSERVE',
|
|
216
|
+
reason: `Action failed: ${error}. Inspecting environment state before deciding next step.`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
resetFailureCount(attemptKey) {
|
|
220
|
+
this.failureCounts.delete(attemptKey);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=actionVerifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionVerifier.js","sourceRoot":"","sources":["actionVerifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentEngine.d.ts","sourceRoot":"","sources":["agentEngine.ts"],"names":[],"mappings":"AAMA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"agentEngine.d.ts","sourceRoot":"","sources":["agentEngine.ts"],"names":[],"mappings":"AAMA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQ7F,OAAO,EAAE,gBAAgB,EAA4B,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,KAAK,mBAAmB,EAAwB,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,KAAK,kBAAkB,EAA6B,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAkB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,wBAAgB,oBAAoB,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GACpB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAwG9D;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACL;AAED,eAAO,MAAM,WAAW,EAAE,cAAc,EA2OvC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,kBAAkB,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACrD,iBAAiB,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,YAAY,SAAI,EAAE,QAAQ,SAAI,GAAG,MAAM,CAyCvG;AAED,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,uBAAuB,CAAa;IAC5C,OAAO,CAAC,eAAe,CAAkD;IACzE,OAAO,CAAC,eAAe,CAIyB;IAEhD,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAU;IAE1B,YAAY,OAAO,CAAC,EAAE,gBAAgB,EA6BrC;IAEM,mBAAmB,IAAI,gBAAgB,CAE7C;IAEM,eAAe,IAAI,gBAAgB,CAEzC;IAEM,qBAAqB,IAAI,mBAAmB,CAElD;IAEM,oBAAoB,IAAI,kBAAkB,CAEhD;IAEM,iBAAiB,IAAI,cAAc,CAEzC;IAEM,kBAAkB,IAAI,eAAe,CAE3C;IAEM,UAAU,IAAI,WAAW,CAE/B;IAEM,eAAe,IAAI,mBAAmB,CAE5C;IAEY,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CActF;IAEM,gBAAgB,IAAI,MAAM,EAAE,CAElC;IAEM,UAAU,IAAI,UAAU,EAAE,CAEhC;IAEM,QAAQ,IAAI;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;KACrB,CAOA;IAEM,YAAY,IAAI,IAAI,CAa1B;IAED,OAAO,CAAC,gBAAgB;IAuCxB,OAAO,CAAC,0BAA0B;IA8ClC,OAAO,CAAC,6BAA6B;IA+ErC,OAAO,CAAC,YAAY;IAmGpB,OAAO,CAAC,0BAA0B;IAerB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CA0ZzF;IAED,OAAO,CAAC,mBAAmB;YAkDb,gBAAgB;CAsrBjC"}
|
|
@@ -12,6 +12,13 @@ import { checkFileSyntax, verifyAndSelfHealFiles, executeSmartCommand, extractEr
|
|
|
12
12
|
import { openApp, executeInApp, cleanupScreenshots } from './appControl.js';
|
|
13
13
|
import { speakText, listenSpeechToText } from './voiceEngine.js';
|
|
14
14
|
import { startLiveScreenShare, stopLiveScreenShare, isLiveScreenShareActive } from './liveScreenEngine.js';
|
|
15
|
+
import { TaskStateManager } from './agentState.js';
|
|
16
|
+
import { OSComputerController } from './computer/index.js';
|
|
17
|
+
import { SemanticBrowserController } from './browser/index.js';
|
|
18
|
+
import { ActionVerifier } from './actionVerifier.js';
|
|
19
|
+
import { ApprovalManager, RiskClassifier } from './safety.js';
|
|
20
|
+
import { TaskPlanner } from './planner.js';
|
|
21
|
+
import { UnifiedToolRegistry } from './toolRegistry.js';
|
|
15
22
|
const execAsync = promisify(exec);
|
|
16
23
|
export function robustSnippetReplace(origContent, target, replacement) {
|
|
17
24
|
if (!origContent || !target) {
|
|
@@ -403,11 +410,27 @@ export class AgentExecutionLoop {
|
|
|
403
410
|
consecutiveScreenChecks = 0;
|
|
404
411
|
toolCallHistory = [];
|
|
405
412
|
scratchpadState = { plan: [], completedSteps: [], notes: '' };
|
|
413
|
+
taskStateManager;
|
|
414
|
+
computerController;
|
|
415
|
+
browserController;
|
|
416
|
+
actionVerifier;
|
|
417
|
+
approvalManager;
|
|
418
|
+
taskPlanner;
|
|
419
|
+
toolRegistry;
|
|
420
|
+
mockMode;
|
|
406
421
|
constructor(options) {
|
|
407
422
|
this.cwd = process.cwd();
|
|
408
423
|
this.autoApprove = Boolean(options?.autoApprove);
|
|
409
424
|
this.maxSteps = options?.maxSteps || 30;
|
|
410
425
|
this.projectName = options?.projectName || path.basename(this.cwd);
|
|
426
|
+
this.mockMode = Boolean(options?.mockMode);
|
|
427
|
+
this.taskStateManager = new TaskStateManager(options?.taskId, '', this.cwd);
|
|
428
|
+
this.computerController = options?.computerController || new OSComputerController();
|
|
429
|
+
this.browserController = options?.browserController || new SemanticBrowserController(this.cwd);
|
|
430
|
+
this.actionVerifier = new ActionVerifier(this.cwd);
|
|
431
|
+
this.approvalManager = new ApprovalManager(this.autoApprove);
|
|
432
|
+
this.taskPlanner = new TaskPlanner();
|
|
433
|
+
this.toolRegistry = new UnifiedToolRegistry();
|
|
411
434
|
if (options?.screenShare) {
|
|
412
435
|
startLiveScreenShare().catch(() => { });
|
|
413
436
|
}
|
|
@@ -429,6 +452,45 @@ export class AgentExecutionLoop {
|
|
|
429
452
|
process.exit(0);
|
|
430
453
|
});
|
|
431
454
|
}
|
|
455
|
+
getTaskStateManager() {
|
|
456
|
+
return this.taskStateManager;
|
|
457
|
+
}
|
|
458
|
+
getStateManager() {
|
|
459
|
+
return this.taskStateManager;
|
|
460
|
+
}
|
|
461
|
+
getComputerController() {
|
|
462
|
+
return this.computerController;
|
|
463
|
+
}
|
|
464
|
+
getBrowserController() {
|
|
465
|
+
return this.browserController;
|
|
466
|
+
}
|
|
467
|
+
getActionVerifier() {
|
|
468
|
+
return this.actionVerifier;
|
|
469
|
+
}
|
|
470
|
+
getApprovalManager() {
|
|
471
|
+
return this.approvalManager;
|
|
472
|
+
}
|
|
473
|
+
getPlanner() {
|
|
474
|
+
return this.taskPlanner;
|
|
475
|
+
}
|
|
476
|
+
getToolRegistry() {
|
|
477
|
+
return this.toolRegistry;
|
|
478
|
+
}
|
|
479
|
+
async resumeGoal(taskId) {
|
|
480
|
+
const saved = TaskStateManager.loadTaskState(taskId, this.cwd);
|
|
481
|
+
if (!saved) {
|
|
482
|
+
return { success: false, summary: `Saved task checkpoint "${taskId}" not found.` };
|
|
483
|
+
}
|
|
484
|
+
this.taskStateManager.resumeFromState(saved);
|
|
485
|
+
if (this.mockMode) {
|
|
486
|
+
this.taskStateManager.setStatus('completed');
|
|
487
|
+
return {
|
|
488
|
+
success: true,
|
|
489
|
+
summary: `Resumed and completed task "${taskId}" in mock mode.`,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return await this.executeGoal(saved.objective);
|
|
493
|
+
}
|
|
432
494
|
getModifiedFiles() {
|
|
433
495
|
return Array.from(this.modifiedFiles);
|
|
434
496
|
}
|
|
@@ -703,6 +765,10 @@ Core Directives & Behavioral Guidelines:
|
|
|
703
765
|
\`\`\`
|
|
704
766
|
This keeps your reasoning crystal-clear, ensures plan progression, and syncs automatically with .ft/scratchpad.md.
|
|
705
767
|
|
|
768
|
+
24. SOCIAL MESSAGING & DM DISPATCH MANDATE (Instagram, WhatsApp, X, Email):
|
|
769
|
+
a. MISSING PARAMETERS: When asked to send a direct message or text (e.g. 'open instagram and send a text message to a user'): If the recipient username/handle or message content is missing or not provided in the goal prompt, NEVER conclude the task or report fake completion! You MUST call the \`ask_user\` tool in Step 1 or Step 2 to ask: "Who would you like to message, and what message should I send?"
|
|
770
|
+
b. REAL UI EXECUTION: For Instagram, navigate directly to the user's profile (\`https://www.instagram.com/<username>/\`) or chat, wait for the single-page application to render, click the "Message" button or focus the chat input, type the message, and hit Enter before concluding with task_completed.
|
|
771
|
+
|
|
706
772
|
${this.getScratchpadPromptContext()}
|
|
707
773
|
|
|
708
774
|
When returning tool calls, use standard OpenAI function calling format or JSON tool call payload format:
|
|
@@ -730,6 +796,21 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
|
|
|
730
796
|
}
|
|
731
797
|
async executeGoal(userGoal) {
|
|
732
798
|
this.resetContext();
|
|
799
|
+
this.taskStateManager.setObjective(userGoal);
|
|
800
|
+
const structuredPlan = this.taskPlanner.createPlan(userGoal);
|
|
801
|
+
this.taskStateManager.setPlan(structuredPlan.steps);
|
|
802
|
+
this.taskStateManager.setAvailableTools(this.toolRegistry.getAllTools().map((t) => t.name));
|
|
803
|
+
if (this.mockMode) {
|
|
804
|
+
for (let i = 1; i <= structuredPlan.steps.length; i++) {
|
|
805
|
+
this.taskStateManager.startStep(i);
|
|
806
|
+
this.taskStateManager.completeStep(i, `Completed step ${i} in mock mode`);
|
|
807
|
+
}
|
|
808
|
+
this.taskStateManager.setStatus('completed');
|
|
809
|
+
return {
|
|
810
|
+
success: true,
|
|
811
|
+
summary: `Mock execution completed successfully for goal: "${userGoal}".`,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
733
814
|
this.historyMessages.push({ role: 'system', content: this.systemPrompt() });
|
|
734
815
|
// Load persistent session history memory (minimum 3 sessions Context)
|
|
735
816
|
const recentSessionsMemory = getRecentSessionsSummary(3);
|
|
@@ -849,6 +930,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
|
|
|
849
930
|
while (step < this.maxSteps) {
|
|
850
931
|
step++;
|
|
851
932
|
this.currentStep = step;
|
|
933
|
+
this.taskStateManager.startStep(step);
|
|
852
934
|
const stepStartTime = Date.now();
|
|
853
935
|
const avgStepMs = stepDurations.length > 0
|
|
854
936
|
? stepDurations.reduce((a, b) => a + b, 0) / stepDurations.length
|
|
@@ -867,7 +949,7 @@ When returning tool calls, use standard OpenAI function calling format or JSON t
|
|
|
867
949
|
return await openai.chat.completions.create({
|
|
868
950
|
model,
|
|
869
951
|
messages: sanitizeMessages(this.historyMessages),
|
|
870
|
-
tools:
|
|
952
|
+
tools: this.toolRegistry.getAllTools().map((t) => ({ type: 'function', function: t })),
|
|
871
953
|
tool_choice: 'auto',
|
|
872
954
|
temperature: 0.1,
|
|
873
955
|
presence_penalty: 0.1,
|
|
@@ -1153,11 +1235,55 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1153
1235
|
actionDesc = `Launching app: ${args.app || ''} (${args.target || 'default'})`;
|
|
1154
1236
|
else if (name === 'app_action')
|
|
1155
1237
|
actionDesc = `Executing app action: ${args.app || ''} -> ${args.action || ''}`;
|
|
1238
|
+
else if (name === 'browser_navigate')
|
|
1239
|
+
actionDesc = `Navigating browser: ${args.url || ''}`;
|
|
1240
|
+
else if (name === 'browser_inspect')
|
|
1241
|
+
actionDesc = `Inspecting web page elements`;
|
|
1242
|
+
else if (name === 'browser_click')
|
|
1243
|
+
actionDesc = `Clicking web element: ${args.selectorOrText || ''}`;
|
|
1244
|
+
else if (name === 'browser_type')
|
|
1245
|
+
actionDesc = `Typing in web input: ${args.selectorOrText || ''}`;
|
|
1246
|
+
else if (name === 'browser_download')
|
|
1247
|
+
actionDesc = `Downloading file: ${args.urlOrSelector || ''}`;
|
|
1248
|
+
else if (name === 'browser_content')
|
|
1249
|
+
actionDesc = `Extracting web page content`;
|
|
1250
|
+
else if (name === 'computer_screenshot')
|
|
1251
|
+
actionDesc = `Capturing computer screenshot`;
|
|
1252
|
+
else if (name === 'computer_click')
|
|
1253
|
+
actionDesc = `Clicking coordinates (${args.x}, ${args.y})`;
|
|
1254
|
+
else if (name === 'computer_type')
|
|
1255
|
+
actionDesc = `Typing text keystrokes`;
|
|
1256
|
+
else if (name === 'computer_hotkey')
|
|
1257
|
+
actionDesc = `Pressing hotkey: ${args.keys || ''}`;
|
|
1258
|
+
else if (name === 'computer_open_app')
|
|
1259
|
+
actionDesc = `Opening app: ${args.name || ''}`;
|
|
1260
|
+
else if (name === 'computer_switch_app')
|
|
1261
|
+
actionDesc = `Switching to app: ${args.name || ''}`;
|
|
1262
|
+
else if (name === 'computer_scroll')
|
|
1263
|
+
actionDesc = `Scrolling ${args.direction || 'down'}`;
|
|
1156
1264
|
else if (name === 'task_completed')
|
|
1157
1265
|
actionDesc = `Task finished`;
|
|
1158
1266
|
const badge = chalk.bgCyan.black.bold(` 🛠️ TOOL: ${name} `);
|
|
1159
1267
|
const descText = actionDesc ? chalk.yellow.bold(` → ${actionDesc}`) : '';
|
|
1160
1268
|
console.log(`\n${badge}${descText} ${chalk.dim(JSON.stringify(args))}`);
|
|
1269
|
+
// Safety and human approval classification layer
|
|
1270
|
+
const risk = this.approvalManager.checkAction(name, args);
|
|
1271
|
+
if (risk.requiresApproval && !this.autoApprove) {
|
|
1272
|
+
this.taskStateManager.setPendingAction({
|
|
1273
|
+
id: `act_${Date.now()}`,
|
|
1274
|
+
tool: name,
|
|
1275
|
+
args,
|
|
1276
|
+
riskLevel: risk.level,
|
|
1277
|
+
riskReason: risk.reason,
|
|
1278
|
+
requestedAt: new Date().toISOString(),
|
|
1279
|
+
});
|
|
1280
|
+
const decision = await this.approvalManager.requestApproval(this.taskStateManager.getState().task_id, name, args, risk);
|
|
1281
|
+
this.taskStateManager.setPendingAction(null);
|
|
1282
|
+
if (!decision.approved) {
|
|
1283
|
+
this.taskStateManager.addObservation(name, `[ACTION BLOCKED: User rejected approval: ${decision.reason || 'Not approved'}]`, false);
|
|
1284
|
+
return { result: `[ACTION REJECTED BY USER]: ${decision.reason || 'User did not approve executing this action.'}` };
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1161
1287
|
switch (name) {
|
|
1162
1288
|
case 'read_file': {
|
|
1163
1289
|
const targetPath = String(args.path || '').trim();
|
|
@@ -1237,11 +1363,20 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1237
1363
|
const syntaxCheck = await checkFileSyntax(targetPath, this.cwd);
|
|
1238
1364
|
if (!syntaxCheck.valid) {
|
|
1239
1365
|
checkSpin.stop(chalk.red(`️ Checking for error: edited ${targetPath} — Compiler/Syntax Error!`));
|
|
1366
|
+
this.taskStateManager.recordError(`Syntax error in "${targetPath}": ${syntaxCheck.errors.join('; ')}`, 'write_file');
|
|
1240
1367
|
return {
|
|
1241
1368
|
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.`
|
|
1242
1369
|
};
|
|
1243
1370
|
}
|
|
1244
1371
|
checkSpin.stop(chalk.green(` Checking for error: edited ${targetPath} — Syntax & build clean.`));
|
|
1372
|
+
this.taskStateManager.recordArtifact({
|
|
1373
|
+
filename: path.basename(targetPath),
|
|
1374
|
+
path: targetPath,
|
|
1375
|
+
source: 'write_file',
|
|
1376
|
+
verified: syntaxCheck.valid,
|
|
1377
|
+
sizeBytes: Buffer.byteLength(content, 'utf-8'),
|
|
1378
|
+
});
|
|
1379
|
+
this.taskStateManager.addObservation('write_file', `Wrote ${numberOfLinesChanged} line(s) to ${targetPath}. Syntax clean: ${syntaxCheck.valid}`, syntaxCheck.valid, targetPath);
|
|
1245
1380
|
return { result: `Successfully wrote ${content.length} characters (${numberOfLinesChanged} lines changed) to "${targetPath}". Rationale: ${reason}. Syntax check passed cleanly.` };
|
|
1246
1381
|
}
|
|
1247
1382
|
case 'edit_file': {
|
|
@@ -1597,10 +1732,111 @@ You MUST output exactly ONE JSON code block per tool call. Do NOT describe what
|
|
|
1597
1732
|
}
|
|
1598
1733
|
return { result: 'No text provided to speak.' };
|
|
1599
1734
|
}
|
|
1735
|
+
case 'browser_navigate': {
|
|
1736
|
+
const targetUrl = String(args.url || '').trim();
|
|
1737
|
+
this.taskStateManager.setActiveUrl(targetUrl);
|
|
1738
|
+
this.taskStateManager.setActiveApplication('browser');
|
|
1739
|
+
const res = await this.browserController.navigate(targetUrl);
|
|
1740
|
+
this.taskStateManager.addObservation('browser_navigate', res.output, res.success, targetUrl);
|
|
1741
|
+
return { result: res.output };
|
|
1742
|
+
}
|
|
1743
|
+
case 'browser_inspect': {
|
|
1744
|
+
const res = await this.browserController.inspect_page();
|
|
1745
|
+
this.taskStateManager.addObservation('browser_inspect', res.summary, res.success, res.url);
|
|
1746
|
+
return { result: res.summary };
|
|
1747
|
+
}
|
|
1748
|
+
case 'browser_click': {
|
|
1749
|
+
const target = String(args.selectorOrText || args.target || '').trim();
|
|
1750
|
+
const res = await this.browserController.click(target);
|
|
1751
|
+
this.taskStateManager.addObservation('browser_click', res.output, res.success, target);
|
|
1752
|
+
return { result: res.output };
|
|
1753
|
+
}
|
|
1754
|
+
case 'browser_type': {
|
|
1755
|
+
const target = String(args.selectorOrText || args.target || '').trim();
|
|
1756
|
+
const text = String(args.text || '');
|
|
1757
|
+
const res = await this.browserController.type(target, text);
|
|
1758
|
+
this.taskStateManager.addObservation('browser_type', res.output, res.success, target);
|
|
1759
|
+
return { result: res.output };
|
|
1760
|
+
}
|
|
1761
|
+
case 'browser_download': {
|
|
1762
|
+
const target = String(args.urlOrSelector || args.url || '').trim();
|
|
1763
|
+
const destPath = args.destPath ? String(args.destPath) : undefined;
|
|
1764
|
+
const res = await this.browserController.download_file(target, destPath);
|
|
1765
|
+
if (res.success && res.details?.path) {
|
|
1766
|
+
const vRes = await this.actionVerifier.verifyFileCreated(res.details.path);
|
|
1767
|
+
const artifact = this.taskStateManager.recordArtifact({
|
|
1768
|
+
filename: res.details.filename || path.basename(res.details.path),
|
|
1769
|
+
path: res.details.path,
|
|
1770
|
+
source: target,
|
|
1771
|
+
application: 'browser',
|
|
1772
|
+
verified: vRes.verified,
|
|
1773
|
+
sizeBytes: res.details.sizeBytes,
|
|
1774
|
+
});
|
|
1775
|
+
this.taskStateManager.addObservation('browser_download', `${res.output} (Verified on disk: ${vRes.verified ? 'YES' : 'NO'})`, vRes.verified, target);
|
|
1776
|
+
return { result: `${res.output}\nTracked Artifact ID: ${artifact.id} (${artifact.path})` };
|
|
1777
|
+
}
|
|
1778
|
+
this.taskStateManager.addObservation('browser_download', res.output, false, target);
|
|
1779
|
+
return { result: res.output };
|
|
1780
|
+
}
|
|
1781
|
+
case 'browser_content': {
|
|
1782
|
+
const res = await this.browserController.extract_content(args.selector);
|
|
1783
|
+
this.taskStateManager.addObservation('browser_content', `Extracted content (${res.output.length} chars)`, res.success);
|
|
1784
|
+
return { result: res.output };
|
|
1785
|
+
}
|
|
1786
|
+
case 'computer_screenshot': {
|
|
1787
|
+
const res = await this.computerController.screenshot();
|
|
1788
|
+
this.taskStateManager.addObservation('computer_screenshot', res.output, res.success);
|
|
1789
|
+
return { result: res.output };
|
|
1790
|
+
}
|
|
1791
|
+
case 'computer_click': {
|
|
1792
|
+
const x = Number(args.x) || 0;
|
|
1793
|
+
const y = Number(args.y) || 0;
|
|
1794
|
+
const res = args.double
|
|
1795
|
+
? await this.computerController.double_click(x, y)
|
|
1796
|
+
: await this.computerController.click(x, y, args.button || 'left');
|
|
1797
|
+
this.taskStateManager.addObservation('computer_click', res.output, res.success, `(${x}, ${y})`);
|
|
1798
|
+
return { result: res.output };
|
|
1799
|
+
}
|
|
1800
|
+
case 'computer_type': {
|
|
1801
|
+
const text = String(args.text || '');
|
|
1802
|
+
const res = await this.computerController.type(text);
|
|
1803
|
+
this.taskStateManager.addObservation('computer_type', res.output, res.success);
|
|
1804
|
+
return { result: res.output };
|
|
1805
|
+
}
|
|
1806
|
+
case 'computer_hotkey': {
|
|
1807
|
+
const keys = String(args.keys || args.key || '');
|
|
1808
|
+
const res = await this.computerController.hotkey(keys);
|
|
1809
|
+
this.taskStateManager.addObservation('computer_hotkey', res.output, res.success, keys);
|
|
1810
|
+
return { result: res.output };
|
|
1811
|
+
}
|
|
1812
|
+
case 'computer_open_app': {
|
|
1813
|
+
const name = String(args.name || args.app || '').trim();
|
|
1814
|
+
const target = args.target ? String(args.target) : undefined;
|
|
1815
|
+
this.taskStateManager.setActiveApplication(name);
|
|
1816
|
+
const res = await this.computerController.open_application(name, target);
|
|
1817
|
+
this.taskStateManager.addObservation('computer_open_app', res.output, res.success, name);
|
|
1818
|
+
return { result: res.output };
|
|
1819
|
+
}
|
|
1820
|
+
case 'computer_switch_app': {
|
|
1821
|
+
const name = String(args.name || args.app || '').trim();
|
|
1822
|
+
this.taskStateManager.setActiveApplication(name);
|
|
1823
|
+
const res = await this.computerController.switch_application(name);
|
|
1824
|
+
this.taskStateManager.addObservation('computer_switch_app', res.output, res.success, name);
|
|
1825
|
+
return { result: res.output };
|
|
1826
|
+
}
|
|
1827
|
+
case 'computer_scroll': {
|
|
1828
|
+
const dir = (args.direction || 'down');
|
|
1829
|
+
const amount = Number(args.amount) || 4;
|
|
1830
|
+
const res = await this.computerController.scroll(dir, amount);
|
|
1831
|
+
this.taskStateManager.addObservation('computer_scroll', res.output, res.success);
|
|
1832
|
+
return { result: res.output };
|
|
1833
|
+
}
|
|
1600
1834
|
case 'task_completed': {
|
|
1601
1835
|
const summaryStr = String(args.summary || 'Task completed successfully.');
|
|
1602
1836
|
speakText(summaryStr, { async: true });
|
|
1603
1837
|
cleanupScreenshots();
|
|
1838
|
+
this.taskStateManager.setStatus('completed');
|
|
1839
|
+
this.taskStateManager.completeStep(this.currentStep, summaryStr);
|
|
1604
1840
|
return { result: summaryStr };
|
|
1605
1841
|
}
|
|
1606
1842
|
default:
|