agentgui 1.0.931 → 1.0.933

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 (129) hide show
  1. package/AGENTS.md +17 -12
  2. package/database.js +31 -2
  3. package/lib/http-handler.js +11 -25
  4. package/lib/routes-registry.js +4 -48
  5. package/lib/server-startup.js +3 -11
  6. package/lib/ws-setup.js +2 -1
  7. package/package.json +3 -3
  8. package/server.js +7 -1
  9. package/site/app/index.html +2 -2
  10. package/site/app/js/app.js +91 -86
  11. package/site/app/js/backend.js +1 -1
  12. package/static/lib/xstate.umd.min.js +1 -1
  13. package/lib/db-queries-chunks.js +0 -195
  14. package/lib/db-queries-chunks2.js +0 -82
  15. package/lib/db-queries-cleanup.js +0 -74
  16. package/lib/db-queries-del.js +0 -141
  17. package/lib/db-queries-events.js +0 -68
  18. package/lib/db-queries-import.js +0 -133
  19. package/lib/db-queries-messages.js +0 -102
  20. package/lib/db-queries-sessions.js +0 -112
  21. package/lib/db-queries-streams.js +0 -100
  22. package/lib/db-queries.js +0 -89
  23. package/lib/jsonl-parser.js +0 -190
  24. package/lib/jsonl-watcher.js +0 -64
  25. package/lib/routes-agent-actions.js +0 -61
  26. package/lib/routes-auth-config.js +0 -30
  27. package/lib/routes-conversations.js +0 -96
  28. package/lib/routes-debug.js +0 -119
  29. package/lib/routes-messages.js +0 -139
  30. package/lib/routes-runs.js +0 -156
  31. package/lib/routes-scripts.js +0 -135
  32. package/lib/routes-sessions.js +0 -144
  33. package/lib/routes-threads.js +0 -100
  34. package/lib/routes-util.js +0 -110
  35. package/lib/ws-handlers-conv.js +0 -138
  36. package/lib/ws-handlers-conv2.js +0 -169
  37. package/lib/ws-handlers-msg.js +0 -121
  38. package/lib/ws-handlers-queue.js +0 -56
  39. package/lib/ws-handlers-run.js +0 -182
  40. package/lib/ws-handlers-scripts.js +0 -66
  41. package/lib/ws-handlers-session.js +0 -105
  42. package/lib/ws-handlers-session2.js +0 -85
  43. package/lib/ws-legacy-handlers.js +0 -51
  44. package/static/app.js +0 -261
  45. package/static/css/app-shell.css +0 -419
  46. package/static/css/brand-bible.css +0 -591
  47. package/static/css/colors_and_type.css +0 -568
  48. package/static/css/gmail-skin.css +0 -663
  49. package/static/css/main.css +0 -4015
  50. package/static/css/tools-popup.css +0 -472
  51. package/static/index.html +0 -418
  52. package/static/js/agent-auth.js +0 -146
  53. package/static/js/app-shortcuts.js +0 -30
  54. package/static/js/audio-recorder-processor.js +0 -18
  55. package/static/js/client-agents.js +0 -155
  56. package/static/js/client-cache.js +0 -171
  57. package/static/js/client-conv.js +0 -198
  58. package/static/js/client-events.js +0 -164
  59. package/static/js/client-exec.js +0 -160
  60. package/static/js/client-helpers.js +0 -199
  61. package/static/js/client-load.js +0 -175
  62. package/static/js/client-render.js +0 -132
  63. package/static/js/client-scroll.js +0 -178
  64. package/static/js/client-status.js +0 -167
  65. package/static/js/client-streaming.js +0 -117
  66. package/static/js/client-streaming2.js +0 -116
  67. package/static/js/client-streaming3.js +0 -153
  68. package/static/js/client-streaming4.js +0 -194
  69. package/static/js/client-ui-controls.js +0 -170
  70. package/static/js/client-ui.js +0 -128
  71. package/static/js/client-ui2.js +0 -160
  72. package/static/js/client-url.js +0 -93
  73. package/static/js/client-utils.js +0 -174
  74. package/static/js/client-ws-msg.js +0 -88
  75. package/static/js/client-ws.js +0 -161
  76. package/static/js/client.js +0 -145
  77. package/static/js/codec.js +0 -4
  78. package/static/js/conv-list-machine.js +0 -145
  79. package/static/js/conv-list-renderer.js +0 -198
  80. package/static/js/conv-machine.js +0 -110
  81. package/static/js/conv-sidebar-actions.js +0 -188
  82. package/static/js/conv-sidebar-clone.js +0 -91
  83. package/static/js/conversations.js +0 -116
  84. package/static/js/dialogs-types.js +0 -111
  85. package/static/js/dialogs.js +0 -53
  86. package/static/js/event-filter-config.js +0 -36
  87. package/static/js/event-processor.js +0 -181
  88. package/static/js/features.js +0 -187
  89. package/static/js/image-loader-element.js +0 -76
  90. package/static/js/image-loader.js +0 -146
  91. package/static/js/prompt-machine.js +0 -108
  92. package/static/js/recording-machine.js +0 -49
  93. package/static/js/script-runner.js +0 -192
  94. package/static/js/state-barrier.js +0 -105
  95. package/static/js/streaming-renderer-dispatch.js +0 -144
  96. package/static/js/streaming-renderer-events.js +0 -163
  97. package/static/js/streaming-renderer-events2.js +0 -125
  98. package/static/js/streaming-renderer-params.js +0 -38
  99. package/static/js/streaming-renderer-render-misc.js +0 -107
  100. package/static/js/streaming-renderer-render.js +0 -181
  101. package/static/js/streaming-renderer-render2.js +0 -149
  102. package/static/js/streaming-renderer-render3.js +0 -142
  103. package/static/js/streaming-renderer-static.js +0 -181
  104. package/static/js/streaming-renderer-static2.js +0 -140
  105. package/static/js/streaming-renderer-stream.js +0 -170
  106. package/static/js/streaming-renderer-text.js +0 -185
  107. package/static/js/streaming-renderer-tools.js +0 -189
  108. package/static/js/streaming-renderer-tools2.js +0 -92
  109. package/static/js/streaming-renderer.js +0 -200
  110. package/static/js/syntax-highlighter-render.js +0 -72
  111. package/static/js/syntax-highlighter.js +0 -131
  112. package/static/js/terminal-machine.js +0 -51
  113. package/static/js/terminal.js +0 -178
  114. package/static/js/ui-components-rendering.js +0 -62
  115. package/static/js/ui-components.js +0 -88
  116. package/static/js/websocket-manager.js +0 -107
  117. package/static/js/ws-client.js +0 -87
  118. package/static/js/ws-core.js +0 -162
  119. package/static/js/ws-latency.js +0 -88
  120. package/static/js/ws-machine.js +0 -68
  121. package/static/lib/msgpackr.min.js +0 -2
  122. package/static/theme.js +0 -74
  123. package/static/vendor/highlight-js.css +0 -10
  124. package/static/vendor/highlight.min.js +0 -1244
  125. package/static/vendor/prism-dark.css +0 -129
  126. package/static/vendor/rippleui.css +0 -35
  127. package/static/vendor/xterm-addon-fit.min.js +0 -8
  128. package/static/vendor/xterm.css +0 -8
  129. package/static/vendor/xterm.min.js +0 -8
