@llm4ts/runner 3.2.6 → 3.4.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/dist/AgentTree.d.ts +2 -3
- package/dist/AgentTree.d.ts.map +1 -1
- package/dist/AgentTree.js +8 -20
- package/dist/AgentTree.js.map +1 -1
- package/dist/AgentTreeSurface.d.ts +1 -1
- package/dist/AgentTreeSurface.d.ts.map +1 -1
- package/dist/AgentTreeSurface.js +2 -5
- package/dist/AgentTreeSurface.js.map +1 -1
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +2 -1
- package/dist/Cli.js.map +1 -1
- package/dist/Connectors.d.ts +33 -2
- package/dist/Connectors.d.ts.map +1 -1
- package/dist/Connectors.js +85 -89
- package/dist/Connectors.js.map +1 -1
- package/dist/Doctor.d.ts.map +1 -1
- package/dist/Doctor.js +8 -11
- package/dist/Doctor.js.map +1 -1
- package/dist/EventPresentation.d.ts +35 -0
- package/dist/EventPresentation.d.ts.map +1 -0
- package/dist/EventPresentation.js +133 -0
- package/dist/EventPresentation.js.map +1 -0
- package/dist/ExecutorRoster.d.ts.map +1 -1
- package/dist/ExecutorRoster.js +4 -6
- package/dist/ExecutorRoster.js.map +1 -1
- package/dist/FlowArgs.d.ts +1 -1
- package/dist/FlowArgs.d.ts.map +1 -1
- package/dist/FlowArgs.js +4 -2
- package/dist/FlowArgs.js.map +1 -1
- package/dist/FlowRunner.d.ts +3 -1
- package/dist/FlowRunner.d.ts.map +1 -1
- package/dist/FlowRunner.js +31 -38
- package/dist/FlowRunner.js.map +1 -1
- package/dist/NodeGeminiCliExecutor.d.ts.map +1 -1
- package/dist/NodeGeminiCliExecutor.js +6 -4
- package/dist/NodeGeminiCliExecutor.js.map +1 -1
- package/dist/NodeGeminiModelBridge.d.ts.map +1 -1
- package/dist/NodeGeminiModelBridge.js +3 -1
- package/dist/NodeGeminiModelBridge.js.map +1 -1
- package/dist/Otel.d.ts.map +1 -1
- package/dist/Otel.js +4 -5
- package/dist/Otel.js.map +1 -1
- package/dist/Packs.d.ts.map +1 -1
- package/dist/Packs.js +2 -1
- package/dist/Packs.js.map +1 -1
- package/dist/PiModels.d.ts.map +1 -1
- package/dist/PiModels.js +6 -9
- package/dist/PiModels.js.map +1 -1
- package/dist/Profile.d.ts +4 -0
- package/dist/Profile.d.ts.map +1 -1
- package/dist/Profile.js +45 -75
- package/dist/Profile.js.map +1 -1
- package/dist/RunReaders.d.ts +1 -0
- package/dist/RunReaders.d.ts.map +1 -1
- package/dist/RunReaders.js +4 -51
- package/dist/RunReaders.js.map +1 -1
- package/dist/Terminal.d.ts +14 -11
- package/dist/Terminal.d.ts.map +1 -1
- package/dist/Terminal.js +25 -78
- package/dist/Terminal.js.map +1 -1
- package/dist/Tools.d.ts.map +1 -1
- package/dist/Tools.js +2 -1
- package/dist/Tools.js.map +1 -1
- package/dist/Transcripts.d.ts +1 -1
- package/dist/Transcripts.d.ts.map +1 -1
- package/dist/Transcripts.js +3 -2
- package/dist/Transcripts.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/AgentTree.ts +16 -25
- package/src/AgentTreeSurface.ts +6 -11
- package/src/Cli.ts +2 -1
- package/src/Connectors.ts +141 -107
- package/src/Doctor.ts +10 -12
- package/src/EventPresentation.ts +168 -0
- package/src/ExecutorRoster.ts +4 -6
- package/src/FlowArgs.ts +7 -2
- package/src/FlowRunner.ts +41 -55
- package/src/NodeGeminiCliExecutor.ts +7 -4
- package/src/NodeGeminiModelBridge.ts +4 -1
- package/src/Otel.ts +4 -6
- package/src/Packs.ts +2 -1
- package/src/PiModels.ts +6 -10
- package/src/Profile.ts +64 -81
- package/src/RunReaders.ts +4 -53
- package/src/Terminal.ts +105 -162
- package/src/Tools.ts +2 -1
- package/src/Transcripts.ts +3 -2
- package/src/index.ts +3 -0
package/src/Profile.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect"
|
|
2
2
|
import * as Schema from "effect/Schema"
|
|
3
3
|
import type { FlowError } from "@llm4ts/flow/FlowError"
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
LaneKind,
|
|
6
|
+
laneClosed,
|
|
7
|
+
laneOf,
|
|
8
|
+
laneOpened,
|
|
9
|
+
type FlowEvent,
|
|
10
|
+
type Timed
|
|
11
|
+
} from "@llm4ts/flow/FlowEvents"
|
|
5
12
|
import { readTrace } from "@llm4ts/flow/Replay"
|
|
6
13
|
import { duration, treeInputsOfTrace, type TreeInput } from "./AgentTree.ts"
|
|
7
14
|
|
|
8
15
|
export { duration }
|
|
16
|
+
import { presentationOf } from "./EventPresentation.ts"
|
|
9
17
|
import { formatCount } from "./Terminal.ts"
|
|
10
18
|
import {
|
|
11
19
|
resolveTraceTarget,
|
|
@@ -81,6 +89,8 @@ export type ToolCalls = typeof ToolCalls.Type
|
|
|
81
89
|
|
|
82
90
|
export const StoryProfile = Schema.Struct({
|
|
83
91
|
id: Schema.String,
|
|
92
|
+
/** What the lane stands for when it is not a story: a roundtable attendee, a taskforce specialist. */
|
|
93
|
+
kind: Schema.optionalKey(LaneKind),
|
|
84
94
|
executor: Schema.optionalKey(Schema.String),
|
|
85
95
|
status: Schema.Literals(["done", "failed", "running"]),
|
|
86
96
|
wallMs: Ms,
|
|
@@ -169,6 +179,7 @@ interface TimedAt {
|
|
|
169
179
|
|
|
170
180
|
interface Lane {
|
|
171
181
|
readonly id: string
|
|
182
|
+
readonly kind: LaneKind
|
|
172
183
|
start: number
|
|
173
184
|
end: number | undefined
|
|
174
185
|
status: "done" | "failed" | "running"
|
|
@@ -204,8 +215,6 @@ interface Lease {
|
|
|
204
215
|
readonly label: string | undefined
|
|
205
216
|
}
|
|
206
217
|
|
|
207
|
-
const storyStage = /^story (\S+)$/u
|
|
208
|
-
|
|
209
218
|
/** Events a coder turn emits while it runs: not where a model call starts. */
|
|
210
219
|
const withinTurn: ReadonlySet<FlowEvent["_tag"]> = new Set([
|
|
211
220
|
"ToolUse",
|
|
@@ -230,66 +239,21 @@ const groupBy = <A>(items: ReadonlyArray<A>, key: (item: A) => string): Map<stri
|
|
|
230
239
|
return groups
|
|
231
240
|
}
|
|
232
241
|
|
|
233
|
-
const laneOf = (event: FlowEvent): string | undefined =>
|
|
234
|
-
"lane" in event && typeof event.lane === "string" ? event.lane : undefined
|
|
235
|
-
|
|
236
242
|
/** An event as the gaps name it: its kind, never its content. */
|
|
237
243
|
const describe = (event: FlowEvent): string => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return "a stage start"
|
|
241
|
-
case "StageCompleted":
|
|
242
|
-
return "a stage end"
|
|
243
|
-
case "StageFailed":
|
|
244
|
-
return "a stage failure"
|
|
245
|
-
case "ToolUse":
|
|
246
|
-
return "a coder tool call"
|
|
247
|
-
case "TokensUsed":
|
|
248
|
-
return "a token report"
|
|
249
|
-
case "Info":
|
|
250
|
-
return "a log note"
|
|
251
|
-
case "ReviewFindings":
|
|
252
|
-
return "a review round"
|
|
253
|
-
case "StoryJudged":
|
|
254
|
-
return "a judge verdict"
|
|
255
|
-
case "AssistantMessage":
|
|
256
|
-
return "an assistant message"
|
|
257
|
-
case "CapabilityUsed":
|
|
258
|
-
case "CapabilityDenied":
|
|
259
|
-
case "CapabilityUnenforceable":
|
|
260
|
-
return "a capability check"
|
|
261
|
-
case "EvidenceChecked":
|
|
262
|
-
return "an evidence check"
|
|
263
|
-
case "ReviewFindingDemoted":
|
|
264
|
-
return "a review finding demoted"
|
|
265
|
-
case "ExecutorLeased":
|
|
266
|
-
case "ExecutorReleased":
|
|
267
|
-
case "ExecutorExcluded":
|
|
268
|
-
case "ExecutorResumed":
|
|
269
|
-
case "ExecutorHandedOver":
|
|
270
|
-
return "a roster change"
|
|
271
|
-
case "Timed":
|
|
272
|
-
switch (event.kind) {
|
|
273
|
-
case "model":
|
|
274
|
-
return `the end of a ${event.label} call`
|
|
275
|
-
case "tool":
|
|
276
|
-
return `the end of tool ${event.label}`
|
|
277
|
-
case "gate":
|
|
278
|
-
return `the end of gate ${event.label}`
|
|
279
|
-
case "wait":
|
|
280
|
-
return `the end of a wait for the ${event.label}`
|
|
281
|
-
default:
|
|
282
|
-
return `the end of ${event.label}`
|
|
283
|
-
}
|
|
284
|
-
case "Began":
|
|
285
|
-
return event.kind === "model"
|
|
286
|
-
? `the start of a ${event.label} call`
|
|
287
|
-
: `the start of ${event.kind} ${event.label}`
|
|
288
|
-
default:
|
|
289
|
-
return "an event"
|
|
290
|
-
}
|
|
244
|
+
const { kind } = presentationOf(event)
|
|
245
|
+
return typeof kind === "function" ? kind(event) : kind
|
|
291
246
|
}
|
|
292
247
|
|
|
248
|
+
const bareLaneStage = /^(\S+) (\S+)$/u
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* A stage that is the lane's own and not one of its coder's tasks: its steps
|
|
252
|
+
* (`story home: setup`) and a bare `<kind> <id>` that names another lane.
|
|
253
|
+
*/
|
|
254
|
+
const ownStage = (lane: Lane, stage: string): boolean =>
|
|
255
|
+
bareLaneStage.exec(stage)?.[1] === lane.kind || stage.startsWith(`${lane.kind} ${lane.id}:`)
|
|
256
|
+
|
|
293
257
|
const gapThresholdMs = 30_000
|
|
294
258
|
|
|
295
259
|
/** The parts of each step between two lane events that no timed interval covers, largest first. */
|
|
@@ -346,14 +310,15 @@ export const profileOf = (inputs: ReadonlyArray<TreeInput>): ProfileReport => {
|
|
|
346
310
|
continue
|
|
347
311
|
}
|
|
348
312
|
const { event, at } = input
|
|
349
|
-
const
|
|
350
|
-
const lane =
|
|
313
|
+
const tagged = laneOf(event)
|
|
314
|
+
const lane = tagged === undefined ? undefined : lanes.get(tagged.lane)
|
|
351
315
|
switch (event._tag) {
|
|
352
316
|
case "StageStarted": {
|
|
353
|
-
const
|
|
354
|
-
if (
|
|
355
|
-
lanes.set(
|
|
356
|
-
id:
|
|
317
|
+
const opened = laneOpened(event)
|
|
318
|
+
if (opened !== undefined) {
|
|
319
|
+
lanes.set(opened.id, {
|
|
320
|
+
id: opened.id,
|
|
321
|
+
kind: opened.kind,
|
|
357
322
|
start: at,
|
|
358
323
|
end: undefined,
|
|
359
324
|
status: "running",
|
|
@@ -377,11 +342,7 @@ export const profileOf = (inputs: ReadonlyArray<TreeInput>): ProfileReport => {
|
|
|
377
342
|
})
|
|
378
343
|
continue
|
|
379
344
|
}
|
|
380
|
-
if (
|
|
381
|
-
lane !== undefined &&
|
|
382
|
-
!storyStage.test(event.stage) &&
|
|
383
|
-
!event.stage.startsWith(`story ${lane.id}:`)
|
|
384
|
-
) {
|
|
345
|
+
if (lane !== undefined && !ownStage(lane, event.stage)) {
|
|
385
346
|
lane.tasks += 1
|
|
386
347
|
lane.firstTask = lane.firstTask ?? at
|
|
387
348
|
}
|
|
@@ -392,7 +353,7 @@ export const profileOf = (inputs: ReadonlyArray<TreeInput>): ProfileReport => {
|
|
|
392
353
|
if (event.stage === "story plan" && event.lane === undefined) {
|
|
393
354
|
planReady = at
|
|
394
355
|
}
|
|
395
|
-
if (lane !== undefined && event
|
|
356
|
+
if (lane !== undefined && laneClosed(event)?.kind === lane.kind) {
|
|
396
357
|
lane.end = at
|
|
397
358
|
lane.status = event._tag === "StageFailed" ? "failed" : "done"
|
|
398
359
|
}
|
|
@@ -462,7 +423,7 @@ export const profileOf = (inputs: ReadonlyArray<TreeInput>): ProfileReport => {
|
|
|
462
423
|
}
|
|
463
424
|
if (lane !== undefined) {
|
|
464
425
|
lane.moments.push({ at, what: describe(event) })
|
|
465
|
-
lane.executor = lane.executor ??
|
|
426
|
+
lane.executor = lane.executor ?? tagged?.executor
|
|
466
427
|
if (!withinTurn.has(event._tag)) {
|
|
467
428
|
lane.last = at
|
|
468
429
|
}
|
|
@@ -673,6 +634,7 @@ const storyProfile = (
|
|
|
673
634
|
lowConfidenceTasks: lane.lowConfidenceTasks,
|
|
674
635
|
...(lane.reviewDiffLines === undefined ? {} : { reviewDiffLines: lane.reviewDiffLines }),
|
|
675
636
|
id: lane.id,
|
|
637
|
+
...(lane.kind === "story" ? {} : { kind: lane.kind }),
|
|
676
638
|
...(lane.executor === undefined ? {} : { executor: lane.executor }),
|
|
677
639
|
status: lane.status,
|
|
678
640
|
wallMs,
|
|
@@ -711,6 +673,24 @@ const share = (ms: number, of: number): string => (of <= 0 ? "" : `${Math.round(
|
|
|
711
673
|
|
|
712
674
|
const plural = (count: number, word: string): string => `${count} ${word}${count === 1 ? "" : "s"}`
|
|
713
675
|
|
|
676
|
+
interface LaneWord {
|
|
677
|
+
readonly one: string
|
|
678
|
+
readonly many: string
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** What a report calls its lanes: their kind when they share one, stories when it has none. */
|
|
682
|
+
const laneWord = (stories: ReadonlyArray<StoryProfile>): LaneWord => {
|
|
683
|
+
const kinds = new Set(stories.map((story) => story.kind ?? "story"))
|
|
684
|
+
const [kind = "story"] = kinds
|
|
685
|
+
return kinds.size > 1
|
|
686
|
+
? { one: "lane", many: "lanes" }
|
|
687
|
+
: kind === "story"
|
|
688
|
+
? { one: "story", many: "stories" }
|
|
689
|
+
: { one: kind, many: `${kind}s` }
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const capitalized = (word: string): string => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`
|
|
693
|
+
|
|
714
694
|
const findingsOf = (facts: {
|
|
715
695
|
readonly steps: { readonly avgSteps: number; readonly avgStepMs: number }
|
|
716
696
|
readonly toolCategories: ReadonlyArray<CountRow>
|
|
@@ -723,7 +703,9 @@ const findingsOf = (facts: {
|
|
|
723
703
|
readonly storyTime: number
|
|
724
704
|
readonly estimated: boolean
|
|
725
705
|
}): ReadonlyArray<Finding> => {
|
|
726
|
-
const
|
|
706
|
+
const word = laneWord(facts.stories)
|
|
707
|
+
const ofStories = (ms: number) =>
|
|
708
|
+
`${duration(ms)} (${share(ms, facts.storyTime)} of ${word.one} time)`
|
|
727
709
|
const byRole = [...groupBy(facts.models, (row) => row.role).entries()].map(([role, rows]) => {
|
|
728
710
|
const calls = sum(rows.map((row) => row.calls))
|
|
729
711
|
const ms = sum(rows.map((row) => row.ms))
|
|
@@ -853,6 +835,7 @@ const table = (
|
|
|
853
835
|
|
|
854
836
|
export const renderProfile = (report: ProfileReport): string => {
|
|
855
837
|
const storyTime = sum(report.stories.map((story) => story.wallMs))
|
|
838
|
+
const word = laneWord(report.stories)
|
|
856
839
|
const categories: ReadonlyArray<readonly [string, number]> = [
|
|
857
840
|
[report.estimated ? "model + tools + gates (estimated)" : "model", report.totals.model],
|
|
858
841
|
["coder tools", report.totals.tools],
|
|
@@ -862,7 +845,7 @@ export const renderProfile = (report: ProfileReport): string => {
|
|
|
862
845
|
["unaccounted", report.totals.unaccounted]
|
|
863
846
|
]
|
|
864
847
|
return [
|
|
865
|
-
`llm4ts profile · ${duration(report.wallMs)} wall · ${report.stories.length} ${report.stories.length === 1 ?
|
|
848
|
+
`llm4ts profile · ${duration(report.wallMs)} wall · ${report.stories.length} ${report.stories.length === 1 ? word.one : word.many}${
|
|
866
849
|
report.estimated
|
|
867
850
|
? " · estimated: this trace has no timings (before 2.22), model time is read from gaps"
|
|
868
851
|
: ""
|
|
@@ -873,16 +856,16 @@ export const renderProfile = (report: ProfileReport): string => {
|
|
|
873
856
|
? [" nothing timed yet"]
|
|
874
857
|
: report.findings.map((finding, index) => ` ${index + 1}. ${finding.text}`)),
|
|
875
858
|
"",
|
|
876
|
-
|
|
859
|
+
`${capitalized(word.one)} time`,
|
|
877
860
|
...table(
|
|
878
861
|
["category", "time", "share"],
|
|
879
862
|
categories.map(([name, ms]) => [name, duration(ms), share(ms, storyTime)])
|
|
880
863
|
),
|
|
881
864
|
"",
|
|
882
|
-
|
|
865
|
+
capitalized(word.many),
|
|
883
866
|
...table(
|
|
884
867
|
[
|
|
885
|
-
|
|
868
|
+
word.one,
|
|
886
869
|
"status",
|
|
887
870
|
"wall",
|
|
888
871
|
"queued",
|
|
@@ -932,10 +915,10 @@ export const renderProfile = (report: ProfileReport): string => {
|
|
|
932
915
|
? []
|
|
933
916
|
: [
|
|
934
917
|
"",
|
|
935
|
-
|
|
918
|
+
`Coder work per ${word.one}`,
|
|
936
919
|
...table(
|
|
937
920
|
[
|
|
938
|
-
|
|
921
|
+
word.one,
|
|
939
922
|
"tasks",
|
|
940
923
|
"explore",
|
|
941
924
|
"edit",
|
|
@@ -962,7 +945,7 @@ export const renderProfile = (report: ProfileReport): string => {
|
|
|
962
945
|
? []
|
|
963
946
|
: [
|
|
964
947
|
"",
|
|
965
|
-
|
|
948
|
+
`Largest review diff per ${word.one}`,
|
|
966
949
|
...report.stories.flatMap((story) =>
|
|
967
950
|
story.reviewDiffLines === undefined
|
|
968
951
|
? []
|
|
@@ -987,7 +970,7 @@ export const renderProfile = (report: ProfileReport): string => {
|
|
|
987
970
|
? []
|
|
988
971
|
: [
|
|
989
972
|
"",
|
|
990
|
-
|
|
973
|
+
`Timed outside any ${word.one} (the run's own planning, or a seat no ${word.one} owns)`,
|
|
991
974
|
...table(
|
|
992
975
|
["label", "count", "total"],
|
|
993
976
|
report.outside.map((row) => [row.label, String(row.count), duration(row.ms)])
|
package/src/RunReaders.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { loadVersioned, type PlainFileStoreShape } from "@llm4ts/flow/Persistenc
|
|
|
24
24
|
import { parsePlan } from "@llm4ts/flow/Plan"
|
|
25
25
|
import { readTrace } from "@llm4ts/flow/Replay"
|
|
26
26
|
import { emptyTree, reduceTree, treeInputsOfTrace, type TreeState } from "./AgentTree.ts"
|
|
27
|
+
import { digestedTags, presentationOf, type DigestLine } from "./EventPresentation.ts"
|
|
27
28
|
import { summarizeTree } from "./RunSummary.ts"
|
|
28
29
|
import { loadTranscript, tailLinesOf, type TranscriptFiles } from "./Transcripts.ts"
|
|
29
30
|
|
|
@@ -146,60 +147,10 @@ export const runStatusOf = (
|
|
|
146
147
|
})
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"StageFailed",
|
|
152
|
-
"Aborted",
|
|
153
|
-
"CapabilityUnenforceable",
|
|
154
|
-
"ExecutorExcluded",
|
|
155
|
-
"ExecutorHandedOver",
|
|
156
|
-
"StoryJudged",
|
|
157
|
-
"Began",
|
|
158
|
-
"RunEnded",
|
|
159
|
-
"CriterionChecked",
|
|
160
|
-
"Parked"
|
|
161
|
-
])
|
|
150
|
+
/** Trace kinds a digest shows: the events with a digest line, and the run's end (a trace line, not an event). */
|
|
151
|
+
export const meaningfulKinds: ReadonlySet<string> = new Set([...digestedTags, "RunEnded"])
|
|
162
152
|
|
|
163
|
-
const textOf = (
|
|
164
|
-
event: FlowEvent
|
|
165
|
-
): { readonly text: string; readonly lane?: string } | undefined => {
|
|
166
|
-
switch (event._tag) {
|
|
167
|
-
case "StoryStatusChanged":
|
|
168
|
-
return { text: `story ${event.id} ${event.status}`, lane: event.id }
|
|
169
|
-
case "CriterionChecked":
|
|
170
|
-
return { text: `criterion ${event.tag} ${event.status}`, lane: event.story }
|
|
171
|
-
case "Parked":
|
|
172
|
-
return { text: `parked ${event.item}: ${event.reason}`, lane: event.story }
|
|
173
|
-
case "StageFailed":
|
|
174
|
-
return {
|
|
175
|
-
text: `${event.stage} failed: ${event.message}`,
|
|
176
|
-
...(event.lane === undefined ? {} : { lane: event.lane })
|
|
177
|
-
}
|
|
178
|
-
case "Aborted":
|
|
179
|
-
return { text: `aborted: ${event.message}` }
|
|
180
|
-
case "CapabilityUnenforceable":
|
|
181
|
-
return { text: `capability unenforceable: ${event.detail}` }
|
|
182
|
-
case "ExecutorExcluded":
|
|
183
|
-
return { text: `${event.executor} excluded: ${event.reason}` }
|
|
184
|
-
case "ExecutorHandedOver":
|
|
185
|
-
return {
|
|
186
|
-
text: `${event.from} handed over ${event.role} (${event.scope}): ${event.reason}`,
|
|
187
|
-
...(event.label === undefined ? {} : { lane: event.label })
|
|
188
|
-
}
|
|
189
|
-
case "StoryJudged":
|
|
190
|
-
return {
|
|
191
|
-
text: `${event.lane} judged round ${event.round}: ${event.cleared ? "cleared" : `${event.issues} issues`}`,
|
|
192
|
-
lane: event.lane
|
|
193
|
-
}
|
|
194
|
-
case "Began":
|
|
195
|
-
return {
|
|
196
|
-
text: `began ${event.kind} ${event.label}`,
|
|
197
|
-
...(event.lane === undefined ? {} : { lane: event.lane })
|
|
198
|
-
}
|
|
199
|
-
default:
|
|
200
|
-
return undefined
|
|
201
|
-
}
|
|
202
|
-
}
|
|
153
|
+
const textOf = (event: FlowEvent): DigestLine | undefined => presentationOf(event).digest?.(event)
|
|
203
154
|
|
|
204
155
|
export const meaningfulEventsAfter = (
|
|
205
156
|
lines: ReadonlyArray<TraceLine>,
|