agentgui 1.0.594 → 1.0.596

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.
@@ -104,7 +104,6 @@ export function register(router, deps) {
104
104
  if (!conv) notFound('Conversation not found');
105
105
  if (!p.content) fail(400, 'Missing content');
106
106
  const entry = activeExecutions.get(p.id);
107
- if (entry && p.eager) fail(409, 'Cannot eagerly inject while execution is running - message queued');
108
107
  const message = queries.createMessage(p.id, 'user', '[INJECTED] ' + p.content);
109
108
  if (!entry) {
110
109
  const agentId = conv.agentId || 'claude-code';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.594",
3
+ "version": "1.0.596",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -79,6 +79,10 @@ class AgentGUIClient {
79
79
  this._inflightRequests = new Map();
80
80
  this._previousConvAbort = null;
81
81
 
82
+ // PHASE 2: Request Lifetime Tracking
83
+ this._loadInProgress = {}; // { [conversationId]: { requestId, abortController, timestamp, prevConversationId } }
84
+ this._currentRequestId = 0; // Auto-incrementing request counter
85
+
82
86
  this._scrollKalman = typeof KalmanFilter !== 'undefined' ? new KalmanFilter({ processNoise: 50, measurementNoise: 100 }) : null;
83
87
  this._scrollTarget = 0;
84
88
  this._scrollAnimating = false;
@@ -491,17 +495,21 @@ class AgentGUIClient {
491
495
  this.showError('Please enter a message to steer');
492
496
  return;
493
497
  }
494
- try {
495
- const data = await window.wsClient.rpc('conv.steer', { id: this.state.currentConversation.id, content: message });
496
- console.log('Steer response:', data);
497
- if (this.ui.messageInput) {
498
- this.ui.messageInput.value = '';
499
- this.ui.messageInput.style.height = 'auto';
500
- }
501
- } catch (err) {
502
- console.error('Failed to steer:', err);
503
- this.showError('Failed to steer: ' + err.message);
498
+
499
+ // Capture message and clear UI immediately (no await)
500
+ const steerMsg = message;
501
+ if (this.ui.messageInput) {
502
+ this.ui.messageInput.value = '';
503
+ this.ui.messageInput.style.height = 'auto';
504
504
  }
505
+
506
+ // Fire RPC in background, don't await
507
+ window.wsClient.rpc('conv.steer', { id: this.state.currentConversation.id, content: steerMsg })
508
+ .then(data => console.log('Steer response:', data))
509
+ .catch(err => {
510
+ console.error('Failed to steer:', err);
511
+ this.showError('Failed to steer: ' + err.message);
512
+ });
505
513
  } else {
506
514
  const instructions = await window.UIDialog.prompt('Enter instructions to inject into the running agent:', '', 'Inject Instructions');
507
515
  if (!instructions) return;
@@ -2521,6 +2529,55 @@ class AgentGUIClient {
2521
2529
  this.conversationCache.delete(conversationId);
2522
2530
  }
2523
2531
 
2532
+ /**
2533
+ * PHASE 2: Create a new load request with lifetime tracking
2534
+ * Assigns unique requestId, tracks in _loadInProgress, returns abort signal
2535
+ * Automatically cancels previous loads to this conversation
2536
+ */
2537
+ _makeLoadRequest(conversationId) {
2538
+ const requestId = ++this._currentRequestId;
2539
+ const abortController = new AbortController();
2540
+
2541
+ // Cancel previous request to this conversation
2542
+ if (this._loadInProgress[conversationId]) {
2543
+ const prevReq = this._loadInProgress[conversationId];
2544
+ try {
2545
+ prevReq.abortController.abort();
2546
+ } catch (e) {}
2547
+ }
2548
+
2549
+ this._loadInProgress[conversationId] = {
2550
+ requestId,
2551
+ abortController,
2552
+ timestamp: Date.now(),
2553
+ prevConversationId: this.state.currentConversation?.id
2554
+ };
2555
+
2556
+ return { requestId, abortController: abortController.signal };
2557
+ }
2558
+
2559
+ /**
2560
+ * PHASE 2: Verify request is still current before rendering
2561
+ * Returns true if requestId matches current load for this conversation
2562
+ * Returns false if newer request arrived, or request was cancelled
2563
+ */
2564
+ _verifyRequestId(conversationId, requestId) {
2565
+ const current = this._loadInProgress[conversationId];
2566
+ if (!current) return false;
2567
+ if (current.requestId !== requestId) return false;
2568
+ return true;
2569
+ }
2570
+
2571
+ /**
2572
+ * PHASE 2: Complete/cleanup a load request
2573
+ */
2574
+ _completeLoadRequest(conversationId, requestId) {
2575
+ const req = this._loadInProgress[conversationId];
2576
+ if (req && req.requestId === requestId) {
2577
+ delete this._loadInProgress[conversationId];
2578
+ }
2579
+ }
2580
+
2524
2581
  async loadConversationMessages(conversationId) {
2525
2582
  try {
2526
2583
  if (this._previousConvAbort) {