apteva 0.4.41 → 0.4.44
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/dist/ActivityPage.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/{App.7fb3e7mp.js → App.qzbx5wtj.js} +1 -1
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -8
- package/src/db.ts +19 -9
- package/src/integrations/agentdojo.ts +1 -0
- package/src/mcp-platform.ts +418 -63
- package/src/openapi.ts +96 -0
- package/src/providers.ts +50 -24
- package/src/routes/api/agent-utils.ts +0 -1
- package/src/routes/api/agents.ts +19 -1
- package/src/routes/api/meta-agent.ts +2 -0
- package/src/routes/api/system.ts +90 -1
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +12 -0
- package/src/web/App.tsx +89 -11
- package/src/web/components/activity/ActivityPage.tsx +14 -14
- package/src/web/components/agents/AgentCard.tsx +14 -14
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +9 -9
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +32 -32
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +34 -15
- package/src/web/components/mcp/IntegrationsPanel.tsx +40 -40
- package/src/web/components/mcp/McpPage.tsx +208 -208
- package/src/web/components/meta-agent/MetaAgent.tsx +12 -10
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +258 -175
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +339 -54
- package/src/web/components/telemetry/TelemetryPage.tsx +135 -64
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +23 -21
- package/src/web/context/ProjectContext.tsx +6 -1
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +2 -0
- package/src/web/styles.css +5 -3
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +0 -4
- package/dist/ActivityPage.7907h64p.js +0 -3
- package/dist/ApiDocsPage.k3jjenpq.js +0 -4
- package/dist/App.01nq20st.js +0 -4
- package/dist/App.1maqvamf.js +0 -4
- package/dist/App.2yjrh32f.js +0 -4
- package/dist/App.3qw8nben.js +0 -20
- package/dist/App.7sy3wq8c.js +0 -4
- package/dist/App.apjrmctz.js +0 -57
- package/dist/App.av6t2yhe.js +0 -4
- package/dist/App.jqj5a094.js +0 -46
- package/dist/App.mc7xf85h.js +0 -4
- package/dist/App.myxqcj9x.js +0 -4
- package/dist/App.nm91r1mp.js +0 -13
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.qcknavjz.js +0 -221
- package/dist/App.vc7vfhg4.js +0 -4
- package/dist/App.z4s9zkw5.js +0 -4
- package/dist/ConnectionsPage.z1pw5xe2.js +0 -3
- package/dist/McpPage.8vc97z0b.js +0 -3
- package/dist/SettingsPage.p61bz8kd.js +0 -3
- package/dist/SkillsPage.r9x43g3g.js +0 -3
- package/dist/TasksPage.1e0zkye4.js +0 -3
- package/dist/TelemetryPage.p9vbe4gf.js +0 -3
- package/dist/TestsPage.d4xy504e.js +0 -3
- package/dist/ThreadsPage.m016am3x.js +0 -3
|
@@ -271,7 +271,7 @@ export function TestsPage() {
|
|
|
271
271
|
running: "bg-blue-900/50 text-blue-400",
|
|
272
272
|
};
|
|
273
273
|
return (
|
|
274
|
-
<span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[status] || "bg-[
|
|
274
|
+
<span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[status] || "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"}`}>
|
|
275
275
|
{status.toUpperCase()}
|
|
276
276
|
</span>
|
|
277
277
|
);
|
|
@@ -297,7 +297,7 @@ export function TestsPage() {
|
|
|
297
297
|
<div className="flex items-center justify-between mb-6">
|
|
298
298
|
<div>
|
|
299
299
|
<h1 className="text-xl font-bold">Tests</h1>
|
|
300
|
-
<p className="text-sm text-[
|
|
300
|
+
<p className="text-sm text-[var(--color-text-muted)] mt-1">
|
|
301
301
|
Describe behavior, AI handles the rest
|
|
302
302
|
</p>
|
|
303
303
|
</div>
|
|
@@ -306,14 +306,14 @@ export function TestsPage() {
|
|
|
306
306
|
<button
|
|
307
307
|
onClick={handleRunAll}
|
|
308
308
|
disabled={runningAll}
|
|
309
|
-
className="px-4 py-2 bg-[
|
|
309
|
+
className="px-4 py-2 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] text-[var(--color-text)] rounded text-sm font-medium transition disabled:opacity-50"
|
|
310
310
|
>
|
|
311
311
|
{runningAll ? "Running..." : "Run All"}
|
|
312
312
|
</button>
|
|
313
313
|
)}
|
|
314
314
|
<button
|
|
315
315
|
onClick={openCreate}
|
|
316
|
-
className="px-4 py-2 bg-[
|
|
316
|
+
className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-white rounded text-sm font-medium transition"
|
|
317
317
|
>
|
|
318
318
|
+ New Test
|
|
319
319
|
</button>
|
|
@@ -322,19 +322,19 @@ export function TestsPage() {
|
|
|
322
322
|
|
|
323
323
|
{/* Test list */}
|
|
324
324
|
{loading ? (
|
|
325
|
-
<div className="text-[
|
|
325
|
+
<div className="text-[var(--color-text-muted)] text-sm">Loading...</div>
|
|
326
326
|
) : tests.length === 0 ? (
|
|
327
327
|
<div className="text-center py-16">
|
|
328
|
-
<div className="text-[
|
|
328
|
+
<div className="text-[var(--color-border-light)] text-4xl mb-4">
|
|
329
329
|
<svg className="w-12 h-12 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
330
330
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
|
331
331
|
</svg>
|
|
332
332
|
</div>
|
|
333
|
-
<p className="text-[
|
|
334
|
-
<p className="text-xs text-[
|
|
333
|
+
<p className="text-[var(--color-text-muted)] mb-2">No tests yet</p>
|
|
334
|
+
<p className="text-xs text-[var(--color-text-faint)] mb-4">Describe what your agents should do and let AI verify it</p>
|
|
335
335
|
<button
|
|
336
336
|
onClick={openCreate}
|
|
337
|
-
className="px-4 py-2 bg-[
|
|
337
|
+
className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-white rounded text-sm font-medium transition"
|
|
338
338
|
>
|
|
339
339
|
Create your first test
|
|
340
340
|
</button>
|
|
@@ -342,7 +342,7 @@ export function TestsPage() {
|
|
|
342
342
|
) : (
|
|
343
343
|
<div className="space-y-3">
|
|
344
344
|
{tests.map(tc => (
|
|
345
|
-
<div key={tc.id} className="bg-[
|
|
345
|
+
<div key={tc.id} className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
346
346
|
<div className="flex items-start justify-between">
|
|
347
347
|
<div className="flex-1 min-w-0">
|
|
348
348
|
<div className="flex items-center gap-2 mb-1">
|
|
@@ -352,18 +352,18 @@ export function TestsPage() {
|
|
|
352
352
|
: tc.last_run && (<>
|
|
353
353
|
{statusBadge(tc.last_run.status)}
|
|
354
354
|
{tc.last_run.score != null && (
|
|
355
|
-
<span className="text-xs text-[
|
|
355
|
+
<span className="text-xs text-[var(--color-text-secondary)] font-mono">{tc.last_run.score}/10</span>
|
|
356
356
|
)}
|
|
357
357
|
</>)
|
|
358
358
|
}
|
|
359
359
|
</div>
|
|
360
360
|
{tc.behavior && (
|
|
361
|
-
<p className="text-xs text-[
|
|
361
|
+
<p className="text-xs text-[var(--color-text-secondary)] mb-1.5 line-clamp-2">{tc.behavior}</p>
|
|
362
362
|
)}
|
|
363
|
-
<div className="text-xs text-[
|
|
363
|
+
<div className="text-xs text-[var(--color-text-muted)] space-y-0.5">
|
|
364
364
|
<div>
|
|
365
365
|
Agent:{" "}
|
|
366
|
-
<span className="text-[
|
|
366
|
+
<span className="text-[var(--color-text-secondary)]">
|
|
367
367
|
{tc.agent_name || (tc.last_run?.selected_agent_name
|
|
368
368
|
? `${tc.last_run.selected_agent_name} (auto-selected)`
|
|
369
369
|
: "Auto (AI picks)")}
|
|
@@ -371,18 +371,18 @@ export function TestsPage() {
|
|
|
371
371
|
</div>
|
|
372
372
|
{tc.last_run?.generated_message && (
|
|
373
373
|
<div className="truncate">
|
|
374
|
-
Message: <span className="text-[
|
|
374
|
+
Message: <span className="text-[var(--color-text-secondary)]">"{tc.last_run.generated_message}"</span>
|
|
375
375
|
</div>
|
|
376
376
|
)}
|
|
377
377
|
{tc.input_message && !tc.last_run?.generated_message && (
|
|
378
378
|
<div className="truncate">
|
|
379
|
-
Message: <span className="text-[
|
|
379
|
+
Message: <span className="text-[var(--color-text-secondary)]">"{tc.input_message}"</span>
|
|
380
380
|
</div>
|
|
381
381
|
)}
|
|
382
382
|
{tc.last_run && (
|
|
383
383
|
<div>
|
|
384
384
|
Last run:{" "}
|
|
385
|
-
<span className="text-[
|
|
385
|
+
<span className="text-[var(--color-text-secondary)]">
|
|
386
386
|
{tc.last_run.duration_ms ? `${(tc.last_run.duration_ms / 1000).toFixed(1)}s` : "---"}
|
|
387
387
|
{tc.last_run.judge_reasoning && ` --- "${tc.last_run.judge_reasoning.slice(0, 80)}${tc.last_run.judge_reasoning.length > 80 ? "..." : ""}"`}
|
|
388
388
|
</span>
|
|
@@ -393,7 +393,7 @@ export function TestsPage() {
|
|
|
393
393
|
<div className="flex items-center gap-1 ml-3 shrink-0">
|
|
394
394
|
<button
|
|
395
395
|
onClick={() => viewRuns(tc.id)}
|
|
396
|
-
className="px-2 py-1 text-xs text-[
|
|
396
|
+
className="px-2 py-1 text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-raised)] rounded transition"
|
|
397
397
|
title="View run history"
|
|
398
398
|
>
|
|
399
399
|
History
|
|
@@ -401,19 +401,19 @@ export function TestsPage() {
|
|
|
401
401
|
<button
|
|
402
402
|
onClick={() => handleRun(tc.id)}
|
|
403
403
|
disabled={runningTests.has(tc.id)}
|
|
404
|
-
className="px-3 py-1 text-xs bg-[
|
|
404
|
+
className="px-3 py-1 text-xs bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] text-[var(--color-text)] rounded transition disabled:opacity-50"
|
|
405
405
|
>
|
|
406
406
|
{runningTests.has(tc.id) ? "Running..." : "Run"}
|
|
407
407
|
</button>
|
|
408
408
|
<button
|
|
409
409
|
onClick={() => openEdit(tc)}
|
|
410
|
-
className="px-2 py-1 text-xs text-[
|
|
410
|
+
className="px-2 py-1 text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-raised)] rounded transition"
|
|
411
411
|
>
|
|
412
412
|
Edit
|
|
413
413
|
</button>
|
|
414
414
|
<button
|
|
415
415
|
onClick={() => handleDelete(tc.id)}
|
|
416
|
-
className="px-2 py-1 text-xs text-[
|
|
416
|
+
className="px-2 py-1 text-xs text-[var(--color-text-muted)] hover:text-red-400 hover:bg-[var(--color-surface-raised)] rounded transition"
|
|
417
417
|
>
|
|
418
418
|
Delete
|
|
419
419
|
</button>
|
|
@@ -428,50 +428,50 @@ export function TestsPage() {
|
|
|
428
428
|
{selectedRuns && (
|
|
429
429
|
<div className="mt-6">
|
|
430
430
|
<div className="flex items-center justify-between mb-3">
|
|
431
|
-
<h2 className="text-sm font-bold text-[
|
|
431
|
+
<h2 className="text-sm font-bold text-[var(--color-text-secondary)]">Run History</h2>
|
|
432
432
|
<button
|
|
433
433
|
onClick={() => { setSelectedRuns(null); setExpandedRun(null); }}
|
|
434
|
-
className="text-xs text-[
|
|
434
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
|
|
435
435
|
>
|
|
436
436
|
Close
|
|
437
437
|
</button>
|
|
438
438
|
</div>
|
|
439
439
|
{selectedRuns.runs.length === 0 ? (
|
|
440
|
-
<p className="text-sm text-[
|
|
440
|
+
<p className="text-sm text-[var(--color-text-muted)]">No runs yet</p>
|
|
441
441
|
) : (
|
|
442
442
|
<div className="space-y-2">
|
|
443
443
|
{selectedRuns.runs.map(run => (
|
|
444
|
-
<div key={run.id} className="bg-[
|
|
444
|
+
<div key={run.id} className="bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded p-3">
|
|
445
445
|
<div
|
|
446
446
|
className="flex items-center justify-between cursor-pointer"
|
|
447
447
|
onClick={() => setExpandedRun(expandedRun === run.id ? null : run.id)}
|
|
448
448
|
>
|
|
449
449
|
<div className="flex items-center gap-3">
|
|
450
450
|
{statusBadge(run.status)}
|
|
451
|
-
<span className="text-xs text-[
|
|
451
|
+
<span className="text-xs text-[var(--color-text-muted)]">
|
|
452
452
|
{run.duration_ms ? `${(run.duration_ms / 1000).toFixed(1)}s` : "---"}
|
|
453
453
|
</span>
|
|
454
454
|
{run.score != null && (
|
|
455
|
-
<span className="text-xs text-[
|
|
455
|
+
<span className="text-xs text-[var(--color-text-secondary)] font-mono">{run.score}/10</span>
|
|
456
456
|
)}
|
|
457
457
|
{run.selected_agent_name && (
|
|
458
|
-
<span className="text-xs text-[
|
|
458
|
+
<span className="text-xs text-[var(--color-text-faint)]">
|
|
459
459
|
Agent: {run.selected_agent_name}
|
|
460
460
|
</span>
|
|
461
461
|
)}
|
|
462
|
-
<span className="text-xs text-[
|
|
462
|
+
<span className="text-xs text-[var(--color-text-faint)]">
|
|
463
463
|
{new Date(run.created_at).toLocaleString()}
|
|
464
464
|
</span>
|
|
465
465
|
</div>
|
|
466
|
-
<span className="text-xs text-[
|
|
466
|
+
<span className="text-xs text-[var(--color-text-faint)]">{expandedRun === run.id ? "---" : "+"}</span>
|
|
467
467
|
</div>
|
|
468
468
|
{expandedRun === run.id && (
|
|
469
469
|
<div className="mt-3 space-y-2">
|
|
470
470
|
{run.planner_reasoning && (
|
|
471
471
|
<div>
|
|
472
|
-
<div className="text-xs text-[
|
|
473
|
-
<div className="text-sm text-[
|
|
474
|
-
{run.selected_agent_name && <span className="text-[
|
|
472
|
+
<div className="text-xs text-[var(--color-text-muted)] mb-1">Planner:</div>
|
|
473
|
+
<div className="text-sm text-[var(--color-text-secondary)] bg-[var(--color-bg)] p-2 rounded">
|
|
474
|
+
{run.selected_agent_name && <span className="text-[var(--color-accent)]">{run.selected_agent_name}</span>}
|
|
475
475
|
{run.selected_agent_name && " --- "}
|
|
476
476
|
{run.planner_reasoning}
|
|
477
477
|
</div>
|
|
@@ -479,26 +479,26 @@ export function TestsPage() {
|
|
|
479
479
|
)}
|
|
480
480
|
{run.generated_message && (
|
|
481
481
|
<div>
|
|
482
|
-
<div className="text-xs text-[
|
|
483
|
-
<div className="text-sm text-[
|
|
482
|
+
<div className="text-xs text-[var(--color-text-muted)] mb-1">Generated Message:</div>
|
|
483
|
+
<div className="text-sm text-[var(--color-text-secondary)] bg-[var(--color-bg)] p-2 rounded">"{run.generated_message}"</div>
|
|
484
484
|
</div>
|
|
485
485
|
)}
|
|
486
486
|
{run.judge_reasoning && (
|
|
487
487
|
<div>
|
|
488
|
-
<div className="text-xs text-[
|
|
489
|
-
<div className="text-sm text-[
|
|
488
|
+
<div className="text-xs text-[var(--color-text-muted)] mb-1">Judge:</div>
|
|
489
|
+
<div className="text-sm text-[var(--color-text-secondary)] bg-[var(--color-bg)] p-2 rounded">{run.judge_reasoning}</div>
|
|
490
490
|
</div>
|
|
491
491
|
)}
|
|
492
492
|
{run.error && (
|
|
493
493
|
<div>
|
|
494
494
|
<div className="text-xs text-red-400 mb-1">Error:</div>
|
|
495
|
-
<div className="text-sm text-red-300 bg-[
|
|
495
|
+
<div className="text-sm text-red-300 bg-[var(--color-bg)] p-2 rounded">{run.error}</div>
|
|
496
496
|
</div>
|
|
497
497
|
)}
|
|
498
498
|
{run.agent_response && (
|
|
499
499
|
<div>
|
|
500
|
-
<div className="text-xs text-[
|
|
501
|
-
<pre className="text-xs text-[
|
|
500
|
+
<div className="text-xs text-[var(--color-text-muted)] mb-1">Agent Response (Thread):</div>
|
|
501
|
+
<pre className="text-xs text-[var(--color-text-secondary)] bg-[var(--color-bg)] p-2 rounded overflow-auto max-h-64">
|
|
502
502
|
{run.agent_response}
|
|
503
503
|
</pre>
|
|
504
504
|
</div>
|
|
@@ -515,34 +515,34 @@ export function TestsPage() {
|
|
|
515
515
|
{/* Create/Edit Form Modal */}
|
|
516
516
|
{showForm && (
|
|
517
517
|
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center" onClick={() => setShowForm(false)}>
|
|
518
|
-
<div className="bg-[
|
|
518
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg w-full max-w-lg mx-4 p-6" onClick={e => e.stopPropagation()}>
|
|
519
519
|
<h2 className="text-lg font-bold mb-4">{editingTest ? "Edit Test" : "New Test"}</h2>
|
|
520
520
|
|
|
521
521
|
<div className="space-y-4">
|
|
522
522
|
<div>
|
|
523
|
-
<label className="block text-xs text-[
|
|
523
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Name</label>
|
|
524
524
|
<input
|
|
525
525
|
value={formName}
|
|
526
526
|
onChange={e => setFormName(e.target.value)}
|
|
527
527
|
placeholder="e.g. Social Media Posting"
|
|
528
|
-
className="w-full bg-[
|
|
528
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--color-accent)]"
|
|
529
529
|
/>
|
|
530
530
|
</div>
|
|
531
531
|
|
|
532
532
|
<div>
|
|
533
|
-
<label className="block text-xs text-[
|
|
533
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Behavior</label>
|
|
534
534
|
<textarea
|
|
535
535
|
value={formBehavior}
|
|
536
536
|
onChange={e => setFormBehavior(e.target.value)}
|
|
537
537
|
placeholder="Describe what should happen, e.g. 'When asked to post on social media, the agent creates a proper post with relevant hashtags and confirms it was published'"
|
|
538
538
|
rows={3}
|
|
539
|
-
className="w-full bg-[
|
|
539
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--color-accent)] resize-none"
|
|
540
540
|
/>
|
|
541
|
-
<p className="text-xs text-[
|
|
541
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-1">AI will generate the test message and evaluate results based on this</p>
|
|
542
542
|
</div>
|
|
543
543
|
|
|
544
544
|
<div>
|
|
545
|
-
<label className="block text-xs text-[
|
|
545
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Agent</label>
|
|
546
546
|
<Select
|
|
547
547
|
value={formAgentId}
|
|
548
548
|
onChange={setFormAgentId}
|
|
@@ -552,21 +552,21 @@ export function TestsPage() {
|
|
|
552
552
|
label: `${a.name} (${a.status})`,
|
|
553
553
|
}))}
|
|
554
554
|
/>
|
|
555
|
-
<p className="text-xs text-[
|
|
555
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-1">Leave empty to let AI choose the right agent</p>
|
|
556
556
|
</div>
|
|
557
557
|
</div>
|
|
558
558
|
|
|
559
559
|
<div className="flex justify-end gap-2 mt-6">
|
|
560
560
|
<button
|
|
561
561
|
onClick={() => setShowForm(false)}
|
|
562
|
-
className="px-4 py-2 text-sm text-[
|
|
562
|
+
className="px-4 py-2 text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition"
|
|
563
563
|
>
|
|
564
564
|
Cancel
|
|
565
565
|
</button>
|
|
566
566
|
<button
|
|
567
567
|
onClick={handleSave}
|
|
568
568
|
disabled={!formName || !formBehavior}
|
|
569
|
-
className="px-4 py-2 bg-[
|
|
569
|
+
className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-white rounded text-sm font-medium transition"
|
|
570
570
|
>
|
|
571
571
|
{editingTest ? "Save" : "Create"}
|
|
572
572
|
</button>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
|
2
2
|
import { Chat, convertApiMessages } from "@apteva/apteva-kit";
|
|
3
|
-
import { useAgentActivity, useAuth, useProjects, useTelemetryContext } from "../../context";
|
|
3
|
+
import { useAgentActivity, useAuth, useProjects, useTelemetryContext, useTheme } from "../../context";
|
|
4
4
|
import type { TelemetryEvent } from "../../context";
|
|
5
5
|
import type { Agent, Route } from "../../types";
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@ interface ThreadsPageProps {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
23
|
+
const { theme } = useTheme();
|
|
23
24
|
const { authFetch } = useAuth();
|
|
24
25
|
const { currentProjectId } = useProjects();
|
|
25
26
|
const { events: realtimeEvents, statusChangeCounter } = useTelemetryContext();
|
|
@@ -146,7 +147,7 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
146
147
|
<div className="px-6 pt-6 pb-4 shrink-0">
|
|
147
148
|
<div className="flex items-center justify-between">
|
|
148
149
|
<h1 className="text-xl font-semibold">Threads</h1>
|
|
149
|
-
<span className="text-sm text-[
|
|
150
|
+
<span className="text-sm text-[var(--color-text-muted)]">
|
|
150
151
|
{threads.length} threads from {runningCount} running agents
|
|
151
152
|
</span>
|
|
152
153
|
</div>
|
|
@@ -162,7 +163,7 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
162
163
|
<button
|
|
163
164
|
onClick={() => setShowAgentPicker(!showAgentPicker)}
|
|
164
165
|
disabled={runningAgents.length === 0}
|
|
165
|
-
className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-[
|
|
166
|
+
className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-[var(--color-accent-10)] text-[var(--color-accent)] text-sm font-medium hover:bg-[var(--color-accent-20)] transition disabled:opacity-30 disabled:cursor-not-allowed"
|
|
166
167
|
>
|
|
167
168
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
168
169
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
@@ -174,15 +175,15 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
174
175
|
{showAgentPicker && (
|
|
175
176
|
<>
|
|
176
177
|
<div className="fixed inset-0 z-40" onClick={() => setShowAgentPicker(false)} />
|
|
177
|
-
<div className="absolute top-full left-0 right-0 mt-1 bg-[
|
|
178
|
+
<div className="absolute top-full left-0 right-0 mt-1 bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg shadow-xl z-50 max-h-60 overflow-auto">
|
|
178
179
|
{runningAgents.map(agent => (
|
|
179
180
|
<button
|
|
180
181
|
key={agent.id}
|
|
181
182
|
onClick={() => startNewChat(agent)}
|
|
182
|
-
className="w-full text-left px-3 py-2.5 hover:bg-[
|
|
183
|
+
className="w-full text-left px-3 py-2.5 hover:bg-[var(--color-surface-raised)] transition"
|
|
183
184
|
>
|
|
184
185
|
<p className="text-sm font-medium truncate">{agent.name}</p>
|
|
185
|
-
<p className="text-[10px] text-[
|
|
186
|
+
<p className="text-[10px] text-[var(--color-text-faint)]">{agent.provider} · {agent.model}</p>
|
|
186
187
|
</button>
|
|
187
188
|
))}
|
|
188
189
|
</div>
|
|
@@ -193,11 +194,11 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
193
194
|
|
|
194
195
|
<div className="flex-1 overflow-auto px-2 pb-2">
|
|
195
196
|
{loadingThreads ? (
|
|
196
|
-
<div className="p-6 text-center text-[
|
|
197
|
+
<div className="p-6 text-center text-[var(--color-text-faint)] text-sm">Loading threads...</div>
|
|
197
198
|
) : threads.length === 0 ? (
|
|
198
|
-
<div className="p-6 text-center text-[
|
|
199
|
+
<div className="p-6 text-center text-[var(--color-text-faint)] text-sm">
|
|
199
200
|
<p>No threads yet</p>
|
|
200
|
-
<p className="mt-1 text-[
|
|
201
|
+
<p className="mt-1 text-[var(--color-text-faint)]">Start a conversation or wait for agents</p>
|
|
201
202
|
</div>
|
|
202
203
|
) : (
|
|
203
204
|
<div className="space-y-0.5">
|
|
@@ -219,7 +220,7 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
219
220
|
<div className="flex-1 flex flex-col min-h-0 overflow-hidden">
|
|
220
221
|
{chatAgentId && chatKey ? (
|
|
221
222
|
loadingMessages ? (
|
|
222
|
-
<div className="flex-1 flex items-center justify-center text-[
|
|
223
|
+
<div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">Loading messages...</div>
|
|
223
224
|
) : (
|
|
224
225
|
<Chat
|
|
225
226
|
key={chatKey}
|
|
@@ -230,17 +231,18 @@ export function ThreadsPage({ agents, onNavigate }: ThreadsPageProps) {
|
|
|
230
231
|
placeholder={`Message ${chatAgentName}...`}
|
|
231
232
|
headerTitle={chatAgentName}
|
|
232
233
|
variant="terminal"
|
|
234
|
+
theme={theme.id as "light" | "dark"}
|
|
233
235
|
showHeader={true}
|
|
234
236
|
/>
|
|
235
237
|
)
|
|
236
238
|
) : (
|
|
237
239
|
<div className="flex-1 flex items-center justify-center">
|
|
238
|
-
<div className="text-center text-[
|
|
239
|
-
<svg className="w-12 h-12 mx-auto mb-3 text-[
|
|
240
|
+
<div className="text-center text-[var(--color-text-faint)]">
|
|
241
|
+
<svg className="w-12 h-12 mx-auto mb-3 text-[var(--color-border-light)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
240
242
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
241
243
|
</svg>
|
|
242
244
|
<p className="text-sm">Select a thread or start a new conversation</p>
|
|
243
|
-
<p className="text-xs text-[
|
|
245
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-1">Chat with any running agent</p>
|
|
244
246
|
</div>
|
|
245
247
|
</div>
|
|
246
248
|
)}
|
|
@@ -267,32 +269,32 @@ function ThreadRow({ thread, selected, activities, onSelect }: {
|
|
|
267
269
|
onClick={onSelect}
|
|
268
270
|
className={`w-full text-left px-3 py-2.5 rounded-lg transition ${
|
|
269
271
|
selected
|
|
270
|
-
? "bg-[
|
|
271
|
-
: "hover:bg-[
|
|
272
|
+
? "bg-[var(--color-accent-10)]"
|
|
273
|
+
: "hover:bg-[var(--color-bg-secondary)]"
|
|
272
274
|
}`}
|
|
273
275
|
>
|
|
274
276
|
<div className="flex items-center justify-between gap-2 mb-1">
|
|
275
277
|
<span className="text-sm font-medium truncate">
|
|
276
278
|
{thread.title || `Thread ${thread.id.slice(0, 8)}`}
|
|
277
279
|
</span>
|
|
278
|
-
<span className="text-[10px] text-[
|
|
280
|
+
<span className="text-[10px] text-[var(--color-text-faint)] shrink-0">{timeAgo(thread.updated_at)}</span>
|
|
279
281
|
</div>
|
|
280
282
|
<div className="flex items-center gap-1.5">
|
|
281
283
|
<span
|
|
282
284
|
className={`w-1.5 h-1.5 rounded-full shrink-0 ${
|
|
283
|
-
isActive ? "bg-green-400 animate-pulse" : "bg-[
|
|
285
|
+
isActive ? "bg-green-400 animate-pulse" : "bg-[var(--color-scrollbar)]"
|
|
284
286
|
}`}
|
|
285
287
|
/>
|
|
286
|
-
<span className="text-[11px] text-[
|
|
288
|
+
<span className="text-[11px] text-[var(--color-accent)]">{thread.agent_name}</span>
|
|
287
289
|
{thread.message_count != null && (
|
|
288
290
|
<>
|
|
289
|
-
<span className="text-[
|
|
290
|
-
<span className="text-[10px] text-[
|
|
291
|
+
<span className="text-[var(--color-border-light)]">·</span>
|
|
292
|
+
<span className="text-[10px] text-[var(--color-text-faint)]">{thread.message_count} msgs</span>
|
|
291
293
|
</>
|
|
292
294
|
)}
|
|
293
295
|
</div>
|
|
294
296
|
{activityText && (
|
|
295
|
-
<p className="text-[11px] text-[
|
|
297
|
+
<p className="text-[11px] text-[var(--color-text-faint)] truncate mt-1">{activityText}</p>
|
|
296
298
|
)}
|
|
297
299
|
</button>
|
|
298
300
|
);
|
|
@@ -20,6 +20,7 @@ interface ProjectContextValue {
|
|
|
20
20
|
unassignedCount: number;
|
|
21
21
|
projectsEnabled: boolean; // Feature flag
|
|
22
22
|
metaAgentEnabled: boolean; // Feature flag
|
|
23
|
+
costTrackingEnabled: boolean; // Feature flag
|
|
23
24
|
setCurrentProjectId: (id: string | null) => void;
|
|
24
25
|
createProject: (data: { name: string; description?: string; color?: string }) => Promise<Project | null>;
|
|
25
26
|
updateProject: (id: string, data: { name?: string; description?: string; color?: string }) => Promise<Project | null>;
|
|
@@ -58,6 +59,7 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
58
59
|
const [unassignedCount, setUnassignedCount] = useState(0);
|
|
59
60
|
const [projectsEnabled, setProjectsEnabled] = useState(false);
|
|
60
61
|
const [metaAgentEnabled, setMetaAgentEnabled] = useState(false);
|
|
62
|
+
const [costTrackingEnabled, setCostTrackingEnabled] = useState(true);
|
|
61
63
|
|
|
62
64
|
// Fetch feature flags on mount
|
|
63
65
|
useEffect(() => {
|
|
@@ -66,10 +68,12 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
66
68
|
.then(data => {
|
|
67
69
|
setProjectsEnabled(data.projects === true);
|
|
68
70
|
setMetaAgentEnabled(data.metaAgent === true);
|
|
71
|
+
setCostTrackingEnabled(data.costTracking !== false);
|
|
69
72
|
})
|
|
70
73
|
.catch(() => {
|
|
71
74
|
setProjectsEnabled(false);
|
|
72
75
|
setMetaAgentEnabled(false);
|
|
76
|
+
setCostTrackingEnabled(true);
|
|
73
77
|
});
|
|
74
78
|
}, []);
|
|
75
79
|
|
|
@@ -198,12 +202,13 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
198
202
|
unassignedCount,
|
|
199
203
|
projectsEnabled,
|
|
200
204
|
metaAgentEnabled,
|
|
205
|
+
costTrackingEnabled,
|
|
201
206
|
setCurrentProjectId,
|
|
202
207
|
createProject,
|
|
203
208
|
updateProject,
|
|
204
209
|
deleteProject,
|
|
205
210
|
refreshProjects,
|
|
206
|
-
}), [projects, currentProjectId, currentProject, isLoading, error, unassignedCount, projectsEnabled, metaAgentEnabled, setCurrentProjectId, createProject, updateProject, deleteProject, refreshProjects]);
|
|
211
|
+
}), [projects, currentProjectId, currentProject, isLoading, error, unassignedCount, projectsEnabled, metaAgentEnabled, costTrackingEnabled, setCurrentProjectId, createProject, updateProject, deleteProject, refreshProjects]);
|
|
207
212
|
|
|
208
213
|
return <ProjectContext.Provider value={value}>{children}</ProjectContext.Provider>;
|
|
209
214
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo, type ReactNode } from "react";
|
|
2
|
+
import { themes, resolveTheme, type ThemeMode, type Theme } from "../themes";
|
|
3
|
+
|
|
4
|
+
interface ThemeContextValue {
|
|
5
|
+
mode: ThemeMode;
|
|
6
|
+
theme: Theme; // resolved theme
|
|
7
|
+
setMode: (mode: ThemeMode) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
|
11
|
+
|
|
12
|
+
export function useTheme(): ThemeContextValue {
|
|
13
|
+
const context = useContext(ThemeContext);
|
|
14
|
+
if (!context) {
|
|
15
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
16
|
+
}
|
|
17
|
+
return context;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const STORAGE_KEY = "apteva_theme_mode";
|
|
21
|
+
|
|
22
|
+
function getSystemPrefersDark(): boolean {
|
|
23
|
+
if (typeof window === "undefined") return true;
|
|
24
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function applyTheme(theme: Theme) {
|
|
28
|
+
const root = document.documentElement;
|
|
29
|
+
for (const [key, value] of Object.entries(theme.colors)) {
|
|
30
|
+
root.style.setProperty(key, value);
|
|
31
|
+
}
|
|
32
|
+
root.setAttribute("data-theme", theme.id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
36
|
+
const [mode, setModeState] = useState<ThemeMode>(() => {
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
39
|
+
if (stored === "dark" || stored === "light" || stored === "auto") return stored;
|
|
40
|
+
}
|
|
41
|
+
return "auto";
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const [prefersDark, setPrefersDark] = useState(getSystemPrefersDark);
|
|
45
|
+
|
|
46
|
+
// Listen for system theme changes
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
49
|
+
const handler = (e: MediaQueryListEvent) => setPrefersDark(e.matches);
|
|
50
|
+
mq.addEventListener("change", handler);
|
|
51
|
+
return () => mq.removeEventListener("change", handler);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const theme = useMemo(() => resolveTheme(mode, prefersDark), [mode, prefersDark]);
|
|
55
|
+
|
|
56
|
+
// Apply CSS variables whenever theme changes
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
applyTheme(theme);
|
|
59
|
+
}, [theme]);
|
|
60
|
+
|
|
61
|
+
const setMode = useCallback((newMode: ThemeMode) => {
|
|
62
|
+
setModeState(newMode);
|
|
63
|
+
localStorage.setItem(STORAGE_KEY, newMode);
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const value = useMemo(() => ({ mode, theme, setMode }), [mode, theme, setMode]);
|
|
67
|
+
|
|
68
|
+
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
|
69
|
+
}
|
package/src/web/context/index.ts
CHANGED
package/src/web/styles.css
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
@tailwind utilities;
|
|
4
4
|
|
|
5
5
|
html, body {
|
|
6
|
-
background-color: #0a0a0a;
|
|
6
|
+
background-color: var(--color-bg, #0a0a0a);
|
|
7
|
+
color: var(--color-text, #e0e0e0);
|
|
7
8
|
min-height: 100%;
|
|
8
9
|
margin: 0;
|
|
9
10
|
-webkit-font-smoothing: antialiased;
|
|
@@ -20,8 +21,8 @@ html, body {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
::selection {
|
|
23
|
-
background-color: #f97316;
|
|
24
|
-
color: #0a0a0a;
|
|
24
|
+
background-color: var(--color-selection-bg, #f97316);
|
|
25
|
+
color: var(--color-selection-text, #0a0a0a);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
.line-clamp-2 {
|
|
@@ -67,3 +68,4 @@ html, body {
|
|
|
67
68
|
animation: slideIn 0.5s cubic-bezier(0.16, 1, 0.3, 1), highlightFade 2s 0.5s ease-out;
|
|
68
69
|
overflow: hidden;
|
|
69
70
|
}
|
|
71
|
+
|