ft-scout 9.0.0 → 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/commands/plan.d.ts.map +1 -1
- package/bin/src/commands/plan.js +19 -5
- package/bin/src/commands/plan.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/bin/src/utils/payment.d.ts.map +1 -1
- package/bin/src/utils/payment.js +23 -3
- package/bin/src/utils/payment.js.map +1 -1
- package/bin/src/utils/pricing.d.ts.map +1 -1
- package/bin/src/utils/pricing.js +32 -10
- package/bin/src/utils/pricing.js.map +1 -1
- package/downloads/INVOICE_2026_ACME.pdf +1 -0
- package/package.json +1 -1
- package/web/app.js +294 -42
- 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,12 +407,14 @@ function openCheckoutModal(targetTier) {
|
|
|
376
407
|
let quota = '100 Credits / mo';
|
|
377
408
|
|
|
378
409
|
if (targetTier === 'pro') {
|
|
379
|
-
const
|
|
380
|
-
|
|
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();
|
|
381
413
|
quota = `${(p.pro?.credits || 1000).toLocaleString()} Credits / mo`;
|
|
382
414
|
} else if (targetTier === 'enterprise') {
|
|
383
|
-
const
|
|
384
|
-
|
|
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();
|
|
385
418
|
quota = `${(p.enterprise?.credits || 10000).toLocaleString()} Credits / mo`;
|
|
386
419
|
}
|
|
387
420
|
|
|
@@ -389,7 +422,7 @@ function openCheckoutModal(targetTier) {
|
|
|
389
422
|
if (getEl('checkout-billing-cycle')) getEl('checkout-billing-cycle').innerText = isAnnual ? 'Billed Annually (Save more)' : 'Billed Monthly';
|
|
390
423
|
if (getEl('checkout-plan-price')) getEl('checkout-plan-price').innerText = price;
|
|
391
424
|
const checkoutCurrency = document.querySelector('.checkout-price-display .price-currency');
|
|
392
|
-
if (checkoutCurrency) checkoutCurrency.innerText = '
|
|
425
|
+
if (checkoutCurrency) checkoutCurrency.innerText = '$';
|
|
393
426
|
if (getEl('checkout-price-period')) getEl('checkout-price-period').innerText = isAnnual ? '/ mo (annual)' : '/ mo';
|
|
394
427
|
if (getEl('checkout-quota-val')) getEl('checkout-quota-val').innerText = quota;
|
|
395
428
|
|
|
@@ -448,46 +481,48 @@ function confirmScheduleDowngrade() {
|
|
|
448
481
|
|
|
449
482
|
function confirmImmediateDowngrade() {
|
|
450
483
|
const targetTier = pendingDowngradeTier || 'free';
|
|
484
|
+
const previousPaidTier = state.paidTier || (state.currentTier !== 'free' ? state.currentTier : null);
|
|
485
|
+
|
|
451
486
|
state.currentTier = targetTier;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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;
|
|
455
494
|
state.creditsTotal = targetTier === 'pro' ? 1000 : (targetTier === 'enterprise' ? 10000 : 100);
|
|
456
|
-
|
|
457
|
-
const now = new Date();
|
|
458
|
-
state.renewAt = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0).toISOString();
|
|
495
|
+
|
|
459
496
|
saveSessionToStorage();
|
|
460
497
|
if (state.serverConnected) syncAuthWithServer();
|
|
461
498
|
closeDowngradeModal();
|
|
462
499
|
renderAllDynamicComponents();
|
|
463
|
-
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');
|
|
464
501
|
}
|
|
465
502
|
|
|
466
503
|
function resumeSubscription() {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
state.creditsTotal = state.currentTier === 'pro' ? 1000 : 10000;
|
|
471
|
-
} else {
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
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;
|
|
475
507
|
state.cancelAtPeriodEnd = false;
|
|
476
508
|
state.pendingDowngrade = null;
|
|
477
|
-
state.
|
|
509
|
+
state.creditsTotal = resumeTier === 'pro' ? 1000 : (resumeTier === 'enterprise' ? 10000 : 100);
|
|
510
|
+
|
|
478
511
|
saveSessionToStorage();
|
|
479
512
|
if (state.serverConnected) syncAuthWithServer();
|
|
480
513
|
renderAllDynamicComponents();
|
|
481
|
-
showToast(`🎉 Subscription resumed! Your ${TIER_DETAILS[
|
|
514
|
+
showToast(`🎉 Subscription resumed! Your ${TIER_DETAILS[resumeTier].name} is active again with no extra charge.`, 'success');
|
|
482
515
|
}
|
|
483
516
|
|
|
484
517
|
// Render and initialize Razorpay button UI
|
|
485
518
|
function renderRazorpayButton(targetTier) {
|
|
486
519
|
const isAnnual = state.billingCycle === 'annual';
|
|
487
520
|
const p = state.pricing || {};
|
|
521
|
+
const isProOffer = p.pro?.offerActive && !isAnnual && p.pro?.offerPriceINR !== undefined;
|
|
522
|
+
const isEntOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.offerPriceINR !== undefined;
|
|
488
523
|
const inrAmount = targetTier === 'enterprise'
|
|
489
|
-
? (isAnnual ? (p.enterprise?.annualPriceINR
|
|
490
|
-
: (isAnnual ? (p.pro?.annualPriceINR
|
|
524
|
+
? (isEntOffer ? p.enterprise.offerPriceINR : (isAnnual ? (p.enterprise?.annualPriceINR ?? 37990) : (p.enterprise?.monthlyPriceINR ?? 3999)))
|
|
525
|
+
: (isProOffer ? p.pro.offerPriceINR : (isAnnual ? (p.pro?.annualPriceINR ?? 45) : (p.pro?.monthlyPriceINR ?? 50)));
|
|
491
526
|
const btnRzp = getEl('btn-razorpay-checkout');
|
|
492
527
|
if (!btnRzp) return;
|
|
493
528
|
|
|
@@ -503,9 +538,11 @@ async function handleRazorpayCheckout() {
|
|
|
503
538
|
const targetTier = pendingCheckoutTier || 'pro';
|
|
504
539
|
const isAnnual = state.billingCycle === 'annual';
|
|
505
540
|
const p = state.pricing || {};
|
|
541
|
+
const isProOffer = p.pro?.offerActive && !isAnnual && p.pro?.offerPriceINR !== undefined;
|
|
542
|
+
const isEntOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.offerPriceINR !== undefined;
|
|
506
543
|
const inrAmount = targetTier === 'enterprise'
|
|
507
|
-
? (isAnnual ? (p.enterprise?.annualPriceINR
|
|
508
|
-
: (isAnnual ? (p.pro?.annualPriceINR
|
|
544
|
+
? (isEntOffer ? p.enterprise.offerPriceINR : (isAnnual ? (p.enterprise?.annualPriceINR ?? 37990) : (p.enterprise?.monthlyPriceINR ?? 3999)))
|
|
545
|
+
: (isProOffer ? p.pro.offerPriceINR : (isAnnual ? (p.pro?.annualPriceINR ?? 45) : (p.pro?.monthlyPriceINR ?? 50)));
|
|
509
546
|
const amountInPaise = inrAmount * 100; // minimum 100 paise
|
|
510
547
|
|
|
511
548
|
const btnRzp = getEl('btn-razorpay-checkout');
|
|
@@ -1044,7 +1081,7 @@ function initTerminalSimulator() {
|
|
|
1044
1081
|
`;
|
|
1045
1082
|
} else if (lower.includes('status')) {
|
|
1046
1083
|
response.innerHTML = `
|
|
1047
|
-
<span class="term-green">✔ Scout CLI Engine: v9.0.
|
|
1084
|
+
<span class="term-green">✔ Scout CLI Engine: v9.0.2 Online</span><br>
|
|
1048
1085
|
↳ User: <strong>${escapeHtml(state.user ? state.user.name : 'Guest Session')}</strong><br>
|
|
1049
1086
|
↳ Active Subscription: <strong>${escapeHtml(TIER_DETAILS[state.currentTier].name)}</strong><br>
|
|
1050
1087
|
↳ Credit Quota: <strong>${state.byokEnabled ? 'BYOK Mode (Unlimited)' : `${state.creditsUsed} / ${state.creditsTotal} Credits Used`}</strong><br>
|
|
@@ -1289,18 +1326,33 @@ function renderTiersUI() {
|
|
|
1289
1326
|
|
|
1290
1327
|
const isAnnual = state.billingCycle === 'annual';
|
|
1291
1328
|
const p = state.pricing || {};
|
|
1292
|
-
const
|
|
1293
|
-
const
|
|
1329
|
+
const isProOffer = p.pro?.offerActive && !isAnnual && p.pro?.offerPriceUSD !== undefined;
|
|
1330
|
+
const isEntOffer = p.enterprise?.offerActive && !isAnnual && p.enterprise?.offerPriceUSD !== undefined;
|
|
1294
1331
|
|
|
1295
|
-
|
|
1296
|
-
|
|
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));
|
|
1334
|
+
|
|
1335
|
+
if (getEl('price-pro-val')) {
|
|
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}`;
|
|
1338
|
+
} else {
|
|
1339
|
+
getEl('price-pro-val').innerText = proUSD;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (getEl('price-pro-period')) getEl('price-pro-period').innerText = isProOffer ? `/ month (${p.pro?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '(billed annually)' : '/ month');
|
|
1297
1343
|
const proCurrency = document.querySelector('#tier-card-pro .price-currency');
|
|
1298
|
-
if (proCurrency) proCurrency.innerText = '
|
|
1344
|
+
if (proCurrency) proCurrency.innerText = isProOffer ? '' : '$';
|
|
1299
1345
|
|
|
1300
|
-
if (getEl('price-ent-val'))
|
|
1301
|
-
|
|
1346
|
+
if (getEl('price-ent-val')) {
|
|
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}`;
|
|
1349
|
+
} else {
|
|
1350
|
+
getEl('price-ent-val').innerText = entUSD;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (getEl('price-ent-period')) getEl('price-ent-period').innerText = isEntOffer ? `/ month (${p.enterprise?.offerBadge || 'PROMO OFFER'})` : (isAnnual ? '(billed annually)' : '/ month');
|
|
1302
1354
|
const entCurrency = document.querySelector('#tier-card-enterprise .price-currency');
|
|
1303
|
-
if (entCurrency) entCurrency.innerText = '
|
|
1355
|
+
if (entCurrency) entCurrency.innerText = isEntOffer ? '' : '$';
|
|
1304
1356
|
|
|
1305
1357
|
['free', 'pro', 'enterprise'].forEach((t) => {
|
|
1306
1358
|
const card = getEl(`tier-card-${t}`);
|
|
@@ -1333,7 +1385,7 @@ function renderTiersUI() {
|
|
|
1333
1385
|
} else if (t === 'pro') {
|
|
1334
1386
|
if (state.paidTier === 'pro' && !isSubscriptionExpired()) {
|
|
1335
1387
|
btn.className = 'btn btn-primary btn-block btn-select-tier';
|
|
1336
|
-
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)`;
|
|
1337
1389
|
btn.disabled = false;
|
|
1338
1390
|
} else if (state.currentTier === 'enterprise') {
|
|
1339
1391
|
if (isScheduledCancel && state.pendingDowngrade === 'pro') {
|
|
@@ -1353,7 +1405,7 @@ function renderTiersUI() {
|
|
|
1353
1405
|
} else if (t === 'enterprise') {
|
|
1354
1406
|
if (state.paidTier === 'enterprise' && !isSubscriptionExpired()) {
|
|
1355
1407
|
btn.className = 'btn btn-warning btn-block btn-select-tier';
|
|
1356
|
-
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)`;
|
|
1357
1409
|
btn.disabled = false;
|
|
1358
1410
|
} else {
|
|
1359
1411
|
btn.className = 'btn btn-outline btn-block btn-select-tier';
|
|
@@ -2280,3 +2332,203 @@ function initPacmanGame() {
|
|
|
2280
2332
|
|
|
2281
2333
|
render();
|
|
2282
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
|
+
|