@contraagent/ui 0.7.2 → 0.8.1
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 +105 -79
- package/dist/adapters/filters.d.ts +7 -1
- package/dist/adapters/filters.d.ts.map +1 -1
- package/dist/adapters/filters.js +8 -2
- package/dist/adapters/filters.js.map +1 -1
- 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 +20 -7
- 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 +38 -17
- 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 +687 -697
- 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 +26 -20
- 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/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/shared/file-card.d.ts.map +1 -1
- package/dist/elements/shared/file-card.js +2 -2
- package/dist/elements/shared/file-card.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 +11 -4
- package/dist/elements/task/ca-transcript.js.map +1 -1
- package/dist/elements/task/context.d.ts +1 -3
- package/dist/elements/task/context.d.ts.map +1 -1
- package/dist/elements/task/context.js +2 -1
- package/dist/elements/task/context.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 +25 -14
- 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 +21 -25
- 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 +25 -32
- package/dist/locales/ja.d.ts.map +1 -1
- package/dist/locales/ja.js +25 -32
- package/dist/locales/ja.js.map +1 -1
- package/dist/words/contract.d.ts +4 -2
- package/dist/words/contract.d.ts.map +1 -1
- package/dist/words/contract.js +7 -5
- package/dist/words/contract.js.map +1 -1
- package/dist/words/index.d.ts +1 -0
- package/dist/words/index.d.ts.map +1 -1
- package/dist/words/index.js +1 -0
- package/dist/words/index.js.map +1 -1
- package/dist/words/lineage.d.ts +11 -0
- package/dist/words/lineage.d.ts.map +1 -1
- package/dist/words/lineage.js +13 -1
- package/dist/words/lineage.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/rounds.d.ts +2 -2
- package/dist/words/rounds.js +4 -4
- package/dist/words/rounds.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/dist/words/working-directory.d.ts +5 -0
- package/dist/words/working-directory.d.ts.map +1 -0
- package/dist/words/working-directory.js +7 -0
- package/dist/words/working-directory.js.map +1 -0
- package/package.json +3 -3
- package/src/adapters/filters.ts +10 -2
- package/src/adapters/journal-columns.ts +21 -7
- package/src/adapters/kpi-tiles.ts +40 -18
- package/src/adapters/transcript/sections.ts +10 -2
- package/src/elements/lists/ca-contracts.ts +26 -20
- 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/pipeline-graph/strings.ts +3 -3
- package/src/elements/shared/ca-definition.ts +1 -2
- package/src/elements/shared/file-card.ts +2 -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 +9 -4
- package/src/elements/task/context.ts +2 -1
- package/src/elements/task/header.ts +27 -15
- package/src/elements/task/panes/io.ts +21 -25
- package/src/elements/trends/ca-trends.ts +2 -1
- package/src/locales/ja.ts +25 -32
- package/src/words/contract.ts +9 -5
- package/src/words/index.ts +1 -0
- package/src/words/lineage.ts +16 -2
- package/src/words/quota.ts +6 -0
- package/src/words/rounds.ts +4 -4
- package/src/words/run-line.ts +8 -5
- package/src/words/trends.ts +5 -3
- package/src/words/working-directory.ts +8 -0
|
@@ -270,7 +270,7 @@ export class CaRunPicker extends LitElement {
|
|
|
270
270
|
</span>`
|
|
271
271
|
: page === 'hosts'
|
|
272
272
|
? html`<span aria-hidden="true">⊞</span><span class="nm">${msg('Hosts')}</span><span class="when page">${hostsLine(this.hosts)}</span>`
|
|
273
|
-
: html`<span aria-hidden="true">▥</span><span class="nm"
|
|
273
|
+
: html`<span aria-hidden="true">▥</span><span class="nm" title=${trendsTip()}>${trendsTitle()}</span>`}
|
|
274
274
|
</div>`;
|
|
275
275
|
}
|
|
276
276
|
}
|
|
@@ -38,6 +38,11 @@ export class CaThemeSwitch extends LitElement {
|
|
|
38
38
|
padding: 2px 10px 2px 7px; font-size: 11px; color: var(--text-muted); cursor: pointer; }
|
|
39
39
|
.trig:hover, .trig[aria-expanded='true'] { color: var(--text); border-color: var(--line2); }
|
|
40
40
|
.trig svg { width: 13px; height: 13px; }
|
|
41
|
+
/* on a phone the icon alone: the word stays the button's accessible name */
|
|
42
|
+
@media (max-width: 480px) {
|
|
43
|
+
.trig { padding: 4px 6px; }
|
|
44
|
+
.trig .lbl { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
45
|
+
}
|
|
41
46
|
.menu { position: absolute; right: 0; top: calc(100% + 8px); min-width: 236px; padding: 6px; z-index: 50;
|
|
42
47
|
background: var(--panel); border: 1px solid var(--line2); border-radius: 12px;
|
|
43
48
|
box-shadow: 0 18px 48px color-mix(in srgb, var(--shadow) 65%, transparent); animation: enter 0.16s var(--ease) both; }
|
|
@@ -148,7 +153,7 @@ export class CaThemeSwitch extends LitElement {
|
|
|
148
153
|
return html`<button type="button" class="trig" aria-haspopup="menu" aria-expanded=${this.open ? 'true' : 'false'} aria-controls="menu"
|
|
149
154
|
title=${msg(str`Theme: ${themeLabel(this.value)}`)} @click=${() => (this.open = !this.open)} @keydown=${(e: KeyboardEvent) => this.onTrigKey(e)}>
|
|
150
155
|
<svg viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6.25" fill="none" stroke="currentColor" stroke-width="1.5" /><path d="M8 1.75a6.25 6.25 0 0 1 0 12.5z" fill="currentColor" /></svg>
|
|
151
|
-
|
|
156
|
+
<span class="lbl">${msg('Theme')}</span>
|
|
152
157
|
</button>
|
|
153
158
|
${this.open
|
|
154
159
|
? html`<div id="menu" class="menu" role="menu" aria-label=${msg('Theme')} @keydown=${(e: KeyboardEvent) => this.onMenuKey(e)}>
|
|
@@ -12,7 +12,7 @@ import './ca-transcript.js';
|
|
|
12
12
|
import type { CaTranscript } from './ca-transcript.js';
|
|
13
13
|
import { tagStyles } from '@contraagent/elements/transcript';
|
|
14
14
|
import { isPy, type TaskContext } from './context.js';
|
|
15
|
-
import { taskHeader } from './header.js';
|
|
15
|
+
import { taskHeader, taskWho } from './header.js';
|
|
16
16
|
import { noTranscript } from './panes/transcript.js';
|
|
17
17
|
import { inputPane, outputPane } from './panes/io.js';
|
|
18
18
|
import { definitionPane } from './panes/definition.js';
|
|
@@ -112,7 +112,7 @@ export class CaTaskView extends LitElement {
|
|
|
112
112
|
const view = this.shownView(d);
|
|
113
113
|
return html`${taskHeader(c, key, (q, nth) => this.revealSearch(q, nth))} ${this.renderViews(d, view)}
|
|
114
114
|
${d.stream
|
|
115
|
-
? html`<ca-transcript .run=${this.run} .stream=${d.stream} base-url=${this.baseUrl} .client=${this.client} .live=${c.live}
|
|
115
|
+
? html`<ca-transcript .run=${this.run} .stream=${d.stream} base-url=${this.baseUrl} .client=${this.client} .live=${c.live} .shownWho=${taskWho(c)}
|
|
116
116
|
.rows=${d.rows} .round=${this.round} mode=${view === 'transcript' ? 'full' : view === 'input' ? 'tabs' : 'none'}
|
|
117
117
|
@ca-rounds=${(e: CustomEvent<RoundsChangedDetail>) => {
|
|
118
118
|
this.launches = e.detail.launches;
|
|
@@ -8,21 +8,23 @@ import { customElement, property, query, state } from 'lit/decorators.js';
|
|
|
8
8
|
import { keyed } from 'lit/directives/keyed.js';
|
|
9
9
|
import { classifyRounds } from '../../adapters/transcript/rounds.js';
|
|
10
10
|
import { transcriptSections } from '../../adapters/transcript/sections.js';
|
|
11
|
+
import type { Who } from '../../adapters/transcript/who.js';
|
|
11
12
|
import { StreamController } from '../../controllers/stream.js';
|
|
12
13
|
import { emit, type RoundInfo, type RoundsChangedDetail } from '../../core/events.js';
|
|
13
14
|
import { getLocale } from '../../core/i18n.js';
|
|
14
15
|
import { RememberedSet, SessionSet, memoryOf, pageMemory } from '../../core/memory.js';
|
|
15
16
|
import { promptText } from '../shared/prompt-text.js';
|
|
16
17
|
import { retryTip } from '../../words/attempts.js';
|
|
17
|
-
import {
|
|
18
|
+
import { correctionRoundTip } from '../../words/contract.js';
|
|
18
19
|
import { modelSwitchTip } from '../../words/hops.js';
|
|
19
20
|
|
|
20
21
|
/** The transcript's words, in the reader's language (call at render time). */
|
|
21
22
|
const strings = (): TranscriptStrings => ({
|
|
23
|
+
// the picker's accessible name: its buttons name each session
|
|
22
24
|
sections: msg('Agent sessions'),
|
|
23
25
|
sectionsTip: [
|
|
24
26
|
`${msg('Retry')}: ${retryTip()}`,
|
|
25
|
-
`${msg('Correction')}: ${
|
|
27
|
+
`${msg('Correction round')}: ${correctionRoundTip()}`,
|
|
26
28
|
`${msg('Model switch')}: ${modelSwitchTip()}`,
|
|
27
29
|
].join('\n'),
|
|
28
30
|
loadEarlier: (n) => (n === 1 ? msg('Show 1 earlier line') : msg(str`Show ${n} earlier lines`)),
|
|
@@ -90,6 +92,8 @@ export class CaTranscript extends LitElement {
|
|
|
90
92
|
@property({ attribute: false }) round: number | null = null;
|
|
91
93
|
/** The task's journal rows (finished tries): each round then says how it ended. */
|
|
92
94
|
@property({ attribute: false }) rows: JournalRow[] = [];
|
|
95
|
+
/** Who the page names already (a task's header): each round's head shows only the coding agent, model and role that differ. */
|
|
96
|
+
@property({ attribute: false }) shownWho: Who | null = null;
|
|
93
97
|
/** `full`; `tabs`: only the round picker (another view of the task is shown); `none`: hidden. */
|
|
94
98
|
@property({ reflect: true }) mode: 'full' | 'tabs' | 'none' = 'full';
|
|
95
99
|
|
|
@@ -144,14 +148,15 @@ export class CaTranscript extends LitElement {
|
|
|
144
148
|
/** The rounds and their sections, drawn again only when the transcript, the rows or the language changed. */
|
|
145
149
|
private sections(): { rounds: RoundInfo[]; sections: TranscriptSection[] } {
|
|
146
150
|
const active = this.live && this.feed.tailing;
|
|
147
|
-
const
|
|
151
|
+
const w = this.shownWho;
|
|
152
|
+
const key = [this.feed.version, this.rows, this.run, active, getLocale(), w?.agent, w?.model, w?.role];
|
|
148
153
|
if (this.drawn?.key.every((k, i) => k === key[i])) return this.drawn;
|
|
149
154
|
const t = this.feed.model;
|
|
150
155
|
const rounds = classifyRounds(
|
|
151
156
|
t.rounds.map((r) => r.launch),
|
|
152
157
|
this.rows,
|
|
153
158
|
);
|
|
154
|
-
this.drawn = { key, rounds, sections: transcriptSections(t, rounds, { run: this.run, rows: this.rows, active }) };
|
|
159
|
+
this.drawn = { key, rounds, sections: transcriptSections(t, rounds, { run: this.run, rows: this.rows, active, shownWho: w }) };
|
|
155
160
|
return this.drawn;
|
|
156
161
|
}
|
|
157
162
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type UpstreamTask,
|
|
17
17
|
} from '@contraagent/api';
|
|
18
18
|
import { html } from 'lit';
|
|
19
|
+
import { correctionCount } from '../../adapters/filters.js';
|
|
19
20
|
import { emit, type RoundInfo } from '../../core/events.js';
|
|
20
21
|
import { kindUnit } from '../../words/format.js';
|
|
21
22
|
import { correctionsText, inputCause, notStartedCause } from '../../words/index.js';
|
|
@@ -95,7 +96,7 @@ export function culprit(c: TaskContext): { key: string; kind: string; unit: stri
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
/** `N corrections` for a journal row whose attempt needed them ('' when none). */
|
|
98
|
-
export const correctionsOf = (r:
|
|
99
|
+
export const correctionsOf = (r: Pick<JournalRow, 'rounds' | 'hook_errors'>): string => correctionsText(correctionCount(r));
|
|
99
100
|
|
|
100
101
|
/** A task's name as a link that sends `ca-open-task`. */
|
|
101
102
|
export const taskLink = (c: TaskContext, u: { key: string; kind: string; unit: string }) =>
|
|
@@ -6,9 +6,9 @@ import { html, nothing, type TemplateResult } from 'lit';
|
|
|
6
6
|
import { toneAttr } from '@contraagent/elements';
|
|
7
7
|
import { pill } from '@contraagent/elements/primitives';
|
|
8
8
|
import { facts, fmtDur, kindUnit, listText, localTime, tokenCount, unreadEvents, usageUnknown, usageUnknownWords, usd } from '../../words/format.js';
|
|
9
|
-
import { attemptNames, deferredText, changedFiles, changedFilesTip, changeLabel, credentialLabel, credentialTip, locksLabel, locksTip, modelSwitch, outcomeLabel, priorityText, priorityTip, replayedLabel, replayedTip, stopLabel, type ChangeKind } from '../../words/index.js';
|
|
9
|
+
import { attemptNames, deferredText, changedFiles, changedFilesTip, changeLabel, credentialLabel, credentialTip, locksLabel, locksTip, modelSwitch, outcomeLabel, priorityText, priorityTip, replayedLabel, replayedTip, stopLabel, workingDirectoryText, workingDirectoryTip, type ChangeKind } from '../../words/index.js';
|
|
10
10
|
import { contextWarnedText, contextWarnedTip, peakContextLine, peakContextTip, peakTry } from '../../words/context.js';
|
|
11
|
-
import { readingShort, readingTip } from '../../words/quota.js';
|
|
11
|
+
import { readingChange, readingShort, readingTip } from '../../words/quota.js';
|
|
12
12
|
import { safetyStopCount, safetyStopLine, safetyStopTip } from '../../words/safety.js';
|
|
13
13
|
import { customColumnTip, customValues } from '../../words/row-extras.js';
|
|
14
14
|
import { divergenceTip, divergenceWord, rerunLine, rerunOf } from '../../words/rerun.js';
|
|
@@ -17,6 +17,7 @@ import { isStop, type Tone } from '../../lib/tone.js';
|
|
|
17
17
|
import { executorIcon } from '../../words/executor.js';
|
|
18
18
|
import '@contraagent/elements/json-tree/define';
|
|
19
19
|
import { tagChips, type TranscriptTag } from '@contraagent/elements/transcript';
|
|
20
|
+
import { correctionCount } from '../../adapters/filters.js';
|
|
20
21
|
import { checkMessage } from '../../adapters/transcript/rounds.js';
|
|
21
22
|
import { agentLabel, launchWho, whoTags, type Who } from '../../adapters/transcript/who.js';
|
|
22
23
|
import { correctionsOf, hadSession, isPy, notStartedWhy, stateOf, type TaskContext } from './context.js';
|
|
@@ -73,7 +74,7 @@ function verdict(c: TaskContext): [text: string, tone: Tone] {
|
|
|
73
74
|
'neutral',
|
|
74
75
|
];
|
|
75
76
|
}
|
|
76
|
-
const corrections = (last
|
|
77
|
+
const corrections = correctionCount(last);
|
|
77
78
|
if (corrections > 0) {
|
|
78
79
|
const errors = [...(last.hook_errors ?? []).flat(), ...(last.round_errors ?? []).flat(), ...c.rounds.flatMap((r) => r.rejectedWith)];
|
|
79
80
|
const first = errors[0] ? checkMessage(runRelative(errors[0], c.run)) : '';
|
|
@@ -91,7 +92,7 @@ function verdict(c: TaskContext): [text: string, tone: Tone] {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/** The model of the latest round, as its launch named it (`--model`). */
|
|
94
|
-
function streamModel(c: TaskContext): string {
|
|
95
|
+
function streamModel(c: Pick<TaskContext, 'launches'>): string {
|
|
95
96
|
for (let i = c.launches.length - 1; i >= 0; i--) {
|
|
96
97
|
const l = c.launches[i];
|
|
97
98
|
const m = l ? launchWho(l).model : '';
|
|
@@ -104,6 +105,19 @@ function streamModel(c: TaskContext): string {
|
|
|
104
105
|
* The header of task `key`. `onSearch(query, nth)` is called when the reader
|
|
105
106
|
* picks one of its web searches (the nth with that query).
|
|
106
107
|
*/
|
|
108
|
+
/** Who ran the task, as its header names it: the running session's agent, model and role, else the latest attempt's. */
|
|
109
|
+
export function taskWho(c: Pick<TaskContext, 'detail' | 'launches'>): Who {
|
|
110
|
+
const { rows, launches } = c.detail;
|
|
111
|
+
const lastRow = rows.slice().sort((a, b) => String(a.ts).localeCompare(String(b.ts)))[rows.length - 1];
|
|
112
|
+
const lc0 = launches[0];
|
|
113
|
+
return {
|
|
114
|
+
agent: agentLabel(lc0?.cmd ? lc0.cmd.split(' ')[0] : lastRow?.agent),
|
|
115
|
+
// The journal may not name the model (an agent that does not report it): fall back to the one the transcript shows.
|
|
116
|
+
model: lc0?.model || lastRow?.model || streamModel(c),
|
|
117
|
+
role: lc0?.role || '',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
export function taskHeader(c: TaskContext, key: string, onSearch: (query: string, nth: number) => void): TemplateResult {
|
|
108
122
|
const d = c.detail;
|
|
109
123
|
const live = c.live;
|
|
@@ -111,14 +125,7 @@ export function taskHeader(c: TaskContext, key: string, onSearch: (query: string
|
|
|
111
125
|
const py = isPy(d);
|
|
112
126
|
const session = hadSession(d);
|
|
113
127
|
const byTs = rows.slice().sort((a, b) => String(a.ts).localeCompare(String(b.ts)));
|
|
114
|
-
const
|
|
115
|
-
const lc0 = launches[0];
|
|
116
|
-
const who: Who = {
|
|
117
|
-
agent: agentLabel(lc0?.cmd ? lc0.cmd.split(' ')[0] : lastRow?.agent),
|
|
118
|
-
// The journal may not name the model (an agent that does not report it): fall back to the one the transcript shows.
|
|
119
|
-
model: lc0?.model || lastRow?.model || streamModel(c),
|
|
120
|
-
role: lc0?.role || '',
|
|
121
|
-
};
|
|
128
|
+
const who = taskWho(c);
|
|
122
129
|
const otherModels = session ? [...new Set(byTs.map((r) => r.model).filter((x): x is string => !!x && x !== who.model))] : [];
|
|
123
130
|
const first = rows[0];
|
|
124
131
|
const tries = rows.filter(isLaunched);
|
|
@@ -139,6 +146,7 @@ export function taskHeader(c: TaskContext, key: string, onSearch: (query: string
|
|
|
139
146
|
</div>`
|
|
140
147
|
: nothing}
|
|
141
148
|
${node?.priority ? html`<div class="meta" title=${priorityTip()}>${priorityText(node.priority)}</div>` : nothing}
|
|
149
|
+
${node?.cwd ? html`<div class="meta" title=${workingDirectoryTip()}>${workingDirectoryText(node.cwd)}</div>` : nothing}
|
|
142
150
|
${rerunMeta(rows, node?.rerun)}
|
|
143
151
|
${stoppedFlowCalls(c)}
|
|
144
152
|
${session
|
|
@@ -283,10 +291,14 @@ function quotaLine(c: TaskContext, tries: JournalRow[]) {
|
|
|
283
291
|
const start = first ? tryQuota(first, readings).start : null;
|
|
284
292
|
if (!start || !last) return nothing;
|
|
285
293
|
const next = tryQuota(last, readings).next;
|
|
286
|
-
// The identity chip names the account; both readings are that account's.
|
|
294
|
+
// The identity chip names the account; both readings are that account's. One line: each window from the first reading to the next.
|
|
295
|
+
const tip = [
|
|
296
|
+
msg(str`Usage at session start: ${readingShort(start)}`),
|
|
297
|
+
readingTip(start),
|
|
298
|
+
...(next ? ['', msg(str`Usage after this task: ${readingShort(next)}`), readingTip(next)] : []),
|
|
299
|
+
].join('\n');
|
|
287
300
|
return html`<div class="spend quota">
|
|
288
|
-
<span title=${
|
|
289
|
-
${next ? html`<span title=${readingTip(next)}>${msg(str`Usage after this task: ${readingShort(next)}`)}</span>` : nothing}
|
|
301
|
+
<span title=${tip}>${msg(str`Usage: ${readingChange(start, next)}`)}</span>
|
|
290
302
|
</div>`;
|
|
291
303
|
}
|
|
292
304
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The input and output panes of `ca-task-view`: what the task read (parameters,
|
|
2
2
|
// files, earlier tasks, the prompt) and what it wrote (files against their
|
|
3
|
-
// contracts,
|
|
3
|
+
// contracts, tokens and cost, later tasks).
|
|
4
4
|
import {
|
|
5
5
|
isLaunched,
|
|
6
6
|
isPython,
|
|
@@ -19,10 +19,10 @@ import { msg, str } from '@lit/localize';
|
|
|
19
19
|
import { html, nothing, type TemplateResult } from 'lit';
|
|
20
20
|
import { pill } from '@contraagent/elements/primitives';
|
|
21
21
|
import '@contraagent/elements/json-tree/define';
|
|
22
|
-
import { facts,
|
|
22
|
+
import { facts, kindUnit, tokenCount, usd } from '../../../words/format.js';
|
|
23
23
|
import { attemptLabel, attemptNames, credentialLabel, credentialTip, outcomeLabel, stopLabel } from '../../../words/index.js';
|
|
24
24
|
import { accountReading, readingShort, readingTip } from '../../../words/quota.js';
|
|
25
|
-
import { outcomeMark, outcomeTone
|
|
25
|
+
import { outcomeMark, outcomeTone } from '../../../lib/tone.js';
|
|
26
26
|
import { executorIcon } from '../../../words/executor.js';
|
|
27
27
|
import { promptText } from '../../shared/prompt-text.js';
|
|
28
28
|
import { contractChip } from '../../shared/contract-parts.js';
|
|
@@ -30,8 +30,8 @@ import { ioFileCard } from '../../shared/file-card.js';
|
|
|
30
30
|
import { taskChip } from '../../shared/task-chip.js';
|
|
31
31
|
import { inputView, notPassed, type EarlierTask, type InputFile } from '../inputs.js';
|
|
32
32
|
import { roundLabel, roundWhy } from '../../../words/rounds.js';
|
|
33
|
-
import { inputFileCheckWord, lineageTip, lineageWord } from '../../../words/lineage.js';
|
|
34
|
-
import {
|
|
33
|
+
import { inputFileCheckWord, inputRootPath, inputRootTip, lineageTip, lineageWord } from '../../../words/lineage.js';
|
|
34
|
+
import { hadSession, isPy, producerOf, stateOf, taskLink, type TaskContext } from '../context.js';
|
|
35
35
|
|
|
36
36
|
const valueText = (v: unknown): string => (typeof v === 'string' ? v : JSON.stringify(v) ?? String(v));
|
|
37
37
|
const sameJson = (a: unknown, b: unknown): boolean => {
|
|
@@ -56,6 +56,8 @@ export function inputPane(c: TaskContext): TemplateResult {
|
|
|
56
56
|
const params = Object.entries(io.params);
|
|
57
57
|
const { files, earlier } = inputView(io);
|
|
58
58
|
const source = (v: unknown) => io.upstream.find((u) => u.outputs.some((o) => o.file.exists && sameJson(o.file.content, v)));
|
|
59
|
+
// An earlier task a parameter names ("From prepare · a") is given its output there: its row below does not say it again.
|
|
60
|
+
const givenAbove = new Set(params.map(([, v]) => source(v)?.key).filter((k): k is string => !!k));
|
|
59
61
|
return html`<h4 class="sect-h">${msg('Parameters')}</h4>
|
|
60
62
|
${params.length
|
|
61
63
|
? html`<table class="ct-fields params">
|
|
@@ -76,13 +78,14 @@ export function inputPane(c: TaskContext): TemplateResult {
|
|
|
76
78
|
${files.map((f) => inputCard(c, f))}`
|
|
77
79
|
: nothing}
|
|
78
80
|
<h4 class="sect-h">${msg('Earlier tasks')}</h4>
|
|
79
|
-
${earlier.length ? earlier.map((e) => earlierTask(c, e)) : html`<div class="meta">${msg('No earlier tasks')}</div>`}
|
|
81
|
+
${earlier.length ? earlier.map((e) => earlierTask(c, e, givenAbove.has(e.task.key))) : html`<div class="meta">${msg('No earlier tasks')}</div>`}
|
|
80
82
|
${isPy(d) ? nothing : promptSection(c)}`;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/**
|
|
84
|
-
* One input file of the task (an `InputFile` param): its check at the start, its path
|
|
85
|
-
* the param that holds it, its hash, and the earlier task whose
|
|
86
|
+
* One input file of the task (an `InputFile` param): its check at the start, its path
|
|
87
|
+
* (with its input root), the param that holds it, its hash, and the earlier task whose
|
|
88
|
+
* output file it is.
|
|
86
89
|
*/
|
|
87
90
|
function inputFileCard(c: TaskContext, f: TaskInputFile) {
|
|
88
91
|
const tone = f.check === 'passed' ? 'done' : f.check === 'refused' ? 'failed' : 'neutral';
|
|
@@ -91,8 +94,10 @@ function inputFileCard(c: TaskContext, f: TaskInputFile) {
|
|
|
91
94
|
const origin = f.writer
|
|
92
95
|
? html`<span>${msg(html`Output of ${taskLink(c, writer ?? { key: f.writer, kind: f.writer, unit: '' })}`)}</span>`
|
|
93
96
|
: nothing;
|
|
97
|
+
const rooted = inputRootPath(f);
|
|
98
|
+
const tip = f.root && f.root !== 'out' ? inputRootTip(f.root, f.path) : f.path;
|
|
94
99
|
return html`<div class="io-card one infile">
|
|
95
|
-
<div class="io-hd">${pill({ text: inputFileCheckWord(f.check), tone, mark })}<span class="jmono ct-path" title=${
|
|
100
|
+
<div class="io-hd">${pill({ text: inputFileCheckWord(f.check), tone, mark })}<span class="jmono ct-path" title=${tip}>${rooted ?? runRelative(f.path, c.run)}</span></div>
|
|
96
101
|
<div class="meta io-from"><span title=${f.sha256}>${facts(msg(str`Param ${f.param}`), `sha256 ${f.sha256.slice(0, 12)}`)}</span>${f.writer
|
|
97
102
|
? html` | ${origin}`
|
|
98
103
|
: nothing}</div>
|
|
@@ -117,9 +122,10 @@ function inputCard(c: TaskContext, f: InputFile) {
|
|
|
117
122
|
/**
|
|
118
123
|
* One earlier task: its status, how this task is joined to it (its lineage, in the words of
|
|
119
124
|
* the graph's edges), how many of its output files this task reads, and the others folded
|
|
120
|
-
* under it (open when one did not pass).
|
|
125
|
+
* under it (open when one did not pass). `givenAbove`: a parameter's "From" names it,
|
|
126
|
+
* so its lineage leaves out "given output" and keeps the rest.
|
|
121
127
|
*/
|
|
122
|
-
function earlierTask(c: TaskContext, e: EarlierTask) {
|
|
128
|
+
function earlierTask(c: TaskContext, e: EarlierTask, givenAbove: boolean) {
|
|
123
129
|
const u = e.task;
|
|
124
130
|
const kindName = isPython(u) ? msg('Python task') : msg('Agent task');
|
|
125
131
|
const state = c.outcomes.get(u.key)?.outcome;
|
|
@@ -131,7 +137,7 @@ function earlierTask(c: TaskContext, e: EarlierTask) {
|
|
|
131
137
|
${executorIcon(isPython(u), kindName)}
|
|
132
138
|
${taskLink(c, u)}
|
|
133
139
|
${state ? outcomeChip(state) : nothing}
|
|
134
|
-
${lineageTags(u.lineage ?? [])}
|
|
140
|
+
${lineageTags((u.lineage ?? []).filter((l) => !(givenAbove && l === 'given_from')))}
|
|
135
141
|
<span class="meta">${summary}</span>
|
|
136
142
|
</div>`;
|
|
137
143
|
if (!e.rest.length) return html`<div class="upstream">${head}</div>`;
|
|
@@ -211,24 +217,14 @@ function usageTable(c: TaskContext, rows: JournalRow[]) {
|
|
|
211
217
|
</table></div>`;
|
|
212
218
|
}
|
|
213
219
|
|
|
214
|
-
/** What the task wrote: its output files,
|
|
220
|
+
/** What the task wrote: its output files, tokens and cost, and the later tasks. */
|
|
215
221
|
export function outputPane(c: TaskContext): TemplateResult {
|
|
216
222
|
const d = c.detail;
|
|
217
223
|
const io = d.io;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return html`<h4 class="sect-h">${msg('Output files')}</h4>
|
|
221
|
-
${io.outputs.length
|
|
224
|
+
// The header above names the attempts and how each ended, and the tab names the files: the pane starts with them.
|
|
225
|
+
return html`${io.outputs.length
|
|
222
226
|
? io.outputs.map((o) => ioFileCard(o, 'output', c.run, { producerOf: producerOf(c) }))
|
|
223
227
|
: html`<div class="meta">${msg('No output files')}</div>`}
|
|
224
|
-
${last
|
|
225
|
-
? html`<h4 class="sect-h">${msg('Outcome')}</h4>
|
|
226
|
-
<div class="ct-in">
|
|
227
|
-
${pill({ text: stopLabel(last.stop, last.reason), tone: stopTone(last.stop) })}
|
|
228
|
-
${isLaunched(last) ? html`<span class="meta">${facts(attemptLabel(last.attempt), fmtDur(last.wall_s), correctionsOf(last))}</span>` : nothing}
|
|
229
|
-
|
|
230
|
-
</div>`
|
|
231
|
-
: nothing}
|
|
232
228
|
${usageTable(c, d.rows)}
|
|
233
229
|
<h4 class="sect-h">${msg('Later tasks')}</h4>
|
|
234
230
|
${io.downstream.length ? laterTasks(c, io.downstream) : html`<div class="meta">${msg('No later tasks')}</div>`}`;
|
|
@@ -150,7 +150,8 @@ export class CaTrends extends LitElement {
|
|
|
150
150
|
/** The period, its time zone and the run group: links of the page's address. */
|
|
151
151
|
private controls() {
|
|
152
152
|
const data = this.view?.data;
|
|
153
|
-
|
|
153
|
+
// The time zone control names the zone; an export has none, so its period names it.
|
|
154
|
+
const period = data ? html`<span class="period">${periodLabel(data.period.from, data.period.to, this.exported ? data.period.tz : '')}</span>` : nothing;
|
|
154
155
|
if (this.exported) return html`<div class="controls"><span class="meta">${exportedPeriod()}</span>${period}</div>`;
|
|
155
156
|
const choice = this.days ? String(this.days) : 'dates';
|
|
156
157
|
const today = data?.period.to || new Date().toISOString().slice(0, 10);
|