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.
Files changed (102) hide show
  1. package/dist/ActivityPage.c48n83h2.js +3 -0
  2. package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
  3. package/dist/App.09yb8t0b.js +1 -0
  4. package/dist/App.152mbs1r.js +4 -0
  5. package/dist/App.3a67nx9w.js +4 -0
  6. package/dist/App.9epx6785.js +4 -0
  7. package/dist/App.d8955awp.js +4 -0
  8. package/dist/App.drwb57jq.js +4 -0
  9. package/dist/App.gssbmajb.js +4 -0
  10. package/dist/App.qw70pc29.js +53 -0
  11. package/dist/{App.7fb3e7mp.js → App.qzbx5wtj.js} +1 -1
  12. package/dist/App.r5serxkt.js +8 -0
  13. package/dist/App.tpmp9020.js +20 -0
  14. package/dist/App.v2wb4d7d.js +61 -0
  15. package/dist/App.vxmaaj0m.js +13 -0
  16. package/dist/App.w4p2tda9.js +4 -0
  17. package/dist/App.wv2ng55q.js +221 -0
  18. package/dist/App.yncnrn0f.js +4 -0
  19. package/dist/ConnectionsPage.k6cspyqq.js +3 -0
  20. package/dist/McpPage.cdxm48xj.js +3 -0
  21. package/dist/SettingsPage.evpv7c2y.js +3 -0
  22. package/dist/SkillsPage.pvzp6c1a.js +3 -0
  23. package/dist/TasksPage.6jnvbpsy.js +3 -0
  24. package/dist/TelemetryPage.t7vk24zc.js +3 -0
  25. package/dist/TestsPage.5x6658aa.js +3 -0
  26. package/dist/ThreadsPage.3fvhtevh.js +3 -0
  27. package/dist/apteva-kit.css +1 -1
  28. package/dist/index.html +1 -1
  29. package/dist/styles.css +1 -1
  30. package/package.json +8 -8
  31. package/src/db.ts +19 -9
  32. package/src/integrations/agentdojo.ts +1 -0
  33. package/src/mcp-platform.ts +418 -63
  34. package/src/openapi.ts +96 -0
  35. package/src/providers.ts +50 -24
  36. package/src/routes/api/agent-utils.ts +0 -1
  37. package/src/routes/api/agents.ts +19 -1
  38. package/src/routes/api/meta-agent.ts +2 -0
  39. package/src/routes/api/system.ts +90 -1
  40. package/src/routes/api/telemetry.ts +19 -1
  41. package/src/routes/share.ts +85 -0
  42. package/src/server.ts +12 -0
  43. package/src/web/App.tsx +89 -11
  44. package/src/web/components/activity/ActivityPage.tsx +14 -14
  45. package/src/web/components/agents/AgentCard.tsx +14 -14
  46. package/src/web/components/agents/AgentPanel.tsx +358 -198
  47. package/src/web/components/agents/AgentsView.tsx +4 -4
  48. package/src/web/components/agents/CreateAgentModal.tsx +21 -79
  49. package/src/web/components/api/ApiDocsPage.tsx +66 -66
  50. package/src/web/components/auth/CreateAccountStep.tsx +16 -16
  51. package/src/web/components/auth/LoginPage.tsx +10 -10
  52. package/src/web/components/common/LoadingSpinner.tsx +2 -2
  53. package/src/web/components/common/Modal.tsx +8 -8
  54. package/src/web/components/common/Select.tsx +9 -9
  55. package/src/web/components/connections/ConnectionsPage.tsx +4 -4
  56. package/src/web/components/connections/IntegrationsTab.tsx +18 -18
  57. package/src/web/components/connections/OverviewTab.tsx +13 -13
  58. package/src/web/components/connections/TriggersTab.tsx +99 -99
  59. package/src/web/components/dashboard/Dashboard.tsx +32 -32
  60. package/src/web/components/layout/Header.tsx +50 -34
  61. package/src/web/components/layout/Sidebar.tsx +34 -15
  62. package/src/web/components/mcp/IntegrationsPanel.tsx +40 -40
  63. package/src/web/components/mcp/McpPage.tsx +208 -208
  64. package/src/web/components/meta-agent/MetaAgent.tsx +12 -10
  65. package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
  66. package/src/web/components/settings/SettingsPage.tsx +258 -175
  67. package/src/web/components/skills/SkillsPage.tsx +88 -88
  68. package/src/web/components/tasks/TasksPage.tsx +339 -54
  69. package/src/web/components/telemetry/TelemetryPage.tsx +135 -64
  70. package/src/web/components/tests/TestsPage.tsx +50 -50
  71. package/src/web/components/threads/ThreadsPage.tsx +23 -21
  72. package/src/web/context/ProjectContext.tsx +6 -1
  73. package/src/web/context/ThemeContext.tsx +69 -0
  74. package/src/web/context/index.ts +2 -0
  75. package/src/web/styles.css +5 -3
  76. package/src/web/themes.ts +99 -0
  77. package/src/web/types.ts +0 -4
  78. package/dist/ActivityPage.7907h64p.js +0 -3
  79. package/dist/ApiDocsPage.k3jjenpq.js +0 -4
  80. package/dist/App.01nq20st.js +0 -4
  81. package/dist/App.1maqvamf.js +0 -4
  82. package/dist/App.2yjrh32f.js +0 -4
  83. package/dist/App.3qw8nben.js +0 -20
  84. package/dist/App.7sy3wq8c.js +0 -4
  85. package/dist/App.apjrmctz.js +0 -57
  86. package/dist/App.av6t2yhe.js +0 -4
  87. package/dist/App.jqj5a094.js +0 -46
  88. package/dist/App.mc7xf85h.js +0 -4
  89. package/dist/App.myxqcj9x.js +0 -4
  90. package/dist/App.nm91r1mp.js +0 -13
  91. package/dist/App.p02f4ret.js +0 -1
  92. package/dist/App.qcknavjz.js +0 -221
  93. package/dist/App.vc7vfhg4.js +0 -4
  94. package/dist/App.z4s9zkw5.js +0 -4
  95. package/dist/ConnectionsPage.z1pw5xe2.js +0 -3
  96. package/dist/McpPage.8vc97z0b.js +0 -3
  97. package/dist/SettingsPage.p61bz8kd.js +0 -3
  98. package/dist/SkillsPage.r9x43g3g.js +0 -3
  99. package/dist/TasksPage.1e0zkye4.js +0 -3
  100. package/dist/TelemetryPage.p9vbe4gf.js +0 -3
  101. package/dist/TestsPage.d4xy504e.js +0 -3
  102. package/dist/ThreadsPage.m016am3x.js +0 -3
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react";
2
2
  import { CheckIcon, CloseIcon, PlusIcon } from "../common/Icons";
3
3
  import { Modal, useConfirm } from "../common/Modal";
4
4
  import { Select } from "../common/Select";
5
- import { useProjects, useAuth, type Project } from "../../context";
5
+ import { useProjects, useAuth, useTheme, type Project } from "../../context";
6
+ import type { ThemeMode } from "../../themes";
6
7
  import type { Provider } from "../../types";
7
8
 
8
9
  type SettingsTab = "general" | "providers" | "projects" | "channels" | "api-keys" | "account" | "updates" | "data" | "assistant";
