agentgui 1.0.728 → 1.0.729

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.
@@ -0,0 +1,137 @@
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
+ conversations = 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
+ } else {
37
+ conversations = incoming;
38
+ }
39
+ return { conversations, version: context.version + 1, lastPollAt: Date.now() };
40
+ }),
41
+ },
42
+ LOAD_ERROR: 'error',
43
+ },
44
+ },
45
+ loaded: {
46
+ on: {
47
+ LOAD_START: 'loading',
48
+ ADD: {
49
+ actions: assign(({ context, event }) => {
50
+ if (context.conversations.some(c => c.id === event.conversation.id)) return {};
51
+ return { conversations: [event.conversation, ...context.conversations], version: context.version + 1 };
52
+ }),
53
+ },
54
+ UPDATE: {
55
+ actions: assign(({ context, event }) => {
56
+ const idx = context.conversations.findIndex(c => c.id === event.conversation.id);
57
+ if (idx < 0) return {};
58
+ const updated = [...context.conversations];
59
+ updated[idx] = Object.assign({}, updated[idx], event.conversation);
60
+ return { conversations: updated, version: context.version + 1 };
61
+ }),
62
+ },
63
+ DELETE: {
64
+ actions: assign(({ context, event }) => ({
65
+ conversations: context.conversations.filter(c => c.id !== event.id),
66
+ activeId: context.activeId === event.id ? null : context.activeId,
67
+ streamingIds: context.streamingIds.filter(id => id !== event.id),
68
+ version: context.version + 1,
69
+ })),
70
+ },
71
+ CLEAR_ALL: {
72
+ actions: assign({ conversations: [], activeId: null, streamingIds: [], version: 0 }),
73
+ },
74
+ SET_STREAMING: {
75
+ actions: assign(({ context, event }) => ({
76
+ streamingIds: context.streamingIds.includes(event.id)
77
+ ? context.streamingIds
78
+ : [...context.streamingIds, event.id],
79
+ })),
80
+ },
81
+ CLEAR_STREAMING: {
82
+ actions: assign(({ context, event }) => ({
83
+ streamingIds: context.streamingIds.filter(id => id !== event.id),
84
+ })),
85
+ },
86
+ SELECT: {
87
+ actions: assign(({ event }) => ({ activeId: event.id })),
88
+ },
89
+ },
90
+ },
91
+ error: {
92
+ on: {
93
+ LOAD_START: 'loading',
94
+ },
95
+ },
96
+ },
97
+ });
98
+
99
+ const actor = createActor(convListMachine);
100
+ actor.start();
101
+
102
+ function sendEvent(event) {
103
+ actor.send(event);
104
+ return actor.getSnapshot();
105
+ }
106
+
107
+ function getState() {
108
+ return actor.getSnapshot().value;
109
+ }
110
+
111
+ function getContext() {
112
+ return actor.getSnapshot().context;
113
+ }
114
+
115
+ function getConversations() {
116
+ return actor.getSnapshot().context.conversations;
117
+ }
118
+
119
+ function getStreamingIds() {
120
+ return actor.getSnapshot().context.streamingIds;
121
+ }
122
+
123
+ function isStreaming(id) {
124
+ return actor.getSnapshot().context.streamingIds.includes(id);
125
+ }
126
+
127
+ function getActiveId() {
128
+ return actor.getSnapshot().context.activeId;
129
+ }
130
+
131
+ function subscribe(fn) {
132
+ return actor.subscribe(fn);
133
+ }
134
+
135
+ window.__convListMachine = actor;
136
+ window.convListMachineAPI = { send: sendEvent, getState, getContext, getConversations, getStreamingIds, isStreaming, getActiveId, subscribe };
137
+ })();
@@ -15,19 +15,12 @@ function pathBasename(p) {
15
15
 
16
16
  class ConversationManager {
17
17
  constructor() {
18
- this.conversations = [];
19
- this.activeId = null;
20
18
  this.listEl = document.querySelector('[data-conversation-list]');
21
19
  this.emptyEl = document.querySelector('[data-conversation-empty]');
22
20
  this.newBtn = document.querySelector('[data-new-conversation]');
23
21
  this.sidebarEl = document.querySelector('[data-sidebar]');
24
- this.streamingConversations = new Set();
25
22
  this.agents = new Map();
26
23
 
27
- this._conversationVersion = 0;
28
- this._lastMutationSource = null;
29
- this._lastMutationTime = 0;
30
-
31
24
  this.folderBrowser = {
32
25
  modal: null,
33
26
  listEl: null,
@@ -43,6 +36,23 @@ class ConversationManager {
43
36
  this.init();
44
37
  }
45
38
 
39
+ get conversations() {
40
+ return window.convListMachineAPI ? window.convListMachineAPI.getConversations() : [];
41
+ }
42
+
43
+ get activeId() {
44
+ return window.convListMachineAPI ? window.convListMachineAPI.getActiveId() : null;
45
+ }
46
+
47
+ set activeId(id) {
48
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SELECT', id });
49
+ }
50
+
51
+ get streamingConversations() {
52
+ const ids = window.convListMachineAPI ? window.convListMachineAPI.getStreamingIds() : [];
53
+ return { has: (id) => ids.includes(id), add: (id) => window.convListMachineAPI?.send({ type: 'SET_STREAMING', id }), delete: (id) => window.convListMachineAPI?.send({ type: 'CLEAR_STREAMING', id }), clear: () => ids.forEach(id => window.convListMachineAPI?.send({ type: 'CLEAR_STREAMING', id })) };
54
+ }
55
+
46
56
  async init() {
47
57
  this.newBtn?.addEventListener('click', () => this.openFolderBrowser());
48
58
  this.setupDelegatedListeners();
@@ -80,27 +90,18 @@ class ConversationManager {
80
90
  }
81
91
  }
82
92
 
83
- _updateConversations(newArray, source, context = {}) {
93
+ _updateConversations(newArray, source) {
94
+ if (!window.convListMachineAPI) return { version: 0, timestamp: Date.now(), oldLen: 0, newLen: 0 };
84
95
  const oldLen = this.conversations.length;
85
96
  const newLen = Array.isArray(newArray) ? newArray.length : 0;
86
- const mutationId = ++this._conversationVersion;
87
- const timestamp = Date.now();
88
-
89
- this.conversations = Array.isArray(newArray) ? newArray : [];
90
- this._lastMutationSource = source;
91
- this._lastMutationTime = timestamp;
92
-
93
- window._conversationCacheVersion = mutationId;
94
-
95
- if (context.verbose) {
96
- console.log(`[ConvMgr] mutation #${mutationId} (${source}): ${oldLen} → ${newLen} items, ts=${timestamp}`);
97
- }
98
-
99
- return { version: mutationId, timestamp, oldLen, newLen };
97
+ window.convListMachineAPI.send({ type: 'LOAD_DONE', conversations: Array.isArray(newArray) ? newArray : [] });
98
+ const version = window.convListMachineAPI.getContext().version;
99
+ window._conversationCacheVersion = version;
100
+ return { version, timestamp: Date.now(), oldLen, newLen };
100
101
  }
101
102
 
102
103
  getConversationCacheVersion() {
103
- return this._conversationVersion;
104
+ return window.convListMachineAPI ? window.convListMachineAPI.getContext().version : 0;
104
105
  }
105
106
 
106
107
  getAgentDisplayName(agentId) {
@@ -315,9 +316,8 @@ class ConversationManager {
315
316
  this.deleteAllBtn.disabled = true;
316
317
  await window.wsClient.rpc('conv.del.all', {});
317
318
  console.log('[ConversationManager] Deleted all conversations');
318
- this._updateConversations([], 'clear_all');
319
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_ALL' });
319
320
  window.ConversationState?.clear('delete_all');
320
- this.activeId = null;
321
321
  window.dispatchEvent(new CustomEvent('conversation-deselected'));
322
322
  this.render();
323
323
  } catch (err) {
@@ -424,6 +424,7 @@ class ConversationManager {
424
424
  }
425
425
 
426
426
  async loadConversations() {
427
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'LOAD_START' });
427
428
  try {
428
429
  const base = window.__BASE_URL || '/gm';
429
430
  const res = await fetch(base + '/api/conversations');
@@ -431,44 +432,25 @@ class ConversationManager {
431
432
  const data = await res.json();
432
433
  const convList = data.conversations || [];
433
434
 
434
- // Never clear conversations on poll if the list is empty — preserve existing state
435
- // Empty list likely indicates a server error, not actually empty conversations
436
- if (convList.length > 0) {
437
- // If poll returns fewer conversations than cached, merge to avoid dropping items
438
- // due to transient server errors or partial responses
439
- if (convList.length < this.conversations.length) {
440
- const polledIds = new Set(convList.map(c => c.id));
441
- const kept = this.conversations.filter(c => !polledIds.has(c.id));
442
- // Update polled items in place, append any cached items not in poll result
443
- const merged = convList.map(pc => {
444
- const cached = this.conversations.find(c => c.id === pc.id);
445
- return cached ? Object.assign({}, cached, pc) : pc;
446
- }).concat(kept);
447
- this._updateConversations(merged, 'poll_merge');
448
- } else {
449
- this._updateConversations(convList, 'poll');
450
- }
451
- } else if (this.conversations.length === 0) {
452
- // First load and empty - show empty state, but don't clear on subsequent polls
453
- this._updateConversations(convList, 'poll');
435
+ if (window.convListMachineAPI) {
436
+ window.convListMachineAPI.send({ type: 'LOAD_DONE', conversations: convList });
454
437
  }
455
- // If convList is empty but this.conversations has items, do nothing - keep existing
456
438
 
457
439
  const clientStreamingMap = window.agentGuiClient?.state?.streamingConversations;
458
440
  for (const conv of this.conversations) {
459
441
  const serverStreaming = conv.isStreaming === 1 || conv.isStreaming === true;
460
442
  const clientStreaming = clientStreamingMap ? clientStreamingMap.has(conv.id) : false;
461
443
  if (serverStreaming || clientStreaming) {
462
- this.streamingConversations.add(conv.id);
444
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SET_STREAMING', id: conv.id });
463
445
  } else {
464
- this.streamingConversations.delete(conv.id);
446
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_STREAMING', id: conv.id });
465
447
  }
466
448
  }
467
449
 
468
450
  this.render();
469
451
  } catch (err) {
470
452
  console.error('Failed to load conversations:', err);
471
- // Don't show error state if we already have conversations cached - server may be transient issue
453
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'LOAD_ERROR' });
472
454
  if (this.conversations.length === 0) {
473
455
  this.showEmpty('Failed to load conversations');
474
456
  }
@@ -595,7 +577,7 @@ class ConversationManager {
595
577
  console.error('[ConvMgr] activeId mutation rejected:', result.reason);
596
578
  return;
597
579
  }
598
- this.activeId = convId;
580
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SELECT', id: convId });
599
581
 
600
582
  document.querySelectorAll('.conversation-item').forEach(item => {
601
583
  item.classList.remove('active');
@@ -622,35 +604,20 @@ class ConversationManager {
622
604
  }
623
605
 
624
606
  addConversation(conv) {
625
- if (this.conversations.some(c => c.id === conv.id)) {
626
- return;
627
- }
628
- const newConvs = [conv, ...this.conversations];
629
- this._updateConversations(newConvs, 'add', { convId: conv.id });
607
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'ADD', conversation: conv });
630
608
  this.render();
631
609
  }
632
610
 
633
611
  updateConversation(convId, updates) {
634
- const idx = this.conversations.findIndex(c => c.id === convId);
635
- if (idx >= 0) {
636
- const updated = Object.assign({}, this.conversations[idx], updates);
637
- const newConvs = [
638
- ...this.conversations.slice(0, idx),
639
- updated,
640
- ...this.conversations.slice(idx + 1)
641
- ];
642
- this._updateConversations(newConvs, 'update', { convId });
643
- this.render();
644
- }
612
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'UPDATE', conversation: Object.assign({ id: convId }, updates) });
613
+ this.render();
645
614
  }
646
615
 
647
616
  deleteConversation(convId) {
648
617
  const wasActive = this.activeId === convId;
649
- const newConvs = this.conversations.filter(c => c.id !== convId);
650
- this._updateConversations(newConvs, 'delete', { convId });
618
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'DELETE', id: convId });
651
619
  if (wasActive) {
652
620
  window.ConversationState?.deleteConversation(convId, 1);
653
- this.activeId = null;
654
621
  window.dispatchEvent(new CustomEvent('conversation-deselected'));
655
622
  }
656
623
  this.render();
@@ -667,16 +634,14 @@ class ConversationManager {
667
634
  } else if (msg.type === 'conversation_deleted') {
668
635
  this.deleteConversation(msg.conversationId);
669
636
  } else if (msg.type === 'all_conversations_deleted') {
670
- this._updateConversations([], 'ws_clear_all');
637
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_ALL' });
671
638
  window.ConversationState?.clear('all_deleted');
672
- this.activeId = null;
673
- this.streamingConversations.clear();
674
639
  this.showEmpty('No conversations yet');
675
640
  } else if (msg.type === 'streaming_start' && msg.conversationId) {
676
- this.streamingConversations.add(msg.conversationId);
641
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'SET_STREAMING', id: msg.conversationId });
677
642
  this.render();
678
643
  } else if ((msg.type === 'streaming_complete' || msg.type === 'streaming_error') && msg.conversationId) {
679
- this.streamingConversations.delete(msg.conversationId);
644
+ if (window.convListMachineAPI) window.convListMachineAPI.send({ type: 'CLEAR_STREAMING', id: msg.conversationId });
680
645
  this.render();
681
646
  }
682
647
  });
