aimeat 1.16.0 → 1.16.2
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/dist/locales/en.json +25 -0
- package/dist/locales/fi.json +25 -0
- package/dist/public/css/views/agents-detail.css +144 -0
- package/dist/public/js/services/agents.js +8 -0
- package/dist/public/views/profile/agents/rate-modal.js +87 -0
- package/dist/public/views/profile/agents/tab-quality.js +58 -4
- package/dist/public/views/profile/agents/tab-tasks.js +4 -2
- package/dist/public/views/profile/agents-tasks-subtab.js +91 -2
- package/dist/src/generated/api-types.d.ts +66 -0
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/routes/agent-activity.d.ts +1 -0
- package/dist/src/routes/agent-activity.d.ts.map +1 -1
- package/dist/src/routes/agent-activity.js +6 -3
- package/dist/src/routes/agent-activity.js.map +1 -1
- package/dist/src/routes/agent-integration.d.ts.map +1 -1
- package/dist/src/routes/agent-integration.js +4 -0
- package/dist/src/routes/agent-integration.js.map +1 -1
- package/dist/src/routes/agents.d.ts.map +1 -1
- package/dist/src/routes/agents.js +35 -0
- package/dist/src/routes/agents.js.map +1 -1
- package/dist/src/storage/interface.d.ts +8 -0
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/interface.js.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +2 -1
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +6 -4
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +5 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +1 -0
package/dist/locales/en.json
CHANGED
|
@@ -420,6 +420,12 @@
|
|
|
420
420
|
"title": "Task Queue",
|
|
421
421
|
"empty": "No tasks yet",
|
|
422
422
|
"newTask": "New Task",
|
|
423
|
+
"concurrency": {
|
|
424
|
+
"label": "Max concurrent tasks",
|
|
425
|
+
"hint": "1 = serial. Higher needs a concurrency-capable runner (e.g. a CrewAI daemon).",
|
|
426
|
+
"saved": "Concurrency updated",
|
|
427
|
+
"error": "Failed to update concurrency"
|
|
428
|
+
},
|
|
423
429
|
"createTask": "Create Task",
|
|
424
430
|
"cancel": "Cancel",
|
|
425
431
|
"titleLabel": "Title (optional)",
|
|
@@ -458,6 +464,21 @@
|
|
|
458
464
|
"deliverable": "Deliverable",
|
|
459
465
|
"viewDeliverable": "View",
|
|
460
466
|
"deliverableGone": "The deliverable entry no longer exists.",
|
|
467
|
+
"rate": {
|
|
468
|
+
"button": "Rate deliverable",
|
|
469
|
+
"rerate": "Re-rate",
|
|
470
|
+
"title": "Rate this deliverable",
|
|
471
|
+
"help": "Give the deliverable 1–5 stars and pick the context it should be judged in. Factual contexts should be checked against the sources, not rated on looks alone.",
|
|
472
|
+
"context": "Context",
|
|
473
|
+
"grounded": "I checked this against the sources / inputs",
|
|
474
|
+
"commentPlaceholder": "Optional: what was good or wrong?",
|
|
475
|
+
"submit": "Submit rating",
|
|
476
|
+
"submitting": "Saving...",
|
|
477
|
+
"rated": "Rated",
|
|
478
|
+
"needStars": "Pick a star rating first",
|
|
479
|
+
"success": "Rating saved",
|
|
480
|
+
"error": "Failed to save rating"
|
|
481
|
+
},
|
|
461
482
|
"cancel": "Cancel",
|
|
462
483
|
"cancelConfirm": "Cancel this task? Work that hasn't started will be skipped",
|
|
463
484
|
"cancelled": "Task cancelled",
|
|
@@ -663,6 +684,10 @@
|
|
|
663
684
|
"performanceDesc": "How much this agent has done and how fast — computed from its tasks.",
|
|
664
685
|
"reviewsTitle": "Reviews by context",
|
|
665
686
|
"reviewsDesc": "Per-context star ratings of the agent's deliverables. Factual contexts are source-grounded.",
|
|
687
|
+
"pendingTitle": "Rate deliverables",
|
|
688
|
+
"pendingDesc": "Completed tasks you can rate. Your ratings build the per-context quality picture above.",
|
|
689
|
+
"allRated": "All completed deliverables are rated.",
|
|
690
|
+
"unrated": "Not rated",
|
|
666
691
|
"customTitle": "Custom metrics",
|
|
667
692
|
"customDesc": "Metrics the agent publishes about itself.",
|
|
668
693
|
"tasksTotal": "Tasks",
|
package/dist/locales/fi.json
CHANGED
|
@@ -420,6 +420,12 @@
|
|
|
420
420
|
"title": "Tehtäväjono",
|
|
421
421
|
"empty": "Ei tehtäviä vielä",
|
|
422
422
|
"newTask": "Uusi tehtävä",
|
|
423
|
+
"concurrency": {
|
|
424
|
+
"label": "Tehtäviä rinnakkain enintään",
|
|
425
|
+
"hint": "1 = sarjallinen. Suurempi vaatii rinnakkaisuutta tukevan ajurin (esim. CrewAI-daemon).",
|
|
426
|
+
"saved": "Rinnakkaisuus päivitetty",
|
|
427
|
+
"error": "Rinnakkaisuuden päivitys epäonnistui"
|
|
428
|
+
},
|
|
423
429
|
"createTask": "Luo tehtävä",
|
|
424
430
|
"cancel": "Peruuta",
|
|
425
431
|
"titleLabel": "Otsikko (valinnainen)",
|
|
@@ -458,6 +464,21 @@
|
|
|
458
464
|
"deliverable": "Tuotos",
|
|
459
465
|
"viewDeliverable": "Näytä",
|
|
460
466
|
"deliverableGone": "Tuotosmerkintää ei ole enää olemassa.",
|
|
467
|
+
"rate": {
|
|
468
|
+
"button": "Arvioi tuotos",
|
|
469
|
+
"rerate": "Arvioi uudelleen",
|
|
470
|
+
"title": "Arvioi tämä tuotos",
|
|
471
|
+
"help": "Anna tuotokselle 1–5 tähteä ja valitse konteksti jossa sitä arvioidaan. Faktakontekstit tulisi tarkistaa lähteitä vasten, ei arvioida pelkän ulkoasun perusteella.",
|
|
472
|
+
"context": "Konteksti",
|
|
473
|
+
"grounded": "Tarkistin tämän lähteitä / syötteitä vasten",
|
|
474
|
+
"commentPlaceholder": "Valinnainen: mikä oli hyvää tai pielessä?",
|
|
475
|
+
"submit": "Tallenna arvio",
|
|
476
|
+
"submitting": "Tallennetaan...",
|
|
477
|
+
"rated": "Arvioitu",
|
|
478
|
+
"needStars": "Valitse ensin tähtiarvio",
|
|
479
|
+
"success": "Arvio tallennettu",
|
|
480
|
+
"error": "Arvion tallennus epäonnistui"
|
|
481
|
+
},
|
|
461
482
|
"cancel": "Peruuta",
|
|
462
483
|
"cancelConfirm": "Peruutetaanko tämä tehtävä? Aloittamaton työ ohitetaan",
|
|
463
484
|
"cancelled": "Tehtävä peruutettu",
|
|
@@ -663,6 +684,10 @@
|
|
|
663
684
|
"performanceDesc": "Kuinka paljon agentti on tehnyt ja kuinka nopeasti — laskettu sen tehtävistä.",
|
|
664
685
|
"reviewsTitle": "Arviot konteksteittain",
|
|
665
686
|
"reviewsDesc": "Agentin tuotosten tähtiarviot konteksteittain. Faktakontekstit ovat lähteisiin perustuvia.",
|
|
687
|
+
"pendingTitle": "Arvioi tuotokset",
|
|
688
|
+
"pendingDesc": "Valmiit tehtävät joita voit arvioida. Arviosi rakentavat yllä olevan kontekstikohtaisen laatukuvan.",
|
|
689
|
+
"allRated": "Kaikki valmiit tuotokset on arvioitu.",
|
|
690
|
+
"unrated": "Ei arvioitu",
|
|
666
691
|
"customTitle": "Omat mittarit",
|
|
667
692
|
"customDesc": "Mittarit, joita agentti julkaisee itsestään.",
|
|
668
693
|
"tasksTotal": "Tehtäviä",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
* v2.9.0 -- 2026-05-31 -- Collapsed bar two-row layout (pf-agd-collapsed-main + pf-agd-collapsed-meta divider) to stop status overflow; move drag grip into the left margin so the card border stays flush
|
|
25
25
|
* v2.10.0 -- 2026-05-31 -- Extend the drag-row drop target left over the grip gutter (padding-left + negative margin-left) so dropping on/near the handle registers; card border stays aligned
|
|
26
26
|
* v2.11.0 -- 2026-05-31 -- Add Quality tab styles (per-context review rows, duration chips, custom-metric rows)
|
|
27
|
+
* v2.12.0 -- 2026-05-31 -- Add deliverable rating control styles (star picker, current-rating row, rate modal fields)
|
|
28
|
+
* v2.13.0 -- 2026-05-31 -- Add Quality-tab "Rate deliverables" pending-list row styles
|
|
29
|
+
* v2.14.0 -- 2026-06-01 -- Add Tasks-tab max-concurrent-tasks config row styles
|
|
27
30
|
* v2.11.0 -- 2026-05-31 -- Widen the solo pop-out window (and pf-agd-solo max-width) so the agent card matches the list-view width and the full tab bar fits
|
|
28
31
|
* v2.12.0 -- 2026-05-31 -- Add completed-task deliverable styles (pf-agd-deliverable*) linking a task to its agent-memory output key
|
|
29
32
|
* v2.13.0 -- 2026-05-31 -- Add per-task blur-title toggle styles (pf-agd-blur-toggle, pf-agd-task-title--blurred) for hiding sensitive titles in screen recordings
|
|
@@ -2790,3 +2793,144 @@
|
|
|
2790
2793
|
text-align: right;
|
|
2791
2794
|
word-break: break-word;
|
|
2792
2795
|
}
|
|
2796
|
+
|
|
2797
|
+
/* ── Deliverable rating (Tasks tab) ── */
|
|
2798
|
+
|
|
2799
|
+
/* Current rating + rate/re-rate button row under a done task's deliverable */
|
|
2800
|
+
.pf .pf-agd-rate-row {
|
|
2801
|
+
display: flex;
|
|
2802
|
+
align-items: center;
|
|
2803
|
+
flex-wrap: wrap;
|
|
2804
|
+
gap: 0.5rem;
|
|
2805
|
+
margin: 0.5rem 0;
|
|
2806
|
+
}
|
|
2807
|
+
.pf .pf-agd-rate-current {
|
|
2808
|
+
display: inline-flex;
|
|
2809
|
+
align-items: center;
|
|
2810
|
+
flex-wrap: wrap;
|
|
2811
|
+
gap: 0.5rem;
|
|
2812
|
+
font-size: 0.85rem;
|
|
2813
|
+
}
|
|
2814
|
+
.pf .pf-agd-rate-current-stars {
|
|
2815
|
+
color: var(--accent, #E8564A);
|
|
2816
|
+
letter-spacing: 1px;
|
|
2817
|
+
}
|
|
2818
|
+
.pf .pf-agd-rate-current-ctx {
|
|
2819
|
+
color: var(--text-dim, #6B7280);
|
|
2820
|
+
font-weight: 600;
|
|
2821
|
+
}
|
|
2822
|
+
.pf .pf-agd-rate-current-comment {
|
|
2823
|
+
color: var(--text-muted, #9CA3AF);
|
|
2824
|
+
font-style: italic;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
/* Star picker inside the rate modal */
|
|
2828
|
+
.pf .pf-agd-rate-stars {
|
|
2829
|
+
display: flex;
|
|
2830
|
+
gap: 0.15rem;
|
|
2831
|
+
margin: 0.5rem 0 0.75rem;
|
|
2832
|
+
}
|
|
2833
|
+
.pf .pf-agd-rate-star {
|
|
2834
|
+
background: none;
|
|
2835
|
+
border: none;
|
|
2836
|
+
cursor: pointer;
|
|
2837
|
+
font-size: 1.6rem;
|
|
2838
|
+
line-height: 1;
|
|
2839
|
+
color: var(--border, #E5E7EB);
|
|
2840
|
+
padding: 0;
|
|
2841
|
+
transition: color 0.12s;
|
|
2842
|
+
}
|
|
2843
|
+
.pf .pf-agd-rate-star:hover {
|
|
2844
|
+
color: var(--accent, #E8564A);
|
|
2845
|
+
}
|
|
2846
|
+
.pf .pf-agd-rate-star--on {
|
|
2847
|
+
color: var(--accent, #E8564A);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
/* Context select + grounded checkbox rows in the rate modal */
|
|
2851
|
+
.pf .pf-agd-rate-field {
|
|
2852
|
+
display: flex;
|
|
2853
|
+
align-items: center;
|
|
2854
|
+
gap: 0.5rem;
|
|
2855
|
+
margin-bottom: 0.6rem;
|
|
2856
|
+
font-size: 0.85rem;
|
|
2857
|
+
color: var(--text-dim, #6B7280);
|
|
2858
|
+
}
|
|
2859
|
+
.pf .pf-agd-rate-field select {
|
|
2860
|
+
flex: 1;
|
|
2861
|
+
}
|
|
2862
|
+
.pf .pf-agd-rate-check {
|
|
2863
|
+
display: flex;
|
|
2864
|
+
align-items: center;
|
|
2865
|
+
gap: 0.5rem;
|
|
2866
|
+
margin-bottom: 0.6rem;
|
|
2867
|
+
font-size: 0.85rem;
|
|
2868
|
+
color: var(--text, #1A1A2E);
|
|
2869
|
+
cursor: pointer;
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
/* Quality tab "Rate deliverables" pending list */
|
|
2873
|
+
.pf .pf-agd-quality-pending {
|
|
2874
|
+
display: flex;
|
|
2875
|
+
flex-direction: column;
|
|
2876
|
+
gap: 0.4rem;
|
|
2877
|
+
}
|
|
2878
|
+
.pf .pf-agd-quality-pending-row {
|
|
2879
|
+
display: flex;
|
|
2880
|
+
align-items: center;
|
|
2881
|
+
gap: 0.5rem;
|
|
2882
|
+
padding: 0.4rem 0.5rem;
|
|
2883
|
+
border: 1px solid var(--border, #E5E7EB);
|
|
2884
|
+
border-radius: 8px;
|
|
2885
|
+
background: var(--card-bg, #FFFFFF);
|
|
2886
|
+
}
|
|
2887
|
+
.pf .pf-agd-quality-pending-title {
|
|
2888
|
+
flex: 1;
|
|
2889
|
+
min-width: 0;
|
|
2890
|
+
overflow: hidden;
|
|
2891
|
+
text-overflow: ellipsis;
|
|
2892
|
+
white-space: nowrap;
|
|
2893
|
+
font-size: 0.85rem;
|
|
2894
|
+
color: var(--text, #1A1A2E);
|
|
2895
|
+
}
|
|
2896
|
+
.pf .pf-agd-quality-pending-rated {
|
|
2897
|
+
font-size: 0.8rem;
|
|
2898
|
+
color: var(--accent, #E8564A);
|
|
2899
|
+
letter-spacing: 0.5px;
|
|
2900
|
+
white-space: nowrap;
|
|
2901
|
+
}
|
|
2902
|
+
.pf .pf-agd-quality-pending-unrated {
|
|
2903
|
+
font-size: 0.72rem;
|
|
2904
|
+
color: var(--text-muted, #9CA3AF);
|
|
2905
|
+
border: 1px solid var(--border, #E5E7EB);
|
|
2906
|
+
border-radius: 6px;
|
|
2907
|
+
padding: 0.05rem 0.4rem;
|
|
2908
|
+
white-space: nowrap;
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
/* Tasks tab — max concurrent tasks runner config */
|
|
2912
|
+
.pf .pf-agd-concurrency {
|
|
2913
|
+
display: flex;
|
|
2914
|
+
align-items: center;
|
|
2915
|
+
flex-wrap: wrap;
|
|
2916
|
+
gap: 0.5rem;
|
|
2917
|
+
margin: 0.25rem 0 0.75rem;
|
|
2918
|
+
font-size: 0.85rem;
|
|
2919
|
+
}
|
|
2920
|
+
.pf .pf-agd-concurrency-label {
|
|
2921
|
+
color: var(--text-dim, #6B7280);
|
|
2922
|
+
font-weight: 600;
|
|
2923
|
+
}
|
|
2924
|
+
.pf .pf-agd-concurrency-input {
|
|
2925
|
+
width: 4rem;
|
|
2926
|
+
padding: 0.25rem 0.4rem;
|
|
2927
|
+
border: 1px solid var(--border, #E5E7EB);
|
|
2928
|
+
border-radius: 6px;
|
|
2929
|
+
background: var(--card-bg, #FFFFFF);
|
|
2930
|
+
color: var(--text, #1A1A2E);
|
|
2931
|
+
font-size: 0.85rem;
|
|
2932
|
+
}
|
|
2933
|
+
.pf .pf-agd-concurrency-hint {
|
|
2934
|
+
color: var(--text-muted, #9CA3AF);
|
|
2935
|
+
font-size: 0.75rem;
|
|
2936
|
+
}
|
|
@@ -30,6 +30,14 @@ export async function updateAgentScopes(name, scopes) {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** Set how many tasks the agent's runner may process concurrently (1–20). */
|
|
34
|
+
export async function setMaxConcurrentTasks(name, max) {
|
|
35
|
+
return api(`/v1/agents/${encodeURIComponent(name)}/max-concurrent-tasks`, {
|
|
36
|
+
method: 'PATCH',
|
|
37
|
+
body: JSON.stringify({ max_concurrent_tasks: max }),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
/** Get agent CORS origins. */
|
|
34
42
|
export async function getAgentCors(name) {
|
|
35
43
|
const data = await apiGet(`/v1/agents/${encodeURIComponent(name)}/cors`);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file rate-modal.js
|
|
3
|
+
* @description Shared "rate a deliverable" modal — 1–5 stars, quality context,
|
|
4
|
+
* an optional "checked against sources" flag, and a comment. Submits the
|
|
5
|
+
* POST /v1/agents/:name/tasks/:id/rate body shape. Used by both the Tasks
|
|
6
|
+
* sub-tab (rate from the task row) and the Quality tab (rate from the
|
|
7
|
+
* pending-deliverables list). Pre-fills from an existing rating so the same
|
|
8
|
+
* modal also does re-rate.
|
|
9
|
+
* @structure
|
|
10
|
+
* - RATE_CONTEXTS -- the RatingContext enum (mirrors src/storage/interface.ts)
|
|
11
|
+
* - RateModal (default export) -- the modal component
|
|
12
|
+
* @usage
|
|
13
|
+
* import RateModal, { RATE_CONTEXTS } from './rate-modal.js';
|
|
14
|
+
* <RateModal open onClose onSubmit submitting existing=${task.rating} />
|
|
15
|
+
* @version-history
|
|
16
|
+
* v1.0.0 -- 2026-05-31 -- Extracted from agents-tasks-subtab.js so the Quality
|
|
17
|
+
* tab can reuse the same rating modal.
|
|
18
|
+
*/
|
|
19
|
+
import { h } from 'preact';
|
|
20
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
21
|
+
import htm from 'htm';
|
|
22
|
+
import { t } from '/js/i18n.js';
|
|
23
|
+
import { Modal } from '/components/Modal.js';
|
|
24
|
+
|
|
25
|
+
const html = htm.bind(h);
|
|
26
|
+
|
|
27
|
+
// Quality contexts an owner can pick when rating a deliverable. Mirrors the
|
|
28
|
+
// RatingContext enum in src/storage/interface.ts; labels come from the shared
|
|
29
|
+
// profile.agents.detail.quality.contexts.* i18n block.
|
|
30
|
+
export const RATE_CONTEXTS = ['factual', 'creative', 'code', 'planning', 'summarization', 'research', 'communication', 'other'];
|
|
31
|
+
|
|
32
|
+
export default function RateModal({ open, onClose, onSubmit, submitting, existing }) {
|
|
33
|
+
const [stars, setStars] = useState(existing?.stars || 0);
|
|
34
|
+
const [context, setContext] = useState(existing?.context || 'creative');
|
|
35
|
+
const [grounded, setGrounded] = useState(existing?.sourceGrounded || false);
|
|
36
|
+
const [comment, setComment] = useState(existing?.comment || '');
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (open) {
|
|
39
|
+
setStars(existing?.stars || 0);
|
|
40
|
+
setContext(existing?.context || 'creative');
|
|
41
|
+
setGrounded(existing?.sourceGrounded || false);
|
|
42
|
+
setComment(existing?.comment || '');
|
|
43
|
+
}
|
|
44
|
+
}, [open]);
|
|
45
|
+
function handleSend() {
|
|
46
|
+
if (!stars) return;
|
|
47
|
+
const body = { stars, context, source_grounded: grounded };
|
|
48
|
+
const c = comment.trim();
|
|
49
|
+
if (c) body.comment = c;
|
|
50
|
+
onSubmit(body);
|
|
51
|
+
}
|
|
52
|
+
return html`<${Modal} open=${open} onClose=${onClose} title=${t('profile.agents.tasks.rate.title')}>
|
|
53
|
+
<p class="pf-agd-modal-help">${t('profile.agents.tasks.rate.help')}</p>
|
|
54
|
+
<div class="pf-agd-rate-stars" role="radiogroup">
|
|
55
|
+
${[1, 2, 3, 4, 5].map(n => html`
|
|
56
|
+
<button key=${n}
|
|
57
|
+
class=${`pf-agd-rate-star ${n <= stars ? 'pf-agd-rate-star--on' : ''}`}
|
|
58
|
+
onClick=${() => setStars(n)}
|
|
59
|
+
aria-label=${String(n)}
|
|
60
|
+
aria-pressed=${n <= stars}>★</button>
|
|
61
|
+
`)}
|
|
62
|
+
</div>
|
|
63
|
+
<label class="pf-agd-rate-field">
|
|
64
|
+
<span>${t('profile.agents.tasks.rate.context')}</span>
|
|
65
|
+
<select value=${context} onChange=${e => setContext(e.target.value)}>
|
|
66
|
+
${RATE_CONTEXTS.map(c => html`<option key=${c} value=${c}>${t(`profile.agents.detail.quality.contexts.${c}`)}</option>`)}
|
|
67
|
+
</select>
|
|
68
|
+
</label>
|
|
69
|
+
<label class="pf-agd-rate-check">
|
|
70
|
+
<input type="checkbox" checked=${grounded} onChange=${e => setGrounded(e.target.checked)} />
|
|
71
|
+
<span>${t('profile.agents.tasks.rate.grounded')}</span>
|
|
72
|
+
</label>
|
|
73
|
+
<textarea
|
|
74
|
+
class="pf-agd-revision-textarea"
|
|
75
|
+
placeholder=${t('profile.agents.tasks.rate.commentPlaceholder')}
|
|
76
|
+
value=${comment}
|
|
77
|
+
onInput=${e => setComment(e.target.value)}
|
|
78
|
+
rows=${3}
|
|
79
|
+
></textarea>
|
|
80
|
+
<div class="modal-footer">
|
|
81
|
+
<button class="btn-ghost" onClick=${onClose} disabled=${submitting}>${t('common.cancel') || 'Cancel'}</button>
|
|
82
|
+
<button class="btn-primary" onClick=${handleSend} disabled=${submitting || !stars}>
|
|
83
|
+
${submitting ? t('profile.agents.tasks.rate.submitting') : t('profile.agents.tasks.rate.submit')}
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
<//>`;
|
|
87
|
+
}
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* @structure default export TabQuality({ agentName })
|
|
11
11
|
* @usage rendered by agent-card.js renderTabContent() for the 'quality' tab
|
|
12
12
|
* @version-history
|
|
13
|
+
* v1.1.0 -- 2026-05-31 -- Add "Rate deliverables" list: completed tasks the owner
|
|
14
|
+
* can rate inline via the shared RateModal (unrated first); refreshes the
|
|
15
|
+
* rollups on submit.
|
|
13
16
|
* v1.0.0 -- 2026-05-31 -- Initial Quality tab (per-context reviews + performance + custom)
|
|
14
17
|
*/
|
|
15
18
|
|
|
@@ -17,7 +20,8 @@ import { h } from 'preact';
|
|
|
17
20
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
18
21
|
import htm from 'htm';
|
|
19
22
|
import { t } from '/js/i18n.js';
|
|
20
|
-
import { getAgentStatistics } from '/js/services/agent-tasks.js';
|
|
23
|
+
import { getAgentStatistics, listTasks, rateTask } from '/js/services/agent-tasks.js';
|
|
24
|
+
import RateModal from './rate-modal.js';
|
|
21
25
|
|
|
22
26
|
const html = htm.bind(h);
|
|
23
27
|
|
|
@@ -38,19 +42,40 @@ function fmtSeconds(secs) {
|
|
|
38
42
|
return `${Number(secs || 0).toLocaleString()}${t('profile.agents.detail.quality.seconds')}`;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
export default function TabQuality({ agentName }) {
|
|
45
|
+
export default function TabQuality({ agentName, showToast }) {
|
|
42
46
|
const [data, setData] = useState(null);
|
|
43
47
|
const [loading, setLoading] = useState(true);
|
|
48
|
+
const [doneTasks, setDoneTasks] = useState([]);
|
|
49
|
+
const [rateTarget, setRateTarget] = useState(null); // task being rated, or null
|
|
50
|
+
const [sendingRate, setSendingRate] = useState(false);
|
|
44
51
|
|
|
45
52
|
async function loadData() {
|
|
46
53
|
setLoading(true);
|
|
47
54
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
55
|
+
const [statsResp, tasksResp] = await Promise.all([
|
|
56
|
+
getAgentStatistics(agentName),
|
|
57
|
+
listTasks(agentName, { status: 'done', per_page: 100 }).catch(() => null),
|
|
58
|
+
]);
|
|
59
|
+
setData(statsResp?.data || null);
|
|
60
|
+
setDoneTasks(tasksResp?.data?.tasks || []);
|
|
50
61
|
} catch { setData(null); }
|
|
51
62
|
setLoading(false);
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
async function handleSubmitRate(body) {
|
|
66
|
+
if (!rateTarget) return;
|
|
67
|
+
setSendingRate(true);
|
|
68
|
+
try {
|
|
69
|
+
await rateTask(agentName, rateTarget.id, body);
|
|
70
|
+
showToast?.(t('profile.agents.tasks.rate.success'));
|
|
71
|
+
setRateTarget(null);
|
|
72
|
+
await loadData();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
showToast?.(err.message || t('profile.agents.tasks.rate.error'), true);
|
|
75
|
+
}
|
|
76
|
+
setSendingRate(false);
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
useEffect(() => { loadData(); }, [agentName]);
|
|
55
80
|
|
|
56
81
|
// Live updates: refresh when a rating lands or tasks change.
|
|
@@ -139,6 +164,27 @@ export default function TabQuality({ agentName }) {
|
|
|
139
164
|
</div>
|
|
140
165
|
`}
|
|
141
166
|
|
|
167
|
+
<!-- Rate deliverables (completed tasks the owner can rate) -->
|
|
168
|
+
<div class="pf-agd-section-title">${t('profile.agents.detail.quality.pendingTitle')}</div>
|
|
169
|
+
<div class="pf-agd-quality-desc">${t('profile.agents.detail.quality.pendingDesc')}</div>
|
|
170
|
+
${doneTasks.length === 0
|
|
171
|
+
? html`<div class="pf-agd-empty">${t('profile.agents.detail.quality.allRated')}</div>`
|
|
172
|
+
: html`
|
|
173
|
+
<div class="pf-agd-quality-pending">
|
|
174
|
+
${[...doneTasks].sort((a, b) => (a.rating ? 1 : 0) - (b.rating ? 1 : 0)).map(task => html`
|
|
175
|
+
<div key=${task.id} class="pf-agd-quality-pending-row">
|
|
176
|
+
<span class="pf-agd-quality-pending-title">${task.title || task.id}</span>
|
|
177
|
+
${task.rating
|
|
178
|
+
? html`<span class="pf-agd-quality-pending-rated">${starGlyphs(task.rating.stars)} ${ctxLabel(task.rating.context)}</span>`
|
|
179
|
+
: html`<span class="pf-agd-quality-pending-unrated">${t('profile.agents.detail.quality.unrated')}</span>`}
|
|
180
|
+
<button class="btn-outline btn-sm" onClick=${() => setRateTarget(task)}>
|
|
181
|
+
${task.rating ? t('profile.agents.tasks.rate.rerate') : t('profile.agents.tasks.rate.button')}
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
`)}
|
|
185
|
+
</div>
|
|
186
|
+
`}
|
|
187
|
+
|
|
142
188
|
<!-- Custom metrics -->
|
|
143
189
|
<div class="pf-agd-section-title">${t('profile.agents.detail.quality.customTitle')}</div>
|
|
144
190
|
<div class="pf-agd-quality-desc">${t('profile.agents.detail.quality.customDesc')}</div>
|
|
@@ -154,6 +200,14 @@ export default function TabQuality({ agentName }) {
|
|
|
154
200
|
`)}
|
|
155
201
|
</div>
|
|
156
202
|
`}
|
|
203
|
+
|
|
204
|
+
<${RateModal}
|
|
205
|
+
open=${!!rateTarget}
|
|
206
|
+
onClose=${() => setRateTarget(null)}
|
|
207
|
+
onSubmit=${handleSubmitRate}
|
|
208
|
+
submitting=${sendingRate}
|
|
209
|
+
existing=${rateTarget?.rating}
|
|
210
|
+
/>
|
|
157
211
|
</div>
|
|
158
212
|
`;
|
|
159
213
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @description Tasks tab wrapper for agent detail tab-view.
|
|
4
4
|
* Delegates to the existing AgentTasksSubtab component.
|
|
5
5
|
* @version-history
|
|
6
|
+
* v1.1.0 -- 2026-06-01 -- Forward the `agent` object so the subtab can show the
|
|
7
|
+
* max_concurrent_tasks runner config.
|
|
6
8
|
* v1.0.0 -- 2026-05-24 -- Initial creation for Agent Detail Tab-View
|
|
7
9
|
*/
|
|
8
10
|
|
|
@@ -12,6 +14,6 @@ import AgentTasksSubtab from '../agents-tasks-subtab.js';
|
|
|
12
14
|
|
|
13
15
|
const html = htm.bind(h);
|
|
14
16
|
|
|
15
|
-
export default function TabTasks({ agentName, session, showToast }) {
|
|
16
|
-
return html`<${AgentTasksSubtab} agentName=${agentName} session=${session} showToast=${showToast} />`;
|
|
17
|
+
export default function TabTasks({ agent, agentName, session, showToast }) {
|
|
18
|
+
return html`<${AgentTasksSubtab} agent=${agent} agentName=${agentName} session=${session} showToast=${showToast} />`;
|
|
17
19
|
}
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
* - TaskItem -- task row with expand/collapse, todo list, start button
|
|
10
10
|
* - RequestChangesModal -- inline modal for owner to send a free-text change request
|
|
11
11
|
* @version-history
|
|
12
|
+
* v4.6.0 -- 2026-06-01 -- Add "max concurrent tasks" runner config (number input,
|
|
13
|
+
* default 1) that PATCHes /v1/agents/:name/max-concurrent-tasks. Consumed by
|
|
14
|
+
* the agent's runner (e.g. a CrewAI daemon) via the integration kit.
|
|
15
|
+
* v4.5.0 -- 2026-05-31 -- Extract RateModal to shared ./agents/rate-modal.js
|
|
16
|
+
* so the Quality tab can reuse it; no behaviour change here.
|
|
17
|
+
* v4.4.0 -- 2026-05-31 -- Add owner "Rate deliverable" control on done tasks
|
|
18
|
+
* (RateModal: stars + context + source-grounded + comment) wired to
|
|
19
|
+
* POST /tasks/:id/rate; shows the current rating with a re-rate affordance.
|
|
12
20
|
* v4.3.0 -- 2026-05-31 -- Add per-task "blur title" toggle in the collapsed
|
|
13
21
|
* task row (eye icon). Hides a task title behind a CSS blur for screen
|
|
14
22
|
* recordings; preference persists per task ID in localStorage (browser-only).
|
|
@@ -41,8 +49,10 @@ const html = htm.bind(h);
|
|
|
41
49
|
import { t } from '/js/i18n.js';
|
|
42
50
|
import { timeAgo } from '/js/utils.js';
|
|
43
51
|
import { apiGet, apiPost } from '/js/api.js';
|
|
44
|
-
import { listTasks, deleteTask, startTask, listEvents, requestChanges, createTask } from '/js/services/agent-tasks.js';
|
|
52
|
+
import { listTasks, deleteTask, startTask, listEvents, requestChanges, createTask, rateTask } from '/js/services/agent-tasks.js';
|
|
53
|
+
import { setMaxConcurrentTasks } from '/js/services/agents.js';
|
|
45
54
|
import { useConfirm, Modal } from '/components/Modal.js';
|
|
55
|
+
import RateModal from './agents/rate-modal.js';
|
|
46
56
|
|
|
47
57
|
const TASK_FILTERS = ['all', 'active', 'queued', 'completed', 'failed'];
|
|
48
58
|
|
|
@@ -209,6 +219,8 @@ function TaskItem({ task, agentName, showToast, onRefresh }) {
|
|
|
209
219
|
const [starting, setStarting] = useState(false);
|
|
210
220
|
const [showRevisionModal, setShowRevisionModal] = useState(false);
|
|
211
221
|
const [sendingRevision, setSendingRevision] = useState(false);
|
|
222
|
+
const [showRateModal, setShowRateModal] = useState(false);
|
|
223
|
+
const [sendingRate, setSendingRate] = useState(false);
|
|
212
224
|
const [showOutdated, setShowOutdated] = useState(false);
|
|
213
225
|
// Deliverable preview: null = not requested, {loading}|{notFound}|{value}.
|
|
214
226
|
const [deliverable, setDeliverable] = useState(null);
|
|
@@ -335,6 +347,24 @@ function TaskItem({ task, agentName, showToast, onRefresh }) {
|
|
|
335
347
|
setShowRevisionModal(true);
|
|
336
348
|
}
|
|
337
349
|
|
|
350
|
+
function handleOpenRate(e) {
|
|
351
|
+
e.stopPropagation();
|
|
352
|
+
setShowRateModal(true);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function handleSubmitRate(body) {
|
|
356
|
+
setSendingRate(true);
|
|
357
|
+
try {
|
|
358
|
+
await rateTask(agentName, task.id, body);
|
|
359
|
+
showToast(t('profile.agents.tasks.rate.success'));
|
|
360
|
+
setShowRateModal(false);
|
|
361
|
+
onRefresh();
|
|
362
|
+
} catch (err) {
|
|
363
|
+
showToast(err.message || t('profile.agents.tasks.rate.error'), true);
|
|
364
|
+
}
|
|
365
|
+
setSendingRate(false);
|
|
366
|
+
}
|
|
367
|
+
|
|
338
368
|
async function handleSubmitRevision(message) {
|
|
339
369
|
setSendingRevision(true);
|
|
340
370
|
try {
|
|
@@ -357,6 +387,8 @@ function TaskItem({ task, agentName, showToast, onRefresh }) {
|
|
|
357
387
|
const isQueued = task.status === 'queued' || task.status === 'draft';
|
|
358
388
|
const isRevisionRequested = task.status === 'revision_requested';
|
|
359
389
|
const isActive = task.status === 'active';
|
|
390
|
+
const isDone = task.status === 'done';
|
|
391
|
+
const rating = task.rating;
|
|
360
392
|
const canStart = isQueued && hasTodos;
|
|
361
393
|
const canRequestChanges = task.status === 'queued' && hasTodos;
|
|
362
394
|
const totalMinutes = todos.reduce((sum, td) => sum + (td.estimateMinutes || 0), 0);
|
|
@@ -409,6 +441,21 @@ function TaskItem({ task, agentName, showToast, onRefresh }) {
|
|
|
409
441
|
`}
|
|
410
442
|
`}
|
|
411
443
|
|
|
444
|
+
${isDone && html`
|
|
445
|
+
<div class="pf-agd-rate-row">
|
|
446
|
+
${rating
|
|
447
|
+
? html`
|
|
448
|
+
<span class="pf-agd-rate-current">
|
|
449
|
+
<span class="pf-agd-rate-current-stars">${'★★★★★'.slice(0, rating.stars)}${'☆☆☆☆☆'.slice(0, 5 - rating.stars)}</span>
|
|
450
|
+
<span class="pf-agd-rate-current-ctx">${t(`profile.agents.detail.quality.contexts.${rating.context}`)}</span>
|
|
451
|
+
${rating.comment && html`<span class="pf-agd-rate-current-comment">${rating.comment}</span>`}
|
|
452
|
+
</span>
|
|
453
|
+
<button class="btn-ghost btn-sm" onClick=${handleOpenRate}>${t('profile.agents.tasks.rate.rerate')}</button>
|
|
454
|
+
`
|
|
455
|
+
: html`<button class="btn-outline btn-sm" onClick=${handleOpenRate}>${t('profile.agents.tasks.rate.button')}</button>`}
|
|
456
|
+
</div>
|
|
457
|
+
`}
|
|
458
|
+
|
|
412
459
|
${task.scope && html`
|
|
413
460
|
<div class="pf-agd-info-row">
|
|
414
461
|
<span class="pf-agd-info-label">${t('profile.agents.detail.tasks.scope')}</span>
|
|
@@ -539,17 +586,42 @@ function TaskItem({ task, agentName, showToast, onRefresh }) {
|
|
|
539
586
|
onSubmit=${handleSubmitRevision}
|
|
540
587
|
submitting=${sendingRevision}
|
|
541
588
|
/>
|
|
589
|
+
<${RateModal}
|
|
590
|
+
open=${showRateModal}
|
|
591
|
+
onClose=${() => setShowRateModal(false)}
|
|
592
|
+
onSubmit=${handleSubmitRate}
|
|
593
|
+
submitting=${sendingRate}
|
|
594
|
+
existing=${rating}
|
|
595
|
+
/>
|
|
542
596
|
</div>
|
|
543
597
|
`}
|
|
544
598
|
</div>
|
|
545
599
|
`;
|
|
546
600
|
}
|
|
547
601
|
|
|
548
|
-
export default function AgentTasksSubtab({ agentName, showToast }) {
|
|
602
|
+
export default function AgentTasksSubtab({ agent, agentName, showToast }) {
|
|
549
603
|
const [tasks, setTasks] = useState(null);
|
|
550
604
|
const [showCreate, setShowCreate] = useState(false);
|
|
551
605
|
const [error, setError] = useState(null);
|
|
552
606
|
const [filter, setFilter] = useState('all');
|
|
607
|
+
// Runner concurrency config (default 1 = serial). Saved to the agent via PATCH.
|
|
608
|
+
const [maxConcurrent, setMaxConcurrent] = useState(agent?.max_concurrent_tasks ?? 1);
|
|
609
|
+
const [savingConcurrency, setSavingConcurrency] = useState(false);
|
|
610
|
+
|
|
611
|
+
async function handleSaveConcurrency(next) {
|
|
612
|
+
const n = Math.max(1, Math.min(20, parseInt(next, 10) || 1));
|
|
613
|
+
setMaxConcurrent(n);
|
|
614
|
+
if (n === (agent?.max_concurrent_tasks ?? 1)) return;
|
|
615
|
+
setSavingConcurrency(true);
|
|
616
|
+
try {
|
|
617
|
+
await setMaxConcurrentTasks(agentName, n);
|
|
618
|
+
if (agent) agent.max_concurrent_tasks = n;
|
|
619
|
+
showToast?.(t('profile.agents.tasks.concurrency.saved'));
|
|
620
|
+
} catch (err) {
|
|
621
|
+
showToast?.(err.message || t('profile.agents.tasks.concurrency.error'), true);
|
|
622
|
+
}
|
|
623
|
+
setSavingConcurrency(false);
|
|
624
|
+
}
|
|
553
625
|
|
|
554
626
|
async function loadTasks() {
|
|
555
627
|
try {
|
|
@@ -605,6 +677,23 @@ export default function AgentTasksSubtab({ agentName, showToast }) {
|
|
|
605
677
|
</button>
|
|
606
678
|
</div>
|
|
607
679
|
|
|
680
|
+
<div class="pf-agd-concurrency">
|
|
681
|
+
<label class="pf-agd-concurrency-label" for="pf-agd-mct-${agentName}">
|
|
682
|
+
${t('profile.agents.tasks.concurrency.label')}
|
|
683
|
+
</label>
|
|
684
|
+
<input
|
|
685
|
+
id="pf-agd-mct-${agentName}"
|
|
686
|
+
class="pf-agd-concurrency-input"
|
|
687
|
+
type="number"
|
|
688
|
+
min="1"
|
|
689
|
+
max="20"
|
|
690
|
+
value=${maxConcurrent}
|
|
691
|
+
disabled=${savingConcurrency}
|
|
692
|
+
onChange=${e => handleSaveConcurrency(e.target.value)}
|
|
693
|
+
/>
|
|
694
|
+
<span class="pf-agd-concurrency-hint">${t('profile.agents.tasks.concurrency.hint')}</span>
|
|
695
|
+
</div>
|
|
696
|
+
|
|
608
697
|
${showCreate && html`
|
|
609
698
|
<${TaskCreateForm} agentName=${agentName} showToast=${showToast} onCreated=${handleCreated} onCancel=${() => setShowCreate(false)} />
|
|
610
699
|
`}
|