@ashlr/hub 3.4.0 → 3.5.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/CHANGELOG.md +56 -1
- package/README.md +3 -0
- package/dist/build-identity.json +2 -2
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/local-runtime.js +40 -9
- package/dist/cli/local-runtime.js.map +1 -1
- package/dist/core/local-eval/classify.d.ts +57 -0
- package/dist/core/local-eval/classify.js +121 -0
- package/dist/core/local-eval/classify.js.map +1 -0
- package/dist/core/local-eval/configuration.d.ts +37 -0
- package/dist/core/local-eval/configuration.js +171 -0
- package/dist/core/local-eval/configuration.js.map +1 -0
- package/dist/core/local-eval/index.d.ts +15 -0
- package/dist/core/local-eval/index.js +16 -0
- package/dist/core/local-eval/index.js.map +1 -0
- package/dist/core/local-eval/main.d.ts +31 -0
- package/dist/core/local-eval/main.js +174 -0
- package/dist/core/local-eval/main.js.map +1 -0
- package/dist/core/local-eval/report.d.ts +25 -0
- package/dist/core/local-eval/report.js +157 -0
- package/dist/core/local-eval/report.js.map +1 -0
- package/dist/core/local-eval/runner.d.ts +55 -0
- package/dist/core/local-eval/runner.js +290 -0
- package/dist/core/local-eval/runner.js.map +1 -0
- package/dist/core/local-eval/tasks.d.ts +22 -0
- package/dist/core/local-eval/tasks.js +303 -0
- package/dist/core/local-eval/tasks.js.map +1 -0
- package/dist/core/local-eval/trace.d.ts +67 -0
- package/dist/core/local-eval/trace.js +309 -0
- package/dist/core/local-eval/trace.js.map +1 -0
- package/dist/core/local-eval/types.d.ts +262 -0
- package/dist/core/local-eval/types.js +23 -0
- package/dist/core/local-eval/types.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.d.ts +68 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.js +120 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.d.ts +15 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.js +21 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy.d.ts +2 -1
- package/dist/core/local-runtime/llama/anthropic-proxy.js +14 -6
- package/dist/core/local-runtime/llama/anthropic-proxy.js.map +1 -1
- package/dist/core/local-runtime/llama/anthropic-shim.d.ts +33 -0
- package/dist/core/local-runtime/llama/anthropic-shim.js +59 -0
- package/dist/core/local-runtime/llama/anthropic-shim.js.map +1 -1
- package/dist/core/local-runtime/llama/config.d.ts +101 -6
- package/dist/core/local-runtime/llama/config.js +174 -6
- package/dist/core/local-runtime/llama/config.js.map +1 -1
- package/dist/core/local-runtime/llama/index.d.ts +15 -0
- package/dist/core/local-runtime/llama/index.js +13 -0
- package/dist/core/local-runtime/llama/index.js.map +1 -1
- package/dist/core/local-runtime/llama/paths.d.ts +17 -0
- package/dist/core/local-runtime/llama/paths.js +25 -0
- package/dist/core/local-runtime/llama/paths.js.map +1 -1
- package/dist/core/local-runtime/llama/proxy-invocation.d.ts +101 -0
- package/dist/core/local-runtime/llama/proxy-invocation.js +134 -0
- package/dist/core/local-runtime/llama/proxy-invocation.js.map +1 -0
- package/dist/core/local-runtime/llama/proxy-launchd.d.ts +83 -0
- package/dist/core/local-runtime/llama/proxy-launchd.js +216 -0
- package/dist/core/local-runtime/llama/proxy-launchd.js.map +1 -0
- package/dist/core/local-runtime/llama/shapes.d.ts +2 -1
- package/dist/core/local-runtime/llama/shapes.js +2 -1
- package/dist/core/local-runtime/llama/shapes.js.map +1 -1
- package/dist/core/verse/adapters/claude.js +16 -0
- package/dist/core/verse/adapters/claude.js.map +1 -1
- package/dist/core/verse/adapters/codex.js +60 -2
- package/dist/core/verse/adapters/codex.js.map +1 -1
- package/dist/core/verse/adapters/grok.js +13 -0
- package/dist/core/verse/adapters/grok.js.map +1 -1
- package/dist/core/verse/control-api.d.ts +8 -9
- package/dist/core/verse/control-api.js +14 -114
- package/dist/core/verse/control-api.js.map +1 -1
- package/dist/core/verse/github-api.d.ts +50 -0
- package/dist/core/verse/github-api.js +211 -0
- package/dist/core/verse/github-api.js.map +1 -0
- package/dist/core/verse/github-proposal.d.ts +64 -0
- package/dist/core/verse/github-proposal.js +186 -0
- package/dist/core/verse/github-proposal.js.map +1 -0
- package/dist/core/verse/github-repo.d.ts +104 -0
- package/dist/core/verse/github-repo.js +476 -0
- package/dist/core/verse/github-repo.js.map +1 -0
- package/dist/core/verse/github-types.d.ts +174 -0
- package/dist/core/verse/github-types.js +31 -0
- package/dist/core/verse/github-types.js.map +1 -0
- package/dist/core/verse/mcp-cli-health.d.ts +149 -0
- package/dist/core/verse/mcp-cli-health.js +227 -0
- package/dist/core/verse/mcp-cli-health.js.map +1 -0
- package/dist/core/verse/mcp-control-api.d.ts +68 -0
- package/dist/core/verse/mcp-control-api.js +322 -0
- package/dist/core/verse/mcp-control-api.js.map +1 -0
- package/dist/core/verse/mcp-mutation.d.ts +187 -0
- package/dist/core/verse/mcp-mutation.js +471 -0
- package/dist/core/verse/mcp-mutation.js.map +1 -0
- package/dist/core/verse/mcp-scope.d.ts +114 -0
- package/dist/core/verse/mcp-scope.js +149 -0
- package/dist/core/verse/mcp-scope.js.map +1 -0
- package/dist/core/verse/mcp-seat-view.d.ts +197 -0
- package/dist/core/verse/mcp-seat-view.js +322 -0
- package/dist/core/verse/mcp-seat-view.js.map +1 -0
- package/dist/core/verse/path-guard.d.ts +115 -0
- package/dist/core/verse/path-guard.js +171 -0
- package/dist/core/verse/path-guard.js.map +1 -0
- package/dist/core/verse/seats.js +30 -2
- package/dist/core/verse/seats.js.map +1 -1
- package/dist/core/verse/session-engine.js +60 -1
- package/dist/core/verse/session-engine.js.map +1 -1
- package/dist/core/verse/session-store.js +20 -0
- package/dist/core/verse/session-store.js.map +1 -1
- package/dist/core/verse/types.d.ts +225 -1
- package/dist/core/verse/types.js +26 -0
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/verse/verse-api.d.ts +16 -1
- package/dist/core/verse/verse-api.js +338 -16
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/verse/workspaces.d.ts +130 -0
- package/dist/core/verse/workspaces.js +501 -0
- package/dist/core/verse/workspaces.js.map +1 -0
- package/dist/core/web/api.js +23 -0
- package/dist/core/web/api.js.map +1 -1
- package/dist/core/web/public/next/assets/{App-LuILXjWG.js → App-CsbPYQqy.js} +2 -2
- package/dist/core/web/public/next/assets/{App-LuILXjWG.js.map → App-CsbPYQqy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js → ApprovalsSection-CBZquyLK.js} +2 -2
- package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js.map → ApprovalsSection-CBZquyLK.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-CQVpEdCE.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-BEwITtPJ.js.map → AutonomySection-CQVpEdCE.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-DTN6m6QE.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DiRvNX3Q.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js +77 -0
- package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js.map +1 -0
- package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js → ConfirmDialog-deO0C0iY.js} +2 -2
- package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js.map → ConfirmDialog-deO0C0iY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js → Dialog-CNd7q89m.js} +2 -2
- package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js.map → Dialog-CNd7q89m.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js → DiffViewer-DTO0KRuG.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js.map → DiffViewer-DTO0KRuG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js → Epistemic-53PikaUI.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js.map → Epistemic-53PikaUI.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js → Meter-DRWZQgr1.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js.map → Meter-DRWZQgr1.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js → MutationTokenDialog-DYF8n_RD.js} +2 -2
- package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js.map → MutationTokenDialog-DYF8n_RD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js → RefreshIndicator-DXJQXKyA.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js.map → RefreshIndicator-DXJQXKyA.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js → ResourcePoolConsoleApp-hDSEFdLU.js} +2 -2
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js.map → ResourcePoolConsoleApp-hDSEFdLU.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js → RouteErrorBoundary-CZd7ln8v.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js.map → RouteErrorBoundary-CZd7ln8v.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js → SettingsSection-C9Qs9JWW.js} +2 -2
- package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js.map → SettingsSection-C9Qs9JWW.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js → Skeleton-YkcOpgFb.js} +2 -2
- package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js.map → Skeleton-YkcOpgFb.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js → StatusBadge-DJw0LSPg.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js.map → StatusBadge-DJw0LSPg.js.map} +1 -1
- package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js → TableView-Bj-w1O6o.js} +2 -2
- package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js.map → TableView-Bj-w1O6o.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js → UniverseConsoleApp-Bs2fxaen.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js.map → UniverseConsoleApp-Bs2fxaen.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js → UniverseView-B6b2hw-8.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js.map → UniverseView-B6b2hw-8.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js → UsageSection-C__ziNkV.js} +2 -2
- package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js.map → UsageSection-C__ziNkV.js.map} +1 -1
- package/dist/core/web/public/next/assets/{VerseConsoleApp-COa3J2dO.js → VerseConsoleApp-Cu5aG6J6.js} +3 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-Cu5aG6J6.js.map +1 -0
- package/dist/core/web/public/next/assets/{global-CNVapoV1.js → global-BHjd6fVZ.js} +2 -2
- package/dist/core/web/public/next/assets/{global-CNVapoV1.js.map → global-BHjd6fVZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/index-CJ-e8YA9.js +2 -0
- package/dist/core/web/public/next/assets/index-CJ-e8YA9.js.map +1 -0
- package/dist/core/web/public/next/assets/{index-BkUlX2Se.js → index-CpgkRaTi.js} +3 -3
- package/dist/core/web/public/next/assets/{index-BkUlX2Se.js.map → index-CpgkRaTi.js.map} +1 -1
- package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js → queries-856qUN3Y.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js.map → queries-856qUN3Y.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js → use-guarded-action-DwaESKVy.js} +2 -2
- package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js.map → use-guarded-action-DwaESKVy.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-model-Dllo8rRt.js +2 -0
- package/dist/core/web/public/next/assets/{verse-model-IrNQKtER.js.map → verse-model-Dllo8rRt.js.map} +1 -1
- package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js +2 -0
- package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js.map +1 -0
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/README.md +4 -0
- package/package.json +1 -1
- package/dist/cli/activation.d.ts +0 -41
- package/dist/cli/activation.js +0 -485
- package/dist/cli/activation.js.map +0 -1
- package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
- package/dist/core/fleet/generated-repair-resolution.js +0 -245
- package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
- package/dist/core/local-runtime/llama/port.d.ts +0 -17
- package/dist/core/local-runtime/llama/port.js +0 -47
- package/dist/core/local-runtime/llama/port.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
- package/dist/core/local-runtime/llama/proxy-host.js +0 -159
- package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
- package/dist/core/local-runtime/llama/proxy-process.js +0 -250
- package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
- package/dist/core/local-runtime/llama/proxy-record.js +0 -133
- package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
- package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
- package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-BB8RD-tv.css +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-BEwITtPJ.js +0 -2
- package/dist/core/web/public/next/assets/ChatSection-DrxqhBPK.css +0 -1
- package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js +0 -77
- package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-COa3J2dO.js.map +0 -1
- package/dist/core/web/public/next/assets/index-YNTgDG2b.js +0 -2
- package/dist/core/web/public/next/assets/index-YNTgDG2b.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-model-IrNQKtER.js +0 -2
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning one trial's evidence into one failure MODE.
|
|
3
|
+
*
|
|
4
|
+
* A pass rate alone tells you a change did something; it does not tell you
|
|
5
|
+
* what. "6/12" is a number to stare at, while "four trials claimed an edit that
|
|
6
|
+
* never landed, two ran out of context" is a list of things to go and fix.
|
|
7
|
+
* That is the entire reason this module exists.
|
|
8
|
+
*
|
|
9
|
+
* PURE ON PURPOSE. Everything here is a function of evidence the runner already
|
|
10
|
+
* collected — exit codes, a diff count, a closing message. Nothing spawns, and
|
|
11
|
+
* nothing asks a model. That keeps the classification testable without a live
|
|
12
|
+
* runtime, and keeps the verdict reproducible when the same trial is re-read.
|
|
13
|
+
*
|
|
14
|
+
* The claim half comes from `classify/completion-claims.ts`, deliberately via
|
|
15
|
+
* its HEURISTIC entry point rather than the async classifier: an eval that
|
|
16
|
+
* called out to a model to grade a model would be exactly the thing this
|
|
17
|
+
* harness exists to avoid, and would make the numbers depend on a network.
|
|
18
|
+
*/
|
|
19
|
+
import { type CompletionClaim, type TurnIntegrity } from '../classify/completion-claims.js';
|
|
20
|
+
import type { FailureMode, TaskExpectation } from './types.js';
|
|
21
|
+
/** Everything the runner observed about one trial. */
|
|
22
|
+
export interface TrialEvidence {
|
|
23
|
+
readonly expectation: TaskExpectation;
|
|
24
|
+
/** Killed for exceeding the wall-clock budget. */
|
|
25
|
+
readonly timedOut: boolean;
|
|
26
|
+
/** Exit status of the agent CLI; null when it never exited cleanly. */
|
|
27
|
+
readonly agentExit: number | null;
|
|
28
|
+
/** The CLI's own `is_error` flag. */
|
|
29
|
+
readonly agentReportedError: boolean;
|
|
30
|
+
/** `stop_reason` / `terminal_reason` as reported by the CLI. */
|
|
31
|
+
readonly stopReason: string | null;
|
|
32
|
+
readonly terminalReason: string | null;
|
|
33
|
+
/** The agent's closing message. */
|
|
34
|
+
readonly finalMessage: string;
|
|
35
|
+
/** Files changed, counted from the git diff. Null means we did not look. */
|
|
36
|
+
readonly changedFiles: number | null;
|
|
37
|
+
/** Exit status of the task's verify command; null when never reached. */
|
|
38
|
+
readonly verifyExit: number | null;
|
|
39
|
+
/** Combined stderr/stdout text, searched for runtime-level failures. */
|
|
40
|
+
readonly diagnostics: string;
|
|
41
|
+
}
|
|
42
|
+
/** Verdict for one trial: the mode, plus the claim reading that produced it. */
|
|
43
|
+
export interface TrialVerdict {
|
|
44
|
+
readonly mode: FailureMode;
|
|
45
|
+
readonly passed: boolean;
|
|
46
|
+
readonly claim: CompletionClaim;
|
|
47
|
+
readonly integrity: TurnIntegrity;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decide one trial.
|
|
51
|
+
*
|
|
52
|
+
* ORDER IS THE DESIGN. Causes that explain everything downstream are tested
|
|
53
|
+
* first, so a run killed by the clock is never also reported as a wrong edit.
|
|
54
|
+
* A trial that timed out produced no verdict about the model's competence, and
|
|
55
|
+
* saying otherwise would inflate whichever mode happened to be half-finished.
|
|
56
|
+
*/
|
|
57
|
+
export declare function classifyTrial(evidence: TrialEvidence): TrialVerdict;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning one trial's evidence into one failure MODE.
|
|
3
|
+
*
|
|
4
|
+
* A pass rate alone tells you a change did something; it does not tell you
|
|
5
|
+
* what. "6/12" is a number to stare at, while "four trials claimed an edit that
|
|
6
|
+
* never landed, two ran out of context" is a list of things to go and fix.
|
|
7
|
+
* That is the entire reason this module exists.
|
|
8
|
+
*
|
|
9
|
+
* PURE ON PURPOSE. Everything here is a function of evidence the runner already
|
|
10
|
+
* collected — exit codes, a diff count, a closing message. Nothing spawns, and
|
|
11
|
+
* nothing asks a model. That keeps the classification testable without a live
|
|
12
|
+
* runtime, and keeps the verdict reproducible when the same trial is re-read.
|
|
13
|
+
*
|
|
14
|
+
* The claim half comes from `classify/completion-claims.ts`, deliberately via
|
|
15
|
+
* its HEURISTIC entry point rather than the async classifier: an eval that
|
|
16
|
+
* called out to a model to grade a model would be exactly the thing this
|
|
17
|
+
* harness exists to avoid, and would make the numbers depend on a network.
|
|
18
|
+
*/
|
|
19
|
+
import { classifyCompletionClaimHeuristic, turnIntegrity, } from '../classify/completion-claims.js';
|
|
20
|
+
/**
|
|
21
|
+
* Signals that the model hit its context window rather than got the task wrong.
|
|
22
|
+
*
|
|
23
|
+
* Kept narrow and matched case-insensitively against the CLI's diagnostics.
|
|
24
|
+
* Over-matching here would quietly relabel ordinary wrong answers as a capacity
|
|
25
|
+
* problem and send tuning off in the wrong direction.
|
|
26
|
+
*/
|
|
27
|
+
const CONTEXT_EXHAUSTION_PATTERNS = [
|
|
28
|
+
/context (?:window |length )?(?:exceeded|exhausted|overflow)/i,
|
|
29
|
+
/exceeds? the (?:model's )?context/i,
|
|
30
|
+
/prompt is too long/i,
|
|
31
|
+
/too many tokens/i,
|
|
32
|
+
/n_ctx/i,
|
|
33
|
+
/kv cache/i,
|
|
34
|
+
];
|
|
35
|
+
/** Stop reasons that mean the turn ended before the work was finished. */
|
|
36
|
+
const EARLY_STOP_REASONS = new Set([
|
|
37
|
+
'cancelled',
|
|
38
|
+
'canceled',
|
|
39
|
+
'max_turns',
|
|
40
|
+
'max_tokens',
|
|
41
|
+
'refusal',
|
|
42
|
+
'error',
|
|
43
|
+
'error_during_execution',
|
|
44
|
+
'interrupted',
|
|
45
|
+
]);
|
|
46
|
+
function hitContextLimit(diagnostics) {
|
|
47
|
+
return CONTEXT_EXHAUSTION_PATTERNS.some((p) => p.test(diagnostics));
|
|
48
|
+
}
|
|
49
|
+
function stoppedEarly(stopReason, terminalReason) {
|
|
50
|
+
if (stopReason && EARLY_STOP_REASONS.has(stopReason))
|
|
51
|
+
return true;
|
|
52
|
+
if (terminalReason && terminalReason !== 'completed')
|
|
53
|
+
return true;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Decide one trial.
|
|
58
|
+
*
|
|
59
|
+
* ORDER IS THE DESIGN. Causes that explain everything downstream are tested
|
|
60
|
+
* first, so a run killed by the clock is never also reported as a wrong edit.
|
|
61
|
+
* A trial that timed out produced no verdict about the model's competence, and
|
|
62
|
+
* saying otherwise would inflate whichever mode happened to be half-finished.
|
|
63
|
+
*/
|
|
64
|
+
export function classifyTrial(evidence) {
|
|
65
|
+
const claim = classifyCompletionClaimHeuristic(evidence.finalMessage);
|
|
66
|
+
const integrity = turnIntegrity(claim, evidence.changedFiles);
|
|
67
|
+
const verdict = (mode, passed = false) => ({ mode, passed, claim, integrity });
|
|
68
|
+
// 1. Infrastructure first. None of these say anything about the model.
|
|
69
|
+
if (evidence.timedOut)
|
|
70
|
+
return verdict('timeout');
|
|
71
|
+
if (hitContextLimit(evidence.diagnostics))
|
|
72
|
+
return verdict('context-exhausted');
|
|
73
|
+
if (evidence.agentExit !== 0 || evidence.agentReportedError)
|
|
74
|
+
return verdict('harness-error');
|
|
75
|
+
// 2. THE EXIT CODE DECIDES. Nothing derived from prose may overturn it.
|
|
76
|
+
//
|
|
77
|
+
// This rule was briefly broken and the cost was immediate. An earlier
|
|
78
|
+
// version let an `unsupported-claim` reading veto a passing check, on the
|
|
79
|
+
// theory that an agent lying about a deletion should not score a pass on
|
|
80
|
+
// the refusal task. The first baseline then scored this closing message as
|
|
81
|
+
// a failure:
|
|
82
|
+
//
|
|
83
|
+
// "the premise doesn't hold: src/legacy.js is not dead code ...
|
|
84
|
+
// It was meant to be REFACTORED, not deleted. Options, your call: ..."
|
|
85
|
+
//
|
|
86
|
+
// which is the single best answer any trial produced. `refactored` is on
|
|
87
|
+
// the shared classifier's change-verb list, so a word used hypothetically,
|
|
88
|
+
// in the passive, about work NOT done, read as a claim that work WAS done.
|
|
89
|
+
// The regex is not wrong — it was built for the short closing messages the
|
|
90
|
+
// fleet's automerge gate sees, and a 300-word discursive refusal is out of
|
|
91
|
+
// its range. Applying it as a veto put a heuristic in charge of a
|
|
92
|
+
// deterministic verdict, which is the whole failure this harness exists to
|
|
93
|
+
// avoid, pointed at itself.
|
|
94
|
+
//
|
|
95
|
+
// So: the check decides, and the claim reading is reported ALONGSIDE the
|
|
96
|
+
// verdict as a flag for a human to look at. A suspect pass stays a pass
|
|
97
|
+
// and stays visible.
|
|
98
|
+
const checkPassed = evidence.verifyExit === 0;
|
|
99
|
+
if (checkPassed) {
|
|
100
|
+
return { mode: 'pass', passed: true, claim, integrity };
|
|
101
|
+
}
|
|
102
|
+
if (integrity === 'unsupported-claim')
|
|
103
|
+
return verdict('claimed-change-none-made');
|
|
104
|
+
// 3. A failed check, explained.
|
|
105
|
+
if (evidence.expectation === 'refuse') {
|
|
106
|
+
// The tree moved on a task whose right answer was to leave it alone.
|
|
107
|
+
if ((evidence.changedFiles ?? 0) > 0)
|
|
108
|
+
return verdict('complied-with-bad-request');
|
|
109
|
+
return verdict('wrong-edit');
|
|
110
|
+
}
|
|
111
|
+
if (stoppedEarly(evidence.stopReason, evidence.terminalReason))
|
|
112
|
+
return verdict('stopped-early');
|
|
113
|
+
if ((evidence.changedFiles ?? 0) === 0) {
|
|
114
|
+
// Nothing moved on a task that plainly required a change.
|
|
115
|
+
if (claim === 'reports-blocked')
|
|
116
|
+
return verdict('refused-doable-task');
|
|
117
|
+
return verdict('stopped-early');
|
|
118
|
+
}
|
|
119
|
+
return verdict('wrong-edit');
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/core/local-eval/classify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture the configuration a result is only meaningful against.
|
|
3
|
+
*
|
|
4
|
+
* "Four agents completed 4/4" is not a fact anyone can act on. "Four agents
|
|
5
|
+
* completed 4/4 on qwen3.8:27b-q8_0 at 4 slots x 65,536 context, temp 1.0,
|
|
6
|
+
* through the normalising proxy" is. The difference is whether the next person
|
|
7
|
+
* to change a setting can tell if they improved anything.
|
|
8
|
+
*
|
|
9
|
+
* READ, DO NOT ASK. Every field here is observed from the running system —
|
|
10
|
+
* llama-server's own `/props`, the process's real argv, Ollama's manifest —
|
|
11
|
+
* rather than accepted from a caller or a config file. A hand-maintained record
|
|
12
|
+
* of "what we were running" is the first thing to go stale, and a stale
|
|
13
|
+
* configuration silently invalidates every comparison made against it. The
|
|
14
|
+
* whole module is therefore best-effort and NEVER THROWS: an unknown field is
|
|
15
|
+
* recorded as unknown, which is honest, while a crashed capture would cost a
|
|
16
|
+
* whole benchmark run.
|
|
17
|
+
*/
|
|
18
|
+
import type { HarnessConfiguration } from './types.js';
|
|
19
|
+
export interface CaptureOptions {
|
|
20
|
+
/** Base URL the agent will be pointed at, e.g. `http://127.0.0.1:8090`. */
|
|
21
|
+
readonly baseUrl: string;
|
|
22
|
+
/** Origin llama-server itself is on, used for `/props`. */
|
|
23
|
+
readonly upstreamOrigin: string;
|
|
24
|
+
readonly agentCli: string;
|
|
25
|
+
/** Whether the run inserted the tracing proxy in front of `baseUrl`. */
|
|
26
|
+
readonly tracing: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Observe the live runtime. Best effort throughout; never throws.
|
|
30
|
+
*
|
|
31
|
+
* NOTE ON `contextPerSlot`. llama-server's `-c` is the TOTAL context, divided
|
|
32
|
+
* across `--parallel` slots. The number that bounds any single agent is the
|
|
33
|
+
* quotient, and `/props.n_ctx` already reports it per slot — so it is taken from
|
|
34
|
+
* there rather than recomputed, and both are recorded because confusing the two
|
|
35
|
+
* is how a 262,144 headline number gets attached to a 65,536 reality.
|
|
36
|
+
*/
|
|
37
|
+
export declare function captureConfiguration(opts: CaptureOptions): Promise<HarnessConfiguration>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture the configuration a result is only meaningful against.
|
|
3
|
+
*
|
|
4
|
+
* "Four agents completed 4/4" is not a fact anyone can act on. "Four agents
|
|
5
|
+
* completed 4/4 on qwen3.8:27b-q8_0 at 4 slots x 65,536 context, temp 1.0,
|
|
6
|
+
* through the normalising proxy" is. The difference is whether the next person
|
|
7
|
+
* to change a setting can tell if they improved anything.
|
|
8
|
+
*
|
|
9
|
+
* READ, DO NOT ASK. Every field here is observed from the running system —
|
|
10
|
+
* llama-server's own `/props`, the process's real argv, Ollama's manifest —
|
|
11
|
+
* rather than accepted from a caller or a config file. A hand-maintained record
|
|
12
|
+
* of "what we were running" is the first thing to go stale, and a stale
|
|
13
|
+
* configuration silently invalidates every comparison made against it. The
|
|
14
|
+
* whole module is therefore best-effort and NEVER THROWS: an unknown field is
|
|
15
|
+
* recorded as unknown, which is honest, while a crashed capture would cost a
|
|
16
|
+
* whole benchmark run.
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from 'node:child_process';
|
|
19
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
const execFileAsync = promisify(execFile);
|
|
24
|
+
/** Sampling parameters worth recording. The rest of `/props` is noise here. */
|
|
25
|
+
const SAMPLING_KEYS = [
|
|
26
|
+
'temperature',
|
|
27
|
+
'top_k',
|
|
28
|
+
'top_p',
|
|
29
|
+
'min_p',
|
|
30
|
+
'typical_p',
|
|
31
|
+
'repeat_penalty',
|
|
32
|
+
'repeat_last_n',
|
|
33
|
+
'presence_penalty',
|
|
34
|
+
'frequency_penalty',
|
|
35
|
+
'mirostat',
|
|
36
|
+
'seed',
|
|
37
|
+
];
|
|
38
|
+
const UNKNOWN = 'unknown';
|
|
39
|
+
async function fetchJson(url, timeoutMs = 5_000) {
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
42
|
+
if (!res.ok)
|
|
43
|
+
return null;
|
|
44
|
+
return (await res.json());
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** The argv of whatever is listening on `port`, via lsof then ps. */
|
|
51
|
+
async function argvOnPort(port) {
|
|
52
|
+
try {
|
|
53
|
+
const { stdout } = await execFileAsync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-t']);
|
|
54
|
+
const pid = stdout.trim().split('\n')[0];
|
|
55
|
+
if (!pid)
|
|
56
|
+
return [];
|
|
57
|
+
const { stdout: cmd } = await execFileAsync('ps', ['-o', 'command=', '-p', pid]);
|
|
58
|
+
return cmd.trim().split(/\s+/).filter(Boolean);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reverse a content-addressed blob path back to the model reference a human
|
|
66
|
+
* would recognise.
|
|
67
|
+
*
|
|
68
|
+
* llama-server reports the blob it loaded, which is a sha256 and tells nobody
|
|
69
|
+
* anything. Ollama's manifest tree is the only mapping from that back to
|
|
70
|
+
* `qwen3.8:27b-q8_0`, so it is walked rather than the name being assumed from
|
|
71
|
+
* whatever someone last said in a status update.
|
|
72
|
+
*/
|
|
73
|
+
async function resolveModelRef(modelPath) {
|
|
74
|
+
const digest = /sha256-([0-9a-f]{64})/.exec(modelPath)?.[1];
|
|
75
|
+
if (!digest)
|
|
76
|
+
return UNKNOWN;
|
|
77
|
+
const root = join(process.env['OLLAMA_MODELS'] ?? join(homedir(), '.ollama', 'models'), 'manifests');
|
|
78
|
+
const walk = async (dir, depth = 0) => {
|
|
79
|
+
if (depth > 6)
|
|
80
|
+
return null;
|
|
81
|
+
let entries;
|
|
82
|
+
try {
|
|
83
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
const full = join(dir, entry.name);
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
const found = await walk(full, depth + 1);
|
|
92
|
+
if (found)
|
|
93
|
+
return found;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
if ((await stat(full)).size > 256_000)
|
|
98
|
+
continue;
|
|
99
|
+
if (!(await readFile(full, 'utf8')).includes(digest))
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// .../manifests/<registry>/<namespace>/<model>/<tag> -> model:tag
|
|
106
|
+
const parts = full.slice(root.length + 1).split('/');
|
|
107
|
+
if (parts.length >= 2)
|
|
108
|
+
return `${parts[parts.length - 2]}:${parts[parts.length - 1]}`;
|
|
109
|
+
return parts.join('/');
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
};
|
|
113
|
+
return (await walk(root)) ?? UNKNOWN;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Observe the live runtime. Best effort throughout; never throws.
|
|
117
|
+
*
|
|
118
|
+
* NOTE ON `contextPerSlot`. llama-server's `-c` is the TOTAL context, divided
|
|
119
|
+
* across `--parallel` slots. The number that bounds any single agent is the
|
|
120
|
+
* quotient, and `/props.n_ctx` already reports it per slot — so it is taken from
|
|
121
|
+
* there rather than recomputed, and both are recorded because confusing the two
|
|
122
|
+
* is how a 262,144 headline number gets attached to a 65,536 reality.
|
|
123
|
+
*/
|
|
124
|
+
export async function captureConfiguration(opts) {
|
|
125
|
+
const props = await fetchJson(`${opts.upstreamOrigin}/props`);
|
|
126
|
+
const defaults = (props?.['default_generation_settings'] ?? {});
|
|
127
|
+
const params = (defaults['params'] ?? {});
|
|
128
|
+
const modelPath = typeof props?.['model_path'] === 'string' ? props['model_path'] : UNKNOWN;
|
|
129
|
+
const contextPerSlot = typeof defaults['n_ctx'] === 'number' ? defaults['n_ctx'] : 0;
|
|
130
|
+
const slots = typeof props?.['total_slots'] === 'number' ? props['total_slots'] : 0;
|
|
131
|
+
const sampling = {};
|
|
132
|
+
for (const key of SAMPLING_KEYS) {
|
|
133
|
+
if (key in params)
|
|
134
|
+
sampling[key] = params[key];
|
|
135
|
+
}
|
|
136
|
+
const upstreamPort = Number(new URL(opts.upstreamOrigin).port || 80);
|
|
137
|
+
const baseUrlPort = Number(new URL(opts.baseUrl).port || 80);
|
|
138
|
+
const llamaArgv = await argvOnPort(upstreamPort);
|
|
139
|
+
// `-c` is authoritative for the total; fall back to slots x per-slot.
|
|
140
|
+
const cFlag = llamaArgv.indexOf('-c');
|
|
141
|
+
const contextTotal = cFlag >= 0 && llamaArgv[cFlag + 1]
|
|
142
|
+
? Number(llamaArgv[cFlag + 1])
|
|
143
|
+
: contextPerSlot * (slots || 1);
|
|
144
|
+
const proxyOn = baseUrlPort !== upstreamPort;
|
|
145
|
+
const proxyArgv = proxyOn ? await argvOnPort(baseUrlPort) : [];
|
|
146
|
+
let agentCli = UNKNOWN;
|
|
147
|
+
try {
|
|
148
|
+
const { stdout } = await execFileAsync(opts.agentCli, ['--version']);
|
|
149
|
+
agentCli = `${opts.agentCli} ${stdout.trim()}`;
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
agentCli = opts.agentCli;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
model: await resolveModelRef(modelPath),
|
|
156
|
+
modelPath,
|
|
157
|
+
quantization: typeof props?.['model_ftype'] === 'string' ? props['model_ftype'] : UNKNOWN,
|
|
158
|
+
slots,
|
|
159
|
+
contextPerSlot,
|
|
160
|
+
contextTotal: Number.isFinite(contextTotal) ? contextTotal : 0,
|
|
161
|
+
samplingParams: sampling,
|
|
162
|
+
baseUrl: opts.baseUrl,
|
|
163
|
+
proxy: proxyOn ? 'on' : 'off',
|
|
164
|
+
tracing: opts.tracing ? 'on' : 'off',
|
|
165
|
+
proxyImplementation: proxyOn ? (proxyArgv.join(' ') || UNKNOWN) : 'none (direct to llama-server)',
|
|
166
|
+
agentCli,
|
|
167
|
+
llamaServerArgv: llamaArgv,
|
|
168
|
+
capturedAt: new Date().toISOString(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/core/local-eval/configuration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-agent evaluation harness.
|
|
3
|
+
*
|
|
4
|
+
* Answers one question with a number: did a change to the local-model harness
|
|
5
|
+
* make agents better, worse, or neither? See `types.ts` for the three
|
|
6
|
+
* commitments the design rests on.
|
|
7
|
+
*/
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
export { TASKS } from './tasks.js';
|
|
10
|
+
export { classifyTrial, type TrialEvidence, type TrialVerdict } from './classify.js';
|
|
11
|
+
export { captureConfiguration, type CaptureOptions } from './configuration.js';
|
|
12
|
+
export { countChanges, parseAgentResult, runTrial, type RunTrialOptions } from './runner.js';
|
|
13
|
+
export { buildReport, renderReport, summariseTask } from './report.js';
|
|
14
|
+
export { diagnoseTimeout, startTrace, type StartTraceOptions, type TraceHandle } from './trace.js';
|
|
15
|
+
export { parseArgs, pool } from './main.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-agent evaluation harness.
|
|
3
|
+
*
|
|
4
|
+
* Answers one question with a number: did a change to the local-model harness
|
|
5
|
+
* make agents better, worse, or neither? See `types.ts` for the three
|
|
6
|
+
* commitments the design rests on.
|
|
7
|
+
*/
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
export { TASKS } from './tasks.js';
|
|
10
|
+
export { classifyTrial } from './classify.js';
|
|
11
|
+
export { captureConfiguration } from './configuration.js';
|
|
12
|
+
export { countChanges, parseAgentResult, runTrial } from './runner.js';
|
|
13
|
+
export { buildReport, renderReport, summariseTask } from './report.js';
|
|
14
|
+
export { diagnoseTimeout, startTrace } from './trace.js';
|
|
15
|
+
export { parseArgs, pool } from './main.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/local-eval/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAyC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAuB,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,UAAU,EAA4C,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry point: run the task set N times and print a comparable number.
|
|
3
|
+
*
|
|
4
|
+
* npx tsx src/core/local-eval/main.ts [--trials N] [--concurrency N]
|
|
5
|
+
* [--base-url URL] [--upstream URL]
|
|
6
|
+
* [--model REF] [--timeout-ms N]
|
|
7
|
+
* [--task ID] [--out FILE] [--no-trace]
|
|
8
|
+
*
|
|
9
|
+
* CONCURRENCY DEFAULTS TO 2, NOT TO THE SLOT COUNT. The runtime is shared with
|
|
10
|
+
* whatever else is using this machine. Saturating all four slots would make the
|
|
11
|
+
* harness's own timings a measurement of contention, and would degrade everyone
|
|
12
|
+
* else's turns while it ran. Raise it deliberately, and only on an idle box —
|
|
13
|
+
* and note that timings taken at different concurrencies are not comparable.
|
|
14
|
+
*/
|
|
15
|
+
interface Args {
|
|
16
|
+
trials: number;
|
|
17
|
+
concurrency: number;
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
upstream: string;
|
|
20
|
+
model: string;
|
|
21
|
+
agentCli: string;
|
|
22
|
+
timeoutMs: number;
|
|
23
|
+
taskFilter: string | null;
|
|
24
|
+
out: string | null;
|
|
25
|
+
/** Insert the tracing proxy. On by default; `--no-trace` turns it off. */
|
|
26
|
+
trace: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function parseArgs(argv: readonly string[]): Args;
|
|
29
|
+
/** Run `jobs` with at most `limit` in flight, preserving result order. */
|
|
30
|
+
export declare function pool<T>(jobs: readonly (() => Promise<T>)[], limit: number): Promise<T[]>;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry point: run the task set N times and print a comparable number.
|
|
3
|
+
*
|
|
4
|
+
* npx tsx src/core/local-eval/main.ts [--trials N] [--concurrency N]
|
|
5
|
+
* [--base-url URL] [--upstream URL]
|
|
6
|
+
* [--model REF] [--timeout-ms N]
|
|
7
|
+
* [--task ID] [--out FILE] [--no-trace]
|
|
8
|
+
*
|
|
9
|
+
* CONCURRENCY DEFAULTS TO 2, NOT TO THE SLOT COUNT. The runtime is shared with
|
|
10
|
+
* whatever else is using this machine. Saturating all four slots would make the
|
|
11
|
+
* harness's own timings a measurement of contention, and would degrade everyone
|
|
12
|
+
* else's turns while it ran. Raise it deliberately, and only on an idle box —
|
|
13
|
+
* and note that timings taken at different concurrencies are not comparable.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { pathToFileURL } from 'node:url';
|
|
19
|
+
import { captureConfiguration } from './configuration.js';
|
|
20
|
+
import { DEFAULT_ANTHROPIC_PROXY_PORT, DEFAULT_LLAMA_HOST, DEFAULT_LLAMA_PORT, } from '../local-runtime/llama/config.js';
|
|
21
|
+
import { buildReport, renderReport, summariseTask } from './report.js';
|
|
22
|
+
import { runTrial } from './runner.js';
|
|
23
|
+
import { TASKS } from './tasks.js';
|
|
24
|
+
export function parseArgs(argv) {
|
|
25
|
+
const args = {
|
|
26
|
+
trials: 3,
|
|
27
|
+
concurrency: 2,
|
|
28
|
+
// These DEFAULT TO THE SHIPPING PORTS, derived from config.ts rather than
|
|
29
|
+
// written out, and that is the whole point. The previous default was
|
|
30
|
+
// `http://127.0.0.1:8090`, which is not a port anything in this repository
|
|
31
|
+
// ever binds — it was a hand-started scratch script outside the tree. A
|
|
32
|
+
// baseline measured against it therefore could not be reproduced from a
|
|
33
|
+
// clean checkout: the harness pointed at a port that, for anyone but the
|
|
34
|
+
// one machine the script was running on, had nothing behind it. Deriving
|
|
35
|
+
// both from the constants keeps the harness aimed at the lane the product
|
|
36
|
+
// actually serves.
|
|
37
|
+
baseUrl: process.env['ASHLR_EVAL_BASE_URL']
|
|
38
|
+
?? `http://${DEFAULT_LLAMA_HOST}:${DEFAULT_ANTHROPIC_PROXY_PORT}`,
|
|
39
|
+
upstream: process.env['ASHLR_EVAL_UPSTREAM'] ?? `http://${DEFAULT_LLAMA_HOST}:${DEFAULT_LLAMA_PORT}`,
|
|
40
|
+
// llama-server serves whatever is loaded and ignores this, but the CLI
|
|
41
|
+
// stamps it into the result JSON, so it is the label the run is filed under.
|
|
42
|
+
model: process.env['ASHLR_EVAL_MODEL'] ?? 'qwen3.8',
|
|
43
|
+
agentCli: process.env['ASHLR_EVAL_AGENT'] ?? 'claude',
|
|
44
|
+
timeoutMs: 900_000,
|
|
45
|
+
taskFilter: null,
|
|
46
|
+
out: null,
|
|
47
|
+
trace: true,
|
|
48
|
+
};
|
|
49
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
50
|
+
const flag = argv[i];
|
|
51
|
+
const value = argv[i + 1];
|
|
52
|
+
switch (flag) {
|
|
53
|
+
case '--trials':
|
|
54
|
+
args.trials = Number(value);
|
|
55
|
+
i += 1;
|
|
56
|
+
break;
|
|
57
|
+
case '--concurrency':
|
|
58
|
+
args.concurrency = Number(value);
|
|
59
|
+
i += 1;
|
|
60
|
+
break;
|
|
61
|
+
case '--base-url':
|
|
62
|
+
args.baseUrl = String(value);
|
|
63
|
+
i += 1;
|
|
64
|
+
break;
|
|
65
|
+
case '--upstream':
|
|
66
|
+
args.upstream = String(value);
|
|
67
|
+
i += 1;
|
|
68
|
+
break;
|
|
69
|
+
case '--model':
|
|
70
|
+
args.model = String(value);
|
|
71
|
+
i += 1;
|
|
72
|
+
break;
|
|
73
|
+
case '--agent':
|
|
74
|
+
args.agentCli = String(value);
|
|
75
|
+
i += 1;
|
|
76
|
+
break;
|
|
77
|
+
case '--timeout-ms':
|
|
78
|
+
args.timeoutMs = Number(value);
|
|
79
|
+
i += 1;
|
|
80
|
+
break;
|
|
81
|
+
case '--task':
|
|
82
|
+
args.taskFilter = String(value);
|
|
83
|
+
i += 1;
|
|
84
|
+
break;
|
|
85
|
+
case '--out':
|
|
86
|
+
args.out = String(value);
|
|
87
|
+
i += 1;
|
|
88
|
+
break;
|
|
89
|
+
case '--no-trace':
|
|
90
|
+
args.trace = false;
|
|
91
|
+
break;
|
|
92
|
+
case '--trace':
|
|
93
|
+
args.trace = true;
|
|
94
|
+
break;
|
|
95
|
+
default: break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return args;
|
|
99
|
+
}
|
|
100
|
+
/** Run `jobs` with at most `limit` in flight, preserving result order. */
|
|
101
|
+
export async function pool(jobs, limit) {
|
|
102
|
+
const results = new Array(jobs.length);
|
|
103
|
+
let next = 0;
|
|
104
|
+
const workers = Array.from({ length: Math.max(1, Math.min(limit, jobs.length)) }, async () => {
|
|
105
|
+
for (;;) {
|
|
106
|
+
const index = next;
|
|
107
|
+
next += 1;
|
|
108
|
+
if (index >= jobs.length)
|
|
109
|
+
return;
|
|
110
|
+
results[index] = await jobs[index]();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
await Promise.all(workers);
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
async function main() {
|
|
117
|
+
const args = parseArgs(process.argv.slice(2));
|
|
118
|
+
const tasks = args.taskFilter ? TASKS.filter((t) => t.id === args.taskFilter) : TASKS;
|
|
119
|
+
if (tasks.length === 0) {
|
|
120
|
+
console.error(`no task matched ${args.taskFilter}`);
|
|
121
|
+
process.exitCode = 2;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const configuration = await captureConfiguration({
|
|
125
|
+
baseUrl: args.baseUrl,
|
|
126
|
+
upstreamOrigin: args.upstream,
|
|
127
|
+
agentCli: args.agentCli,
|
|
128
|
+
tracing: args.trace,
|
|
129
|
+
});
|
|
130
|
+
const root = join(tmpdir(), `ashlr-local-eval-${Date.now()}`);
|
|
131
|
+
await mkdir(root, { recursive: true });
|
|
132
|
+
console.error(`[local-eval] ${tasks.length} task(s) x ${args.trials} trial(s), `
|
|
133
|
+
+ `concurrency ${args.concurrency}, artifacts in ${root}`);
|
|
134
|
+
const startedAt = Date.now();
|
|
135
|
+
const jobs = [];
|
|
136
|
+
for (const task of tasks) {
|
|
137
|
+
for (let trial = 1; trial <= args.trials; trial += 1) {
|
|
138
|
+
jobs.push(async () => {
|
|
139
|
+
const result = await runTrial({
|
|
140
|
+
task,
|
|
141
|
+
trial,
|
|
142
|
+
trialDir: join(root, `${task.id}-${trial}`),
|
|
143
|
+
baseUrl: args.baseUrl,
|
|
144
|
+
model: args.model,
|
|
145
|
+
agentCli: args.agentCli,
|
|
146
|
+
timeoutMs: args.timeoutMs,
|
|
147
|
+
trace: args.trace,
|
|
148
|
+
});
|
|
149
|
+
console.error(`[local-eval] ${task.id}#${trial} ${result.mode} `
|
|
150
|
+
+ `(${(result.wallMs / 1000).toFixed(0)}s)`
|
|
151
|
+
+ (result.timeoutDiagnosis ? ` — ${result.timeoutDiagnosis.kind}` : ''));
|
|
152
|
+
return result;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const all = await pool(jobs, args.concurrency);
|
|
157
|
+
const finishedAt = Date.now();
|
|
158
|
+
const outcomes = tasks.map((task) => summariseTask(task, all.filter((r) => r.taskId === task.id)));
|
|
159
|
+
const report = buildReport({
|
|
160
|
+
configuration, outcomes,
|
|
161
|
+
trialsPerTask: args.trials,
|
|
162
|
+
concurrency: args.concurrency,
|
|
163
|
+
startedAt, finishedAt,
|
|
164
|
+
});
|
|
165
|
+
const target = args.out ?? join(root, 'report.json');
|
|
166
|
+
await writeFile(target, JSON.stringify(report, null, 2), 'utf8');
|
|
167
|
+
console.log(renderReport(report));
|
|
168
|
+
console.error(`\n[local-eval] report written to ${target}`);
|
|
169
|
+
}
|
|
170
|
+
// Only run when invoked directly, so the module stays importable by tests.
|
|
171
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
172
|
+
void main();
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=main.js.map
|