agentgui 1.0.595 → 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.595",
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;
@@ -2525,6 +2529,55 @@ class AgentGUIClient {
2525
2529
  this.conversationCache.delete(conversationId);
2526
2530
  }
2527
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
+
2528
2581
  async loadConversationMessages(conversationId) {
2529
2582
  try {
2530
2583
  if (this._previousConvAbort) {