@agent-link/server 0.1.117 → 0.1.119

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.117",
3
+ "version": "0.1.119",
4
4
  "description": "AgentLink relay server",
5
5
  "license": "MIT",
6
6
  "repository": {
package/web/app.js CHANGED
@@ -50,6 +50,7 @@ const App = {
50
50
  const isCompacting = ref(false);
51
51
  const latency = ref(null);
52
52
  const queuedMessages = ref([]);
53
+ const usageStats = ref(null);
53
54
  const inputRef = ref(null);
54
55
 
55
56
  // Sidebar state
@@ -130,6 +131,7 @@ const App = {
130
131
  toolMsgMap: _getToolMsgMap(),
131
132
  messageIdCounter: streaming.getMessageIdCounter(),
132
133
  queuedMessages: queuedMessages.value,
134
+ usageStats: usageStats.value,
133
135
  };
134
136
  }
135
137
 
@@ -149,6 +151,7 @@ const App = {
149
151
  streaming.setMessageIdCounter(cached.messageIdCounter || 0);
150
152
  _restoreToolMsgMap(cached.toolMsgMap || new Map());
151
153
  queuedMessages.value = cached.queuedMessages || [];
154
+ usageStats.value = cached.usageStats || null;
152
155
  } else {
153
156
  // New blank conversation
154
157
  messages.value = [];
@@ -163,6 +166,7 @@ const App = {
163
166
  streaming.reset();
164
167
  _clearToolMsgMap();
165
168
  queuedMessages.value = [];
169
+ usageStats.value = null;
166
170
  }
167
171
 
168
172
  currentConversationId.value = newConvId;
@@ -247,8 +251,8 @@ const App = {
247
251
  const { connect, wsSend, closeWs, submitPassword, setDequeueNext, setFileBrowser, getToolMsgMap, restoreToolMsgMap, clearToolMsgMap } = createConnection({
248
252
  status, agentName, hostname, workDir, sessionId, error,
249
253
  serverVersion, agentVersion, latency,
250
- messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
251
- historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
254
+ messages, isProcessing, isCompacting, visibleLimit, queuedMessages, usageStats,
255
+ historySessions, currentClaudeSessionId, needsResume, loadingSessions, loadingHistory,
252
256
  folderPickerLoading, folderPickerEntries, folderPickerPath,
253
257
  authRequired, authPassword, authError, authAttempts, authLocked,
254
258
  streaming, sidebar, scrollToBottom,
@@ -412,6 +416,21 @@ const App = {
412
416
  document.title = name ? `${name} — AgentLink` : 'AgentLink';
413
417
  });
414
418
 
419
+ // ── Usage formatting ──
420
+ function formatTokens(n) {
421
+ if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
422
+ return String(n);
423
+ }
424
+ function formatUsage(u) {
425
+ if (!u) return '';
426
+ const pct = u.contextWindow ? Math.round(u.inputTokens / u.contextWindow * 100) : 0;
427
+ const ctx = formatTokens(u.inputTokens) + ' / ' + formatTokens(u.contextWindow) + ' (' + pct + '%)';
428
+ const cost = '$' + u.totalCost.toFixed(2);
429
+ const model = u.model.replace(/^claude-/, '').replace(/-\d{8}$/, '').replace(/-1m$/, '');
430
+ const dur = (u.durationMs / 1000).toFixed(1) + 's';
431
+ return 'Context ' + ctx + ' \u00b7 Cost ' + cost + ' \u00b7 ' + model + ' \u00b7 ' + dur;
432
+ }
433
+
415
434
  // ── Lifecycle ──
416
435
  onMounted(() => { connect(scheduleHighlight); });
417
436
  onUnmounted(() => { closeWs(); streaming.cleanup(); window.removeEventListener('resize', _resizeHandler); document.removeEventListener('click', _workdirMenuClickHandler); document.removeEventListener('keydown', _workdirMenuKeyHandler); });
@@ -420,11 +439,11 @@ const App = {
420
439
  status, agentName, hostname, workDir, sessionId, error,
421
440
  serverVersion, agentVersion, latency,
422
441
  messages, visibleMessages, hasMoreMessages, loadMoreMessages,
423
- inputText, isProcessing, isCompacting, canSend, hasInput, inputRef, queuedMessages,
442
+ inputText, isProcessing, isCompacting, canSend, hasInput, inputRef, queuedMessages, usageStats,
424
443
  sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
425
444
  getRenderedContent, copyMessage, toggleTool,
426
445
  isPrevAssistant: _isPrevAssistant,
427
- toggleContextSummary, formatTimestamp,
446
+ toggleContextSummary, formatTimestamp, formatUsage,
428
447
  getToolIcon, getToolSummary, isEditTool, getEditDiffHtml, getFormattedToolInput, autoResize,
429
448
  // AskUserQuestion
430
449
  selectQuestionOption,
@@ -907,6 +926,7 @@ const App = {
907
926
  <button class="queue-item-remove" @click="removeQueuedMessage(qm.id)" title="Remove from queue">&times;</button>
908
927
  </div>
909
928
  </div>
929
+ <div v-if="usageStats" class="usage-bar">{{ formatUsage(usageStats) }}</div>
910
930
  <div
911
931
  :class="['input-card', { 'drag-over': dragOver }]"
912
932
  @dragover="handleDragOver"
@@ -14,8 +14,8 @@ export function createConnection(deps) {
14
14
  const {
15
15
  status, agentName, hostname, workDir, sessionId, error,
16
16
  serverVersion, agentVersion, latency,
17
- messages, isProcessing, isCompacting, visibleLimit, queuedMessages,
18
- historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
17
+ messages, isProcessing, isCompacting, visibleLimit, queuedMessages, usageStats,
18
+ historySessions, currentClaudeSessionId, needsResume, loadingSessions, loadingHistory,
19
19
  folderPickerLoading, folderPickerEntries, folderPickerPath,
20
20
  authRequired, authPassword, authError, authAttempts, authLocked,
21
21
  streaming, sidebar,
@@ -202,9 +202,11 @@ export function createConnection(deps) {
202
202
  }
203
203
  cache.isProcessing = false;
204
204
  cache.isCompacting = false;
205
+ if (msg.usage) cache.usageStats = msg.usage;
205
206
  if (cache.toolMsgMap) cache.toolMsgMap.clear();
206
207
  processingConversations.value[convId] = false;
207
208
  if (msg.type === 'execution_cancelled') {
209
+ cache.needsResume = true;
208
210
  cache.messages.push({
209
211
  id: ++cache.messageIdCounter, role: 'system',
210
212
  content: 'Generation stopped.', timestamp: new Date(),
@@ -586,10 +588,12 @@ export function createConnection(deps) {
586
588
  isProcessing.value = false;
587
589
  isCompacting.value = false;
588
590
  toolMsgMap.clear();
591
+ if (msg.usage) usageStats.value = msg.usage;
589
592
  if (currentConversationId && currentConversationId.value) {
590
593
  processingConversations.value[currentConversationId.value] = false;
591
594
  }
592
595
  if (msg.type === 'execution_cancelled') {
596
+ needsResume.value = true;
593
597
  messages.value.push({
594
598
  id: streaming.nextId(), role: 'system',
595
599
  content: 'Generation stopped.', timestamp: new Date(),
package/web/style.css CHANGED
@@ -955,6 +955,16 @@ body {
955
955
  color: var(--error);
956
956
  }
957
957
 
958
+ .usage-bar {
959
+ max-width: 768px;
960
+ margin: 0 auto 6px;
961
+ padding: 4px 10px;
962
+ font-size: 0.75rem;
963
+ color: var(--text-secondary);
964
+ text-align: center;
965
+ opacity: 0.7;
966
+ }
967
+
958
968
  .assistant-bubble {
959
969
  background: transparent;
960
970
  padding: 0.2rem 0;