Repository navigation
chore(models): refresh stale model defaults (NodeAgent) - #15
Conversation
db984b0 to
f8d6f9a
Compare
|
Live-eval attempt on Reason: no provider key was available on the machine that ran this. Result: passed = false. The changed roles ( To produce the receipt (budget cap $1.5, one call per role), run once per role with the matching key set, e.g. |
f8d6f9a to
5a8ab49
Compare
Live provider smoke defaults move off retired/shut-down ids and the request shapes are fixed for the new targets (SPEC Part A §A.2 NodeAgent). - gpt-5.5 -> gpt-6.1-sol, claude-3-5-haiku-latest -> claude-haiku-5-5, gemini-2.0-flash -> gemini-3.8-flash - delete nex-agi/nex-n2-pro (absent from OpenRouter catalog 2026-10-10) from the priority list, .env.example and README - drop temperature (400 on Haiku 5.5, removed on GPT-6, deprecated on Gemini 3.6+); 16-token caps -> 256; OpenAI branch sends max_completion_tokens + reasoning_effort low; Anthropic text read by type - add request-shape test (fake fetch) for all four providers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read - clear NODEAGENT_*_MODEL overrides in beforeEach so a host-exported override no longer fails the default-resolution assertions - give the non-text fixture block a text field and assert it is excluded, so reverting the type filter in the Anthropic parser now fails the test Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ression test Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ider smoke
The smoke report recorded only the requested model, so none of the four
keyed runs could produce a receipt review would accept. Add the rule 3
fields; no model site, precedence or request shape changes.
- builders keep the response `model` (Gemini `modelVersion`) and usage;
OpenRouter `usage.cost` is recorded as costUsd, null elsewhere
- rows[{requestedModel, resolvedModel, echoedModel, usage, costUsd,
fallbackUsed}], candidateIds, echoedModels, command, environment
- testedCommit (git rev-parse HEAD), gitDirty (ignores docs/eval),
constantSha256 (this file at HEAD), budget with maxUsd stop
- fake-fetch tests: echoed model differs from requested and is recorded;
fallbackUsed set when the OpenRouter chain moves past index 0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…efusal row metadata spentUsd is null when any dispatched row has no reported cost (direct OpenAI, Anthropic, Gemini); the maxUsd guard reserves the per-request estimate before dispatch. gitDirty uses top-anchored pathspecs so it no longer depends on the caller's cwd. A refused Anthropic row now records echoedModel and usage. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…replies; quote receipt command The gitDirty probe was named *.tmp, which .gitignore:44 ignores, so git status never saw it and the test only passed on an already-dirty tree. The probe is now gitdirty-probe.txt, asserted not ignored, with a clean-tree precondition. A successful reply costing more than maxUsd now sets stoppedBy=maxUsd. The receipt command quotes whitespace arguments so it can be replayed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…command, request accounting An unreported-cost row no longer hides an over-budget reply: the known spend is compared to maxUsd even when spentUsd is null (retry after a 503, then a $0.11 reply, now sets stoppedBy). testedCommit, gitDirty and constantSha256 are captured by gitIdentity() before the first dispatch, so a non-git directory fails before any spend and a mid-run checkout change cannot misattribute the run. The receipt command is POSIX shell-quoted (semicolons, empty and quote characters replay). The credential is checked before a request is counted, so a missing key records requests:0 and no row. The gitDirty test now runs against an isolated temp git repo and no longer depends on the working tree. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s catch the regressions they name gitIdentity() now passes --untracked-files=all, so a user's status.showUntrackedFiles=no can no longer make an untracked file report gitDirty:false (SPEC-v3 A.0 rule 3 provenance). The gitIdentity test sets that config in the fixture and still expects gitDirty:true. It also asserts the docs/eval receipt leaves the tree clean from both the repo root and docs/ before any root probe file exists (catches a cwd-relative exclude), and constantSha256 equals the sha256 of the committed blob rather than only matching 64 hex characters. The non-git test title no longer claims main() ordering it does not exercise. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… replayable npx command; git tests get a time budget and assert the git error Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… at the estimate The post-request check summed only reported costs, so a 503 with no cost followed by a 0.095 reply (estimate total 0.105 > maxUsd 0.10) ended ok:true, stoppedBy:null. It now uses the same accounting as the pre-dispatch reservation. spentUsd stays null when any row is unknown. Adds the 503-then-0.095 fake-fetch case. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…at that sha constantSha256 used `git show HEAD:...` after a separate `git rev-parse HEAD`, so a checkout between the two calls produced a receipt naming commit A with commit B's hash. Hash `<testedCommit>:<script>` instead; a test moves HEAD between the two calls and asserts the hash belongs to the captured sha. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nking_budget, candidate_count, logprobs) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
8b12bea to
f7963e2
Compare
… gemini-3.8-flash Receipt-only commit (SPEC-v3 A.0 rule 3 form a). testedCommit f7963e2, clean tree, one request per role. OpenRouter chain recorded as live-unverified (no key, SPEC D2). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Live eval receipt: PASS for every changed non-OpenRouter role. Receipt-only commit 66360e3 on top of testedCommit f7963e2 (clean tree; nothing outside docs/eval changed after it; constantSha256 74d9f21a... matches
Echoed ids are the exact section 1.2 ids (no dated snapshot). Spend: 3 requests, no retries; direct providers report no cost, estimated well under $0.01 from token usage (cap $1.5; the script's own cap is $0.10, stoppedBy null). OpenRouter chain: not called (SPEC D2, no working key). Recorded as Command per role: |
Summary
Refreshes stale model defaults in the live provider smoke and fixes the request shapes the new ids require.
Runtime sites (9: migrate 4, freeze 5, defer 0)
Line numbers in the two tables below are SPEC/inventory coordinates on
origin/main(SPEC-v3 §A.2.3), so a reviewer can match them to the plan. Where the code now sits at headf7963e2: frozen chainscripts/nodeagent-live-provider-smoke.ts:68-71(nex-agi/nex-n2-prodeleted), defaults:212(openai) /:214(anthropic) /:216(gemini), env-chain resolverselectModelCandidates:202-210,openAiCompatibleChat:400.scripts/nodeagent-live-provider-smoke.ts:34z-ai/glm-5.2:35moonshotai/kimi-k2.7-code:36cohere/north-mini-code:free:37nvidia/nemotron-3-ultra-550b-a55b:38nex-agi/nex-n2-pro.env.example:29andREADME.md:172-176:177gpt-5.5gpt-6.1-sol:179claude-3-5-haiku-latestclaude-haiku-5-5(retired 2026-02-19):181gemini-2.0-flashgemini-3.8-flash(shut down 2026-06-01)nodekit.yaml:57E / R sites (5: migrate 1, freeze 3, defer 1)
smoke.ts:173env-chain resolverNODEAGENT_OPENROUTER_MODEL_PRIORITY ?? NODEAGENT_OPENROUTER_MODEL ?? constunchangedsmoke.ts:248openAiCompatibleChatsrc/features/node-agent/providers/piAiAdapter.ts:34src/features/node-agent/protocol/model.ts:60package.json:100pi-ai pinClass N (doc 4, example 2, other 2, test-assertion 1): docs loop =
README.mddefault-priority sentence and.env.examplechain only; no other doc/evidence file names a moved id (evidence trees are hash-bound, untouched).Breaking-parameter fixes
temperature: 0removed from OpenAI-compatible, Anthropic and Gemini bodies (400 on Haiku 5.5, removed on GPT-6, deprecated on Gemini 3.6+).SMOKE_MAX_TOKENS) at all three sites (thinking counts toward the cap).max_completion_tokens+reasoning_effort: "low", notemperature, no tools. OpenRouter branch keepsmax_tokens, notemperature.type === "text"; astop_reason: "refusal"response now throwsprovider_refusalbefore content is read (migration guide Haiku 5.5 checklist, [BLOCKS]).Test proof
tests/liveProviderSmokeRequestShape.test.ts(20 cases: 6 request-shape incl. an Anthropicstop_reason: "refusal"regression, 14 receipt; the request-shape cases also assert the SPEC §1.3 forbidden keys absent: GeminithinkingConfig.thinkingBudget/thinking_budget/candidateCount/candidate_count, OpenAIlogprobs/top_logprobs. Each assertion observed to fail against a mutant that adds the key to the builder, mutant reverted) drives the script's owncallProvider/selectModelCandidates/probeLlm/gitIdentity/receiptIdentitywith a fakefetch. The git-identity cases run against a throwaway temp git repo, so the suite passes on a dirty or untracked working tree (verified with an untracked file in the checkout).Fail-before evidence, re-measured at head
f7963e2in an isolated worktree: all 14 per-site mutants are killed bytests/liveProviderSmokeRequestShape.test.ts. Mutants: each of the three literals reverted (gpt-5.5,claude-3-5-haiku-latest,gemini-2.0-flash);nex-agi/nex-n2-prore-added;temperature: 0re-added to the Anthropic, OpenAI-compatible and Gemini bodies; cap set back to 16; OpenAImax_tokensinstead ofmax_completion_tokens;toolsadded;thinking: {type: "disabled"}; an assistant prefill; text no longer filtered by block type; refusal check removed. Swapping inorigin/main's whole script gives 20 of 20 failing, mostly becauseorigin/mainhas no exports.Round-5 fixes, each observed to fail without its fix: (a)
gitDirtycase: the probe was*.tmp, ignored by.gitignore:44, so git never saw it and the case passed only on an already-dirty tree. The case now runs against an isolated temp git repo (makeRepo(), probe fileprobe.txt, no dependence on the working tree), asserting a clean tree, then thatdocs/evaloutput is ignored, then that an untracked root file givesgitDirty: truefrom both the repo root anddocs/. With the:(top)anchors replaced by.the case fails withexpected [ true, false ] to deeply equal [ true, true ]. (b) A successful reply costing0.11againstmaxUsd 0.10now setsstoppedBy: "maxUsd"; (c) the receiptcommandquotes whitespace args. Against the previous head's script (3044810), (b) and (c) fail (2 failed | 13 passed).Round-6 fixes: (a)
budget.stoppedBynow compares the known (non-null) spend tomaxUsdeven when another row reported no cost; a retry after an HTTP 503 followed by a0.11reply stops the run (spentUsdstaysnull,stoppedBy: "maxUsd"; the test fails when the oldspentUsd !== nullguard is restored). (b)gitIdentity(cwd)replaces the git calls insidereceiptIdentity; thegitDirtycase uses a temp repo, sonpm test/npm run checkno longer depend on the state of the working tree. (c)main()capturestestedCommit,gitDirty,constantSha256before the first paid dispatch, so a non-git directory fails before any spend and a checkout change mid-run cannot misattribute the run. (d) The receiptcommandis POSIX shell-quoted (;, empty and'arguments replay). (e) A missing provider key is detected before a request is counted:requests: 0, no row.Round-7 fixes: (a)
gitIdentitypasses--untracked-files=all; the test setsstatus.showUntrackedFiles=noin the fixture and still expectsgitDirty: true(fails without the flag). (b) The gitIdentity test now assertsgitDirty === falsefrom the repo root and fromdocs/afterdocs/eval/receipt.jsonis written and before the root probe (fails with a cwd-relative:(exclude)docs/eval), and assertsconstantSha256equals the sha256 of the committed blob. (c) The non-git test is retitledgitIdentity throws outside a git checkout; the call-before-dispatch ordering inmain()is not covered by a test and is not claimed to be.Check output (
npm run prepush, the repo's declared check)Branch rebased onto
origin/maina918e85 (0 behind, 12 ahead). Re-run 2026-10-15 on a clean tree at headf7963e2(git statusclean before the run; no provider keys in the environment).npm run prepushexit 0: 12 test files, 137/137 tests, live smoke SKIP, build ok, 0 vulnerabilities. The request-shape file alone (npx vitest run tests/liveProviderSmokeRequestShape.test.ts): 20/20. The log is local to the author's machine and not part of the PR, so the output below is the evidence (counts quoted for earlier heads no longer apply): Re-run again 2026-10-16 at the same headf7963e2(clean tree, provider keys unset; one earlier attempt under machine load hit 2 timeout failures in the cancellation tests and passed 137/137 on re-run):npm run prepushexit 0, 12 files, 137/137, live smoke SKIP, 0 vulnerabilities; request-shape file 20/20; the fourdocs/eval/*.jsonfiles restored.prepushrewrites fourdocs/eval/*.jsonsmoke outputs; those were restored (not committed). No paid provider calls were made.Notes
D2 scope: settled by D2a (SPEC-v3.md:6). D2 (SPEC-v3.md:5) chooses the target only for OpenRouter-routed roles that this refresh moves under A.0 rule 1; D2a states that frozen paid OpenRouter routes with no vendor-named successor, such as
z-ai/glm-5.2, stay frozen. So the frozen paid chain (z-ai/glm-5.2first, atscripts/nodeagent-live-provider-smoke.ts:68, mirrored in.env.example:29andREADME.md:172-174) is unchanged and only the deadnex-agi/nex-n2-proentry is deleted. The sibling PRs apply the same rule (NodeRoom#261 and NodeSlide#203 keepz-ai/glm-5.2; NodeBenchAI#662 and parity-studio#101 keepkimi-k2.6). No decision is pending here; a broader "every OpenRouter role goes:free" reading would be a follow-up PR per D2a..env.example:29is uncommented, so anyone who copied it keeps the old list includingnex-agi/nex-n2-pro; they should re-copy or remove that entry.needs-live-eval(block, not a note): merge waits for the three native keyed receipts below, taken atf7963e2or a later commit (rule 3:testedCommitmust be that commit or a descendant). The barenpm run nodeagent:live-provider:smokeis NOT sufficient:selectProviderpicks the first keyed provider (openrouter, openai, anthropic, gemini), so on a machine withOPENROUTER_API_KEYit only tests the frozen chain, and the npm script hard-codes one--json-out. Required runs (all three before the label is removed; run each separately, attach each output):npx tsx scripts/nodeagent-live-provider-smoke.ts --provider openai --model gpt-6.1-sol --json-out docs/eval/nodeagent-live-provider-smoke-openai.json(gpt-6.1-sol)npx tsx scripts/nodeagent-live-provider-smoke.ts --provider anthropic --model claude-haiku-5-5 --json-out docs/eval/nodeagent-live-provider-smoke-anthropic.json(claude-haiku-5-5)npx tsx scripts/nodeagent-live-provider-smoke.ts --provider gemini --model gemini-3.8-flash --json-out docs/eval/nodeagent-live-provider-smoke-gemini.json(gemini-3.8-flash)OpenRouter role (the
nex-agi/nex-n2-prodeletion and the frozen chain): live-unverified (no key, Homen 2026-10-16). Per decision D2 (SPEC-v3.md:5) no working OpenRouter key exists (the local one returns 401 "User not found"), the role carries this marking, and merge is allowed with it; the OpenRouter run is not a merge condition. Public-list evidence instead, fetched without a key at2026-10-16T10:00:05Z(GET https://openrouter.ai/api/v1/models, 465 rows, snapshot_scratch/or_models_2026-10-16.jsonin the model-defaults workspace, not in this repo):nex-agi/nex-n2-prois absent;z-ai/glm-5.2,moonshotai/kimi-k2.7-code,cohere/north-mini-code:freeandnvidia/nemotron-3-ultra-550b-a55bare all listed with noexpiration_date, so the frozen chain stays as is. Optional, only when a working key exists:npx tsx scripts/nodeagent-live-provider-smoke.ts --provider openrouter --json-out docs/eval/nodeagent-live-provider-smoke-openrouter.json.--modelis pinned on the three direct-provider runs becauseloadEnvFilesloadsNODEAGENT_LIVE_MODELandNODEAGENT_<PROVIDER>_MODELfrom.env.localandselectModelCandidateshonours them, so a stale override there would silently test another model (the receipt would be rejected under rule 3, but the paid run is wasted). The optional OpenRouter run is unpinned (no--model), andselectModelCandidates(scripts/nodeagent-live-provider-smoke.ts:205) checksNODEAGENT_LIVE_MODELfirst, thenNODEAGENT_OPENROUTER_MODEL_PRIORITY, thenNODEAGENT_OPENROUTER_MODEL; any of the three replaces the frozen chain, andloadEnvFilesdoes not override variables already in the process env. Before that run (optional, see above), unset all three in the shell (unset NODEAGENT_LIVE_MODEL NODEAGENT_OPENROUTER_MODEL_PRIORITY NODEAGENT_OPENROUTER_MODEL) and remove them from.env.localand.env; after the run, check that the receipt'scandidateIdsequals the four-entry frozen chain (z-ai/glm-5.2,moonshotai/kimi-k2.7-code,cohere/north-mini-code:free,nvidia/nemotron-3-ultra-550b-a55b), otherwise the receipt is rejected under rule 3 and the run is wasted. Risk the keyed receipt may surface: Haiku 5.5 (adaptive thinking, medium effort by default) and gemini-3.8-flash (default thinking) count thinking tokens toward the spec's 256-token cap, so a reply could end inmax_tokenswith no text and the smoke would fail closed (expected NODEAGENT_LIVE_OK); the migration guide's lever then is effortlowor a larger cap.Estimated cost: a few cents total for the three required runs (one ~30-token reply each). The optional OpenRouter run adds up to 4 attempts. Not run here (no paid calls).
Receipt schema (§A.0 rule 3) — fixed in this PR. The smoke report now carries
command(POSIX shell-quoted, so semicolons, empty and spaced arguments replay),candidateIds,echoedModels,rows[{requestedModel, resolvedModel, echoedModel, usage, costUsd, fallbackUsed}],testedCommit(git rev-parse HEAD),gitDirty(top-anchored pathspecs and an explicit--untracked-files=all, so it is the same from any working directory and independent of the user'sstatus.showUntrackedFiles; ignoresdocs/eval, the evidence directory),constantSha256(this script as committed attestedCommit, read once from a singlegit rev-parse HEAD; recompute withgit show <testedCommit>:scripts/nodeagent-live-provider-smoke.ts | sha256sum),budget{maxUsd, estimateUsd, spentUsd|null, requests, stoppedBy}andenvironment.echoedModelis the responsemodel(Anthropic/OpenAI/OpenRouter) ormodelVersion(Gemini);fallbackUsedis true when the OpenRouter chain moves past index 0; a budget stop (including a reply whose reported cost exceedsmaxUsd, even when an earlier attempt reported no cost) setsok: false. The git identity is read before the first dispatch. No model site, precedence or frozen site changed. Runtime behaviour does change in the smoke: the OpenRouter chain stops when committed spend (the reported cost, or the$0.01estimate only where a row reports none) plus one more$0.01estimate would exceedmaxUsd $0.10; the cap is in dollars, not attempts, so cheap failed replies that report a small cost can run more than 10 attempts, and a budget stop fails the run (ok: false, exit 1). Fake-fetch tests assert a differing echoed model is recorded. Limits:costUsdis only filled where the provider reports it (OpenRouterusage.cost), otherwisenull, and thenbudget.spentUsdisnull(unknown), never0. Before each dispatch the smoke requires committed spend (reported cost, or the$0.01estimate for an unreported row) plus one more estimate to stay withinmaxUsd, so the cap holds for direct providers too.estimateUsdis a flat$0.01per request, not a price table: it bounds the built-in and frozen ids (gpt-6.1-sol at 256 output tokens is about$0.0026), but an operator-suppliedNODEAGENT_OPENROUTER_MODEL_PRIORITYof pricier ids can overshootmaxUsdby up to one request; such a run endsstoppedBy: "maxUsd",ok: false, so it is flagged, not silent. A refused Anthropic row recordsechoedModelandusagebefore the run fails. Merge waits for the three native keyed runs (not run here, no paid calls; OpenRouter is live-unverified per D2); a receipt with a fallback row,gitDirty, orstoppedByis rejected and re-run.Resolved by D1 (Homen 2026-10-16):
--model/NODEAGENT_LIVE_MODEL/NODEAGENT_*_MODELstay pass-through; every built-in default is a §1.2 id or a known frozen chain id.Round-8 fixes (
b2d3fd3): smoke budget wording no longer claims a hard bound; receiptcommandrecordsnpx tsx ...(replays verbatim); the git-spawning tests have a 30 s timeout (assertions unchanged); the not-a-repo test asserts/not a git repository/i.Round-10 fix (
429ba6c):gitIdentityreadgit rev-parse HEADand then hashedgit show HEAD:<script>in a separate call, so a checkout between the two would give a receipt naming commit A with commit B's hash. It now reads HEAD once and hashes<testedCommit>:<script>. A request-shape test moves HEAD between the two calls (via a pass-throughnode:child_processhook) and asserts the hash is the captured commit's blob; reverting the fix toHEAD:fails it. The PR-body changes in this round:--modelon the keyed commands, the stale-override and thinking-token notes, and the mixed-list sentence. In the verifyingprepushrun, the first attempt had one 5000 ms timeout in the untouchedtests/nodeAgentChatAdapter.test.ts(a load flake, 136/137); the immediate re-run was 137/137, exit 0.Round-11 fixes (
f7963e2, after rebase onto a918e85): the request-shape test now asserts the SPEC §1.3 forbidden keys absent (defect: a mutant addingthinkingConfig: { thinkingBudget: 0 }, candidateCount: 1to the GeminigenerationConfigpassed 20/20; it now fails, as does alogprobs: truemutant on the OpenAI body). The keyed receipts must be taken at headf7963e2or later sotestedCommitis an ancestor of the PR head (rule 3); a receipt taken at the pre-rebase8b12beais not.Round-9 fixes (
2a0faa8): the end-of-request budget check now counts a row with no reported cost at the$0.01estimate, the same accounting as the pre-dispatch reservation. Before, a 503 with no cost followed by a$0.095reply (estimate total$0.105vsmaxUsd $0.10) endedok: true, stoppedBy: null; now it endsstoppedBy: "maxUsd"whilespentUsdstaysnull(a row is unknown). The new fake-fetch case fails against the previous script and passes now.Spec: model-defaults SPEC Part A §A.2 (NodeAgent)
🤖 Generated with Claude Code