@makemore/agent-frontend 2.8.3 → 2.9.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makemore/agent-frontend",
3
- "version": "2.8.3",
3
+ "version": "2.9.0",
4
4
  "description": "A lightweight chat widget for AI agents. Use as an embeddable script tag or import directly into React/Preact projects.",
5
5
  "type": "module",
6
6
  "main": "dist/chat-widget.cjs.js",
@@ -30,7 +30,7 @@
30
30
  "build:dev": "node build.js --dev",
31
31
  "build:embed": "esbuild src/index.js --bundle --minify --format=iife --global-name=ChatWidgetModule --outfile=dist/chat-widget.js",
32
32
  "watch": "node watch.js",
33
- "copy": "cp -f dist/chat-widget.js ../django_agent_studio/static/agent-frontend/chat-widget.js 2>/dev/null || true && echo 'Copied to django_agent_studio'",
33
+ "copy": "mkdir -p ../django_agent_studio/static/agent-frontend && cp -f dist/chat-widget.js dist/chat-widget.css dist/chat-widget-markdown.js ../django_agent_studio/static/agent-frontend/ 2>/dev/null || true && echo 'Copied to django_agent_studio'",
34
34
  "prepublishOnly": "npm run build",
35
35
  "serve": "python -m http.server 8080"
36
36
  },
@@ -10,9 +10,11 @@ import { InputForm } from './InputForm.js';
10
10
  import { Sidebar } from './Sidebar.js';
11
11
  import { ModelSelector } from './ModelSelector.js';
12
12
  import { TaskList } from './TaskList.js';
13
+ import { DevToolbar } from './DevToolbar.js';
13
14
  import { useChat } from '../hooks/useChat.js';
14
15
  import { useModels } from '../hooks/useModels.js';
15
16
  import { useTasks } from '../hooks/useTasks.js';
17
+ import { useSystems } from '../hooks/useSystems.js';
16
18
  import { createApiClient } from '../utils/api.js';
17
19
  import { createStorage, getContrastingTextColor } from '../utils/helpers.js';
18
20
 
@@ -55,8 +57,21 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
55
57
  return createApiClient(config, getState, setState);
56
58
  }, [config, authToken, storage]);
57
59
 
58
- // Chat hook
59
- const chat = useChat(config, api, storage);
60
+ // Systems/agents discovery hook (for dev tools)
61
+ const systemsHook = useSystems(config, api, storage);
62
+
63
+ // Create a config override with the effective agent key from dev tools
64
+ const effectiveConfig = useMemo(() => {
65
+ if (!config.showDevTools) return config;
66
+ const effectiveKey = systemsHook.getEffectiveAgentKey();
67
+ if (effectiveKey && effectiveKey !== config.agentKey) {
68
+ return { ...config, agentKey: effectiveKey };
69
+ }
70
+ return config;
71
+ }, [config, systemsHook.getEffectiveAgentKey]);
72
+
73
+ // Chat hook — uses effective config so agentKey is dynamic
74
+ const chat = useChat(effectiveConfig, api, storage);
60
75
 
61
76
  // Models hook
62
77
  const models = useModels(config, api, storage);
@@ -113,12 +128,13 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
113
128
  // Load conversations for sidebar
114
129
  const loadConversations = useCallback(async () => {
115
130
  if (!config.showConversationSidebar) return;
116
-
131
+
117
132
  setConversationsLoading(true);
118
133
  try {
119
- const url = `${config.backendUrl}${config.apiPaths.conversations}?agent_key=${encodeURIComponent(config.agentKey)}`;
134
+ const agentKey = effectiveConfig.agentKey;
135
+ const url = `${config.backendUrl}${config.apiPaths.conversations}?agent_key=${encodeURIComponent(agentKey)}`;
120
136
  const response = await fetch(url, api.getFetchOptions({ method: 'GET' }));
121
-
137
+
122
138
  if (response.ok) {
123
139
  const data = await response.json();
124
140
  setConversations(data.results || data);
@@ -129,7 +145,7 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
129
145
  } finally {
130
146
  setConversationsLoading(false);
131
147
  }
132
- }, [config, api]);
148
+ }, [config, effectiveConfig, api]);
133
149
 
