agentgui 1.0.453 → 1.0.455

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/server.js CHANGED
@@ -24,6 +24,7 @@ import { register as registerRunHandlers } from './lib/ws-handlers-run.js';
24
24
  import { register as registerUtilHandlers } from './lib/ws-handlers-util.js';
25
25
  import { startAll as startACPTools, stopAll as stopACPTools, getStatus as getACPStatus, getPort as getACPPort, queryModels as queryACPModels, touch as touchACP } from './lib/acp-manager.js';
26
26
  import { installGMAgentConfigs } from './lib/gm-agent-configs.js';
27
+ import * as toolManager from './lib/tool-manager.js';
27
28
 
28
29
 
29
30
  process.on('uncaughtException', (err, origin) => {
@@ -1780,6 +1781,141 @@ const server = http.createServer(async (req, res) => {
1780
1781
  return;
1781
1782
  }
1782
1783
 
1784
+ if (pathOnly === '/api/tools' && req.method === 'GET') {
1785
+ console.log('[TOOLS-API] Handling GET /api/tools');
1786
+ const tools = toolManager.getAllTools();
1787
+ const toolsWithUpdates = await Promise.all(tools.map(async (t) => {
1788
+ if (t.installed) {
1789
+ const updates = await toolManager.checkForUpdates(t.id, t.version);
1790
+ return { ...t, hasUpdate: updates.hasUpdate, latestVersion: updates.latestVersion };
1791
+ }
1792
+ return { ...t, hasUpdate: false, latestVersion: null };
1793
+ }));
1794
+ sendJSON(req, res, 200, { tools: toolsWithUpdates });
1795
+ return;
1796
+ }
1797
+
1798
+ if (pathOnly.match(/^\/api\/tools\/([^/]+)\/status$/)) {
1799
+ const toolId = pathOnly.match(/^\/api\/tools\/([^/]+)\/status$/)[1];
1800
+ const status = toolManager.checkToolStatus(toolId);
1801
+ if (!status) {
1802
+ sendJSON(req, res, 404, { error: 'Tool not found' });
1803
+ return;
1804
+ }
1805
+ if (status.installed) {
1806
+ const updates = await toolManager.checkForUpdates(toolId, status.version);
1807
+ status.hasUpdate = updates.hasUpdate;
1808
+ status.latestVersion = updates.latestVersion;
1809
+ }
1810
+ sendJSON(req, res, 200, status);
1811
+ return;
1812
+ }
1813
+
1814
+ if (pathOnly.match(/^\/api\/tools\/([^/]+)\/install$/) && req.method === 'POST') {
1815
+ const toolId = pathOnly.match(/^\/api\/tools\/([^/]+)\/install$/)[1];
1816
+ const tool = toolManager.getToolConfig(toolId);
1817
+ if (!tool) {
1818
+ sendJSON(req, res, 404, { error: 'Tool not found' });
1819
+ return;
1820
+ }
1821
+ queries.updateToolStatus(toolId, { status: 'installing' });
1822
+ sendJSON(req, res, 200, { success: true, installing: true, estimatedTime: 60000 });
1823
+ toolManager.install(toolId, (msg) => {
1824
+ if (wsOptimizer && wsOptimizer.broadcast) {
1825
+ wsOptimizer.broadcast({ type: 'tool_install_progress', toolId, data: msg });
1826
+ }
1827
+ }).then((result) => {
1828
+ if (result.success) {
1829
+ queries.updateToolStatus(toolId, { status: 'installed', version: result.version, installed_at: Date.now() });
1830
+ if (wsOptimizer && wsOptimizer.broadcast) {
1831
+ wsOptimizer.broadcast({ type: 'tool_install_complete', toolId, data: result });
1832
+ }
1833
+ queries.addToolInstallHistory(toolId, 'install', 'success', null);
1834
+ } else {
1835
+ queries.updateToolStatus(toolId, { status: 'failed', error_message: result.error });
1836
+ if (wsOptimizer && wsOptimizer.broadcast) {
1837
+ wsOptimizer.broadcast({ type: 'tool_install_failed', toolId, data: result });
1838
+ }
1839
+ queries.addToolInstallHistory(toolId, 'install', 'failed', result.error);
1840
+ }
1841
+ });
1842
+ return;
1843
+ }
1844
+
1845
+ if (pathOnly.match(/^\/api\/tools\/([^/]+)\/update$/) && req.method === 'POST') {
1846
+ const toolId = pathOnly.match(/^\/api\/tools\/([^/]+)\/update$/)[1];
1847
+ const body = await parseBody(req);
1848
+ const tool = toolManager.getToolConfig(toolId);
1849
+ if (!tool) {
1850
+ sendJSON(req, res, 404, { error: 'Tool not found' });
1851
+ return;
1852
+ }
1853
+ const current = toolManager.checkToolStatus(toolId);
1854
+ if (!current || !current.installed) {
1855
+ sendJSON(req, res, 400, { error: 'Tool not installed' });
1856
+ return;
1857
+ }
1858
+ queries.updateToolStatus(toolId, { status: 'updating' });
1859
+ sendJSON(req, res, 200, { success: true, updating: true });
1860
+ toolManager.update(toolId, body.targetVersion, (msg) => {
1861
+ if (wsOptimizer && wsOptimizer.broadcast) {
1862
+ wsOptimizer.broadcast({ type: 'tool_update_progress', toolId, data: msg });
1863
+ }
1864
+ }).then((result) => {
1865
+ if (result.success) {
1866
+ queries.updateToolStatus(toolId, { status: 'installed', version: result.version, installed_at: Date.now() });
1867
+ if (wsOptimizer && wsOptimizer.broadcast) {
1868
+ wsOptimizer.broadcast({ type: 'tool_update_complete', toolId, data: result });
1869
+ }
1870
+ queries.addToolInstallHistory(toolId, 'update', 'success', null);
1871
+ } else {
1872
+ queries.updateToolStatus(toolId, { status: 'failed', error_message: result.error });
1873
+ if (wsOptimizer && wsOptimizer.broadcast) {
1874
+ wsOptimizer.broadcast({ type: 'tool_update_failed', toolId, data: result });
1875
+ }
1876
+ queries.addToolInstallHistory(toolId, 'update', 'failed', result.error);
1877
+ }
1878
+ });
1879
+ return;
1880
+ }
1881
+
1882
+ if (pathOnly.match(/^\/api\/tools\/([^/]+)\/history$/) && req.method === 'GET') {
1883
+ const toolId = pathOnly.match(/^\/api\/tools\/([^/]+)\/history$/)[1];
1884
+ const url = new URL(req.url, 'http://localhost');
1885
+ const limit = Math.min(parseInt(url.searchParams.get('limit')) || 20, 100);
1886
+ const offset = parseInt(url.searchParams.get('offset')) || 0;
1887
+ const history = queries.getToolInstallHistory(toolId, limit, offset);
1888
+ sendJSON(req, res, 200, { history });
1889
+ return;
1890
+ }
1891
+
1892
+ if (pathOnly === '/api/tools/refresh-all' && req.method === 'POST') {
1893
+ sendJSON(req, res, 200, { refreshing: true, toolCount: 4 });
1894
+ if (wsOptimizer && wsOptimizer.broadcast) {
1895
+ wsOptimizer.broadcast({ type: 'tools_refresh_started' });
1896
+ }
1897
+ setImmediate(async () => {
1898
+ const tools = toolManager.getAllTools();
1899
+ for (const tool of tools) {
1900
+ queries.updateToolStatus(tool.id, {
1901
+ status: tool.installed ? 'installed' : 'not_installed',
1902
+ version: tool.version,
1903
+ last_check_at: Date.now()
1904
+ });
1905
+ if (tool.installed) {
1906
+ const updates = await toolManager.checkForUpdates(tool.id, tool.version);
1907
+ if (updates.hasUpdate) {
1908
+ queries.updateToolStatus(tool.id, { update_available: 1, latest_version: updates.latestVersion });
1909
+ }
1910
+ }
1911
+ }
1912
+ if (wsOptimizer && wsOptimizer.broadcast) {
1913
+ wsOptimizer.broadcast({ type: 'tools_refresh_complete', data: tools });
1914
+ }
1915
+ });
1916
+ return;
1917
+ }
1918
+
1783
1919
  if (pathOnly === '/api/ws-stats' && req.method === 'GET') {
1784
1920
  const stats = wsOptimizer.getStats();
1785
1921
  sendJSON(req, res, 200, stats);
@@ -4213,6 +4349,7 @@ function onServerReady() {
4213
4349
  installGMAgentConfigs().catch(err => console.error('[GM-CONFIG] Startup error:', err.message));
4214
4350
 
4215
4351
  startACPTools().then(() => {
4352
+ console.log('[ACP] On-demand startup enabled (ACP tools start when first used)');
4216
4353
  setTimeout(() => {
4217
4354
  const acpStatus = getACPStatus();
4218
4355
  for (const s of acpStatus) {
@@ -4227,6 +4364,20 @@ function onServerReady() {
4227
4364
  }, 6000);
4228
4365
  }).catch(err => console.error('[ACP] Startup error:', err.message));
4229
4366
 
4367
+ const toolIds = ['gm-oc', 'gm-gc', 'gm-kilo', 'gm-cc'];
4368
+ queries.initializeToolInstallations(toolIds.map(id => ({ id })));
4369
+ for (const toolId of toolIds) {
4370
+ const status = toolManager.checkToolStatus(toolId);
4371
+ if (status) {
4372
+ queries.updateToolStatus(toolId, {
4373
+ status: status.installed ? 'installed' : 'not_installed',
4374
+ version: status.version,
4375
+ last_check_at: Date.now()
4376
+ });
4377
+ }
4378
+ }
4379
+ console.log('[TOOLS] Initialization complete');
4380
+
4230
4381
  ensureModelsDownloaded().then(async ok => {
4231
4382
  if (ok) console.log('[MODELS] Speech models ready');
4232
4383
  else console.log('[MODELS] Speech model download failed');
package/static/index.html CHANGED
@@ -1238,6 +1238,16 @@
1238
1238
  100% { border-color: var(--color-border); }
1239
1239
  }
1240
1240
 
1241
+ /* ===== TOOLS VIEW ===== */
1242
+ .tools-container {
1243
+ flex: 1;
1244
+ min-height: 0;
1245
+ overflow-y: auto;
1246
+ overflow-x: hidden;
1247
+ padding: 1.5rem 2rem;
1248
+ -webkit-overflow-scrolling: touch;
1249
+ }
1250
+
1241
1251
  .voice-mic-btn.recording {
1242
1252
  background: var(--color-error);
1243
1253
  border-color: var(--color-error);
@@ -3025,6 +3035,7 @@
3025
3035
  .toast-error { background: var(--color-error); color: white; }
3026
3036
  .toast-warning { background: var(--color-warning); color: white; }
3027
3037
  </style>
3038
+ <link rel="stylesheet" href="/gm/css/tool-status.css">
3028
3039
  </head>
3029
3040
  <body>
3030
3041
  <!-- Sidebar overlay (mobile) -->
@@ -3111,6 +3122,7 @@
3111
3122
  <button class="view-toggle-btn" data-view="files">Files</button>
3112
3123
  <button class="view-toggle-btn" data-view="voice" id="voiceTabBtn" style="display:none;">Voice</button>
3113
3124
  <button class="view-toggle-btn" data-view="terminal" id="terminalTabBtn">Terminal</button>
3125
+ <button class="view-toggle-btn" data-view="tools">Tools</button>
3114
3126
  </div>
3115
3127
 
3116
3128
  <!-- Messages scroll area -->
@@ -3174,6 +3186,11 @@
3174
3186
  </div>
3175
3187
  </div>
3176
3188
 
3189
+ <!-- Tools management view -->
3190
+ <div id="toolsContainer" class="tools-container" style="display:none;">
3191
+ <div id="tool-status-container"></div>
3192
+ </div>
3193
+
3177
3194
  <!-- Input area: fixed at bottom -->
3178
3195
  <div class="input-section">
3179
3196
  <div class="input-wrapper">
@@ -3243,6 +3260,7 @@
3243
3260
  <script defer src="/gm/js/conversations.js"></script>
3244
3261
  <script defer src="/gm/js/terminal.js"></script>
3245
3262
  <script defer src="/gm/js/script-runner.js"></script>
3263
+ <script defer src="/gm/js/tool-status.js"></script>
3246
3264
  <script defer src="/gm/js/client.js"></script>
3247
3265
  <script type="module" src="/gm/js/voice.js"></script>
3248
3266
  <script defer src="/gm/js/features.js"></script>
@@ -149,6 +149,7 @@
149
149
  var fileIframe = document.getElementById('fileBrowserIframe');
150
150
  var voiceContainer = document.getElementById('voiceContainer');
151
151
  var terminalContainer = document.getElementById('terminalContainer');
152
+ var toolsContainer = document.getElementById('toolsContainer');
152
153
  if (!bar) return;
153
154
  bar.querySelectorAll('.view-toggle-btn').forEach(function(btn) {
154
155
  btn.classList.toggle('active', btn.dataset.view === view);
@@ -158,6 +159,7 @@
158
159
  if (fileBrowser) fileBrowser.style.display = view === 'files' ? 'flex' : 'none';
159
160
  if (voiceContainer) voiceContainer.style.display = view === 'voice' ? 'flex' : 'none';
160
161
  if (terminalContainer) terminalContainer.style.display = view === 'terminal' ? 'flex' : 'none';
162
+ if (toolsContainer) toolsContainer.style.display = view === 'tools' ? 'flex' : 'none';
161
163
  if (view === 'files' && fileIframe && currentConversation) {
162
164
  var src = BASE + '/files/' + currentConversation + '/';
163
165
  if (fileIframe.src !== location.origin + src) fileIframe.src = src;
@@ -0,0 +1,48 @@
1
+ const ToolStatusComponent = {
2
+ state: { tools: [], refreshing: false },
3
+
4
+ init() {
5
+ this.loadTools();
6
+ if (window.wsManager) {
7
+ const events = ['tool_install_started', 'tool_install_progress', 'tool_install_complete', 'tool_install_failed', 'tool_update_complete', 'tool_update_failed', 'tools_refresh_complete'];
8
+ events.forEach(e => window.wsManager.on(e, (d) => this.handleEvent(e, d)));
9
+ }
10
+ },
11
+
12
+ async loadTools() {
13
+ try { const res = await fetch('/gm/api/tools'); this.state.tools = (await res.json()).tools || []; this.render(); } catch (e) { console.error('[TOOL-STATUS]', e.message); }
14
+ },
15
+
16
+ async act(action, toolId) {
17
+ try { const res = await fetch(`/gm/api/tools/${toolId}/${action}`, { method: 'POST' }); const data = await res.json(); if (!data.success) alert(`${action} failed: ${data.error || 'Unknown error'}`); } catch (e) { alert(`${action} failed: ${e.message}`); }
18
+ },
19
+
20
+ async refreshTools() { this.state.refreshing = true; this.render(); try { await fetch('/gm/api/tools/refresh-all', { method: 'POST' }); } catch (e) { console.error('[TOOL-STATUS]', e.message); } },
21
+
22
+ handleEvent(event, data) {
23
+ const tool = this.state.tools.find(t => t.id === data.toolId);
24
+ if (!tool) return;
25
+ if (event === 'tool_install_started') { tool.status = 'installing'; tool.progress = 0; }
26
+ else if (event === 'tool_install_progress') { tool.progress = Math.min((tool.progress || 0) + 5, 90); }
27
+ else if (event.includes('_complete')) { tool.status = 'installed'; tool.version = data.data.version; tool.hasUpdate = false; tool.progress = 100; setTimeout(() => this.loadTools(), 1000); }
28
+ else if (event.includes('_failed')) { tool.status = 'failed'; tool.error_message = data.data.error; tool.progress = 0; }
29
+ else if (event === 'tools_refresh_complete') { this.state.refreshing = false; this.loadTools(); return; }
30
+ this.render();
31
+ },
32
+
33
+ getStatusColor(tool) { return tool.status === 'installed' && !tool.hasUpdate ? '#4CAF50' : tool.status === 'installed' && tool.hasUpdate ? '#FFC107' : ['installing', 'updating'].includes(tool.status) ? '#2196F3' : tool.status === 'failed' ? '#F44336' : '#9E9E9E'; },
34
+
35
+ getStatusText(tool) { return tool.status === 'installed' ? (tool.hasUpdate ? `Update available (v${tool.latestVersion})` : `Installed v${tool.version || '?'}`) : tool.status === 'installing' ? 'Installing...' : tool.status === 'updating' ? 'Updating...' : tool.status === 'failed' ? 'Failed' : 'Not installed'; },
36
+
37
+ render() {
38
+ const c = document.getElementById('tool-status-container');
39
+ if (!c) return;
40
+ c.innerHTML = `
41
+ <div class="tool-status-header"><h3>Tools</h3><button onclick="window.toolStatus.refreshTools()" ${this.state.refreshing ? 'disabled' : ''} class="tool-refresh-btn">${this.state.refreshing ? 'Refreshing...' : 'Refresh'}</button></div>
42
+ <div class="tool-grid">${this.state.tools.map(t => `<div class="tool-card" style="border-left: 4px solid ${this.getStatusColor(t)}"><div class="tool-name">${t.name || t.id}</div><div class="tool-status">${this.getStatusText(t)}</div>${t.progress !== undefined && ['installing', 'updating'].includes(t.status) ? `<div class="tool-progress"><div class="progress-bar" style="width: ${t.progress}%"></div></div>` : ''}<div class="tool-actions">${!t.installed ? `<button onclick="window.toolStatus.act('install','${t.id}')" class="tool-btn tool-btn-primary">Install</button>` : t.hasUpdate ? `<button onclick="window.toolStatus.act('update','${t.id}')" class="tool-btn tool-btn-primary">Update</button>` : `<button onclick="window.toolStatus.refreshTools()" class="tool-btn tool-btn-secondary">Check Updates</button>`}${t.status === 'failed' ? `<button onclick="window.toolStatus.act('install','${t.id}')" class="tool-btn tool-btn-warning">Retry</button>` : ''}${t.error_message ? `<div class="tool-error" title="${t.error_message}">Error: ${t.error_message.substring(0, 30)}...<a href="#" onclick="alert('${t.error_message.replace(/"/g, '\\"')}'); return false;">Details</a></div>` : ''}</div></div>`).join('')}</div>
43
+ `;
44
+ window.toolStatus = this;
45
+ }
46
+ };
47
+
48
+ if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => ToolStatusComponent.init()); } else { ToolStatusComponent.init(); }
@@ -1,119 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const http = require('http');
4
-
5
- const BASE_URL = '/gm';
6
- const PORT = 3000;
7
-
8
- function makeRequest(method, path, body = null) {
9
- return new Promise((resolve, reject) => {
10
- const options = {
11
- hostname: 'localhost',
12
- port: PORT,
13
- path: BASE_URL + path,
14
- method: method,
15
- headers: body ? {
16
- 'Content-Type': 'application/json',
17
- 'Content-Length': Buffer.byteLength(JSON.stringify(body))
18
- } : {}
19
- };
20
-
21
- const req = http.request(options, (res) => {
22
- let data = '';
23
- res.on('data', (chunk) => { data += chunk; });
24
- res.on('end', () => {
25
- try {
26
- resolve({ status: res.statusCode, data: data ? JSON.parse(data) : null, raw: data });
27
- } catch {
28
- resolve({ status: res.statusCode, data: null, raw: data });
29
- }
30
- });
31
- });
32
-
33
- req.on('error', reject);
34
-
35
- if (body) {
36
- req.write(JSON.stringify(body));
37
- }
38
- req.end();
39
- });
40
- }
41
-
42
- async function runTests() {
43
- console.log('Testing ACP Agents & Stateless Runs Endpoints\n');
44
-
45
- const tests = [
46
- {
47
- name: 'POST /api/agents/search - empty search',
48
- test: async () => {
49
- const res = await makeRequest('POST', '/api/agents/search', {});
50
- return res.status === 200 && res.data.agents !== undefined;
51
- }
52
- },
53
- {
54
- name: 'POST /api/agents/search - search by name',
55
- test: async () => {
56
- const res = await makeRequest('POST', '/api/agents/search', { name: 'Claude' });
57
- return res.status === 200 && Array.isArray(res.data.agents);
58
- }
59
- },
60
- {
61
- name: 'GET /api/agents/claude-code',
62
- test: async () => {
63
- const res = await makeRequest('GET', '/api/agents/claude-code');
64
- return res.status === 200 || res.status === 404;
65
- }
66
- },
67
- {
68
- name: 'GET /api/agents/claude-code/descriptor',
69
- test: async () => {
70
- const res = await makeRequest('GET', '/api/agents/claude-code/descriptor');
71
- return (res.status === 200 && res.data.metadata && res.data.specs) || res.status === 404;
72
- }
73
- },
74
- {
75
- name: 'POST /api/runs/search',
76
- test: async () => {
77
- const res = await makeRequest('POST', '/api/runs/search', {});
78
- return res.status === 200 && res.data.runs !== undefined;
79
- }
80
- },
81
- {
82
- name: 'POST /api/runs - missing agent_id',
83
- test: async () => {
84
- const res = await makeRequest('POST', '/api/runs', {});
85
- return res.status === 422;
86
- }
87
- }
88
- ];
89
-
90
- let passed = 0;
91
- let failed = 0;
92
-
93
- for (const t of tests) {
94
- try {
95
- const success = await t.test();
96
- if (success) {
97
- console.log(`✓ ${t.name}`);
98
- passed++;
99
- } else {
100
- console.log(`✗ ${t.name}`);
101
- failed++;
102
- }
103
- } catch (err) {
104
- console.log(`✗ ${t.name} - ${err.message}`);
105
- failed++;
106
- }
107
- }
108
-
109
- console.log(`\nResults: ${passed} passed, ${failed} failed`);
110
- process.exit(failed > 0 ? 1 : 0);
111
- }
112
-
113
- http.get(`http://localhost:${PORT}${BASE_URL}/`, (res) => {
114
- console.log('Server is running\n');
115
- runTests();
116
- }).on('error', () => {
117
- console.log('Server is not running. Please start with: npm run dev');
118
- process.exit(1);
119
- });
package/test-wave4-ui.mjs DELETED
@@ -1,141 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Wave 4 UI Consistency Test
4
- * Tests agent/model persistence and display consolidation
5
- */
6
-
7
- import http from 'http';
8
-
9
- const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
10
- const API_BASE = `${BASE_URL}/gm/api`;
11
-
12
- function request(method, path, body = null) {
13
- return new Promise((resolve, reject) => {
14
- const url = new URL(path, API_BASE);
15
- const options = {
16
- method,
17
- headers: body ? { 'Content-Type': 'application/json' } : {}
18
- };
19
-
20
- const req = http.request(url, options, (res) => {
21
- let data = '';
22
- res.on('data', chunk => data += chunk);
23
- res.on('end', () => {
24
- try {
25
- resolve({ status: res.statusCode, data: data ? JSON.parse(data) : null });
26
- } catch (e) {
27
- resolve({ status: res.statusCode, data });
28
- }
29
- });
30
- });
31
-
32
- req.on('error', reject);
33
- if (body) req.write(JSON.stringify(body));
34
- req.end();
35
- });
36
- }
37
-
38
- async function test() {
39
- console.log('=== Wave 4 UI Consistency Tests ===\n');
40
-
41
- try {
42
- // Test 1: Create conversation with specific agent and model
43
- console.log('Test 1: Create conversation with agent and model');
44
- const createRes = await request('POST', '/conversations', {
45
- agentId: 'claude-code',
46
- title: 'Wave 4 Test Conversation',
47
- workingDirectory: '/tmp/test',
48
- model: 'claude-sonnet-4-5'
49
- });
50
-
51
- if (createRes.status !== 200) {
52
- console.error('❌ Failed to create conversation:', createRes.status);
53
- return;
54
- }
55
-
56
- const conversation = createRes.data.conversation;
57
- console.log('✓ Created conversation:', conversation.id);
58
- console.log(' - agentId:', conversation.agentId);
59
- console.log(' - model:', conversation.model);
60
-
61
- // Test 2: Fetch conversation and verify agent/model are returned
62
- console.log('\nTest 2: Fetch conversation via /full endpoint');
63
- const fullRes = await request('GET', `/conversations/${conversation.id}/full`);
64
-
65
- if (fullRes.status !== 200) {
66
- console.error('❌ Failed to fetch conversation:', fullRes.status);
67
- return;
68
- }
69
-
70
- const fullConv = fullRes.data.conversation;
71
- console.log('✓ Fetched conversation');
72
- console.log(' - agentId:', fullConv.agentId);
73
- console.log(' - agentType:', fullConv.agentType);
74
- console.log(' - model:', fullConv.model);
75
-
76
- if (!fullConv.agentId && !fullConv.agentType) {
77
- console.error('❌ agentId/agentType missing from response');
78
- } else {
79
- console.log('✓ agentId/agentType present');
80
- }
81
-
82
- if (!fullConv.model) {
83
- console.error('❌ model missing from response');
84
- } else {
85
- console.log('✓ model present');
86
- }
87
-
88
- // Test 3: List conversations and verify agent/model in list
89
- console.log('\nTest 3: List conversations');
90
- const listRes = await request('GET', '/conversations');
91
-
92
- if (listRes.status !== 200) {
93
- console.error('❌ Failed to list conversations:', listRes.status);
94
- return;
95
- }
96
-
97
- const listedConv = listRes.data.conversations.find(c => c.id === conversation.id);
98
- if (!listedConv) {
99
- console.error('❌ Conversation not found in list');
100
- return;
101
- }
102
-
103
- console.log('✓ Conversation in list');
104
- console.log(' - agentId:', listedConv.agentId);
105
- console.log(' - agentType:', listedConv.agentType);
106
- console.log(' - model:', listedConv.model);
107
-
108
- // Test 4: Update conversation model
109
- console.log('\nTest 4: Update conversation model');
110
- const updateRes = await request('POST', `/conversations/${conversation.id}`, {
111
- model: 'claude-opus-4-6'
112
- });
113
-
114
- if (updateRes.status !== 200) {
115
- console.error('❌ Failed to update conversation:', updateRes.status);
116
- return;
117
- }
118
-
119
- const updatedConv = updateRes.data.conversation;
120
- console.log('✓ Updated conversation');
121
- console.log(' - model:', updatedConv.model);
122
-
123
- if (updatedConv.model !== 'claude-opus-4-6') {
124
- console.error('❌ Model not updated correctly');
125
- } else {
126
- console.log('✓ Model updated correctly');
127
- }
128
-
129
- // Cleanup
130
- console.log('\nCleanup: Deleting test conversation');
131
- await request('DELETE', `/conversations/${conversation.id}`);
132
- console.log('✓ Deleted test conversation');
133
-
134
- console.log('\n=== All Tests Passed ===');
135
- } catch (error) {
136
- console.error('❌ Test error:', error.message);
137
- process.exit(1);
138
- }
139
- }
140
-
141
- test();