@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/Terminal.ts
CHANGED
|
@@ -5,18 +5,18 @@ import * as Effect from "effect/Effect"
|
|
|
5
5
|
import * as Ref from "effect/Ref"
|
|
6
6
|
import * as Semaphore from "effect/Semaphore"
|
|
7
7
|
import type * as Scope from "effect/Scope"
|
|
8
|
-
import * as Schema from "effect/Schema"
|
|
9
|
-
import * as Stream from "effect/Stream"
|
|
10
8
|
import {
|
|
11
|
-
|
|
9
|
+
eventLanes,
|
|
12
10
|
type FlowEvent,
|
|
13
11
|
type FlowEventHub,
|
|
12
|
+
laneOf,
|
|
13
|
+
type LaneRef,
|
|
14
14
|
rosterEventMessage,
|
|
15
15
|
type ReviewFindings
|
|
16
16
|
} from "@llm4ts/flow/FlowEvents"
|
|
17
|
+
import { atLeast, presentationOf, Verbosity } from "./EventPresentation.ts"
|
|
17
18
|
|
|
18
|
-
export
|
|
19
|
-
export type Verbosity = typeof Verbosity.Type
|
|
19
|
+
export { Verbosity }
|
|
20
20
|
|
|
21
21
|
export const parseVerbosity = (value: string | undefined): Verbosity => {
|
|
22
22
|
switch (value?.trim().toLowerCase()) {
|
|
@@ -32,45 +32,10 @@ export const parseVerbosity = (value: string | undefined): Verbosity => {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const rendersEvent = (verbosity: Verbosity, event: FlowEvent): boolean => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// cleared it. None is a line of its own.
|
|
40
|
-
case "JudgmentObserved":
|
|
41
|
-
case "BlackboardRun":
|
|
42
|
-
case "UsageProgress":
|
|
43
|
-
case "ExecutorReleased":
|
|
44
|
-
case "StoryJudged":
|
|
45
|
-
case "RoundtableTurn":
|
|
46
|
-
case "BriefReported":
|
|
47
|
-
case "Timed":
|
|
48
|
-
case "TasksPlanned":
|
|
49
|
-
case "TaskStarted":
|
|
50
|
-
case "TaskCompleted":
|
|
51
|
-
case "StoryStatusChanged":
|
|
52
|
-
return false
|
|
53
|
-
// A gate or a setup may run for many minutes: its start is a line, so a
|
|
54
|
-
// long one reads as running, not stuck. Other work starts too often.
|
|
55
|
-
case "Began":
|
|
56
|
-
return event.kind === "gate" && verbosity !== "Quiet"
|
|
57
|
-
case "StageStarted":
|
|
58
|
-
case "StageCompleted":
|
|
59
|
-
case "StageFailed":
|
|
60
|
-
case "Aborted":
|
|
61
|
-
case "CapabilityDenied":
|
|
62
|
-
case "CapabilityUnenforceable":
|
|
63
|
-
case "EvidenceChecked":
|
|
64
|
-
case "ReviewFindingDemoted":
|
|
65
|
-
return true
|
|
66
|
-
// Every git read and gate command is a capability event: useful in a
|
|
67
|
-
// trace, noise on screen once several stories run.
|
|
68
|
-
case "TokensUsed":
|
|
69
|
-
case "CapabilityUsed":
|
|
70
|
-
return verbosity === "Verbose" || verbosity === "Debug"
|
|
71
|
-
default:
|
|
72
|
-
return verbosity !== "Quiet"
|
|
73
|
-
}
|
|
35
|
+
const { line } = presentationOf(event)
|
|
36
|
+
return typeof line === "function"
|
|
37
|
+
? line(event, verbosity)
|
|
38
|
+
: line !== "never" && atLeast(verbosity, line)
|
|
74
39
|
}
|
|
75
40
|
|
|
76
41
|
export const terminalSafe = (value: string): string => {
|
|
@@ -548,7 +513,6 @@ export interface TerminalRunStats {
|
|
|
548
513
|
}
|
|
549
514
|
|
|
550
515
|
export interface TerminalConsumer {
|
|
551
|
-
readonly consumed: Ref.Ref<number>
|
|
552
516
|
readonly stats: Effect.Effect<TerminalRunStats>
|
|
553
517
|
readonly awaitDrained: (timeout?: Duration.Input) => Effect.Effect<void>
|
|
554
518
|
}
|
|
@@ -562,33 +526,20 @@ interface OpenStage {
|
|
|
562
526
|
readonly startedAt: number
|
|
563
527
|
}
|
|
564
528
|
|
|
565
|
-
/**
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
case "TasksPlanned":
|
|
580
|
-
case "TaskStarted":
|
|
581
|
-
case "TaskCompleted":
|
|
582
|
-
return event.lane === undefined
|
|
583
|
-
? undefined
|
|
584
|
-
: {
|
|
585
|
-
lane: event.lane,
|
|
586
|
-
...(event.executor === undefined ? {} : { executor: event.executor })
|
|
587
|
-
}
|
|
588
|
-
default:
|
|
589
|
-
return undefined
|
|
590
|
-
}
|
|
591
|
-
}
|
|
529
|
+
/**
|
|
530
|
+
* The lane an event belongs to on screen (a concurrent story), if it carries
|
|
531
|
+
* one. This is `laneOf` with two exclusions, both kept as they were:
|
|
532
|
+
* - an event whose producer names its lane (a verdict, a roundtable turn, an
|
|
533
|
+
* evidence check, a parked item) prints, when it prints, as a line of the
|
|
534
|
+
* run: only the events `withLane` stamps drive a lane's row;
|
|
535
|
+
* - `Timed` and `Began`: one arriving after its lane's last stage closed
|
|
536
|
+
* would reopen a row that never closes (`UsageProgress` has the same
|
|
537
|
+
* hazard and is guarded where the consumer reads it).
|
|
538
|
+
*/
|
|
539
|
+
export const laneOfEvent = (event: FlowEvent): LaneRef | undefined =>
|
|
540
|
+
eventLanes[event._tag].lane !== "stamped" || event._tag === "Timed" || event._tag === "Began"
|
|
541
|
+
? undefined
|
|
542
|
+
: laneOf(event)
|
|
592
543
|
|
|
593
544
|
/** `bonifici-list · lemonade-deepseek` — how a lane is named on screen. */
|
|
594
545
|
export const laneLabel = (lane: string, executor: string | undefined): string =>
|
|
@@ -744,9 +695,7 @@ export const consumeTerminalEvents = Effect.fn("@llm4ts/runner/Terminal.consume"
|
|
|
744
695
|
// Tokens of events outside any lane: the whole run, for a flow with one coder.
|
|
745
696
|
const runTokens = emptyTally()
|
|
746
697
|
const runInFlight = new Map<string, TokenTally>()
|
|
747
|
-
const consumed = yield* Ref.make(0)
|
|
748
698
|
const statsRef = yield* Ref.make<TerminalRunStats>({ stagesCompleted: 0, stagesFailed: 0 })
|
|
749
|
-
const subscription = yield* events.subscribe
|
|
750
699
|
const palette = surface.palette
|
|
751
700
|
const timestampPrefix =
|
|
752
701
|
options.timestamps === true
|
|
@@ -800,99 +749,93 @@ export const consumeTerminalEvents = Effect.fn("@llm4ts/runner/Terminal.consume"
|
|
|
800
749
|
return `${line} ${palette.dim(`(${formatDurationMs(now - closed.startedAt)})`)}`
|
|
801
750
|
})
|
|
802
751
|
|
|
803
|
-
yield*
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
lanes.
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (tagged.executor !== undefined) {
|
|
829
|
-
state.executor = tagged.executor
|
|
830
|
-
}
|
|
831
|
-
let closed: OpenStage | undefined
|
|
832
|
-
if (closesChild(event)) {
|
|
833
|
-
state.depth = Math.max(0, state.depth - 1)
|
|
834
|
-
}
|
|
835
|
-
const lineDepth = state.depth
|
|
836
|
-
if (event._tag === "StageStarted") {
|
|
837
|
-
state.depth += 1
|
|
838
|
-
state.stages = [...state.stages, { name: terminalSafe(event.stage), startedAt: now }]
|
|
839
|
-
} else if (closesChild(event)) {
|
|
840
|
-
closed = state.stages.at(-1)
|
|
841
|
-
state.stages = state.stages.slice(0, -1)
|
|
842
|
-
yield* countStage(event)
|
|
843
|
-
} else if (event._tag === "ToolUse") {
|
|
844
|
-
state.tools += 1
|
|
845
|
-
state.lastTool = { tool: terminalSafe(event.tool), at: now }
|
|
846
|
-
}
|
|
847
|
-
addTokens(state.tokens, event)
|
|
848
|
-
trackProgress(state.inFlight, event)
|
|
849
|
-
if (state.stages.length === 0 && closesChild(event)) {
|
|
850
|
-
lanes.delete(tagged.lane)
|
|
851
|
-
}
|
|
852
|
-
if (rendersLanedEvent(verbosity, event)) {
|
|
853
|
-
const tag = palette.lane(state.index, `[${laneLabel(tagged.lane, state.executor)}]`)
|
|
854
|
-
const line = yield* withDuration(event, terminalLine(event, palette, verbosity), closed)
|
|
855
|
-
const prefix = yield* timestampPrefix
|
|
856
|
-
yield* surface.log(`${prefix}${tag} ${indentBlock(lineDepth, line)}`)
|
|
857
|
-
}
|
|
858
|
-
yield* refreshStatus
|
|
859
|
-
yield* Ref.update(consumed, (count) => count + 1)
|
|
860
|
-
return
|
|
752
|
+
const consumer = yield* events.consume("terminal", (event) =>
|
|
753
|
+
Effect.gen(function* () {
|
|
754
|
+
const tagged = laneOfEvent(event)
|
|
755
|
+
if (tagged !== undefined && event._tag === "UsageProgress" && !lanes.has(tagged.lane)) {
|
|
756
|
+
// A call that ends after its story's row closed: nothing to show.
|
|
757
|
+
return
|
|
758
|
+
}
|
|
759
|
+
if (tagged !== undefined) {
|
|
760
|
+
const now = yield* Clock.currentTimeMillis
|
|
761
|
+
const state =
|
|
762
|
+
lanes.get(tagged.lane) ??
|
|
763
|
+
({
|
|
764
|
+
index: lanes.size,
|
|
765
|
+
depth: 0,
|
|
766
|
+
stages: [],
|
|
767
|
+
tools: 0,
|
|
768
|
+
executor: undefined,
|
|
769
|
+
startedAt: now,
|
|
770
|
+
tokens: emptyTally(),
|
|
771
|
+
inFlight: new Map(),
|
|
772
|
+
lastTool: undefined
|
|
773
|
+
} satisfies LaneState)
|
|
774
|
+
lanes.set(tagged.lane, state)
|
|
775
|
+
if (tagged.executor !== undefined) {
|
|
776
|
+
state.executor = tagged.executor
|
|
861
777
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
yield* refreshStatus
|
|
778
|
+
let closed: OpenStage | undefined
|
|
779
|
+
if (closesChild(event)) {
|
|
780
|
+
state.depth = Math.max(0, state.depth - 1)
|
|
866
781
|
}
|
|
867
|
-
const
|
|
868
|
-
? yield* Ref.updateAndGet(depth, (value) => Math.max(0, value - 1))
|
|
869
|
-
: yield* Ref.get(depth)
|
|
870
|
-
let closedStage: OpenStage | undefined
|
|
782
|
+
const lineDepth = state.depth
|
|
871
783
|
if (event._tag === "StageStarted") {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
const startedAt = yield* Clock.currentTimeMillis
|
|
875
|
-
yield* Ref.update(stages, (current) => [...current, { name: active, startedAt }])
|
|
876
|
-
yield* refreshStatus
|
|
784
|
+
state.depth += 1
|
|
785
|
+
state.stages = [...state.stages, { name: terminalSafe(event.stage), startedAt: now }]
|
|
877
786
|
} else if (closesChild(event)) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
yield* refreshStatus
|
|
787
|
+
closed = state.stages.at(-1)
|
|
788
|
+
state.stages = state.stages.slice(0, -1)
|
|
881
789
|
yield* countStage(event)
|
|
790
|
+
} else if (event._tag === "ToolUse") {
|
|
791
|
+
state.tools += 1
|
|
792
|
+
state.lastTool = { tool: terminalSafe(event.tool), at: now }
|
|
882
793
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
794
|
+
addTokens(state.tokens, event)
|
|
795
|
+
trackProgress(state.inFlight, event)
|
|
796
|
+
if (state.stages.length === 0 && closesChild(event)) {
|
|
797
|
+
lanes.delete(tagged.lane)
|
|
798
|
+
}
|
|
799
|
+
if (rendersLanedEvent(verbosity, event)) {
|
|
800
|
+
const tag = palette.lane(state.index, `[${laneLabel(tagged.lane, state.executor)}]`)
|
|
801
|
+
const line = yield* withDuration(event, terminalLine(event, palette, verbosity), closed)
|
|
889
802
|
const prefix = yield* timestampPrefix
|
|
890
|
-
yield* surface.log(`${prefix}${indentBlock(
|
|
803
|
+
yield* surface.log(`${prefix}${tag} ${indentBlock(lineDepth, line)}`)
|
|
891
804
|
}
|
|
892
|
-
yield*
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
805
|
+
yield* refreshStatus
|
|
806
|
+
return
|
|
807
|
+
}
|
|
808
|
+
if (event._tag === "TokensUsed" || event._tag === "UsageProgress") {
|
|
809
|
+
addTokens(runTokens, event)
|
|
810
|
+
trackProgress(runInFlight, event)
|
|
811
|
+
yield* refreshStatus
|
|
812
|
+
}
|
|
813
|
+
const currentDepth = closesChild(event)
|
|
814
|
+
? yield* Ref.updateAndGet(depth, (value) => Math.max(0, value - 1))
|
|
815
|
+
: yield* Ref.get(depth)
|
|
816
|
+
let closedStage: OpenStage | undefined
|
|
817
|
+
if (event._tag === "StageStarted") {
|
|
818
|
+
yield* Ref.update(depth, (value) => value + 1)
|
|
819
|
+
const active = terminalSafe(event.stage)
|
|
820
|
+
const startedAt = yield* Clock.currentTimeMillis
|
|
821
|
+
yield* Ref.update(stages, (current) => [...current, { name: active, startedAt }])
|
|
822
|
+
yield* refreshStatus
|
|
823
|
+
} else if (closesChild(event)) {
|
|
824
|
+
closedStage = (yield* Ref.get(stages)).at(-1)
|
|
825
|
+
yield* Ref.update(stages, (current) => current.slice(0, -1))
|
|
826
|
+
yield* refreshStatus
|
|
827
|
+
yield* countStage(event)
|
|
828
|
+
}
|
|
829
|
+
if (rendersEvent(verbosity, event)) {
|
|
830
|
+
const line = yield* withDuration(
|
|
831
|
+
event,
|
|
832
|
+
terminalLine(event, palette, verbosity),
|
|
833
|
+
closedStage
|
|
834
|
+
)
|
|
835
|
+
const prefix = yield* timestampPrefix
|
|
836
|
+
yield* surface.log(`${prefix}${indentBlock(currentDepth, line)}`)
|
|
837
|
+
}
|
|
838
|
+
})
|
|
896
839
|
)
|
|
897
840
|
// Elapsed time and "Running <tool>…" age between events too.
|
|
898
841
|
yield* Effect.sleep("1 second").pipe(
|
|
@@ -901,8 +844,8 @@ export const consumeTerminalEvents = Effect.fn("@llm4ts/runner/Terminal.consume"
|
|
|
901
844
|
Effect.forkScoped
|
|
902
845
|
)
|
|
903
846
|
// Display only, so a drain that gives up just means a trailing line was
|
|
904
|
-
// never printed; the
|
|
847
|
+
// never printed; the hub already bounds the wait.
|
|
905
848
|
const awaitDrained = (timeout?: Duration.Input): Effect.Effect<void> =>
|
|
906
|
-
Effect.asVoid(
|
|
907
|
-
return {
|
|
849
|
+
Effect.asVoid(consumer.caughtUp(timeout))
|
|
850
|
+
return { stats: Ref.get(statsRef), awaitDrained }
|
|
908
851
|
})
|
package/src/Tools.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { builtinTools } from "@llm4ts/flow/tools/Builtin"
|
|
|
7
7
|
import type { ToolReport, ToolSpec } from "@llm4ts/flow/tools/Tool"
|
|
8
8
|
import { isToolName, renderUsage, specOf, type ToolShape } from "@llm4ts/flow/tools/Tool"
|
|
9
9
|
import type { ToolPreset } from "@llm4ts/flow/Pack"
|
|
10
|
+
import { settingValue } from "@llm4ts/flow/Settings"
|
|
10
11
|
import type { DiscoveredKit } from "./Kits.ts"
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -177,7 +178,7 @@ export const toolBrief = (
|
|
|
177
178
|
specs: ReadonlyMap<string, ToolSpec>,
|
|
178
179
|
environment: Readonly<Record<string, string | undefined>> = process.env
|
|
179
180
|
): string | undefined => {
|
|
180
|
-
if (
|
|
181
|
+
if (!settingValue("LLM4TS_TOOL_BRIEF", environment)) {
|
|
181
182
|
return undefined
|
|
182
183
|
}
|
|
183
184
|
const lines = [
|
package/src/Transcripts.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as Effect from "effect/Effect"
|
|
|
4
4
|
import * as Schema from "effect/Schema"
|
|
5
5
|
import { summariseToolArgs } from "@llm4ts/flow/Activity"
|
|
6
6
|
import { TranscriptEntry, type TranscriptSink } from "@llm4ts/flow/Transcript"
|
|
7
|
+
import { settingValue } from "@llm4ts/flow/Settings"
|
|
7
8
|
import { duration } from "./AgentTree.ts"
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -14,10 +15,10 @@ import { duration } from "./AgentTree.ts"
|
|
|
14
15
|
* never fails the run.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
|
-
/** `LLM4TS_TRANSCRIPT=on` (what `--transcript` sets)
|
|
18
|
+
/** `LLM4TS_TRANSCRIPT=on` (what `--transcript` sets). */
|
|
18
19
|
export const transcriptsWanted = (
|
|
19
20
|
environment: Readonly<Record<string, string | undefined>>
|
|
20
|
-
): boolean =>
|
|
21
|
+
): boolean => settingValue("LLM4TS_TRANSCRIPT", environment) === true
|
|
21
22
|
|
|
22
23
|
export const transcriptFileOf = (lane: string | undefined): string =>
|
|
23
24
|
`${(lane ?? "run").replace(/[^A-Za-z0-9._-]/gu, "_")}.jsonl`
|