ft-scout 9.0.1 → 9.0.2
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 +233 -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 +19 -6
- 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/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
package/web/app.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
|
-
FrontTerrain Scout — Multi-Page Application Engine v9.0.
|
|
2
|
+
FrontTerrain Scout — Multi-Page Application Engine v9.0.2
|
|
3
3
|
========================================================================== */
|
|
4
4
|
|
|
5
5
|
const FIREBASE_CONFIG = {
|
|
@@ -185,14 +185,35 @@ async function checkPaymentRedirectCallback() {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// Dynamic Pricing Loader & Admin Navigation Hook
|
|
188
|
+
function loadPricingFromStorage() {
|
|
189
|
+
try {
|
|
190
|
+
const stored = localStorage.getItem('scout_pricing_config');
|
|
191
|
+
if (stored) {
|
|
192
|
+
const parsed = JSON.parse(stored);
|
|
193
|
+
if (parsed && typeof parsed === 'object' && parsed.pro) {
|
|
194
|
+
state.pricing = { ...state.pricing, ...parsed };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch { }
|
|
198
|
+
}
|
|
199
|
+
|
|
188
200
|
async function loadDynamicPricing() {
|
|
201
|
+
// 1. Instantly apply locally saved admin pricing configuration
|
|
202
|
+
loadPricingFromStorage();
|
|
203
|
+
renderTiersUI();
|
|
204
|
+
|
|
205
|
+
// 2. Fetch fresh server pricing if connected
|
|
189
206
|
try {
|
|
190
207
|
const res = await fetch('/api/pricing');
|
|
191
208
|
if (res.ok) {
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
const contentType = res.headers.get('content-type') || '';
|
|
210
|
+
if (contentType.includes('application/json')) {
|
|
211
|
+
const data = await res.json();
|
|
212
|
+
if (data && data.success && data.pricing) {
|
|
213
|
+
state.pricing = data.pricing;
|
|
214
|
+
localStorage.setItem('scout_pricing_config', JSON.stringify(data.pricing));
|
|
215
|
+
renderTiersUI();
|
|
216
|
+
}
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
} catch { }
|
|
@@ -240,9 +261,18 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
240
261
|
await loadDynamicPricing();
|
|
241
262
|
await checkPaymentRedirectCallback();
|
|
242
263
|
|
|
264
|
+
window.addEventListener('storage', (e) => {
|
|
265
|
+
if (e.key === 'scout_pricing_config') {
|
|
266
|
+
loadPricingFromStorage();
|
|
267
|
+
renderTiersUI();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
243
271
|
if (getEl('pacman-canvas')) {
|
|
244
272
|
initPacmanGame();
|
|
245
273
|
}
|
|
274
|
+
|
|
275
|
+
initAutonomousAgentMonitor();
|
|
246
276
|
});
|
|
247
277
|
|
|
248
278
|
// Time Helper Function for Dynamic Relative Timestamps
|
|
@@ -350,6 +380,7 @@ function loadTierAndCreditsFromStorage() {
|
|
|
350
380
|
state.customApiKey = localStorage.getItem('scout_byok_key') || '';
|
|
351
381
|
state.customBaseUrl = localStorage.getItem('scout_byok_endpoint') || '';
|
|
352
382
|
|
|
383
|
+
loadPricingFromStorage();
|
|
353
384
|
checkSubscriptionExpiration();
|
|
354
385
|
}
|
|
355
386
|
|
|
@@ -376,14 +407,14 @@ function openCheckoutModal(targetTier) {
|
|
|
376
407
|
let quota = '100 Credits / mo';
|
|
377
408
|
|
|
378
409
|
if (targetTier === 'pro') {
|
|
379
|
-
const isOffer = p.pro?.offerActive && !isAnnual && p.pro?.
|
|
380
|
-
const
|
|
381
|
-
price =
|
|
410
|
+
const isOffer = p.pro?.offerActive && !isAnnual && p.pro?.offerPriceUSD !== undefined;
|
|
411
|
+
const usd = isOffer ? p.pro.offerPriceUSD : (isAnnual ? (p.pro?.annualPriceUSD ?? 0.79) : (p.pro?.monthlyPriceUSD ?? 0.99));
|
|
412
|
+
price = usd.toString();
|
|
382
413
|
quota = `${(p.pro?.credits || 1000).toLocaleString()} Credits / mo`;
|
|
383
414
|
} else if (targetTier === 'enterprise') {
|
|
384
|
-
const isOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.
|
|
385
|
-
const
|
|
386
|
-
price =
|
|
415
|
+
const isOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.offerPriceUSD !== undefined;
|
|
416
|
+
const usd = isOffer ? p.enterprise.offerPriceUSD : (isAnnual ? (p.enterprise?.annualPriceUSD ?? 39) : (p.enterprise?.monthlyPriceUSD ?? 49));
|
|
417
|
+
price = usd.toString();
|
|
387
418
|
quota = `${(p.enterprise?.credits || 10000).toLocaleString()} Credits / mo`;
|
|
388
419
|
}
|
|
389
420
|
|
|
@@ -391,7 +422,7 @@ function openCheckoutModal(targetTier) {
|
|
|
391
422
|
if (getEl('checkout-billing-cycle')) getEl('checkout-billing-cycle').innerText = isAnnual ? 'Billed Annually (Save more)' : 'Billed Monthly';
|
|
392
423
|
if (getEl('checkout-plan-price')) getEl('checkout-plan-price').innerText = price;
|
|
393
424
|
const checkoutCurrency = document.querySelector('.checkout-price-display .price-currency');
|
|
394
|
-
if (checkoutCurrency) checkoutCurrency.innerText = '
|
|
425
|
+
if (checkoutCurrency) checkoutCurrency.innerText = '$';
|
|
395
426
|
if (getEl('checkout-price-period')) getEl('checkout-price-period').innerText = isAnnual ? '/ mo (annual)' : '/ mo';
|
|
396
427
|
if (getEl('checkout-quota-val')) getEl('checkout-quota-val').innerText = quota;
|
|
397
428
|
|
|
@@ -450,37 +481,37 @@ function confirmScheduleDowngrade() {
|
|
|
450
481
|
|
|
451
482
|
function confirmImmediateDowngrade() {
|
|
452
483
|
const targetTier = pendingDowngradeTier || 'free';
|
|
484
|
+
const previousPaidTier = state.paidTier || (state.currentTier !== 'free' ? state.currentTier : null);
|
|
485
|
+
|
|
453
486
|
state.currentTier = targetTier;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
487
|
+
if (previousPaidTier && !isSubscriptionExpired()) {
|
|
488
|
+
state.paidTier = previousPaidTier;
|
|
489
|
+
} else {
|
|
490
|
+
state.paidTier = targetTier === 'free' ? null : targetTier;
|
|
491
|
+
}
|
|
492
|
+
state.cancelAtPeriodEnd = true;
|
|
493
|
+
state.pendingDowngrade = targetTier;
|
|
457
494
|
state.creditsTotal = targetTier === 'pro' ? 1000 : (targetTier === 'enterprise' ? 10000 : 100);
|
|
458
|
-
|
|
459
|
-
const now = new Date();
|
|
460
|
-
state.renewAt = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0).toISOString();
|
|
495
|
+
|
|
461
496
|
saveSessionToStorage();
|
|
462
497
|
if (state.serverConnected) syncAuthWithServer();
|
|
463
498
|
closeDowngradeModal();
|
|
464
499
|
renderAllDynamicComponents();
|
|
465
|
-
showToast(`
|
|
500
|
+
showToast(`Switched to ${TIER_DETAILS[targetTier].name}. You can resume ${TIER_DETAILS[previousPaidTier || 'pro']?.name || 'your plan'} anytime before ${formatRenewDate(state.renewAt)} without paying again!`, 'info');
|
|
466
501
|
}
|
|
467
502
|
|
|
468
503
|
function resumeSubscription() {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
state.creditsTotal = state.currentTier === 'pro' ? 1000 : 10000;
|
|
473
|
-
} else {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
504
|
+
const resumeTier = (state.paidTier && state.paidTier !== 'free' && !isSubscriptionExpired()) ? state.paidTier : (state.currentTier !== 'free' ? state.currentTier : 'pro');
|
|
505
|
+
state.currentTier = resumeTier;
|
|
506
|
+
state.paidTier = resumeTier;
|
|
477
507
|
state.cancelAtPeriodEnd = false;
|
|
478
508
|
state.pendingDowngrade = null;
|
|
479
|
-
state.
|
|
509
|
+
state.creditsTotal = resumeTier === 'pro' ? 1000 : (resumeTier === 'enterprise' ? 10000 : 100);
|
|
510
|
+
|
|
480
511
|
saveSessionToStorage();
|
|
481
512
|
if (state.serverConnected) syncAuthWithServer();
|
|
482
513
|
renderAllDynamicComponents();
|
|
483
|
-
showToast(`🎉 Subscription resumed! Your ${TIER_DETAILS[
|
|
514
|
+
showToast(`🎉 Subscription resumed! Your ${TIER_DETAILS[resumeTier].name} is active again with no extra charge.`, 'success');
|
|
484
515
|
}
|
|
485
516
|
|
|
486
517
|
// Render and initialize Razorpay button UI
|
|
@@ -1050,7 +1081,7 @@ function initTerminalSimulator() {
|
|
|
1050
1081
|
`;
|
|
1051
1082
|
} else if (lower.includes('status')) {
|
|
1052
1083
|
response.innerHTML = `
|
|
1053
|
-
<span class="term-green">✔ Scout CLI Engine: v9.0.
|
|
1084
|
+
<span class="term-green">✔ Scout CLI Engine: v9.0.2 Online</span><br>
|
|
1054
1085
|
↳ User: <strong>${escapeHtml(state.user ? state.user.name : 'Guest Session')}</strong><br>
|
|
1055
1086
|
↳ Active Subscription: <strong>${escapeHtml(TIER_DETAILS[state.currentTier].name)}</strong><br>
|
|
1056
1087
|
↳ Credit Quota: <strong>${state.byokEnabled ? 'BYOK Mode (Unlimited)' : `${state.creditsUsed} / ${state.creditsTotal} Credits Used`}</strong><br>
|
|
@@ -1295,33 +1326,33 @@ function renderTiersUI() {
|
|
|
1295
1326
|
|
|
1296
1327
|
const isAnnual = state.billingCycle === 'annual';
|
|
1297
1328
|
const p = state.pricing || {};
|
|
1298
|
-
const isProOffer = p.pro?.offerActive && !isAnnual && p.pro?.
|
|
1299
|
-
const isEntOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.
|
|
1329
|
+
const isProOffer = p.pro?.offerActive && !isAnnual && p.pro?.offerPriceUSD !== undefined;
|
|
1330
|
+
const isEntOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.offerPriceUSD !== undefined;
|
|
1300
1331
|
|
|
1301
|
-
const
|
|
1302
|
-
const
|
|
1332
|
+
const proUSD = isProOffer ? p.pro.offerPriceUSD : (isAnnual ? (p.pro?.annualPriceUSD ?? 0.79) : (p.pro?.monthlyPriceUSD ?? 0.99));
|
|
1333
|
+
const entUSD = isEntOffer ? p.enterprise.offerPriceUSD : (isAnnual ? (p.enterprise?.annualPriceUSD ?? 39) : (p.enterprise?.monthlyPriceUSD ?? 49));
|
|
1303
1334
|
|
|
1304
1335
|
if (getEl('price-pro-val')) {
|
|
1305
|
-
if (isProOffer && p.pro?.
|
|
1306
|
-
getEl('price-pro-val').innerHTML = `<s style="font-size: 0.55em; opacity: 0.55; margin-right: 6px;"
|
|
1336
|
+
if (isProOffer && p.pro?.monthlyPriceUSD) {
|
|
1337
|
+
getEl('price-pro-val').innerHTML = `<s style="font-size: 0.55em; opacity: 0.55; margin-right: 6px;">$${p.pro.monthlyPriceUSD}</s>$${proUSD}`;
|
|
1307
1338
|
} else {
|
|
1308
|
-
getEl('price-pro-val').innerText =
|
|
1339
|
+
getEl('price-pro-val').innerText = proUSD;
|
|
1309
1340
|
}
|
|
1310
1341
|
}
|
|
1311
|
-
if (getEl('price-pro-period')) getEl('price-pro-period').innerText = isProOffer ? `/ month (${p.pro?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '
|
|
1342
|
+
if (getEl('price-pro-period')) getEl('price-pro-period').innerText = isProOffer ? `/ month (${p.pro?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '(billed annually)' : '/ month');
|
|
1312
1343
|
const proCurrency = document.querySelector('#tier-card-pro .price-currency');
|
|
1313
|
-
if (proCurrency) proCurrency.innerText = isProOffer ? '' : '
|
|
1344
|
+
if (proCurrency) proCurrency.innerText = isProOffer ? '' : '$';
|
|
1314
1345
|
|
|
1315
1346
|
if (getEl('price-ent-val')) {
|
|
1316
|
-
if (isEntOffer && p.enterprise?.
|
|
1317
|
-
getEl('price-ent-val').innerHTML = `<s style="font-size: 0.55em; opacity: 0.55; margin-right: 6px;"
|
|
1347
|
+
if (isEntOffer && p.enterprise?.monthlyPriceUSD) {
|
|
1348
|
+
getEl('price-ent-val').innerHTML = `<s style="font-size: 0.55em; opacity: 0.55; margin-right: 6px;">$${p.enterprise.monthlyPriceUSD}</s>$${entUSD}`;
|
|
1318
1349
|
} else {
|
|
1319
|
-
getEl('price-ent-val').innerText =
|
|
1350
|
+
getEl('price-ent-val').innerText = entUSD;
|
|
1320
1351
|
}
|
|
1321
1352
|
}
|
|
1322
|
-
if (getEl('price-ent-period')) getEl('price-ent-period').innerText = isEntOffer ? `/ month (${p.enterprise?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '
|
|
1353
|
+
if (getEl('price-ent-period')) getEl('price-ent-period').innerText = isEntOffer ? `/ month (${p.enterprise?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '(billed annually)' : '/ month');
|
|
1323
1354
|
const entCurrency = document.querySelector('#tier-card-enterprise .price-currency');
|
|
1324
|
-
if (entCurrency) entCurrency.innerText = isEntOffer ? '' : '
|
|
1355
|
+
if (entCurrency) entCurrency.innerText = isEntOffer ? '' : '$';
|
|
1325
1356
|
|
|
1326
1357
|
['free', 'pro', 'enterprise'].forEach((t) => {
|
|
1327
1358
|
const card = getEl(`tier-card-${t}`);
|
|
@@ -1354,7 +1385,7 @@ function renderTiersUI() {
|
|
|
1354
1385
|
} else if (t === 'pro') {
|
|
1355
1386
|
if (state.paidTier === 'pro' && !isSubscriptionExpired()) {
|
|
1356
1387
|
btn.className = 'btn btn-primary btn-block btn-select-tier';
|
|
1357
|
-
btn.innerHTML = `<i class="fa-solid fa-rotate-left"></i>
|
|
1388
|
+
btn.innerHTML = `<i class="fa-solid fa-rotate-left"></i> Resume Pro Plan (No Extra Charge)`;
|
|
1358
1389
|
btn.disabled = false;
|
|
1359
1390
|
} else if (state.currentTier === 'enterprise') {
|
|
1360
1391
|
if (isScheduledCancel && state.pendingDowngrade === 'pro') {
|
|
@@ -1374,7 +1405,7 @@ function renderTiersUI() {
|
|
|
1374
1405
|
} else if (t === 'enterprise') {
|
|
1375
1406
|
if (state.paidTier === 'enterprise' && !isSubscriptionExpired()) {
|
|
1376
1407
|
btn.className = 'btn btn-warning btn-block btn-select-tier';
|
|
1377
|
-
btn.innerHTML = `<i class="fa-solid fa-rotate-left"></i>
|
|
1408
|
+
btn.innerHTML = `<i class="fa-solid fa-rotate-left"></i> Resume Enterprise (No Extra Charge)`;
|
|
1378
1409
|
btn.disabled = false;
|
|
1379
1410
|
} else {
|
|
1380
1411
|
btn.className = 'btn btn-outline btn-block btn-select-tier';
|
|
@@ -2301,3 +2332,203 @@ function initPacmanGame() {
|
|
|
2301
2332
|
|
|
2302
2333
|
render();
|
|
2303
2334
|
}
|
|
2335
|
+
|
|
2336
|
+
/* ==========================================================================
|
|
2337
|
+
Autonomous Agent Execution Monitor & Human-in-the-Loop Controller
|
|
2338
|
+
========================================================================== */
|
|
2339
|
+
let agentMonitorPollTimer = null;
|
|
2340
|
+
let currentPendingApprovalId = null;
|
|
2341
|
+
|
|
2342
|
+
function initAutonomousAgentMonitor() {
|
|
2343
|
+
const monitorPanel = getEl('agent-monitor-panel');
|
|
2344
|
+
if (!monitorPanel) return;
|
|
2345
|
+
|
|
2346
|
+
const taskForm = getEl('agent-task-form');
|
|
2347
|
+
const goalInput = getEl('agent-goal-input');
|
|
2348
|
+
const autoApproveToggle = getEl('agent-auto-approve-toggle');
|
|
2349
|
+
const btnRefresh = getEl('btn-refresh-agent-state');
|
|
2350
|
+
const btnApprove = getEl('btn-approve-action');
|
|
2351
|
+
const btnDeny = getEl('btn-deny-action');
|
|
2352
|
+
|
|
2353
|
+
async function fetchAgentState() {
|
|
2354
|
+
try {
|
|
2355
|
+
const apiBase = state.apiBaseUrl || '';
|
|
2356
|
+
const res = await fetch(`${apiBase}/api/agent/state`);
|
|
2357
|
+
if (!res.ok) return;
|
|
2358
|
+
const data = await res.json();
|
|
2359
|
+
if (!data.success) return;
|
|
2360
|
+
|
|
2361
|
+
renderAgentState(data.state, data.pendingApprovals, data.scratchpad);
|
|
2362
|
+
} catch (err) {
|
|
2363
|
+
console.warn('Agent state poll error:', err);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
function renderAgentState(taskState, pendingApprovals, scratchpad) {
|
|
2368
|
+
if (!taskState) return;
|
|
2369
|
+
|
|
2370
|
+
// Status badge
|
|
2371
|
+
const badge = getEl('agent-status-badge');
|
|
2372
|
+
if (badge) {
|
|
2373
|
+
const st = (taskState.status || 'idle').toUpperCase();
|
|
2374
|
+
badge.innerText = st;
|
|
2375
|
+
badge.className = 'badge';
|
|
2376
|
+
if (st === 'COMPLETED') badge.classList.add('badge-success');
|
|
2377
|
+
else if (st === 'EXECUTING' || st === 'PLANNING') badge.classList.add('badge-accent');
|
|
2378
|
+
else if (st === 'WAITING_APPROVAL') badge.classList.add('badge-warning');
|
|
2379
|
+
else if (st === 'FAILED') badge.classList.add('badge-danger');
|
|
2380
|
+
else badge.classList.add('badge-outline');
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
// Step counter
|
|
2384
|
+
const stepCounter = getEl('agent-step-counter');
|
|
2385
|
+
if (stepCounter) {
|
|
2386
|
+
stepCounter.innerText = `Step ${taskState.currentStep || 0} / ${taskState.maxSteps || 30}`;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// Plan progress & list
|
|
2390
|
+
const planList = getEl('agent-plan-list');
|
|
2391
|
+
const planText = getEl('agent-plan-progress-text');
|
|
2392
|
+
if (planList && taskState.plan && taskState.plan.length > 0) {
|
|
2393
|
+
const completedCount = taskState.plan.filter(p => p.status === 'completed').length;
|
|
2394
|
+
if (planText) planText.innerText = `${completedCount} / ${taskState.plan.length} steps completed`;
|
|
2395
|
+
|
|
2396
|
+
planList.innerHTML = taskState.plan.map(step => {
|
|
2397
|
+
let icon = '<i class="fa-regular fa-circle text-tertiary"></i>';
|
|
2398
|
+
if (step.status === 'completed') icon = '<i class="fa-solid fa-circle-check text-success"></i>';
|
|
2399
|
+
else if (step.status === 'in_progress') icon = '<i class="fa-solid fa-spinner fa-spin text-primary"></i>';
|
|
2400
|
+
else if (step.status === 'failed') icon = '<i class="fa-solid fa-circle-xmark text-danger"></i>';
|
|
2401
|
+
|
|
2402
|
+
return `
|
|
2403
|
+
<div class="agent-plan-step-item step-${step.status}">
|
|
2404
|
+
${icon}
|
|
2405
|
+
<span style="font-weight: 500;">Step ${step.id}: ${escapeHtml(step.description)}</span>
|
|
2406
|
+
${step.toolName ? `<span class="badge" style="font-size: 10px; margin-left: auto;">${escapeHtml(step.toolName)}</span>` : ''}
|
|
2407
|
+
</div>
|
|
2408
|
+
`;
|
|
2409
|
+
}).join('');
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// Pending Approvals Banner
|
|
2413
|
+
const banner = getEl('agent-approval-banner');
|
|
2414
|
+
if (banner) {
|
|
2415
|
+
if (pendingApprovals && pendingApprovals.length > 0) {
|
|
2416
|
+
const req = pendingApprovals[0];
|
|
2417
|
+
currentPendingApprovalId = req.id;
|
|
2418
|
+
banner.classList.remove('hidden');
|
|
2419
|
+
const riskBadge = getEl('approval-risk-badge');
|
|
2420
|
+
if (riskBadge) {
|
|
2421
|
+
riskBadge.innerText = `⚠️ ${req.riskLevel.toUpperCase()} RISK ACTION AWAITING APPROVAL`;
|
|
2422
|
+
riskBadge.style.background = req.riskLevel === 'CRITICAL' ? '#dc2626' : '#ef4444';
|
|
2423
|
+
}
|
|
2424
|
+
const actTitle = getEl('approval-action-title');
|
|
2425
|
+
if (actTitle) actTitle.innerText = `Action: ${req.action} on ${req.target || 'system'}`;
|
|
2426
|
+
const actDesc = getEl('approval-action-desc');
|
|
2427
|
+
if (actDesc) actDesc.innerText = req.description;
|
|
2428
|
+
} else {
|
|
2429
|
+
banner.classList.add('hidden');
|
|
2430
|
+
currentPendingApprovalId = null;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// Scratchpad view
|
|
2435
|
+
const scratchpadView = getEl('agent-scratchpad-view');
|
|
2436
|
+
if (scratchpadView && scratchpad) {
|
|
2437
|
+
scratchpadView.innerText = scratchpad;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
// Artifacts list
|
|
2441
|
+
const artList = getEl('agent-artifacts-list');
|
|
2442
|
+
if (artList) {
|
|
2443
|
+
if (taskState.artifacts && taskState.artifacts.length > 0) {
|
|
2444
|
+
artList.innerHTML = taskState.artifacts.map(a => `
|
|
2445
|
+
<div style="display: flex; align-items: center; justify-content: space-between; padding: 4px 8px; background: rgba(255,255,255,0.04); border-radius: 4px;">
|
|
2446
|
+
<span><i class="fa-solid fa-file-code text-accent"></i> <strong>${escapeHtml(a.name)}</strong></span>
|
|
2447
|
+
<span style="color: var(--text-tertiary); font-size: 11px;">${escapeHtml(a.type)}</span>
|
|
2448
|
+
</div>
|
|
2449
|
+
`).join('');
|
|
2450
|
+
} else {
|
|
2451
|
+
artList.innerHTML = '<span style="color: var(--text-tertiary);">No artifacts produced yet.</span>';
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
taskForm?.addEventListener('submit', async (e) => {
|
|
2457
|
+
e.preventDefault();
|
|
2458
|
+
const goal = goalInput?.value?.trim();
|
|
2459
|
+
if (!goal) return;
|
|
2460
|
+
const autoApprove = autoApproveToggle?.checked || false;
|
|
2461
|
+
|
|
2462
|
+
const btn = getEl('btn-start-agent');
|
|
2463
|
+
if (btn) {
|
|
2464
|
+
btn.disabled = true;
|
|
2465
|
+
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Initializing...';
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
try {
|
|
2469
|
+
const apiBase = state.apiBaseUrl || '';
|
|
2470
|
+
const res = await fetch(`${apiBase}/api/agent/task`, {
|
|
2471
|
+
method: 'POST',
|
|
2472
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2473
|
+
body: JSON.stringify({ goal, autoApprove }),
|
|
2474
|
+
});
|
|
2475
|
+
const data = await res.json();
|
|
2476
|
+
if (data.success) {
|
|
2477
|
+
showToast(`Autonomous Agent started! Task ID: ${data.taskId.slice(0, 8)}...`, 'success');
|
|
2478
|
+
setTimeout(fetchAgentState, 600);
|
|
2479
|
+
} else {
|
|
2480
|
+
showToast(data.error || 'Failed to start agent task', 'error');
|
|
2481
|
+
}
|
|
2482
|
+
} catch (err) {
|
|
2483
|
+
showToast('Failed to connect to agent server', 'error');
|
|
2484
|
+
} finally {
|
|
2485
|
+
if (btn) {
|
|
2486
|
+
btn.disabled = false;
|
|
2487
|
+
btn.innerHTML = '<i class="fa-solid fa-play"></i> Run Agent';
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
});
|
|
2491
|
+
|
|
2492
|
+
btnRefresh?.addEventListener('click', () => {
|
|
2493
|
+
fetchAgentState();
|
|
2494
|
+
showToast('Agent state refreshed', 'info');
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2497
|
+
btnApprove?.addEventListener('click', async () => {
|
|
2498
|
+
if (!currentPendingApprovalId) return;
|
|
2499
|
+
try {
|
|
2500
|
+
const apiBase = state.apiBaseUrl || '';
|
|
2501
|
+
await fetch(`${apiBase}/api/agent/approval`, {
|
|
2502
|
+
method: 'POST',
|
|
2503
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2504
|
+
body: JSON.stringify({ requestId: currentPendingApprovalId, approved: true }),
|
|
2505
|
+
});
|
|
2506
|
+
showToast('Action approved! Agent resuming execution...', 'success');
|
|
2507
|
+
fetchAgentState();
|
|
2508
|
+
} catch {
|
|
2509
|
+
showToast('Failed to send approval', 'error');
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
btnDeny?.addEventListener('click', async () => {
|
|
2514
|
+
if (!currentPendingApprovalId) return;
|
|
2515
|
+
try {
|
|
2516
|
+
const apiBase = state.apiBaseUrl || '';
|
|
2517
|
+
await fetch(`${apiBase}/api/agent/approval`, {
|
|
2518
|
+
method: 'POST',
|
|
2519
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2520
|
+
body: JSON.stringify({ requestId: currentPendingApprovalId, approved: false, reason: 'Denied by user from Dashboard Web UI' }),
|
|
2521
|
+
});
|
|
2522
|
+
showToast('Action denied. Agent informed to seek alternate path.', 'warning');
|
|
2523
|
+
fetchAgentState();
|
|
2524
|
+
} catch {
|
|
2525
|
+
showToast('Failed to send denial', 'error');
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2528
|
+
|
|
2529
|
+
// Initial fetch & polling every 2.5s
|
|
2530
|
+
fetchAgentState();
|
|
2531
|
+
if (agentMonitorPollTimer) clearInterval(agentMonitorPollTimer);
|
|
2532
|
+
agentMonitorPollTimer = setInterval(fetchAgentState, 2500);
|
|
2533
|
+
}
|
|
2534
|
+
|
package/web/styles.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
|
-
FrontTerrain Scout — Design System & Visual Engine v9.0.
|
|
2
|
+
FrontTerrain Scout — Design System & Visual Engine v9.0.2
|
|
3
3
|
Inspired by HappyRobot, Linear.app & Vercel aesthetics
|
|
4
4
|
========================================================================== */
|
|
5
5
|
|
|
@@ -1063,7 +1063,7 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
1063
1063
|
.footer-links a:hover { color: var(--hr-rust); }
|
|
1064
1064
|
|
|
1065
1065
|
/* ==========================================================================
|
|
1066
|
-
Visual Perception & Live Screen Sharing Engine v9.0.
|
|
1066
|
+
Visual Perception & Live Screen Sharing Engine v9.0.2
|
|
1067
1067
|
========================================================================== */
|
|
1068
1068
|
.vision-banner {
|
|
1069
1069
|
margin-bottom: 28px;
|
|
@@ -1543,3 +1543,80 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
1543
1543
|
font-size: 12px;
|
|
1544
1544
|
}
|
|
1545
1545
|
}
|
|
1546
|
+
|
|
1547
|
+
/* ==========================================================================
|
|
1548
|
+
Autonomous Agent Execution Monitor & Human-in-the-Loop Styles
|
|
1549
|
+
========================================================================== */
|
|
1550
|
+
.agent-monitor-card {
|
|
1551
|
+
border: 1px solid rgba(0, 229, 255, 0.2);
|
|
1552
|
+
position: relative;
|
|
1553
|
+
overflow: hidden;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
.agent-monitor-card::before {
|
|
1557
|
+
content: '';
|
|
1558
|
+
position: absolute;
|
|
1559
|
+
top: 0;
|
|
1560
|
+
left: 0;
|
|
1561
|
+
right: 0;
|
|
1562
|
+
height: 2px;
|
|
1563
|
+
background: linear-gradient(90deg, #38bdf8, #818cf8, #e15b37);
|
|
1564
|
+
opacity: 0.8;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
.agent-pulse-icon {
|
|
1568
|
+
width: 36px;
|
|
1569
|
+
height: 36px;
|
|
1570
|
+
border-radius: 8px;
|
|
1571
|
+
background: rgba(0, 229, 255, 0.12);
|
|
1572
|
+
border: 1px solid rgba(0, 229, 255, 0.3);
|
|
1573
|
+
display: flex;
|
|
1574
|
+
align-items: center;
|
|
1575
|
+
justify-content: center;
|
|
1576
|
+
font-size: 18px;
|
|
1577
|
+
animation: pulse-border 2.5s infinite;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
@keyframes pulse-border {
|
|
1581
|
+
0%, 100% { box-shadow: 0 0 0 0 rgba(0, 229, 255, 0.4); }
|
|
1582
|
+
50% { box-shadow: 0 0 12px 2px rgba(0, 229, 255, 0.3); }
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
.approval-alert-box {
|
|
1586
|
+
animation: slide-down 0.3s ease-out;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
@keyframes slide-down {
|
|
1590
|
+
from { opacity: 0; transform: translateY(-8px); }
|
|
1591
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
.agent-plan-step-item {
|
|
1595
|
+
display: flex;
|
|
1596
|
+
align-items: center;
|
|
1597
|
+
gap: 10px;
|
|
1598
|
+
padding: 6px 10px;
|
|
1599
|
+
border-radius: 4px;
|
|
1600
|
+
font-size: 0.82rem;
|
|
1601
|
+
background: rgba(255, 255, 255, 0.03);
|
|
1602
|
+
border: 1px solid rgba(255, 255, 255, 0.04);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
.agent-plan-step-item.step-completed {
|
|
1606
|
+
border-left: 3px solid #10b981;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
.agent-plan-step-item.step-in_progress {
|
|
1610
|
+
border-left: 3px solid #38bdf8;
|
|
1611
|
+
background: rgba(56, 189, 248, 0.08);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.agent-plan-step-item.step-failed {
|
|
1615
|
+
border-left: 3px solid #ef4444;
|
|
1616
|
+
background: rgba(239, 68, 68, 0.08);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
.agent-plan-step-item.step-pending {
|
|
1620
|
+
opacity: 0.65;
|
|
1621
|
+
}
|
|
1622
|
+
|