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
@@ -1,145 +0,0 @@
1
-
2
- class AgentGUIClient {
3
- constructor(config = {}) {
4
- this.config = {
5
- containerId: config.containerId || 'app',
6
- outputContainerId: config.outputContainerId || 'output',
7
- scrollContainerId: config.scrollContainerId || 'output-scroll',
8
- autoConnect: config.autoConnect !== false,
9
- ...config
10
- };
11
-
12
- this.renderer = new StreamingRenderer(config.renderer || {});
13
- this.wsManager = window.wsManager || new WebSocketManager(config.websocket || {});
14
- if (!window.wsManager) window.wsManager = this.wsManager;
15
- this.eventProcessor = new EventProcessor(config.eventProcessor || {});
16
-
17
- this.state = {
18
- isInitialized: false,
19
- currentSession: null,
20
- currentConversation: null,
21
- streamingConversations: new Map(),
22
- sessionEvents: [],
23
- conversations: [],
24
- agents: []
25
- };
26
-
27
- this.conversationCache = new Map();
28
- this.MAX_CACHE_SIZE = 10;
29
-
30
- this.conversationListCache = {
31
- data: [],
32
- timestamp: 0,
33
- ttl: 30000
34
- };
35
-
36
- this.draftPrompts = new Map();
37
-
38
- this.eventHandlers = {};
39
-
40
- this.ui = {
41
- statusIndicator: null,
42
- messageInput: null,
43
- sendButton: null,
44
- cliSelector: null,
45
- agentSelector: null,
46
- modelSelector: null
47
- };
48
-
49
- this._agentLocked = false;
50
- this._isLoadingConversation = false;
51
- this._modelCache = new Map();
52
-
53
- this._renderedSeqs = {};
54
- this._inflightRequests = new Map();
55
- this._previousConvAbort = null;
56
-
57
- this._bgCache = new Map();
58
- this.BG_CACHE_MAX = 50;
59
-
60
- this._loadInProgress = {};
61
- this._currentRequestId = 0;
62
-
63
-
64
- this._scrollTarget = 0;
65
- this._scrollAnimating = false;
66
- this._scrollLerpFactor = config.scrollAnimationSpeed || 0.15;
67
-
68
-
69
- this._serverProcessingEstimate = 2000;
70
- this._lastSendTime = 0;
71
- this._countdownTimer = null;
72
-
73
- this.routerState = {
74
- currentConversationId: null,
75
- currentSessionId: null
76
- };
77
-
78
- this._debug = typeof localStorage !== 'undefined' && localStorage.getItem('debug') === '1';
79
- }
80
-
81
- _dbg(...args) { if (this._debug) console.log('[AgentGUI]', ...args); }
82
-
83
- async init() {
84
- try {
85
- this._dbg('Initializing AgentGUI client');
86
-
87
- const wsReady = this.config.autoConnect ? this.connectWebSocket() : Promise.resolve();
88
-
89
- this.renderer.init(this.config.outputContainerId, this.config.scrollContainerId);
90
-
91
- if (typeof ImageLoader !== 'undefined') {
92
- window.imageLoader = new ImageLoader();
93
- }
94
-
95
- this.setupWebSocketListeners();
96
- this.setupRendererListeners();
97
- this.setupUI();
98
-
99
- await wsReady;
100
- await Promise.all([
101
- this.loadAgents(),
102
- this.loadConversations(),
103
- this.checkSpeechStatus()
104
- ]);
105
-
106
- this.enableControls();
107
-
108
- this.restoreStateFromUrl();
109
-
110
- this.state.isInitialized = true;
111
- this.emit('initialized');
112
- this._setupDebugHooks();
113
-
114
- this._dbg('AgentGUI client initialized');
115
- return this;
116
- } catch (error) {
117
- console.error('Client initialization error:', error);
118
- this.showError('Failed to initialize client: ' + error.message);
119
- throw error;
120
- }
121
- }
122
-
123
- }
124
-
125
- window.__convPerfMetrics = () => {
126
- const entries = performance.getEntriesByType('measure').filter(e => e.name.startsWith('conv-'));
127
- return entries.map(e => ({ name: e.name, ms: Math.round(e.duration) }));
128
- };
129
-
130
- let agentGUIClient = null;
131
-
132
- document.addEventListener('DOMContentLoaded', async () => {
133
- try {
134
- agentGUIClient = new AgentGUIClient();
135
- window.agentGuiClient = agentGUIClient;
136
- await agentGUIClient.init();
137
- agentGUIClient._dbg('AgentGUI ready');
138
- } catch (error) {
139
- console.error('Failed to initialize AgentGUI:', error);
140
- }
141
- });
142
-
143
- if (typeof module !== 'undefined' && module.exports) {
144
- module.exports = AgentGUIClient;
145
- }
@@ -1,4 +0,0 @@
1
- // Client-side msgpack codec — loaded via dynamic import() in ws-client.js
2
- // msgpackr is loaded globally from static/lib/msgpackr.min.js
3
- export const encode = (obj) => msgpackr.pack(obj);
4
- export const decode = (buf) => msgpackr.unpack(new Uint8Array(buf));
@@ -1,145 +0,0 @@
1
- (function() {
2
- const { createMachine, createActor, assign } = XState;
3
-
4
- const convListMachine = createMachine({
5
- id: 'conv-list',
6
- initial: 'unloaded',
7
- context: {
8
- conversations: [],
9
- activeId: null,
10
- streamingIds: [],
11
- version: 0,
12
- lastPollAt: null,
13
- },
14
- states: {
15
- unloaded: {
16
- on: {
17
- LOAD_START: 'loading',
18
- },
19
- },
20
- loading: {
21
- on: {
22
- LOAD_DONE: {
23
- target: 'loaded',
24
- actions: assign(({ context, event }) => {
25
- const incoming = event.conversations || [];
26
- let conversations;
27
- if (incoming.length === 0 && context.conversations.length > 0) {
28
- conversations = context.conversations;
29
- } else if (incoming.length > 0 && incoming.length < context.conversations.length) {
30
- const polledIds = new Set(incoming.map(c => c.id));
31
- const kept = context.conversations.filter(c => !polledIds.has(c.id));
32
- const merged = incoming.map(pc => {
33
- const cached = context.conversations.find(c => c.id === pc.id);
34
- return cached ? Object.assign({}, cached, pc) : pc;
35
- }).concat(kept);
36
- merged.sort((a, b) => {
37
- const ap = a.pinned ? 1 : 0, bp = b.pinned ? 1 : 0;
38
- if (ap !== bp) return bp - ap;
39
- const as_ = a.sortOrder ?? 999999, bs_ = b.sortOrder ?? 999999;
40
- if (as_ !== bs_) return as_ - bs_;
41
- return (b.updated_at || 0) - (a.updated_at || 0);
42
- });
43
- conversations = merged;
44
- } else {
45
- conversations = incoming;
46
- }
47
- return { conversations, version: context.version + 1, lastPollAt: Date.now() };
48
- }),
49
- },
50
- LOAD_ERROR: 'error',
51
- },
52
- },
53
- loaded: {
54
- on: {
55
- LOAD_START: 'loading',
56
- ADD: {
57
- actions: assign(({ context, event }) => {
58
- if (context.conversations.some(c => c.id === event.conversation.id)) return {};
59
- return { conversations: [event.conversation, ...context.conversations], version: context.version + 1 };
60
- }),
61
- },
62
- UPDATE: {
63
- actions: assign(({ context, event }) => {
64
- const idx = context.conversations.findIndex(c => c.id === event.conversation.id);
65
- if (idx < 0) return {};
66
- const updated = [...context.conversations];
67
- updated[idx] = Object.assign({}, updated[idx], event.conversation);
68
- return { conversations: updated, version: context.version + 1 };
69
- }),
70
- },
71
- DELETE: {
72
- actions: assign(({ context, event }) => ({
73
- conversations: context.conversations.filter(c => c.id !== event.id),
74
- activeId: context.activeId === event.id ? null : context.activeId,
75
- streamingIds: context.streamingIds.filter(id => id !== event.id),
76
- version: context.version + 1,
77
- })),
78
- },
79
- CLEAR_ALL: {
80
- actions: assign({ conversations: [], activeId: null, streamingIds: [], version: 0 }),
81
- },
82
- SET_STREAMING: {
83
- actions: assign(({ context, event }) => ({
84
- streamingIds: context.streamingIds.includes(event.id)
85
- ? context.streamingIds
86
- : [...context.streamingIds, event.id],
87
- })),
88
- },
89
- CLEAR_STREAMING: {
90
- actions: assign(({ context, event }) => ({
91
- streamingIds: context.streamingIds.filter(id => id !== event.id),
92
- })),
93
- },
94
- SELECT: {
95
- actions: assign(({ event }) => ({ activeId: event.id })),
96
- },
97
- },
98
- },
99
- error: {
100
- on: {
101
- LOAD_START: 'loading',
102
- },
103
- },
104
- },
105
- });
106
-
107
- const actor = createActor(convListMachine);
108
- actor.start();
109
-
110
- function sendEvent(event) {
111
- actor.send(event);
112
- return actor.getSnapshot();
113
- }
114
-
115
- function getState() {
116
- return actor.getSnapshot().value;
117
- }
118
-
119
- function getContext() {
120
- return actor.getSnapshot().context;
121
- }
122
-
123
- function getConversations() {
124
- return actor.getSnapshot().context.conversations;
125
- }
126
-
127
- function getStreamingIds() {
128
- return actor.getSnapshot().context.streamingIds;
129
- }
130
-
131
- function isStreaming(id) {
132
- return actor.getSnapshot().context.streamingIds.includes(id);
133
- }
134
-
135
- function getActiveId() {
136
- return actor.getSnapshot().context.activeId;
137
- }
138
-
139
- function subscribe(fn) {
140
- return actor.subscribe(fn);
141
- }
142
-
143
- window.__convListMachine = actor;
144
- window.convListMachineAPI = { send: sendEvent, getState, getContext, getConversations, getStreamingIds, isStreaming, getActiveId, subscribe };
145
- })();
@@ -1,198 +0,0 @@
1
- Object.assign(ConversationManager.prototype, {
2
- async loadConversations() {
3
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'LOAD_START' });
4
- try {
5
- const base = window.__BASE_URL || '/gm';
6
- const res = await fetch(base + '/api/conversations');
7
- if (!res.ok) throw new Error('HTTP ' + res.status);
8
- const data = await res.json();
9
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'LOAD_DONE', conversations: data.conversations || [] });
10
- const cliStreaming = window.agentGuiClient?.state?.streamingConversations;
11
- for (const conv of this.conversations) {
12
- const streaming = conv.isStreaming === 1 || conv.isStreaming === true || (cliStreaming ? cliStreaming.has(conv.id) : false);
13
- window.convListMachineAPI?.send({ type: streaming ? 'SET_STREAMING' : 'CLEAR_STREAMING', id: conv.id });
14
- }
15
- this.render();
16
- } catch (err) {
17
- console.error('Failed to load conversations:', err);
18
- window.convListMachineAPI?.send({ type: 'LOAD_ERROR' });
19
- if (this.conversations.length === 0) this.showEmpty('Failed to load conversations');
20
- }
21
- },
22
-
23
- _convVnode(conv) {
24
- const h = window.webjsx?.createElement;
25
- if (!h) return null;
26
- const isActive = conv.id === this.activeId;
27
- const isStreaming = this.streamingConversations.has(conv.id);
28
- const title = conv.title || `Conversation ${conv.id.slice(0, 8)}`;
29
- const timestamp = conv.created_at ? new Date(conv.created_at).toLocaleDateString() : 'Unknown';
30
- const agent = this.getAgentDisplayName(conv.agentId || conv.agentType);
31
- const modelLabel = this.formatModelLabel(conv.model);
32
- const wd = conv.workingDirectory ? pathBasename(conv.workingDirectory) : '';
33
- const metaParts = [agent + modelLabel, timestamp];
34
- if (wd) metaParts.push(wd);
35
- const badge = isStreaming ? h('span', { class: 'conversation-streaming-badge', title: 'Streaming in progress' }, h('span', { class: 'streaming-dot' })) : null;
36
- const dragAttrs = conv.pinned ? { draggable: 'true', 'data-drag-conv': conv.id } : {};
37
- return h('li', { class: 'conversation-item' + (isActive ? ' active' : '') + (conv.pinned ? ' pinned' : ''), 'data-conv-id': conv.id, ...dragAttrs },
38
- h('div', { class: 'conversation-item-content' },
39
- h('div', { class: 'conversation-item-title' }, ...(badge ? [badge, title] : [title])),
40
- h('div', { class: 'conversation-item-meta' }, metaParts.join(' • '))
41
- ),
42
- h('input', { type: 'checkbox', class: 'conversation-item-checkbox', 'data-bulk-check': conv.id, title: 'Select for bulk action', onclick: 'event.stopPropagation()' }),
43
- h('button', { class: 'conversation-item-export', title: 'Export as markdown', 'data-export-conv': conv.id },
44
- h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
45
- h('path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }),
46
- h('polyline', { points: '7 10 12 15 17 10' }),
47
- h('line', { x1: '12', y1: '15', x2: '12', y2: '3' }))),
48
- h('button', { class: 'conversation-item-archive', title: 'Archive conversation', 'data-archive-conv': conv.id },
49
- h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
50
- h('path', { d: 'M21 8v13H3V8' }), h('path', { d: 'M1 3h22v5H1z' }), h('path', { d: 'M10 12h4' }))),
51
- h('button', { class: 'conversation-item-delete', title: 'Delete conversation', 'data-delete-conv': conv.id },
52
- h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
53
- h('polyline', { points: '3 6 5 6 21 6' }),
54
- h('path', { d: 'M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2' })))
55
- );
56
- },
57
-
58
- render() {
59
- if (!this.listEl) return;
60
- if (this.conversations.length === 0) { this.showEmpty(); return; }
61
- this.emptyEl.style.display = 'none';
62
- // Conversations already arrive sorted from server (pinned DESC, sortOrder ASC, updated_at DESC)
63
- const convs = this.conversations;
64
- if (window.webjsx?.applyDiff) {
65
- window.webjsx.applyDiff(this.listEl, convs.map(conv => this._convVnode(conv)).filter(Boolean));
66
- } else {
67
- this._renderFallback(convs);
68
- }
69
- },
70
-
71
- _renderFallback(sorted) {
72
- const existingMap = {};
73
- for (const child of Array.from(this.listEl.children)) {
74
- if (child.dataset.convId) existingMap[child.dataset.convId] = child;
75
- }
76
- const frag = document.createDocumentFragment();
77
- for (const conv of sorted) {
78
- const el = existingMap[conv.id] || document.createElement('li');
79
- el.className = 'conversation-item' + (conv.id === this.activeId ? ' active' : '');
80
- el.dataset.convId = conv.id;
81
- delete existingMap[conv.id];
82
- frag.appendChild(el);
83
- }
84
- for (const orphan of Object.values(existingMap)) orphan.remove();
85
- this.listEl.appendChild(frag);
86
- },
87
-
88
- async confirmDelete(convId, title) {
89
- const confirmed = await window.UIDialog.confirm(`Delete conversation "${title || 'Untitled'}"?\n\nThis will also delete any associated Claude Code session data. This action cannot be undone.`, 'Delete Conversation');
90
- if (!confirmed) return;
91
- try {
92
- await window.wsClient.rpc('conv.del', { id: convId });
93
- this.deleteConversation(convId);
94
- } catch (err) {
95
- window.UIDialog.alert('Failed to delete conversation: ' + (err.message || 'Unknown error'), 'Error');
96
- }
97
- },
98
-
99
- select(convId) {
100
- const result = window.ConversationState?.selectConversation(convId, 'user_click', 1) || { success: false };
101
- if (!result.success && result.reason !== 'already_selected') { console.error('[ConvMgr] activeId mutation rejected:', result.reason); return; }
102
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SELECT', id: convId });
103
- document.querySelectorAll('.conversation-item').forEach(item => item.classList.remove('active'));
104
- const active = document.querySelector(`[data-conv-id="${convId}"]`);
105
- if (active) active.classList.add('active');
106
- window.dispatchEvent(new CustomEvent('conversation-selected', { detail: { conversationId: convId } }));
107
- },
108
-
109
- createNew() {
110
- window.dispatchEvent(new CustomEvent('preparing-new-conversation'));
111
- window.dispatchEvent(new CustomEvent('create-new-conversation'));
112
- },
113
-
114
- showEmpty(message = 'No conversations yet') {
115
- if (!this.listEl) return;
116
- this.listEl.innerHTML = '';
117
- this.emptyEl.textContent = message;
118
- this.emptyEl.style.display = 'block';
119
- },
120
-
121
- addConversation(conv) {
122
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'ADD', conversation: conv });
123
- this.render();
124
- },
125
-
126
- updateConversation(convId, updates) {
127
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'UPDATE', conversation: Object.assign({ id: convId }, updates) });
128
- this.render();
129
- },
130
-
131
- deleteConversation(convId) {
132
- const wasActive = this.activeId === convId;
133
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'DELETE', id: convId });
134
- if (wasActive) { window.ConversationState?.deleteConversation(convId, 1); window.dispatchEvent(new CustomEvent('conversation-deselected')); }
135
- this.render();
136
- },
137
-
138
- getSelectedIds() {
139
- return [...this.listEl.querySelectorAll('.conversation-item-checkbox:checked')].map(cb => cb.dataset.bulkCheck);
140
- },
141
-
142
- async bulkArchive() {
143
- const ids = this.getSelectedIds();
144
- if (ids.length === 0) return;
145
- if (!await window.UIDialog?.confirm(`Archive ${ids.length} conversation(s)?`, 'Bulk Archive')) return;
146
- const base = window.__BASE_URL || '';
147
- for (const id of ids) {
148
- try { await fetch(`${base}/api/conversations/${id}/archive`, { method: 'POST' }); } catch (_) {}
149
- this.deleteConversation(id);
150
- }
151
- },
152
-
153
- async bulkDelete() {
154
- const ids = this.getSelectedIds();
155
- if (ids.length === 0) return;
156
- if (!await window.UIDialog?.confirm(`Permanently delete ${ids.length} conversation(s)?`, 'Bulk Delete')) return;
157
- for (const id of ids) this.confirmDelete(id, '');
158
- },
159
-
160
- async exportConversation(convId) {
161
- try {
162
- const result = await window.wsClient.rpc('conv.export', { id: convId, format: 'markdown' });
163
- const blob = new Blob([result.markdown], { type: 'text/markdown' });
164
- const url = URL.createObjectURL(blob);
165
- const a = document.createElement('a');
166
- a.href = url; a.download = (result.title || 'conversation').replace(/[^a-zA-Z0-9_-]/g, '_') + '.md';
167
- a.click(); URL.revokeObjectURL(url);
168
- } catch (e) { console.error('[export] Failed:', e.message); }
169
- },
170
-
171
- async archiveConversation(convId) {
172
- try {
173
- const resp = await fetch(`${window.__BASE_URL || ''}/api/conversations/${convId}/archive`, { method: 'POST' });
174
- if (!resp.ok) return;
175
- this.deleteConversation(convId);
176
- } catch (e) { console.error('[archive] Failed:', e.message); }
177
- },
178
-
179
- setupWebSocketListener() {
180
- window.addEventListener('ws-message', (event) => {
181
- const msg = event.detail;
182
- if (msg.type === 'conversation_created') { this.addConversation(msg.conversation); }
183
- else if (msg.type === 'conversation_updated') { this.updateConversation(msg.conversation.id, msg.conversation); }
184
- else if (msg.type === 'conversation_deleted') { this.deleteConversation(msg.conversationId); }
185
- else if (msg.type === 'all_conversations_deleted') {
186
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_ALL' });
187
- window.ConversationState?.clear('all_deleted');
188
- this.showEmpty('No conversations yet');
189
- } else if (msg.type === 'streaming_start' && msg.conversationId) {
190
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SET_STREAMING', id: msg.conversationId });
191
- this.render();
192
- } else if ((msg.type === 'streaming_complete' || msg.type === 'streaming_error') && msg.conversationId) {
193
- if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_STREAMING', id: msg.conversationId });
194
- this.render();
195
- }
196
- });
197
- }
198
- });
@@ -1,110 +0,0 @@
1
-
2
- const { createMachine: cmCreateMachine, createActor: cmCreateActor, assign: cmAssign } = XState;
3
-
4
- const convMachine = cmCreateMachine({
5
- id: 'conv-ui',
6
- initial: 'idle',
7
- context: {
8
- sessionId: null,
9
- agentId: null,
10
- queueLength: 0,
11
- },
12
- states: {
13
- idle: {
14
- entry: cmAssign({ sessionId: null, agentId: null, queueLength: 0 }),
15
- on: {
16
- STREAM_START: {
17
- target: 'streaming',
18
- actions: cmAssign(({ event }) => ({
19
- sessionId: event.sessionId || null,
20
- agentId: event.agentId || null,
21
- })),
22
- },
23
- },
24
- },
25
- streaming: {
26
- on: {
27
- COMPLETE: [
28
- {
29
- guard: ({ context }) => context.queueLength > 0,
30
- target: 'queued',
31
- },
32
- { target: 'idle' },
33
- ],
34
- ERROR: 'idle',
35
- QUEUE_UPDATE: {
36
- actions: cmAssign(({ event }) => ({ queueLength: event.queueLength || 0 })),
37
- },
38
- STREAM_START: {
39
- actions: cmAssign(({ event }) => ({
40
- sessionId: event.sessionId || null,
41
- agentId: event.agentId || null,
42
- })),
43
- },
44
- },
45
- },
46
- queued: {
47
- on: {
48
- STREAM_START: {
49
- target: 'streaming',
50
- actions: cmAssign(({ event }) => ({
51
- sessionId: event.sessionId || null,
52
- agentId: event.agentId || null,
53
- })),
54
- },
55
- QUEUE_UPDATE: {
56
- actions: cmAssign(({ event }) => ({ queueLength: event.queueLength || 0 })),
57
- guard: ({ event }) => event.queueLength > 0,
58
- },
59
- QUEUE_EMPTY: 'idle',
60
- COMPLETE: 'idle',
61
- ERROR: 'idle',
62
- },
63
- },
64
- },
65
- });
66
-
67
- const convMachines = new Map();
68
-
69
- function getOrCreate(convId) {
70
- if (convMachines.has(convId)) return convMachines.get(convId);
71
- const actor = cmCreateActor(convMachine);
72
- actor.start();
73
- convMachines.set(convId, actor);
74
- return actor;
75
- }
76
-
77
- function send(convId, event) {
78
- const actor = getOrCreate(convId);
79
- actor.send(event);
80
- return actor.getSnapshot();
81
- }
82
-
83
- function getState(convId) {
84
- const actor = convMachines.get(convId);
85
- return actor ? actor.getSnapshot().value : 'idle';
86
- }
87
-
88
- function isStreaming(convId) {
89
- return getState(convId) === 'streaming';
90
- }
91
-
92
- function isActive(convId) {
93
- const s = getState(convId);
94
- return s === 'streaming' || s === 'queued';
95
- }
96
-
97
- function getQueueLength(convId) {
98
- const actor = convMachines.get(convId);
99
- return actor ? actor.getSnapshot().context.queueLength : 0;
100
- }
101
-
102
- function remove(convId) {
103
- const actor = convMachines.get(convId);
104
- if (actor) { actor.stop(); convMachines.delete(convId); }
105
- }
106
-
107
- if (typeof window !== 'undefined') {
108
- window.__convMachines = convMachines;
109
- window.convMachineAPI = { getOrCreate, send, getState, isStreaming, isActive, getQueueLength, remove };
110
- }