agentgui 1.0.383 → 1.0.385

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 (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +9 -573
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.383",
3
+ "version": "1.0.385",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -12,7 +12,8 @@ import { OAuth2Client } from 'google-auth-library';
12
12
  import express from 'express';
13
13
  import Busboy from 'busboy';
14
14
  import fsbrowse from 'fsbrowse';
15
- import { queries } from './database.js';
15
+ import { queries, db, prepare } from './database.js';
16
+ import { createACPQueries } from './acp-queries.js';
16
17
  import { runClaudeWithStreaming } from './lib/claude-runner.js';
17
18
  import { initializeDescriptors, getAgentDescriptor } from './lib/agent-descriptors.js';
18
19
 
@@ -340,6 +341,8 @@ function discoverAgents() {
340
341
 
341
342
  const discoveredAgents = discoverAgents();
342
343
  initializeDescriptors(discoveredAgents);
344
+ const acpQueries = createACPQueries(db, prepare);
345
+ acpQueries.getAgentDescriptor = getAgentDescriptor;
343
346
 
344
347
  const modelCache = new Map();
345
348
 
@@ -1512,7 +1515,7 @@ const server = http.createServer(async (req, res) => {
1512
1515
  }
1513
1516
 
1514
1517
  const oldRunCancelMatch = pathOnly.match(/^\/api\/runs\/([^/]+)\/cancel$/);
1515
- if (runCancelMatch && req.method === 'POST') {
1518
+ if (oldRunCancelMatch && req.method === 'POST') {
1516
1519
  const runId = oldRunCancelMatch[1];
1517
1520
  const session = queries.getSession(runId);
1518
1521
 
@@ -2073,7 +2076,7 @@ const server = http.createServer(async (req, res) => {
2073
2076
  }
2074
2077
 
2075
2078
  const oldRunCancelMatch1 = pathOnly.match(/^\/api\/runs\/([^/]+)\/cancel$/);
2076
- if (runCancelMatch && req.method === 'POST') {
2079
+ if (oldRunCancelMatch1 && req.method === 'POST') {
2077
2080
  const runId = oldRunCancelMatch1[1];
2078
2081
  try {
2079
2082
  const run = acpQueries.cancelRun(runId);
@@ -2323,573 +2326,6 @@ const server = http.createServer(async (req, res) => {
2323
2326
  return;
2324
2327
  }
2325
2328
 
2326
- const threadsMatch = pathOnly.match(/^\/api\/threads$/);
2327
- if (threadsMatch && req.method === 'POST') {
2328
- let body = '';
2329
- for await (const chunk of req) { body += chunk; }
2330
- let parsed = {};
2331
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2332
-
2333
- const thread = queries.createConversation(parsed.agentId || 'claude-code', parsed.title || 'New Thread', parsed.workingDirectory || STARTUP_CWD);
2334
- sendJSON(req, res, 200, {
2335
- id: thread.id,
2336
- agentId: thread.agentId,
2337
- title: thread.title,
2338
- created_at: thread.created_at,
2339
- status: thread.status,
2340
- state: null
2341
- });
2342
- return;
2343
- }
2344
-
2345
- const threadsSearchMatch = pathOnly.match(/^\/api\/threads\/search$/);
2346
- if (threadsSearchMatch && req.method === 'POST') {
2347
- const conversations = queries.getConversations();
2348
- const threads = conversations.map(c => ({
2349
- id: c.id,
2350
- agentId: c.agentId,
2351
- title: c.title,
2352
- created_at: c.created_at,
2353
- updated_at: c.updated_at,
2354
- status: c.status,
2355
- state: null
2356
- }));
2357
- sendJSON(req, res, 200, threads);
2358
- return;
2359
- }
2360
-
2361
- const oldThreadByIdMatch = pathOnly.match(/^\/api\/threads\/([^/]+)$/);
2362
- if (oldThreadByIdMatch) {
2363
- const threadId = oldThreadByIdMatch[1];
2364
- const conv = queries.getConversation(threadId);
2365
-
2366
- if (!conv) {
2367
- sendJSON(req, res, 404, { error: 'Thread not found' });
2368
- return;
2369
- }
2370
-
2371
- if (req.method === 'GET') {
2372
- sendJSON(req, res, 200, {
2373
- id: conv.id,
2374
- agentId: conv.agentId,
2375
- title: conv.title,
2376
- created_at: conv.created_at,
2377
- updated_at: conv.updated_at,
2378
- status: conv.status,
2379
- state: null
2380
- });
2381
- return;
2382
- }
2383
-
2384
- if (req.method === 'DELETE') {
2385
- const activeEntry = activeExecutions.get(threadId);
2386
- if (activeEntry) {
2387
- sendJSON(req, res, 409, { error: 'Thread has an active run, cannot delete' });
2388
- return;
2389
- }
2390
- queries.deleteConversation(threadId);
2391
- sendJSON(req, res, 204, {});
2392
- return;
2393
- }
2394
-
2395
- if (req.method === 'PATCH') {
2396
- let body = '';
2397
- for await (const chunk of req) { body += chunk; }
2398
- let parsed = {};
2399
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2400
-
2401
- const updates = {};
2402
- if (parsed.title !== undefined) updates.title = parsed.title;
2403
- if (parsed.state !== undefined) updates.state = parsed.state;
2404
-
2405
- if (Object.keys(updates).length > 0) {
2406
- queries.updateConversation(threadId, updates);
2407
- }
2408
-
2409
- const updated = queries.getConversation(threadId);
2410
- sendJSON(req, res, 200, {
2411
- id: updated.id,
2412
- agentId: updated.agentId,
2413
- title: updated.title,
2414
- created_at: updated.created_at,
2415
- updated_at: updated.updated_at,
2416
- status: updated.status,
2417
- state: updated.state
2418
- });
2419
- return;
2420
- }
2421
- }
2422
-
2423
- const oldThreadHistoryMatch = pathOnly.match(/^\/api\/threads\/([^/]+)\/history$/);
2424
- if (threadHistoryMatch && req.method === 'GET') {
2425
- const threadId = oldThreadHistoryMatch[1];
2426
- const conv = queries.getConversation(threadId);
2427
-
2428
- if (!conv) {
2429
- sendJSON(req, res, 404, { error: 'Thread not found' });
2430
- return;
2431
- }
2432
-
2433
- const limit = parseInt(new URL(req.url, 'http://localhost').searchParams.get('limit') || '10', 10);
2434
- const sessions = queries.getSessionsByConversation(threadId, limit);
2435
-
2436
- const history = sessions.map(s => ({
2437
- checkpoint: s.id,
2438
- state: null,
2439
- created_at: s.started_at,
2440
- runId: s.id,
2441
- status: s.status
2442
- })).reverse();
2443
-
2444
- sendJSON(req, res, 200, history);
2445
- return;
2446
- }
2447
-
2448
- const oldThreadCopyMatch = pathOnly.match(/^\/api\/threads\/([^/]+)\/copy$/);
2449
- if (threadCopyMatch && req.method === 'POST') {
2450
- const threadId = threadCopyMatch[1];
2451
- const original = queries.getConversation(threadId);
2452
-
2453
- if (!original) {
2454
- sendJSON(req, res, 404, { error: 'Thread not found' });
2455
- return;
2456
- }
2457
-
2458
- const newThread = queries.createConversation(original.agentId, original.title + ' (copy)', original.workingDirectory);
2459
-
2460
- const messages = queries.getMessages(threadId, 1000, 0);
2461
- for (const msg of messages) {
2462
- queries.createMessage(newThread.id, msg.role, msg.content);
2463
- }
2464
-
2465
- sendJSON(req, res, 200, {
2466
- id: newThread.id,
2467
- agentId: newThread.agentId,
2468
- title: newThread.title,
2469
- created_at: newThread.created_at,
2470
- status: newThread.status,
2471
- state: null
2472
- });
2473
- return;
2474
- }
2475
-
2476
- const threadRunsMatch = pathOnly.match(/^\/api\/threads\/([^/]+)\/runs$/);
2477
- if (threadRunsMatch) {
2478
- const threadId = threadRunsMatch[1];
2479
- const conv = queries.getConversation(threadId);
2480
-
2481
- if (!conv) {
2482
- sendJSON(req, res, 404, { error: 'Thread not found' });
2483
- return;
2484
- }
2485
-
2486
- if (req.method === 'GET') {
2487
- const limit = parseInt(new URL(req.url, 'http://localhost').searchParams.get('limit') || '10', 10);
2488
- const offset = parseInt(new URL(req.url, 'http://localhost').searchParams.get('offset') || '0', 10);
2489
- const sessions = queries.getSessionsByConversation(threadId, limit, offset);
2490
-
2491
- const runs = sessions.map(s => ({
2492
- id: s.id,
2493
- threadId: s.conversationId,
2494
- status: s.status,
2495
- started_at: s.started_at,
2496
- completed_at: s.completed_at,
2497
- agentId: s.agentId,
2498
- input: null,
2499
- output: null
2500
- }));
2501
-
2502
- sendJSON(req, res, 200, runs);
2503
- return;
2504
- }
2505
-
2506
- if (req.method === 'POST') {
2507
- const activeEntry = activeExecutions.get(threadId);
2508
- if (activeEntry) {
2509
- sendJSON(req, res, 409, { error: 'Thread already has an active run' });
2510
- return;
2511
- }
2512
-
2513
- let body = '';
2514
- for await (const chunk of req) { body += chunk; }
2515
- let parsed = {};
2516
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2517
-
2518
- const { input, agentId, webhook } = parsed;
2519
- if (!input) {
2520
- sendJSON(req, res, 400, { error: 'Missing input in request body' });
2521
- return;
2522
- }
2523
-
2524
- const resolvedAgentId = agentId || conv.agentId || 'claude-code';
2525
- const resolvedModel = parsed.model || conv.model || null;
2526
- const cwd = conv.workingDirectory || STARTUP_CWD;
2527
-
2528
- const session = queries.createSession(threadId, resolvedAgentId, 'pending');
2529
- const message = queries.createMessage(threadId, 'user', typeof input === 'string' ? input : JSON.stringify(input));
2530
-
2531
- processMessageWithStreaming(threadId, message.id, session.id, typeof input === 'string' ? input : JSON.stringify(input), resolvedAgentId, resolvedModel);
2532
-
2533
- sendJSON(req, res, 200, {
2534
- id: session.id,
2535
- threadId: threadId,
2536
- status: 'pending',
2537
- started_at: session.started_at,
2538
- agentId: resolvedAgentId
2539
- });
2540
- return;
2541
- }
2542
- }
2543
-
2544
- const threadRunsStreamMatch = pathOnly.match(/^\/api\/threads\/([^\/]+)\/runs\/stream$/);
2545
- if (threadRunsStreamMatch && req.method === 'POST') {
2546
- const threadId = threadRunsStreamMatch[1];
2547
- const conv = queries.getConversation(threadId);
2548
-
2549
- if (!conv) {
2550
- sendJSON(req, res, 404, { error: 'Thread not found' });
2551
- return;
2552
- }
2553
-
2554
- const activeEntry = activeExecutions.get(threadId);
2555
- if (activeEntry) {
2556
- sendJSON(req, res, 409, { error: 'Thread already has an active run' });
2557
- return;
2558
- }
2559
-
2560
- let body = '';
2561
- for await (const chunk of req) { body += chunk; }
2562
- let parsed = {};
2563
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2564
-
2565
- const { input, agentId } = parsed;
2566
- if (!input) {
2567
- sendJSON(req, res, 400, { error: 'Missing input in request body' });
2568
- return;
2569
- }
2570
-
2571
- const resolvedAgentId = agentId || conv.agentId || 'claude-code';
2572
- const resolvedModel = parsed.model || conv.model || null;
2573
-
2574
- const session = queries.createSession(threadId, resolvedAgentId, 'pending');
2575
- const message = queries.createMessage(threadId, 'user', typeof input === 'string' ? input : JSON.stringify(input));
2576
-
2577
- res.writeHead(200, {
2578
- 'Content-Type': 'text/event-stream',
2579
- 'Cache-Control': 'no-cache',
2580
- 'Connection': 'keep-alive'
2581
- });
2582
-
2583
- const eventListeners = [];
2584
- const broadcastListener = (event) => {
2585
- if (event.sessionId === session.id) {
2586
- if (event.type === 'streaming_progress') {
2587
- res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`);
2588
- } else if (event.type === 'streaming_complete') {
2589
- res.write(`event: done\ndata: ${JSON.stringify({ status: 'completed' })}\n\n`);
2590
- res.end();
2591
- } else if (event.type === 'streaming_error') {
2592
- res.write(`event: error\ndata: ${JSON.stringify({ error: event.error })}\n\n`);
2593
- res.end();
2594
- }
2595
- }
2596
- };
2597
- eventListeners.push(broadcastListener);
2598
-
2599
- req.on('close', () => {
2600
- eventListeners.length = 0;
2601
- });
2602
-
2603
- processMessageWithStreaming(threadId, message.id, session.id, typeof input === 'string' ? input : JSON.stringify(input), resolvedAgentId, resolvedModel);
2604
- return;
2605
- }
2606
-
2607
- const threadRunsWaitMatch = pathOnly.match(/^\/api\/threads\/([^\/]+)\/runs\/wait$/);
2608
- if (threadRunsWaitMatch && req.method === 'POST') {
2609
- const threadId = threadRunsWaitMatch[1];
2610
- const conv = queries.getConversation(threadId);
2611
-
2612
- if (!conv) {
2613
- sendJSON(req, res, 404, { error: 'Thread not found' });
2614
- return;
2615
- }
2616
-
2617
- const activeEntry = activeExecutions.get(threadId);
2618
- if (activeEntry) {
2619
- sendJSON(req, res, 409, { error: 'Thread already has an active run' });
2620
- return;
2621
- }
2622
-
2623
- let body = '';
2624
- for await (const chunk of req) { body += chunk; }
2625
- let parsed = {};
2626
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2627
-
2628
- const { input, agentId } = parsed;
2629
- if (!input) {
2630
- sendJSON(req, res, 400, { error: 'Missing input in request body' });
2631
- return;
2632
- }
2633
-
2634
- const resolvedAgentId = agentId || conv.agentId || 'claude-code';
2635
- const resolvedModel = parsed.model || conv.model || null;
2636
-
2637
- const session = queries.createSession(threadId, resolvedAgentId, 'pending');
2638
- const message = queries.createMessage(threadId, 'user', typeof input === 'string' ? input : JSON.stringify(input));
2639
-
2640
- const waitPromise = new Promise((resolve, reject) => {
2641
- const checkInterval = setInterval(() => {
2642
- const updatedSession = queries.getSession(session.id);
2643
- if (!updatedSession) {
2644
- clearInterval(checkInterval);
2645
- reject(new Error('Session not found'));
2646
- return;
2647
- }
2648
- if (['success', 'error', 'interrupted', 'cancelled'].includes(updatedSession.status)) {
2649
- clearInterval(checkInterval);
2650
- resolve(updatedSession);
2651
- }
2652
- }, 500);
2653
-
2654
- setTimeout(() => {
2655
- clearInterval(checkInterval);
2656
- const updatedSession = queries.getSession(session.id);
2657
- resolve(updatedSession || session);
2658
- }, 300000);
2659
- });
2660
-
2661
- processMessageWithStreaming(threadId, message.id, session.id, typeof input === 'string' ? input : JSON.stringify(input), resolvedAgentId, resolvedModel);
2662
-
2663
- try {
2664
- const completedSession = await waitPromise;
2665
- sendJSON(req, res, 200, {
2666
- id: completedSession.id,
2667
- threadId: threadId,
2668
- status: completedSession.status,
2669
- started_at: completedSession.started_at,
2670
- completed_at: completedSession.completed_at,
2671
- agentId: resolvedAgentId,
2672
- output: completedSession.response || null
2673
- });
2674
- } catch (err) {
2675
- sendJSON(req, res, 500, { error: err.message });
2676
- }
2677
- return;
2678
- }
2679
-
2680
-
2681
- const threadRunByIdMatch = pathOnly.match(/^\/api\/threads\/([^/]+)\/runs\/([^/]+)$/);
2682
- if (threadRunByIdMatch) {
2683
- const threadId = threadRunByIdMatch[1];
2684
- const runId = threadRunByIdMatch[2];
2685
- const session = queries.getSession(runId);
2686
-
2687
- if (!session || session.conversationId !== threadId) {
2688
- sendJSON(req, res, 404, { error: 'Run not found' });
2689
- return;
2690
- }
2691
-
2692
- if (req.method === 'GET') {
2693
- sendJSON(req, res, 200, {
2694
- id: session.id,
2695
- threadId: session.conversationId,
2696
- status: session.status,
2697
- started_at: session.started_at,
2698
- completed_at: session.completed_at,
2699
- agentId: session.agentId,
2700
- input: null,
2701
- output: null
2702
- });
2703
- return;
2704
- }
2705
-
2706
- if (req.method === 'DELETE') {
2707
- queries.deleteSession(runId);
2708
- sendJSON(req, res, 204, {});
2709
- return;
2710
- }
2711
-
2712
- if (req.method === 'POST') {
2713
- if (session.status !== 'interrupted') {
2714
- sendJSON(req, res, 409, { error: 'Can only resume interrupted runs' });
2715
- return;
2716
- }
2717
-
2718
- let body = '';
2719
- for await (const chunk of req) { body += chunk; }
2720
- let parsed = {};
2721
- try { parsed = body ? JSON.parse(body) : {}; } catch {}
2722
-
2723
- const { input } = parsed;
2724
- if (!input) {
2725
- sendJSON(req, res, 400, { error: 'Missing input in request body' });
2726
- return;
2727
- }
2728
-
2729
- const conv = queries.getConversation(threadId);
2730
- const resolvedAgentId = session.agentId || conv.agentId || 'claude-code';
2731
- const resolvedModel = conv?.model || null;
2732
- const cwd = conv?.workingDirectory || STARTUP_CWD;
2733
-
2734
- queries.updateSession(runId, { status: 'pending' });
2735
-
2736
- const message = queries.createMessage(threadId, 'user', typeof input === 'string' ? input : JSON.stringify(input));
2737
-
2738
- processMessageWithStreaming(threadId, message.id, runId, typeof input === 'string' ? input : JSON.stringify(input), resolvedAgentId, resolvedModel);
2739
-
2740
- sendJSON(req, res, 200, {
2741
- id: session.id,
2742
- threadId: threadId,
2743
- status: 'pending',
2744
- started_at: session.started_at,
2745
- agentId: resolvedAgentId
2746
- });
2747
- return;
2748
- }
2749
- }
2750
-
2751
- const threadRunWaitMatch = pathOnly.match(/^\/api\/threads\/([^\/]+)\/runs\/([^\/]+)\/wait$/);
2752
- if (threadRunWaitMatch && req.method === 'GET') {
2753
- const threadId = threadRunWaitMatch[1];
2754
- const runId = threadRunWaitMatch[2];
2755
- const session = queries.getSession(runId);
2756
-
2757
- if (!session || session.conversationId !== threadId) {
2758
- sendJSON(req, res, 404, { error: 'Run not found' });
2759
- return;
2760
- }
2761
-
2762
- const waitPromise = new Promise((resolve) => {
2763
- const checkInterval = setInterval(() => {
2764
- const updatedSession = queries.getSession(runId);
2765
- if (!updatedSession) {
2766
- clearInterval(checkInterval);
2767
- resolve(session);
2768
- return;
2769
- }
2770
- if (['success', 'error', 'interrupted', 'cancelled'].includes(updatedSession.status)) {
2771
- clearInterval(checkInterval);
2772
- resolve(updatedSession);
2773
- }
2774
- }, 500);
2775
-
2776
- setTimeout(() => {
2777
- clearInterval(checkInterval);
2778
- const updatedSession = queries.getSession(runId) || session;
2779
- resolve(updatedSession);
2780
- }, 30000);
2781
- });
2782
-
2783
- try {
2784
- const completedSession = await waitPromise;
2785
- sendJSON(req, res, 200, {
2786
- id: completedSession.id,
2787
- threadId: threadId,
2788
- status: completedSession.status,
2789
- started_at: completedSession.started_at,
2790
- completed_at: completedSession.completed_at,
2791
- agentId: completedSession.agentId,
2792
- output: completedSession.response || null
2793
- });
2794
- } catch (err) {
2795
- sendJSON(req, res, 500, { error: err.message });
2796
- }
2797
- return;
2798
- }
2799
-
2800
- const threadRunStreamMatch = pathOnly.match(/^\/api\/threads\/([^\/]+)\/runs\/([^\/]+)\/stream$/);
2801
- if (threadRunStreamMatch && req.method === 'GET') {
2802
- const threadId = threadRunStreamMatch[1];
2803
- const runId = threadRunStreamMatch[2];
2804
- const session = queries.getSession(runId);
2805
-
2806
- if (!session || session.conversationId !== threadId) {
2807
- sendJSON(req, res, 404, { error: 'Run not found' });
2808
- return;
2809
- }
2810
-
2811
- res.writeHead(200, {
2812
- 'Content-Type': 'text/event-stream',
2813
- 'Cache-Control': 'no-cache',
2814
- 'Connection': 'keep-alive'
2815
- });
2816
-
2817
- const chunks = queries.getSessionChunks(runId, 0);
2818
- for (const chunk of chunks) {
2819
- res.write(`event: message\ndata: ${JSON.stringify(chunk)}\n\n`);
2820
- }
2821
-
2822
- const eventListeners = [];
2823
- const broadcastListener = (event) => {
2824
- if (event.sessionId === runId) {
2825
- if (event.type === 'streaming_progress') {
2826
- res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`);
2827
- } else if (event.type === 'streaming_complete') {
2828
- res.write(`event: done\ndata: ${JSON.stringify({ status: 'completed' })}\n\n`);
2829
- res.end();
2830
- } else if (event.type === 'streaming_error') {
2831
- res.write(`event: error\ndata: ${JSON.stringify({ error: event.error })}\n\n`);
2832
- res.end();
2833
- }
2834
- }
2835
- };
2836
- eventListeners.push(broadcastListener);
2837
-
2838
- req.on('close', () => {
2839
- eventListeners.length = 0;
2840
- });
2841
-
2842
- if (['success', 'error', 'interrupted', 'cancelled'].includes(session.status)) {
2843
- res.write(`event: done\ndata: ${JSON.stringify({ status: session.status })}\n\n`);
2844
- res.end();
2845
- }
2846
-
2847
- return;
2848
- }
2849
-
2850
-
2851
- const threadRunCancelMatch = pathOnly.match(/^\/api\/threads\/([^/]+)\/runs\/([^/]+)\/cancel$/);
2852
- if (threadRunCancelMatch && req.method === 'POST') {
2853
- const threadId = threadRunCancelMatch[1];
2854
- const runId = threadRunCancelMatch[2];
2855
- const session = queries.getSession(runId);
2856
-
2857
- if (!session || session.conversationId !== threadId) {
2858
- sendJSON(req, res, 404, { error: 'Run not found' });
2859
- return;
2860
- }
2861
-
2862
- const entry = activeExecutions.get(threadId);
2863
-
2864
- if (entry && entry.sessionId === runId) {
2865
- const { pid } = entry;
2866
- if (pid) {
2867
- try {
2868
- process.kill(-pid, 'SIGKILL');
2869
- } catch {
2870
- try {
2871
- process.kill(pid, 'SIGKILL');
2872
- } catch (e) {}
2873
- }
2874
- }
2875
- }
2876
-
2877
- queries.updateSession(runId, { status: 'interrupted', completed_at: Date.now() });
2878
- queries.setIsStreaming(threadId, false);
2879
- activeExecutions.delete(threadId);
2880
-
2881
- broadcastSync({
2882
- type: 'streaming_complete',
2883
- sessionId: runId,
2884
- conversationId: threadId,
2885
- interrupted: true,
2886
- timestamp: Date.now()
2887
- });
2888
-
2889
- sendJSON(req, res, 204, {});
2890
- return;
2891
- }
2892
-
2893
2329
  if (pathOnly === '/api/stt' && req.method === 'POST') {
2894
2330
  try {
2895
2331
  const chunks = [];
@@ -3198,7 +2634,7 @@ const server = http.createServer(async (req, res) => {
3198
2634
  // GET /threads/{thread_id} - Get thread by ID
3199
2635
  const acpThreadMatch = pathOnly.match(/^\/api\/threads\/([a-f0-9-]{36})$/);
3200
2636
  if (acpThreadMatch && req.method === 'GET') {
3201
- const threadId = threadByIdMatch[1];
2637
+ const threadId = acpThreadMatch[1];
3202
2638
  try {
3203
2639
  const thread = queries.getThread(threadId);
3204
2640
  if (!thread) {
@@ -3214,7 +2650,7 @@ const server = http.createServer(async (req, res) => {
3214
2650
 
3215
2651
  // PATCH /threads/{thread_id} - Update thread metadata
3216
2652
  if (acpThreadMatch && req.method === 'PATCH') {
3217
- const threadId = threadByIdMatch[1];
2653
+ const threadId = acpThreadMatch[1];
3218
2654
  try {
3219
2655
  const body = await parseBody(req);
3220
2656
  const thread = queries.patchThread(threadId, body);
@@ -3231,7 +2667,7 @@ const server = http.createServer(async (req, res) => {
3231
2667
 
3232
2668
  // DELETE /threads/{thread_id} - Delete thread (fail if pending runs exist)
3233
2669
  if (acpThreadMatch && req.method === 'DELETE') {
3234
- const threadId = threadByIdMatch[1];
2670
+ const threadId = acpThreadMatch[1];
3235
2671
  try {
3236
2672
  queries.deleteThread(threadId);
3237
2673
  res.writeHead(204);