@llm4ts/runner 3.3.0 → 4.0.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 +27 -2
- package/dist/Connectors.d.ts.map +1 -1
- package/dist/Connectors.js +76 -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 +132 -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 +13 -4
- package/dist/FlowRunner.d.ts.map +1 -1
- package/dist/FlowRunner.js +50 -39
- package/dist/FlowRunner.js.map +1 -1
- package/dist/NodeGeminiCliExecutor.d.ts.map +1 -1
- package/dist/NodeGeminiCliExecutor.js +7 -4
- package/dist/NodeGeminiCliExecutor.js.map +1 -1
- package/dist/NodeGeminiModelBridge.d.ts.map +1 -1
- package/dist/NodeGeminiModelBridge.js +4 -1
- package/dist/NodeGeminiModelBridge.js.map +1 -1
- package/dist/NodeHttpClient.d.ts.map +1 -1
- package/dist/NodeHttpClient.js +1 -0
- package/dist/NodeHttpClient.js.map +1 -1
- package/dist/Otel.d.ts.map +1 -1
- package/dist/Otel.js +5 -5
- package/dist/Otel.js.map +1 -1
- package/dist/Package.d.ts.map +1 -1
- package/dist/Package.js +1 -0
- package/dist/Package.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/Terminal.d.ts +13 -11
- package/dist/Terminal.d.ts.map +1 -1
- package/dist/Terminal.js +24 -80
- 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/WindowsCommand.d.ts.map +1 -1
- package/dist/WindowsCommand.js +1 -0
- package/dist/WindowsCommand.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/package.json +9 -21
- package/src/AgentTree.ts +16 -25
- package/src/AgentTreeSurface.ts +6 -11
- package/src/Cli.ts +2 -1
- package/src/Connectors.ts +131 -107
- package/src/Doctor.ts +10 -12
- package/src/EventPresentation.ts +167 -0
- package/src/ExecutorRoster.ts +4 -6
- package/src/FlowArgs.ts +7 -2
- package/src/FlowRunner.ts +66 -60
- package/src/NodeGeminiCliExecutor.ts +8 -4
- package/src/NodeGeminiModelBridge.ts +5 -1
- package/src/NodeHttpClient.ts +1 -0
- package/src/Otel.ts +5 -6
- package/src/Package.ts +1 -0
- package/src/Packs.ts +2 -1
- package/src/PiModels.ts +6 -10
- package/src/Terminal.ts +104 -165
- package/src/Tools.ts +2 -1
- package/src/Transcripts.ts +3 -2
- package/src/WindowsCommand.ts +1 -0
- package/src/index.ts +3 -2
- package/dist/Costs.d.ts +0 -52
- package/dist/Costs.d.ts.map +0 -1
- package/dist/Costs.js +0 -95
- package/dist/Costs.js.map +0 -1
- package/dist/McpStdio.d.ts +0 -7
- package/dist/McpStdio.d.ts.map +0 -1
- package/dist/McpStdio.js +0 -19
- package/dist/McpStdio.js.map +0 -1
- package/dist/Profile.d.ts +0 -309
- package/dist/Profile.d.ts.map +0 -1
- package/dist/Profile.js +0 -967
- package/dist/Profile.js.map +0 -1
- package/dist/ReviewFingerprint.d.ts +0 -2
- package/dist/ReviewFingerprint.d.ts.map +0 -1
- package/dist/ReviewFingerprint.js +0 -13
- package/dist/ReviewFingerprint.js.map +0 -1
- package/dist/RunReaders.d.ts +0 -69
- package/dist/RunReaders.d.ts.map +0 -1
- package/dist/RunReaders.js +0 -283
- package/dist/RunReaders.js.map +0 -1
- package/dist/StoryReaders.d.ts +0 -16
- package/dist/StoryReaders.d.ts.map +0 -1
- package/dist/StoryReaders.js +0 -107
- package/dist/StoryReaders.js.map +0 -1
- package/dist/Watch.d.ts +0 -68
- package/dist/Watch.d.ts.map +0 -1
- package/dist/Watch.js +0 -261
- package/dist/Watch.js.map +0 -1
- package/src/Costs.ts +0 -148
- package/src/McpStdio.ts +0 -41
- package/src/Profile.ts +0 -1213
- package/src/ReviewFingerprint.ts +0 -13
- package/src/RunReaders.ts +0 -419
- package/src/StoryReaders.ts +0 -157
- package/src/Watch.ts +0 -446
package/src/FlowRunner.ts
CHANGED
|
@@ -27,6 +27,8 @@ import { createConnectorRegistry } from "@llm4ts/core/providers/ConnectorFactori
|
|
|
27
27
|
import { ConnectorIds } from "@llm4ts/core/Models"
|
|
28
28
|
import { ProviderError } from "@llm4ts/core/Errors"
|
|
29
29
|
import { makeCostTracker, type CostTracker } from "@llm4ts/flow/CostTracker"
|
|
30
|
+
import { settingValue, unreadableEnvironment, type Environment } from "@llm4ts/flow/Settings"
|
|
31
|
+
import { ScriptUsage } from "./FlowArgs.ts"
|
|
30
32
|
import { runGeminiAcpBridge } from "./NodeGeminiAcpBridge.ts"
|
|
31
33
|
import { runGeminiModelBridge } from "./NodeGeminiModelBridge.ts"
|
|
32
34
|
import {
|
|
@@ -51,7 +53,7 @@ import {
|
|
|
51
53
|
type FlowError
|
|
52
54
|
} from "@llm4ts/flow/FlowError"
|
|
53
55
|
import {
|
|
54
|
-
|
|
56
|
+
type DrainReport,
|
|
55
57
|
FlowEvents,
|
|
56
58
|
FlowEventsValues,
|
|
57
59
|
Info,
|
|
@@ -119,9 +121,9 @@ import {
|
|
|
119
121
|
formatDurationMs,
|
|
120
122
|
makeTerminalSurface,
|
|
121
123
|
parseVerbosity,
|
|
122
|
-
type TerminalSurface
|
|
123
|
-
type Verbosity
|
|
124
|
+
type TerminalSurface
|
|
124
125
|
} from "./Terminal.ts"
|
|
126
|
+
import type { Verbosity } from "./EventPresentation.ts"
|
|
125
127
|
|
|
126
128
|
const describeSeat = (config: ConnectorConfig): string => {
|
|
127
129
|
const model = config.model === undefined ? "" : ` (${config.model})`
|
|
@@ -264,12 +266,6 @@ export interface FlowRunnerBundle {
|
|
|
264
266
|
readonly announceStartupNotices: Effect.Effect<void>
|
|
265
267
|
}
|
|
266
268
|
|
|
267
|
-
const truthy = (value: string | undefined): boolean =>
|
|
268
|
-
value !== undefined && ["1", "true", "on", "yes"].includes(value.trim().toLowerCase())
|
|
269
|
-
|
|
270
|
-
const switchedOff = (value: string | undefined): boolean =>
|
|
271
|
-
value !== undefined && ["0", "false", "no", "off"].includes(value.trim().toLowerCase())
|
|
272
|
-
|
|
273
269
|
const isPiSeat = (config: ConnectorConfig | undefined): config is CliConnectorConfig =>
|
|
274
270
|
config instanceof CliConnectorConfig && config.connectorId.value === ConnectorIds.Pi.value
|
|
275
271
|
|
|
@@ -289,7 +285,7 @@ const geminiBridgeSeats = Effect.fn("@llm4ts/runner/FlowRunner.geminiBridgeSeats
|
|
|
289
285
|
environment: Readonly<Record<string, string | undefined>>
|
|
290
286
|
): Effect.fn.Return<(config: ConnectorConfig) => ConnectorConfig, FlowError, Scope.Scope> {
|
|
291
287
|
const identity = (config: ConnectorConfig): ConnectorConfig => config
|
|
292
|
-
if (!
|
|
288
|
+
if (!settingValue("LLM4TS_GEMINI_BRIDGE", environment)) {
|
|
293
289
|
return identity
|
|
294
290
|
}
|
|
295
291
|
// The reasoning seat defaults to the coder, so a pi coder makes this a pi
|
|
@@ -304,7 +300,7 @@ const geminiBridgeSeats = Effect.fn("@llm4ts/runner/FlowRunner.geminiBridgeSeats
|
|
|
304
300
|
const resolution = resolveBridgeModel(
|
|
305
301
|
readPiModelsJson(),
|
|
306
302
|
port,
|
|
307
|
-
environment
|
|
303
|
+
settingValue("LLM4TS_GEMINI_BRIDGE_MODEL", environment)
|
|
308
304
|
)
|
|
309
305
|
const problem = bridgeModelProblem(resolution, port)
|
|
310
306
|
if (problem !== undefined || resolution._tag !== "Resolved") {
|
|
@@ -322,8 +318,9 @@ const geminiBridgeSeats = Effect.fn("@llm4ts/runner/FlowRunner.geminiBridgeSeats
|
|
|
322
318
|
})
|
|
323
319
|
)
|
|
324
320
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
321
|
+
const requestModel = settingValue("LLM4TS_GEMINI_MODEL", environment)
|
|
322
|
+
const geminiModel = requestModel === undefined ? {} : { model: requestModel }
|
|
323
|
+
const token = settingValue("LLM4TS_GEMINI_BRIDGE_TOKEN", environment)
|
|
327
324
|
if (mode.mode === "model") {
|
|
328
325
|
// ADR 0037: one locked-down `gemini` call per request, in an empty
|
|
329
326
|
// directory so no project GEMINI.md reaches the model.
|
|
@@ -337,10 +334,7 @@ const geminiBridgeSeats = Effect.fn("@llm4ts/runner/FlowRunner.geminiBridgeSeats
|
|
|
337
334
|
executor: dependencies.process,
|
|
338
335
|
temporaryFiles: dependencies.temporaryFiles ?? nodeTemporaryFiles,
|
|
339
336
|
...geminiModel,
|
|
340
|
-
...(
|
|
341
|
-
environment.LLM4TS_GEMINI_BRIDGE_TOKEN.length === 0
|
|
342
|
-
? {}
|
|
343
|
-
: { token: environment.LLM4TS_GEMINI_BRIDGE_TOKEN })
|
|
337
|
+
...(token === undefined ? {} : { token })
|
|
344
338
|
}).pipe(Effect.mapError(FlowLlmError.from))
|
|
345
339
|
} else {
|
|
346
340
|
yield* runGeminiAcpBridge({
|
|
@@ -368,8 +362,8 @@ export const runIdentityFromEnvironment = (
|
|
|
368
362
|
options: Pick<FlowRunnerOptions, "runId" | "tracePath">,
|
|
369
363
|
environment: Readonly<Record<string, string | undefined>>
|
|
370
364
|
): { readonly runId?: string; readonly tracePath?: string } => {
|
|
371
|
-
const runId = nonBlank(options.runId) ??
|
|
372
|
-
const tracePath = nonBlank(options.tracePath) ??
|
|
365
|
+
const runId = nonBlank(options.runId) ?? settingValue("LLM4TS_RUN_ID", environment)
|
|
366
|
+
const tracePath = nonBlank(options.tracePath) ?? settingValue("LLM4TS_TRACE_PATH", environment)
|
|
373
367
|
return {
|
|
374
368
|
...(runId === undefined ? {} : { runId }),
|
|
375
369
|
...(tracePath === undefined ? {} : { tracePath })
|
|
@@ -521,7 +515,7 @@ export const makeFlowRunnerContext = Effect.fn("@llm4ts/runner/FlowRunner.makeCo
|
|
|
521
515
|
// notably the judgment seat and the worktree seats `contextFor` rebinds.
|
|
522
516
|
const runEnvironment = options.environment ?? process.env
|
|
523
517
|
const estimateOptions = estimatedUsageOptionsFromEnv(runEnvironment)
|
|
524
|
-
const meterUsage = options.estimateUsage ??
|
|
518
|
+
const meterUsage = options.estimateUsage ?? settingValue("LLM4TS_ESTIMATE_USAGE", runEnvironment)
|
|
525
519
|
// Every seat's spans (ADR 0026): content only when asked, an estimate on a
|
|
526
520
|
// call the backend reports no usage for, and who served it when known.
|
|
527
521
|
const seatOptions = (executor?: Effect.Effect<string | undefined>): TimedSeatOptions => ({
|
|
@@ -578,11 +572,7 @@ export const makeFlowRunnerContext = Effect.fn("@llm4ts/runner/FlowRunner.makeCo
|
|
|
578
572
|
seatConfig: ConnectorConfig,
|
|
579
573
|
environment: Readonly<Record<string, string | undefined>>
|
|
580
574
|
): Effect.Effect<JudgmentShape, FlowError> => {
|
|
581
|
-
const backend =
|
|
582
|
-
options.judgmentBackend ??
|
|
583
|
-
(environment.LLM4TS_JUDGMENT_BACKEND?.trim().toLowerCase() === "typesafe"
|
|
584
|
-
? "typesafe"
|
|
585
|
-
: "llm")
|
|
575
|
+
const backend = options.judgmentBackend ?? settingValue("LLM4TS_JUDGMENT_BACKEND", environment)
|
|
586
576
|
if (backend !== "typesafe") {
|
|
587
577
|
return Effect.succeed(
|
|
588
578
|
makeLlmJudgment(
|
|
@@ -592,7 +582,7 @@ export const makeFlowRunnerContext = Effect.fn("@llm4ts/runner/FlowRunner.makeCo
|
|
|
592
582
|
...(seatConfig.model === undefined ? {} : { model: seatConfig.model }),
|
|
593
583
|
// LLM4TS_JUDGMENT_BATCHING=shared-prefix sends each request's state
|
|
594
584
|
// once with every question; anything else keeps them independent.
|
|
595
|
-
...(environment
|
|
585
|
+
...(settingValue("LLM4TS_JUDGMENT_BATCHING", environment) === "shared-prefix"
|
|
596
586
|
? { batching: "shared-prefix" as const }
|
|
597
587
|
: {})
|
|
598
588
|
}),
|
|
@@ -987,6 +977,21 @@ export const makeFlowRunnerContext = Effect.fn("@llm4ts/runner/FlowRunner.makeCo
|
|
|
987
977
|
}
|
|
988
978
|
})
|
|
989
979
|
|
|
980
|
+
/** What the run prints when `drained` reports a consumer short. */
|
|
981
|
+
export const drainShortfall = (report: DrainReport): string => {
|
|
982
|
+
const behind = report.behind.map(
|
|
983
|
+
(consumer) => `${consumer.name} was ${consumer.missing.toString()} event(s) behind`
|
|
984
|
+
)
|
|
985
|
+
const late = report.late.map(
|
|
986
|
+
(consumer) =>
|
|
987
|
+
`${consumer.name} started listening after ${consumer.missed.toString()} event(s) were published`
|
|
988
|
+
)
|
|
989
|
+
return (
|
|
990
|
+
`some run events were still unconsumed: ${[...late, ...behind].join("; ")}. ` +
|
|
991
|
+
"The totals below, the ledger, and the trace may be short"
|
|
992
|
+
)
|
|
993
|
+
}
|
|
994
|
+
|
|
990
995
|
/** `claude (coder·reviewer·judge ×3)` — how the run header names an executor. */
|
|
991
996
|
const describeExecutor = (spec: ExecutorSpec): string =>
|
|
992
997
|
`${spec.id} (${rolesOf(spec).join("·")} ×${spec.slots ?? 1})`
|
|
@@ -1004,7 +1009,8 @@ export const runWithBundle = Effect.fn("@llm4ts/runner/FlowRunner.runWithBundle"
|
|
|
1004
1009
|
const dependencies = withRunStateIgnore(given)
|
|
1005
1010
|
const startedAt = yield* Clock.currentTimeMillis
|
|
1006
1011
|
const environment = options.environment ?? process.env
|
|
1007
|
-
const verbosity =
|
|
1012
|
+
const verbosity =
|
|
1013
|
+
options.verbosity ?? parseVerbosity(settingValue("LLM4TS_VERBOSITY", environment))
|
|
1008
1014
|
const persistRun = options.persistRun ?? true
|
|
1009
1015
|
// OpenTelemetry export (ADR 0026): decided from the environment, built once
|
|
1010
1016
|
// per run, flushed when the run's scope closes.
|
|
@@ -1043,7 +1049,7 @@ export const runWithBundle = Effect.fn("@llm4ts/runner/FlowRunner.runWithBundle"
|
|
|
1043
1049
|
const surface = tree?.surface ?? classic
|
|
1044
1050
|
const palette = surface.palette
|
|
1045
1051
|
const terminal = yield* consumeTerminalEvents(bundle.events, surface, verbosity, {
|
|
1046
|
-
timestamps:
|
|
1052
|
+
timestamps: settingValue("LLM4TS_TIMESTAMPS", environment)
|
|
1047
1053
|
})
|
|
1048
1054
|
if (verbosity !== "Quiet") {
|
|
1049
1055
|
const reviewers = options.reviewers ?? []
|
|
@@ -1130,10 +1136,11 @@ export const runWithBundle = Effect.fn("@llm4ts/runner/FlowRunner.runWithBundle"
|
|
|
1130
1136
|
tracePath === undefined
|
|
1131
1137
|
? bundle.context
|
|
1132
1138
|
: { ...bundle.context, trace: { runId: bundle.runId, path: tracePath } }
|
|
1139
|
+
const flow = settingValue("LLM4TS_FLOW", environment)
|
|
1133
1140
|
const runAttributes: Record<string, unknown> = {
|
|
1134
1141
|
[attr.run]: bundle.runId,
|
|
1135
1142
|
[attr.session]: bundle.runId,
|
|
1136
|
-
...(
|
|
1143
|
+
...(flow === undefined ? {} : { [attr.flow]: flow })
|
|
1137
1144
|
}
|
|
1138
1145
|
// The run is a CHAIN span every other span descends from or links to; its
|
|
1139
1146
|
// attributes ride on all of them. Effect's own function spans stay
|
|
@@ -1159,33 +1166,13 @@ export const runWithBundle = Effect.fn("@llm4ts/runner/FlowRunner.runWithBundle"
|
|
|
1159
1166
|
Effect.provideService(FlowContext, context),
|
|
1160
1167
|
Effect.andThen((value) => Effect.as(enforceBudget, value)),
|
|
1161
1168
|
Effect.ensuring(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
bundle.judgmentLog === undefined
|
|
1170
|
-
? Effect.succeed(true)
|
|
1171
|
-
: bundle.judgmentLog.awaitDrained(bundle.events)
|
|
1172
|
-
],
|
|
1173
|
-
{ concurrency: "unbounded" }
|
|
1174
|
-
)
|
|
1175
|
-
],
|
|
1176
|
-
{ concurrency: "unbounded" }
|
|
1177
|
-
).pipe(
|
|
1178
|
-
// A consumer that gave up before catching up leaves the totals below
|
|
1179
|
-
// short. Say so rather than printing a quietly incomplete summary.
|
|
1180
|
-
Effect.flatMap(([, accounted]) =>
|
|
1181
|
-
accounted.every((drained) => drained)
|
|
1182
|
-
? Effect.void
|
|
1183
|
-
: surface.log(
|
|
1184
|
-
palette.fail(
|
|
1185
|
-
`some run events were still unconsumed after ${defaultDrainTimeout} — ` +
|
|
1186
|
-
"the totals below, the ledger, and the trace may be short"
|
|
1187
|
-
)
|
|
1188
|
-
)
|
|
1169
|
+
// One wait for every consumer of the run: the terminal, the totals,
|
|
1170
|
+
// the trace, the judgment log. A consumer that fell short leaves the
|
|
1171
|
+
// totals below short, so the run names it instead of printing a
|
|
1172
|
+
// quietly incomplete summary.
|
|
1173
|
+
bundle.events.drained().pipe(
|
|
1174
|
+
Effect.flatMap((report) =>
|
|
1175
|
+
report.complete ? Effect.void : surface.log(palette.fail(drainShortfall(report)))
|
|
1189
1176
|
),
|
|
1190
1177
|
Effect.andThen(tree === undefined ? Effect.void : tree.close),
|
|
1191
1178
|
Effect.andThen(appendLedger),
|
|
@@ -1254,7 +1241,7 @@ const wantsAgentTree = (
|
|
|
1254
1241
|
output: { readonly isTTY?: boolean; readonly columns?: number },
|
|
1255
1242
|
input: { readonly isTTY?: boolean }
|
|
1256
1243
|
): boolean =>
|
|
1257
|
-
|
|
1244
|
+
settingValue("LLM4TS_UI", environment) === "tree" &&
|
|
1258
1245
|
environment.NO_COLOR === undefined &&
|
|
1259
1246
|
output.isTTY === true &&
|
|
1260
1247
|
input.isTTY === true &&
|
|
@@ -1267,11 +1254,24 @@ const runOutcomeOf = <A, E>(exit: Exit.Exit<A, E>): RunOutcome =>
|
|
|
1267
1254
|
? "interrupted"
|
|
1268
1255
|
: "failed"
|
|
1269
1256
|
|
|
1257
|
+
/**
|
|
1258
|
+
* The shell's launch check for a run that did not come through it: an
|
|
1259
|
+
* environment value the settings registry cannot read stops the run before
|
|
1260
|
+
* anything starts, instead of reading as unset.
|
|
1261
|
+
*/
|
|
1262
|
+
const requireReadableEnvironment = (environment: Environment): Effect.Effect<void, ScriptUsage> => {
|
|
1263
|
+
const problems = unreadableEnvironment(environment)
|
|
1264
|
+
return problems.length === 0
|
|
1265
|
+
? Effect.void
|
|
1266
|
+
: Effect.fail(ScriptUsage.make({ message: problems.join("\n") }))
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1270
1269
|
export const runEmbedded = Effect.fn("@llm4ts/runner/FlowRunner.runEmbedded")(function* <A, E, R>(
|
|
1271
1270
|
options: FlowRunnerOptions,
|
|
1272
1271
|
body: (context: FlowContextShape) => Effect.Effect<A, E, R>,
|
|
1273
1272
|
dependencies: FlowRunnerDependencies = nodeFlowRunnerDependencies()
|
|
1274
|
-
): Effect.fn.Return<A, E | FlowError, R | Scope.Scope> {
|
|
1273
|
+
): Effect.fn.Return<A, E | FlowError | ScriptUsage, R | Scope.Scope> {
|
|
1274
|
+
yield* requireReadableEnvironment(options.environment ?? process.env)
|
|
1275
1275
|
const bundle = yield* makeFlowRunnerContext(options, dependencies)
|
|
1276
1276
|
return yield* runWithBundle(bundle, options, body, dependencies)
|
|
1277
1277
|
})
|
|
@@ -1279,7 +1279,7 @@ export const runEmbedded = Effect.fn("@llm4ts/runner/FlowRunner.runEmbedded")(fu
|
|
|
1279
1279
|
export const runNode = <A, E, R>(
|
|
1280
1280
|
options: FlowRunnerOptions,
|
|
1281
1281
|
body: (context: FlowContextShape) => Effect.Effect<A, E, R>
|
|
1282
|
-
): Effect.Effect<A, E | FlowError, R> => Effect.scoped(runEmbedded(options, body))
|
|
1282
|
+
): Effect.Effect<A, E | FlowError | ScriptUsage, R> => Effect.scoped(runEmbedded(options, body))
|
|
1283
1283
|
|
|
1284
1284
|
const mainErrorMessage = (error: unknown): string =>
|
|
1285
1285
|
error instanceof Error && error.message.length > 0 ? error.message : String(error)
|
|
@@ -1359,9 +1359,15 @@ const processSignals: SignalSource = {
|
|
|
1359
1359
|
exit: (code) => process.exit(code)
|
|
1360
1360
|
}
|
|
1361
1361
|
|
|
1362
|
+
/**
|
|
1363
|
+
* A flow script's entry point. An environment value the settings registry
|
|
1364
|
+
* cannot read stops it before `program` starts, as `llm4ts run` would have;
|
|
1365
|
+
* any failure is printed and exits 1.
|
|
1366
|
+
*/
|
|
1362
1367
|
export const runFlowMain = <E>(program: Effect.Effect<void, E>): void => {
|
|
1363
1368
|
const fiber = Effect.runFork(
|
|
1364
|
-
|
|
1369
|
+
requireReadableEnvironment(process.env).pipe(
|
|
1370
|
+
Effect.andThen(program),
|
|
1365
1371
|
Effect.match({
|
|
1366
1372
|
onFailure: (error) =>
|
|
1367
1373
|
Effect.sync(() => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @internal: Gemini CLI executor wired by FlowRunner
|
|
1
2
|
import * as Effect from "effect/Effect"
|
|
2
3
|
import * as Layer from "effect/Layer"
|
|
3
4
|
import * as Stream from "effect/Stream"
|
|
@@ -19,6 +20,9 @@ import {
|
|
|
19
20
|
} from "@llm4ts/core/providers/GeminiCliProvider"
|
|
20
21
|
import { nodeProcessExecutor } from "./NodeProcessExecutor.ts"
|
|
21
22
|
import { nodeTemporaryFiles } from "./NodeTemporaryFiles.ts"
|
|
23
|
+
import { cliExecutable } from "@llm4ts/core/Models"
|
|
24
|
+
|
|
25
|
+
const executable = cliExecutable("GeminiCli")
|
|
22
26
|
|
|
23
27
|
const failure = (message: string): ProviderError => ProviderError.make({ message })
|
|
24
28
|
|
|
@@ -47,7 +51,7 @@ export const makeNodeGeminiCliExecutor = (
|
|
|
47
51
|
const supportsAdminPolicy: Effect.Effect<boolean> = Effect.suspend(() =>
|
|
48
52
|
adminPolicy !== undefined
|
|
49
53
|
? Effect.succeed(adminPolicy)
|
|
50
|
-
: executor.run([
|
|
54
|
+
: executor.run([executable, "--help"], process.cwd(), {}).pipe(
|
|
51
55
|
Effect.map((result) =>
|
|
52
56
|
geminiSupportsAdminPolicy([...result.stdout, ...result.stderr].join("\n"))
|
|
53
57
|
),
|
|
@@ -75,7 +79,7 @@ export const makeNodeGeminiCliExecutor = (
|
|
|
75
79
|
)
|
|
76
80
|
return {
|
|
77
81
|
checkGeminiInstalled: executor
|
|
78
|
-
.run([
|
|
82
|
+
.run([executable, "--version"], process.cwd(), {})
|
|
79
83
|
.pipe(
|
|
80
84
|
Effect.flatMap((result) =>
|
|
81
85
|
result.exitCode === 0
|
|
@@ -89,7 +93,7 @@ export const makeNodeGeminiCliExecutor = (
|
|
|
89
93
|
const environment = yield* environmentFor(context)
|
|
90
94
|
const args = yield* argsFor(config, context, "json")
|
|
91
95
|
const result = yield* executor.runWithStdin(
|
|
92
|
-
[
|
|
96
|
+
[executable, ...args],
|
|
93
97
|
context.cwd ?? process.cwd(),
|
|
94
98
|
environment,
|
|
95
99
|
prompt
|
|
@@ -111,7 +115,7 @@ export const makeNodeGeminiCliExecutor = (
|
|
|
111
115
|
([environment, args]) =>
|
|
112
116
|
executor
|
|
113
117
|
.runStreamingWithStdin(
|
|
114
|
-
[
|
|
118
|
+
[executable, ...args],
|
|
115
119
|
context.cwd ?? process.cwd(),
|
|
116
120
|
environment,
|
|
117
121
|
prompt
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @internal: Gemini bridge wiring for FlowRunner, Doctor and the bridge server
|
|
1
2
|
import { existsSync, readdirSync } from "node:fs"
|
|
2
3
|
import type { IncomingMessage, ServerResponse } from "node:http"
|
|
3
4
|
import { join } from "node:path"
|
|
@@ -34,6 +35,9 @@ import {
|
|
|
34
35
|
requestPath,
|
|
35
36
|
writeJson
|
|
36
37
|
} from "./NodeGeminiAcpBridge.ts"
|
|
38
|
+
import { cliExecutable } from "@llm4ts/core/Models"
|
|
39
|
+
|
|
40
|
+
const executable = cliExecutable("GeminiCli")
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* The bridge's model route (ADR 0037): `/v1/chat/completions` in OpenAI's
|
|
@@ -131,7 +135,7 @@ const geminiTurn = (
|
|
|
131
135
|
config.executor
|
|
132
136
|
.runStreamingWithStdin(
|
|
133
137
|
[
|
|
134
|
-
|
|
138
|
+
executable,
|
|
135
139
|
// Bound to its flag, so it can never read as a flag of its own.
|
|
136
140
|
`--model=${model}`,
|
|
137
141
|
"--output-format",
|
package/src/NodeHttpClient.ts
CHANGED
package/src/Otel.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @internal: OpenTelemetry export wired by FlowRunner and Doctor
|
|
1
2
|
// OpenTelemetry export (ADR 0026): Effect's own OTLP tracer and metrics
|
|
2
3
|
// exporters over core's fetch client, so a run can show up in Phoenix,
|
|
3
4
|
// Langfuse, SigNoz or any collector with no SDK and no new dependency.
|
|
@@ -12,6 +13,7 @@ import * as OtlpExporter from "effect/observability/OtlpExporter"
|
|
|
12
13
|
import * as OtlpMetrics from "effect/observability/OtlpMetrics"
|
|
13
14
|
import * as OtlpSerialization from "effect/observability/OtlpSerialization"
|
|
14
15
|
import * as OtlpTracer from "effect/observability/OtlpTracer"
|
|
16
|
+
import { settingValue } from "@llm4ts/flow/Settings"
|
|
15
17
|
import { packageVersion } from "./Package.ts"
|
|
16
18
|
|
|
17
19
|
export type OtelConfig =
|
|
@@ -23,8 +25,6 @@ export const phoenixTracesUrl = "http://localhost:6006/v1/traces"
|
|
|
23
25
|
|
|
24
26
|
const truthy = (value: string | undefined): boolean =>
|
|
25
27
|
/^(on|1|true|yes)$/iu.test(value?.trim() ?? "")
|
|
26
|
-
const falsy = (value: string | undefined): boolean =>
|
|
27
|
-
/^(off|0|false|no)$/iu.test(value?.trim() ?? "")
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Off unless asked. An OTLP endpoint in the environment wins and is used as
|
|
@@ -34,7 +34,8 @@ const falsy = (value: string | undefined): boolean =>
|
|
|
34
34
|
export const otelConfig = (
|
|
35
35
|
environment: Readonly<Record<string, string | undefined>>
|
|
36
36
|
): OtelConfig => {
|
|
37
|
-
|
|
37
|
+
const asked = settingValue("LLM4TS_OTEL", environment)
|
|
38
|
+
if (truthy(environment.OTEL_SDK_DISABLED) || asked === false) {
|
|
38
39
|
return { mode: "off" }
|
|
39
40
|
}
|
|
40
41
|
// Under the test runner the exporter's sleeps run on a TestClock nobody
|
|
@@ -49,9 +50,7 @@ export const otelConfig = (
|
|
|
49
50
|
if (endpoint !== undefined && endpoint.length > 0) {
|
|
50
51
|
return { mode: "env", endpoint }
|
|
51
52
|
}
|
|
52
|
-
return
|
|
53
|
-
? { mode: "phoenix", tracesUrl: phoenixTracesUrl }
|
|
54
|
-
: { mode: "off" }
|
|
53
|
+
return asked === true ? { mode: "phoenix", tracesUrl: phoenixTracesUrl } : { mode: "off" }
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export interface OtelResource {
|
package/src/Package.ts
CHANGED
package/src/Packs.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { PlanParseError } from "@llm4ts/flow/FlowError"
|
|
|
7
7
|
import { loadPack, type Pack } from "@llm4ts/flow/Pack"
|
|
8
8
|
import { loadPatternCards, type PatternCard } from "@llm4ts/flow/Patterns"
|
|
9
9
|
import type { WorkspaceError, WorkspaceShape } from "@llm4ts/flow/Workspace"
|
|
10
|
+
import { settingValue } from "@llm4ts/flow/Settings"
|
|
10
11
|
import {
|
|
11
12
|
builtinKitsDir,
|
|
12
13
|
discoverKits,
|
|
@@ -108,7 +109,7 @@ export const openPack = Effect.fn("@llm4ts/runner/Packs.openPack")(function* (op
|
|
|
108
109
|
/** Kit tiers to search; defaults to the standard tiers around `launchDir` and `flowDir`. */
|
|
109
110
|
readonly kits?: KitTierPaths
|
|
110
111
|
}): Effect.fn.Return<OpenedPack, PackNotFound | WorkspaceError | PlanParseError> {
|
|
111
|
-
const reference = options.environment
|
|
112
|
+
const reference = settingValue("LLM4TS_PACK", options.environment) ?? defaultPackName
|
|
112
113
|
const builtinDir = builtinKitsDir(options.flowDir)
|
|
113
114
|
const tiers =
|
|
114
115
|
options.kits ??
|
package/src/PiModels.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
2
|
import { homedir } from "node:os"
|
|
3
3
|
import { join } from "node:path"
|
|
4
|
+
import { settingGiven, settingText } from "@llm4ts/flow/Settings"
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Reading `pi`'s own model config (ADR 0016). Separate from `Doctor.ts` so
|
|
@@ -29,10 +30,7 @@ export const defaultReadPiModelsJson = (): string | undefined => {
|
|
|
29
30
|
|
|
30
31
|
export const geminiBridgePort = (
|
|
31
32
|
environment: Readonly<Record<string, string | undefined>>
|
|
32
|
-
): string =>
|
|
33
|
-
isSet(environment.LLM4TS_GEMINI_BRIDGE_PORT)
|
|
34
|
-
? environment.LLM4TS_GEMINI_BRIDGE_PORT.trim()
|
|
35
|
-
: defaultGeminiBridgePort
|
|
33
|
+
): string => settingText("LLM4TS_GEMINI_BRIDGE_PORT", environment) ?? defaultGeminiBridgePort
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
36
|
* Which route the bridge serves (ADR 0037): `acp` (ADR 0016's Anthropic
|
|
@@ -46,12 +44,10 @@ export type GeminiBridgeMode =
|
|
|
46
44
|
export const geminiBridgeMode = (
|
|
47
45
|
environment: Readonly<Record<string, string | undefined>>
|
|
48
46
|
): GeminiBridgeMode => {
|
|
49
|
-
const
|
|
50
|
-
return
|
|
51
|
-
? { _tag: "
|
|
52
|
-
:
|
|
53
|
-
? { _tag: "Mode", mode: "model" }
|
|
54
|
-
: { _tag: "Unknown", value }
|
|
47
|
+
const given = settingGiven("LLM4TS_GEMINI_BRIDGE_MODE", environment)
|
|
48
|
+
return given._tag === "Unreadable"
|
|
49
|
+
? { _tag: "Unknown", value: given.text.toLowerCase() }
|
|
50
|
+
: { _tag: "Mode", mode: given._tag === "Read" ? given.value : "acp" }
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
/**
|