package/AGENTS.md CHANGED
@@ -1,22 +1,27 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
- ## New architecture (2026-05-02 pivot)
3
+ ## Architecture (2026-05-19 pivot — single surface)
4
4
 
5
- There are now **two parallel surfaces** in this repo. Don't conflate them when editing.
5
+ One surface. `server.js` serves `site/app/` at `/` and mounts `ccsniff`'s `/v1/history/*` Express router in-process. The legacy `static/` tree and the legacy `lib/routes-*`/`lib/db-queries-*`/`lib/jsonl-watcher.js` modules are gone. `acptoapi` is no longer used by this project.
6
6
 
7
- 1. **Live client** at `site/app/`single static page, imports [`anentrypoint-design`](https://www.npmjs.com/package/anentrypoint-design) from unpkg, talks to any [`acptoapi`](https://www.npmjs.com/package/acptoapi) backend over fetch / SSE. No build step. Deploys to GH Pages at `/app/` via `.github/workflows/gh-pages.yml` (post-flatspace copy step).
8
- - `site/app/index.html` — shell + CSS
9
- - `site/app/js/backend.js` — acptoapi client (models, chat-stream, history, search, SSE)
10
- - `site/app/js/app.js` — webjsx view + state, exposes `window.__agentgui` for debug
11
- - Configurable backend URL via `?backend=…` query string or `localStorage['agentgui.backend']`
7
+ - `site/app/index.html`shell + CSS, imports `anentrypoint-design` from unpkg
8
+ - `site/app/js/backend.js` — same-origin client (`DEFAULT_BACKEND = ''`); `?backend=` query override for cross-origin debugging
9
+ - `site/app/js/app.js` — webjsx view + state, kits-only rendering (PageHeader, SearchInput, TextField, EventList, Panel, Row, Section); exposes `window.__agentgui`
10
+ - `server.js` — boots ACP/agents/websocket plugins, mounts `createHistoryRouter()` from `ccsniff` at `/`, serves `site/app/` as static root
11
+ - Plugins kept (lib/plugins/): acp, agents, database, files, stream, websocket, workflow
12
12
 
13
- 2. **Legacy server** (`server.js`, `lib/`, `static/`) — the npm-installable Node app, still works, still gets `npm run dev`. Being phased out as the static client + acptoapi cover its features. Don't delete in passing; full removal is its own PR.
13
+ Dependencies:
14
+ - `ccsniff` (>=1.1.0) — exports `createHistoryRouter({projectsDir})` mountable on Express; serves `/v1/history/{sessions,sessions/:sid/events,search,snapshot,reindex,stream}`. Reads `~/.claude/projects` (override via `CLAUDE_PROJECTS_DIR`).
15
+ - `anentrypoint-design` (>=0.0.119) — kit library, single-file ESM from unpkg
14
16
 
15
- Backend dependencies:
16
- - `acptoapi` provides chat / messages / models endpoints (existing) + new history endpoints (`/v1/history/sessions`, `/v1/history/sessions/:sid/events`, `/v1/history/search`, `/v1/history/stream`) — see `c:\dev\acptoapi\lib\history\` (ccsniff functionality merged in 2026-05-02).
17
- - `anentrypoint-design` provides AppShell / Chat / FileGrid / etc. — single-file ESM from unpkg, no install.
17
+ ## Browser Witness (2026-05-19)
18
18
 
19
- The static client never imports anything from `lib/` or `server.js`. Cross-contamination = bug.
19
+ Local server on PORT=3056 (default), `bun server.js`:
20
+ - `GET /health` → 200 JSON
21
+ - `GET /v1/history/sessions` → `{"sessions":[]}` from ccsniff
22
+ - `GET /` → site/app/index.html
23
+ - WS `/sync` → opens, sync_connected
24
+ - Browser at `localhost:3056/`: AppShell renders, nav=[chat,history,settings], SSE `hello` received (live.connected=true, eventCount=1), 0 console errors, backend resolves to `''` (same origin).
20
25
 
21
26
  ## Learning audit
22
27
 
package/database.js CHANGED
@@ -5,7 +5,36 @@ import { createRequire } from 'module';
5
5
  import { initSchema } from './database-schema.js';
6
6
  import { migrateFromJson, migrateToACP, migrateConversationColumns } from './database-migrations.js';
7
7
  import { migrateACPSchema, migrateBackfillMessages, migrateFTS, migrateAutoVacuum } from './database-migrations-acp.js';
8
- import { createQueries } from './lib/db-queries.js';
8
+ // db-queries layer removed; queries is now a no-op proxy. History is served by ccsniff.
9
+ function createQueries(db) {
10
+ const noop = () => undefined;
11
+ const target = {
12
+ _db: db,
13
+ cleanup: noop,
14
+ cleanupEmptyConversations: () => 0,
15
+ cleanupOrphanedSessions: () => 0,
16
+ clearAllStreamingFlags: () => 0,
17
+ getStreamingConversations: () => [],
18
+ getResumableConversations: () => [],
19
+ getActiveSessions: () => [],
20
+ getSessionsProcessingLongerThan: () => [],
21
+ getConversationsList: () => [],
22
+ getConversation: () => null,
23
+ getSession: () => null,
24
+ getLatestSession: () => null,
25
+ getAllSessions: () => [],
26
+ getStreamChunks: () => [],
27
+ getExecutionEvents: () => [],
28
+ searchAgents: () => [],
29
+ };
30
+ return new Proxy(target, {
31
+ get(t, k) {
32
+ if (k in t) return t[k];
33
+ if (typeof k === 'symbol') return undefined;
34
+ return () => undefined;
35
+ },
36
+ });
37
+ }
9
38
 
10
39
  const require = createRequire(import.meta.url);
11
40
 
@@ -75,6 +104,6 @@ function generateId(prefix) {
75
104
  return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
76
105
  }
77
106
 
78
- export const queries = createQueries(db, prep, generateId);
107
+ export const queries = createQueries(db);
79
108
 
80
109
  export default { queries };
@@ -33,7 +33,7 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
33
33
  }
34
34
 
35
35
  const pathOnly = req.url.split('?')[0];
36
- if (pathOnly.startsWith(BASE_URL + '/api/upload/') || pathOnly.startsWith(BASE_URL + '/files/')) return expressApp(req, res);
36
+ if (pathOnly.startsWith(BASE_URL + '/api/upload/') || pathOnly.startsWith(BASE_URL + '/files/') || pathOnly.startsWith('/v1/history')) return expressApp(req, res);
37
37
 
38
38
  if (req.url === '/favicon.ico' || req.url === BASE_URL + '/favicon.ico') {
39
39
  const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" rx="20" fill="#3b82f6"/><text x="50" y="68" font-size="50" font-family="sans-serif" font-weight="bold" fill="white" text-anchor="middle">G</text></svg>';
@@ -41,13 +41,14 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
41
41
  res.end(svg); return;
42
42
  }
43
43
 
44
- if (req.url === '/') { res.writeHead(302, { Location: BASE_URL + '/' }); res.end(); return; }
44
+ // serve index.html at root directly (no redirect)
45
45
 
46
46
  let routePath = req.url;
47
47
  if (req.url.startsWith(BASE_URL + '/')) { routePath = req.url.slice(BASE_URL.length); }
48
48
  else if (req.url === BASE_URL) { routePath = '/'; }
49
49
  else if (req.url.startsWith('/api/') || req.url.startsWith('/js/') || req.url.startsWith('/css/') ||
50
50
  req.url.startsWith('/vendor/') || req.url.startsWith('/sync') || req.url === '/' ||
51
+ req.url === '/health' || req.url.startsWith('/v1/') ||
51
52
  req.url.startsWith('/conversations/')) { routePath = req.url; }
52
53
  else { res.writeHead(404); res.end('Not found'); return; }
53
54
 
@@ -56,7 +57,7 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
56
57
  try {
57
58
  const pathOnly = routePath.split('?')[0];
58
59
 
59
- if (pathOnly === '/api/health' && req.method === 'GET') {
60
+ if ((pathOnly === '/api/health' || pathOnly === '/health') && req.method === 'GET') {
60
61
  let dbStatus = { ok: true };
61
62
  try { queries._db.prepare('SELECT 1').get(); } catch (e) { dbStatus = { ok: false, error: e.message }; }
62
63
  const queueSizes = {};
@@ -65,28 +66,13 @@ export function createHttpHandler({ BASE_URL, expressApp, queries, sendJSON, ser
65
66
  return;
66
67
  }
67
68
 
68
- const convHandler = routes.conv._match(req.method, pathOnly);
69
- if (convHandler) { await convHandler(req, res); return; }
70
- const messagesHandler = routes.messages._match(req.method, pathOnly);
71
- if (messagesHandler) { await messagesHandler(req, res); return; }
72
- const sessionsHandler = routes.sessions._match(req.method, pathOnly);
73
- if (sessionsHandler) { await sessionsHandler(req, res); return; }
74
- const scriptsHandler = routes.scripts._match(req.method, pathOnly);
75
- if (scriptsHandler) { await scriptsHandler(req, res); return; }
76
- const runsHandler = routes.runs._match(req.method, pathOnly);
77
- if (runsHandler) { await runsHandler(req, res); return; }
78
- const agentHandler = routes.agents._match(req.method, pathOnly);
79
- if (agentHandler) { await agentHandler(req, res); return; }
80
- const agentActionsHandler = routes.agentActions._match(req.method, pathOnly);
81
- if (agentActionsHandler) { await agentActionsHandler(req, res); return; }
82
- const authConfigHandler = routes.authConfig._match(req.method, pathOnly);
83
- if (authConfigHandler) { await authConfigHandler(req, res); return; }
84
- const utilHandler = routes.util._match(req.method, pathOnly);
85
- if (utilHandler) { await utilHandler(req, res); return; }
86
- const threadHandler = routes.threads._match(req.method, pathOnly);
87
- if (threadHandler) { await threadHandler(req, res); return; }
88
- const debugHandler = routes.debug._match(req.method, pathOnly);
89
- if (debugHandler) { await debugHandler(req, res); return; }
69
+ // Legacy REST handlers removed. History served by ccsniff at /v1/history/*.
70
+ for (const key of Object.keys(routes)) {
71
+ try {
72
+ const h = routes[key]?._match?.(req.method, pathOnly);
73
+ if (h) { await h(req, res); return; }
74
+ } catch (_) {}
75
+ }
90
76
  if (routePath.startsWith('/api/image/')) {
91
77
  const imagePath = routePath.slice('/api/image/'.length);
92
78
  const decodedPath = decodeURIComponent(imagePath);
@@ -1,50 +1,6 @@
1
- import { register as registerUtilRoutes } from './routes-util.js';
2
- import { register as registerThreadRoutes } from './routes-threads.js';
3
- import { register as registerDebugRoutes } from './routes-debug.js';
4
- import { register as registerConvRoutes } from './routes-conversations.js';
5
- import { register as registerAgentRoutes } from './routes-agents.js';
6
- import { register as registerMessagesRoutes } from './routes-messages.js';
7
- import { register as registerSessionsRoutes } from './routes-sessions.js';
8
- import { register as registerRunsRoutes } from './routes-runs.js';
9
- import { register as registerScriptsRoutes } from './routes-scripts.js';
10
- import { register as registerAgentActionsRoutes } from './routes-agent-actions.js';
11
- import { register as registerAuthConfigRoutes } from './routes-auth-config.js';
12
- import { register as registerConvHandlers } from './ws-handlers-conv.js';
13
- import { register as registerConvHandlers2 } from './ws-handlers-conv2.js';
14
- import { register as registerSessionHandlers } from './ws-handlers-session.js';
15
- import { register as registerSessionHandlers2 } from './ws-handlers-session2.js';
16
- import { register as registerRunHandlers } from './ws-handlers-run.js';
17
- import { register as registerUtilHandlers } from './ws-handlers-util.js';
18
- import { register as registerScriptHandlers } from './ws-handlers-scripts.js';
19
- import { register as registerQueueHandlers } from './ws-handlers-queue.js';
20
- import { register as registerMsgHandlers } from './ws-handlers-msg.js';
21
- import { getAgentDescriptor } from './agent-descriptors.js';
22
- import { getProviderConfigs, saveProviderConfig } from './provider-config.js';
23
-
1
+ // Legacy route/ws registry stripped down. Old REST routes and WS handlers
2
+ // removed in favor of ccsniff /v1/history/* and the static site/app client.
3
+ // Keep as a no-op shim so server.js can still call it.
24
4
  export function createRegistry(wsRouter, deps) {
25
- const { queries, sendJSON, parseBody, broadcastSync, debugLog, PORT, BASE_URL, rootDir, STARTUP_CWD, PKG_VERSION, processMessageWithStreaming, activeExecutions, activeProcessesByRunId, activeScripts, messageQueues, rateLimitState, cleanupExecution, discoveredAgents, getACPStatus, modelCache, getModelsForAgent, logError, syncClients, wsOptimizer, errLogPath, getJsonlWatcher, routes } = deps;
26
-
27
- routes.util = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
28
- routes.threads = registerThreadRoutes({ sendJSON, parseBody, queries });
29
- routes.debug = registerDebugRoutes({ sendJSON, queries, activeExecutions, messageQueues, syncClients, wsOptimizer, _errLogPath: errLogPath });
30
- routes.conv = registerConvRoutes({ sendJSON, parseBody, queries, activeExecutions, broadcastSync });
31
- routes.agents = registerAgentRoutes({ sendJSON, parseBody, queries, discoveredAgents, getACPStatus, modelCache, getModelsForAgent, debugLog });
32
- routes.messages = registerMessagesRoutes({ queries, sendJSON, parseBody, broadcastSync, processMessageWithStreaming, activeExecutions, messageQueues, debugLog, logError });
33
- routes.sessions = registerSessionsRoutes({ queries, sendJSON, activeExecutions, rateLimitState, debugLog });
34
- routes.runs = registerRunsRoutes({ sendJSON, parseBody, queries, broadcastSync, processMessageWithStreaming, activeExecutions, activeProcessesByRunId, discoveredAgents, STARTUP_CWD });
35
- routes.scripts = registerScriptsRoutes({ sendJSON, parseBody, queries, broadcastSync, activeScripts, activeExecutions, processMessageWithStreaming, STARTUP_CWD });
36
- routes.agentActions = registerAgentActionsRoutes({ sendJSON, queries, broadcastSync, discoveredAgents, activeScripts, modelCache, PORT, BASE_URL, rootDir });
37
- routes.authConfig = registerAuthConfigRoutes({ sendJSON, parseBody, getProviderConfigs, saveProviderConfig });
38
-
39
- registerConvHandlers(wsRouter, { queries, activeExecutions, rateLimitState, broadcastSync, processMessageWithStreaming, cleanupExecution, getJsonlWatcher });
40
- registerConvHandlers2(wsRouter, { queries, activeExecutions, rateLimitState, broadcastSync, processMessageWithStreaming, cleanupExecution, getJsonlWatcher });
41
- registerMsgHandlers(wsRouter, { queries, activeExecutions, messageQueues, broadcastSync, processMessageWithStreaming, logError });
42
- registerQueueHandlers(wsRouter, { queries, messageQueues, broadcastSync });
43
- debugLog('[INIT] registerSessionHandlers, agents: ' + discoveredAgents.length);
44
- registerSessionHandlers(wsRouter, { db: queries, discoveredAgents, modelCache, getAgentDescriptor, activeScripts, broadcastSync });
45
- registerSessionHandlers2(wsRouter, { discoveredAgents, modelCache, activeScripts, broadcastSync });
46
- debugLog('[INIT] registerSessionHandlers completed');
47
- registerRunHandlers(wsRouter, { queries, discoveredAgents, activeExecutions, activeProcessesByRunId, broadcastSync, processMessageWithStreaming, cleanupExecution });
48
- registerUtilHandlers(wsRouter, { queries, wsOptimizer, broadcastSync, getProviderConfigs, saveProviderConfig, STARTUP_CWD, discoveredAgents });
49
- registerScriptHandlers(wsRouter, { queries, broadcastSync, STARTUP_CWD, activeScripts });
5
+ // intentionally empty
50
6
  }
@@ -1,4 +1,5 @@
1
- import { JsonlWatcher } from './jsonl-watcher.js';
1
+ // JsonlWatcher removed; history now served by ccsniff in-process.
2
+ const JsonlWatcher = null;
2
3
 
3
4
  export function createOnServerReady({ queries, broadcastSync, warmAssetCache, staticDir, discoveredAgents, PORT, BASE_URL, watch, setWatcher, resumeInterruptedStreams, activeExecutions, debugLog, installGMAgentConfigs, startACPTools, getACPStatus, execMachine, performAutoImport, performAgentHealthCheck, recoverStaleSessions }) {
4
5
  let jsonlWatcher = null;
@@ -21,16 +22,7 @@ export function createOnServerReady({ queries, broadcastSync, warmAssetCache, st
21
22
  try { queries.cleanup(); console.log('[cleanup] Scheduled DB cleanup complete'); } catch (e) { console.error('[cleanup] Error:', e.message); }
22
23
  }, 6 * 60 * 60 * 1000);
23
24
 
24
- if (process.env.AGENTGUI_SKIP_AUTO_IMPORT === '1') {
25
- console.log('[JSONL] Watcher skipped (AGENTGUI_SKIP_AUTO_IMPORT=1)');
26
- } else {
27
- try {
28
- jsonlWatcher = new JsonlWatcher({ broadcastSync, queries });
29
- jsonlWatcher.start();
30
- if (setWatcher) setWatcher(jsonlWatcher);
31
- console.log('[JSONL] Watcher started');
32
- } catch (err) { console.error('[JSONL] Watcher failed to start:', err.message); }
33
- }
25
+ // JsonlWatcher removed; ccsniff handles JSONL history via its own watcher.
34
26
 
35
27
  resumeInterruptedStreams().catch(err => console.error('[RESUME] Startup error:', err.message));
36
28
 
package/lib/ws-setup.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { WebSocketServer } from 'ws';
4
- import { registerLegacyHandler } from './ws-legacy-handlers.js';
4
+ // Legacy WS handlers removed; no-op shim kept for callsite compatibility.
5
+ const registerLegacyHandler = () => {};
5
6
 
6
7
  export function createWsSetup(server, { BASE_URL, watch, staticDir, _assetCache, htmlState, sendWs, wsRouter, debugLog, subscriptionIndex, syncClients, wsOptimizer, legacyDeps }) {
7
8
  const hotReloadClients = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.931",
3
+ "version": "1.0.933",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -31,6 +31,7 @@
31
31
  "better-sqlite3": "^12.9.0",
32
32
  "busboy": "^1.6.0",
33
33
  "ccfollow": "^1.0.7",
34
+ "ccsniff": "github:AnEntrypoint/ccsniff#main",
34
35
  "execa": "^9.6.1",
35
36
  "express": "^5.2.1",
36
37
  "form-data": "^4.0.5",
@@ -48,7 +49,6 @@
48
49
  },
49
50
  "devDependencies": {
50
51
  "electron": "^35.0.0",
51
- "playwright": "^1.59.1",
52
- "rippleui": "^1.12.1"
52
+ "playwright": "^1.59.1"
53
53
  }
54
54
  }
package/server.js CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { LRUCache } from 'lru-cache';
6
6
  const PKG_VERSION = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version;
7
7
  import { createExpressApp } from './lib/routes-upload.js';
8
+ import { createHistoryRouter } from 'ccsniff';
8
9
  import { queries } from './database.js';
9
10
  import { runClaudeWithStreaming } from './lib/claude-runner-run.js';
10
11
  import { initializeDescriptors, getAgentDescriptor } from './lib/agent-descriptors.js';
@@ -64,10 +65,15 @@ const STARTUP_CWD = (() => {
64
65
  const cwd = process.env.STARTUP_CWD || process.cwd();
65
66
  try { fs.accessSync(cwd, fs.constants.R_OK); return cwd; } catch { console.warn(`[server] STARTUP_CWD "${cwd}" not accessible, falling back to ${process.cwd()}`); return process.cwd(); }
66
67
  })();
67
- const staticDir = path.join(rootDir, 'static');
68
+ const staticDir = path.join(rootDir, 'site', 'app');
68
69
  if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
69
70
 
70
71
  const expressApp = createExpressApp({ queries, BASE_URL });
72
+ try {
73
+ const historyRouter = await createHistoryRouter({ projectsDir: process.env.CLAUDE_PROJECTS_DIR });
74
+ expressApp.use('/', historyRouter);
75
+ console.log('[ccsniff] /v1/history/* mounted');
76
+ } catch (e) { console.error('[ccsniff] mount failed:', e.message); }
71
77
 
72
78
  let discoveredAgents = [];
73
79
  initializeDescriptors(discoveredAgents);
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <title>agentgui</title>
7
7
  <meta name="description" content="agentgui — live client for any acptoapi backend.">
8
- <link rel="stylesheet" href="https://unpkg.com/anentrypoint-design@latest/dist/247420.css">
8
+ <link rel="stylesheet" href="https://unpkg.com/anentrypoint-design@0.0.127/dist/247420.css">
9
9
  <script type="importmap">
10
- { "imports": { "anentrypoint-design": "https://unpkg.com/anentrypoint-design@latest/dist/247420.js" } }
10
+ { "imports": { "anentrypoint-design": "https://unpkg.com/anentrypoint-design@0.0.127/dist/247420.js" } }
11
11
  </script>
12
12
  <style>
13
13
  html, body { margin: 0; height: 100%; }
@@ -3,7 +3,7 @@ import * as B from './backend.js';
3
3
 
4
4
  installStyles().catch(() => {});
5
5
 
6
- const { AppShell, Topbar, Crumb, Side, Status, Chat, ChatComposer, Row, Panel } = C;
6
+ const { AppShell, Topbar, Crumb, Side, Status, Chat, ChatComposer, Row, Panel, PageHeader, SearchInput, TextField, Select, Btn, EventList } = C;
7
7
 
8
8
  const state = {
9
9
  backend: B.getBackend(),
@@ -114,18 +114,16 @@ function view() {
114
114
 
115
115
  const crumbRight = state.tab === 'chat'
116
116
  ? [
117
- h('select', {
118
- key: 'modelsel',
119
- onchange: (e) => { state.selectedModel = e.target.value; render(); },
120
- },
121
- h('option', { key: '__', value: '' }, '— model —'),
122
- ...state.models.map(m =>
123
- h('option', { key: m.id, value: m.id, selected: m.id === state.selectedModel }, m.id)
124
- ),
125
- ),
117
+ Select({
118
+ key: 'modelsel',
119
+ value: state.selectedModel,
120
+ placeholder: '— model —',
121
+ options: state.models.map(m => ({ value: m.id, label: m.id })),
122
+ onChange: (v) => { state.selectedModel = v; render(); },
123
+ }),
126
124
  state.chat.busy
127
- ? h('a', { key: 'stop', onclick: cancelChat, style: 'cursor:pointer' }, '◼ stop')
128
- : h('a', { key: 'new', onclick: newChat, style: 'cursor:pointer' }, '+ new'),
125
+ ? Btn({ key: 'stop', onClick: cancelChat, children: '◼ stop' })
126
+ : Btn({ key: 'new', onClick: newChat, children: '+ new' }),
129
127
  dot,
130
128
  ]
131
129
  : [dot];
@@ -169,13 +167,22 @@ function mainContent() {
169
167
 
170
168
  // ── chat ───────────────────────────────────────────────────────────────────
171
169
  function chatMain() {
172
- const msgs = state.chat.messages.map((m, i) => ({
173
- key: String(i),
174
- who: m.role === 'user' ? 'you' : 'them',
175
- name: m.role === 'assistant' ? (state.selectedModel || 'agent') : 'you',
176
- time: m.time || '',
177
- parts: [{ kind: 'text', text: m.content || '' }],
178
- }));
170
+ const lastIdx = state.chat.messages.length - 1;
171
+ const msgs = state.chat.messages.map((m, i) => {
172
+ const isAssistant = m.role === 'assistant';
173
+ const isStreaming = state.chat.busy && i === lastIdx && isAssistant;
174
+ const isEmptyStreaming = isStreaming && !m.content;
175
+ return {
176
+ key: String(i),
177
+ who: isAssistant ? 'them' : 'you',
178
+ name: isAssistant ? (state.selectedModel || 'agent') : 'you',
179
+ time: m.time || '',
180
+ typing: isEmptyStreaming,
181
+ parts: isEmptyStreaming
182
+ ? undefined
183
+ : [{ kind: isAssistant ? 'md' : 'text', text: m.content || '' }],
184
+ };
185
+ });
179
186
 
180
187
  const composer = ChatComposer({
181
188
  value: state.chat.draft,
@@ -235,33 +242,28 @@ async function sendChat() {
235
242
 
236
243
  // ── history ────────────────────────────────────────────────────────────────
237
244
  function historyMain() {
238
- const head = h('div', { class: 'ds-section' },
239
- h('h1', {}, '§ history'),
240
- h('p', { class: 'lede' },
241
- state.selectedSid
242
- ? 'session ' + state.selectedSid
243
- : 'pick a session from the sidebar — events stream from acptoapi /v1/history.',
244
- ),
245
- );
245
+ const head = PageHeader({
246
+ title: '§ history',
247
+ lede: state.selectedSid
248
+ ? 'session ' + state.selectedSid
249
+ : 'pick a session from the sidebar — events stream from ccsniff /v1/history.',
250
+ });
246
251
 
247
252
  if (!state.selectedSid) return [head];
248
253
  if (state.events.length === 0) return [head, Panel({ title: 'events', children: h('p', { class: 'lede' }, '◌ loading…') })];
249
254
 
250
- const rows = state.events.map((e, i) =>
251
- Row({
252
- key: 'ev' + i,
253
- rank: String(i + 1).padStart(3, '0'),
254
- title: (e.text || '').slice(0, 200) || '(empty)',
255
- sub: new Date(e.ts).toLocaleString() + ' · ' + (e.role || '?') + ' · ' + (e.type || '?') + (e.tool ? ' · ⌘ ' + e.tool : ''),
256
- rail: e.role === 'error' ? 'flame' : (e.role === 'user' ? 'green' : 'purple'),
257
- })
258
- );
259
-
260
255
  return [
261
256
  head,
262
257
  Panel({
263
258
  title: state.events.length + ' events',
264
- children: h('div', { class: 'ds-section' }, ...rows),
259
+ children: EventList({
260
+ items: state.events.map((e, i) => ({
261
+ key: 'ev' + i,
262
+ code: String(i + 1).padStart(3, '0'),
263
+ title: (e.text || '').slice(0, 200) || '(empty)',
264
+ sub: new Date(e.ts).toLocaleString() + ' · ' + (e.role || '?') + ' · ' + (e.type || '?') + (e.tool ? ' · ⌘ ' + e.tool : ''),
265
+ })),
266
+ }),
265
267
  }),
266
268
  ];
267
269
  }
@@ -306,15 +308,15 @@ function historySide() {
306
308
  }),
307
309
  Panel({
308
310
  title: searching ? 'matches' : 'sessions',
309
- children: h('div', { class: 'ds-section' },
310
- h('input', {
311
- type: 'search',
311
+ children: [
312
+ SearchInput({
313
+ key: 'searchInput',
312
314
  placeholder: 'search sessions…',
313
315
  value: state.searchQ,
314
- oninput: (e) => { state.searchQ = e.target.value; runSearch(); },
316
+ onInput: (v) => { state.searchQ = v; runSearch(); },
315
317
  }),
316
- rows.length ? h('div', {}, ...rows) : h('p', { class: 'lede' }, 'no sessions yet'),
317
- ),
318
+ rows.length ? h('div', { key: 'rows' }, ...rows) : h('p', { key: 'empty', class: 'lede' }, 'no sessions yet'),
319
+ ],
318
320
  }),
319
321
  ];
320
322
  }
@@ -323,48 +325,51 @@ function historySide() {
323
325
  function settingsMain() {
324
326
  const ok = state.health.status === 'ok';
325
327
  return [
326
- h('div', { class: 'ds-section' },
327
- h('h1', {}, '⌘ settings'),
328
- h('p', { class: 'lede' }, 'point agentgui at any acptoapi backend. ?backend=… in the URL or the field below both persist via localStorage.'),
329
- Panel({
330
- title: 'backend',
331
- children: h('div', { class: 'ds-section' },
332
- h('p', { class: 'lede' }, 'backend url'),
333
- h('input', {
334
- type: 'text',
335
- value: state.backendDraft,
336
- oninput: (e) => { state.backendDraft = e.target.value; render(); },
337
- }),
338
- h('p', { class: 'lede' }, (ok ? '● ' : '○ ') + JSON.stringify(state.health)),
339
- h('button', {
340
- onclick: () => {
341
- B.setBackend(state.backendDraft);
342
- state.backend = state.backendDraft;
343
- state.health = { status: 'unknown' };
344
- render();
345
- init();
346
- },
347
- }, 'save + reconnect'),
348
- ),
349
- }),
350
- Panel({
351
- title: 'models',
352
- children: h('div', { class: 'ds-section' },
353
- state.models.length
354
- ? h('div', {}, ...state.models.slice(0, 40).map((m, i) =>
355
- Row({
356
- key: 'm' + i,
357
- rank: String(i + 1).padStart(3, '0'),
358
- title: m.id,
359
- sub: m.owned_by || m.object || 'model',
360
- rail: m.id === state.selectedModel ? 'green' : 'purple',
361
- onClick: () => { state.selectedModel = m.id; render(); },
362
- })
363
- ))
364
- : h('p', { class: 'lede' }, 'no models loaded'),
365
- ),
366
- }),
367
- ),
328
+ PageHeader({
329
+ title: '⌘ settings',
330
+ lede: 'point agentgui at any backend. blank = same-origin (ccsniff in-process). ?backend=… or the field below persists via localStorage.',
331
+ }),
332
+ Panel({
333
+ title: 'backend',
334
+ children: [
335
+ TextField({
336
+ key: 'backendField',
337
+ label: 'backend url',
338
+ value: state.backendDraft,
339
+ placeholder: '(blank = same origin)',
340
+ onInput: (v) => { state.backendDraft = v; render(); },
341
+ }),
342
+ h('p', { key: 'hp', class: 'lede' }, (ok ? '● ' : '○ ') + JSON.stringify(state.health)),
343
+ Btn({
344
+ key: 'savebtn',
345
+ primary: true,
346
+ onClick: (e) => {
347
+ e.preventDefault();
348
+ B.setBackend(state.backendDraft);
349
+ state.backend = state.backendDraft;
350
+ state.health = { status: 'unknown' };
351
+ render();
352
+ init();
353
+ },
354
+ children: 'save + reconnect',
355
+ }),
356
+ ],
357
+ }),
358
+ Panel({
359
+ title: 'models',
360
+ children: state.models.length
361
+ ? state.models.slice(0, 40).map((m, i) =>
362
+ Row({
363
+ key: 'm' + i,
364
+ rank: String(i + 1).padStart(3, '0'),
365
+ title: m.id,
366
+ sub: m.owned_by || m.object || 'model',
367
+ rail: m.id === state.selectedModel ? 'green' : 'purple',
368
+ onClick: () => { state.selectedModel = m.id; render(); },
369
+ })
370
+ )
371
+ : h('p', { key: 'none', class: 'lede' }, 'no models loaded'),
372
+ }),
368
373
  ];
369
374
  }
370
375
 
@@ -1,6 +1,6 @@
1
1
  // acptoapi backend client. Resolves base URL from ?backend= or localStorage or default.
2
2
  const KEY = 'agentgui.backend';
3
- const DEFAULT_BACKEND = 'http://localhost:4800';
3
+ const DEFAULT_BACKEND = '';
4
4
 
5
5
  export function getBackend() {
6
6
  const u = new URL(location.href);