humanish 0.49.0 → 0.50.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 +52 -3
- package/dist/concurrent-shared-world-lab.d.ts +5 -0
- package/dist/concurrent-shared-world-lab.js +28 -0
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +7 -0
- package/dist/cua-actor-lab.js +46 -2
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/e2b-terminal-lab.d.ts +7 -0
- package/dist/e2b-terminal-lab.js +54 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/init-templates.js +7 -2
- package/dist/init-templates.js.map +1 -1
- package/dist/lab-engine.d.ts +11 -0
- package/dist/lab-engine.js +18 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/oss-lab.d.ts +3 -0
- package/dist/oss-lab.js.map +1 -1
- package/dist/oss-meta-lab.d.ts +3 -0
- package/dist/oss-meta-lab.js +13 -0
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/program.d.ts +14 -0
- package/dist/program.js +127 -9
- package/dist/program.js.map +1 -1
- package/dist/run-detail.d.ts +59 -0
- package/dist/run-detail.js +108 -0
- package/dist/run-detail.js.map +1 -0
- package/dist/run-index.d.ts +71 -0
- package/dist/run-index.js +206 -0
- package/dist/run-index.js.map +1 -0
- package/dist/run-paths.js +16 -1
- package/dist/run-paths.js.map +1 -1
- package/dist/run-projection.d.ts +182 -0
- package/dist/run-projection.js +349 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/run-status.d.ts +139 -0
- package/dist/run-status.js +218 -0
- package/dist/run-status.js.map +1 -0
- package/dist/run.d.ts +15 -0
- package/dist/run.js +91 -6
- package/dist/run.js.map +1 -1
- package/dist/scripted-browser-lab.d.ts +5 -0
- package/dist/scripted-browser-lab.js +27 -0
- package/dist/scripted-browser-lab.js.map +1 -1
- package/dist/shared-world-lab.d.ts +5 -0
- package/dist/shared-world-lab.js +27 -0
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/tui-app.js +402 -0
- package/dist/tui-contract.d.ts +73 -0
- package/dist/tui-contract.js +32 -0
- package/dist/tui-contract.js.map +1 -0
- package/dist/tui-launch.d.ts +48 -0
- package/dist/tui-launch.js +159 -0
- package/dist/tui-launch.js.map +1 -0
- package/docs/contracts/run-bundle.md +8 -0
- package/docs/contracts/schemas.md +70 -1
- package/docs/goals/current.md +3 -2
- package/docs/ramp/README.md +1 -1
- package/package.json +8 -5
- package/skills/humanish/SKILL.md +19 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { RunIndexEntry } from "./run-index.js";
|
|
2
|
+
/** One lab as the labs list shows it. */
|
|
3
|
+
export interface LabRollup {
|
|
4
|
+
labId: string;
|
|
5
|
+
/** Total runs attributed to this lab. */
|
|
6
|
+
runs: number;
|
|
7
|
+
/** Runs whose status record is fresh right now. */
|
|
8
|
+
live: number;
|
|
9
|
+
/** The newest run of this lab, whatever its state. */
|
|
10
|
+
latest?: RunIndexEntry;
|
|
11
|
+
/** Live runs, newest first — the labs list shows the first one inline. */
|
|
12
|
+
liveRuns: RunIndexEntry[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Group runs by lab, newest-first within each lab and by recency between labs. Runs with no lab
|
|
16
|
+
* attribution are collected under `unattributed` rather than invented into a lab — the honest
|
|
17
|
+
* home for pre-contract runs and library callers.
|
|
18
|
+
*/
|
|
19
|
+
export declare function groupRunsByLab(entries: readonly RunIndexEntry[]): {
|
|
20
|
+
labs: LabRollup[];
|
|
21
|
+
unattributed: RunIndexEntry[];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* What the launch screen may claim about a lab, and on what evidence. Every figure carries the
|
|
25
|
+
* number of runs it came from; `sample: 0` means there is nothing to claim and the caller must
|
|
26
|
+
* say so rather than showing an empty number.
|
|
27
|
+
*/
|
|
28
|
+
export interface LabExpectation {
|
|
29
|
+
/** How many completed runs of this lab the figures are drawn from. */
|
|
30
|
+
sample: number;
|
|
31
|
+
medianDurationMs?: number;
|
|
32
|
+
/** Range across the sample, so the UI can show a span instead of a false point estimate. */
|
|
33
|
+
durationRangeMs?: {
|
|
34
|
+
min: number;
|
|
35
|
+
max: number;
|
|
36
|
+
};
|
|
37
|
+
medianCostUsd?: number;
|
|
38
|
+
/** Completed runs whose cost was declared absent — excluded from the median, reported here so
|
|
39
|
+
* a partial sample can never masquerade as a full one. */
|
|
40
|
+
costUnknown: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Derive what to expect from a lab's own history. Only FINISHED runs count: an interrupted run's
|
|
44
|
+
* duration is the length of an accident, not of a study, and including it would quietly bias the
|
|
45
|
+
* estimate the operator uses to decide whether to press Start.
|
|
46
|
+
*
|
|
47
|
+
* MODE MATTERS, and mixing modes is a lie rather than an imprecision. A dry run spends nothing and
|
|
48
|
+
* takes no time, so a median over nine dry runs and one live one reports that a live run is free —
|
|
49
|
+
* next to a control that spends money. Pass the mode the figure is about; omit it only for a
|
|
50
|
+
* summary that is not attached to an action.
|
|
51
|
+
*/
|
|
52
|
+
export declare function expectationFor(entries: readonly RunIndexEntry[], mode?: "dry-run" | "live"): LabExpectation;
|
|
53
|
+
/**
|
|
54
|
+
* The one sentence the launch screen shows about time and money, with its denominator attached.
|
|
55
|
+
* A lab with no history says so plainly instead of borrowing another lab's numbers or inventing
|
|
56
|
+
* a range — "no runs yet" is information, and the operator can still press Start.
|
|
57
|
+
*/
|
|
58
|
+
export declare function expectationLine(expectation: LabExpectation): string;
|
|
59
|
+
/**
|
|
60
|
+
* The one line a lab may say about itself, wherever it is shown.
|
|
61
|
+
*
|
|
62
|
+
* Prefers LIVE figures: cost and duration are what someone reads before spending, and a median
|
|
63
|
+
* diluted by dry runs reports a live study as cheaper and faster than it has ever been. With no
|
|
64
|
+
* live history it reports the count and claims nothing about time or money — which is why this is
|
|
65
|
+
* shared rather than reimplemented per surface, since the two disagreeing is the whole failure.
|
|
66
|
+
*/
|
|
67
|
+
export declare function labSummaryLine(row: Pick<LabRow, "runs" | "declared" | "expectation" | "liveExpectation">): string;
|
|
68
|
+
/** Compact duration: seconds under a minute, then m/s, then h/m. */
|
|
69
|
+
export declare function formatDuration(ms: number): string;
|
|
70
|
+
export interface NormalizedThought {
|
|
71
|
+
lines: string[];
|
|
72
|
+
/** True when the source text did not fit and was cut — shown, never silent. */
|
|
73
|
+
truncated: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Prepare a participant's recorded reasoning summary for a fixed-width surface. Provider summaries
|
|
77
|
+
* arrive with markdown section leads (`**Thinking through setup**`) and hard newlines; a terminal
|
|
78
|
+
* needs plain text wrapped to a line budget.
|
|
79
|
+
*
|
|
80
|
+
* The text is never paraphrased or shortened by meaning — only wrapped, and cut at a word boundary
|
|
81
|
+
* with an ellipsis when it does not fit. `truncated` is how the surface says so.
|
|
82
|
+
*/
|
|
83
|
+
export declare function normalizeThought(text: string, options: {
|
|
84
|
+
width: number;
|
|
85
|
+
maxLines: number;
|
|
86
|
+
}): NormalizedThought;
|
|
87
|
+
/**
|
|
88
|
+
* Which slice of a list to draw, keeping the selection visible with a little context around it.
|
|
89
|
+
* Ink has no scroll container, so every long list windows by hand; getting this wrong is how a
|
|
90
|
+
* selection disappears off the top of a phone screen.
|
|
91
|
+
*/
|
|
92
|
+
export declare function listWindow(args: {
|
|
93
|
+
total: number;
|
|
94
|
+
selected: number;
|
|
95
|
+
viewport: number;
|
|
96
|
+
/** Rows of context to keep above/below the selection when scrolling. */
|
|
97
|
+
margin?: number;
|
|
98
|
+
}): {
|
|
99
|
+
start: number;
|
|
100
|
+
end: number;
|
|
101
|
+
};
|
|
102
|
+
/** The label for a run's state, in the register the surfaces share. */
|
|
103
|
+
export declare function livenessLabel(entry: Pick<RunIndexEntry, "liveness" | "verdict">): string;
|
|
104
|
+
/** A lab as the labs list shows it: what is declared, joined to what actually happened. */
|
|
105
|
+
export interface LabRow {
|
|
106
|
+
/**
|
|
107
|
+
* Stable identity for THIS ROW. A manifest's path when it has one, else the lab id — because two
|
|
108
|
+
* manifests can declare the same id, and keying rows by id makes them collapse into a pair of
|
|
109
|
+
* indistinguishable duplicates.
|
|
110
|
+
*/
|
|
111
|
+
key: string;
|
|
112
|
+
/** The declared id. Run history attributes to this, so it is NOT unique across manifests. */
|
|
113
|
+
labId: string;
|
|
114
|
+
/**
|
|
115
|
+
* The handle an operator would actually type. Lab resolution is by FILENAME, so the manifest
|
|
116
|
+
* `\`.humanish/labs/persona-contrast-live.yaml\`` is reached as `persona-contrast-live` even when
|
|
117
|
+
* the id inside it says something else.
|
|
118
|
+
*/
|
|
119
|
+
name: string;
|
|
120
|
+
/** The manifest's own title, when it has one. */
|
|
121
|
+
title?: string;
|
|
122
|
+
/**
|
|
123
|
+
* The shortest label that is UNIQUE among the rows it is listed with: the title when no other
|
|
124
|
+
* lab shares it, else the filename, else the full path. A list is only navigable if every row can
|
|
125
|
+
* be told from every other one, and a title is not guaranteed to be distinct — two manifests in
|
|
126
|
+
* this repo carry the same title AND the same declared id, differing only by filename.
|
|
127
|
+
*/
|
|
128
|
+
label: string;
|
|
129
|
+
/** Repo-relative manifest path, absent for a lab known only from run history. */
|
|
130
|
+
path?: string;
|
|
131
|
+
origin?: "committed" | "ignored" | "explicit";
|
|
132
|
+
/**
|
|
133
|
+
* False for a lab that has runs but NO manifest here — renamed, deleted, or run from a path that
|
|
134
|
+
* is gone. Its runs are still evidence and must stay reachable, so it is listed and marked rather
|
|
135
|
+
* than dropped.
|
|
136
|
+
*/
|
|
137
|
+
declared: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* How many OTHER manifests declare this same lab id. Above zero, the run history below is shared
|
|
140
|
+
* between them and cannot be attributed to one file — worth saying, because it is a
|
|
141
|
+
* misconfiguration the operator almost certainly does not know about.
|
|
142
|
+
*/
|
|
143
|
+
sharesIdWith: number;
|
|
144
|
+
runs: number;
|
|
145
|
+
live: number;
|
|
146
|
+
latest?: RunIndexEntry;
|
|
147
|
+
liveRuns: RunIndexEntry[];
|
|
148
|
+
/** Across every finished run, whatever its mode. A summary, never attached to a spend decision. */
|
|
149
|
+
expectation: LabExpectation;
|
|
150
|
+
/** Live runs only — the figure that belongs beside anything that spends money. */
|
|
151
|
+
liveExpectation: LabExpectation;
|
|
152
|
+
}
|
|
153
|
+
/** The addressable handle for a manifest: its filename without directory or extension. */
|
|
154
|
+
export declare function labNameFromPath(manifestPath: string): string;
|
|
155
|
+
export interface DeclaredLab {
|
|
156
|
+
id: string;
|
|
157
|
+
title?: string;
|
|
158
|
+
path?: string;
|
|
159
|
+
origin?: "committed" | "ignored" | "explicit";
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Join declared lab manifests to run history.
|
|
163
|
+
*
|
|
164
|
+
* Neither side alone is the truth. A fresh project has manifests and no runs — those labs are the
|
|
165
|
+
* whole screen, and a list built only from history would be empty on exactly the first visit that
|
|
166
|
+
* matters. A long-lived project accumulates runs from labs whose manifest has since been renamed or
|
|
167
|
+
* deleted — that evidence still exists on disk, so dropping those rows would make real runs
|
|
168
|
+
* unreachable from the surface that is supposed to list them.
|
|
169
|
+
*
|
|
170
|
+
* ONE ROW PER MANIFEST, not per id. The two are not the same thing: a manifest is addressed by its
|
|
171
|
+
* filename while its runs attribute to the id declared inside it, so several files can legitimately
|
|
172
|
+
* share an id. Collapsing them hides a real file; keying by id duplicates a row with no way to tell
|
|
173
|
+
* the copies apart. Both happen in practice — this repo has exactly that pair.
|
|
174
|
+
*
|
|
175
|
+
* Order puts a lab someone is working in first, then labs by how recently they ran, then declared
|
|
176
|
+
* labs that have never run (alphabetically BY WHAT IS DISPLAYED, so the order on screen is the
|
|
177
|
+
* order a reader can predict), then labs known only from history.
|
|
178
|
+
*/
|
|
179
|
+
export declare function labRows(declared: readonly DeclaredLab[], entries: readonly RunIndexEntry[]): {
|
|
180
|
+
rows: LabRow[];
|
|
181
|
+
unattributed: RunIndexEntry[];
|
|
182
|
+
};
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// The disk→view-model projection (#455 PR 2). Pure functions, no I/O, no rendering: this is where
|
|
2
|
+
// lab grouping, the trust line's arithmetic, thought normalization and list-window math live, so
|
|
3
|
+
// the parts most likely to be wrong are testable without a terminal.
|
|
4
|
+
//
|
|
5
|
+
// The honesty rules these encode, because they are the ones a UI is most tempted to break:
|
|
6
|
+
// - a statistic ALWAYS carries its denominator, and says when it has none;
|
|
7
|
+
// - an unknown cost is `null` (declared absent), never 0;
|
|
8
|
+
// - truncation is reported, never silent;
|
|
9
|
+
// - a participant's reported thinking is quoted, never paraphrased or summarized.
|
|
10
|
+
/**
|
|
11
|
+
* Group runs by lab, newest-first within each lab and by recency between labs. Runs with no lab
|
|
12
|
+
* attribution are collected under `unattributed` rather than invented into a lab — the honest
|
|
13
|
+
* home for pre-contract runs and library callers.
|
|
14
|
+
*/
|
|
15
|
+
export function groupRunsByLab(entries) {
|
|
16
|
+
const byLab = new Map();
|
|
17
|
+
const unattributed = [];
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const labId = entry.lab?.id;
|
|
20
|
+
if (labId === undefined) {
|
|
21
|
+
unattributed.push(entry);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const bucket = byLab.get(labId);
|
|
25
|
+
if (bucket === undefined)
|
|
26
|
+
byLab.set(labId, [entry]);
|
|
27
|
+
else
|
|
28
|
+
bucket.push(entry);
|
|
29
|
+
}
|
|
30
|
+
const labs = [...byLab.entries()].map(([labId, runs]) => {
|
|
31
|
+
const liveRuns = runs.filter((run) => run.liveness === "running");
|
|
32
|
+
return {
|
|
33
|
+
labId,
|
|
34
|
+
runs: runs.length,
|
|
35
|
+
live: liveRuns.length,
|
|
36
|
+
...(runs[0] === undefined ? {} : { latest: runs[0] }),
|
|
37
|
+
liveRuns
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
// A lab someone is working in sorts first; otherwise most recently used.
|
|
41
|
+
labs.sort((left, right) => {
|
|
42
|
+
if (left.live !== right.live)
|
|
43
|
+
return right.live - left.live;
|
|
44
|
+
return recencyOf(right.latest) - recencyOf(left.latest);
|
|
45
|
+
});
|
|
46
|
+
return { labs, unattributed };
|
|
47
|
+
}
|
|
48
|
+
function recencyOf(entry) {
|
|
49
|
+
if (entry === undefined)
|
|
50
|
+
return 0;
|
|
51
|
+
for (const candidate of [entry.completedAt, entry.updatedAt, entry.startedAt]) {
|
|
52
|
+
if (candidate === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
const parsed = Date.parse(candidate);
|
|
55
|
+
if (Number.isFinite(parsed))
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Derive what to expect from a lab's own history. Only FINISHED runs count: an interrupted run's
|
|
62
|
+
* duration is the length of an accident, not of a study, and including it would quietly bias the
|
|
63
|
+
* estimate the operator uses to decide whether to press Start.
|
|
64
|
+
*
|
|
65
|
+
* MODE MATTERS, and mixing modes is a lie rather than an imprecision. A dry run spends nothing and
|
|
66
|
+
* takes no time, so a median over nine dry runs and one live one reports that a live run is free —
|
|
67
|
+
* next to a control that spends money. Pass the mode the figure is about; omit it only for a
|
|
68
|
+
* summary that is not attached to an action.
|
|
69
|
+
*/
|
|
70
|
+
export function expectationFor(entries, mode) {
|
|
71
|
+
const scoped = mode === undefined ? entries : entries.filter((entry) => entry.mode === mode);
|
|
72
|
+
const finished = scoped.filter((entry) => entry.liveness === "finished");
|
|
73
|
+
const durations = finished
|
|
74
|
+
.map((entry) => entry.durationMs)
|
|
75
|
+
.filter((value) => typeof value === "number" && Number.isFinite(value) && value >= 0)
|
|
76
|
+
.sort((a, b) => a - b);
|
|
77
|
+
const costs = finished
|
|
78
|
+
.map((entry) => entry.estimatedCostUsd)
|
|
79
|
+
.filter((value) => typeof value === "number" && Number.isFinite(value))
|
|
80
|
+
.sort((a, b) => a - b);
|
|
81
|
+
// A cost is unknown whether it was DECLARED absent (`null`) or never recorded at all
|
|
82
|
+
// (`undefined` — every fail-closed exit finalized with no outcome). Counting only the first
|
|
83
|
+
// reports "~$1.20 median · 3 runs" for a sample where two runs were never priced, which claims a
|
|
84
|
+
// denominator the figure does not have.
|
|
85
|
+
const costUnknown = finished.filter((entry) => entry.estimatedCostUsd === null || entry.estimatedCostUsd === undefined).length;
|
|
86
|
+
return {
|
|
87
|
+
sample: finished.length,
|
|
88
|
+
...(durations.length === 0
|
|
89
|
+
? {}
|
|
90
|
+
: {
|
|
91
|
+
medianDurationMs: median(durations),
|
|
92
|
+
durationRangeMs: { min: durations[0], max: durations[durations.length - 1] }
|
|
93
|
+
}),
|
|
94
|
+
...(costs.length === 0 ? {} : { medianCostUsd: median(costs) }),
|
|
95
|
+
costUnknown
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function median(sorted) {
|
|
99
|
+
const middle = Math.floor(sorted.length / 2);
|
|
100
|
+
if (sorted.length % 2 === 1)
|
|
101
|
+
return sorted[middle];
|
|
102
|
+
return (sorted[middle - 1] + sorted[middle]) / 2;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The one sentence the launch screen shows about time and money, with its denominator attached.
|
|
106
|
+
* A lab with no history says so plainly instead of borrowing another lab's numbers or inventing
|
|
107
|
+
* a range — "no runs yet" is information, and the operator can still press Start.
|
|
108
|
+
*/
|
|
109
|
+
export function expectationLine(expectation) {
|
|
110
|
+
if (expectation.sample === 0)
|
|
111
|
+
return "no runs yet";
|
|
112
|
+
const parts = [];
|
|
113
|
+
if (expectation.durationRangeMs !== undefined) {
|
|
114
|
+
const { min, max } = expectation.durationRangeMs;
|
|
115
|
+
parts.push(min === max ? formatDuration(min) : `${formatDuration(min)}–${formatDuration(max)}`);
|
|
116
|
+
}
|
|
117
|
+
if (expectation.medianCostUsd !== undefined) {
|
|
118
|
+
parts.push(`~$${expectation.medianCostUsd.toFixed(2)} median`);
|
|
119
|
+
}
|
|
120
|
+
const sample = `${expectation.sample} run${expectation.sample === 1 ? "" : "s"}`;
|
|
121
|
+
// "2 of 3 unpriced" and "none of them priced" are different facts, and a line that reports the
|
|
122
|
+
// second as the first reads as though a median existed for the rest.
|
|
123
|
+
const allUnpriced = expectation.sample > 0 && expectation.costUnknown === expectation.sample;
|
|
124
|
+
const unknown = expectation.costUnknown === 0 ? "" : allUnpriced ? ", none priced" : `, ${expectation.costUnknown} unpriced`;
|
|
125
|
+
if (parts.length === 0) {
|
|
126
|
+
return allUnpriced ? `${sample}, nothing recorded` : `${sample}${unknown}, nothing timed`;
|
|
127
|
+
}
|
|
128
|
+
return `${parts.join(" · ")} · ${sample}${unknown}`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The one line a lab may say about itself, wherever it is shown.
|
|
132
|
+
*
|
|
133
|
+
* Prefers LIVE figures: cost and duration are what someone reads before spending, and a median
|
|
134
|
+
* diluted by dry runs reports a live study as cheaper and faster than it has ever been. With no
|
|
135
|
+
* live history it reports the count and claims nothing about time or money — which is why this is
|
|
136
|
+
* shared rather than reimplemented per surface, since the two disagreeing is the whole failure.
|
|
137
|
+
*/
|
|
138
|
+
export function labSummaryLine(row) {
|
|
139
|
+
if (row.runs === 0)
|
|
140
|
+
return row.declared ? "never run" : "no runs";
|
|
141
|
+
if (row.liveExpectation.sample > 0)
|
|
142
|
+
return expectationLine(row.liveExpectation);
|
|
143
|
+
return `${row.runs} ${row.runs === 1 ? "run" : "runs"}, none live`;
|
|
144
|
+
}
|
|
145
|
+
/** Compact duration: seconds under a minute, then m/s, then h/m. */
|
|
146
|
+
export function formatDuration(ms) {
|
|
147
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
148
|
+
if (seconds < 60)
|
|
149
|
+
return `${seconds}s`;
|
|
150
|
+
const minutes = Math.floor(seconds / 60);
|
|
151
|
+
if (minutes < 60)
|
|
152
|
+
return seconds % 60 === 0 ? `${minutes}m` : `${minutes}m ${seconds % 60}s`;
|
|
153
|
+
const hours = Math.floor(minutes / 60);
|
|
154
|
+
return minutes % 60 === 0 ? `${hours}h` : `${hours}h ${minutes % 60}m`;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Prepare a participant's recorded reasoning summary for a fixed-width surface. Provider summaries
|
|
158
|
+
* arrive with markdown section leads (`**Thinking through setup**`) and hard newlines; a terminal
|
|
159
|
+
* needs plain text wrapped to a line budget.
|
|
160
|
+
*
|
|
161
|
+
* The text is never paraphrased or shortened by meaning — only wrapped, and cut at a word boundary
|
|
162
|
+
* with an ellipsis when it does not fit. `truncated` is how the surface says so.
|
|
163
|
+
*/
|
|
164
|
+
export function normalizeThought(text, options) {
|
|
165
|
+
const width = Math.max(8, Math.floor(options.width));
|
|
166
|
+
const maxLines = Math.max(1, Math.floor(options.maxLines));
|
|
167
|
+
const flat = text
|
|
168
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1") // markdown bold leads: keep the words, drop the syntax
|
|
169
|
+
.replace(/\s*\n+\s*/g, " ") // hard newlines are the provider's formatting, not the meaning
|
|
170
|
+
.replace(/\s{2,}/g, " ")
|
|
171
|
+
.trim();
|
|
172
|
+
if (flat === "")
|
|
173
|
+
return { lines: [], truncated: false };
|
|
174
|
+
const words = flat.split(" ");
|
|
175
|
+
const lines = [];
|
|
176
|
+
let current = "";
|
|
177
|
+
let index = 0;
|
|
178
|
+
for (; index < words.length; index += 1) {
|
|
179
|
+
const word = words[index];
|
|
180
|
+
const candidate = current === "" ? word : `${current} ${word}`;
|
|
181
|
+
if (candidate.length <= width) {
|
|
182
|
+
current = candidate;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (current !== "")
|
|
186
|
+
lines.push(current);
|
|
187
|
+
current = word.length <= width ? word : `${word.slice(0, width - 1)}…`;
|
|
188
|
+
if (word.length > width) {
|
|
189
|
+
// A single unbreakable token longer than the line (a URL, a long id) is cut rather than
|
|
190
|
+
// allowed to overflow the pane.
|
|
191
|
+
lines.push(current);
|
|
192
|
+
current = "";
|
|
193
|
+
}
|
|
194
|
+
if (lines.length === maxLines)
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
if (lines.length < maxLines && current !== "") {
|
|
198
|
+
lines.push(current);
|
|
199
|
+
index = words.length;
|
|
200
|
+
}
|
|
201
|
+
const truncated = index < words.length;
|
|
202
|
+
if (truncated && lines.length > 0) {
|
|
203
|
+
const last = lines[lines.length - 1];
|
|
204
|
+
lines[lines.length - 1] = last.length + 1 <= width ? `${last}…` : `${last.slice(0, width - 1)}…`;
|
|
205
|
+
}
|
|
206
|
+
return { lines, truncated };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Which slice of a list to draw, keeping the selection visible with a little context around it.
|
|
210
|
+
* Ink has no scroll container, so every long list windows by hand; getting this wrong is how a
|
|
211
|
+
* selection disappears off the top of a phone screen.
|
|
212
|
+
*/
|
|
213
|
+
export function listWindow(args) {
|
|
214
|
+
const total = Math.max(0, Math.floor(args.total));
|
|
215
|
+
const viewport = Math.max(1, Math.floor(args.viewport));
|
|
216
|
+
if (total <= viewport)
|
|
217
|
+
return { start: 0, end: total };
|
|
218
|
+
const selected = Math.min(Math.max(0, Math.floor(args.selected)), total - 1);
|
|
219
|
+
const margin = Math.min(Math.max(0, Math.floor(args.margin ?? 1)), Math.floor((viewport - 1) / 2));
|
|
220
|
+
let start = Math.min(Math.max(0, selected - margin), total - viewport);
|
|
221
|
+
if (selected >= start + viewport - margin)
|
|
222
|
+
start = Math.min(selected - viewport + 1 + margin, total - viewport);
|
|
223
|
+
start = Math.max(0, Math.min(start, total - viewport));
|
|
224
|
+
return { start, end: start + viewport };
|
|
225
|
+
}
|
|
226
|
+
/** The label for a run's state, in the register the surfaces share. */
|
|
227
|
+
export function livenessLabel(entry) {
|
|
228
|
+
switch (entry.liveness) {
|
|
229
|
+
case "running":
|
|
230
|
+
return "running";
|
|
231
|
+
case "interrupted":
|
|
232
|
+
return "interrupted";
|
|
233
|
+
default:
|
|
234
|
+
return entry.verdict ?? "finished";
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** The addressable handle for a manifest: its filename without directory or extension. */
|
|
238
|
+
export function labNameFromPath(manifestPath) {
|
|
239
|
+
const base = manifestPath.split("/").pop() ?? manifestPath;
|
|
240
|
+
return base.replace(/\.(ya?ml)$/i, "");
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Join declared lab manifests to run history.
|
|
244
|
+
*
|
|
245
|
+
* Neither side alone is the truth. A fresh project has manifests and no runs — those labs are the
|
|
246
|
+
* whole screen, and a list built only from history would be empty on exactly the first visit that
|
|
247
|
+
* matters. A long-lived project accumulates runs from labs whose manifest has since been renamed or
|
|
248
|
+
* deleted — that evidence still exists on disk, so dropping those rows would make real runs
|
|
249
|
+
* unreachable from the surface that is supposed to list them.
|
|
250
|
+
*
|
|
251
|
+
* ONE ROW PER MANIFEST, not per id. The two are not the same thing: a manifest is addressed by its
|
|
252
|
+
* filename while its runs attribute to the id declared inside it, so several files can legitimately
|
|
253
|
+
* share an id. Collapsing them hides a real file; keying by id duplicates a row with no way to tell
|
|
254
|
+
* the copies apart. Both happen in practice — this repo has exactly that pair.
|
|
255
|
+
*
|
|
256
|
+
* Order puts a lab someone is working in first, then labs by how recently they ran, then declared
|
|
257
|
+
* labs that have never run (alphabetically BY WHAT IS DISPLAYED, so the order on screen is the
|
|
258
|
+
* order a reader can predict), then labs known only from history.
|
|
259
|
+
*/
|
|
260
|
+
export function labRows(declared, entries) {
|
|
261
|
+
const { labs, unattributed } = groupRunsByLab(entries);
|
|
262
|
+
const byId = new Map(labs.map((lab) => [lab.labId, lab]));
|
|
263
|
+
const runsOf = new Map();
|
|
264
|
+
for (const entry of entries) {
|
|
265
|
+
const labId = entry.lab?.id;
|
|
266
|
+
if (labId === undefined)
|
|
267
|
+
continue;
|
|
268
|
+
const bucket = runsOf.get(labId);
|
|
269
|
+
if (bucket === undefined)
|
|
270
|
+
runsOf.set(labId, [entry]);
|
|
271
|
+
else
|
|
272
|
+
bucket.push(entry);
|
|
273
|
+
}
|
|
274
|
+
const idCounts = new Map();
|
|
275
|
+
for (const lab of declared)
|
|
276
|
+
idCounts.set(lab.id, (idCounts.get(lab.id) ?? 0) + 1);
|
|
277
|
+
const build = (labId, manifest, isDeclared) => {
|
|
278
|
+
const rollup = byId.get(labId);
|
|
279
|
+
const manifestPath = manifest?.path;
|
|
280
|
+
return {
|
|
281
|
+
key: manifestPath ?? `id:${labId}`,
|
|
282
|
+
labId,
|
|
283
|
+
name: manifestPath === undefined ? labId : labNameFromPath(manifestPath),
|
|
284
|
+
...(manifest?.title === undefined ? {} : { title: manifest.title }),
|
|
285
|
+
...(manifestPath === undefined ? {} : { path: manifestPath }),
|
|
286
|
+
...(manifest?.origin === undefined ? {} : { origin: manifest.origin }),
|
|
287
|
+
declared: isDeclared,
|
|
288
|
+
// Replaced by assignLabels once the whole set is known; a label is only meaningful relative
|
|
289
|
+
// to the rows it sits beside.
|
|
290
|
+
label: manifest?.title ?? (manifestPath === undefined ? labId : labNameFromPath(manifestPath)),
|
|
291
|
+
sharesIdWith: Math.max(0, (idCounts.get(labId) ?? 0) - 1),
|
|
292
|
+
runs: rollup?.runs ?? 0,
|
|
293
|
+
live: rollup?.live ?? 0,
|
|
294
|
+
...(rollup?.latest === undefined ? {} : { latest: rollup.latest }),
|
|
295
|
+
liveRuns: rollup?.liveRuns ?? [],
|
|
296
|
+
expectation: expectationFor(runsOf.get(labId) ?? []),
|
|
297
|
+
liveExpectation: expectationFor(runsOf.get(labId) ?? [], "live")
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
const declaredIds = new Set(declared.map((lab) => lab.id));
|
|
301
|
+
const rows = [
|
|
302
|
+
...declared.map((lab) => build(lab.id, lab, true)),
|
|
303
|
+
...labs.filter((lab) => !declaredIds.has(lab.labId)).map((lab) => build(lab.labId, undefined, false))
|
|
304
|
+
];
|
|
305
|
+
// Resolve each row's label BEFORE sorting, so the list is ordered by what a reader actually sees.
|
|
306
|
+
assignLabels(rows);
|
|
307
|
+
const label = (row) => row.label;
|
|
308
|
+
rows.sort((left, right) => {
|
|
309
|
+
if (left.live !== right.live)
|
|
310
|
+
return right.live - left.live;
|
|
311
|
+
const rank = (row) => (row.runs > 0 ? 0 : row.declared ? 1 : 2);
|
|
312
|
+
if (rank(left) !== rank(right))
|
|
313
|
+
return rank(left) - rank(right);
|
|
314
|
+
if (left.runs > 0 && right.runs > 0) {
|
|
315
|
+
const recency = recencyOf(right.latest) - recencyOf(left.latest);
|
|
316
|
+
if (recency !== 0)
|
|
317
|
+
return recency;
|
|
318
|
+
}
|
|
319
|
+
const byLabel = label(left).localeCompare(label(right));
|
|
320
|
+
return byLabel !== 0 ? byLabel : left.key.localeCompare(right.key);
|
|
321
|
+
});
|
|
322
|
+
return { rows, unattributed };
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Give every row the shortest label that distinguishes it: title, else filename, else path.
|
|
326
|
+
*
|
|
327
|
+
* Falling straight back to the filename whenever a title repeats — rather than decorating the
|
|
328
|
+
* duplicate with a suffix — keeps the label something the operator can act on, because the filename
|
|
329
|
+
* is exactly what `humanish lab run` takes.
|
|
330
|
+
*/
|
|
331
|
+
function assignLabels(rows) {
|
|
332
|
+
const count = (values) => {
|
|
333
|
+
const counts = new Map();
|
|
334
|
+
for (const value of values)
|
|
335
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
336
|
+
return counts;
|
|
337
|
+
};
|
|
338
|
+
const titles = count(rows.map((row) => row.title ?? row.name));
|
|
339
|
+
const names = count(rows.map((row) => row.name));
|
|
340
|
+
for (const row of rows) {
|
|
341
|
+
const title = row.title ?? row.name;
|
|
342
|
+
if ((titles.get(title) ?? 0) === 1) {
|
|
343
|
+
row.label = title;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
row.label = (names.get(row.name) ?? 0) === 1 ? row.name : (row.path ?? row.name);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=run-projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-projection.js","sourceRoot":"","sources":["../src/run-projection.ts"],"names":[],"mappings":"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"}
|