@swarmclawai/swarmclaw 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -22
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +36 -7
- package/src/app/api/agents/route.ts +12 -1
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/main-loop/route.ts +7 -88
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +18 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/route.ts +16 -0
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +4 -0
- package/src/cli/index.ts +3 -10
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +207 -16
- package/src/components/agents/inspector-panel.tsx +108 -48
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/chat/chat-area.tsx +29 -13
- package/src/components/chat/chat-card.tsx +4 -20
- package/src/components/chat/chat-header.tsx +255 -353
- package/src/components/chat/chat-list.tsx +7 -9
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +3 -1
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/layout/app-layout.tsx +383 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +77 -0
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +245 -46
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +250 -61
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +45 -5
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +946 -110
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +59 -1
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +13 -39
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +27 -967
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +17 -6
- package/src/lib/server/orchestrator.ts +2 -2
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +822 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/queue.ts +3 -20
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +70 -32
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery.ts +22 -4
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +237 -24
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +56 -1
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +150 -7
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +86 -23
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +36 -3
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/subagent.ts +193 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +896 -100
- package/src/lib/server/storage.ts +226 -7
- package/src/lib/server/stream-agent-chat.ts +46 -21
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +44 -7
- package/src/lib/server/tool-capability-policy.ts +6 -0
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +7 -0
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +0 -6
- package/src/stores/use-chat-store.ts +31 -2
- package/src/types/index.ts +287 -44
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useState, type ReactNode } from 'react'
|
|
4
4
|
import type { Agent } from '@/types'
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
|
+
import { AgentAvatar } from './agent-avatar'
|
|
6
7
|
import { AgentFilesEditor } from './agent-files-editor'
|
|
7
8
|
import { OpenClawSkillsPanel } from './openclaw-skills-panel'
|
|
8
9
|
import { PermissionPresetSelector } from './permission-preset-selector'
|
|
@@ -29,6 +30,24 @@ const TABS: { id: InspectorTab; label: string; openclawOnly?: boolean }[] = [
|
|
|
29
30
|
{ id: 'advanced', label: 'Advanced' },
|
|
30
31
|
]
|
|
31
32
|
|
|
33
|
+
const PROVIDER_LABELS: Record<string, string> = {
|
|
34
|
+
'claude-cli': 'Claude CLI',
|
|
35
|
+
'codex-cli': 'Codex CLI',
|
|
36
|
+
'opencode-cli': 'OpenCode CLI',
|
|
37
|
+
openai: 'OpenAI',
|
|
38
|
+
anthropic: 'Anthropic',
|
|
39
|
+
openclaw: 'OpenClaw',
|
|
40
|
+
ollama: 'Ollama',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function panelCardClass(className = '') {
|
|
44
|
+
return `rounded-[16px] border border-white/[0.06] bg-white/[0.03] shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] ${className}`.trim()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function SectionLabel({ children }: { children: ReactNode }) {
|
|
48
|
+
return <label className="block text-[11px] font-700 uppercase tracking-[0.16em] text-text-3/45 mb-2">{children}</label>
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDeleteChat, isMainChat }: Props) {
|
|
33
52
|
const inspectorTab = useAppStore((s) => s.inspectorTab)
|
|
34
53
|
const setInspectorTab = useAppStore((s) => s.setInspectorTab)
|
|
@@ -55,15 +74,40 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
55
74
|
}, [setInspectorOpen])
|
|
56
75
|
|
|
57
76
|
const agentSchedules = Object.values(schedules).filter((s) => s.agentId === agent.id)
|
|
77
|
+
const providerLabel = PROVIDER_LABELS[agent.provider] || agent.provider.replace(/-/g, ' ')
|
|
58
78
|
|
|
59
79
|
return (
|
|
60
|
-
<div className="w-[
|
|
80
|
+
<div className="w-[420px] shrink-0 border-l border-white/[0.06] bg-bg flex flex-col h-full overflow-hidden fade-up-delay"
|
|
81
|
+
style={{ background: 'radial-gradient(circle at top right, rgba(66, 211, 255, 0.06), transparent 30%), linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0))' }}>
|
|
61
82
|
{/* Header */}
|
|
62
|
-
<div className="
|
|
63
|
-
<
|
|
83
|
+
<div className="px-4 pt-4 pb-3 border-b border-white/[0.06] shrink-0 bg-black/[0.12]">
|
|
84
|
+
<div className="flex items-start gap-3">
|
|
85
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={40} />
|
|
86
|
+
<div className="min-w-0 flex-1">
|
|
87
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
88
|
+
<h3 className="font-display text-[16px] font-700 text-text truncate tracking-[-0.02em]">{agent.name}</h3>
|
|
89
|
+
{agent.heartbeatEnabled && (
|
|
90
|
+
<span className="inline-flex items-center gap-1 rounded-[7px] border border-emerald-400/15 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-emerald-300">
|
|
91
|
+
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
|
92
|
+
Heartbeat
|
|
93
|
+
</span>
|
|
94
|
+
)}
|
|
95
|
+
</div>
|
|
96
|
+
<div className="mt-1 flex flex-wrap items-center gap-1.5">
|
|
97
|
+
<span className="inline-flex items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-600 text-text-3/70">
|
|
98
|
+
{providerLabel}
|
|
99
|
+
</span>
|
|
100
|
+
<span className="inline-flex max-w-[180px] items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-mono text-text-3/70 truncate">
|
|
101
|
+
{agent.model || 'Default model'}
|
|
102
|
+
</span>
|
|
103
|
+
<span className="inline-flex items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-600 text-text-3/70">
|
|
104
|
+
{(agent.plugins?.length ?? 0)} plugins
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
64
108
|
<button
|
|
65
109
|
onClick={() => setInspectorOpen(false)}
|
|
66
|
-
className="p-1 rounded-[
|
|
110
|
+
className="p-1.5 rounded-[8px] text-text-3/50 hover:text-text-3 bg-transparent border-none cursor-pointer transition-all hover:bg-white/[0.04]"
|
|
67
111
|
aria-label="Close inspector"
|
|
68
112
|
>
|
|
69
113
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
@@ -71,26 +115,29 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
71
115
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
72
116
|
</svg>
|
|
73
117
|
</button>
|
|
118
|
+
</div>
|
|
74
119
|
</div>
|
|
75
120
|
|
|
76
121
|
{/* Tab bar */}
|
|
77
|
-
<div className="
|
|
122
|
+
<div className="px-4 py-3 shrink-0">
|
|
123
|
+
<div className="flex gap-1 rounded-[12px] border border-white/[0.06] bg-black/[0.12] p-1 overflow-x-auto" role="tablist">
|
|
78
124
|
{visibleTabs.map((tab) => (
|
|
79
125
|
<button
|
|
80
126
|
key={tab.id}
|
|
81
127
|
role="tab"
|
|
82
128
|
onClick={() => setInspectorTab(tab.id)}
|
|
83
129
|
aria-selected={inspectorTab === tab.id}
|
|
84
|
-
className={`px-
|
|
130
|
+
className={`px-3 py-1.5 rounded-[9px] text-[11px] font-700 cursor-pointer transition-all whitespace-nowrap focus-visible:ring-1 focus-visible:ring-accent-bright/50
|
|
85
131
|
${inspectorTab === tab.id
|
|
86
|
-
? 'bg-
|
|
87
|
-
: 'bg-transparent text-text-3 hover:text-text-2'}`}
|
|
132
|
+
? 'bg-white/[0.08] text-text'
|
|
133
|
+
: 'bg-transparent text-text-3/65 hover:text-text-2'}`}
|
|
88
134
|
style={{ fontFamily: 'inherit' }}
|
|
89
135
|
>
|
|
90
136
|
{tab.label}
|
|
91
137
|
</button>
|
|
92
138
|
))}
|
|
93
139
|
</div>
|
|
140
|
+
</div>
|
|
94
141
|
|
|
95
142
|
{/* Tab content */}
|
|
96
143
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
@@ -141,30 +188,43 @@ interface OverviewTabProps {
|
|
|
141
188
|
}
|
|
142
189
|
|
|
143
190
|
function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDeleteChat, isMainChat }: OverviewTabProps) {
|
|
191
|
+
const summaryStats = [
|
|
192
|
+
{ label: 'Provider', value: PROVIDER_LABELS[agent.provider] || agent.provider.replace(/-/g, ' ') },
|
|
193
|
+
{ label: 'Model', value: agent.model || 'Default' },
|
|
194
|
+
{ label: 'Plugins', value: String(agent.plugins?.length ?? 0) },
|
|
195
|
+
{ label: 'Heartbeat', value: agent.heartbeatEnabled ? `Every ${agent.heartbeatIntervalSec ?? 120}s` : 'Off' },
|
|
196
|
+
]
|
|
197
|
+
|
|
144
198
|
return (
|
|
145
199
|
<div className="p-4 flex flex-col gap-4">
|
|
146
|
-
<div>
|
|
147
|
-
<
|
|
148
|
-
<p className="text-[
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
200
|
+
<div className={panelCardClass('p-4 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.02))]')}>
|
|
201
|
+
<SectionLabel>Overview</SectionLabel>
|
|
202
|
+
<p className="text-[14px] text-text-2 leading-relaxed">
|
|
203
|
+
{agent.description || 'No description yet. Use the agent editor to define what this agent is for.'}
|
|
204
|
+
</p>
|
|
205
|
+
<div className="mt-4 grid grid-cols-2 gap-2">
|
|
206
|
+
{summaryStats.map((item) => (
|
|
207
|
+
<div key={item.label} className="rounded-[12px] border border-white/[0.06] bg-black/[0.14] px-3 py-2.5">
|
|
208
|
+
<div className="text-[10px] font-700 uppercase tracking-[0.14em] text-text-3/45">{item.label}</div>
|
|
209
|
+
<div className="mt-1 text-[12px] text-text-2 font-medium break-words">{item.value}</div>
|
|
210
|
+
</div>
|
|
211
|
+
))}
|
|
212
|
+
</div>
|
|
153
213
|
</div>
|
|
154
214
|
{agent.systemPrompt && (
|
|
155
|
-
<div>
|
|
156
|
-
<
|
|
157
|
-
<p className="text-[12px] text-text-3 bg-
|
|
215
|
+
<div className={panelCardClass('p-4')}>
|
|
216
|
+
<SectionLabel>System Prompt</SectionLabel>
|
|
217
|
+
<p className="text-[12px] text-text-3 bg-black/[0.14] rounded-[12px] p-3 border border-white/[0.04] max-h-[220px] overflow-y-auto whitespace-pre-wrap font-mono leading-relaxed">
|
|
158
218
|
{agent.systemPrompt}
|
|
159
219
|
</p>
|
|
160
220
|
</div>
|
|
161
221
|
)}
|
|
162
222
|
{agent.capabilities && agent.capabilities.length > 0 && (
|
|
163
|
-
<div>
|
|
164
|
-
<
|
|
165
|
-
<div className="flex flex-wrap gap-1">
|
|
223
|
+
<div className={panelCardClass('p-4')}>
|
|
224
|
+
<SectionLabel>Capabilities</SectionLabel>
|
|
225
|
+
<div className="flex flex-wrap gap-1.5">
|
|
166
226
|
{agent.capabilities.map((cap) => (
|
|
167
|
-
<span key={cap} className="px-2 py-
|
|
227
|
+
<span key={cap} className="px-2.5 py-1 rounded-[8px] text-[11px] font-700 bg-accent-soft/70 text-accent-bright border border-accent-bright/10">
|
|
168
228
|
{cap}
|
|
169
229
|
</span>
|
|
170
230
|
))}
|
|
@@ -172,11 +232,11 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
172
232
|
</div>
|
|
173
233
|
)}
|
|
174
234
|
{agent.plugins && agent.plugins.length > 0 && (
|
|
175
|
-
<div>
|
|
176
|
-
<
|
|
177
|
-
<div className="flex flex-wrap gap-1">
|
|
235
|
+
<div className={panelCardClass('p-4')}>
|
|
236
|
+
<SectionLabel>Plugins</SectionLabel>
|
|
237
|
+
<div className="flex flex-wrap gap-1.5">
|
|
178
238
|
{agent.plugins.map((tool) => (
|
|
179
|
-
<span key={tool} className="px-2 py-
|
|
239
|
+
<span key={tool} className="px-2.5 py-1 rounded-[8px] text-[11px] font-700 bg-sky-400/[0.08] text-sky-300 border border-sky-400/[0.08]">
|
|
180
240
|
{tool}
|
|
181
241
|
</span>
|
|
182
242
|
))}
|
|
@@ -186,13 +246,13 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
186
246
|
|
|
187
247
|
{/* Actions */}
|
|
188
248
|
{(onEditAgent || onClearHistory || onDeleteAgent || onDeleteChat) && (
|
|
189
|
-
|
|
190
|
-
<
|
|
249
|
+
<div className={panelCardClass('p-4')}>
|
|
250
|
+
<SectionLabel>Actions</SectionLabel>
|
|
191
251
|
<div className="flex flex-col gap-2">
|
|
192
252
|
{onEditAgent && (
|
|
193
253
|
<button
|
|
194
254
|
onClick={onEditAgent}
|
|
195
|
-
className="w-full px-3 py-2 rounded-[
|
|
255
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-accent-bright bg-accent-soft/50 border border-accent-bright/10 cursor-pointer transition-all hover:bg-accent-soft text-left"
|
|
196
256
|
style={{ fontFamily: 'inherit' }}
|
|
197
257
|
>
|
|
198
258
|
Edit Agent
|
|
@@ -200,12 +260,12 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
200
260
|
)}
|
|
201
261
|
{(onClearHistory || onDeleteAgent || onDeleteChat) && (
|
|
202
262
|
<>
|
|
203
|
-
<
|
|
263
|
+
<SectionLabel>Danger Zone</SectionLabel>
|
|
204
264
|
<div className="flex flex-col gap-1.5">
|
|
205
265
|
{onClearHistory && (
|
|
206
266
|
<button
|
|
207
267
|
onClick={onClearHistory}
|
|
208
|
-
className="w-full px-3 py-2 rounded-[
|
|
268
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
209
269
|
style={{ fontFamily: 'inherit' }}
|
|
210
270
|
>
|
|
211
271
|
Clear History
|
|
@@ -214,7 +274,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
214
274
|
{onDeleteAgent && !isMainChat && (
|
|
215
275
|
<button
|
|
216
276
|
onClick={onDeleteAgent}
|
|
217
|
-
className="w-full px-3 py-2 rounded-[
|
|
277
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
218
278
|
style={{ fontFamily: 'inherit' }}
|
|
219
279
|
>
|
|
220
280
|
Delete Agent
|
|
@@ -223,7 +283,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
223
283
|
{onDeleteChat && !isMainChat && (
|
|
224
284
|
<button
|
|
225
285
|
onClick={onDeleteChat}
|
|
226
|
-
className="w-full px-3 py-2 rounded-[
|
|
286
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
227
287
|
style={{ fontFamily: 'inherit' }}
|
|
228
288
|
>
|
|
229
289
|
Delete Chat
|
|
@@ -233,7 +293,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
233
293
|
</>
|
|
234
294
|
)}
|
|
235
295
|
</div>
|
|
236
|
-
|
|
296
|
+
</div>
|
|
237
297
|
)}
|
|
238
298
|
</div>
|
|
239
299
|
)
|
|
@@ -277,7 +337,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
277
337
|
<div className="p-4 flex flex-col gap-3">
|
|
278
338
|
{/* Local schedules */}
|
|
279
339
|
{schedules.map((s) => (
|
|
280
|
-
<div key={s.id} className=
|
|
340
|
+
<div key={s.id} className={panelCardClass('py-2.5 px-3.5')}>
|
|
281
341
|
<div className="flex items-center gap-2">
|
|
282
342
|
<span className="text-[13px] font-600 text-text truncate flex-1">{s.name}</span>
|
|
283
343
|
<span className={`text-[10px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[4px]
|
|
@@ -296,7 +356,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
296
356
|
<>
|
|
297
357
|
{cronLoading && <div className="text-[12px] text-text-3/50">Loading gateway crons...</div>}
|
|
298
358
|
{gatewayCrons.map((c) => (
|
|
299
|
-
<div key={c.id} className=
|
|
359
|
+
<div key={c.id} className={panelCardClass('py-2.5 px-3.5')}>
|
|
300
360
|
<div className="flex items-center gap-2">
|
|
301
361
|
<span className="text-[13px] font-600 text-text truncate flex-1">{c.name}</span>
|
|
302
362
|
<span className={`text-[10px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[4px]
|
|
@@ -329,7 +389,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
329
389
|
)}
|
|
330
390
|
|
|
331
391
|
{!schedules.length && !gatewayCrons.length && !cronLoading && !showCronForm && (
|
|
332
|
-
<div className=
|
|
392
|
+
<div className={panelCardClass('p-4 text-[13px] text-text-3/50')}>No automations linked to this agent.</div>
|
|
333
393
|
)}
|
|
334
394
|
</div>
|
|
335
395
|
)
|
|
@@ -354,8 +414,8 @@ function AdvancedTab({ agent }: { agent: Agent }) {
|
|
|
354
414
|
)}
|
|
355
415
|
|
|
356
416
|
{agent.heartbeatEnabled && (
|
|
357
|
-
<div>
|
|
358
|
-
<
|
|
417
|
+
<div className={panelCardClass('p-4')}>
|
|
418
|
+
<SectionLabel>Heartbeat</SectionLabel>
|
|
359
419
|
<p className="text-[13px] text-text-2">
|
|
360
420
|
Every {agent.heartbeatIntervalSec ?? 120}s
|
|
361
421
|
{agent.heartbeatModel && ` (${agent.heartbeatModel})`}
|
|
@@ -363,21 +423,21 @@ function AdvancedTab({ agent }: { agent: Agent }) {
|
|
|
363
423
|
</div>
|
|
364
424
|
)}
|
|
365
425
|
{agent.thinkingLevel && (
|
|
366
|
-
<div>
|
|
367
|
-
<
|
|
426
|
+
<div className={panelCardClass('p-4')}>
|
|
427
|
+
<SectionLabel>Thinking Level</SectionLabel>
|
|
368
428
|
<p className="text-[13px] text-text-2 capitalize">{agent.thinkingLevel}</p>
|
|
369
429
|
</div>
|
|
370
430
|
)}
|
|
371
|
-
<div>
|
|
372
|
-
<
|
|
431
|
+
<div className={panelCardClass('p-4')}>
|
|
432
|
+
<SectionLabel>Agent ID</SectionLabel>
|
|
373
433
|
<p className="text-[12px] text-text-3 font-mono select-all">{agent.id}</p>
|
|
374
434
|
</div>
|
|
375
|
-
<div>
|
|
376
|
-
<
|
|
435
|
+
<div className={panelCardClass('p-4')}>
|
|
436
|
+
<SectionLabel>Created</SectionLabel>
|
|
377
437
|
<p className="text-[12px] text-text-3">{new Date(agent.createdAt).toLocaleString()}</p>
|
|
378
438
|
</div>
|
|
379
|
-
<div>
|
|
380
|
-
<
|
|
439
|
+
<div className={panelCardClass('p-4')}>
|
|
440
|
+
<SectionLabel>Updated</SectionLabel>
|
|
381
441
|
<p className="text-[12px] text-text-3">{new Date(agent.updatedAt).toLocaleString()}</p>
|
|
382
442
|
</div>
|
|
383
443
|
</div>
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { useState, useEffect } from 'react'
|
|
4
4
|
import { setStoredAccessKey } from '@/lib/api-client'
|
|
5
5
|
import { fetchWithTimeout } from '@/lib/fetch-timeout'
|
|
6
|
-
import { copyTextToClipboard } from '@/lib/clipboard'
|
|
7
6
|
|
|
8
7
|
interface AccessKeyGateProps {
|
|
9
8
|
onAuthenticated: () => void
|
|
@@ -19,8 +18,6 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
19
18
|
|
|
20
19
|
// First-time setup state
|
|
21
20
|
const [firstTime, setFirstTime] = useState(false)
|
|
22
|
-
const [generatedKey, setGeneratedKey] = useState('')
|
|
23
|
-
const [copied, setCopied] = useState(false)
|
|
24
21
|
|
|
25
22
|
useEffect(() => {
|
|
26
23
|
let cancelled = false
|
|
@@ -28,9 +25,8 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
28
25
|
try {
|
|
29
26
|
const res = await fetchWithTimeout('/api/auth', {}, AUTH_CHECK_TIMEOUT_MS)
|
|
30
27
|
const data = await res.json().catch(() => ({}))
|
|
31
|
-
if (!cancelled && data.firstTime
|
|
28
|
+
if (!cancelled && data.firstTime) {
|
|
32
29
|
setFirstTime(true)
|
|
33
|
-
setGeneratedKey(data.key)
|
|
34
30
|
}
|
|
35
31
|
} catch (err) {
|
|
36
32
|
console.error('Auth check failed:', err)
|
|
@@ -41,38 +37,6 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
41
37
|
return () => { cancelled = true }
|
|
42
38
|
}, [])
|
|
43
39
|
|
|
44
|
-
const handleCopyKey = async () => {
|
|
45
|
-
try {
|
|
46
|
-
const copiedKey = await copyTextToClipboard(generatedKey)
|
|
47
|
-
if (!copiedKey) return
|
|
48
|
-
setCopied(true)
|
|
49
|
-
setTimeout(() => setCopied(false), 2000)
|
|
50
|
-
} catch {
|
|
51
|
-
// Fallback: select the text
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const handleClaimKey = async () => {
|
|
56
|
-
setLoading(true)
|
|
57
|
-
try {
|
|
58
|
-
const res = await fetchWithTimeout('/api/auth', {
|
|
59
|
-
method: 'POST',
|
|
60
|
-
headers: { 'Content-Type': 'application/json' },
|
|
61
|
-
body: JSON.stringify({ key: generatedKey }),
|
|
62
|
-
}, AUTH_CHECK_TIMEOUT_MS)
|
|
63
|
-
if (res.ok) {
|
|
64
|
-
setStoredAccessKey(generatedKey)
|
|
65
|
-
onAuthenticated()
|
|
66
|
-
} else {
|
|
67
|
-
setError('Invalid access key')
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
setError('Connection failed')
|
|
71
|
-
} finally {
|
|
72
|
-
setLoading(false)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
40
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
77
41
|
e.preventDefault()
|
|
78
42
|
const trimmed = key.trim()
|
|
@@ -148,77 +112,52 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
148
112
|
</div>
|
|
149
113
|
|
|
150
114
|
{firstTime ? (
|
|
151
|
-
/* ── First-time setup:
|
|
115
|
+
/* ── First-time setup: prompt for the key without disclosing it over HTTP ── */
|
|
152
116
|
<>
|
|
153
117
|
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.1s both' }}>
|
|
154
118
|
<h1 className="font-display text-[36px] font-800 leading-[1.05] tracking-[-0.04em] mb-3">
|
|
155
|
-
|
|
119
|
+
First-Time Setup
|
|
156
120
|
</h1>
|
|
157
121
|
<p className="text-[14px] text-text-2 mb-8">
|
|
158
|
-
|
|
122
|
+
Enter the access key generated for this server. It is shown in the terminal on first launch and stored in <code className="text-text-2">.env.local</code>.
|
|
159
123
|
</p>
|
|
160
124
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
className="text-text-3 shrink-0"
|
|
176
|
-
>
|
|
177
|
-
{copied ? (
|
|
178
|
-
<path d="M20 6L9 17l-5-5" />
|
|
179
|
-
) : (
|
|
180
|
-
<>
|
|
181
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
182
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
183
|
-
</>
|
|
184
|
-
)}
|
|
185
|
-
</svg>
|
|
125
|
+
<form onSubmit={handleSubmit} className="flex flex-col items-center gap-4">
|
|
126
|
+
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.2s both', width: '100%', display: 'flex', justifyContent: 'center' }}>
|
|
127
|
+
<input
|
|
128
|
+
type="password"
|
|
129
|
+
value={key}
|
|
130
|
+
onChange={(e) => { setKey(e.target.value); setError('') }}
|
|
131
|
+
placeholder="Paste access key"
|
|
132
|
+
autoFocus
|
|
133
|
+
autoComplete="off"
|
|
134
|
+
className="w-full max-w-[320px] px-6 py-4 rounded-[16px] border border-white/[0.08] bg-surface
|
|
135
|
+
text-text text-[16px] text-center font-mono outline-none
|
|
136
|
+
transition-all duration-200 placeholder:text-text-3/70
|
|
137
|
+
focus:border-accent-bright/30 focus:shadow-[0_0_30px_rgba(99,102,241,0.1)]"
|
|
138
|
+
/>
|
|
186
139
|
</div>
|
|
187
|
-
</div>
|
|
188
140
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}}
|
|
196
|
-
>
|
|
197
|
-
<span className="text-text-3">Click to copy · Also saved in </span>
|
|
198
|
-
<code className="text-text-2">.env.local</code>
|
|
199
|
-
</p>
|
|
200
|
-
<p
|
|
201
|
-
className="absolute inset-x-0 text-[12px] text-emerald-400 font-medium transition-all duration-300"
|
|
202
|
-
style={{
|
|
203
|
-
opacity: copied ? 1 : 0,
|
|
204
|
-
transform: copied ? 'translateY(0)' : 'translateY(4px)',
|
|
205
|
-
}}
|
|
206
|
-
>
|
|
207
|
-
Key copied to clipboard
|
|
141
|
+
{error && (
|
|
142
|
+
<p className="text-[13px] text-red-400" style={{ animation: 'ai-shake 0.5s' }}>{error}</p>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
<p className="text-[12px] text-text-3 max-w-[340px]" style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.3s both' }}>
|
|
146
|
+
The key is never sent back by the server over unauthenticated HTTP anymore. Read it from the launch terminal, or open <code className="text-text-2">.env.local</code> locally.
|
|
208
147
|
</p>
|
|
209
|
-
</div>
|
|
210
148
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
149
|
+
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.4s both' }}>
|
|
150
|
+
<button
|
|
151
|
+
type="submit"
|
|
152
|
+
disabled={loading || !key.trim()}
|
|
153
|
+
className="px-12 py-4 rounded-[16px] border-none bg-accent-bright text-white text-[16px] font-display font-600
|
|
154
|
+
cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
|
|
155
|
+
shadow-[0_6px_28px_rgba(99,102,241,0.3)] disabled:opacity-30"
|
|
156
|
+
>
|
|
157
|
+
{loading ? 'Connecting...' : 'Connect'}
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
</form>
|
|
222
161
|
</>
|
|
223
162
|
) : (
|
|
224
163
|
/* ── Returning user: enter key ── */
|
|
@@ -22,12 +22,20 @@ import { Dropdown, DropdownItem } from '@/components/shared/dropdown'
|
|
|
22
22
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
23
23
|
import { speak } from '@/lib/tts'
|
|
24
24
|
import { api } from '@/lib/api-client'
|
|
25
|
+
import { messagesDiffer } from '@/lib/chat-streaming-state'
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
+
const DIRECT_PROMPT_SUGGESTIONS = [
|
|
27
28
|
{ text: 'What can you help me with?', icon: 'book', gradient: 'from-[#6366F1]/10 to-[#818CF8]/5' },
|
|
29
|
+
{ text: 'Help me choose the right agent for this', icon: 'bot', gradient: 'from-[#34D399]/10 to-[#6EE7B7]/5' },
|
|
28
30
|
{ text: 'Help me set up a new connector', icon: 'link', gradient: 'from-[#EC4899]/10 to-[#F472B6]/5' },
|
|
29
|
-
{ text: '
|
|
30
|
-
|
|
31
|
+
{ text: 'Summarize what needs attention in this workspace', icon: 'check', gradient: 'from-[#F59E0B]/10 to-[#FBBF24]/5' },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const AGENT_PROMPT_SUGGESTIONS = [
|
|
35
|
+
{ text: 'Give me a quick overview of what you can help with', icon: 'book', gradient: 'from-[#6366F1]/10 to-[#818CF8]/5' },
|
|
36
|
+
{ text: 'Review what needs attention right now', icon: 'check', gradient: 'from-[#F59E0B]/10 to-[#FBBF24]/5' },
|
|
37
|
+
{ text: 'Summarize our recent context before we continue', icon: 'link', gradient: 'from-[#EC4899]/10 to-[#F472B6]/5' },
|
|
38
|
+
{ text: 'Help me map the next best step', icon: 'bot', gradient: 'from-[#34D399]/10 to-[#6EE7B7]/5' },
|
|
31
39
|
]
|
|
32
40
|
|
|
33
41
|
export function ChatArea() {
|
|
@@ -52,6 +60,10 @@ export function ChatArea() {
|
|
|
52
60
|
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
|
|
53
61
|
const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
|
|
54
62
|
const currentAgent = session?.agentId ? agents[session.agentId] ?? null : null
|
|
63
|
+
const promptSuggestions = useMemo(
|
|
64
|
+
() => (currentAgent ? AGENT_PROMPT_SUGGESTIONS : DIRECT_PROMPT_SUGGESTIONS),
|
|
65
|
+
[currentAgent],
|
|
66
|
+
)
|
|
55
67
|
|
|
56
68
|
const voice = useVoiceConversation()
|
|
57
69
|
const handleVoiceToggle = useCallback(() => {
|
|
@@ -140,13 +152,12 @@ export function ChatArea() {
|
|
|
140
152
|
}
|
|
141
153
|
}, [sessionId])
|
|
142
154
|
|
|
143
|
-
// Auto-poll messages for
|
|
144
|
-
const isOrchestrated = session?.sessionType === 'orchestrated'
|
|
155
|
+
// Auto-poll messages for sessions that are actively running on the server
|
|
145
156
|
const isServerActive = session?.active === true
|
|
146
157
|
const isOngoingMonitored = appSettings.loopMode === 'ongoing' && !!session?.plugins?.length
|
|
147
|
-
const shouldPollMessages = !!sessionId && (
|
|
148
|
-
const
|
|
149
|
-
|
|
158
|
+
const shouldPollMessages = !!sessionId && (isServerActive || isOngoingMonitored)
|
|
159
|
+
const messagesRef = useRef(messages)
|
|
160
|
+
messagesRef.current = messages
|
|
150
161
|
const isServerActiveRef = useRef(isServerActive)
|
|
151
162
|
isServerActiveRef.current = isServerActive
|
|
152
163
|
const ttsEnabledRef = useRef(ttsEnabled)
|
|
@@ -156,8 +167,9 @@ export function ChatArea() {
|
|
|
156
167
|
if (!sessionId) return
|
|
157
168
|
try {
|
|
158
169
|
const msgs = await fetchMessages(sessionId)
|
|
159
|
-
|
|
160
|
-
|
|
170
|
+
const previous = messagesRef.current
|
|
171
|
+
if (messagesDiffer(msgs, previous)) {
|
|
172
|
+
const newMsgs = msgs.length > previous.length ? msgs.slice(previous.length) : []
|
|
161
173
|
setMessages(msgs)
|
|
162
174
|
if (ttsEnabledRef.current && typeof document !== 'undefined' && document.visibilityState === 'visible') {
|
|
163
175
|
const latestAssistant = [...newMsgs].reverse().find((m) => {
|
|
@@ -385,15 +397,19 @@ export function ChatArea() {
|
|
|
385
397
|
<h1 className="font-display text-[28px] md:text-[36px] font-800 leading-[1.1] tracking-[-0.04em] mb-3">
|
|
386
398
|
Hi{currentUser ? ', ' : ' '}<span className="text-accent-bright">{currentUser || 'there'}</span>
|
|
387
399
|
<br />
|
|
388
|
-
<span className="text-text-2">
|
|
400
|
+
<span className="text-text-2">
|
|
401
|
+
{currentAgent ? `Start with ${currentAgent.name}` : 'Start the conversation'}
|
|
402
|
+
</span>
|
|
389
403
|
</h1>
|
|
390
404
|
<p className="text-[13px] text-text-3 mt-2">
|
|
391
|
-
|
|
405
|
+
{currentAgent
|
|
406
|
+
? `Ask ${currentAgent.name} anything, hand over work, or start with one of these openers.`
|
|
407
|
+
: 'Pick a prompt or type your own below.'}
|
|
392
408
|
</p>
|
|
393
409
|
</div>
|
|
394
410
|
|
|
395
411
|
<div className="relative grid grid-cols-2 md:grid-cols-4 gap-3 max-w-[640px] w-full mb-6">
|
|
396
|
-
{
|
|
412
|
+
{promptSuggestions.map((prompt, i) => (
|
|
397
413
|
<button
|
|
398
414
|
key={prompt.text}
|
|
399
415
|
onClick={() => handlePrompt(prompt.text)}
|
|
@@ -66,6 +66,9 @@ export function ChatCard({ session, active, onClick }: Props) {
|
|
|
66
66
|
: 'No messages'
|
|
67
67
|
const providerLabel = PROVIDER_LABELS[session.provider] || session.provider
|
|
68
68
|
const agent = session.agentId ? agents[session.agentId] : null
|
|
69
|
+
const displayName = session.shortcutForAgentId && agent?.id === session.shortcutForAgentId
|
|
70
|
+
? agent.name
|
|
71
|
+
: session.name
|
|
69
72
|
const connector = getSessionConnector(session, connectors)
|
|
70
73
|
const loopIsOngoing = appSettings.loopMode === 'ongoing'
|
|
71
74
|
const explicitOptIn = session.heartbeatEnabled === true || agent?.heartbeatEnabled === true
|
|
@@ -109,26 +112,7 @@ export function ChatCard({ session, active, onClick }: Props) {
|
|
|
109
112
|
title={`${connector.name} (${connector.platform})`}
|
|
110
113
|
/>
|
|
111
114
|
)}
|
|
112
|
-
<span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{
|
|
113
|
-
{session.mainLoopState?.status && session.mainLoopState.status !== 'idle' && (
|
|
114
|
-
<span className={`shrink-0 flex items-center gap-1 text-[9px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[5px] ${
|
|
115
|
-
session.mainLoopState.status === 'progress' ? 'text-blue-400/90 bg-blue-400/[0.08]'
|
|
116
|
-
: session.mainLoopState.status === 'blocked' ? 'text-amber-400/90 bg-amber-400/[0.08]'
|
|
117
|
-
: 'text-emerald-400/90 bg-emerald-400/[0.08]'
|
|
118
|
-
}`}>
|
|
119
|
-
<span className={`w-[5px] h-[5px] rounded-full ${
|
|
120
|
-
session.mainLoopState.status === 'progress' ? 'bg-blue-400'
|
|
121
|
-
: session.mainLoopState.status === 'blocked' ? 'bg-amber-400'
|
|
122
|
-
: 'bg-emerald-400'
|
|
123
|
-
}`} />
|
|
124
|
-
{session.mainLoopState.status}
|
|
125
|
-
</span>
|
|
126
|
-
)}
|
|
127
|
-
{session.sessionType === 'orchestrated' && (
|
|
128
|
-
<span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-amber-400/80 bg-amber-400/[0.08] px-2 py-0.5 rounded-[6px]">
|
|
129
|
-
AI
|
|
130
|
-
</span>
|
|
131
|
-
)}
|
|
115
|
+
<span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{displayName}</span>
|
|
132
116
|
{providerLabel && (
|
|
133
117
|
<span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-text-3/70 bg-white/[0.03] px-2 py-0.5 rounded-[6px]">
|
|
134
118
|
{providerLabel}
|