@@ -26,7 +27,7 @@ export function SettingsPage() {
26
27
  return (
27
28
  <div className="flex-1 flex flex-col md:flex-row overflow-hidden">
28
29
  {/* Mobile: Horizontal scrolling tabs */}
29
- <div className="md:hidden border-b border-[#1a1a1a] bg-[#0a0a0a]">
30
+ <div className="md:hidden border-b border-[var(--color-border)] bg-[var(--color-bg)]">
30
31
  <div className="flex overflow-x-auto" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
31
32
  {tabs.map(tab => (
32
33
  <button
@@ -34,8 +35,8 @@ export function SettingsPage() {
34
35
  onClick={() => setActiveTab(tab.key)}
35
36
  className={`flex-shrink-0 px-4 py-3 text-sm font-medium border-b-2 transition ${
36
37
  activeTab === tab.key
37
- ? "border-[#f97316] text-[#f97316]"
38
- : "border-transparent text-[#666] hover:text-[#888]"
38
+ ? "border-[var(--color-accent)] text-[var(--color-accent)]"
39
+ : "border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
39
40
  }`}
40
41
  >
41
42
  {tab.label}
@@ -45,8 +46,8 @@ export function SettingsPage() {
45
46
  </div>
46
47
 
47
48
  {/* Desktop: Settings Sidebar */}
48
- <div className="hidden md:block w-48 border-r border-[#1a1a1a] p-4 flex-shrink-0">
49
- <h2 className="text-sm font-medium text-[#666] uppercase tracking-wider mb-3">Settings</h2>
49
+ <div className="hidden md:block w-48 border-r border-[var(--color-border)] p-4 flex-shrink-0">
50
+ <h2 className="text-sm font-medium text-[var(--color-text-muted)] uppercase tracking-wider mb-3">Settings</h2>
50
51
  <nav className="space-y-1">
51
52
  {tabs.map(tab => (
52
53
  <SettingsNavItem
@@ -89,8 +90,8 @@ function SettingsNavItem({
89
90
  onClick={onClick}
90
91
  className={`w-full text-left px-3 py-2 rounded text-sm transition ${
91
92
  active
92
- ? "bg-[#1a1a1a] text-[#e0e0e0]"
93
- : "text-[#666] hover:bg-[#111] hover:text-[#888]"
93
+ ? "bg-[var(--color-surface-raised)] text-[var(--color-text)]"
94
+ : "text-[var(--color-text-muted)] hover:bg-[var(--color-surface)] hover:text-[var(--color-text-secondary)]"
94
95
  }`}
95
96
  >
96
97
  {label}
@@ -100,6 +101,7 @@ function SettingsNavItem({
100
101
 
101
102
  function GeneralSettings() {
102
103
  const { authFetch } = useAuth();
104
+ const { mode, setMode } = useTheme();
103
105
  const [instanceUrl, setInstanceUrl] = useState("");
104
106
  const [loading, setLoading] = useState(true);
105
107
  const [saving, setSaving] = useState(false);
@@ -141,21 +143,58 @@ function GeneralSettings() {
141
143
  setSaving(false);
142
144
  };
143
145
 
146
+ const themeOptions: { value: ThemeMode; label: string; description: string }[] = [
147
+ { value: "auto", label: "Auto", description: "Follow system preference" },
148
+ { value: "dark", label: "Dark", description: "Dark background" },
149
+ { value: "light", label: "Light", description: "Light background" },
150
+ ];
151
+
144
152
  return (
145
153
  <div className="max-w-4xl w-full">
146
154
  <div className="mb-6">
147
155
  <h1 className="text-2xl font-semibold mb-1">General</h1>
148
- <p className="text-[#666]">Instance configuration.</p>
156
+ <p className="text-[var(--color-text-muted)]">Instance configuration and appearance.</p>
157
+ </div>
158
+
159
+ {/* Theme */}
160
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-4">
161
+ <h3 className="font-medium mb-2">Theme</h3>
162
+ <p className="text-sm text-[var(--color-text-muted)] mb-4">
163
+ Choose your preferred color scheme. Auto follows your operating system setting.
164
+ </p>
165
+ <div className="flex gap-3">
166
+ {themeOptions.map(opt => (
167
+ <button
168
+ key={opt.value}
169
+ onClick={() => setMode(opt.value)}
170
+ className={`flex-1 max-w-[160px] px-4 py-3 rounded-lg border text-left transition ${
171
+ mode === opt.value
172
+ ? "border-[var(--color-accent)] bg-[var(--color-accent-10)]"
173
+ : "border-[var(--color-border-light)] bg-[var(--color-bg)] hover:border-[var(--color-scrollbar)]"
174
+ }`}
175
+ >
176
+ <div className="flex items-center gap-2 mb-1">
177
+ <div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${
178
+ mode === opt.value ? "border-[var(--color-accent)]" : "border-[var(--color-scrollbar)]"
179
+ }`}>
180
+ {mode === opt.value && <div className="w-2 h-2 rounded-full bg-[var(--color-accent)]" />}
181
+ </div>
182
+ <span className="text-sm font-medium">{opt.label}</span>
183
+ </div>
184
+ <p className="text-xs text-[var(--color-text-muted)] ml-6">{opt.description}</p>
185
+ </button>
186
+ ))}
187
+ </div>
149
188
  </div>
150
189
 
151
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
190
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
152
191
  <h3 className="font-medium mb-2">Instance URL</h3>
153
- <p className="text-sm text-[#666] mb-4">
192
+ <p className="text-sm text-[var(--color-text-muted)] mb-4">
154
193
  The public HTTPS URL for this instance. Used for webhook callbacks from external services like Composio.
155
194
  </p>
156
195
 
157
196
  {loading ? (
158
- <div className="text-[#666] text-sm">Loading...</div>
197
+ <div className="text-[var(--color-text-muted)] text-sm">Loading...</div>
159
198
  ) : (
160
199
  <div className="space-y-3 max-w-lg">
161
200
  <input
@@ -163,7 +202,7 @@ function GeneralSettings() {
163
202
  value={instanceUrl}
164
203
  onChange={e => setInstanceUrl(e.target.value)}
165
204
  placeholder="https://your-domain.com"
166
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316] font-mono text-sm"
205
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)] font-mono text-sm"
167
206
  />
168
207
 
169
208
  {message && (
@@ -179,7 +218,7 @@ function GeneralSettings() {
179
218
  <button
180
219
  onClick={handleSave}
181
220
  disabled={saving}
182
- className="px-4 py-2 bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 text-black rounded text-sm font-medium transition"
221
+ className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black rounded text-sm font-medium transition"
183
222
  >
184
223
  {saving ? "Saving..." : "Save"}
185
224
  </button>
@@ -320,7 +359,7 @@ function ProvidersSettings() {
320
359
  <div>
321
360
  <div className="mb-6">
322
361
  <h1 className="text-2xl font-semibold mb-1">AI Providers</h1>
323
- <p className="text-[#666]">
362
+ <p className="text-[var(--color-text-muted)]">
324
363
  Manage your API keys for AI providers. {llmConfiguredCount} of {llmProviders.length} configured.
325
364
  </p>
326
365
  </div>
@@ -358,7 +397,7 @@ function ProvidersSettings() {
358
397
  <div>
359
398
  <div className="mb-6">
360
399
  <h2 className="text-xl font-semibold mb-1">MCP Integrations</h2>
361
- <p className="text-[#666]">
400
+ <p className="text-[var(--color-text-muted)]">
362
401
  Connect to MCP gateways for tool integrations. {intConfiguredCount} of {integrations.length} configured.
363
402
  </p>
364
403
  </div>
@@ -399,7 +438,7 @@ function ProvidersSettings() {
399
438
  <div>
400
439
  <div className="mb-6">
401
440
  <h2 className="text-xl font-semibold mb-1">Browser Providers</h2>
402
- <p className="text-[#666]">
441
+ <p className="text-[var(--color-text-muted)]">
403
442
  Configure browser environments for operator mode (computer use). {browserConfiguredCount} of {browserProviders.length} configured.
404
443
  </p>
405
444
  </div>
@@ -485,13 +524,13 @@ function ProjectsSettings() {
485
524
  <div className="mb-6 flex items-center justify-between gap-4">
486
525
  <div>
487
526
  <h1 className="text-2xl font-semibold mb-1">Projects</h1>
488
- <p className="text-[#666]">
527
+ <p className="text-[var(--color-text-muted)]">
489
528
  Organize agents into projects for better management.
490
529
  </p>
491
530
  </div>
492
531
  <button
493
532
  onClick={openCreate}
494
- className="flex items-center gap-2 bg-[#f97316] hover:bg-[#fb923c] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
533
+ className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
495
534
  >
496
535
  <PlusIcon className="w-4 h-4" />
497
536
  New Project
@@ -500,7 +539,7 @@ function ProjectsSettings() {
500
539
 
501
540
  {/* Project List */}
502
541
  {projects.length === 0 ? (
503
- <div className="text-center py-12 text-[#666]">
542
+ <div className="text-center py-12 text-[var(--color-text-muted)]">
504
543
  <p className="text-lg mb-2">No projects yet</p>
505
544
  <p className="text-sm">Create a project to organize your agents.</p>
506
545
  </div>
@@ -509,7 +548,7 @@ function ProjectsSettings() {
509
548
  {projects.map(project => (
510
549
  <div
511
550
  key={project.id}
512
- className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4 flex items-center gap-4"
551
+ className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 flex items-center gap-4"
513
552
  >
514
553
  <div
515
554
  className="w-4 h-4 rounded-full flex-shrink-0"
@@ -518,16 +557,16 @@ function ProjectsSettings() {
518
557
  <div className="flex-1 min-w-0">
519
558
  <h3 className="font-medium">{project.name}</h3>
520
559
  {project.description && (
521
- <p className="text-sm text-[#666] truncate">{project.description}</p>
560
+ <p className="text-sm text-[var(--color-text-muted)] truncate">{project.description}</p>
522
561
  )}
523
- <p className="text-xs text-[#666] mt-1">
562
+ <p className="text-xs text-[var(--color-text-muted)] mt-1">
524
563
  {project.agentCount} agent{project.agentCount !== 1 ? "s" : ""}
525
564
  </p>
526
565
  </div>
527
566
  <div className="flex items-center gap-2">
528
567
  <button
529
568
  onClick={() => openEdit(project)}
530
- className="text-sm text-[#888] hover:text-[#e0e0e0] px-2 py-1"
569
+ className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] px-2 py-1"
531
570
  >
532
571
  Edit
533
572
  </button>
@@ -601,30 +640,30 @@ function ProjectModal({ project, onSave, onClose }: ProjectModalProps) {
601
640
 
602
641
  <div className="space-y-4">
603
642
  <div>
604
- <label className="block text-sm text-[#666] mb-1">Name</label>
643
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Name</label>
605
644
  <input
606
645
  type="text"
607
646
  value={name}
608
647
  onChange={e => setName(e.target.value)}
609
- className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
648
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
610
649
  placeholder="My Project"
611
650
  autoFocus
612
651
  />
613
652
  </div>
614
653
 
615
654
  <div>
616
- <label className="block text-sm text-[#666] mb-1">Description (optional)</label>
655
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Description (optional)</label>
617
656
  <input
618
657
  type="text"
619
658
  value={description}
620
659
  onChange={e => setDescription(e.target.value)}
621
- className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
660
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
622
661
  placeholder="A short description"
623
662
  />
624
663
  </div>
625
664
 
626
665
  <div>
627
- <label className="block text-sm text-[#666] mb-1">Color</label>
666
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Color</label>
628
667
  <div className="flex gap-3 flex-wrap">
629
668
  {DEFAULT_PROJECT_COLORS.map(c => (
630
669
  <button
@@ -646,14 +685,14 @@ function ProjectModal({ project, onSave, onClose }: ProjectModalProps) {
646
685
  <div className="flex gap-3 mt-6">
647
686
  <button
648
687
  onClick={onClose}
649
- className="flex-1 border border-[#333] hover:border-[#f97316] hover:text-[#f97316] px-4 py-2 rounded font-medium transition"
688
+ className="flex-1 border border-[var(--color-border-light)] hover:border-[var(--color-accent)] hover:text-[var(--color-accent)] px-4 py-2 rounded font-medium transition"
650
689
  >
651
690
  Cancel
652
691
  </button>
653
692
  <button
654
693
  onClick={handleSubmit}
655
694
  disabled={saving || !name.trim()}
656
- className="flex-1 bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 text-black px-4 py-2 rounded font-medium transition"
695
+ className="flex-1 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black px-4 py-2 rounded font-medium transition"
657
696
  >
658
697
  {saving ? "Saving..." : project ? "Update" : "Create"}
659
698
  </button>
@@ -754,13 +793,13 @@ function UpdatesSettings() {
754
793
  <div className="max-w-4xl w-full">
755
794
  <div className="mb-6">
756
795
  <h1 className="text-2xl font-semibold mb-1">Updates</h1>
757
- <p className="text-[#666]">
796
+ <p className="text-[var(--color-text-muted)]">
758
797
  Check for new versions of apteva and the agent binary.
759
798
  </p>
760
799
  </div>
761
800
 
762
801
  {checking && !versions ? (
763
- <div className="text-[#666]">Checking version info...</div>
802
+ <div className="text-[var(--color-text-muted)]">Checking version info...</div>
764
803
  ) : error && !versions ? (
765
804
  <div className="text-red-400">{error}</div>
766
805
  ) : versions?.isDocker ? (
@@ -773,17 +812,17 @@ function UpdatesSettings() {
773
812
  </svg>
774
813
  <span className="font-medium">Docker Environment</span>
775
814
  </div>
776
- <p className="text-sm text-[#888]">
815
+ <p className="text-sm text-[var(--color-text-secondary)]">
777
816
  Updates are automatic when you pull a new image version.
778
817
  </p>
779
818
  </div>
780
819
 
781
820
  {/* Current Version */}
782
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-5">
821
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
783
822
  <div className="flex items-center justify-between mb-4">
784
823
  <div>
785
824
  <h3 className="font-medium text-lg">Current Version</h3>
786
- <p className="text-sm text-[#666]">apteva + agent binary</p>
825
+ <p className="text-sm text-[var(--color-text-muted)]">apteva + agent binary</p>
787
826
  </div>
788
827
  <div className="text-right">
789
828
  <div className="text-xl font-mono">v{versions.apteva.installed || "?"}</div>
@@ -791,15 +830,15 @@ function UpdatesSettings() {
791
830
  </div>
792
831
 
793
832
  {hasAnyUpdate ? (
794
- <div className="bg-[#f97316]/10 border border-[#f97316]/30 rounded-lg p-4">
795
- <p className="text-sm text-[#888] mb-3">
833
+ <div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
834
+ <p className="text-sm text-[var(--color-text-secondary)] mb-3">
796
835
  A newer version (v{versions.apteva.latest}) is available. To update:
797
836
  </p>
798
837
  <div className="space-y-2">
799
- <code className="block bg-[#0a0a0a] px-3 py-2 rounded font-mono text-sm text-[#888]">
838
+ <code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
800
839
  docker pull apteva/apteva:latest
801
840
  </code>
802
- <code className="block bg-[#0a0a0a] px-3 py-2 rounded font-mono text-sm text-[#888]">
841
+ <code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
803
842
  docker compose up -d
804
843
  </code>
805
844
  </div>
@@ -809,7 +848,7 @@ function UpdatesSettings() {
809
848
  setCopied("docker");
810
849
  setTimeout(() => setCopied(null), 2000);
811
850
  }}
812
- className="mt-3 px-3 py-1.5 bg-[#1a1a1a] hover:bg-[#222] rounded text-sm"
851
+ className="mt-3 px-3 py-1.5 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm"
813
852
  >
814
853
  {copied === "docker" ? "Copied!" : "Copy commands"}
815
854
  </button>
@@ -822,7 +861,7 @@ function UpdatesSettings() {
822
861
  )}
823
862
  </div>
824
863
 
825
- <p className="text-xs text-[#555]">
864
+ <p className="text-xs text-[var(--color-text-faint)]">
826
865
  Your data is stored in a Docker volume and persists across updates.
827
866
  </p>
828
867
  </div>
@@ -842,32 +881,32 @@ function UpdatesSettings() {
842
881
  )}
843
882
 
844
883
  {/* Apteva App Version */}
845
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-5">
884
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
846
885
  <div className="flex items-center justify-between mb-4">
847
886
  <div>
848
887
  <h3 className="font-medium text-lg">apteva</h3>
849
- <p className="text-sm text-[#666]">The app you're running</p>
888
+ <p className="text-sm text-[var(--color-text-muted)]">The app you're running</p>
850
889
  </div>
851
890
  <div className="text-right">
852
891
  <div className="text-xl font-mono">v{versions.apteva.installed || "?"}</div>
853
892
  {versions.apteva.updateAvailable && (
854
- <div className="text-sm text-[#f97316]">→ v{versions.apteva.latest}</div>
893
+ <div className="text-sm text-[var(--color-accent)]">→ v{versions.apteva.latest}</div>
855
894
  )}
856
895
  </div>
857
896
  </div>
858
897
 
859
898
  {versions.apteva.updateAvailable ? (
860
- <div className="bg-[#f97316]/10 border border-[#f97316]/30 rounded-lg p-4">
861
- <p className="text-sm text-[#888] mb-3">
899
+ <div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
900
+ <p className="text-sm text-[var(--color-text-secondary)] mb-3">
862
901
  Update by running:
863
902
  </p>
864
903
  <div className="flex items-center gap-2">
865
- <code className="flex-1 bg-[#0a0a0a] px-3 py-2 rounded font-mono text-sm text-[#888]">
904
+ <code className="flex-1 bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
866
905
  npx apteva@latest
867
906
  </code>
868
907
  <button
869
908
  onClick={() => copyCommand("npx apteva@latest", "apteva")}
870
- className="px-3 py-2 bg-[#1a1a1a] hover:bg-[#222] rounded text-sm"
909
+ className="px-3 py-2 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm"
871
910
  >
872
911
  {copied === "apteva" ? "Copied!" : "Copy"}
873
912
  </button>
@@ -882,30 +921,30 @@ function UpdatesSettings() {
882
921
  </div>
883
922
 
884
923
  {/* Agent Binary Version */}
885
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-5">
924
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
886
925
  <div className="flex items-center justify-between mb-4">
887
926
  <div>
888
927
  <h3 className="font-medium text-lg">Agent Binary</h3>
889
- <p className="text-sm text-[#666]">The Go binary that runs agents</p>
928
+ <p className="text-sm text-[var(--color-text-muted)]">The Go binary that runs agents</p>
890
929
  </div>
891
930
  <div className="text-right">
892
931
  <div className="text-xl font-mono">v{versions.agent.installed || "?"}</div>
893
932
  {versions.agent.updateAvailable && (
894
- <div className="text-sm text-[#f97316]">→ v{versions.agent.latest}</div>
933
+ <div className="text-sm text-[var(--color-accent)]">→ v{versions.agent.latest}</div>
895
934
  )}
896
935
  </div>
897
936
  </div>
898
937
 
899
938
  {versions.agent.updateAvailable ? (
900
- <div className="bg-[#f97316]/10 border border-[#f97316]/30 rounded-lg p-4">
901
- <p className="text-sm text-[#888] mb-3">
939
+ <div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
940
+ <p className="text-sm text-[var(--color-text-secondary)] mb-3">
902
941
  A new version is available. Stop all agents before updating.
903
942
  </p>
904
943
  <div className="flex items-center gap-2">
905
944
  <button
906
945
  onClick={updateAgent}
907
946
  disabled={updatingAgent}
908
- className="px-4 py-2 bg-[#f97316] text-black rounded font-medium text-sm disabled:opacity-50"
947
+ className="px-4 py-2 bg-[var(--color-accent)] text-black rounded font-medium text-sm disabled:opacity-50"
909
948
  >
910
949
  {updatingAgent ? "Updating..." : "Update Agent"}
911
950
  </button>
@@ -929,7 +968,7 @@ function UpdatesSettings() {
929
968
  <button
930
969
  onClick={checkForUpdates}
931
970
  disabled={checking}
932
- className="text-sm text-[#666] hover:text-[#888] disabled:opacity-50"
971
+ className="text-sm text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] disabled:opacity-50"
933
972
  >
934
973
  {checking ? "Checking..." : "Check for updates"}
935
974
  </button>
@@ -999,13 +1038,13 @@ function ProviderKeyCard({
999
1038
  };
1000
1039
 
1001
1040
  return (
1002
- <div className={`bg-[#111] border rounded-lg p-4 ${
1003
- provider.hasKey ? 'border-green-500/20' : 'border-[#1a1a1a]'
1041
+ <div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
1042
+ provider.hasKey ? 'border-green-500/20' : 'border-[var(--color-border)]'
1004
1043
  }`}>
1005
1044
  <div className="flex items-start justify-between gap-2 mb-2">
1006
1045
  <div className="min-w-0">
1007
1046
  <h3 className="font-medium">{provider.name}</h3>
1008
- <p className="text-sm text-[#666] truncate">
1047
+ <p className="text-sm text-[var(--color-text-muted)] truncate">
1009
1048
  {isBrowser
1010
1049
  ? (provider.description || "Browser automation")
1011
1050
  : provider.type === "integration"
@@ -1036,36 +1075,36 @@ function ProviderKeyCard({
1036
1075
  )}
1037
1076
  </span>
1038
1077
  ) : (
1039
- <span className="text-[#666] text-xs bg-[#1a1a1a] px-2 py-1 rounded whitespace-nowrap flex-shrink-0">
1078
+ <span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded whitespace-nowrap flex-shrink-0">
1040
1079
  Not configured
1041
1080
  </span>
1042
1081
  )}
1043
1082
  </div>
1044
1083
 
1045
- <div className="mt-3 pt-3 border-t border-[#1a1a1a]">
1084
+ <div className="mt-3 pt-3 border-t border-[var(--color-border)]">
1046
1085
  {isEditing ? (
1047
1086
  <div className="space-y-3">
1048
1087
  {isMultiField ? (
1049
1088
  <>
1050
1089
  <div>
1051
- <label className="block text-xs text-[#888] mb-1">API Key</label>
1090
+ <label className="block text-xs text-[var(--color-text-secondary)] mb-1">API Key</label>
1052
1091
  <input
1053
1092
  type="password"
1054
1093
  value={apiKey}
1055
1094
  onChange={e => onApiKeyChange(e.target.value)}
1056
1095
  placeholder={provider.hasKey ? "Enter new API key..." : "Enter API key..."}
1057
1096
  autoFocus
1058
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1097
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1059
1098
  />
1060
1099
  </div>
1061
1100
  <div>
1062
- <label className="block text-xs text-[#888] mb-1">Project ID</label>
1101
+ <label className="block text-xs text-[var(--color-text-secondary)] mb-1">Project ID</label>
1063
1102
  <input
1064
1103
  type="text"
1065
1104
  value={extraField || ""}
1066
1105
  onChange={e => onExtraFieldChange?.(e.target.value)}
1067
1106
  placeholder="Enter your Browserbase project ID..."
1068
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1107
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1069
1108
  />
1070
1109
  </div>
1071
1110
  </>
@@ -1079,11 +1118,11 @@ function ProviderKeyCard({
1079
1118
  : isCDP ? "ws://localhost:9222"
1080
1119
  : provider.hasKey ? "Enter new API key..." : "Enter API key..."}
1081
1120
  autoFocus
1082
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1121
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1083
1122
  />
1084
1123
  )}
1085
1124
  {isUrlBased && (
1086
- <p className="text-xs text-[#666]">
1125
+ <p className="text-xs text-[var(--color-text-muted)]">
1087
1126
  {isCDP
1088
1127
  ? "Enter the CDP URL of your browser (e.g., ws://localhost:9222)"
1089
1128
  : "Enter your Ollama server URL. Default is http://localhost:11434"}
@@ -1094,14 +1133,14 @@ function ProviderKeyCard({
1094
1133
  <div className="flex gap-2">
1095
1134
  <button
1096
1135
  onClick={onCancelEdit}
1097
- className="flex-1 px-3 py-1.5 border border-[#333] rounded text-sm hover:border-[#666]"
1136
+ className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
1098
1137
  >
1099
1138
  Cancel
1100
1139
  </button>
1101
1140
  <button
1102
1141
  onClick={onSave}
1103
1142
  disabled={!apiKey || saving}
1104
- className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
1143
+ className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
1105
1144
  >
1106
1145
  {testing ? "Validating..." : saving ? "Saving..." : isUrlBased ? "Connect" : "Save"}
1107
1146
  </button>
@@ -1141,7 +1180,7 @@ function ProviderKeyCard({
1141
1180
  <div className="flex items-center gap-3">
1142
1181
  <button
1143
1182
  onClick={onStartEdit}
1144
- className="text-sm text-[#888] hover:text-[#e0e0e0]"
1183
+ className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
1145
1184
  >
1146
1185
  {isUrlBased ? "Change URL" : "Update key"}
1147
1186
  </button>
@@ -1187,7 +1226,7 @@ function ProviderKeyCard({
1187
1226
  )}
1188
1227
  <button
1189
1228
  onClick={onStartEdit}
1190
- className="text-sm text-[#f97316] hover:text-[#fb923c]"
1229
+ className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
1191
1230
  >
1192
1231
  {isUrlBased ? "Configure" : "+ Add key"}
1193
1232
  </button>
@@ -1332,27 +1371,27 @@ function IntegrationKeyCard({
1332
1371
  // Simple view when projects not enabled
1333
1372
  if (!projectsEnabled) {
1334
1373
  return (
1335
- <div className={`bg-[#111] border rounded-lg p-4 ${
1336
- provider.hasKey ? 'border-[#f97316]/20' : 'border-[#1a1a1a]'
1374
+ <div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
1375
+ provider.hasKey ? 'border-[var(--color-accent-20)]' : 'border-[var(--color-border)]'
1337
1376
  }`}>
1338
1377
  <div className="flex items-center justify-between mb-2">
1339
1378
  <div>
1340
1379
  <h3 className="font-medium">{provider.name}</h3>
1341
- <p className="text-sm text-[#666]">{provider.description || "MCP integration"}</p>
1380
+ <p className="text-sm text-[var(--color-text-muted)]">{provider.description || "MCP integration"}</p>
1342
1381
  </div>
1343
1382
  {provider.hasKey ? (
1344
- <span className="text-[#f97316] text-xs flex items-center gap-1 bg-[#f97316]/10 px-2 py-1 rounded">
1383
+ <span className="text-[var(--color-accent)] text-xs flex items-center gap-1 bg-[var(--color-accent-10)] px-2 py-1 rounded">
1345
1384
  <CheckIcon className="w-3 h-3" />
1346
1385
  {provider.keyHint}
1347
1386
  </span>
1348
1387
  ) : (
1349
- <span className="text-[#666] text-xs bg-[#1a1a1a] px-2 py-1 rounded">
1388
+ <span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded">
1350
1389
  Not configured
1351
1390
  </span>
1352
1391
  )}
1353
1392
  </div>
1354
1393
 
1355
- <div className="mt-3 pt-3 border-t border-[#1a1a1a]">
1394
+ <div className="mt-3 pt-3 border-t border-[var(--color-border)]">
1356
1395
  {isEditing ? (
1357
1396
  <div className="space-y-3">
1358
1397
  <input
@@ -1361,21 +1400,21 @@ function IntegrationKeyCard({
1361
1400
  onChange={e => onApiKeyChange(e.target.value)}
1362
1401
  placeholder={provider.hasKey ? `Enter new ${isUrlBased ? "URL" : "API key"}...` : inputPlaceholder}
1363
1402
  autoFocus
1364
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1403
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1365
1404
  />
1366
1405
  {error && <p className="text-red-400 text-sm">{error}</p>}
1367
1406
  {success && <p className="text-green-400 text-sm">{success}</p>}
1368
1407
  <div className="flex gap-2">
1369
1408
  <button
1370
1409
  onClick={onCancelEdit}
1371
- className="flex-1 px-3 py-1.5 border border-[#333] rounded text-sm hover:border-[#666]"
1410
+ className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
1372
1411
  >
1373
1412
  Cancel
1374
1413
  </button>
1375
1414
  <button
1376
1415
  onClick={onSave}
1377
1416
  disabled={!apiKey || saving}
1378
- className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
1417
+ className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
1379
1418
  >
1380
1419
  {testing ? "Validating..." : saving ? "Saving..." : "Save"}
1381
1420
  </button>
@@ -1394,7 +1433,7 @@ function IntegrationKeyCard({
1394
1433
  <div className="flex items-center gap-3">
1395
1434
  <button
1396
1435
  onClick={onStartEdit}
1397
- className="text-sm text-[#888] hover:text-[#e0e0e0]"
1436
+ className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
1398
1437
  >
1399
1438
  Update key
1400
1439
  </button>
@@ -1418,7 +1457,7 @@ function IntegrationKeyCard({
1418
1457
  </a>
1419
1458
  <button
1420
1459
  onClick={onStartEdit}
1421
- className="text-sm text-[#f97316] hover:text-[#fb923c]"
1460
+ className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
1422
1461
  >
1423
1462
  + Add key
1424
1463
  </button>
@@ -1440,21 +1479,21 @@ function IntegrationKeyCard({
1440
1479
  return (
1441
1480
  <>
1442
1481
  {ConfirmDialog}
1443
- <div className={`bg-[#111] border rounded-lg p-4 ${
1444
- keys.length > 0 ? 'border-[#f97316]/20' : 'border-[#1a1a1a]'
1482
+ <div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
1483
+ keys.length > 0 ? 'border-[var(--color-accent-20)]' : 'border-[var(--color-border)]'
1445
1484
  }`}>
1446
1485
  <div className="flex items-center justify-between mb-2">
1447
1486
  <div>
1448
1487
  <h3 className="font-medium">{provider.name}</h3>
1449
- <p className="text-sm text-[#666]">{provider.description || "MCP integration"}</p>
1488
+ <p className="text-sm text-[var(--color-text-muted)]">{provider.description || "MCP integration"}</p>
1450
1489
  </div>
1451
1490
  {keys.length > 0 ? (
1452
- <span className="text-[#f97316] text-xs flex items-center gap-1 bg-[#f97316]/10 px-2 py-1 rounded">
1491
+ <span className="text-[var(--color-accent)] text-xs flex items-center gap-1 bg-[var(--color-accent-10)] px-2 py-1 rounded">
1453
1492
  <CheckIcon className="w-3 h-3" />
1454
1493
  {keys.length} key{keys.length !== 1 ? "s" : ""}
1455
1494
  </span>
1456
1495
  ) : (
1457
- <span className="text-[#666] text-xs bg-[#1a1a1a] px-2 py-1 rounded">
1496
+ <span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded">
1458
1497
  Not configured
1459
1498
  </span>
1460
1499
  )}
@@ -1465,11 +1504,11 @@ function IntegrationKeyCard({
1465
1504
  <div className="mt-3 space-y-2">
1466
1505
  {/* Global Key */}
1467
1506
  {globalKey && (
1468
- <div className="flex items-center justify-between text-sm bg-[#0a0a0a] rounded px-3 py-2">
1507
+ <div className="flex items-center justify-between text-sm bg-[var(--color-bg)] rounded px-3 py-2">
1469
1508
  <div className="flex items-center gap-2">
1470
- <span className="text-[#888]">Global</span>
1471
- <span className="text-[#555]">·</span>
1472
- <span className="text-[#666] font-mono text-xs">{globalKey.key_hint}</span>
1509
+ <span className="text-[var(--color-text-secondary)]">Global</span>
1510
+ <span className="text-[var(--color-text-faint)]">·</span>
1511
+ <span className="text-[var(--color-text-muted)] font-mono text-xs">{globalKey.key_hint}</span>
1473
1512
  </div>
1474
1513
  <button
1475
1514
  onClick={() => handleDeleteKey(globalKey.id, "Global")}
@@ -1482,15 +1521,15 @@ function IntegrationKeyCard({
1482
1521
 
1483
1522
  {/* Project Keys - show first 2, expand for more */}
1484
1523
  {projectKeys.slice(0, expanded ? undefined : 2).map(key => (
1485
- <div key={key.id} className="flex items-center justify-between text-sm bg-[#0a0a0a] rounded px-3 py-2">
1524
+ <div key={key.id} className="flex items-center justify-between text-sm bg-[var(--color-bg)] rounded px-3 py-2">
1486
1525
  <div className="flex items-center gap-2 min-w-0">
1487
1526
  <span
1488
1527
  className="w-2 h-2 rounded-full flex-shrink-0"
1489
1528
  style={{ backgroundColor: getProjectColor(key.project_id!) }}
1490
1529
  />
1491
- <span className="text-[#888] truncate">{key.name || getProjectName(key.project_id!)}</span>
1492
- <span className="text-[#555]">·</span>
1493
- <span className="text-[#666] font-mono text-xs">{key.key_hint}</span>
1530
+ <span className="text-[var(--color-text-secondary)] truncate">{key.name || getProjectName(key.project_id!)}</span>
1531
+ <span className="text-[var(--color-text-faint)]">·</span>
1532
+ <span className="text-[var(--color-text-muted)] font-mono text-xs">{key.key_hint}</span>
1494
1533
  </div>
1495
1534
  <button
1496
1535
  onClick={() => handleDeleteKey(key.id, key.name || getProjectName(key.project_id!))}
@@ -1504,7 +1543,7 @@ function IntegrationKeyCard({
1504
1543
  {projectKeys.length > 2 && !expanded && (
1505
1544
  <button
1506
1545
  onClick={() => setExpanded(true)}
1507
- className="text-xs text-[#666] hover:text-[#888] w-full text-center py-1"
1546
+ className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] w-full text-center py-1"
1508
1547
  >
1509
1548
  Show {projectKeys.length - 2} more...
1510
1549
  </button>
@@ -1512,7 +1551,7 @@ function IntegrationKeyCard({
1512
1551
  </div>
1513
1552
  )}
1514
1553
 
1515
- <div className="mt-3 pt-3 border-t border-[#1a1a1a]">
1554
+ <div className="mt-3 pt-3 border-t border-[var(--color-border)]">
1516
1555
  {isEditing ? (
1517
1556
  <div className="space-y-3">
1518
1557
  <input
@@ -1521,7 +1560,7 @@ function IntegrationKeyCard({
1521
1560
  onChange={e => onApiKeyChange(e.target.value)}
1522
1561
  placeholder={inputPlaceholder}
1523
1562
  autoFocus
1524
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1563
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1525
1564
  />
1526
1565
 
1527
1566
  {isBrowserbase && (
@@ -1530,7 +1569,7 @@ function IntegrationKeyCard({
1530
1569
  value={bbProjectId}
1531
1570
  onChange={e => setBbProjectId(e.target.value)}
1532
1571
  placeholder="Browserbase Project ID (optional)"
1533
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316] text-sm"
1572
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)] text-sm"
1534
1573
  />
1535
1574
  )}
1536
1575
 
@@ -1553,14 +1592,14 @@ function IntegrationKeyCard({
1553
1592
  setSelectedProjectId("");
1554
1593
  setLocalError(null);
1555
1594
  }}
1556
- className="flex-1 px-3 py-1.5 border border-[#333] rounded text-sm hover:border-[#666]"
1595
+ className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
1557
1596
  >
1558
1597
  Cancel
1559
1598
  </button>
1560
1599
  <button
1561
1600
  onClick={handleSaveWithProject}
1562
1601
  disabled={!apiKey || localSaving}
1563
- className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
1602
+ className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
1564
1603
  >
1565
1604
  {localSaving ? "Saving..." : "Save"}
1566
1605
  </button>
@@ -1578,7 +1617,7 @@ function IntegrationKeyCard({
1578
1617
  </a>
1579
1618
  <button
1580
1619
  onClick={onStartEdit}
1581
- className="text-sm text-[#f97316] hover:text-[#fb923c]"
1620
+ className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
1582
1621
  >
1583
1622
  + Add key
1584
1623
  </button>
@@ -1697,14 +1736,14 @@ function ApiKeysSettings() {
1697
1736
  <div className="mb-6 flex items-center justify-between gap-4">
1698
1737
  <div>
1699
1738
  <h1 className="text-2xl font-semibold mb-1">API Keys</h1>
1700
- <p className="text-[#666]">
1701
- Create personal API keys for programmatic access. Use them with the <code className="text-[#888] bg-[#1a1a1a] px-1 rounded text-xs">X-API-Key</code> header.
1739
+ <p className="text-[var(--color-text-muted)]">
1740
+ Create personal API keys for programmatic access. Use them with the <code className="text-[var(--color-text-secondary)] bg-[var(--color-surface-raised)] px-1 rounded text-xs">X-API-Key</code> header.
1702
1741
  </p>
1703
1742
  </div>
1704
1743
  {!showCreate && !newKey && (
1705
1744
  <button
1706
1745
  onClick={() => { setShowCreate(true); setError(null); }}
1707
- className="flex items-center gap-2 bg-[#f97316] hover:bg-[#fb923c] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
1746
+ className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
1708
1747
  >
1709
1748
  <PlusIcon className="w-4 h-4" />
1710
1749
  New Key
@@ -1719,23 +1758,23 @@ function ApiKeysSettings() {
1719
1758
  <CheckIcon className="w-5 h-5" />
1720
1759
  <span className="font-medium">API key created</span>
1721
1760
  </div>
1722
- <p className="text-sm text-[#888] mb-3">
1761
+ <p className="text-sm text-[var(--color-text-secondary)] mb-3">
1723
1762
  Copy this key now. You won't be able to see it again.
1724
1763
  </p>
1725
1764
  <div className="flex items-center gap-2">
1726
- <code className="flex-1 bg-[#0a0a0a] px-3 py-2 rounded font-mono text-sm text-[#e0e0e0] break-all select-all">
1765
+ <code className="flex-1 bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text)] break-all select-all">
1727
1766
  {newKey}
1728
1767
  </code>
1729
1768
  <button
1730
1769
  onClick={copyKey}
1731
- className="px-3 py-2 bg-[#1a1a1a] hover:bg-[#222] rounded text-sm flex-shrink-0"
1770
+ className="px-3 py-2 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm flex-shrink-0"
1732
1771
  >
1733
1772
  {copied ? "Copied!" : "Copy"}
1734
1773
  </button>
1735
1774
  </div>
1736
1775
  <button
1737
1776
  onClick={() => { setNewKey(null); setShowCreate(false); }}
1738
- className="mt-3 text-sm text-[#666] hover:text-[#888]"
1777
+ className="mt-3 text-sm text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
1739
1778
  >
1740
1779
  Done
1741
1780
  </button>
@@ -1744,26 +1783,26 @@ function ApiKeysSettings() {
1744
1783
 
1745
1784
  {/* Create Form */}
1746
1785
  {showCreate && !newKey && (
1747
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4 mb-6">
1786
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-6">
1748
1787
  <h3 className="font-medium mb-4">Create new API key</h3>
1749
1788
  <div className="space-y-4 max-w-md">
1750
1789
  <div>
1751
- <label className="block text-sm text-[#666] mb-1">Name</label>
1790
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Name</label>
1752
1791
  <input
1753
1792
  type="text"
1754
1793
  value={name}
1755
1794
  onChange={e => setName(e.target.value)}
1756
1795
  placeholder="e.g. CI Pipeline, My Script"
1757
1796
  autoFocus
1758
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1797
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1759
1798
  />
1760
1799
  </div>
1761
1800
  <div>
1762
- <label className="block text-sm text-[#666] mb-1">Expiration</label>
1801
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Expiration</label>
1763
1802
  <select
1764
1803
  value={expiresInDays}
1765
1804
  onChange={e => setExpiresInDays(e.target.value)}
1766
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1805
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1767
1806
  >
1768
1807
  <option value="30">30 days</option>
1769
1808
  <option value="90">90 days</option>
@@ -1778,14 +1817,14 @@ function ApiKeysSettings() {
1778
1817
  <div className="flex gap-2">
1779
1818
  <button
1780
1819
  onClick={() => { setShowCreate(false); setError(null); setName(""); }}
1781
- className="flex-1 px-3 py-2 border border-[#333] rounded text-sm hover:border-[#666]"
1820
+ className="flex-1 px-3 py-2 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
1782
1821
  >
1783
1822
  Cancel
1784
1823
  </button>
1785
1824
  <button
1786
1825
  onClick={handleCreate}
1787
1826
  disabled={creating || !name.trim()}
1788
- className="flex-1 px-3 py-2 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
1827
+ className="flex-1 px-3 py-2 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
1789
1828
  >
1790
1829
  {creating ? "Creating..." : "Create Key"}
1791
1830
  </button>
@@ -1796,7 +1835,7 @@ function ApiKeysSettings() {
1796
1835
 
1797
1836
  {/* Keys List */}
1798
1837
  {keys.length === 0 ? (
1799
- <div className="text-center py-12 text-[#666]">
1838
+ <div className="text-center py-12 text-[var(--color-text-muted)]">
1800
1839
  <p className="text-lg mb-2">No API keys yet</p>
1801
1840
  <p className="text-sm">Create an API key to access apteva programmatically.</p>
1802
1841
  </div>
@@ -1805,8 +1844,8 @@ function ApiKeysSettings() {
1805
1844
  {keys.map(key => (
1806
1845
  <div
1807
1846
  key={key.id}
1808
- className={`bg-[#111] border rounded-lg p-4 flex items-center gap-4 ${
1809
- !key.is_active || isExpired(key.expires_at) ? "border-[#1a1a1a] opacity-60" : "border-[#1a1a1a]"
1847
+ className={`bg-[var(--color-surface)] border rounded-lg p-4 flex items-center gap-4 ${
1848
+ !key.is_active || isExpired(key.expires_at) ? "border-[var(--color-border)] opacity-60" : "border-[var(--color-border)]"
1810
1849
  }`}
1811
1850
  >
1812
1851
  <div className="flex-1 min-w-0">
@@ -1819,8 +1858,8 @@ function ApiKeysSettings() {
1819
1858
  <span className="text-xs text-yellow-400 bg-yellow-500/10 px-2 py-0.5 rounded">Expired</span>
1820
1859
  )}
1821
1860
  </div>
1822
- <div className="flex items-center gap-3 text-sm text-[#666]">
1823
- <code className="font-mono text-xs bg-[#0a0a0a] px-2 py-0.5 rounded">{key.prefix}...</code>
1861
+ <div className="flex items-center gap-3 text-sm text-[var(--color-text-muted)]">
1862
+ <code className="font-mono text-xs bg-[var(--color-bg)] px-2 py-0.5 rounded">{key.prefix}...</code>
1824
1863
  <span>Created {formatDate(key.created_at)}</span>
1825
1864
  {key.expires_at && <span>Expires {formatDate(key.expires_at)}</span>}
1826
1865
  {key.last_used_at && <span>Last used {formatDate(key.last_used_at)}</span>}
@@ -1841,9 +1880,9 @@ function ApiKeysSettings() {
1841
1880
 
1842
1881
  {/* Usage Info */}
1843
1882
  {keys.length > 0 && (
1844
- <div className="mt-6 bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
1883
+ <div className="mt-6 bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
1845
1884
  <h3 className="font-medium mb-2 text-sm">Usage</h3>
1846
- <code className="block bg-[#0a0a0a] px-3 py-2 rounded font-mono text-xs text-[#888]">
1885
+ <code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-xs text-[var(--color-text-secondary)]">
1847
1886
  curl -H "X-API-Key: apt_..." http://localhost:4280/api/agents
1848
1887
  </code>
1849
1888
  </div>
@@ -1909,26 +1948,26 @@ function AccountSettings() {
1909
1948
  <div className="max-w-4xl w-full">
1910
1949
  <div className="mb-6">
1911
1950
  <h1 className="text-2xl font-semibold mb-1">Account Settings</h1>
1912
- <p className="text-[#666]">Manage your account and security.</p>
1951
+ <p className="text-[var(--color-text-muted)]">Manage your account and security.</p>
1913
1952
  </div>
1914
1953
 
1915
1954
  {/* User Info */}
1916
1955
  {user && (
1917
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4 mb-6">
1956
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-6">
1918
1957
  <h3 className="font-medium mb-3">Profile</h3>
1919
1958
  <div className="space-y-2 text-sm">
1920
1959
  <div className="flex justify-between">
1921
- <span className="text-[#666]">Username</span>
1960
+ <span className="text-[var(--color-text-muted)]">Username</span>
1922
1961
  <span>{user.username}</span>
1923
1962
  </div>
1924
1963
  {user.email && (
1925
1964
  <div className="flex justify-between">
1926
- <span className="text-[#666]">Email</span>
1965
+ <span className="text-[var(--color-text-muted)]">Email</span>
1927
1966
  <span>{user.email}</span>
1928
1967
  </div>
1929
1968
  )}
1930
1969
  <div className="flex justify-between">
1931
- <span className="text-[#666]">Role</span>
1970
+ <span className="text-[var(--color-text-muted)]">Role</span>
1932
1971
  <span className="capitalize">{user.role}</span>
1933
1972
  </div>
1934
1973
  </div>
@@ -1936,37 +1975,37 @@ function AccountSettings() {
1936
1975
  )}
1937
1976
 
1938
1977
  {/* Change Password */}
1939
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
1978
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
1940
1979
  <h3 className="font-medium mb-4">Change Password</h3>
1941
1980
 
1942
1981
  <div className="space-y-4 max-w-md">
1943
1982
  <div>
1944
- <label className="block text-sm text-[#666] mb-1">Current Password</label>
1983
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Current Password</label>
1945
1984
  <input
1946
1985
  type="password"
1947
1986
  value={currentPassword}
1948
1987
  onChange={(e) => setCurrentPassword(e.target.value)}
1949
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1988
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1950
1989
  />
1951
1990
  </div>
1952
1991
 
1953
1992
  <div>
1954
- <label className="block text-sm text-[#666] mb-1">New Password</label>
1993
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">New Password</label>
1955
1994
  <input
1956
1995
  type="password"
1957
1996
  value={newPassword}
1958
1997
  onChange={(e) => setNewPassword(e.target.value)}
1959
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
1998
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1960
1999
  />
1961
2000
  </div>
1962
2001
 
1963
2002
  <div>
1964
- <label className="block text-sm text-[#666] mb-1">Confirm New Password</label>
2003
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Confirm New Password</label>
1965
2004
  <input
1966
2005
  type="password"
1967
2006
  value={confirmPassword}
1968
2007
  onChange={(e) => setConfirmPassword(e.target.value)}
1969
- className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
2008
+ className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
1970
2009
  />
1971
2010
  </div>
1972
2011
 
@@ -1983,7 +2022,7 @@ function AccountSettings() {
1983
2022
  <button
1984
2023
  onClick={handleChangePassword}
1985
2024
  disabled={saving || !currentPassword || !newPassword || !confirmPassword}
1986
- className="px-4 py-2 bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 disabled:cursor-not-allowed text-black rounded text-sm font-medium transition"
2025
+ className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black rounded text-sm font-medium transition"
1987
2026
  >
1988
2027
  {saving ? "Updating..." : "Update Password"}
1989
2028
  </button>
@@ -2044,12 +2083,12 @@ function DataSettings() {
2044
2083
  <div className="max-w-4xl w-full">
2045
2084
  <div className="mb-6">
2046
2085
  <h1 className="text-2xl font-semibold mb-1">Data Management</h1>
2047
- <p className="text-[#666]">Manage stored data and telemetry.</p>
2086
+ <p className="text-[var(--color-text-muted)]">Manage stored data and telemetry.</p>
2048
2087
  </div>
2049
2088
 
2050
- <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
2089
+ <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
2051
2090
  <h3 className="font-medium mb-2">Telemetry Data</h3>
2052
- <p className="text-sm text-[#666] mb-4">
2091
+ <p className="text-sm text-[var(--color-text-muted)] mb-4">
2053
2092
  {eventCount !== null
2054
2093
  ? `${eventCount.toLocaleString()} events stored`
2055
2094
  : "Loading..."}
@@ -2197,7 +2236,7 @@ function ChannelsSettings() {
2197
2236
 
2198
2237
  const statusColors: Record<string, string> = {
2199
2238
  running: "bg-green-500/20 text-green-400",
2200
- stopped: "bg-[#333] text-[#666]",
2239
+ stopped: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]",
2201
2240
  error: "bg-red-500/20 text-red-400",
2202
2241
  };
2203
2242
 
@@ -2212,11 +2251,11 @@ function ChannelsSettings() {
2212
2251
  <div className="flex items-center justify-between mb-6">
2213
2252
  <div>
2214
2253
  <h2 className="text-xl font-semibold mb-1">Channels</h2>
2215
- <p className="text-sm text-[#666]">Connect agents to external messaging platforms</p>
2254
+ <p className="text-sm text-[var(--color-text-muted)]">Connect agents to external messaging platforms</p>
2216
2255
  </div>
2217
2256
  <button
2218
2257
  onClick={() => setShowForm(!showForm)}
2219
- className="flex items-center gap-2 bg-[#f97316] hover:bg-[#fb923c] text-black px-3 py-1.5 rounded text-sm font-medium transition"
2258
+ className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-3 py-1.5 rounded text-sm font-medium transition"
2220
2259
  >
2221
2260
  <PlusIcon /> Add Channel
2222
2261
  </button>
@@ -2233,22 +2272,22 @@ function ChannelsSettings() {
2233
2272
 
2234
2273
  {/* Create form */}
2235
2274
  {showForm && (
2236
- <div className="mb-6 bg-[#111] border border-[#1a1a1a] rounded-lg p-4 space-y-3">
2237
- <h3 className="text-sm font-medium text-[#888] mb-2">New Telegram Channel</h3>
2275
+ <div className="mb-6 bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 space-y-3">
2276
+ <h3 className="text-sm font-medium text-[var(--color-text-secondary)] mb-2">New Telegram Channel</h3>
2238
2277
 
2239
2278
  <div>
2240
- <label className="block text-xs text-[#666] mb-1">Channel Name</label>
2279
+ <label className="block text-xs text-[var(--color-text-muted)] mb-1">Channel Name</label>
2241
2280
  <input
2242
2281
  type="text"
2243
2282
  value={formData.name}
2244
2283
  onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
2245
2284
  placeholder="e.g. My Telegram Bot"
2246
- className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#f97316]"
2285
+ 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)]"
2247
2286
  />
2248
2287
  </div>
2249
2288
 
2250
2289
  <div>
2251
- <label className="block text-xs text-[#666] mb-1">Agent</label>
2290
+ <label className="block text-xs text-[var(--color-text-muted)] mb-1">Agent</label>
2252
2291
  <Select
2253
2292
  value={formData.agent_id}
2254
2293
  options={agents.map(a => ({ value: a.id, label: a.name }))}
@@ -2258,16 +2297,16 @@ function ChannelsSettings() {
2258
2297
  </div>
2259
2298
 
2260
2299
  <div>
2261
- <label className="block text-xs text-[#666] mb-1">Bot Token</label>
2300
+ <label className="block text-xs text-[var(--color-text-muted)] mb-1">Bot Token</label>
2262
2301
  <input
2263
2302
  type="password"
2264
2303
  value={formData.botToken}
2265
2304
  onChange={e => setFormData(prev => ({ ...prev, botToken: e.target.value }))}
2266
2305
  placeholder="From @BotFather on Telegram"
2267
- className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#f97316]"
2306
+ 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)]"
2268
2307
  />
2269
- <p className="text-xs text-[#555] mt-1">
2270
- Create a bot via <a href="https://t.me/BotFather" target="_blank" className="text-[#f97316] hover:underline">@BotFather</a> on Telegram to get a token.
2308
+ <p className="text-xs text-[var(--color-text-faint)] mt-1">
2309
+ Create a bot via <a href="https://t.me/BotFather" target="_blank" className="text-[var(--color-accent)] hover:underline">@BotFather</a> on Telegram to get a token.
2271
2310
  </p>
2272
2311
  </div>
2273
2312
 
@@ -2275,13 +2314,13 @@ function ChannelsSettings() {
2275
2314
  <button
2276
2315
  onClick={createChannel}
2277
2316
  disabled={creating || !formData.name || !formData.agent_id || !formData.botToken}
2278
- className="bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 text-black px-4 py-1.5 rounded text-sm font-medium transition"
2317
+ className="bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black px-4 py-1.5 rounded text-sm font-medium transition"
2279
2318
  >
2280
2319
  {creating ? "Creating..." : "Create"}
2281
2320
  </button>
2282
2321
  <button
2283
2322
  onClick={() => { setShowForm(false); setFormData({ name: "", agent_id: "", botToken: "" }); }}
2284
- className="border border-[#333] hover:border-[#444] px-4 py-1.5 rounded text-sm transition"
2323
+ className="border border-[var(--color-border-light)] hover:border-[var(--color-scrollbar)] px-4 py-1.5 rounded text-sm transition"
2285
2324
  >
2286
2325
  Cancel
2287
2326
  </button>
@@ -2291,16 +2330,16 @@ function ChannelsSettings() {
2291
2330
 
2292
2331
  {/* Channel list */}
2293
2332
  {loading ? (
2294
- <p className="text-[#666] text-sm">Loading channels...</p>
2333
+ <p className="text-[var(--color-text-muted)] text-sm">Loading channels...</p>
2295
2334
  ) : channels.length === 0 ? (
2296
- <div className="text-center py-12 text-[#666]">
2335
+ <div className="text-center py-12 text-[var(--color-text-muted)]">
2297
2336
  <p className="text-lg mb-2">No channels configured</p>
2298
2337
  <p className="text-sm">Add a Telegram channel to let users message your agents directly.</p>
2299
2338
  </div>
2300
2339
  ) : (
2301
2340
  <div className="space-y-3">
2302
2341
  {channels.map(channel => (
2303
- <div key={channel.id} className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
2342
+ <div key={channel.id} className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
2304
2343
  <div className="flex items-start justify-between">
2305
2344
  <div className="flex-1 min-w-0">
2306
2345
  <div className="flex items-center gap-2 mb-1">
@@ -2309,7 +2348,7 @@ function ChannelsSettings() {
2309
2348
  {channel.status}
2310
2349
  </span>
2311
2350
  </div>
2312
- <p className="text-sm text-[#666]">
2351
+ <p className="text-sm text-[var(--color-text-muted)]">
2313
2352
  {channel.type === "telegram" ? "Telegram" : channel.type} → {getAgentName(channel.agent_id)}
2314
2353
  </p>
2315
2354
  {channel.status === "error" && channel.error && (
@@ -2321,7 +2360,7 @@ function ChannelsSettings() {
2321
2360
  onClick={() => toggleChannel(channel)}
2322
2361
  className={`px-3 py-1 rounded text-xs font-medium transition ${
2323
2362
  channel.status === "running"
2324
- ? "bg-[#f97316]/20 text-[#f97316] hover:bg-[#f97316]/30"
2363
+ ? "bg-[var(--color-accent-20)] text-[var(--color-accent)] hover:bg-[var(--color-accent-30)]"
2325
2364
  : "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
2326
2365
  }`}
2327
2366
  >
@@ -2329,7 +2368,7 @@ function ChannelsSettings() {
2329
2368
  </button>
2330
2369
  <button
2331
2370
  onClick={() => deleteChannel(channel)}
2332
- className="text-[#666] hover:text-red-400 transition text-sm"
2371
+ className="text-[var(--color-text-muted)] hover:text-red-400 transition text-sm"
2333
2372
  >
2334
2373
  ×
2335
2374
  </button>
@@ -2355,9 +2394,11 @@ function AssistantSettings() {
2355
2394
  const [saving, setSaving] = useState(false);
2356
2395
  const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
2357
2396
  const [starting, setStarting] = useState(false);
2397
+ const [webSearch, setWebSearch] = useState(false);
2398
+ const [webFetch, setWebFetch] = useState(false);
2358
2399
 
2359
2400
  // Original values for change detection
2360
- const [original, setOriginal] = useState({ provider: "", model: "", systemPrompt: "" });
2401
+ const [original, setOriginal] = useState({ provider: "", model: "", systemPrompt: "", webSearch: false, webFetch: false });
2361
2402
 
2362
2403
  useEffect(() => {
2363
2404
  const fetchData = async () => {
@@ -2376,7 +2417,11 @@ function AssistantSettings() {
2376
2417
  setModel(a.model || "");
2377
2418
  setSystemPrompt(a.systemPrompt || "");
2378
2419
  setStatus(a.status || "stopped");
2379
- setOriginal({ provider: a.provider || "", model: a.model || "", systemPrompt: a.systemPrompt || "" });
2420
+ const ws = a.features?.builtinTools?.webSearch || false;
2421
+ const wf = a.features?.builtinTools?.webFetch || false;
2422
+ setWebSearch(ws);
2423
+ setWebFetch(wf);
2424
+ setOriginal({ provider: a.provider || "", model: a.model || "", systemPrompt: a.systemPrompt || "", webSearch: ws, webFetch: wf });
2380
2425
  }
2381
2426
  } catch {
2382
2427
  setMessage({ type: "error", text: "Failed to load assistant config" });
@@ -2397,7 +2442,7 @@ function AssistantSettings() {
2397
2442
  setModel(defaultModel);
2398
2443
  };
2399
2444
 
2400
- const hasChanges = provider !== original.provider || model !== original.model || systemPrompt !== original.systemPrompt;
2445
+ const hasChanges = provider !== original.provider || model !== original.model || systemPrompt !== original.systemPrompt || webSearch !== original.webSearch || webFetch !== original.webFetch;
2401
2446
 
2402
2447
  const handleSave = async () => {
2403
2448
  setSaving(true);
@@ -2406,10 +2451,10 @@ function AssistantSettings() {
2406
2451
  const res = await authFetch("/api/agents/apteva-assistant", {
2407
2452
  method: "PUT",
2408
2453
  headers: { "Content-Type": "application/json" },
2409
- body: JSON.stringify({ provider, model, systemPrompt }),
2454
+ body: JSON.stringify({ provider, model, systemPrompt, features: { builtinTools: { webSearch, webFetch } } }),
2410
2455
  });
2411
2456
  if (res.ok) {
2412
- setOriginal({ provider, model, systemPrompt });
2457
+ setOriginal({ provider, model, systemPrompt, webSearch, webFetch });
2413
2458
  setMessage({ type: "success", text: "Assistant settings saved" });
2414
2459
  setTimeout(() => setMessage(null), 3000);
2415
2460
  } else {
@@ -2443,13 +2488,13 @@ function AssistantSettings() {
2443
2488
  };
2444
2489
 
2445
2490
  if (loading) {
2446
- return <div className="text-[#666]">Loading assistant settings...</div>;
2491
+ return <div className="text-[var(--color-text-muted)]">Loading assistant settings...</div>;
2447
2492
  }
2448
2493
 
2449
2494
  return (
2450
2495
  <div className="max-w-2xl">
2451
2496
  <h2 className="text-lg font-medium mb-1">Apteva Assistant</h2>
2452
- <p className="text-sm text-[#666] mb-6">Configure the built-in AI assistant that manages your agents and platform.</p>
2497
+ <p className="text-sm text-[var(--color-text-muted)] mb-6">Configure the built-in AI assistant that manages your agents and platform.</p>
2453
2498
 
2454
2499
  {message && (
2455
2500
  <div className={`mb-4 px-3 py-2 rounded text-sm ${
@@ -2461,9 +2506,9 @@ function AssistantSettings() {
2461
2506
 
2462
2507
  {/* Status */}
2463
2508
  <div className="mb-6 flex items-center gap-3">
2464
- <span className="text-sm text-[#666]">Status:</span>
2509
+ <span className="text-sm text-[var(--color-text-muted)]">Status:</span>
2465
2510
  <span className={`px-2 py-1 rounded text-xs font-medium ${
2466
- status === "running" ? "bg-[#3b82f6]/20 text-[#3b82f6]" : "bg-[#333] text-[#666]"
2511
+ status === "running" ? "bg-[#3b82f6]/20 text-[#3b82f6]" : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
2467
2512
  }`}>
2468
2513
  {status}
2469
2514
  </span>
@@ -2472,7 +2517,7 @@ function AssistantSettings() {
2472
2517
  disabled={starting}
2473
2518
  className={`px-3 py-1.5 rounded text-sm font-medium transition ${
2474
2519
  status === "running"
2475
- ? "bg-[#f97316]/20 text-[#f97316] hover:bg-[#f97316]/30"
2520
+ ? "bg-[var(--color-accent-20)] text-[var(--color-accent)] hover:bg-[var(--color-accent-30)]"
2476
2521
  : "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
2477
2522
  } disabled:opacity-50`}
2478
2523
  >
@@ -2482,7 +2527,7 @@ function AssistantSettings() {
2482
2527
 
2483
2528
  {/* Provider */}
2484
2529
  <div className="mb-4">
2485
- <label className="block text-sm text-[#666] mb-1">Provider</label>
2530
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Provider</label>
2486
2531
  <Select
2487
2532
  value={provider}
2488
2533
  onChange={handleProviderChange}
@@ -2493,7 +2538,7 @@ function AssistantSettings() {
2493
2538
 
2494
2539
  {/* Model */}
2495
2540
  <div className="mb-4">
2496
- <label className="block text-sm text-[#666] mb-1">Model</label>
2541
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Model</label>
2497
2542
  <Select
2498
2543
  value={model}
2499
2544
  onChange={setModel}
@@ -2502,14 +2547,52 @@ function AssistantSettings() {
2502
2547
  />
2503
2548
  </div>
2504
2549
 
2550
+ {/* Built-in Tools - Anthropic only */}
2551
+ {provider === "anthropic" && (
2552
+ <div className="mb-4">
2553
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">Built-in Tools</label>
2554
+ <div className="flex flex-wrap gap-2">
2555
+ <button
2556
+ type="button"
2557
+ onClick={() => setWebSearch(!webSearch)}
2558
+ className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
2559
+ webSearch
2560
+ ? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
2561
+ : "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
2562
+ }`}
2563
+ >
2564
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2565
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
2566
+ </svg>
2567
+ <span className="text-sm">Web Search</span>
2568
+ </button>
2569
+ <button
2570
+ type="button"
2571
+ onClick={() => setWebFetch(!webFetch)}
2572
+ className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
2573
+ webFetch
2574
+ ? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
2575
+ : "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
2576
+ }`}
2577
+ >
2578
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2579
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
2580
+ </svg>
2581
+ <span className="text-sm">Web Fetch</span>
2582
+ </button>
2583
+ </div>
2584
+ <p className="text-xs text-[var(--color-text-faint)] mt-2">Provider-native tools for real-time web access</p>
2585
+ </div>
2586
+ )}
2587
+
2505
2588
  {/* System Prompt */}
2506
2589
  <div className="mb-6">
2507
- <label className="block text-sm text-[#666] mb-1">System Prompt</label>
2590
+ <label className="block text-sm text-[var(--color-text-muted)] mb-1">System Prompt</label>
2508
2591
  <textarea
2509
2592
  value={systemPrompt}
2510
2593
  onChange={e => setSystemPrompt(e.target.value)}
2511
2594
  rows={12}
2512
- className="w-full bg-[#111] border border-[#1a1a1a] rounded px-3 py-2 text-sm font-mono focus:outline-none focus:border-[#f97316] resize-y"
2595
+ className="w-full bg-[var(--color-surface)] border border-[var(--color-border)] rounded px-3 py-2 text-sm font-mono focus:outline-none focus:border-[var(--color-accent)] resize-y"
2513
2596
  />
2514
2597
  </div>
2515
2598
 
@@ -2517,13 +2600,13 @@ function AssistantSettings() {
2517
2600
  <button
2518
2601
  onClick={handleSave}
2519
2602
  disabled={!hasChanges || saving}
2520
- className="bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
2603
+ className="bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
2521
2604
  >
2522
2605
  {saving ? "Saving..." : "Save Changes"}
2523
2606
  </button>
2524
2607
 
2525
2608
  {status === "running" && hasChanges && (
2526
- <p className="text-xs text-[#666] mt-2">Changes will be applied to the running assistant</p>
2609
+ <p className="text-xs text-[var(--color-text-muted)] mt-2">Changes will be applied to the running assistant</p>
2527
2610
  )}
2528
2611
  </div>
2529
2612
  );