@prismatic-io/lux 0.0.2-preview.17 → 0.0.2-preview.20
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/lib/answerers/persona/index.d.ts +5 -1
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/assertions/rubric/index.d.ts +3 -1
- package/lib/assertions/rubric/index.d.ts.map +1 -1
- package/lib/authoring.d.ts +3 -1
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +2 -1
- package/lib/authoring.js.map +1 -1
- package/lib/cli/doctor.d.ts +55 -0
- package/lib/cli/doctor.d.ts.map +1 -0
- package/lib/cli/doctor.js +14 -0
- package/lib/cli/doctor.js.map +1 -0
- package/lib/cli/harness-plan.d.ts +26 -0
- package/lib/cli/harness-plan.d.ts.map +1 -0
- package/lib/cli/harness-plan.js +75 -0
- package/lib/cli/harness-plan.js.map +1 -0
- package/lib/cli/init-templates.d.ts +2 -2
- package/lib/cli/init-templates.d.ts.map +1 -1
- package/lib/cli/init-templates.js +11 -4
- package/lib/cli/init-templates.js.map +1 -1
- package/lib/cli/init.d.ts +2 -0
- package/lib/cli/init.d.ts.map +1 -1
- package/lib/cli/init.js +6 -3
- package/lib/cli/init.js.map +1 -1
- package/lib/cli/output-schemas.d.ts +7 -0
- package/lib/cli/output-schemas.d.ts.map +1 -1
- package/lib/cli/program.d.ts +8 -11
- package/lib/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +54 -89
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/run-options.d.ts +19 -0
- package/lib/cli/run-options.d.ts.map +1 -1
- package/lib/cli/run-options.js +92 -0
- package/lib/cli/run-options.js.map +1 -1
- package/lib/core/case.d.ts +2 -0
- package/lib/core/case.d.ts.map +1 -1
- package/lib/core/case.js +3 -0
- package/lib/core/case.js.map +1 -1
- package/lib/core/driver.d.ts +13 -0
- package/lib/core/driver.d.ts.map +1 -1
- package/lib/core/driver.js +10 -0
- package/lib/core/driver.js.map +1 -1
- package/lib/core/harness-catalog.d.ts +35 -0
- package/lib/core/harness-catalog.d.ts.map +1 -0
- package/lib/core/harness-catalog.js +45 -0
- package/lib/core/harness-catalog.js.map +1 -0
- package/lib/core/harness-catalogs/antigravity.d.ts +11 -0
- package/lib/core/harness-catalogs/antigravity.d.ts.map +1 -0
- package/lib/core/harness-catalogs/antigravity.js +23 -0
- package/lib/core/harness-catalogs/antigravity.js.map +1 -0
- package/lib/core/harness-catalogs/claude-code.d.ts +12 -0
- package/lib/core/harness-catalogs/claude-code.d.ts.map +1 -0
- package/lib/core/harness-catalogs/claude-code.js +24 -0
- package/lib/core/harness-catalogs/claude-code.js.map +1 -0
- package/lib/core/harness-catalogs/codex.d.ts +11 -0
- package/lib/core/harness-catalogs/codex.d.ts.map +1 -0
- package/lib/core/harness-catalogs/codex.js +26 -0
- package/lib/core/harness-catalogs/codex.js.map +1 -0
- package/lib/core/harness-catalogs/copilot.d.ts +29 -0
- package/lib/core/harness-catalogs/copilot.d.ts.map +1 -0
- package/lib/core/harness-catalogs/copilot.js +45 -0
- package/lib/core/harness-catalogs/copilot.js.map +1 -0
- package/lib/core/harness-catalogs/cursor.d.ts +2 -0
- package/lib/core/harness-catalogs/cursor.d.ts.map +1 -0
- package/lib/core/harness-catalogs/cursor.js +13 -0
- package/lib/core/harness-catalogs/cursor.js.map +1 -0
- package/lib/core/harness-catalogs/index.d.ts +67 -0
- package/lib/core/harness-catalogs/index.d.ts.map +1 -0
- package/lib/core/harness-catalogs/index.js +15 -0
- package/lib/core/harness-catalogs/index.js.map +1 -0
- package/lib/core/harness-matrix.d.ts +70 -0
- package/lib/core/harness-matrix.d.ts.map +1 -0
- package/lib/core/harness-matrix.js +194 -0
- package/lib/core/harness-matrix.js.map +1 -0
- package/lib/core/index.d.ts +3 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +3 -0
- package/lib/core/index.js.map +1 -1
- package/lib/core/model-cli.d.ts +4 -11
- package/lib/core/model-cli.d.ts.map +1 -1
- package/lib/core/model-cli.js +23 -17
- package/lib/core/model-cli.js.map +1 -1
- package/lib/core/platform-process.d.ts +1 -0
- package/lib/core/platform-process.d.ts.map +1 -1
- package/lib/core/platform-process.js +1 -1
- package/lib/core/platform-process.js.map +1 -1
- package/lib/core/run.d.ts +4 -0
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +2 -0
- package/lib/core/run.js.map +1 -1
- package/lib/drivers/antigravity/config.d.ts +46 -0
- package/lib/drivers/antigravity/config.d.ts.map +1 -0
- package/lib/drivers/antigravity/config.js +39 -0
- package/lib/drivers/antigravity/config.js.map +1 -0
- package/lib/drivers/antigravity/events.d.ts +4 -0
- package/lib/drivers/antigravity/events.d.ts.map +1 -0
- package/lib/drivers/antigravity/events.js +193 -0
- package/lib/drivers/antigravity/events.js.map +1 -0
- package/lib/drivers/antigravity/index.d.ts +60 -0
- package/lib/drivers/antigravity/index.d.ts.map +1 -0
- package/lib/drivers/antigravity/index.js +90 -0
- package/lib/drivers/antigravity/index.js.map +1 -0
- package/lib/drivers/claude-code/index.d.ts +93 -7
- package/lib/drivers/claude-code/index.d.ts.map +1 -1
- package/lib/drivers/claude-code/index.js +8 -6
- package/lib/drivers/claude-code/index.js.map +1 -1
- package/lib/drivers/codex/config.d.ts +55 -4
- package/lib/drivers/codex/config.d.ts.map +1 -1
- package/lib/drivers/codex/config.js +9 -5
- package/lib/drivers/codex/config.js.map +1 -1
- package/lib/drivers/codex/index.d.ts +56 -5
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +5 -2
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/drivers/copilot/config.d.ts +81 -0
- package/lib/drivers/copilot/config.d.ts.map +1 -0
- package/lib/drivers/copilot/config.js +54 -0
- package/lib/drivers/copilot/config.js.map +1 -0
- package/lib/drivers/copilot/index.d.ts +103 -0
- package/lib/drivers/copilot/index.d.ts.map +1 -0
- package/lib/drivers/copilot/index.js +256 -0
- package/lib/drivers/copilot/index.js.map +1 -0
- package/lib/drivers/cursor/config.d.ts +6 -1
- package/lib/drivers/cursor/config.d.ts.map +1 -1
- package/lib/drivers/cursor/config.js +2 -3
- package/lib/drivers/cursor/config.js.map +1 -1
- package/lib/drivers/cursor/index.d.ts +7 -2
- package/lib/drivers/cursor/index.d.ts.map +1 -1
- package/lib/drivers/cursor/index.js +9 -6
- package/lib/drivers/cursor/index.js.map +1 -1
- package/lib/drivers/cursor/interaction.d.ts +1 -2
- package/lib/drivers/cursor/interaction.d.ts.map +1 -1
- package/lib/drivers/cursor/interaction.js +3 -14
- package/lib/drivers/cursor/interaction.js.map +1 -1
- package/lib/drivers/{cursor/events.d.ts → shared/acp-events.d.ts} +5 -3
- package/lib/drivers/shared/acp-events.d.ts.map +1 -0
- package/lib/drivers/{cursor/events.js → shared/acp-events.js} +20 -10
- package/lib/drivers/shared/acp-events.js.map +1 -0
- package/lib/drivers/shared/acp-interaction.d.ts +7 -0
- package/lib/drivers/shared/acp-interaction.d.ts.map +1 -0
- package/lib/drivers/shared/acp-interaction.js +20 -0
- package/lib/drivers/shared/acp-interaction.js.map +1 -0
- package/lib/drivers/{cursor → shared}/acp-transport.d.ts +2 -0
- package/lib/drivers/shared/acp-transport.d.ts.map +1 -0
- package/lib/drivers/{cursor → shared}/acp-transport.js +10 -10
- package/lib/drivers/shared/acp-transport.js.map +1 -0
- package/lib/index.d.ts +7 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -2
- package/lib/index.js.map +1 -1
- package/lib/optimization/reflective.d.ts +2 -0
- package/lib/optimization/reflective.d.ts.map +1 -1
- package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
- package/lib/orchestrator/comparison-identity.js +1 -0
- package/lib/orchestrator/comparison-identity.js.map +1 -1
- package/lib/orchestrator/config.d.ts +7 -0
- package/lib/orchestrator/config.d.ts.map +1 -1
- package/lib/orchestrator/config.js +8 -1
- package/lib/orchestrator/config.js.map +1 -1
- package/lib/orchestrator/doctor.d.ts +1 -0
- package/lib/orchestrator/doctor.d.ts.map +1 -1
- package/lib/orchestrator/doctor.js +86 -8
- package/lib/orchestrator/doctor.js.map +1 -1
- package/lib/orchestrator/experiment-context.d.ts +5 -3
- package/lib/orchestrator/experiment-context.d.ts.map +1 -1
- package/lib/orchestrator/experiment-context.js.map +1 -1
- package/lib/orchestrator/experiment-corpus.d.ts +2 -1
- package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.js +9 -1
- package/lib/orchestrator/experiment-corpus.js.map +1 -1
- package/lib/orchestrator/experiment-identity.d.ts +3 -0
- package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-identity.js +1 -1
- package/lib/orchestrator/experiment-identity.js.map +1 -1
- package/lib/orchestrator/experiment-preflight.d.ts +3 -0
- package/lib/orchestrator/experiment-preflight.d.ts.map +1 -0
- package/lib/orchestrator/experiment-preflight.js +21 -0
- package/lib/orchestrator/experiment-preflight.js.map +1 -0
- package/lib/orchestrator/experiment.d.ts +3 -1
- package/lib/orchestrator/experiment.d.ts.map +1 -1
- package/lib/orchestrator/experiment.js +3 -6
- package/lib/orchestrator/experiment.js.map +1 -1
- package/lib/orchestrator/harness-catalog-drift.d.ts +22 -0
- package/lib/orchestrator/harness-catalog-drift.d.ts.map +1 -0
- package/lib/orchestrator/harness-catalog-drift.js +90 -0
- package/lib/orchestrator/harness-catalog-drift.js.map +1 -0
- package/lib/orchestrator/orchestrator.d.ts +9 -1
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +14 -22
- package/lib/orchestrator/orchestrator.js.map +1 -1
- package/lib/orchestrator/run-dir.d.ts +2 -0
- package/lib/orchestrator/run-dir.d.ts.map +1 -1
- package/lib/orchestrator/run-dir.js +1 -1
- package/lib/orchestrator/run-dir.js.map +1 -1
- package/lib/orchestrator/run-execution.d.ts +1 -0
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +2 -0
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/lib/orchestrator/suite-compare.d.ts +3 -0
- package/lib/orchestrator/suite-compare.d.ts.map +1 -1
- package/lib/orchestrator/suite-compare.js +27 -17
- package/lib/orchestrator/suite-compare.js.map +1 -1
- package/lib/orchestrator/suite-runs.d.ts +3 -0
- package/lib/orchestrator/suite-runs.d.ts.map +1 -1
- package/lib/orchestrator/suite-runs.js +2 -0
- package/lib/orchestrator/suite-runs.js.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts +9 -0
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +14 -4
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/package.json +1 -1
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/authoring.ts +10 -3
- package/src/cli/doctor.ts +17 -0
- package/src/cli/harness-plan.ts +117 -0
- package/src/cli/init-templates.ts +12 -5
- package/src/cli/init.ts +12 -5
- package/src/cli/program.ts +60 -107
- package/src/cli/run-options.ts +123 -0
- package/src/core/case.ts +3 -0
- package/src/core/driver.ts +26 -0
- package/src/core/harness-catalog.ts +100 -0
- package/src/core/harness-catalogs/antigravity.ts +24 -0
- package/src/core/harness-catalogs/claude-code.ts +25 -0
- package/src/core/harness-catalogs/codex.ts +27 -0
- package/src/core/harness-catalogs/copilot.ts +46 -0
- package/src/core/harness-catalogs/cursor.ts +13 -0
- package/src/core/harness-catalogs/index.ts +22 -0
- package/src/core/harness-matrix.ts +298 -0
- package/src/core/index.ts +3 -0
- package/src/core/model-cli.ts +38 -21
- package/src/core/platform-process.ts +4 -4
- package/src/core/run.ts +2 -0
- package/src/drivers/antigravity/README.md +46 -0
- package/src/drivers/antigravity/config.ts +44 -0
- package/src/drivers/antigravity/events.ts +235 -0
- package/src/drivers/antigravity/index.ts +109 -0
- package/src/drivers/claude-code/index.ts +12 -5
- package/src/drivers/codex/config.ts +11 -5
- package/src/drivers/codex/index.ts +5 -1
- package/src/drivers/copilot/README.md +52 -0
- package/src/drivers/copilot/config.ts +58 -0
- package/src/drivers/copilot/index.ts +292 -0
- package/src/drivers/cursor/README.md +9 -6
- package/src/drivers/cursor/config.ts +2 -3
- package/src/drivers/cursor/index.ts +10 -6
- package/src/drivers/cursor/interaction.ts +3 -19
- package/src/drivers/{cursor/events.ts → shared/acp-events.ts} +20 -9
- package/src/drivers/shared/acp-interaction.ts +27 -0
- package/src/drivers/{cursor → shared}/acp-transport.ts +15 -10
- package/src/index.ts +44 -3
- package/src/orchestrator/comparison-identity.ts +1 -0
- package/src/orchestrator/config.ts +8 -0
- package/src/orchestrator/doctor.ts +149 -8
- package/src/orchestrator/experiment-context.ts +3 -2
- package/src/orchestrator/experiment-corpus.ts +20 -2
- package/src/orchestrator/experiment-identity.ts +2 -2
- package/src/orchestrator/experiment-preflight.ts +27 -0
- package/src/orchestrator/experiment.ts +3 -5
- package/src/orchestrator/harness-catalog-drift.ts +132 -0
- package/src/orchestrator/orchestrator.ts +31 -26
- package/src/orchestrator/run-dir.ts +8 -1
- package/src/orchestrator/run-execution.ts +3 -0
- package/src/orchestrator/suite-compare.ts +29 -19
- package/src/orchestrator/suite-runs.ts +3 -1
- package/src/orchestrator/suite-summary.ts +19 -4
- package/lib/drivers/cursor/acp-transport.d.ts.map +0 -1
- package/lib/drivers/cursor/acp-transport.js.map +0 -1
- package/lib/drivers/cursor/events.d.ts.map +0 -1
- package/lib/drivers/cursor/events.js.map +0 -1
- package/skills/lux/SKILL.md +0 -89
- package/skills/lux/references/cli.md +0 -128
- package/skills/lux/references/eval-authoring.md +0 -266
package/src/index.ts
CHANGED
|
@@ -258,9 +258,11 @@ export {
|
|
|
258
258
|
type DriverRuntime,
|
|
259
259
|
defineDriver,
|
|
260
260
|
driverRef,
|
|
261
|
+
type HarnessSelection,
|
|
261
262
|
type ResolvedFixtures,
|
|
262
263
|
ResolvedFixturesSchema,
|
|
263
264
|
type StartContext,
|
|
265
|
+
selectModelAndReasoningEffort,
|
|
264
266
|
} from "./core/driver.js";
|
|
265
267
|
export {
|
|
266
268
|
type DriverMetric,
|
|
@@ -344,6 +346,38 @@ export {
|
|
|
344
346
|
type OptimizerInput,
|
|
345
347
|
} from "./core/experiment.js";
|
|
346
348
|
export { type GlobalIdParts, tryParseGlobalId } from "./core/global-id.js";
|
|
349
|
+
export {
|
|
350
|
+
type CatalogModels,
|
|
351
|
+
catalogEfforts,
|
|
352
|
+
defaultCatalogEffort,
|
|
353
|
+
defineHarnessCatalog,
|
|
354
|
+
HARNESS_EFFORTS,
|
|
355
|
+
type HarnessCatalog,
|
|
356
|
+
type ReasoningEffort,
|
|
357
|
+
ReasoningEffortSchema,
|
|
358
|
+
} from "./core/harness-catalog.js";
|
|
359
|
+
export {
|
|
360
|
+
antigravityCatalog,
|
|
361
|
+
type BuiltInHarnessDriver,
|
|
362
|
+
builtInHarnessCatalogs,
|
|
363
|
+
claudeCodeCatalog,
|
|
364
|
+
codexCatalog,
|
|
365
|
+
copilotCatalog,
|
|
366
|
+
cursorCatalog,
|
|
367
|
+
harnessCatalogFor,
|
|
368
|
+
} from "./core/harness-catalogs/index.js";
|
|
369
|
+
export {
|
|
370
|
+
type BuiltInHarnessMatrixEntry,
|
|
371
|
+
BuiltInHarnessMatrixEntrySchema,
|
|
372
|
+
type CustomHarnessMatrixEntry,
|
|
373
|
+
expandHarnessMatrix,
|
|
374
|
+
type HarnessMatrix,
|
|
375
|
+
type HarnessMatrixEntry,
|
|
376
|
+
HarnessMatrixEntrySchema,
|
|
377
|
+
HarnessMatrixSchema,
|
|
378
|
+
type HarnessPermutation,
|
|
379
|
+
permutationId,
|
|
380
|
+
} from "./core/harness-matrix.js";
|
|
347
381
|
export {
|
|
348
382
|
defineFixtures,
|
|
349
383
|
type LifecycleFixture,
|
|
@@ -379,14 +413,11 @@ export {
|
|
|
379
413
|
export {
|
|
380
414
|
DEFAULT_CLAUDE_MODEL,
|
|
381
415
|
DEFAULT_CODEX_MODEL,
|
|
382
|
-
DEFAULT_CODEX_REASONING_EFFORT,
|
|
383
416
|
defaultHarnessModel,
|
|
384
417
|
type HarnessModelConfig,
|
|
385
418
|
HarnessModelConfigSchema,
|
|
386
419
|
type ModelCliProvider,
|
|
387
420
|
ModelCliProviderSchema,
|
|
388
|
-
type ReasoningEffort,
|
|
389
|
-
ReasoningEffortSchema,
|
|
390
421
|
} from "./core/model-cli.js";
|
|
391
422
|
export {
|
|
392
423
|
type AssertionProgress,
|
|
@@ -472,6 +503,11 @@ export {
|
|
|
472
503
|
type WireEvent,
|
|
473
504
|
WireEventSchema,
|
|
474
505
|
} from "./core/wire.js";
|
|
506
|
+
export {
|
|
507
|
+
type AntigravityDriverConfig,
|
|
508
|
+
AntigravityDriverConfigSchema,
|
|
509
|
+
antigravityDriver,
|
|
510
|
+
} from "./drivers/antigravity/index.js";
|
|
475
511
|
export {
|
|
476
512
|
type ClaudeCodeDriverConfig,
|
|
477
513
|
ClaudeCodeDriverConfigSchema,
|
|
@@ -482,6 +518,11 @@ export {
|
|
|
482
518
|
CodexDriverConfigSchema,
|
|
483
519
|
codexDriver,
|
|
484
520
|
} from "./drivers/codex/index.js";
|
|
521
|
+
export {
|
|
522
|
+
type CopilotDriverConfig,
|
|
523
|
+
CopilotDriverConfigSchema,
|
|
524
|
+
copilotDriver,
|
|
525
|
+
} from "./drivers/copilot/index.js";
|
|
485
526
|
export {
|
|
486
527
|
type CursorDriverConfig,
|
|
487
528
|
CursorDriverConfigSchema,
|
|
@@ -36,6 +36,7 @@ export const runComparisonIdentity = (metadata: RunMetadata): string =>
|
|
|
36
36
|
assertionsHash: metadata.provenance?.assertionsHash,
|
|
37
37
|
fixturesHash: metadata.provenance?.fixturesHash,
|
|
38
38
|
profileId: metadata.provenance?.profileId,
|
|
39
|
+
permutationId: metadata.provenance?.permutationId,
|
|
39
40
|
driverConfigHash: metadata.provenance?.driverConfigHash,
|
|
40
41
|
answererConfigHash: metadata.provenance?.answererConfigHash,
|
|
41
42
|
evaluatorConfigHash: metadata.provenance?.evaluatorConfigHash,
|
|
@@ -18,13 +18,16 @@ import {
|
|
|
18
18
|
type DriverPlugin,
|
|
19
19
|
DriverRefSchema,
|
|
20
20
|
formatZodIssues,
|
|
21
|
+
HarnessMatrixSchema,
|
|
21
22
|
HarnessModelConfigSchema,
|
|
22
23
|
type LifecycleHooksPlugin,
|
|
23
24
|
lifecycleHooksPluginIssue,
|
|
24
25
|
Registry,
|
|
25
26
|
} from "../core/index.js";
|
|
27
|
+
import { antigravityDriver } from "../drivers/antigravity/index.js";
|
|
26
28
|
import { claudeCodeDriver } from "../drivers/claude-code/index.js";
|
|
27
29
|
import { codexDriver } from "../drivers/codex/index.js";
|
|
30
|
+
import { copilotDriver } from "../drivers/copilot/index.js";
|
|
28
31
|
import { cursorDriver } from "../drivers/cursor/index.js";
|
|
29
32
|
import { mcpProbeDriver } from "../drivers/mcp/index.js";
|
|
30
33
|
import { subprocessDriver } from "../drivers/subprocess/index.js";
|
|
@@ -80,6 +83,8 @@ const LuxConfigSchema = z
|
|
|
80
83
|
subjectRoot: z.string().optional(),
|
|
81
84
|
defaultAnswerer: AnswererRefSchema.default("persona"),
|
|
82
85
|
defaultDriver: DriverRefSchema.optional(),
|
|
86
|
+
/** Driver, model, and effort permutations every case runs through by default. */
|
|
87
|
+
matrix: HarnessMatrixSchema.optional(),
|
|
83
88
|
/** Shared provider/model defaults for persona answering and rubric judging. */
|
|
84
89
|
harness: HarnessModelConfigSchema.optional(),
|
|
85
90
|
concurrency: z.number().int().min(1).default(1),
|
|
@@ -166,6 +171,7 @@ export const loadLuxConfig = async (cwd: string = process.cwd()): Promise<Resolv
|
|
|
166
171
|
...(validated.fixturesRoot ? { fixturesRoot: resolve(root, validated.fixturesRoot) } : {}),
|
|
167
172
|
defaultAnswerer: validated.defaultAnswerer,
|
|
168
173
|
...(validated.defaultDriver ? { defaultDriver: validated.defaultDriver } : {}),
|
|
174
|
+
...(validated.matrix ? { matrix: validated.matrix } : {}),
|
|
169
175
|
...(validated.harness ? { harness: validated.harness } : {}),
|
|
170
176
|
concurrency: validated.concurrency,
|
|
171
177
|
...(validated.environment ? { environment: validated.environment } : {}),
|
|
@@ -181,6 +187,8 @@ const builtinDrivers: DriverPlugin<never>[] = [
|
|
|
181
187
|
claudeCodeDriver,
|
|
182
188
|
codexDriver,
|
|
183
189
|
cursorDriver,
|
|
190
|
+
copilotDriver,
|
|
191
|
+
antigravityDriver,
|
|
184
192
|
subprocessDriver,
|
|
185
193
|
mcpProbeDriver,
|
|
186
194
|
];
|
|
@@ -4,7 +4,15 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { glob } from "tinyglobby";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { RubricConfigSchema } from "../assertions/rubric/index.js";
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
casePermutations,
|
|
9
|
+
type EvalCase,
|
|
10
|
+
expandHarnessMatrix,
|
|
11
|
+
type HarnessPermutation,
|
|
12
|
+
permutationDriverRef,
|
|
13
|
+
permutationId,
|
|
14
|
+
type Registry,
|
|
15
|
+
} from "../core/index.js";
|
|
8
16
|
import { registryForEvalCase } from "./case-extensions.js";
|
|
9
17
|
import { buildRegistryFromConfig, loadLuxConfig, type ResolvedLuxConfig } from "./config.js";
|
|
10
18
|
import { type DiscoveredCase, type DiscoveryResult, discoverCases } from "./discover.js";
|
|
@@ -17,6 +25,12 @@ import {
|
|
|
17
25
|
} from "./experiment-loader.js";
|
|
18
26
|
import { cliIdentity } from "./experiment-runtime-identity.js";
|
|
19
27
|
import { assertionConfigForSchema } from "./grading.js";
|
|
28
|
+
import {
|
|
29
|
+
describeHarnessCatalogDrift,
|
|
30
|
+
harnessCatalogDrift,
|
|
31
|
+
harnessCatalogProbes,
|
|
32
|
+
hasHarnessCatalogDrift,
|
|
33
|
+
} from "./harness-catalog-drift.js";
|
|
20
34
|
import { loadEvalCase } from "./loader.js";
|
|
21
35
|
import { validatePluginConfig, validatePluginConfigAsync } from "./validate-plugin-config.js";
|
|
22
36
|
|
|
@@ -62,6 +76,7 @@ export const DoctorOptionsSchema = z
|
|
|
62
76
|
.object({
|
|
63
77
|
cwd: z.string().min(1),
|
|
64
78
|
campaigns: z.array(z.string()).optional(),
|
|
79
|
+
static: z.boolean().optional(),
|
|
65
80
|
})
|
|
66
81
|
.strict();
|
|
67
82
|
export type DoctorOptions = z.input<typeof DoctorOptionsSchema>;
|
|
@@ -131,7 +146,28 @@ const effectiveDriverReference = (local: unknown, fallback: unknown): unknown =>
|
|
|
131
146
|
};
|
|
132
147
|
};
|
|
133
148
|
|
|
134
|
-
const
|
|
149
|
+
const boundPermutation = (
|
|
150
|
+
permutation: HarnessPermutation,
|
|
151
|
+
config: ResolvedLuxConfig,
|
|
152
|
+
registry: Registry,
|
|
153
|
+
): unknown => {
|
|
154
|
+
const plugin = registry.getDriver(permutation.driver);
|
|
155
|
+
if (!plugin) throw new Error(`unknown driver '${permutation.driver}'`);
|
|
156
|
+
return permutationDriverRef(permutation, config.defaultDriver, plugin);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const caseDriverReferences = (
|
|
160
|
+
authored: Pick<EvalCase, "driver" | "matrix">,
|
|
161
|
+
config: ResolvedLuxConfig,
|
|
162
|
+
registry: Registry,
|
|
163
|
+
): unknown[] => {
|
|
164
|
+
const local = authored.driver;
|
|
165
|
+
const permutations = casePermutations(authored, config.matrix);
|
|
166
|
+
if (permutations.length > 0) {
|
|
167
|
+
return permutations.map((permutation) =>
|
|
168
|
+
effectiveDriverReference(local, boundPermutation(permutation, config, registry)),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
135
171
|
const fallbacks = [
|
|
136
172
|
config.defaultDriver,
|
|
137
173
|
...Object.values(config.profiles ?? {}).map((profile) => profile.driver),
|
|
@@ -191,6 +227,17 @@ const configuredCliTargets = (config: ResolvedLuxConfig, registry: Registry): Cl
|
|
|
191
227
|
};
|
|
192
228
|
for (const command of driverCommands(config.defaultDriver, registry))
|
|
193
229
|
add("default driver", command);
|
|
230
|
+
for (const permutation of expandHarnessMatrix(config.matrix ?? [])) {
|
|
231
|
+
try {
|
|
232
|
+
for (const command of driverCommands(
|
|
233
|
+
boundPermutation(permutation, config, registry),
|
|
234
|
+
registry,
|
|
235
|
+
))
|
|
236
|
+
add(`matrix '${permutationId(permutation)}' driver`, command);
|
|
237
|
+
} catch {
|
|
238
|
+
/* Reported by validateConfiguredMatrix. */
|
|
239
|
+
}
|
|
240
|
+
}
|
|
194
241
|
add("default answerer", answererCommand(config.defaultAnswerer));
|
|
195
242
|
if (config.harness) {
|
|
196
243
|
add(
|
|
@@ -356,6 +403,32 @@ const validateConfiguredDrivers = (
|
|
|
356
403
|
}
|
|
357
404
|
};
|
|
358
405
|
|
|
406
|
+
const validateConfiguredMatrix = (
|
|
407
|
+
config: ResolvedLuxConfig,
|
|
408
|
+
registry: Registry,
|
|
409
|
+
recorder: DoctorRecorder,
|
|
410
|
+
): void => {
|
|
411
|
+
for (const permutation of expandHarnessMatrix(config.matrix ?? [])) {
|
|
412
|
+
const id = permutationId(permutation);
|
|
413
|
+
try {
|
|
414
|
+
const reference = boundPermutation(permutation, config, registry);
|
|
415
|
+
const plugin = registry.getDriver(permutation.driver);
|
|
416
|
+
validatePluginConfig(
|
|
417
|
+
plugin?.configSchema,
|
|
418
|
+
rawRefConfig(reference),
|
|
419
|
+
`matrix '${id}' driver '${permutation.driver}'`,
|
|
420
|
+
);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
recorder.add(
|
|
423
|
+
`matrix:${id}`,
|
|
424
|
+
"fail",
|
|
425
|
+
`matrix permutation '${id}' is not runnable`,
|
|
426
|
+
firstLine(error),
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
359
432
|
const validateConfiguredAnswerers = (
|
|
360
433
|
config: ResolvedLuxConfig,
|
|
361
434
|
registry: Registry,
|
|
@@ -575,7 +648,7 @@ const inspectCase = async (
|
|
|
575
648
|
try {
|
|
576
649
|
const authored = await loadEvalCase(discovered.path);
|
|
577
650
|
const caseRegistry = registryForEvalCase(registry, authored);
|
|
578
|
-
const drivers = caseDriverReferences(authored
|
|
651
|
+
const drivers = caseDriverReferences(authored, config, caseRegistry);
|
|
579
652
|
const answerer = authored.answerer ?? config.defaultAnswerer;
|
|
580
653
|
validateCaseDrivers(discovered.id, drivers, caseRegistry, recorder, authored.requires);
|
|
581
654
|
validateCaseAnswerer(discovered.id, answerer, caseRegistry, recorder);
|
|
@@ -658,6 +731,54 @@ const inspectCliTargets = async (
|
|
|
658
731
|
}
|
|
659
732
|
};
|
|
660
733
|
|
|
734
|
+
const catalogDriverReferences = (config: ResolvedLuxConfig): unknown[] => [
|
|
735
|
+
...configuredDriverReferences(config).map(([, reference]) => reference),
|
|
736
|
+
...(config.matrix ?? []).map((entry) => ({ name: entry.driver, config: entry.config })),
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
const configuredDriverCommand = (config: ResolvedLuxConfig, driver: string): string | undefined => {
|
|
740
|
+
for (const reference of catalogDriverReferences(config)) {
|
|
741
|
+
if (refName(reference) !== driver) continue;
|
|
742
|
+
const command = (rawRefConfig(reference) as { command?: unknown } | undefined)?.command;
|
|
743
|
+
if (typeof command === "string") return command;
|
|
744
|
+
}
|
|
745
|
+
return undefined;
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
const inspectHarnessCatalogs = async (
|
|
749
|
+
config: ResolvedLuxConfig,
|
|
750
|
+
recorder: DoctorRecorder,
|
|
751
|
+
): Promise<void> => {
|
|
752
|
+
const configured = new Set(
|
|
753
|
+
catalogDriverReferences(config)
|
|
754
|
+
.map(refName)
|
|
755
|
+
.filter((name): name is string => name !== undefined),
|
|
756
|
+
);
|
|
757
|
+
for (const probe of harnessCatalogProbes) {
|
|
758
|
+
const driver = probe.catalog.driver;
|
|
759
|
+
if (!configured.has(driver)) continue;
|
|
760
|
+
const live = await probe.read(configuredDriverCommand(config, driver));
|
|
761
|
+
if (!live) {
|
|
762
|
+
recorder.add(
|
|
763
|
+
`catalog:${driver}`,
|
|
764
|
+
"warn",
|
|
765
|
+
`${driver} harness catalog (verified ${probe.catalog.verified}) could not be compared with the installed CLI`,
|
|
766
|
+
`${probe.source} was unavailable`,
|
|
767
|
+
);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
const drift = harnessCatalogDrift(probe.catalog, live);
|
|
771
|
+
recorder.add(
|
|
772
|
+
`catalog:${driver}`,
|
|
773
|
+
hasHarnessCatalogDrift(drift) ? "warn" : "pass",
|
|
774
|
+
hasHarnessCatalogDrift(drift)
|
|
775
|
+
? `${driver} harness catalog (verified ${probe.catalog.verified}) differs from the installed CLI`
|
|
776
|
+
: `${driver} harness catalog matches the installed CLI`,
|
|
777
|
+
describeHarnessCatalogDrift(drift) || undefined,
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
|
|
661
782
|
const isBlockingCampaignWarning = (warning: string): boolean =>
|
|
662
783
|
warning.includes("below confidence minSamples=") || warning.includes("cannot attain confidence");
|
|
663
784
|
|
|
@@ -666,6 +787,7 @@ const inspectCampaign = async (
|
|
|
666
787
|
config: ResolvedLuxConfig,
|
|
667
788
|
registry: Registry,
|
|
668
789
|
recorder: DoctorRecorder,
|
|
790
|
+
staticChecks: boolean,
|
|
669
791
|
): Promise<void> => {
|
|
670
792
|
try {
|
|
671
793
|
const loaded = await loadExperimentCampaign(path);
|
|
@@ -679,7 +801,9 @@ const inspectCampaign = async (
|
|
|
679
801
|
placeholders.join(", "),
|
|
680
802
|
);
|
|
681
803
|
}
|
|
682
|
-
const plan = await planExperimentCampaign({ config, registry, loaded }, mode
|
|
804
|
+
const plan = await planExperimentCampaign({ config, registry, loaded }, mode, {
|
|
805
|
+
static: staticChecks,
|
|
806
|
+
});
|
|
683
807
|
recorder.campaigns.push({ path, campaignId: loaded.campaign.id, mode, plan });
|
|
684
808
|
recorder.add(
|
|
685
809
|
`campaign:${loaded.campaign.id}`,
|
|
@@ -711,6 +835,7 @@ const inspectCampaigns = async (
|
|
|
711
835
|
config: ResolvedLuxConfig,
|
|
712
836
|
registry: Registry,
|
|
713
837
|
recorder: DoctorRecorder,
|
|
838
|
+
staticChecks: boolean,
|
|
714
839
|
): Promise<void> => {
|
|
715
840
|
let paths: string[];
|
|
716
841
|
try {
|
|
@@ -733,7 +858,7 @@ const inspectCampaigns = async (
|
|
|
733
858
|
);
|
|
734
859
|
}
|
|
735
860
|
for (const path of paths) {
|
|
736
|
-
await inspectCampaign(path, config, registry, recorder);
|
|
861
|
+
await inspectCampaign(path, config, registry, recorder, staticChecks);
|
|
737
862
|
}
|
|
738
863
|
};
|
|
739
864
|
|
|
@@ -747,14 +872,30 @@ export const doctorProject = async (options: DoctorOptions): Promise<DoctorRepor
|
|
|
747
872
|
if (!registry) return recorder.finish(config.rootDir);
|
|
748
873
|
|
|
749
874
|
validateConfiguredDrivers(config, registry, recorder);
|
|
875
|
+
validateConfiguredMatrix(config, registry, recorder);
|
|
750
876
|
validateConfiguredAnswerers(config, registry, recorder);
|
|
751
877
|
const caseCliTargets = await inspectCases(config, registry, recorder);
|
|
752
|
-
|
|
753
|
-
|
|
878
|
+
if (parsedOptions.static) {
|
|
879
|
+
recorder.add(
|
|
880
|
+
"static-checks",
|
|
881
|
+
"warn",
|
|
882
|
+
"static checks only; installed executables and campaign execution identity were not checked",
|
|
883
|
+
);
|
|
884
|
+
} else {
|
|
885
|
+
await inspectCliTargets(
|
|
886
|
+
[...configuredCliTargets(config, registry), ...caseCliTargets],
|
|
887
|
+
config,
|
|
888
|
+
recorder,
|
|
889
|
+
);
|
|
890
|
+
await inspectHarnessCatalogs(config, recorder);
|
|
891
|
+
}
|
|
892
|
+
await inspectCampaigns(
|
|
893
|
+
parsedOptions.campaigns ?? [],
|
|
754
894
|
config,
|
|
895
|
+
registry,
|
|
755
896
|
recorder,
|
|
897
|
+
parsedOptions.static ?? false,
|
|
756
898
|
);
|
|
757
|
-
await inspectCampaigns(parsedOptions.campaigns ?? [], config, registry, recorder);
|
|
758
899
|
return recorder.finish(config.rootDir);
|
|
759
900
|
};
|
|
760
901
|
|
|
@@ -3,17 +3,18 @@ import type { ResolvedLuxConfig } from "./config.js";
|
|
|
3
3
|
import type { discoverCases } from "./discover.js";
|
|
4
4
|
import type { LoadedExperimentCampaign } from "./experiment-loader.js";
|
|
5
5
|
|
|
6
|
-
export type
|
|
6
|
+
export type CorpusSelection =
|
|
7
7
|
| {
|
|
8
8
|
kind: "agent";
|
|
9
9
|
cases: Awaited<ReturnType<typeof discoverCases>>["cases"];
|
|
10
|
-
hash: string;
|
|
11
10
|
}
|
|
12
11
|
| {
|
|
13
12
|
kind: "grader";
|
|
14
13
|
hash: string;
|
|
15
14
|
};
|
|
16
15
|
|
|
16
|
+
export type EvaluationCorpus = CorpusSelection & { hash: string };
|
|
17
|
+
|
|
17
18
|
export type CampaignExecutionOptions = {
|
|
18
19
|
config: ResolvedLuxConfig;
|
|
19
20
|
registry: Registry;
|
|
@@ -13,7 +13,11 @@ import {
|
|
|
13
13
|
import { assertDisjointAnnotationSplits, selectGraderAnnotations } from "./annotation-loader.js";
|
|
14
14
|
import { discoverCases, selectCases } from "./discover.js";
|
|
15
15
|
import { experimentDriverIdentity } from "./driver-identity.js";
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
CampaignExecutionOptions,
|
|
18
|
+
CorpusSelection,
|
|
19
|
+
EvaluationCorpus,
|
|
20
|
+
} from "./experiment-context.js";
|
|
17
21
|
import { profileOrThrow } from "./experiment-context.js";
|
|
18
22
|
import { cliIdentity, referencedAnswererCli } from "./experiment-runtime-identity.js";
|
|
19
23
|
import { fixtureIdentity } from "./fixture-identity.js";
|
|
@@ -252,6 +256,21 @@ export const graderCorpusHash = async (
|
|
|
252
256
|
export const prepareCorpus = async (
|
|
253
257
|
execution: CampaignExecutionOptions,
|
|
254
258
|
): Promise<EvaluationCorpus> => {
|
|
259
|
+
const corpus = await inspectCorpus(execution);
|
|
260
|
+
if (corpus.kind === "grader") return corpus;
|
|
261
|
+
return {
|
|
262
|
+
...corpus,
|
|
263
|
+
hash: await agentCorpusHash(
|
|
264
|
+
execution,
|
|
265
|
+
corpus.cases,
|
|
266
|
+
selectorsBySplit(execution.loaded.campaign),
|
|
267
|
+
),
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export const inspectCorpus = async (
|
|
272
|
+
execution: CampaignExecutionOptions,
|
|
273
|
+
): Promise<CorpusSelection> => {
|
|
255
274
|
const campaign = execution.loaded.campaign;
|
|
256
275
|
const splits = selectorsBySplit(campaign);
|
|
257
276
|
if (campaign.evaluation.kind === "grader") {
|
|
@@ -344,7 +363,6 @@ export const prepareCorpus = async (
|
|
|
344
363
|
return {
|
|
345
364
|
kind: "agent",
|
|
346
365
|
cases: discovery.cases,
|
|
347
|
-
hash: await agentCorpusHash(execution, discovery.cases, splits),
|
|
348
366
|
};
|
|
349
367
|
};
|
|
350
368
|
|
|
@@ -253,9 +253,9 @@ const authoredConfigDependencyHash = async (
|
|
|
253
253
|
}
|
|
254
254
|
return authoredDependencyGraphHash(configRoot, [...entrypoints]);
|
|
255
255
|
};
|
|
256
|
-
const validateAgentCaseProfileDrivers = async (
|
|
256
|
+
export const validateAgentCaseProfileDrivers = async (
|
|
257
257
|
execution: CampaignExecutionOptions,
|
|
258
|
-
corpus: Extract<EvaluationCorpus, { kind: "agent" }>,
|
|
258
|
+
corpus: Pick<Extract<EvaluationCorpus, { kind: "agent" }>, "cases">,
|
|
259
259
|
): Promise<void> => {
|
|
260
260
|
const selected = [
|
|
261
261
|
...new Map(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { assertValidationArgumentsBound } from "./candidates.js";
|
|
2
|
+
import {
|
|
3
|
+
type CampaignExecutionOptions,
|
|
4
|
+
type CorpusSelection,
|
|
5
|
+
profileOrThrow,
|
|
6
|
+
} from "./experiment-context.js";
|
|
7
|
+
import { inspectCorpus, prepareCorpus } from "./experiment-corpus.js";
|
|
8
|
+
import { evaluationIdentity, validateAgentCaseProfileDrivers } from "./experiment-identity.js";
|
|
9
|
+
|
|
10
|
+
export const preparePlanningCorpus = async (
|
|
11
|
+
execution: CampaignExecutionOptions,
|
|
12
|
+
mode: "experiment" | "optimize",
|
|
13
|
+
staticChecks: boolean,
|
|
14
|
+
): Promise<CorpusSelection> => {
|
|
15
|
+
if (!staticChecks) {
|
|
16
|
+
const corpus = await prepareCorpus(execution);
|
|
17
|
+
await evaluationIdentity(execution, corpus, mode);
|
|
18
|
+
return corpus;
|
|
19
|
+
}
|
|
20
|
+
const corpus = await inspectCorpus(execution);
|
|
21
|
+
const campaign = execution.loaded.campaign;
|
|
22
|
+
for (const profileId of campaign.profiles) profileOrThrow(execution.config, profileId);
|
|
23
|
+
if (corpus.kind === "agent") await validateAgentCaseProfileDrivers(execution, corpus);
|
|
24
|
+
for (const validation of campaign.subject.validate)
|
|
25
|
+
assertValidationArgumentsBound(execution.loaded.subjectRoot, validation.args);
|
|
26
|
+
return corpus;
|
|
27
|
+
};
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
resolvedProfileSnapshot,
|
|
49
49
|
runtimeCliReferences,
|
|
50
50
|
} from "./experiment-identity.js";
|
|
51
|
+
import { preparePlanningCorpus } from "./experiment-preflight.js";
|
|
51
52
|
import {
|
|
52
53
|
type ExperimentEvaluation,
|
|
53
54
|
loadExperimentCandidates,
|
|
@@ -182,6 +183,7 @@ const evaluateAcross = async (options: {
|
|
|
182
183
|
export const planExperimentCampaign = async (
|
|
183
184
|
execution: CampaignExecutionOptions,
|
|
184
185
|
mode: "experiment" | "optimize",
|
|
186
|
+
options: { static?: boolean } = {},
|
|
185
187
|
): Promise<ExperimentCampaignPlan> => {
|
|
186
188
|
const campaign = execution.loaded.campaign;
|
|
187
189
|
if (mode === "optimize") {
|
|
@@ -192,11 +194,7 @@ export const planExperimentCampaign = async (
|
|
|
192
194
|
}
|
|
193
195
|
const baseline = await captureSeedCandidate(execution.loaded.subjectRoot, campaign.subject);
|
|
194
196
|
await assertGraderSubject(execution, baseline);
|
|
195
|
-
const corpus = await
|
|
196
|
-
// Plan is the deterministic preflight for execution. Build the exact same
|
|
197
|
-
// identity now so profile, behavior-tree, and custom CLI failures happen
|
|
198
|
-
// before a user approves any model spend.
|
|
199
|
-
await evaluationIdentity(execution, corpus, mode);
|
|
197
|
+
const corpus = await preparePlanningCorpus(execution, mode, options.static ?? false);
|
|
200
198
|
const splitSelectors = selectorsBySplit(campaign);
|
|
201
199
|
const graderAnnotations = execution.loaded.graderAnnotations?.annotations ?? [];
|
|
202
200
|
const splits = splitSelectors.map(({ name, selector }) => ({
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import {
|
|
6
|
+
type ClosedHarnessCatalog,
|
|
7
|
+
codexCatalog,
|
|
8
|
+
copilotCatalog,
|
|
9
|
+
exec,
|
|
10
|
+
type ReasoningEffort,
|
|
11
|
+
tryParseJson,
|
|
12
|
+
} from "../core/index.js";
|
|
13
|
+
|
|
14
|
+
export type LiveHarnessModel = {
|
|
15
|
+
id: string;
|
|
16
|
+
efforts?: readonly ReasoningEffort[] | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type HarnessCatalogDrift = {
|
|
20
|
+
unlisted: string[];
|
|
21
|
+
mismatched: string[];
|
|
22
|
+
unavailable: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const harnessCatalogDrift = (
|
|
26
|
+
catalog: ClosedHarnessCatalog,
|
|
27
|
+
live: readonly LiveHarnessModel[],
|
|
28
|
+
): HarnessCatalogDrift => {
|
|
29
|
+
const liveIds = new Set(live.map((model) => model.id));
|
|
30
|
+
return {
|
|
31
|
+
unlisted: live.filter((model) => !(model.id in catalog.models)).map((model) => model.id),
|
|
32
|
+
mismatched: live
|
|
33
|
+
.filter(
|
|
34
|
+
(model) =>
|
|
35
|
+
model.efforts !== undefined &&
|
|
36
|
+
model.id in catalog.models &&
|
|
37
|
+
model.efforts.join(",") !== catalog.models[model.id]?.join(","),
|
|
38
|
+
)
|
|
39
|
+
.map((model) => `${model.id} (${model.efforts?.join(", ") || "no effort"})`),
|
|
40
|
+
unavailable: Object.keys(catalog.models).filter((id) => !liveIds.has(id)),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const hasHarnessCatalogDrift = (drift: HarnessCatalogDrift): boolean =>
|
|
45
|
+
drift.unlisted.length > 0 || drift.mismatched.length > 0;
|
|
46
|
+
|
|
47
|
+
export const describeHarnessCatalogDrift = (drift: HarnessCatalogDrift): string =>
|
|
48
|
+
[
|
|
49
|
+
drift.unlisted.length > 0 ? `not in catalog: ${drift.unlisted.join(", ")}` : undefined,
|
|
50
|
+
drift.mismatched.length > 0 ? `efforts differ: ${drift.mismatched.join("; ")}` : undefined,
|
|
51
|
+
drift.unavailable.length > 0 ? `not offered here: ${drift.unavailable.join(", ")}` : undefined,
|
|
52
|
+
]
|
|
53
|
+
.filter((part) => part !== undefined)
|
|
54
|
+
.join(". ");
|
|
55
|
+
|
|
56
|
+
const CodexModelsCacheSchema = z.object({
|
|
57
|
+
models: z.array(
|
|
58
|
+
z.looseObject({
|
|
59
|
+
slug: z.string(),
|
|
60
|
+
visibility: z.string().optional(),
|
|
61
|
+
supported_reasoning_levels: z.array(z.looseObject({ effort: z.string() })).default([]),
|
|
62
|
+
}),
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const codexModelsCachePath = (env: NodeJS.ProcessEnv = process.env): string =>
|
|
67
|
+
join(env.CODEX_HOME ?? join(homedir(), ".codex"), "models_cache.json");
|
|
68
|
+
|
|
69
|
+
export const parseCodexModelsCache = (text: string): LiveHarnessModel[] | undefined => {
|
|
70
|
+
const parsed = CodexModelsCacheSchema.safeParse(tryParseJson(text));
|
|
71
|
+
if (!parsed.success) return undefined;
|
|
72
|
+
return parsed.data.models
|
|
73
|
+
.filter((model) => model.visibility !== "hide")
|
|
74
|
+
.map((model) => ({
|
|
75
|
+
id: model.slug,
|
|
76
|
+
efforts: model.supported_reasoning_levels.map((level) => level.effort as ReasoningEffort),
|
|
77
|
+
}));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const readCodexLiveModels = async (
|
|
81
|
+
path = codexModelsCachePath(),
|
|
82
|
+
): Promise<LiveHarnessModel[] | undefined> => {
|
|
83
|
+
try {
|
|
84
|
+
return parseCodexModelsCache(await readFile(path, "utf8"));
|
|
85
|
+
} catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const parseCopilotModelValues = (helpText: string): LiveHarnessModel[] | undefined => {
|
|
91
|
+
const lines = helpText.split("\n");
|
|
92
|
+
const start = lines.findIndex((line) => /^\s*`model`:/.test(line));
|
|
93
|
+
if (start < 0) return undefined;
|
|
94
|
+
const models: LiveHarnessModel[] = [];
|
|
95
|
+
for (const line of lines.slice(start + 1)) {
|
|
96
|
+
const match = /^\s*-\s*"([^"]+)"\s*$/.exec(line);
|
|
97
|
+
if (!match?.[1]) break;
|
|
98
|
+
models.push({ id: match[1] });
|
|
99
|
+
}
|
|
100
|
+
return models.length > 0 ? models : undefined;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const readCopilotLiveModels = async (
|
|
104
|
+
command = "copilot",
|
|
105
|
+
): Promise<LiveHarnessModel[] | undefined> => {
|
|
106
|
+
try {
|
|
107
|
+
const result = await exec(command, ["help", "config"], { timeoutMs: 15_000 });
|
|
108
|
+
if (result.exitCode !== 0 || result.timedOut) return undefined;
|
|
109
|
+
return parseCopilotModelValues(result.stdout);
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type HarnessCatalogProbe = {
|
|
116
|
+
catalog: ClosedHarnessCatalog;
|
|
117
|
+
source: string;
|
|
118
|
+
read: (command: string | undefined) => Promise<LiveHarnessModel[] | undefined>;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const harnessCatalogProbes: readonly HarnessCatalogProbe[] = [
|
|
122
|
+
{
|
|
123
|
+
catalog: codexCatalog,
|
|
124
|
+
source: "models_cache.json",
|
|
125
|
+
read: () => readCodexLiveModels(),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
catalog: copilotCatalog,
|
|
129
|
+
source: "help config",
|
|
130
|
+
read: (command) => readCopilotLiveModels(command),
|
|
131
|
+
},
|
|
132
|
+
];
|