agent-relay-server 0.4.23 → 0.4.24
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 +1 -1
- package/public/dashboard.js +935 -12
- package/public/index.html +353 -32
- package/src/db.ts +98 -0
- package/src/routes.ts +231 -3
- package/src/security.ts +1 -0
- package/src/types.ts +38 -0
package/public/index.html
CHANGED
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
.status-dot.idle { background: var(--tblr-success); }
|
|
36
36
|
.status-dot.busy { background: var(--tblr-warning); }
|
|
37
37
|
.status-dot.offline { background: var(--tblr-secondary); opacity: 0.5; }
|
|
38
|
+
.status-dot.stale { background: var(--tblr-danger); box-shadow: 0 0 6px var(--tblr-danger); }
|
|
39
|
+
.status-dot.reconnecting { animation: pulse-dot 1s ease-in-out infinite; }
|
|
40
|
+
.status-dot.paired { box-shadow: 0 0 0 3px rgba(var(--tblr-success-rgb), 0.18); }
|
|
41
|
+
.status-dot.attention { box-shadow: 0 0 0 3px rgba(var(--tblr-danger-rgb), 0.18); }
|
|
38
42
|
|
|
39
43
|
@keyframes pulse-dot {
|
|
40
44
|
0%, 100% { opacity: 1; box-shadow: 0 0 6px var(--tblr-success); }
|
|
@@ -91,6 +95,18 @@
|
|
|
91
95
|
button.attention-card:hover { background: var(--tblr-bg-surface-secondary); }
|
|
92
96
|
.attention-empty { border-left-color: var(--tblr-border-color); }
|
|
93
97
|
.attention-badges .badge { font-weight: 500; }
|
|
98
|
+
.presence-badges .badge { font-weight: 600; }
|
|
99
|
+
.activity-item { cursor: pointer; }
|
|
100
|
+
.activity-item:hover { background: var(--tblr-bg-surface-secondary); }
|
|
101
|
+
.activity-icon {
|
|
102
|
+
width: 32px;
|
|
103
|
+
height: 32px;
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
border-radius: 6px;
|
|
108
|
+
flex: 0 0 32px;
|
|
109
|
+
}
|
|
94
110
|
.pair-badge .ti { font-size: 13px; vertical-align: -2px; }
|
|
95
111
|
.pair-card { border-left: 3px solid var(--tblr-border-color); }
|
|
96
112
|
.pair-card.active { border-left-color: var(--tblr-success); }
|
|
@@ -119,6 +135,25 @@
|
|
|
119
135
|
gap: 12px;
|
|
120
136
|
align-items: start;
|
|
121
137
|
}
|
|
138
|
+
.command-palette {
|
|
139
|
+
position: fixed;
|
|
140
|
+
inset: 0;
|
|
141
|
+
z-index: 1060;
|
|
142
|
+
background: rgba(0, 0, 0, 0.55);
|
|
143
|
+
display: grid;
|
|
144
|
+
place-items: start center;
|
|
145
|
+
padding: 10vh 16px 16px;
|
|
146
|
+
}
|
|
147
|
+
.command-palette-panel {
|
|
148
|
+
width: min(720px, 100%);
|
|
149
|
+
background: var(--tblr-bg-surface);
|
|
150
|
+
border: 1px solid var(--tblr-border-color);
|
|
151
|
+
border-radius: 8px;
|
|
152
|
+
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
|
|
153
|
+
overflow: hidden;
|
|
154
|
+
}
|
|
155
|
+
.command-palette-item { cursor: pointer; }
|
|
156
|
+
.command-palette-item:hover { background: var(--tblr-bg-surface-secondary); }
|
|
122
157
|
|
|
123
158
|
.fade-in { animation: fadeIn 0.2s ease-in; }
|
|
124
159
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -140,7 +175,7 @@
|
|
|
140
175
|
</style>
|
|
141
176
|
</head>
|
|
142
177
|
<body>
|
|
143
|
-
<div x-data="relay" x-init="init()" class="d-flex">
|
|
178
|
+
<div x-data="relay" x-init="init()" class="d-flex" @keydown.window.escape="commandPaletteOpen && closeCommandPalette()">
|
|
144
179
|
|
|
145
180
|
<!-- Sidebar -->
|
|
146
181
|
<aside class="ar-sidebar d-none d-md-flex">
|
|
@@ -159,6 +194,10 @@
|
|
|
159
194
|
<i class="ti ti-inbox"></i>Inbox
|
|
160
195
|
<span class="badge bg-danger text-white ms-auto" x-show="attentionSummary.unreadInbox > 0" x-text="attentionSummary.unreadInbox"></span>
|
|
161
196
|
</a>
|
|
197
|
+
<a href="#" class="nav-link" :class="{ active: view === 'activity' }" @click.prevent="switchView('activity')">
|
|
198
|
+
<i class="ti ti-activity"></i>Activity
|
|
199
|
+
<span class="badge bg-secondary text-white ms-auto" x-show="activityItems.length > 0" x-text="activityItems.length"></span>
|
|
200
|
+
</a>
|
|
162
201
|
<a href="#" class="nav-link" :class="{ active: view === 'pairs' }" @click.prevent="switchView('pairs')">
|
|
163
202
|
<i class="ti ti-link"></i>Pairs
|
|
164
203
|
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.pendingPairInvites > 0" x-text="attentionSummary.pendingPairInvites"></span>
|
|
@@ -167,6 +206,10 @@
|
|
|
167
206
|
<a href="#" class="nav-link" :class="{ active: view === 'messages' }" @click.prevent="switchView('messages')">
|
|
168
207
|
<i class="ti ti-messages"></i>Messages
|
|
169
208
|
</a>
|
|
209
|
+
<a href="#" class="nav-link" :class="{ active: view === 'work' }" @click.prevent="switchView('work')">
|
|
210
|
+
<i class="ti ti-list-check"></i>Work Queue
|
|
211
|
+
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.claimableTasks > 0" x-text="attentionSummary.claimableTasks"></span>
|
|
212
|
+
</a>
|
|
170
213
|
<a href="#" class="nav-link" :class="{ active: view === 'tasks' }" @click.prevent="switchView('tasks')">
|
|
171
214
|
<i class="ti ti-checkup-list"></i>Tasks
|
|
172
215
|
<span class="badge bg-warning text-white ms-auto" x-show="attentionSummary.claimableTasks > 0" x-text="attentionSummary.claimableTasks"></span>
|
|
@@ -181,12 +224,21 @@
|
|
|
181
224
|
<span class="status-dot" :class="connected ? 'online' : 'offline'"></span>
|
|
182
225
|
<span class="small" x-text="authNeeded ? 'Auth required' : connected ? 'Live' : 'Reconnecting…'"></span>
|
|
183
226
|
</div>
|
|
227
|
+
<button class="btn btn-sm btn-ghost-secondary w-100 mb-2 justify-content-start" @click="openCommandPalette()">
|
|
228
|
+
<i class="ti ti-command me-1"></i>Command palette
|
|
229
|
+
</button>
|
|
184
230
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
185
231
|
<label class="form-check form-switch mb-0">
|
|
186
232
|
<input type="checkbox" class="form-check-input" x-model="showOffline">
|
|
187
233
|
<span class="form-check-label small">Show offline</span>
|
|
188
234
|
</label>
|
|
189
235
|
</div>
|
|
236
|
+
<div class="d-flex align-items-center gap-2 mb-2">
|
|
237
|
+
<label class="form-check form-switch mb-0">
|
|
238
|
+
<input type="checkbox" class="form-check-input" x-model="showBuiltIns">
|
|
239
|
+
<span class="form-check-label small">System agents</span>
|
|
240
|
+
</label>
|
|
241
|
+
</div>
|
|
190
242
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
191
243
|
<label class="form-check form-switch mb-0">
|
|
192
244
|
<input type="checkbox" class="form-check-input" x-model="autoRefresh">
|
|
@@ -199,7 +251,7 @@
|
|
|
199
251
|
|
|
200
252
|
<!-- Mobile nav -->
|
|
201
253
|
<div class="mobile-nav d-none border-bottom p-2 gap-1 position-fixed top-0 w-100 bg-dark" style="z-index:50">
|
|
202
|
-
<template x-for="v in ['overview','agents','inbox','pairs','messages','tasks','analytics']">
|
|
254
|
+
<template x-for="v in ['overview','agents','inbox','activity','pairs','messages','work','tasks','analytics']">
|
|
203
255
|
<button class="btn btn-sm" :class="view === v ? 'btn-primary' : 'btn-ghost-secondary'" @click="switchView(v)" x-text="v.charAt(0).toUpperCase() + v.slice(1)"></button>
|
|
204
256
|
</template>
|
|
205
257
|
</div>
|
|
@@ -293,9 +345,24 @@
|
|
|
293
345
|
<div class="small">All checks passing</div>
|
|
294
346
|
</template>
|
|
295
347
|
<template x-if="healthIssues.length > 0">
|
|
296
|
-
<div class="
|
|
297
|
-
<template x-for="
|
|
298
|
-
<
|
|
348
|
+
<div class="mt-2">
|
|
349
|
+
<template x-for="diagnostic in healthDiagnostics" :key="diagnostic.name">
|
|
350
|
+
<div class="border rounded p-2 mb-2">
|
|
351
|
+
<div class="d-flex align-items-start gap-2">
|
|
352
|
+
<span class="badge" :class="diagnostic.status === 'error' ? 'bg-danger-lt' : 'bg-warning-lt'" x-text="diagnostic.name"></span>
|
|
353
|
+
<div class="flex-grow-1 min-width-0">
|
|
354
|
+
<div class="small fw-bold" x-text="diagnostic.detail"></div>
|
|
355
|
+
<div class="small text-secondary" x-text="diagnostic.impact"></div>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="d-flex gap-1 mt-2 flex-wrap">
|
|
359
|
+
<template x-for="action in diagnostic.actions" :key="diagnostic.name + action.label">
|
|
360
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="runHealthAction(action)">
|
|
361
|
+
<i class="ti me-1" :class="action.icon"></i><span x-text="action.label"></span>
|
|
362
|
+
</button>
|
|
363
|
+
</template>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
299
366
|
</template>
|
|
300
367
|
</div>
|
|
301
368
|
</template>
|
|
@@ -337,7 +404,7 @@
|
|
|
337
404
|
</button>
|
|
338
405
|
</div>
|
|
339
406
|
<div class="col-md-6 col-xl">
|
|
340
|
-
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.claimableTasks === 0 }" @click="switchView('
|
|
407
|
+
<button class="card attention-card w-100 text-start" :class="{ 'attention-empty': attentionSummary.claimableTasks === 0 }" @click="switchView('work')">
|
|
341
408
|
<div class="card-body py-3">
|
|
342
409
|
<div class="text-secondary small">Claimable waiting</div>
|
|
343
410
|
<div class="h2 mb-0" :class="attentionSummary.claimableTasks ? 'text-warning' : ''" x-text="attentionSummary.claimableTasks"></div>
|
|
@@ -357,7 +424,7 @@
|
|
|
357
424
|
<div class="list-group list-group-flush" style="max-height: 60vh; overflow-y: auto">
|
|
358
425
|
<template x-for="a in sortedAgents.slice(0, 20)" :key="a.id">
|
|
359
426
|
<div class="list-group-item d-flex align-items-center gap-2" style="cursor:pointer" @click="openAgentDetail(a)">
|
|
360
|
-
<span class="status-dot" :class="
|
|
427
|
+
<span class="status-dot" :class="agentStatusClass(a)"></span>
|
|
361
428
|
<span class="agent-type-icon" :class="agentType(a)" :title="agentTypeTitle(a)" :aria-label="agentTypeTitle(a)">
|
|
362
429
|
<i class="ti" :class="agentTypeIcon(a)"></i>
|
|
363
430
|
</span>
|
|
@@ -371,6 +438,11 @@
|
|
|
371
438
|
</template>
|
|
372
439
|
</div>
|
|
373
440
|
<div class="text-secondary small text-truncate" x-text="a.id"></div>
|
|
441
|
+
<div class="presence-badges d-flex gap-1 mt-1 flex-wrap">
|
|
442
|
+
<template x-for="badge in agentPresenceBadges(a)" :key="badge.label">
|
|
443
|
+
<span class="badge" :class="badge.className" x-text="badge.label"></span>
|
|
444
|
+
</template>
|
|
445
|
+
</div>
|
|
374
446
|
<template x-if="agentPair(a)">
|
|
375
447
|
<span class="badge pair-badge mt-1" :class="pairBadgeClass(agentPair(a))" :title="pairTitle(agentPair(a), a.id)">
|
|
376
448
|
<i class="ti ti-link me-1"></i><span x-text="pairBadgeLabel(agentPair(a), a.id)"></span>
|
|
@@ -397,29 +469,32 @@
|
|
|
397
469
|
<div class="col-lg-7">
|
|
398
470
|
<div class="card">
|
|
399
471
|
<div class="card-header d-flex align-items-center">
|
|
400
|
-
<h3 class="card-title">Recent
|
|
401
|
-
<
|
|
472
|
+
<h3 class="card-title">Recent Activity</h3>
|
|
473
|
+
<button class="btn btn-sm btn-ghost-secondary ms-auto" @click="switchView('activity')">
|
|
474
|
+
<i class="ti ti-arrow-up-right"></i>
|
|
475
|
+
</button>
|
|
402
476
|
</div>
|
|
403
477
|
<div class="card-body p-0" style="max-height: 60vh; overflow-y: auto">
|
|
404
|
-
<template x-for="
|
|
405
|
-
<
|
|
406
|
-
<div class="d-flex align-items-
|
|
407
|
-
<span class="
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
<
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
<
|
|
417
|
-
|
|
478
|
+
<template x-for="item in activityItems.slice(0, 15)" :key="item.id">
|
|
479
|
+
<button type="button" class="list-group-item list-group-item-action activity-item text-start w-100 border-0 border-bottom rounded-0" @click="openActivityItem(item)">
|
|
480
|
+
<div class="d-flex align-items-start gap-3">
|
|
481
|
+
<span class="activity-icon" :class="activityKindClass(item.kind)">
|
|
482
|
+
<i class="ti" :class="item.icon"></i>
|
|
483
|
+
</span>
|
|
484
|
+
<div class="flex-grow-1 min-width-0">
|
|
485
|
+
<div class="d-flex align-items-center gap-2">
|
|
486
|
+
<span class="fw-bold small text-truncate" x-text="item.title"></span>
|
|
487
|
+
<span class="badge" :class="activityKindClass(item.kind)" x-text="item.kind"></span>
|
|
488
|
+
<span class="text-secondary small ms-auto" x-text="timeAgo(item.ts)"></span>
|
|
489
|
+
</div>
|
|
490
|
+
<div class="msg-body text-secondary text-truncate" x-show="item.body" x-text="item.body"></div>
|
|
491
|
+
<div class="text-secondary small text-truncate" x-show="item.meta" x-text="item.meta"></div>
|
|
492
|
+
</div>
|
|
418
493
|
</div>
|
|
419
|
-
</
|
|
494
|
+
</button>
|
|
420
495
|
</template>
|
|
421
|
-
<template x-if="
|
|
422
|
-
<div class="p-3 text-secondary text-center">No
|
|
496
|
+
<template x-if="activityItems.length === 0">
|
|
497
|
+
<div class="p-3 text-secondary text-center">No activity</div>
|
|
423
498
|
</template>
|
|
424
499
|
</div>
|
|
425
500
|
</div>
|
|
@@ -433,6 +508,18 @@
|
|
|
433
508
|
<div class="d-flex align-items-center mb-3 gap-3 flex-wrap">
|
|
434
509
|
<h2 class="page-title mb-0">Agents</h2>
|
|
435
510
|
<div class="ms-auto d-flex gap-2 align-items-center">
|
|
511
|
+
<select class="form-select form-select-sm" style="width:auto; min-width: 150px" x-model="agentPresetFilter">
|
|
512
|
+
<option value="">View: All</option>
|
|
513
|
+
<option value="active">View: Active agents</option>
|
|
514
|
+
<option value="offline_stale">View: Offline stale</option>
|
|
515
|
+
<option value="claude">View: Claude</option>
|
|
516
|
+
<option value="codex">View: Codex</option>
|
|
517
|
+
<option value="paired">View: Paired</option>
|
|
518
|
+
<option value="unpaired">View: Unpaired</option>
|
|
519
|
+
<option value="waiting">View: Waiting for me</option>
|
|
520
|
+
<option value="claimable">View: Claimable</option>
|
|
521
|
+
<option value="errors">View: Errors</option>
|
|
522
|
+
</select>
|
|
436
523
|
<select class="form-select form-select-sm" style="width:auto; min-width: 140px" x-model="agentStatusFilter">
|
|
437
524
|
<option value="">Status: All</option>
|
|
438
525
|
<option value="starting">Status: Starting</option>
|
|
@@ -453,6 +540,10 @@
|
|
|
453
540
|
<option value="lastSeen">Sort: Last seen</option>
|
|
454
541
|
<option value="created">Sort: Created</option>
|
|
455
542
|
</select>
|
|
543
|
+
<label class="form-check form-switch mb-0">
|
|
544
|
+
<input type="checkbox" class="form-check-input" x-model="showBuiltIns">
|
|
545
|
+
<span class="form-check-label small">System agents</span>
|
|
546
|
+
</label>
|
|
456
547
|
<button class="btn btn-sm btn-ghost-secondary" @click="agentSortDir = agentSortDir === 'asc' ? 'desc' : 'asc'">
|
|
457
548
|
<i class="ti" :class="agentSortDir === 'asc' ? 'ti-sort-ascending' : 'ti-sort-descending'"></i>
|
|
458
549
|
</button>
|
|
@@ -473,7 +564,7 @@
|
|
|
473
564
|
<div class="card agent-card" :class="{ selected: selectedAgent === a.id }" @click="openAgentDetail(a)">
|
|
474
565
|
<div class="card-body">
|
|
475
566
|
<div class="d-flex align-items-start gap-2">
|
|
476
|
-
<span class="status-dot mt-1" :class="
|
|
567
|
+
<span class="status-dot mt-1" :class="agentStatusClass(a)" :title="agentStatusTitle(a)"></span>
|
|
477
568
|
<span class="agent-type-icon mt-0" :class="agentType(a)" :title="agentTypeTitle(a)" :aria-label="agentTypeTitle(a)">
|
|
478
569
|
<i class="ti" :class="agentTypeIcon(a)"></i>
|
|
479
570
|
</span>
|
|
@@ -485,6 +576,14 @@
|
|
|
485
576
|
<span class="text-truncate" :class="a.label ? 'text-secondary small' : 'fw-bold'" x-text="a.name || a.id.slice(-12)"></span>
|
|
486
577
|
</div>
|
|
487
578
|
<div class="text-secondary small text-truncate mt-1" x-text="a.id"></div>
|
|
579
|
+
<div class="presence-badges d-flex gap-1 mt-1 flex-wrap">
|
|
580
|
+
<span class="badge" :class="'bg-' + agentPresence(a).tone + '-lt'">
|
|
581
|
+
<i class="ti me-1" :class="agentPresence(a).icon"></i><span x-text="agentPresence(a).label"></span>
|
|
582
|
+
</span>
|
|
583
|
+
<template x-for="badge in agentPresenceBadges(a)" :key="badge.label">
|
|
584
|
+
<span class="badge" :class="badge.className" x-text="badge.label"></span>
|
|
585
|
+
</template>
|
|
586
|
+
</div>
|
|
488
587
|
<div class="d-flex gap-1 mt-1 flex-wrap">
|
|
489
588
|
<template x-if="agentAttention(a).total > 0">
|
|
490
589
|
<span class="badge bg-warning text-white" :title="agentAttentionTitle(a)">
|
|
@@ -526,6 +625,9 @@
|
|
|
526
625
|
<button class="btn btn-sm btn-ghost-secondary p-1" title="Send message" @click.stop="openComposeToAgent(a)">
|
|
527
626
|
<i class="ti ti-send"></i>
|
|
528
627
|
</button>
|
|
628
|
+
<button class="btn btn-sm btn-ghost-secondary p-1" title="Pair with..." @click.stop="openPairInvite(a.id)">
|
|
629
|
+
<i class="ti ti-link-plus"></i>
|
|
630
|
+
</button>
|
|
529
631
|
<button class="btn btn-sm btn-ghost-secondary p-1" title="Rename" @click.stop="openRename(a)">
|
|
530
632
|
<i class="ti ti-pencil"></i>
|
|
531
633
|
</button>
|
|
@@ -551,7 +653,7 @@
|
|
|
551
653
|
<i class="ti ti-robot-off" style="font-size:48px; opacity:0.3"></i>
|
|
552
654
|
<p
|
|
553
655
|
class="mt-2"
|
|
554
|
-
x-text="(agentStatusFilter || agentTagFilter) ? 'No agents match the current filters' : (showOffline ? 'No agents registered' : 'No active agents — enable Show Offline')"
|
|
656
|
+
x-text="(agentPresetFilter || agentStatusFilter || agentTagFilter) ? 'No agents match the current filters' : (hiddenBuiltInAgentCount ? 'Only system agents hidden — enable System agents' : (showOffline ? 'No agents registered' : 'No active agents — enable Show Offline'))"
|
|
555
657
|
></p>
|
|
556
658
|
</div>
|
|
557
659
|
</div>
|
|
@@ -684,6 +786,12 @@
|
|
|
684
786
|
<button class="btn btn-sm btn-ghost-secondary" :title="selectedInboxThreadData.archived ? 'Unarchive' : 'Archive'" @click="selectedInboxThreadData.archived ? unarchiveInboxThread(selectedInboxThreadData) : archiveInboxThread(selectedInboxThreadData)">
|
|
685
787
|
<i class="ti" :class="selectedInboxThreadData.archived ? 'ti-archive-off' : 'ti-archive'"></i>
|
|
686
788
|
</button>
|
|
789
|
+
<button class="btn btn-sm btn-ghost-secondary" title="Export thread Markdown" @click="exportThread(selectedInboxThreadData, 'markdown')">
|
|
790
|
+
<i class="ti ti-file-export"></i>
|
|
791
|
+
</button>
|
|
792
|
+
<button class="btn btn-sm btn-ghost-secondary" title="Export thread JSON" @click="exportThread(selectedInboxThreadData, 'json')">
|
|
793
|
+
<i class="ti ti-braces"></i>
|
|
794
|
+
</button>
|
|
687
795
|
<button class="btn btn-sm btn-ghost-danger" title="Delete thread" @click="confirmDeleteInboxThread(selectedInboxThreadData)">
|
|
688
796
|
<i class="ti ti-trash"></i>
|
|
689
797
|
</button>
|
|
@@ -753,6 +861,64 @@
|
|
|
753
861
|
</div>
|
|
754
862
|
</div>
|
|
755
863
|
|
|
864
|
+
<!-- ==================== ACTIVITY ==================== -->
|
|
865
|
+
<div x-show="view === 'activity'" x-cloak class="fade-in">
|
|
866
|
+
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
867
|
+
<h2 class="page-title mb-0">Activity</h2>
|
|
868
|
+
<span class="badge bg-secondary-lt" x-text="activityItems.length + ' events'"></span>
|
|
869
|
+
<div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
|
|
870
|
+
<select class="form-select form-select-sm" style="width:auto; min-width: 150px" x-model="activityFilter">
|
|
871
|
+
<option value="">Type: All</option>
|
|
872
|
+
<option value="question">Questions</option>
|
|
873
|
+
<option value="reply">Replies</option>
|
|
874
|
+
<option value="message">Messages</option>
|
|
875
|
+
<option value="operator">Operator</option>
|
|
876
|
+
<option value="pair">Pairs</option>
|
|
877
|
+
<option value="task">Tasks</option>
|
|
878
|
+
<option value="state">State</option>
|
|
879
|
+
</select>
|
|
880
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="refreshLiveData()">
|
|
881
|
+
<i class="ti ti-refresh"></i>
|
|
882
|
+
</button>
|
|
883
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="exportActivity('markdown')" title="Export timeline Markdown">
|
|
884
|
+
<i class="ti ti-file-export"></i>
|
|
885
|
+
</button>
|
|
886
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="exportActivity('json')" title="Export timeline JSON">
|
|
887
|
+
<i class="ti ti-braces"></i>
|
|
888
|
+
</button>
|
|
889
|
+
</div>
|
|
890
|
+
</div>
|
|
891
|
+
|
|
892
|
+
<div class="card">
|
|
893
|
+
<div class="list-group list-group-flush">
|
|
894
|
+
<template x-for="item in activityItems" :key="item.id">
|
|
895
|
+
<button type="button" class="list-group-item list-group-item-action activity-item text-start" @click="openActivityItem(item)">
|
|
896
|
+
<div class="d-flex align-items-start gap-3">
|
|
897
|
+
<span class="activity-icon" :class="activityKindClass(item.kind)">
|
|
898
|
+
<i class="ti" :class="item.icon"></i>
|
|
899
|
+
</span>
|
|
900
|
+
<div class="flex-grow-1 min-width-0">
|
|
901
|
+
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
902
|
+
<span class="fw-bold text-truncate" x-text="item.title"></span>
|
|
903
|
+
<span class="badge" :class="activityKindClass(item.kind)" x-text="item.kind"></span>
|
|
904
|
+
<span class="text-secondary small ms-auto" x-text="timeAgo(item.ts)"></span>
|
|
905
|
+
</div>
|
|
906
|
+
<div class="msg-body mt-1" x-show="item.body" x-text="item.body"></div>
|
|
907
|
+
<div class="text-secondary small mt-1 text-truncate" x-show="item.meta" x-text="item.meta"></div>
|
|
908
|
+
</div>
|
|
909
|
+
</div>
|
|
910
|
+
</button>
|
|
911
|
+
</template>
|
|
912
|
+
<template x-if="activityItems.length === 0">
|
|
913
|
+
<div class="list-group-item text-secondary text-center py-5">
|
|
914
|
+
<i class="ti ti-activity" style="font-size:48px; opacity:0.3"></i>
|
|
915
|
+
<p class="mt-2 mb-0">No activity</p>
|
|
916
|
+
</div>
|
|
917
|
+
</template>
|
|
918
|
+
</div>
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
|
|
756
922
|
<!-- ==================== PAIRS ==================== -->
|
|
757
923
|
<div x-show="view === 'pairs'" x-cloak class="fade-in">
|
|
758
924
|
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
@@ -828,6 +994,12 @@
|
|
|
828
994
|
<i class="ti ti-phone-off me-1"></i>Hang up
|
|
829
995
|
</button>
|
|
830
996
|
</template>
|
|
997
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="exportPair(pair, 'markdown')" title="Export pair Markdown">
|
|
998
|
+
<i class="ti ti-file-export"></i>
|
|
999
|
+
</button>
|
|
1000
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="exportPair(pair, 'json')" title="Export pair JSON">
|
|
1001
|
+
<i class="ti ti-braces"></i>
|
|
1002
|
+
</button>
|
|
831
1003
|
<span class="text-secondary small ms-auto" x-text="'Created ' + fmtTime(pair.createdAt)"></span>
|
|
832
1004
|
</div>
|
|
833
1005
|
</div>
|
|
@@ -944,6 +1116,107 @@
|
|
|
944
1116
|
</div>
|
|
945
1117
|
</div>
|
|
946
1118
|
|
|
1119
|
+
<!-- ==================== WORK QUEUE ==================== -->
|
|
1120
|
+
<div x-show="view === 'work'" x-cloak class="fade-in">
|
|
1121
|
+
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
1122
|
+
<h2 class="page-title mb-0">Work Queue</h2>
|
|
1123
|
+
<span class="badge bg-warning-lt" x-show="attentionSummary.claimableTasks > 0" x-text="attentionSummary.claimableTasks + ' claimable waiting'"></span>
|
|
1124
|
+
<div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
|
|
1125
|
+
<select class="form-select form-select-sm" style="width: auto; min-width: 160px" x-model="selectedAgent">
|
|
1126
|
+
<option value="">Claim as...</option>
|
|
1127
|
+
<template x-for="a in composeAgents" :key="a.id">
|
|
1128
|
+
<option :value="a.id" x-text="displayName(a) + ' [' + a.id.slice(-6) + ']'"></option>
|
|
1129
|
+
</template>
|
|
1130
|
+
</select>
|
|
1131
|
+
<button class="btn btn-sm btn-ghost-secondary" @click="fetchMessages(); fetchTasks()">
|
|
1132
|
+
<i class="ti ti-refresh"></i>
|
|
1133
|
+
</button>
|
|
1134
|
+
</div>
|
|
1135
|
+
</div>
|
|
1136
|
+
|
|
1137
|
+
<div class="card">
|
|
1138
|
+
<div class="table-responsive">
|
|
1139
|
+
<table class="table table-vcenter card-table">
|
|
1140
|
+
<thead>
|
|
1141
|
+
<tr>
|
|
1142
|
+
<th>Work</th>
|
|
1143
|
+
<th>Severity</th>
|
|
1144
|
+
<th>Status</th>
|
|
1145
|
+
<th>Owner</th>
|
|
1146
|
+
<th>Age</th>
|
|
1147
|
+
<th class="w-1"></th>
|
|
1148
|
+
</tr>
|
|
1149
|
+
</thead>
|
|
1150
|
+
<tbody>
|
|
1151
|
+
<template x-for="item in workQueueItems" :key="item.id">
|
|
1152
|
+
<tr>
|
|
1153
|
+
<td class="min-width-0">
|
|
1154
|
+
<div class="d-flex align-items-center gap-2">
|
|
1155
|
+
<span class="badge bg-secondary-lt" x-text="item.sourceType"></span>
|
|
1156
|
+
<span class="fw-bold text-truncate" x-text="item.title"></span>
|
|
1157
|
+
</div>
|
|
1158
|
+
<div class="text-secondary small text-truncate mt-1" x-text="item.body"></div>
|
|
1159
|
+
<div class="d-flex gap-1 mt-1 flex-wrap">
|
|
1160
|
+
<span class="badge bg-secondary-lt" x-text="item.source"></span>
|
|
1161
|
+
<span class="badge bg-primary-lt" x-text="displayTarget(item.target)"></span>
|
|
1162
|
+
<span class="badge bg-warning-lt" x-show="item.channel" x-text="'#' + item.channel"></span>
|
|
1163
|
+
</div>
|
|
1164
|
+
</td>
|
|
1165
|
+
<td><span class="badge" :class="severityClass(item.severity)" x-text="item.severity"></span></td>
|
|
1166
|
+
<td>
|
|
1167
|
+
<template x-if="item.sourceType === 'task'">
|
|
1168
|
+
<select class="form-select form-select-sm" style="min-width: 140px" :value="item.status" @change="doUpdateTaskStatus(item.task, $event.target.value)">
|
|
1169
|
+
<option value="open">Open</option>
|
|
1170
|
+
<option value="claimed">Claimed</option>
|
|
1171
|
+
<option value="in_progress">In Progress</option>
|
|
1172
|
+
<option value="blocked">Blocked</option>
|
|
1173
|
+
<option value="done">Done</option>
|
|
1174
|
+
<option value="failed">Failed</option>
|
|
1175
|
+
<option value="canceled">Canceled</option>
|
|
1176
|
+
</select>
|
|
1177
|
+
</template>
|
|
1178
|
+
<template x-if="item.sourceType === 'message'">
|
|
1179
|
+
<span class="badge bg-secondary-lt" x-text="item.status"></span>
|
|
1180
|
+
</template>
|
|
1181
|
+
</td>
|
|
1182
|
+
<td>
|
|
1183
|
+
<span x-show="item.owner" x-text="displayTarget(item.owner)"></span>
|
|
1184
|
+
<span class="text-secondary" x-show="!item.owner">Unassigned</span>
|
|
1185
|
+
</td>
|
|
1186
|
+
<td class="text-secondary small" x-text="timeAgo(item.updatedAt)"></td>
|
|
1187
|
+
<td>
|
|
1188
|
+
<div class="d-flex gap-1 justify-content-end">
|
|
1189
|
+
<template x-if="item.sourceType === 'message' && item.claimable">
|
|
1190
|
+
<button class="btn btn-sm btn-ghost-warning py-0 px-1" @click="doClaim(item.message.id)">
|
|
1191
|
+
<i class="ti ti-hand-grab me-1"></i>Claim
|
|
1192
|
+
</button>
|
|
1193
|
+
</template>
|
|
1194
|
+
<template x-if="item.sourceType === 'task' && item.claimable">
|
|
1195
|
+
<button class="btn btn-sm btn-ghost-warning py-0 px-1" @click="doClaimTask(item.task.id)">
|
|
1196
|
+
<i class="ti ti-hand-grab me-1"></i>Claim
|
|
1197
|
+
</button>
|
|
1198
|
+
</template>
|
|
1199
|
+
<template x-if="item.sourceType === 'task'">
|
|
1200
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="openTaskEvents(item.task)">
|
|
1201
|
+
<i class="ti ti-history"></i>
|
|
1202
|
+
</button>
|
|
1203
|
+
</template>
|
|
1204
|
+
</div>
|
|
1205
|
+
</td>
|
|
1206
|
+
</tr>
|
|
1207
|
+
</template>
|
|
1208
|
+
</tbody>
|
|
1209
|
+
</table>
|
|
1210
|
+
</div>
|
|
1211
|
+
<template x-if="workQueueItems.length === 0">
|
|
1212
|
+
<div class="card-body text-center text-secondary py-5">
|
|
1213
|
+
<i class="ti ti-list-check" style="font-size:48px; opacity:0.3"></i>
|
|
1214
|
+
<p class="mt-2">No queued work</p>
|
|
1215
|
+
</div>
|
|
1216
|
+
</template>
|
|
1217
|
+
</div>
|
|
1218
|
+
</div>
|
|
1219
|
+
|
|
947
1220
|
<!-- ==================== TASKS ==================== -->
|
|
948
1221
|
<div x-show="view === 'tasks'" x-cloak class="fade-in">
|
|
949
1222
|
<div class="d-flex align-items-center mb-3 gap-2 flex-wrap">
|
|
@@ -1010,6 +1283,12 @@
|
|
|
1010
1283
|
<i class="ti ti-history" style="font-size:14px"></i>
|
|
1011
1284
|
Events
|
|
1012
1285
|
</button>
|
|
1286
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="exportTask(task, 'markdown')" title="Export task Markdown">
|
|
1287
|
+
<i class="ti ti-file-export" style="font-size:14px"></i>
|
|
1288
|
+
</button>
|
|
1289
|
+
<button class="btn btn-sm btn-ghost-secondary py-0 px-1" @click="exportTask(task, 'json')" title="Export task JSON">
|
|
1290
|
+
<i class="ti ti-braces" style="font-size:14px"></i>
|
|
1291
|
+
</button>
|
|
1013
1292
|
</div>
|
|
1014
1293
|
</div>
|
|
1015
1294
|
<span class="text-secondary small" x-text="timeAgo(task.updatedAt)"></span>
|
|
@@ -1101,7 +1380,44 @@
|
|
|
1101
1380
|
</div>
|
|
1102
1381
|
|
|
1103
1382
|
</div>
|
|
1104
|
-
|
|
1383
|
+
</main>
|
|
1384
|
+
|
|
1385
|
+
<!-- ==================== COMMAND PALETTE ==================== -->
|
|
1386
|
+
<div class="command-palette" x-show="commandPaletteOpen" x-cloak @click.self="closeCommandPalette()">
|
|
1387
|
+
<div class="command-palette-panel" @keydown.escape.stop="closeCommandPalette()">
|
|
1388
|
+
<div class="p-3 border-bottom">
|
|
1389
|
+
<div class="input-icon">
|
|
1390
|
+
<span class="input-icon-addon"><i class="ti ti-search"></i></span>
|
|
1391
|
+
<input
|
|
1392
|
+
type="search"
|
|
1393
|
+
class="form-control form-control-lg"
|
|
1394
|
+
placeholder="Command"
|
|
1395
|
+
x-model.debounce.100ms="commandQuery"
|
|
1396
|
+
x-ref="commandSearch"
|
|
1397
|
+
@keydown.enter.prevent="runCommand(commandPaletteItems[0])"
|
|
1398
|
+
>
|
|
1399
|
+
</div>
|
|
1400
|
+
</div>
|
|
1401
|
+
<div class="list-group list-group-flush" style="max-height: 60vh; overflow-y: auto">
|
|
1402
|
+
<template x-for="command in commandPaletteItems" :key="command.id">
|
|
1403
|
+
<button type="button" class="list-group-item list-group-item-action command-palette-item text-start" @click="runCommand(command)">
|
|
1404
|
+
<div class="d-flex align-items-center gap-3">
|
|
1405
|
+
<span class="activity-icon bg-secondary-lt">
|
|
1406
|
+
<i class="ti" :class="command.icon"></i>
|
|
1407
|
+
</span>
|
|
1408
|
+
<div class="min-width-0">
|
|
1409
|
+
<div class="fw-bold text-truncate" x-text="command.title"></div>
|
|
1410
|
+
<div class="text-secondary small text-truncate" x-text="command.subtitle"></div>
|
|
1411
|
+
</div>
|
|
1412
|
+
</div>
|
|
1413
|
+
</button>
|
|
1414
|
+
</template>
|
|
1415
|
+
<template x-if="commandPaletteItems.length === 0">
|
|
1416
|
+
<div class="list-group-item text-secondary text-center py-4">No commands</div>
|
|
1417
|
+
</template>
|
|
1418
|
+
</div>
|
|
1419
|
+
</div>
|
|
1420
|
+
</div>
|
|
1105
1421
|
|
|
1106
1422
|
<!-- ==================== AGENT DETAIL DRAWER ==================== -->
|
|
1107
1423
|
<template x-if="selectedAgentDetail">
|
|
@@ -1109,7 +1425,7 @@
|
|
|
1109
1425
|
<div class="agent-drawer-backdrop" x-show="agentDetailOpen" x-cloak @click="closeAgentDetail()"></div>
|
|
1110
1426
|
<aside class="agent-drawer" x-show="agentDetailOpen" x-cloak>
|
|
1111
1427
|
<div class="p-3 border-bottom d-flex align-items-start gap-2">
|
|
1112
|
-
<span class="status-dot mt-2" :class="
|
|
1428
|
+
<span class="status-dot mt-2" :class="agentStatusClass(selectedAgentDetail)"></span>
|
|
1113
1429
|
<span class="agent-type-icon mt-1" :class="agentType(selectedAgentDetail)" :title="agentTypeTitle(selectedAgentDetail)">
|
|
1114
1430
|
<i class="ti" :class="agentTypeIcon(selectedAgentDetail)"></i>
|
|
1115
1431
|
</span>
|
|
@@ -1138,7 +1454,7 @@
|
|
|
1138
1454
|
<i class="ti ti-link me-1"></i>Pairs
|
|
1139
1455
|
</button>
|
|
1140
1456
|
<button class="btn btn-sm btn-ghost-secondary" @click="openPairInvite(selectedAgentDetail.id); closeAgentDetail()">
|
|
1141
|
-
<i class="ti ti-link-plus me-1"></i>Pair
|
|
1457
|
+
<i class="ti ti-link-plus me-1"></i>Pair with...
|
|
1142
1458
|
</button>
|
|
1143
1459
|
<button class="btn btn-sm btn-ghost-secondary" @click="openRename(selectedAgentDetail)">
|
|
1144
1460
|
<i class="ti ti-pencil me-1"></i>Rename
|
|
@@ -1162,7 +1478,12 @@
|
|
|
1162
1478
|
<div class="detail-row mb-2">
|
|
1163
1479
|
<div class="text-secondary small">State</div>
|
|
1164
1480
|
<div>
|
|
1165
|
-
<span class="badge bg-
|
|
1481
|
+
<span class="badge" :class="'bg-' + agentPresence(selectedAgentDetail).tone + '-lt'">
|
|
1482
|
+
<i class="ti me-1" :class="agentPresence(selectedAgentDetail).icon"></i><span x-text="agentPresence(selectedAgentDetail).label"></span>
|
|
1483
|
+
</span>
|
|
1484
|
+
<template x-for="badge in agentPresenceBadges(selectedAgentDetail)" :key="badge.label">
|
|
1485
|
+
<span class="badge ms-1" :class="badge.className" x-text="badge.label"></span>
|
|
1486
|
+
</template>
|
|
1166
1487
|
<template x-if="selectedAgentDetail.ready">
|
|
1167
1488
|
<span class="badge bg-success-lt ms-1">ready</span>
|
|
1168
1489
|
</template>
|