@@ -0,0 +1,108 @@
1
+ (function() {
2
+ const { createMachine, createActor, assign } = XState;
3
+
4
+ const promptMachine = createMachine({
5
+ id: 'prompt-area',
6
+ initial: 'ready',
7
+ context: {
8
+ conversationId: null,
9
+ queueLength: 0,
10
+ },
11
+ states: {
12
+ ready: {
13
+ on: {
14
+ LOADING: 'loading',
15
+ STREAMING: {
16
+ target: 'streaming',
17
+ actions: assign(({ event }) => ({ conversationId: event.conversationId || null })),
18
+ },
19
+ DISABLED: 'disabled',
20
+ },
21
+ },
22
+ loading: {
23
+ on: {
24
+ READY: 'ready',
25
+ STREAMING: {
26
+ target: 'streaming',
27
+ actions: assign(({ event }) => ({ conversationId: event.conversationId || null })),
28
+ },
29
+ DISABLED: 'disabled',
30
+ },
31
+ },
32
+ streaming: {
33
+ on: {
34
+ QUEUED: {
35
+ target: 'queued',
36
+ actions: assign(({ event }) => ({ queueLength: event.queueLength || 1 })),
37
+ },
38
+ READY: {
39
+ target: 'ready',
40
+ actions: assign({ conversationId: null, queueLength: 0 }),
41
+ },
42
+ STREAMING: {
43
+ actions: assign(({ event }) => ({ conversationId: event.conversationId || null })),
44
+ },
45
+ QUEUE_UPDATE: {
46
+ actions: assign(({ event }) => ({ queueLength: event.queueLength || 0 })),
47
+ },
48
+ },
49
+ },
50
+ queued: {
51
+ on: {
52
+ STREAMING: {
53
+ target: 'streaming',
54
+ actions: assign(({ event }) => ({ conversationId: event.conversationId || null, queueLength: 0 })),
55
+ },
56
+ READY: {
57
+ target: 'ready',
58
+ actions: assign({ conversationId: null, queueLength: 0 }),
59
+ },
60
+ QUEUE_UPDATE: {
61
+ actions: assign(({ event }) => ({ queueLength: event.queueLength || 0 })),
62
+ guard: ({ event }) => (event.queueLength || 0) > 0,
63
+ },
64
+ QUEUE_EMPTY: {
65
+ target: 'ready',
66
+ actions: assign({ conversationId: null, queueLength: 0 }),
67
+ },
68
+ },
69
+ },
70
+ disabled: {
71
+ on: {
72
+ READY: 'ready',
73
+ },
74
+ },
75
+ },
76
+ });
77
+
78
+ const actor = createActor(promptMachine);
79
+ actor.start();
80
+
81
+ function sendEvent(event) {
82
+ actor.send(event);
83
+ return actor.getSnapshot();
84
+ }
85
+
86
+ function getState() {
87
+ return actor.getSnapshot().value;
88
+ }
89
+
90
+ function isReady() {
91
+ return getState() === 'ready';
92
+ }
93
+
94
+ function isStreaming() {
95
+ return getState() === 'streaming';
96
+ }
97
+
98
+ function isDisabled() {
99
+ return getState() === 'disabled';
100
+ }
101
+
102
+ function subscribe(fn) {
103
+ return actor.subscribe(fn);
104
+ }
105
+
106
+ window.__promptMachine = actor;
107
+ window.promptMachineAPI = { send: sendEvent, getState, isReady, isStreaming, isDisabled, subscribe };
108
+ })();
@@ -0,0 +1,155 @@
1
+ (function() {
2
+ const { createMachine, createActor, assign } = XState;
3
+
4
+ const toolInstallMachine = createMachine({
5
+ id: 'tool-install-ui',
6
+ initial: 'idle',
7
+ context: {
8
+ version: null,
9
+ error: null,
10
+ progress: 0,
11
+ installedVersion: null,
12
+ publishedVersion: null,
13
+ },
14
+ states: {
15
+ idle: {
16
+ entry: assign({ error: null, progress: 0 }),
17
+ on: {
18
+ INSTALL: 'installing',
19
+ UPDATE: 'updating',
20
+ SET_INSTALLED: {
21
+ target: 'installed',
22
+ actions: assign(({ event }) => ({
23
+ installedVersion: event.installedVersion || null,
24
+ publishedVersion: event.publishedVersion || null,
25
+ version: event.version || event.installedVersion || null,
26
+ })),
27
+ },
28
+ SET_NEEDS_UPDATE: {
29
+ target: 'needs_update',
30
+ actions: assign(({ event }) => ({
31
+ installedVersion: event.installedVersion || null,
32
+ publishedVersion: event.publishedVersion || null,
33
+ })),
34
+ },
35
+ SET_FAILED: {
36
+ target: 'failed',
37
+ actions: assign(({ event }) => ({ error: event.error || null })),
38
+ },
39
+ },
40
+ },
41
+ installing: {
42
+ entry: assign({ error: null, progress: 0 }),
43
+ on: {
44
+ PROGRESS: {
45
+ actions: assign(({ context, event }) => ({
46
+ progress: Math.min(event.progress || context.progress + 5, 90),
47
+ })),
48
+ },
49
+ COMPLETE: {
50
+ target: 'installed',
51
+ actions: assign(({ event }) => ({
52
+ version: event.version || null,
53
+ installedVersion: event.installedVersion || null,
54
+ publishedVersion: event.publishedVersion || null,
55
+ progress: 100,
56
+ error: null,
57
+ })),
58
+ },
59
+ FAILED: {
60
+ target: 'failed',
61
+ actions: assign(({ event }) => ({ error: event.error || null, progress: 0 })),
62
+ },
63
+ },
64
+ },
65
+ installed: {
66
+ on: {
67
+ UPDATE: 'updating',
68
+ SET_NEEDS_UPDATE: {
69
+ target: 'needs_update',
70
+ actions: assign(({ event }) => ({
71
+ installedVersion: event.installedVersion || null,
72
+ publishedVersion: event.publishedVersion || null,
73
+ })),
74
+ },
75
+ },
76
+ },
77
+ updating: {
78
+ entry: assign({ error: null, progress: 0 }),
79
+ on: {
80
+ PROGRESS: {
81
+ actions: assign(({ context, event }) => ({
82
+ progress: Math.min(event.progress || context.progress + 5, 90),
83
+ })),
84
+ },
85
+ COMPLETE: {
86
+ target: 'installed',
87
+ actions: assign(({ event }) => ({
88
+ version: event.version || null,
89
+ installedVersion: event.installedVersion || null,
90
+ publishedVersion: event.publishedVersion || null,
91
+ progress: 100,
92
+ error: null,
93
+ })),
94
+ },
95
+ FAILED: {
96
+ target: 'failed',
97
+ actions: assign(({ event }) => ({ error: event.error || null, progress: 0 })),
98
+ },
99
+ },
100
+ },
101
+ needs_update: {
102
+ on: {
103
+ UPDATE: 'updating',
104
+ SET_INSTALLED: {
105
+ target: 'installed',
106
+ actions: assign(({ event }) => ({
107
+ installedVersion: event.installedVersion || null,
108
+ publishedVersion: event.publishedVersion || null,
109
+ })),
110
+ },
111
+ },
112
+ },
113
+ failed: {
114
+ on: {
115
+ INSTALL: 'installing',
116
+ UPDATE: 'updating',
117
+ },
118
+ },
119
+ },
120
+ });
121
+
122
+ const toolInstallMachines = new Map();
123
+
124
+ function getOrCreate(toolId) {
125
+ if (toolInstallMachines.has(toolId)) return toolInstallMachines.get(toolId);
126
+ const actor = createActor(toolInstallMachine);
127
+ actor.start();
128
+ toolInstallMachines.set(toolId, actor);
129
+ return actor;
130
+ }
131
+
132
+ function sendEvent(toolId, event) {
133
+ const actor = getOrCreate(toolId);
134
+ actor.send(event);
135
+ return actor.getSnapshot();
136
+ }
137
+
138
+ function getState(toolId) {
139
+ const actor = toolInstallMachines.get(toolId);
140
+ return actor ? actor.getSnapshot().value : 'idle';
141
+ }
142
+
143
+ function isLocked(toolId) {
144
+ const s = getState(toolId);
145
+ return s === 'installing' || s === 'updating';
146
+ }
147
+
148
+ function remove(toolId) {
149
+ const actor = toolInstallMachines.get(toolId);
150
+ if (actor) { actor.stop(); toolInstallMachines.delete(toolId); }
151
+ }
152
+
153
+ window.__toolInstallMachines = toolInstallMachines;
154
+ window.toolInstallMachineAPI = { getOrCreate, send: sendEvent, getState, isLocked, remove };
155
+ })();