134
150
  // Handlers
135
151
  const handleToggleSidebar = useCallback(() => {
@@ -243,6 +259,22 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
243
259
  onToggleSidebar=${handleToggleSidebar}
244
260
  />
245
261
 
262
+ ${config.showDevTools && html`
263
+ <${DevToolbar}
264
+ systems=${systemsHook.systems}
265
+ agents=${systemsHook.agents}
266
+ selectedSystem=${systemsHook.selectedSystem}
267
+ selectedAgent=${systemsHook.selectedAgent}
268
+ selectedSystemVersion=${systemsHook.selectedSystemVersion}
269
+ selectedAgentVersion=${systemsHook.selectedAgentVersion}
270
+ onSelectSystem=${systemsHook.selectSystem}
271
+ onSelectAgent=${systemsHook.selectAgent}
272
+ onSelectSystemVersion=${systemsHook.selectSystemVersion}
273
+ onSelectAgentVersion=${systemsHook.selectAgentVersion}
274
+ disabled=${chat.isLoading}
275
+ />
276
+ `}
277
+
246
278
  ${config.showTasksTab !== false && html`
247
279
  <div class="cw-tabs">
248
280
  <button
@@ -0,0 +1,135 @@
1
+ /**
2
+ * DevToolbar component - system/agent/version picker for developers and testers.
3
+ *
4
+ * Always shows when rendered — displays info badges for single items,
5
+ * dropdowns when there are multiple options to choose from.
6
+ */
7
+
8
+ import { html } from 'htm/preact';
9
+ import { escapeHtml } from '../utils/helpers.js';
10
+
11
+ export function DevToolbar({
12
+ systems,
13
+ agents,
14
+ selectedSystem,
15
+ selectedAgent,
16
+ selectedSystemVersion,
17
+ selectedAgentVersion,
18
+ onSelectSystem,
19
+ onSelectAgent,
20
+ onSelectSystemVersion,
21
+ onSelectAgentVersion,
22
+ disabled,
23
+ }) {
24
+ // Find current system and agent objects
25
+ const currentSystem = systems.find(s => s.slug === selectedSystem);
26
+ const currentAgent = agents.find(a => a.slug === selectedAgent);
27
+
28
+ // Get versions for current selections
29
+ const systemVersions = currentSystem?.versions || [];
30
+ const agentVersions = currentAgent?.versions || [];
31
+
32
+ // Nothing loaded yet
33
+ if (systems.length === 0 && agents.length === 0) return null;
34
+
35
+ const hasMultipleSystems = systems.length > 1;
36
+ const hasMultipleAgents = agents.length > 1;
37
+ const hasMultipleSystemVersions = systemVersions.length > 1;
38
+ const hasMultipleAgentVersions = agentVersions.length > 1;
39
+
40
+ return html`
41
+ <div class="cw-dev-toolbar">
42
+ <div class="cw-dev-toolbar-label">
43
+ <span class="cw-dev-toolbar-icon">🛠️</span>
44
+ <span>Dev</span>
45
+ </div>
46
+
47
+ <div class="cw-dev-toolbar-selectors">
48
+ <!-- System -->
49
+ <div class="cw-dev-select-group">
50
+ <label class="cw-dev-label">System</label>
51
+ ${hasMultipleSystems ? html`
52
+ <select
53
+ class="cw-dev-select"
54
+ value=${selectedSystem || ''}
55
+ onChange=${(e) => onSelectSystem(e.target.value)}
56
+ disabled=${disabled}
57
+ >
58
+ ${systems.map(sys => html`
59
+ <option key=${sys.slug} value=${sys.slug}>
60
+ ${sys.name}
61
+ </option>
62
+ `)}
63
+ </select>
64
+ ` : html`
65
+ <span class="cw-dev-badge">${currentSystem?.name || '—'}</span>
66
+ `}
67
+ </div>
68
+
69
+ <!-- System Version -->
70
+ <div class="cw-dev-select-group">
71
+ <label class="cw-dev-label">Sys Ver</label>
72
+ ${hasMultipleSystemVersions ? html`
73
+ <select
74
+ class="cw-dev-select"
75
+ value=${selectedSystemVersion || ''}
76
+ onChange=${(e) => onSelectSystemVersion(e.target.value || null)}
77
+ disabled=${disabled}
78
+ >
79
+ ${systemVersions.map(v => html`
80
+ <option key=${v.version} value=${v.version}>
81
+ ${v.version}${v.is_active ? ' ✓' : ''}${v.is_draft ? ' (draft)' : ''}
82
+ </option>
83
+ `)}
84
+ </select>
85
+ ` : html`
86
+ <span class="cw-dev-badge">${systemVersions.length === 1 ? systemVersions[0].version : 'none'}</span>
87
+ `}
88
+ </div>
89
+
90
+ <!-- Agent -->
91
+ <div class="cw-dev-select-group">
92
+ <label class="cw-dev-label">Agent</label>
93
+ ${hasMultipleAgents ? html`
94
+ <select
95
+ class="cw-dev-select"
96
+ value=${selectedAgent || ''}
97
+ onChange=${(e) => onSelectAgent(e.target.value)}
98
+ disabled=${disabled}
99
+ >
100
+ ${agents.map(agent => html`
101
+ <option key=${agent.slug} value=${agent.slug}>
102
+ ${agent.name}${currentSystem?.entry_agent?.slug === agent.slug ? ' ★' : ''}
103
+ </option>
104
+ `)}
105
+ </select>
106
+ ` : html`
107
+ <span class="cw-dev-badge">${currentAgent?.name || agents[0]?.name || '—'}${currentSystem?.entry_agent?.slug === (currentAgent?.slug || agents[0]?.slug) ? ' ★' : ''}</span>
108
+ `}
109
+ </div>
110
+
111
+ <!-- Agent Version -->
112
+ <div class="cw-dev-select-group">
113
+ <label class="cw-dev-label">Agent Ver</label>
114
+ ${hasMultipleAgentVersions ? html`
115
+ <select
116
+ class="cw-dev-select"
117
+ value=${selectedAgentVersion || ''}
118
+ onChange=${(e) => onSelectAgentVersion(e.target.value || null)}
119
+ disabled=${disabled}
120
+ >
121
+ ${agentVersions.map(v => html`
122
+ <option key=${v.version} value=${v.version}>
123
+ ${v.version}${v.is_active ? ' ✓' : ''}${v.is_draft ? ' (draft)' : ''}
124
+ </option>
125
+ `)}
126
+ </select>
127
+ ` : html`
128
+ <span class="cw-dev-badge">${agentVersions.length === 1 ? agentVersions[0].version : (currentAgent?.active_version || '—')}</span>
129
+ `}
130
+ </div>
131
+ </div>
132
+ </div>
133
+ `;
134
+ }
135
+
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Systems hook - manages system/agent/version discovery and selection
3
+ * for developer/tester UI (DevToolbar).
4
+ */
5
+
6
+ import { useState, useEffect, useCallback } from 'preact/hooks';
7
+
8
+ const SYSTEM_KEY = 'cw_selected_system';
9
+ const AGENT_KEY = 'cw_selected_agent';
10
+ const SYSTEM_VERSION_KEY = 'cw_selected_system_version';
11
+ const AGENT_VERSION_KEY = 'cw_selected_agent_version';
12
+
13
+ export function useSystems(config, api, storage) {
14
+ const [systems, setSystems] = useState([]);
15
+ const [agents, setAgents] = useState([]);
16
+ const [selectedSystem, setSelectedSystem] = useState(null);
17
+ const [selectedAgent, setSelectedAgent] = useState(null);
18
+ const [selectedSystemVersion, setSelectedSystemVersion] = useState(null);
19
+ const [selectedAgentVersion, setSelectedAgentVersion] = useState(null);
20
+ const [isLoading, setIsLoading] = useState(false);
21
+
22
+ // Compute the effective agent key to use for runs
23
+ const getEffectiveAgentKey = useCallback(() => {
24
+ if (selectedAgent) return selectedAgent;
25
+ if (selectedSystem) {
26
+ const sys = systems.find(s => s.slug === selectedSystem);
27
+ if (sys?.entry_agent) return sys.entry_agent.slug;
28
+ }
29
+ return config.agentKey;
30
+ }, [selectedAgent, selectedSystem, systems, config.agentKey]);
31
+
32
+ // Load systems on mount
33
+ useEffect(() => {
34
+ if (!config.showDevTools) return;
35
+
36
+ const loadSystems = async () => {
37
+ setIsLoading(true);
38
+ try {
39
+ const response = await fetch(
40
+ `${config.backendUrl}${config.apiPaths.systems}`,
41
+ api.getFetchOptions({ method: 'GET' })
42
+ );
43
+
44
+ if (response.ok) {
45
+ const data = await response.json();
46
+ const systemsList = data.results || data;
47
+ setSystems(systemsList);
48
+
49
+ // Restore saved selection or auto-select if only 1 system
50
+ const saved = storage?.get(SYSTEM_KEY);
51
+ if (saved && systemsList.some(s => s.slug === saved)) {
52
+ setSelectedSystem(saved);
53
+ } else if (systemsList.length === 1) {
54
+ setSelectedSystem(systemsList[0].slug);
55
+ }
56
+ }
57
+ } catch (err) {
58
+ console.warn('[ChatWidget] Failed to load systems:', err);
59
+ } finally {
60
+ setIsLoading(false);
61
+ }
62
+ };
63
+
64
+ loadSystems();
65
+ }, [config.backendUrl, config.apiPaths.systems, config.showDevTools, api, storage]);
66
+
67
+ // Load agents when system changes
68
+ useEffect(() => {
69
+ if (!config.showDevTools) return;
70
+
71
+ const loadAgents = async () => {
72
+ try {
73
+ const systemParam = selectedSystem ? `?system=${encodeURIComponent(selectedSystem)}` : '';
74
+ const response = await fetch(
75
+ `${config.backendUrl}${config.apiPaths.agents}${systemParam}`,
76
+ api.getFetchOptions({ method: 'GET' })
77
+ );
78
+
79
+ if (response.ok) {
80
+ const data = await response.json();
81
+ const agentsList = data.results || data;
82
+ setAgents(agentsList);
83
+
84
+ // Restore saved agent or default to entry agent
85
+ const saved = storage?.get(AGENT_KEY);
86
+ if (saved && agentsList.some(a => a.slug === saved)) {
87
+ setSelectedAgent(saved);
88
+ } else if (selectedSystem) {
89
+ const sys = systems.find(s => s.slug === selectedSystem);
90
+ if (sys?.entry_agent) {
91
+ setSelectedAgent(sys.entry_agent.slug);
92
+ }
93
+ }
94
+
95
+ // Restore saved agent version
96
+ const savedVersion = storage?.get(AGENT_VERSION_KEY);
97
+ if (savedVersion) {
98
+ setSelectedAgentVersion(savedVersion);
99
+ }
100
+ }
101
+ } catch (err) {
102
+ console.warn('[ChatWidget] Failed to load agents:', err);
103
+ }
104
+ };
105
+
106
+ loadAgents();
107
+ }, [config.backendUrl, config.apiPaths.agents, config.showDevTools, selectedSystem, api, storage, systems]);
108
+
109
+ // Select a system
110
+ const selectSystem = useCallback((slug) => {
111
+ setSelectedSystem(slug);
112
+ storage?.set(SYSTEM_KEY, slug);
113
+ // Reset agent selection — will be re-set by the agents useEffect
114
+ setSelectedAgent(null);
115
+ setSelectedAgentVersion(null);
116
+ storage?.set(AGENT_KEY, null);
117
+ storage?.set(AGENT_VERSION_KEY, null);
118
+ // Set system version
119
+ const sys = systems.find(s => s.slug === slug);
120
+ const activeVer = sys?.active_version || null;
121
+ setSelectedSystemVersion(activeVer);
122
+ storage?.set(SYSTEM_VERSION_KEY, activeVer);
123
+ }, [storage, systems]);
124
+
125
+ // Select an agent
126
+ const selectAgent = useCallback((slug) => {
127
+ setSelectedAgent(slug);
128
+ storage?.set(AGENT_KEY, slug);
129
+ // Reset agent version to active
130
+ const agent = agents.find(a => a.slug === slug);
131
+ const activeVer = agent?.active_version || null;
132
+ setSelectedAgentVersion(activeVer);
133
+ storage?.set(AGENT_VERSION_KEY, activeVer);
134
+ }, [storage, agents]);
135
+
136
+ // Select system version
137
+ const selectSystemVersion = useCallback((version) => {
138
+ setSelectedSystemVersion(version);
139
+ storage?.set(SYSTEM_VERSION_KEY, version);
140
+ }, [storage]);
141
+
142
+ // Select agent version
143
+ const selectAgentVersion = useCallback((version) => {
144
+ setSelectedAgentVersion(version);
145
+ storage?.set(AGENT_VERSION_KEY, version);
146
+ }, [storage]);
147
+
148
+ return {
149
+ systems,
150
+ agents,
151
+ selectedSystem,
152
+ selectedAgent,
153
+ selectedSystemVersion,
154
+ selectedAgentVersion,
155
+ isLoading,
156
+ selectSystem,
157
+ selectAgent,
158
+ selectSystemVersion,
159
+ selectAgentVersion,
160
+ getEffectiveAgentKey,
161
+ };
162
+ }
163
+
package/src/index.js CHANGED
@@ -52,6 +52,9 @@ class ChatWidgetInstance {
52
52
  document.body.appendChild(this.container);
53
53
  }
54
54
 
55
+ // Apply theme class
56
+ this._applyTheme();
57
+
55
58
  // Render the Preact component
56
59
  this._render();
57
60
 
@@ -59,6 +62,16 @@ class ChatWidgetInstance {
59
62
  return this;
60
63
  }
61
64
 
65
+ _applyTheme() {
66
+ if (!this.container) return;
67
+ this.container.classList.remove('cw-dark', 'cw-auto');
68
+ if (this.config.theme === 'dark') {
69
+ this.container.classList.add('cw-dark');
70
+ } else if (this.config.theme === 'auto') {
71
+ this.container.classList.add('cw-auto');
72
+ }
73
+ }
74
+
62
75
  _render(configOverrides = {}) {
63
76
  if (!this.container) return;
64
77
  render(
@@ -172,6 +185,10 @@ class ChatWidgetInstance {
172
185
  ...this.config,
173
186
  ...configUpdates,
174
187
  };
188
+ // Re-apply theme if it changed
189
+ if ('theme' in configUpdates) {
190
+ this._applyTheme();
191
+ }
175
192
  // Re-render with updated config
176
193
  this._render();
177
194
  console.log(`[ChatWidget] Instance ${this.instanceId} config updated`);
@@ -41,6 +41,8 @@ export const DEFAULT_CONFIG = {
41
41
  ttsVoices: '/api/tts/voices/',
42
42
  ttsSetVoice: '/api/tts/set-voice/',
43
43
  models: '/api/agent-runtime/models/',
44
+ systems: '/api/agent-runtime/systems/',
45
+ agents: '/api/agent-runtime/agents/',
44
46
  },
45
47
 
46
48
  // API case style: 'camel', 'snake', or 'auto' (accepts both, sends camelCase)
@@ -49,6 +51,9 @@ export const DEFAULT_CONFIG = {
49
51
  // - 'auto': Accept both in responses, send snake_case in requests (default)
50
52
  apiCaseStyle: 'auto',
51
53
 
54
+ // Theme: 'light' (default), 'dark', or 'auto' (follows prefers-color-scheme)
55
+ theme: 'light',
56
+
52
57
  // UI options
53
58
  showConversationSidebar: true,
54
59
  showClearButton: true,
@@ -57,6 +62,7 @@ export const DEFAULT_CONFIG = {
57
62
  showVoiceSettings: true,
58
63
  showExpandButton: true,
59
64
  showModelSelector: false,
65
+ showDevTools: false, // Show system/agent/version picker for developers/testers
60
66
  enableVoice: true, // Enable voice input (speech-to-text)
61
67
 
62
68
  // Model selection