@contraagent/ui 0.8.0 → 0.9.0
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/README.md +1 -1
- package/custom-elements.json +104 -78
- package/dist/adapters/journal-columns.d.ts +2 -0
- package/dist/adapters/journal-columns.d.ts.map +1 -1
- package/dist/adapters/journal-columns.js +19 -6
- package/dist/adapters/journal-columns.js.map +1 -1
- package/dist/adapters/kpi-tiles.d.ts +8 -3
- package/dist/adapters/kpi-tiles.d.ts.map +1 -1
- package/dist/adapters/kpi-tiles.js +35 -15
- package/dist/adapters/kpi-tiles.js.map +1 -1
- package/dist/adapters/transcript/sections.d.ts +5 -0
- package/dist/adapters/transcript/sections.d.ts.map +1 -1
- package/dist/adapters/transcript/sections.js +7 -1
- package/dist/adapters/transcript/sections.js.map +1 -1
- package/dist/contraagent-ui.js +689 -700
- package/dist/elements/lists/ca-contracts.d.ts +3 -4
- package/dist/elements/lists/ca-contracts.d.ts.map +1 -1
- package/dist/elements/lists/ca-contracts.js +25 -19
- package/dist/elements/lists/ca-contracts.js.map +1 -1
- package/dist/elements/overview/ca-kpis.d.ts +3 -1
- package/dist/elements/overview/ca-kpis.d.ts.map +1 -1
- package/dist/elements/overview/ca-kpis.js +7 -2
- package/dist/elements/overview/ca-kpis.js.map +1 -1
- package/dist/elements/overview/ca-model-usage.d.ts +2 -1
- package/dist/elements/overview/ca-model-usage.d.ts.map +1 -1
- package/dist/elements/overview/ca-model-usage.js +3 -2
- package/dist/elements/overview/ca-model-usage.js.map +1 -1
- package/dist/elements/overview/ca-overview.d.ts +1 -5
- package/dist/elements/overview/ca-overview.d.ts.map +1 -1
- package/dist/elements/overview/ca-overview.js +53 -77
- package/dist/elements/overview/ca-overview.js.map +1 -1
- package/dist/elements/overview/ca-pipeline-graph.d.ts +8 -1
- package/dist/elements/overview/ca-pipeline-graph.d.ts.map +1 -1
- package/dist/elements/overview/ca-pipeline-graph.js +11 -6
- package/dist/elements/overview/ca-pipeline-graph.js.map +1 -1
- package/dist/elements/overview/ca-run-driver.d.ts.map +1 -1
- package/dist/elements/overview/ca-run-driver.js +6 -5
- package/dist/elements/overview/ca-run-driver.js.map +1 -1
- package/dist/elements/overview/ca-workers.js +1 -1
- package/dist/elements/overview/pipeline-graph/strings.js +3 -3
- package/dist/elements/overview/pipeline-graph/strings.js.map +1 -1
- package/dist/elements/shared/ca-definition.d.ts.map +1 -1
- package/dist/elements/shared/ca-definition.js +1 -2
- package/dist/elements/shared/ca-definition.js.map +1 -1
- package/dist/elements/shell/ca-hosts.d.ts.map +1 -1
- package/dist/elements/shell/ca-hosts.js +1 -2
- package/dist/elements/shell/ca-hosts.js.map +1 -1
- package/dist/elements/shell/ca-monitor.d.ts +3 -1
- package/dist/elements/shell/ca-monitor.d.ts.map +1 -1
- package/dist/elements/shell/ca-monitor.js +20 -9
- package/dist/elements/shell/ca-monitor.js.map +1 -1
- package/dist/elements/shell/ca-run-picker.js +1 -1
- package/dist/elements/shell/ca-run-picker.js.map +1 -1
- package/dist/elements/shell/ca-theme-switch.d.ts.map +1 -1
- package/dist/elements/shell/ca-theme-switch.js +6 -1
- package/dist/elements/shell/ca-theme-switch.js.map +1 -1
- package/dist/elements/task/ca-task-view.js +2 -2
- package/dist/elements/task/ca-task-view.js.map +1 -1
- package/dist/elements/task/ca-transcript.d.ts +3 -0
- package/dist/elements/task/ca-transcript.d.ts.map +1 -1
- package/dist/elements/task/ca-transcript.js +9 -2
- package/dist/elements/task/ca-transcript.js.map +1 -1
- package/dist/elements/task/header.d.ts +3 -0
- package/dist/elements/task/header.d.ts.map +1 -1
- package/dist/elements/task/header.js +22 -13
- package/dist/elements/task/header.js.map +1 -1
- package/dist/elements/task/panes/io.d.ts +1 -1
- package/dist/elements/task/panes/io.d.ts.map +1 -1
- package/dist/elements/task/panes/io.js +14 -21
- package/dist/elements/task/panes/io.js.map +1 -1
- package/dist/elements/trends/ca-trends.d.ts.map +1 -1
- package/dist/elements/trends/ca-trends.js +2 -1
- package/dist/elements/trends/ca-trends.js.map +1 -1
- package/dist/locales/ja.d.ts +16 -29
- package/dist/locales/ja.d.ts.map +1 -1
- package/dist/locales/ja.js +16 -29
- package/dist/locales/ja.js.map +1 -1
- package/dist/words/quota.d.ts +2 -0
- package/dist/words/quota.d.ts.map +1 -1
- package/dist/words/quota.js +4 -0
- package/dist/words/quota.js.map +1 -1
- package/dist/words/run-line.d.ts +5 -3
- package/dist/words/run-line.d.ts.map +1 -1
- package/dist/words/run-line.js +6 -4
- package/dist/words/run-line.js.map +1 -1
- package/dist/words/trends.d.ts +2 -2
- package/dist/words/trends.d.ts.map +1 -1
- package/dist/words/trends.js +5 -3
- package/dist/words/trends.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/journal-columns.ts +20 -6
- package/src/adapters/kpi-tiles.ts +37 -16
- package/src/adapters/transcript/sections.ts +10 -2
- package/src/elements/lists/ca-contracts.ts +25 -19
- package/src/elements/overview/ca-kpis.ts +4 -2
- package/src/elements/overview/ca-model-usage.ts +3 -2
- package/src/elements/overview/ca-overview.ts +49 -79
- package/src/elements/overview/ca-pipeline-graph.ts +11 -6
- package/src/elements/overview/ca-run-driver.ts +5 -6
- package/src/elements/overview/ca-workers.ts +1 -1
- package/src/elements/overview/pipeline-graph/strings.ts +3 -3
- package/src/elements/shared/ca-definition.ts +1 -2
- package/src/elements/shell/ca-hosts.ts +1 -2
- package/src/elements/shell/ca-monitor.ts +19 -8
- package/src/elements/shell/ca-run-picker.ts +1 -1
- package/src/elements/shell/ca-theme-switch.ts +6 -1
- package/src/elements/task/ca-task-view.ts +2 -2
- package/src/elements/task/ca-transcript.ts +7 -2
- package/src/elements/task/header.ts +24 -14
- package/src/elements/task/panes/io.ts +14 -21
- package/src/elements/trends/ca-trends.ts +2 -1
- package/src/locales/ja.ts +16 -29
- package/src/words/quota.ts +6 -0
- package/src/words/run-line.ts +8 -5
- package/src/words/trends.ts +5 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contraagent/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Web components for the contraagent monitor: transcripts, task tables, the journal, plans, contracts. Framework-free (Lit), typed by @contraagent/api",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -109,8 +109,8 @@
|
|
|
109
109
|
"./dist/elements/**"
|
|
110
110
|
],
|
|
111
111
|
"dependencies": {
|
|
112
|
-
"@contraagent/api": "0.
|
|
113
|
-
"@contraagent/elements": "0.
|
|
112
|
+
"@contraagent/api": "0.9.0",
|
|
113
|
+
"@contraagent/elements": "0.9.0"
|
|
114
114
|
},
|
|
115
115
|
"peerDependencies": {
|
|
116
116
|
"@lit/localize": "^0.12.2",
|
|
@@ -36,9 +36,19 @@ export function shortStop(stop: string | null | undefined): string {
|
|
|
36
36
|
return stop === 'invalid_input' ? outcomeLabel('not_started') : stopLabel(stop);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/** The
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/** The identities an attempt authenticated as, in order; both when a model switch changed it. */
|
|
40
|
+
const identities = (e: JournalRow): string[] => [...new Set([...(e.hops ?? []).map((h) => h.credential), e.credential].filter((c): c is string => !!c))];
|
|
41
|
+
|
|
42
|
+
/** The one identity every attempt that ran authenticated as alone; '' when they differ or none is known. */
|
|
43
|
+
export function sharedIdentity(journal: readonly JournalRow[]): string {
|
|
44
|
+
const seen = new Set(journal.filter(isLaunched).map((e) => identities(e).join('\n')));
|
|
45
|
+
const [only] = seen;
|
|
46
|
+
return seen.size === 1 && only && !only.includes('\n') ? only : '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The identity the attempt authenticated as (both when a model switch changed it), unless `shared`: then the column's header names it. */
|
|
50
|
+
function identity(e: JournalRow, shared: string) {
|
|
51
|
+
const ids = identities(e);
|
|
42
52
|
if (!ids.length) return nothing;
|
|
43
53
|
// The subscription usage the attempt's first session started at, when the agent reported one.
|
|
44
54
|
const start = (e.quota ?? [])[0];
|
|
@@ -47,6 +57,7 @@ function identity(e: JournalRow) {
|
|
|
47
57
|
const own = start && ids.length === 1 && (start.credential ?? '') === ids[0];
|
|
48
58
|
const shown = start ? (own ? readingShort(start) : accountReading(start)) : '';
|
|
49
59
|
const tips = [tip, ...ids.map(credentialTip)].filter(Boolean).join('\n');
|
|
60
|
+
if (shared) return start ? html`<span class="sub" title=${tips || nothing}><span class="q">${shown}</span></span>` : nothing;
|
|
50
61
|
return html`<span class="sub" title=${tips || nothing}>${ids.map((c) => credentialLabel(c)).join(' → ')}${start ? html` <span class="q">${shown}</span>` : nothing}</span>`;
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -55,6 +66,8 @@ export function journalColumns(ctx: JournalColumnsContext): DataColumn<JournalRo
|
|
|
55
66
|
const maxWall = Math.max(1, ...ctx.rows.map((e) => e.wall_s || 0));
|
|
56
67
|
const tries = attemptFacts(ctx.journal);
|
|
57
68
|
const index = new Map(ctx.journal.map((r, i) => [r, i]));
|
|
69
|
+
// Every attempt ran as one identity: the header names it once, the rows do not.
|
|
70
|
+
const shared = sharedIdentity(ctx.journal);
|
|
58
71
|
/** The attempt that later did the task, after this one failed (0: none). */
|
|
59
72
|
const laterTry = (e: JournalRow): number => {
|
|
60
73
|
const f = tries.get(e.key);
|
|
@@ -133,11 +146,12 @@ export function journalColumns(ctx: JournalColumnsContext): DataColumn<JournalRo
|
|
|
133
146
|
},
|
|
134
147
|
{
|
|
135
148
|
id: 'agent',
|
|
136
|
-
header: msg('Agent, model, identity'),
|
|
149
|
+
header: shared ? msg('Agent, model') : msg('Agent, model, identity'),
|
|
150
|
+
...(shared ? { tip: [msg(str`Identity: ${credentialLabel(shared)}`), credentialTip(shared)].filter(Boolean).join('\n') } : {}),
|
|
137
151
|
width: 'minmax(150px,1fr)',
|
|
138
152
|
cell: (e) =>
|
|
139
153
|
html`<span class="jmono jdim two" title=${facts(e.route, modelSwitch(e.hops, null, e.skipped_hops)) || nothing}>${isLaunched(e)
|
|
140
|
-
? html`<span>${[e.agent, e.model].filter(Boolean).join(' / ') || '—'}</span>${identity(e)}`
|
|
154
|
+
? html`<span>${[e.agent, e.model].filter(Boolean).join(' / ') || '—'}</span>${identity(e, shared)}`
|
|
141
155
|
: '—'}</span>`,
|
|
142
156
|
},
|
|
143
157
|
{
|
|
@@ -196,7 +210,7 @@ export function journalColumns(ctx: JournalColumnsContext): DataColumn<JournalRo
|
|
|
196
210
|
>${safetyStopCount(stops.length)}</span
|
|
197
211
|
>`
|
|
198
212
|
: nothing}${unread
|
|
199
|
-
? html` <span class="unread nw" title=${msg(str`
|
|
213
|
+
? html` <span class="unread nw" title=${msg(str`Events this contraagent version could not read (unread_events): ${unread}`)}
|
|
200
214
|
><span aria-hidden="true">⚠</span> ${msg('Unrecognized events')}</span
|
|
201
215
|
>`
|
|
202
216
|
: nothing}${unknown
|
|
@@ -31,9 +31,10 @@ export interface Kpis {
|
|
|
31
31
|
/** Tries after a task's first, and the tasks they belong to. */
|
|
32
32
|
retries: number;
|
|
33
33
|
retriedTasks: number;
|
|
34
|
-
/** The keys of the tasks that were retried / had fix-ups (to open one directly when it is the only one). */
|
|
34
|
+
/** The keys of the tasks that were retried / had fix-ups / switched models (to name or open one directly when it is the only one). */
|
|
35
35
|
retriedKeys: string[];
|
|
36
36
|
fixupKeys: string[];
|
|
37
|
+
switchKeys: string[];
|
|
37
38
|
/** Fix-ups (sessions resumed with check errors), and the tasks that had any. */
|
|
38
39
|
fixups: number;
|
|
39
40
|
fixupTasks: number;
|
|
@@ -67,6 +68,7 @@ export function kpis(journal: readonly JournalRow[]): Kpis {
|
|
|
67
68
|
fixupTasks: tasks(fixed),
|
|
68
69
|
switches: switched.reduce((a, e) => a + (e.hops || []).length - 1, 0),
|
|
69
70
|
switchTasks: tasks(switched),
|
|
71
|
+
switchKeys: keys(switched),
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -100,21 +102,40 @@ function notStartedCauses(outcomes: Iterable<TaskOutcome>): string[] {
|
|
|
100
102
|
/** The history filtered by `stop`, named `label`. */
|
|
101
103
|
const history = (stop: string, label: string): StatTile['action'] => ({ id: `history:${stop}`, label });
|
|
102
104
|
|
|
103
|
-
/** The
|
|
104
|
-
function
|
|
105
|
+
/** The one task behind a count, by key and name; undefined when there are several. */
|
|
106
|
+
function onlyTask(journal: readonly JournalRow[], keys: readonly string[]): { key: string; name: string } | undefined {
|
|
105
107
|
const only = keys.length === 1 ? keys[0] : undefined;
|
|
106
|
-
if (!only) return
|
|
108
|
+
if (!only) return undefined;
|
|
107
109
|
const row = journal.find((r) => r.key === only);
|
|
108
|
-
return {
|
|
110
|
+
return { key: only, name: row ? kindUnit(row) : only };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The tasks behind a count: the task itself when it is one (named), else the history filtered to them (`label`). */
|
|
114
|
+
function show(journal: readonly JournalRow[], keys: readonly string[], stop: string, label: string): StatTile['action'] {
|
|
115
|
+
const one = onlyTask(journal, keys);
|
|
116
|
+
return one ? { id: `task:${one.key}`, label: one.name } : history(stop, label);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The line under a count of events: the one task they belong to by name, else the
|
|
121
|
+
* count of tasks (`many`) when it differs from the count of events; none at 0.
|
|
122
|
+
*/
|
|
123
|
+
function tasksLine(journal: readonly JournalRow[], count: number, keys: readonly string[], many: (n: number) => string): string[][] {
|
|
124
|
+
if (!count) return [];
|
|
125
|
+
const one = onlyTask(journal, keys);
|
|
126
|
+
if (one) return [[one.name]];
|
|
127
|
+
return keys.length === count ? [] : [[many(keys.length)]];
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
/**
|
|
112
131
|
* The tiles: tasks (with `d` from the plan; from the journal alone otherwise),
|
|
113
132
|
* attempts, cost and tokens (when reported), failed attempts, not started,
|
|
114
133
|
* agent time, retries, corrections and model switches. Neutral by default:
|
|
115
|
-
* only a problem count above 0 takes its tone (`problemTone`).
|
|
134
|
+
* only a problem count above 0 takes its tone (`problemTone`). A tile's lines
|
|
135
|
+
* add facts the value does not give: a 0 has none. `highestCost: false` leaves
|
|
136
|
+
* the kind with the highest cost out of the cost tile.
|
|
116
137
|
*/
|
|
117
|
-
export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'outcomes' | 'counts'
|
|
138
|
+
export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'outcomes' | 'counts'>, opts: { highestCost?: boolean } = {}): StatTile[] {
|
|
118
139
|
const k = kpis(journal);
|
|
119
140
|
const { outcomes, counts: c } = d;
|
|
120
141
|
// The total is neutral; the parts that are problems carry their own status.
|
|
@@ -135,13 +156,13 @@ export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'ou
|
|
|
135
156
|
sub: [factList(k.launches === 1 ? msg('1 agent session') : msg(str`${k.launches} agent sessions`), k.python === 1 ? msg('1 Python task attempt') : msg(str`${k.python} Python task attempts`))],
|
|
136
157
|
tip: msg('Attempts that ran, retries included (journal.jsonl)'),
|
|
137
158
|
},
|
|
138
|
-
...spendTiles(journal),
|
|
159
|
+
...spendTiles(journal, opts.highestCost ?? true),
|
|
139
160
|
{
|
|
140
161
|
id: 'bad',
|
|
141
162
|
tone: problemTone(k.bad, 'failed'),
|
|
142
163
|
label: msg('Failed attempts'),
|
|
143
164
|
value: k.bad,
|
|
144
|
-
sub: [
|
|
165
|
+
sub: [],
|
|
145
166
|
...(k.bad ? { action: history('bad', msg('Failed attempts in history')) } : {}),
|
|
146
167
|
},
|
|
147
168
|
{
|
|
@@ -149,7 +170,7 @@ export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'ou
|
|
|
149
170
|
tone: problemTone(c.not_started, 'attention'),
|
|
150
171
|
label: msg('Not started'),
|
|
151
172
|
value: c.not_started,
|
|
152
|
-
sub:
|
|
173
|
+
sub: c.not_started ? [notStartedCauses(outcomes.values())] : [],
|
|
153
174
|
...(c.not_started ? { action: history('invalid_input', msg('Tasks not started in history')) } : {}),
|
|
154
175
|
},
|
|
155
176
|
{
|
|
@@ -163,7 +184,7 @@ export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'ou
|
|
|
163
184
|
id: 'retry',
|
|
164
185
|
label: msg('Retries'),
|
|
165
186
|
value: k.retries,
|
|
166
|
-
sub:
|
|
187
|
+
sub: tasksLine(journal, k.retries, k.retriedKeys, (n) => msg(str`${n} tasks retried`)),
|
|
167
188
|
tip: retryTip(),
|
|
168
189
|
...(k.retries ? { action: show(journal, k.retriedKeys, 'retried', msg('Retried tasks in history')) } : {}),
|
|
169
190
|
},
|
|
@@ -171,7 +192,7 @@ export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'ou
|
|
|
171
192
|
id: 'ct',
|
|
172
193
|
label: msg('Corrections'),
|
|
173
194
|
value: k.fixups,
|
|
174
|
-
sub:
|
|
195
|
+
sub: tasksLine(journal, k.fixups, k.fixupKeys, (n) => msg(str`${n} tasks with corrections`)),
|
|
175
196
|
tip: correctionTip(),
|
|
176
197
|
...(k.fixups ? { action: show(journal, k.fixupKeys, 'fixup', msg('Attempts with corrections in history')) } : {}),
|
|
177
198
|
},
|
|
@@ -179,14 +200,14 @@ export function kpiTiles(journal: readonly JournalRow[], d: Pick<RunDerived, 'ou
|
|
|
179
200
|
id: 'fo',
|
|
180
201
|
label: msg('Model switches'),
|
|
181
202
|
value: k.switches,
|
|
182
|
-
sub:
|
|
203
|
+
sub: tasksLine(journal, k.switches, k.switchKeys, (n) => msg(str`${n} tasks with model switches`)),
|
|
183
204
|
tip: modelSwitchTip(),
|
|
184
205
|
},
|
|
185
206
|
];
|
|
186
207
|
}
|
|
187
208
|
|
|
188
|
-
/** Cost and tokens, when any attempt reported them
|
|
189
|
-
function spendTiles(journal: readonly JournalRow[]): StatTile[] {
|
|
209
|
+
/** Cost and tokens, when any attempt reported them; the cost tile names the kind with the highest cost when `highest`. */
|
|
210
|
+
function spendTiles(journal: readonly JournalRow[], highest: boolean): StatTile[] {
|
|
190
211
|
const s = spendOf(journal);
|
|
191
212
|
if (!s) return [];
|
|
192
213
|
const top = spendByKind(journal)[0];
|
|
@@ -195,7 +216,7 @@ function spendTiles(journal: readonly JournalRow[]): StatTile[] {
|
|
|
195
216
|
id: 'cost',
|
|
196
217
|
label: msg('Cost'),
|
|
197
218
|
value: usd(s.cost),
|
|
198
|
-
sub: top && top.share !== null ? [[msg(str`Highest cost: ${top.kind}`)], factList(usd(top.spend.cost), msg(str`${Math.round(top.share * 100)}% of the total`))] : [[]],
|
|
219
|
+
sub: highest && top && top.share !== null ? [[msg(str`Highest cost: ${top.kind}`)], factList(usd(top.spend.cost), msg(str`${Math.round(top.share * 100)}% of the total`))] : [[]],
|
|
199
220
|
tip: msg('Reported by the coding agent'),
|
|
200
221
|
},
|
|
201
222
|
{
|
|
@@ -9,7 +9,7 @@ import { attemptNames } from '../../words/attempts.js';
|
|
|
9
9
|
import { roundLabel, roundMark, switchText } from '../../words/rounds.js';
|
|
10
10
|
import { stopLabel } from '../../words/stop.js';
|
|
11
11
|
import type { Transcript } from './model.js';
|
|
12
|
-
import { whoTags } from './who.js';
|
|
12
|
+
import { whoTags, type Who } from './who.js';
|
|
13
13
|
|
|
14
14
|
export interface SectionsContext {
|
|
15
15
|
/** The run id: paths in a reason are shown relative to it. */
|
|
@@ -18,6 +18,14 @@ export interface SectionsContext {
|
|
|
18
18
|
rows: readonly JournalRow[];
|
|
19
19
|
/** The latest round is still being written (the task runs and its transcript is tailed). */
|
|
20
20
|
active: boolean;
|
|
21
|
+
/** Who the page names already (the task's header): a section's head shows only what differs from it. */
|
|
22
|
+
shownWho?: Who | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What of `who` differs from `shown`: the agent, model and role the reader has not seen yet. */
|
|
26
|
+
export function whoBeyond(who: Who | null | undefined, shown: Who | null | undefined): Who | null {
|
|
27
|
+
if (!who || !shown) return who ?? null;
|
|
28
|
+
return { agent: who.agent === shown.agent ? '' : who.agent, model: who.model === shown.model ? '' : who.model, role: who.role === shown.role ? '' : who.role };
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
/** The command line, and the prompt with what was not saved of it. */
|
|
@@ -72,7 +80,7 @@ export function transcriptSections(t: Transcript, rounds: readonly RoundInfo[],
|
|
|
72
80
|
facts: model ? [model.replace(/^claude-/, '')] : [],
|
|
73
81
|
verdict: mark ? { tone: mark === '✓' ? 'done' : 'failed', text: word, tip } : undefined,
|
|
74
82
|
active: i === last && ctx.active,
|
|
75
|
-
tags: r.launch ? whoTags(r.who) : [],
|
|
83
|
+
tags: r.launch ? whoTags(whoBeyond(r.who, ctx.shownWho)) : [],
|
|
76
84
|
details: r.launch ? launchDetails(r.launch) : [],
|
|
77
85
|
// the model leaves an event's title to the reader's words: the only event is an API request sent again
|
|
78
86
|
items: r.items.map((it): TranscriptItem => (it.type === 'event' ? { ...it, title: msg('API request sent again') } : it)),
|
|
@@ -8,19 +8,16 @@ import { RememberedSet } from '../../core/memory.js';
|
|
|
8
8
|
import type { TaskView } from '../../core/view-route.js';
|
|
9
9
|
import { base } from '@contraagent/elements/styles';
|
|
10
10
|
import { executorIcon, executorIconStyles } from '../../words/executor.js';
|
|
11
|
-
import { kindUnit } from '../../words/format.js';
|
|
12
|
-
import { correctionRoundTip, correctionRoundsLabel, notRunText, type Producer } from '../../words/contract.js';
|
|
11
|
+
import { facts, kindUnit } from '../../words/format.js';
|
|
12
|
+
import { contractStatusLabel, correctionRoundTip, correctionRoundsLabel, notRunText, type Producer } from '../../words/contract.js';
|
|
13
13
|
import { contractChip, contractStyles, fieldCheckTable, fileConditions, inputChip } from '../shared/contract-parts.js';
|
|
14
14
|
|
|
15
15
|
const CT_ORDER: readonly ContractStatus[] = ['invalid', 'refused', 'failed', 'running', 'pending', 'valid'];
|
|
16
16
|
|
|
17
|
-
/** Output contracts per status, worst first
|
|
18
|
-
|
|
17
|
+
/** Output contracts per status that is not passed, worst first: the passed ones head their own group. */
|
|
18
|
+
function ctChips(outputs: Outputs | null) {
|
|
19
19
|
const t = outputs?.tally;
|
|
20
|
-
|
|
21
|
-
return chips.length
|
|
22
|
-
? chips.map((k) => contractChip(k, t?.[k]))
|
|
23
|
-
: html`<span class="meta">${msg('No checked files')}</span>`;
|
|
20
|
+
return (t ? CT_ORDER.filter((k) => k !== 'valid' && t[k]) : []).map((k) => contractChip(k, t?.[k]));
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
const shortPath = (p: string) => p.split('/').slice(-2).join('/');
|
|
@@ -34,8 +31,9 @@ const rank = (s: ContractStatus | null | undefined) => (s ? CT_ORDER.indexOf(s)
|
|
|
34
31
|
|
|
35
32
|
/**
|
|
36
33
|
* The files each task must write (`OutputContract`) and read (`InputContract`),
|
|
37
|
-
* problems first, each
|
|
38
|
-
*
|
|
34
|
+
* problems first, each with the chip of its status, then the passed ones under
|
|
35
|
+
* one chip; each checked field by field against its schema. Links open the
|
|
36
|
+
* task (`ca-open-task`).
|
|
39
37
|
*
|
|
40
38
|
* @fires ca-open-task - `{key, view}`: a link to the task's input, output or transcript
|
|
41
39
|
*/
|
|
@@ -49,6 +47,8 @@ export class CaContracts extends LitElement {
|
|
|
49
47
|
css`
|
|
50
48
|
:host { display: block; container-type: inline-size; }
|
|
51
49
|
.ct-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
|
|
50
|
+
/* the passed outputs: one chip heads them, their rows carry none */
|
|
51
|
+
.passed { margin: 6px 0 0; padding: 10px 0 8px; font: inherit; border-top: 1px solid var(--line); }
|
|
52
52
|
details.ct { border-top: 1px solid var(--line); }
|
|
53
53
|
details.ct > summary { list-style: none; cursor: pointer; padding: 7px 4px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
|
|
54
54
|
details.ct > summary > * { min-width: 0; overflow-wrap: anywhere; }
|
|
@@ -85,24 +85,30 @@ export class CaContracts extends LitElement {
|
|
|
85
85
|
const cs = this.outputs?.contracts ?? [];
|
|
86
86
|
this.outcomes = this.plan ? deriveRun(this.journal, this.plan).outcomes : new Map<string, TaskOutcome>();
|
|
87
87
|
if (!cs.length) {
|
|
88
|
-
return html`<div class="blank"><span class="ic" aria-hidden="true">▣</span><b>${msg('No checked files')}</b
|
|
89
|
-
<span>${msg('No task declares an OutputContract or an InputContract')}</span></div>`;
|
|
88
|
+
return html`<div class="blank"><span class="ic" aria-hidden="true">▣</span><b>${facts(msg('No checked files'), msg('no task declares an OutputContract or an InputContract'))}</b></div>`;
|
|
90
89
|
}
|
|
91
90
|
const outs = cs
|
|
92
91
|
.flatMap((c) => (c.outputs ?? []).map((o) => [c, o] as const))
|
|
93
92
|
.sort(([a, x], [b, y]) => rank(x.status) - rank(y.status) || a.key.localeCompare(b.key));
|
|
94
93
|
const ins = cs.flatMap((c) => (c.inputs ?? []).map((i) => [c, i] as const));
|
|
94
|
+
const others = outs.filter(([, o]) => o.status !== 'valid');
|
|
95
|
+
const passed = outs.filter(([, o]) => o.status === 'valid');
|
|
96
|
+
const chips = ctChips(this.outputs);
|
|
95
97
|
return html`<div class="g12">
|
|
96
98
|
<section class="panel c12">
|
|
97
|
-
<h3 title=${msg('OutputContract')}>${msg('Outputs')}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
${
|
|
99
|
+
<h3 title=${msg('Files each task must write (OutputContract)')}>${msg('Outputs')}</h3>
|
|
100
|
+
${chips.length ? html`<div class="ct-row">${chips}</div>` : nothing}
|
|
101
|
+
${repeat(others, ([c, o]) => `out|${c.key}|${o.path}`, ([c, o]) => this.outRow(c, o))}
|
|
102
|
+
${passed.length
|
|
103
|
+
? html`<h4 class="passed">${contractChip('valid', passed.length)}</h4>
|
|
104
|
+
<div role="group" aria-label=${contractStatusLabel('valid')}>
|
|
105
|
+
${repeat(passed, ([c, o]) => `out|${c.key}|${o.path}`, ([c, o]) => this.outRow(c, o))}
|
|
106
|
+
</div>`
|
|
107
|
+
: nothing}
|
|
101
108
|
</section>
|
|
102
109
|
${ins.length
|
|
103
110
|
? html`<section class="panel c12">
|
|
104
|
-
<h3 title=${msg('InputContract')}>${msg('Inputs')}
|
|
105
|
-
<span class="r">${msg('Files each task reads')}</span></h3>
|
|
111
|
+
<h3 title=${msg('Files each task reads (InputContract)')}>${msg('Inputs')}</h3>
|
|
106
112
|
${repeat(ins, ([c, i]) => `in|${c.key}|${i.path}`, ([c, i]) => this.inRow(c, i))}
|
|
107
113
|
</section>`
|
|
108
114
|
: nothing}
|
|
@@ -165,7 +171,7 @@ export class CaContracts extends LitElement {
|
|
|
165
171
|
// The chip says why there is no file; the size column shows a size only when there is one.
|
|
166
172
|
return this.details(
|
|
167
173
|
`out|${c.key}|${o.path}`,
|
|
168
|
-
html`${contractChip(status)}
|
|
174
|
+
html`${status === 'valid' ? nothing : contractChip(status)}
|
|
169
175
|
${rounds > 1 ? html`<span class="meta" title=${correctionRoundTip()}>${correctionRoundsLabel(rounds, status === 'valid')}</span>` : nothing}
|
|
170
176
|
${this.who(c)}
|
|
171
177
|
<span class="jmono jdim ct-path" title=${o.path}>${shortPath(o.path)}</span>
|
|
@@ -19,7 +19,7 @@ import { emit } from '../../core/events.js';
|
|
|
19
19
|
* @csspart label - a tile's label
|
|
20
20
|
* @csspart value - a tile's value
|
|
21
21
|
* @csspart sub - each line under the value
|
|
22
|
-
* @csspart action - a tile's link
|
|
22
|
+
* @csspart action - a tile's link, over the whole tile
|
|
23
23
|
*/
|
|
24
24
|
@customElement('ca-kpis')
|
|
25
25
|
@localized()
|
|
@@ -31,6 +31,8 @@ export class CaKpis extends LitElement {
|
|
|
31
31
|
@property({ attribute: false }) journal: JournalRow[] = [];
|
|
32
32
|
/** The scheduled tasks; without it, tasks are counted from the journal. */
|
|
33
33
|
@property({ attribute: false }) plan: Plan = { nodes: [], groups: [] };
|
|
34
|
+
/** Leaves the kind with the highest cost out of the cost tile, for a page that shows the cost by kind beside it. */
|
|
35
|
+
@property({ type: Boolean, attribute: 'no-highest-cost' }) noHighestCost = false;
|
|
34
36
|
|
|
35
37
|
private readonly onAction = (e: CustomEvent<{ action: string }>): void => {
|
|
36
38
|
e.stopPropagation();
|
|
@@ -40,7 +42,7 @@ export class CaKpis extends LitElement {
|
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
override render() {
|
|
43
|
-
return html`<cae-stat-tiles exportparts="tiles, tile, label, value, sub, action" .tiles=${kpiTiles(this.journal, deriveRun(this.journal, this.plan))} @cae-action=${this.onAction}></cae-stat-tiles>`;
|
|
45
|
+
return html`<cae-stat-tiles exportparts="tiles, tile, label, value, sub, action" .tiles=${kpiTiles(this.journal, deriveRun(this.journal, this.plan), { highestCost: !this.noHighestCost })} @cae-action=${this.onAction}></cae-stat-tiles>`;
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -16,7 +16,8 @@ const COLUMNS: readonly ModelMeasure[] = ['cost', 'tokens', 'time', 'sessions'];
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* What each model was used for in a run (`Plan.models`): the share of each model in
|
|
19
|
-
* the chosen measure (cost, tokens, time or agent sessions) as one bar
|
|
19
|
+
* the chosen measure (cost, tokens, time or agent sessions) as one bar (the table
|
|
20
|
+
* names each segment, so the bar has no names under it), and a table
|
|
20
21
|
* of every model with its share and every measure, and its peak context when
|
|
21
22
|
* a session on it reported one. A model keeps its colour, and
|
|
22
23
|
* its place, in the order it was first used, whatever the measure; past six models
|
|
@@ -74,7 +75,7 @@ export class CaModelUsage extends LitElement {
|
|
|
74
75
|
@cae-change=${(e: CustomEvent<{ value: string }>) => (this.measure = e.detail.value as ModelMeasure)}></cae-segmented>
|
|
75
76
|
</div>`
|
|
76
77
|
: nothing}
|
|
77
|
-
${shareBar(shareParts(this.models, measure), { label: measureLabel(measure) })}
|
|
78
|
+
${shareBar(shareParts(this.models, measure), { label: measureLabel(measure), names: false })}
|
|
78
79
|
<div class="wrap" data-scroll="x" tabindex="0" role="region" aria-label=${msg('Models')}>
|
|
79
80
|
<table>
|
|
80
81
|
<thead>
|