@agent-link/server 0.1.90 → 0.1.91

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-link/server",
3
- "version": "0.1.90",
3
+ "version": "0.1.91",
4
4
  "description": "AgentLink relay server",
5
5
  "license": "MIT",
6
6
  "repository": {
package/web/app.js CHANGED
@@ -47,6 +47,7 @@ const App = {
47
47
  const inputText = ref('');
48
48
  const isProcessing = ref(false);
49
49
  const isCompacting = ref(false);
50
+ const queuedMessages = ref([]);
50
51
  const inputRef = ref(null);
51
52
 
52
53
  // Sidebar state
@@ -157,7 +158,7 @@ const App = {
157
158
  const { connect, wsSend, closeWs, submitPassword, setDequeueNext } = createConnection({
158
159
  status, agentName, hostname, workDir, sessionId, error,
159
160
  serverVersion, agentVersion,
160
- messages, isProcessing, isCompacting, visibleLimit,
161
+ messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
161
162
  historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
162
163
  folderPickerLoading, folderPickerEntries, folderPickerPath,
163
164
  authRequired, authPassword, authError, authAttempts, authLocked,
@@ -216,9 +217,7 @@ const App = {
216
217
  };
217
218
 
218
219
  if (isProcessing.value) {
219
- userMsg.status = 'queued';
220
- userMsg._queuedPayload = payload;
221
- messages.value.push(userMsg);
220
+ queuedMessages.value.push({ id: streaming.nextId(), content: userMsg.content, attachments: userMsg.attachments, payload });
222
221
  } else {
223
222
  userMsg.status = 'sent';
224
223
  messages.value.push(userMsg);
@@ -235,19 +234,22 @@ const App = {
235
234
  }
236
235
 
237
236
  function dequeueNext() {
238
- const idx = messages.value.findIndex(m => m.status === 'queued');
239
- if (idx === -1) return;
240
- const msg = messages.value[idx];
241
- msg.status = 'sent';
237
+ if (queuedMessages.value.length === 0) return;
238
+ const queued = queuedMessages.value.shift();
239
+ const userMsg = {
240
+ id: queued.id, role: 'user', status: 'sent',
241
+ content: queued.content, attachments: queued.attachments,
242
+ timestamp: new Date(),
243
+ };
244
+ messages.value.push(userMsg);
242
245
  isProcessing.value = true;
243
- wsSend(msg._queuedPayload);
244
- delete msg._queuedPayload;
246
+ wsSend(queued.payload);
245
247
  scrollToBottom(true);
246
248
  }
247
249
 
248
250
  function removeQueuedMessage(msgId) {
249
- const idx = messages.value.findIndex(m => m.id === msgId);
250
- if (idx !== -1) messages.value.splice(idx, 1);
251
+ const idx = queuedMessages.value.findIndex(m => m.id === msgId);
252
+ if (idx !== -1) queuedMessages.value.splice(idx, 1);
251
253
  }
252
254
 
253
255
  function handleKeydown(e) {
@@ -283,7 +285,7 @@ const App = {
283
285
  status, agentName, hostname, workDir, sessionId, error,
284
286
  serverVersion, agentVersion,
285
287
  messages, visibleMessages, hasMoreMessages, loadMoreMessages,
286
- inputText, isProcessing, isCompacting, canSend, hasInput, inputRef,
288
+ inputText, isProcessing, isCompacting, canSend, hasInput, inputRef, queuedMessages,
287
289
  sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
288
290
  getRenderedContent, copyMessage, toggleTool,
289
291
  isPrevAssistant: _isPrevAssistant,
@@ -484,8 +486,7 @@ const App = {
484
486
  <!-- User message -->
485
487
  <template v-if="msg.role === 'user'">
486
488
  <div class="message-role-label user-label">You</div>
487
- <div :class="['message-bubble', 'user-bubble', { queued: msg.status === 'queued' }]" :title="formatTimestamp(msg.timestamp)">
488
- <button v-if="msg.status === 'queued'" class="queue-remove-btn" @click="removeQueuedMessage(msg.id)" title="Remove from queue">&times;</button>
489
+ <div class="message-bubble user-bubble" :title="formatTimestamp(msg.timestamp)">
489
490
  <div class="message-content">{{ msg.content }}</div>
490
491
  <div v-if="msg.attachments && msg.attachments.length" class="message-attachments">
491
492
  <div v-for="(att, ai) in msg.attachments" :key="ai" class="message-attachment-chip">
@@ -496,7 +497,6 @@ const App = {
496
497
  <span>{{ att.name }}</span>
497
498
  </div>
498
499
  </div>
499
- <div v-if="msg.status === 'queued'" class="queue-badge">queued</div>
500
500
  </div>
501
501
  </template>
502
502
 
@@ -616,6 +616,17 @@ const App = {
616
616
  @change="handleFileSelect"
617
617
  accept="image/*,text/*,.pdf,.json,.md,.py,.js,.ts,.tsx,.jsx,.css,.html,.xml,.yaml,.yml,.toml,.sh,.sql,.csv"
618
618
  />
619
+ <div v-if="queuedMessages.length > 0" class="queue-bar">
620
+ <div v-for="(qm, qi) in queuedMessages" :key="qm.id" class="queue-item">
621
+ <span class="queue-item-num">{{ qi + 1 }}.</span>
622
+ <span class="queue-item-text">{{ qm.content }}</span>
623
+ <span v-if="qm.attachments && qm.attachments.length" class="queue-item-attach" :title="qm.attachments.map(a => a.name).join(', ')">
624
+ <svg viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></svg>
625
+ {{ qm.attachments.length }}
626
+ </span>
627
+ <button class="queue-item-remove" @click="removeQueuedMessage(qm.id)" title="Remove from queue">&times;</button>
628
+ </div>
629
+ </div>
619
630
  <div
620
631
  :class="['input-card', { 'drag-over': dragOver }]"
621
632
  @dragover="handleDragOver"
@@ -14,7 +14,7 @@ export function createConnection(deps) {
14
14
  const {
15
15
  status, agentName, hostname, workDir, sessionId, error,
16
16
  serverVersion, agentVersion,
17
- messages, isProcessing, isCompacting, visibleLimit,
17
+ messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
18
18
  historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
19
19
  folderPickerLoading, folderPickerEntries, folderPickerPath,
20
20
  authRequired, authPassword, authError, authAttempts, authLocked,
@@ -207,6 +207,7 @@ export function createConnection(deps) {
207
207
  error.value = 'Agent disconnected. Waiting for reconnect...';
208
208
  isProcessing.value = false;
209
209
  isCompacting.value = false;
210
+ queuedMessages.value = [];
210
211
  } else if (msg.type === 'agent_reconnected') {
211
212
  status.value = 'Connected';
212
213
  error.value = '';
@@ -393,6 +394,7 @@ export function createConnection(deps) {
393
394
  localStorage.setItem(`agentlink-workdir-${sessionId.value}`, msg.workDir);
394
395
  sidebar.addToWorkdirHistory(msg.workDir);
395
396
  messages.value = [];
397
+ queuedMessages.value = [];
396
398
  toolMsgMap.clear();
397
399
  visibleLimit.value = 50;
398
400
  streaming.setMessageIdCounter(0);
@@ -414,6 +416,7 @@ export function createConnection(deps) {
414
416
  const wasConnected = status.value === 'Connected' || status.value === 'Connecting...';
415
417
  isProcessing.value = false;
416
418
  isCompacting.value = false;
419
+ queuedMessages.value = [];
417
420
 
418
421
  // Don't auto-reconnect if auth-locked or still in auth prompt
419
422
  if (authLocked.value || authRequired.value) return;
package/web/style.css CHANGED
@@ -787,27 +787,56 @@ body {
787
787
  color: var(--text-primary);
788
788
  }
789
789
 
790
- /* ── Queued message (pending send) ── */
791
- .user-bubble.queued {
792
- background: transparent;
790
+ /* ── Queue bar (pending messages above input) ── */
791
+ .queue-bar {
792
+ max-width: 768px;
793
+ margin: 0 auto 6px;
794
+ display: flex;
795
+ flex-direction: column;
796
+ gap: 3px;
797
+ }
798
+
799
+ .queue-item {
800
+ display: flex;
801
+ align-items: center;
802
+ gap: 6px;
803
+ background: var(--bg-secondary);
793
804
  border: 1px dashed var(--border);
794
- opacity: 0.7;
805
+ border-radius: 8px;
806
+ padding: 4px 8px;
807
+ font-size: 0.8rem;
808
+ line-height: 1.3;
809
+ color: var(--text-secondary);
810
+ opacity: 0.85;
795
811
  }
796
812
 
797
- .queue-badge {
798
- font-size: 0.65rem;
813
+ .queue-item-num {
814
+ flex-shrink: 0;
799
815
  font-weight: 600;
800
- text-transform: uppercase;
801
- letter-spacing: 0.05em;
802
816
  color: var(--text-secondary);
803
- text-align: right;
804
- margin-top: 0.2rem;
817
+ font-size: 0.7rem;
818
+ opacity: 0.6;
805
819
  }
806
820
 
807
- .queue-remove-btn {
808
- position: absolute;
809
- top: 4px;
810
- right: 6px;
821
+ .queue-item-text {
822
+ flex: 1;
823
+ min-width: 0;
824
+ overflow: hidden;
825
+ text-overflow: ellipsis;
826
+ white-space: nowrap;
827
+ }
828
+
829
+ .queue-item-attach {
830
+ flex-shrink: 0;
831
+ display: flex;
832
+ align-items: center;
833
+ gap: 2px;
834
+ font-size: 0.7rem;
835
+ opacity: 0.6;
836
+ }
837
+
838
+ .queue-item-remove {
839
+ flex-shrink: 0;
811
840
  background: none;
812
841
  border: none;
813
842
  color: var(--text-secondary);
@@ -815,15 +844,12 @@ body {
815
844
  line-height: 1;
816
845
  cursor: pointer;
817
846
  padding: 0 2px;
818
- opacity: 0;
847
+ opacity: 0.5;
819
848
  transition: color 0.15s, opacity 0.15s;
820
849
  }
821
850
 
822
- .user-bubble.queued:hover .queue-remove-btn {
851
+ .queue-item-remove:hover {
823
852
  opacity: 1;
824
- }
825
-
826
- .queue-remove-btn:hover {
827
853
  color: var(--error);
828
854
  }
829
855