rovecode 0.4.0-beta.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -69
- package/THIRD_PARTY_NOTICES.md +0 -44
- package/bin/rovecode.ts +21 -0
- package/package.json +16 -37
- package/src/account/keys.ts +97 -0
- package/src/account/login.ts +158 -0
- package/src/account/provision.ts +47 -0
- package/src/account/store.ts +63 -0
- package/src/acp/server.ts +373 -0
- package/src/cli/account-cmd.ts +116 -0
- package/src/cli/connect.ts +244 -0
- package/src/cli/context-cmd.ts +199 -0
- package/src/cli/dispatch.ts +109 -0
- package/src/cli/doctor.ts +324 -0
- package/src/cli/export.ts +278 -0
- package/src/cli/help.ts +240 -0
- package/src/cli/is-tui-invocation.ts +8 -0
- package/src/cli/main.ts +599 -0
- package/src/cli/market-cmd.ts +658 -0
- package/src/cli/mcp-market-cmd.ts +299 -0
- package/src/cli/output.ts +382 -0
- package/src/cli/repl.ts +172 -0
- package/src/cli/resume.ts +32 -0
- package/src/cli/run-limits.ts +78 -0
- package/src/cli/runtime.ts +792 -0
- package/src/cli/setup.ts +187 -0
- package/src/cli/update-cmd.ts +78 -0
- package/src/cli/workflow-cmd.ts +100 -0
- package/src/coding/checkpoints.ts +270 -0
- package/src/coding/diff.ts +136 -0
- package/src/coding/files.ts +339 -0
- package/src/coding/hashline.ts +319 -0
- package/src/coding/lsp.ts +406 -0
- package/src/coding/repomap-cache.ts +99 -0
- package/src/coding/repomap-files.ts +110 -0
- package/src/coding/repomap.ts +392 -0
- package/src/core/compaction.ts +399 -0
- package/src/core/config.ts +289 -0
- package/src/core/context-report.ts +228 -0
- package/src/core/context.ts +60 -0
- package/src/core/count-remote.ts +107 -0
- package/src/core/execpolicy-rules.ts +196 -0
- package/src/core/execpolicy.ts +385 -0
- package/src/core/executor.ts +397 -0
- package/src/core/guardrails.ts +400 -0
- package/src/core/hooks.ts +398 -0
- package/src/core/images.ts +230 -0
- package/src/core/intro.ts +236 -0
- package/src/core/loop.ts +621 -0
- package/src/core/modes.ts +372 -0
- package/src/core/orchestrator.ts +207 -0
- package/src/core/reflection.ts +165 -0
- package/src/core/sandbox-config.ts +167 -0
- package/src/core/session-images.ts +73 -0
- package/src/core/session.ts +398 -0
- package/src/core/settings.ts +98 -0
- package/src/core/stuck-detector.ts +273 -0
- package/src/core/tasks.ts +374 -0
- package/src/core/token-scale.ts +108 -0
- package/src/core/tool-output-budget.ts +166 -0
- package/src/core/tools.ts +288 -0
- package/src/core/types.ts +330 -0
- package/src/core/update-check.ts +171 -0
- package/src/core/update.ts +158 -0
- package/src/core/usage.ts +204 -0
- package/src/core/validate.ts +121 -0
- package/src/core/verify-gate.ts +159 -0
- package/src/core/verify.ts +237 -0
- package/src/core/voice.ts +158 -0
- package/src/core/win-job.ts +183 -0
- package/src/design/audit.ts +797 -0
- package/src/design/direction.ts +190 -0
- package/src/design/rules.ts +157 -0
- package/src/eval/bench.ts +150 -0
- package/src/eval/gauntlet-runner.ts +218 -0
- package/src/eval/gauntlet.ts +226 -0
- package/src/eval/grader.ts +186 -0
- package/src/eval/record.ts +202 -0
- package/src/eval/redact.ts +141 -0
- package/src/eval/replay.ts +147 -0
- package/src/eval/trajectory.ts +373 -0
- package/src/index.ts +17 -0
- package/src/market/catalogs/mcp-docs.json +111 -0
- package/src/market/catalogs/plugins.json +111 -0
- package/src/market/catalogs/skills.json +478 -0
- package/src/market/clone.ts +72 -0
- package/src/market/context-cost.ts +121 -0
- package/src/market/digest.ts +106 -0
- package/src/market/index.ts +22 -0
- package/src/market/install.ts +578 -0
- package/src/market/manifest.ts +187 -0
- package/src/market/prereq.ts +145 -0
- package/src/market/registry.ts +363 -0
- package/src/market/resolve.ts +111 -0
- package/src/market/types.ts +236 -0
- package/src/market/validate.ts +227 -0
- package/src/mcp/client.ts +431 -0
- package/src/mcp/config.ts +239 -0
- package/src/mcp/local-package.ts +211 -0
- package/src/mcp/market-catalog.ts +84 -0
- package/src/mcp/market-install.ts +289 -0
- package/src/mcp/market.ts +0 -0
- package/src/mcp/tools.ts +131 -0
- package/src/mcp/trust.ts +49 -0
- package/src/memory/blocks.ts +175 -0
- package/src/memory/recall.ts +355 -0
- package/src/memory/store.ts +105 -0
- package/src/memory/tools.ts +99 -0
- package/src/plugins/cli.ts +123 -0
- package/src/plugins/discover.ts +108 -0
- package/src/plugins/index.ts +50 -0
- package/src/plugins/init.ts +140 -0
- package/src/plugins/install.ts +184 -0
- package/src/plugins/load.ts +149 -0
- package/src/plugins/manifest.ts +106 -0
- package/src/plugins/state.ts +83 -0
- package/src/providers/auth.ts +293 -0
- package/src/providers/cache.ts +223 -0
- package/src/providers/catalog-local.ts +160 -0
- package/src/providers/catalog.ts +408 -0
- package/src/providers/middleware-context.ts +86 -0
- package/src/providers/middleware.ts +373 -0
- package/src/providers/profile-glm53.ts +111 -0
- package/src/providers/profile-sonnet5-persona.ts +65 -0
- package/src/providers/profile-sonnet5-voice.ts +23 -0
- package/src/providers/profiles.ts +156 -0
- package/src/providers/provider-config.ts +311 -0
- package/src/providers/registry.ts +302 -0
- package/src/providers/response-validation.ts +80 -0
- package/src/providers/retry.ts +234 -0
- package/src/providers/router.ts +294 -0
- package/src/providers/sse.ts +26 -0
- package/src/providers/stream-errors.ts +117 -0
- package/src/providers/stream.ts +569 -0
- package/src/providers/thinking.ts +189 -0
- package/src/providers/wire-messages.ts +129 -0
- package/src/sdk/client.ts +225 -0
- package/src/sdk/index.ts +3 -0
- package/src/server/dashboard.ts +144 -0
- package/src/server/http.ts +343 -0
- package/src/server/openapi.ts +246 -0
- package/src/sextant/card-hits.ts +102 -0
- package/src/sextant/card-keys.ts +55 -0
- package/src/sextant/context-source.ts +157 -0
- package/src/sextant/draw-agents.ts +273 -0
- package/src/sextant/draw-code.ts +388 -0
- package/src/sextant/draw-context.ts +222 -0
- package/src/sextant/draw-frame.ts +164 -0
- package/src/sextant/draw-market.ts +573 -0
- package/src/sextant/draw-messages.ts +386 -0
- package/src/sextant/draw-pet.ts +230 -0
- package/src/sextant/draw-plan.ts +159 -0
- package/src/sextant/draw-tabs.ts +85 -0
- package/src/sextant/draw-util.ts +65 -0
- package/src/sextant/engine.ts +230 -0
- package/src/sextant/frame-hits.ts +25 -0
- package/src/sextant/frame.ts +101 -0
- package/src/sextant/git-status.ts +197 -0
- package/src/sextant/grid.ts +59 -0
- package/src/sextant/input.ts +119 -0
- package/src/sextant/keys.ts +488 -0
- package/src/sextant/layout.ts +86 -0
- package/src/sextant/local-commands.ts +156 -0
- package/src/sextant/market-source.ts +287 -0
- package/src/sextant/mentions.ts +141 -0
- package/src/sextant/message-hits.ts +26 -0
- package/src/sextant/model.ts +387 -0
- package/src/sextant/overlays.ts +451 -0
- package/src/sextant/panel-hits.ts +38 -0
- package/src/sextant/pet.ts +399 -0
- package/src/sextant/screen.ts +324 -0
- package/src/sextant/scroll-hits.ts +66 -0
- package/src/sextant/scrollbar.ts +82 -0
- package/src/sextant/selection.ts +123 -0
- package/src/sextant/sextant-bridge.ts +174 -0
- package/src/sextant/sextant-cards.ts +142 -0
- package/src/sextant/sextant-diff-base.ts +63 -0
- package/src/sextant/sextant-files.ts +154 -0
- package/src/sextant/sextant-frame-loop.ts +314 -0
- package/src/sextant/sextant-renderer.ts +478 -0
- package/src/sextant/sextant-repo.ts +131 -0
- package/src/sextant/theme.ts +66 -0
- package/src/sextant/tool-rows.ts +189 -0
- package/src/sextant/types.ts +473 -0
- package/src/skills/index.ts +306 -0
- package/src/skills/tools.ts +69 -0
- package/src/skills/versioned.ts +227 -0
- package/src/telemetry/otel.ts +353 -0
- package/src/telemetry/otlp.ts +68 -0
- package/src/tools/ask-user.ts +156 -0
- package/src/tools/design.ts +151 -0
- package/src/tools/evalcell.ts +338 -0
- package/src/tools/html-text.ts +139 -0
- package/src/tools/provider.ts +149 -0
- package/src/tools/task.ts +216 -0
- package/src/tools/todo.ts +320 -0
- package/src/tools/webfetch.ts +331 -0
- package/src/tui/app.ts +608 -0
- package/src/tui/attach.ts +127 -0
- package/src/tui/checkpoints-cmd.ts +70 -0
- package/src/tui/clipboard-image.ts +81 -0
- package/src/tui/commands.ts +277 -0
- package/src/tui/cost.ts +108 -0
- package/src/tui/info-cmd.ts +144 -0
- package/src/tui/mcp-cmd.ts +128 -0
- package/src/tui/modes-cmd.ts +45 -0
- package/src/tui/overlays.ts +97 -0
- package/src/tui/pi-renderer.ts +424 -0
- package/src/tui/providers-cmd.ts +366 -0
- package/src/tui/renderer.ts +101 -0
- package/src/tui/replay-marker.ts +29 -0
- package/src/tui/session-cmd.ts +146 -0
- package/src/tui/sextant-attach.ts +68 -0
- package/src/tui/sextant-io.ts +184 -0
- package/src/tui/sextant-smoke.ts +110 -0
- package/src/tui/smoke.ts +72 -0
- package/src/tui/theme.ts +59 -0
- package/src/tui/todo-label.ts +7 -0
- package/src/workflow/engine.ts +266 -0
- package/tsconfig.json +30 -0
- package/vendor/pi-tui/LICENSE +21 -0
- package/vendor/pi-tui/PATCHES.md +12 -0
- package/vendor/pi-tui/PROVENANCE.md +12 -0
- package/vendor/pi-tui/README.upstream.md +854 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +158 -0
- package/vendor/pi-tui/src/autocomplete.ts +827 -0
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +52 -0
- package/vendor/pi-tui/src/components/box.ts +138 -0
- package/vendor/pi-tui/src/components/cancellable-loader.ts +41 -0
- package/vendor/pi-tui/src/components/editor.ts +2364 -0
- package/vendor/pi-tui/src/components/h-stack.ts +45 -0
- package/vendor/pi-tui/src/components/image.ts +128 -0
- package/vendor/pi-tui/src/components/input.ts +448 -0
- package/vendor/pi-tui/src/components/loader.ts +93 -0
- package/vendor/pi-tui/src/components/markdown.ts +1016 -0
- package/vendor/pi-tui/src/components/scroll-view.ts +217 -0
- package/vendor/pi-tui/src/components/select-list.ts +230 -0
- package/vendor/pi-tui/src/components/settings-list.ts +277 -0
- package/vendor/pi-tui/src/components/spacer.ts +29 -0
- package/vendor/pi-tui/src/components/stack.ts +155 -0
- package/vendor/pi-tui/src/components/text.ts +108 -0
- package/vendor/pi-tui/src/components/truncated-text.ts +66 -0
- package/vendor/pi-tui/src/components/v-stack.ts +34 -0
- package/vendor/pi-tui/src/editor-component.ts +75 -0
- package/vendor/pi-tui/src/fuzzy.ts +138 -0
- package/vendor/pi-tui/src/index.ts +149 -0
- package/vendor/pi-tui/src/keybindings.ts +321 -0
- package/vendor/pi-tui/src/keys.ts +1402 -0
- package/vendor/pi-tui/src/kill-ring.ts +47 -0
- package/vendor/pi-tui/src/latex.ts +1381 -0
- package/vendor/pi-tui/src/layout-node.ts +52 -0
- package/vendor/pi-tui/src/layout.ts +411 -0
- package/vendor/pi-tui/src/native-modifiers.ts +60 -0
- package/vendor/pi-tui/src/native-module-path.ts +32 -0
- package/vendor/pi-tui/src/stdin-buffer.ts +445 -0
- package/vendor/pi-tui/src/terminal-colors.ts +74 -0
- package/vendor/pi-tui/src/terminal-image.ts +701 -0
- package/vendor/pi-tui/src/terminal.ts +554 -0
- package/vendor/pi-tui/src/tui-alt-screen.ts +1379 -0
- package/vendor/pi-tui/src/tui-main-screen.ts +655 -0
- package/vendor/pi-tui/src/tui.ts +1264 -0
- package/vendor/pi-tui/src/undo-stack.ts +29 -0
- package/vendor/pi-tui/src/utils.ts +1327 -0
- package/vendor/pi-tui/src/word-navigation.ts +118 -0
- package/vendor/pi-tui/test/test-themes.ts +39 -0
- package/vendor/pi-tui/test/virtual-terminal.ts +219 -0
- package/CHANGELOG.md +0 -512
- package/bin/rovecode.js +0 -24
- package/dist/cli/app-dybnr56b.js +0 -2
- package/dist/cli/ask-user-p8hq4xgj.js +0 -2
- package/dist/cli/auth-login-ewpgw5sm.js +0 -2
- package/dist/cli/auth-m8p9grty.js +0 -2
- package/dist/cli/bench-xv3ypwev.js +0 -9
- package/dist/cli/catalog-737wb2s0.js +0 -2
- package/dist/cli/cli-arhg40m0.js +0 -2
- package/dist/cli/client-cf2pxx8q.js +0 -2
- package/dist/cli/commands-3p7e4xxs.js +0 -2
- package/dist/cli/connect-3q93d7cb.js +0 -2
- package/dist/cli/context-cmd-eqxmxhzq.js +0 -2
- package/dist/cli/context-report-hbw9zfes.js +0 -2
- package/dist/cli/count-remote-mby98cd0.js +0 -2
- package/dist/cli/design-122y0axd.js +0 -2
- package/dist/cli/dispatch-b4egzvvh.js +0 -2
- package/dist/cli/doctor-x4jkv72e.js +0 -3
- package/dist/cli/executor-ftvg6tsy.js +0 -2
- package/dist/cli/export-pdgdhkch.js +0 -2
- package/dist/cli/files-cez9a96p.js +0 -2
- package/dist/cli/gauntlet-r3xxaszc.js +0 -2
- package/dist/cli/gauntlet-runner-r515m7kk.js +0 -10
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +0 -5
- package/dist/cli/gauntlet-wave4-acs9s60q.js +0 -14
- package/dist/cli/hashline-ewg5hbe3.js +0 -2
- package/dist/cli/http-n0kehsk8.js +0 -5
- package/dist/cli/index-z5qt1s76.js +0 -2
- package/dist/cli/install-80mp63kx.js +0 -2
- package/dist/cli/loop-12twjcat.js +0 -2
- package/dist/cli/main-01pv9206.js +0 -4
- package/dist/cli/main-0jys2ccn.js +0 -3
- package/dist/cli/main-1ztz6fkj.js +0 -10
- package/dist/cli/main-23q7cmww.js +0 -9
- package/dist/cli/main-2rzbexn2.js +0 -3
- package/dist/cli/main-2wyax8k9.js +0 -9
- package/dist/cli/main-2yeveeve.js +0 -6
- package/dist/cli/main-2z3dek0b.js +0 -3
- package/dist/cli/main-2zgsknth.js +0 -3
- package/dist/cli/main-45ejth3a.js +0 -4
- package/dist/cli/main-45rn3trk.js +0 -22
- package/dist/cli/main-4p4e2w7x.js +0 -4
- package/dist/cli/main-4y0tnfpa.js +0 -16
- package/dist/cli/main-5py0rkmc.js +0 -4
- package/dist/cli/main-6dtqmbt6.js +0 -7
- package/dist/cli/main-6h9x282m.js +0 -4
- package/dist/cli/main-6vjeds42.js +0 -3
- package/dist/cli/main-78gq4bt9.js +0 -6
- package/dist/cli/main-7jd5vh3x.js +0 -4
- package/dist/cli/main-7kt6r53y.js +0 -4
- package/dist/cli/main-8c1tbazx.js +0 -58
- package/dist/cli/main-9a9rnh47.js +0 -19
- package/dist/cli/main-9ht36z12.js +0 -3
- package/dist/cli/main-a2yfvcy9.js +0 -7
- package/dist/cli/main-a3f51n0x.js +0 -5
- package/dist/cli/main-b8zq261k.js +0 -3
- package/dist/cli/main-bxtvnf6d.js +0 -13
- package/dist/cli/main-edxc3yzt.js +0 -4
- package/dist/cli/main-evgz4mp5.js +0 -21
- package/dist/cli/main-f33fc5je.js +0 -9
- package/dist/cli/main-fvnpq46y.js +0 -12
- package/dist/cli/main-gbbty4d4.js +0 -3
- package/dist/cli/main-gth53dnt.js +0 -25
- package/dist/cli/main-hqbz10aw.js +0 -9
- package/dist/cli/main-hrrvcfan.js +0 -38
- package/dist/cli/main-hzwtsb2m.js +0 -5
- package/dist/cli/main-j7ttv0sd.js +0 -34
- package/dist/cli/main-jak598k9.js +0 -5
- package/dist/cli/main-kba6zeyd.js +0 -6
- package/dist/cli/main-kwwsz6rq.js +0 -3
- package/dist/cli/main-m8vm17zq.js +0 -3
- package/dist/cli/main-mg4f96e1.js +0 -3
- package/dist/cli/main-mg9b20ac.js +0 -18
- package/dist/cli/main-mgb9ccnx.js +0 -3
- package/dist/cli/main-mjt2p7aj.js +0 -3
- package/dist/cli/main-n6qrdbmy.js +0 -3
- package/dist/cli/main-na7wse0x.js +0 -5
- package/dist/cli/main-nqveez48.js +0 -4
- package/dist/cli/main-ntqef02r.js +0 -10
- package/dist/cli/main-nvc3yjay.js +0 -136
- package/dist/cli/main-p0cfn6nr.js +0 -16
- package/dist/cli/main-qj2djy17.js +0 -19
- package/dist/cli/main-qsevpgsv.js +0 -3
- package/dist/cli/main-qvarybsp.js +0 -3
- package/dist/cli/main-rebtt91r.js +0 -5
- package/dist/cli/main-rpg7h8mb.js +0 -3
- package/dist/cli/main-rsy72qmw.js +0 -15
- package/dist/cli/main-rvetps99.js +0 -18
- package/dist/cli/main-s4bb0jav.js +0 -3
- package/dist/cli/main-s9v8k74e.js +0 -3
- package/dist/cli/main-tjvwmscs.js +0 -3
- package/dist/cli/main-tkgarpjj.js +0 -4
- package/dist/cli/main-v8y60bb2.js +0 -3
- package/dist/cli/main-vhrrq337.js +0 -3
- package/dist/cli/main-vp2dfb7s.js +0 -4
- package/dist/cli/main-vqbr22sz.js +0 -8
- package/dist/cli/main-vxnwe5xx.js +0 -18
- package/dist/cli/main-wgph00xf.js +0 -5
- package/dist/cli/main-wk2csfnj.js +0 -5
- package/dist/cli/main-wm997zjx.js +0 -3
- package/dist/cli/main-wpkyraxh.js +0 -3
- package/dist/cli/main-wqt32p5x.js +0 -4
- package/dist/cli/main-x9ct6y1a.js +0 -3
- package/dist/cli/main-xfekqh9m.js +0 -7
- package/dist/cli/main-xt9zc3n6.js +0 -7
- package/dist/cli/main-xx2z3zh5.js +0 -4
- package/dist/cli/main-y5c82rxr.js +0 -3
- package/dist/cli/main-yrjt2sqt.js +0 -14
- package/dist/cli/main-ys6zj3yr.js +0 -3
- package/dist/cli/main-ywbxshqc.js +0 -8
- package/dist/cli/main-z13755t8.js +0 -25
- package/dist/cli/main-zc7pyrbj.js +0 -4
- package/dist/cli/main.js +0 -279
- package/dist/cli/market-cmd-bm5xvn9f.js +0 -5
- package/dist/cli/mcp-login-bthtfpt7.js +0 -2
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +0 -2
- package/dist/cli/notify-54v5z9dz.js +0 -2
- package/dist/cli/oauth-g5gme95c.js +0 -2
- package/dist/cli/output-satndjap.js +0 -16
- package/dist/cli/profiles-sfhpbq3m.js +0 -2
- package/dist/cli/provider-config-hv3xtdt4.js +0 -2
- package/dist/cli/provider-kwzq6g84.js +0 -2
- package/dist/cli/registry-fh0hdnyn.js +0 -2
- package/dist/cli/registry-y1y8e94r.js +0 -2
- package/dist/cli/repl-t4z03mqq.js +0 -11
- package/dist/cli/resume-fqt4chg8.js +0 -2
- package/dist/cli/run-flags-rysbag9t.js +0 -2
- package/dist/cli/runtime-j19fjbsa.js +0 -2
- package/dist/cli/sandbox-config-g4qxd7y5.js +0 -2
- package/dist/cli/server-r0b6bksk.js +0 -5
- package/dist/cli/session-arg-txmn5g4x.js +0 -2
- package/dist/cli/session-ed250d9j.js +0 -2
- package/dist/cli/sessions-cmd-adw7svfn.js +0 -7
- package/dist/cli/settings-y9rzcqx8.js +0 -2
- package/dist/cli/setup-jmbr11j0.js +0 -2
- package/dist/cli/sextant-smoke-tcth0vea.js +0 -5
- package/dist/cli/skills-cmd-zbdy99v6.js +0 -2
- package/dist/cli/smoke-1bg937kx.js +0 -8
- package/dist/cli/start-chat-p01cdks3.js +0 -12
- package/dist/cli/stream-4wmyaypz.js +0 -2
- package/dist/cli/task-eg4s093s.js +0 -2
- package/dist/cli/tasks-12v9rr9k.js +0 -2
- package/dist/cli/thinking-a5ngvqyh.js +0 -2
- package/dist/cli/todo-1wxpcecx.js +0 -2
- package/dist/cli/tools-2ftsya7w.js +0 -2
- package/dist/cli/tools-x1tj4fxm.js +0 -2
- package/dist/cli/trust-cmd-hccxehzb.js +0 -2
- package/dist/cli/update-check-ygt3vd7m.js +0 -2
- package/dist/cli/update-cmd-v23qhr8c.js +0 -2
- package/dist/cli/voice-g1gtck92.js +0 -2
- package/dist/cli/webfetch-0nnrjgb5.js +0 -2
- package/dist/cli/websearch-f0vr2p7d.js +0 -2
- package/dist/cli/workspace-9rq1w4ta.js +0 -2
- package/dist/lib/index.js +0 -62
- package/dist/lib/models-index.json +0 -1
- package/dist/lib/plugins.js +0 -6
- package/dist/lib/providers.js +0 -17
- package/dist/lib/public-api.js +0 -20
- package/dist/rovecode.exe +0 -4
- /package/{dist/cli → src/providers}/models-index.json +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/** Anthropic prompt-cache boundary placement — pure transform for the /messages request body.
|
|
2
|
+
*
|
|
3
|
+
* Prompt caching is a PREFIX match: the API render order is tools → system → messages, and a
|
|
4
|
+
* `cache_control: {type:"ephemeral"}` marker on a content block caches everything from the start
|
|
5
|
+
* of the request up to and including that block. Any byte change before a marker invalidates it.
|
|
6
|
+
* Therefore boundaries only pay off on STABLE prefixes — a boundary after content that changes
|
|
7
|
+
* every turn is a wasted breakpoint (Anthropic allows at most 4 per request).
|
|
8
|
+
*
|
|
9
|
+
* Pattern reference (hermes-agent snapshot, research/source_snapshots/hermes-agent):
|
|
10
|
+
* - agent/prompt_caching.py:1-8 — "The default layout uses 4 cache_control breakpoints: the static
|
|
11
|
+
* system prefix, the end of the system prompt, and the last 2 non-system messages."
|
|
12
|
+
* - agent/prompt_caching.py:157-162 (_build_marker) — the marker is `{"type": "ephemeral"}`.
|
|
13
|
+
* - agent/prompt_caching.py:113-121 (_apply_cache_marker) — string content is converted to a
|
|
14
|
+
* single text block carrying the marker; list content gets the marker on its LAST block.
|
|
15
|
+
* - agent/prompt_caching.py:99-103, 331-333 — volatile tails ride UNMARKED ("a changed ticket ID
|
|
16
|
+
* or timestamp no longer invalidates the whole skill body"), and empty text blocks are never
|
|
17
|
+
* emitted (HTTP 400 on native Anthropic).
|
|
18
|
+
* - agent/prompt_caching.py:567-573 (apply_anthropic_cache_control) — idempotence contract:
|
|
19
|
+
* re-application "can never accumulate past 4 markers".
|
|
20
|
+
* - agent/system_prompt.py:911-924 and agent/turn_context.py:214-217 — the stability principle:
|
|
21
|
+
* volatile per-turn facts are kept OUT of / AFTER the cached band so the prefix stays
|
|
22
|
+
* byte-stable turn-over-turn.
|
|
23
|
+
*
|
|
24
|
+
* Placement policy implemented here (deliberately simpler than Hermes' 4-marker layout):
|
|
25
|
+
* (1) SYSTEM boundary — `system: string` is converted to
|
|
26
|
+
* `[{type:"text", text, cache_control:{type:"ephemeral"}}]` when it is ≥ minChunkChars.
|
|
27
|
+
* A system-block array already provided by the caller gets the marker on its last block
|
|
28
|
+
* (unless any block is already marked). Because caching is prefix-based, this single marker
|
|
29
|
+
* also covers the `tools` array rendered before `system` — no separate tools marker needed.
|
|
30
|
+
* (2) CONVERSATION-PREFIX boundary — the marker goes on the LAST content block of the most
|
|
31
|
+
* recent message that has ≥ 2 messages after it (index length-3): an agent turn appends
|
|
32
|
+
* the trailing exchange (assistant tool_use + tool_result, or user + assistant), so the
|
|
33
|
+
* tail churns every turn while the prefix up to there is CANONICALLY stable — not
|
|
34
|
+
* byte-identical. Two rewrites make consecutive requests differ in raw bytes: the marker
|
|
35
|
+
* moves forward each turn (dropped from the old anchor, added at the new one), and the
|
|
36
|
+
* marked message's string content is rewritten to [{type:"text",...}] by
|
|
37
|
+
* withMarkerOnLastBlock below. Anthropic's cache key normalizes exactly those two
|
|
38
|
+
* (cache_control is placement metadata, and string content is equivalent to a single
|
|
39
|
+
* text block), so the previous request's prefix still cache-hits. Pinned by the
|
|
40
|
+
* consecutive-turn stability test in test/unit/cache.test.ts.
|
|
41
|
+
* If the anchor message cannot carry a marker (empty content — cf. Hermes _can_carry_marker,
|
|
42
|
+
* prompt_caching.py:127-140, "so the breakpoints land on messages that count"), the
|
|
43
|
+
* boundary reallocates to the nearest earlier eligible message. String content is
|
|
44
|
+
* converted to a one-block array, preserving the text byte-for-byte.
|
|
45
|
+
* (3) BUDGET — never exceed maxBreakpoints total, counting markers already present in
|
|
46
|
+
* system/messages/tools before placing new ones. System wins over conversation prefix.
|
|
47
|
+
* (4) IDEMPOTENT — a position that already carries a marker is left untouched, existing markers
|
|
48
|
+
* count toward the budget, and untouched inputs are returned by reference, so
|
|
49
|
+
* apply(apply(body)) deep-equals apply(body).
|
|
50
|
+
* (5) Bodies without messages/system pass through untouched (same reference), as does anything
|
|
51
|
+
* whose shapes don't match — this is a best-effort decorator, never a validator.
|
|
52
|
+
*
|
|
53
|
+
* minChunkChars default: 4096 chars ≈ 1024 tokens (~4 chars/token) — the documented minimum
|
|
54
|
+
* cacheable prefix for Sonnet/Opus-class models (model-dependent, 512–4096 tokens per the API
|
|
55
|
+
* docs). Prefixes below the model minimum are silently NOT cached, so spending a breakpoint on
|
|
56
|
+
* them is pure waste. For the message boundary the gate compares the CUMULATIVE prefix
|
|
57
|
+
* (system + messages[0..anchor]) against minChunkChars, since that whole span is what the
|
|
58
|
+
* marker would cache (tools text is not counted — a conservative undercount).
|
|
59
|
+
*
|
|
60
|
+
* Pure: the input body is never mutated; modified paths are copied copy-on-write.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
export interface CacheOptions {
|
|
64
|
+
/** Maximum cache_control markers allowed in the request. Anthropic's limit — and the default — is 4. */
|
|
65
|
+
maxBreakpoints?: number;
|
|
66
|
+
/** Don't spend a breakpoint on a prefix shorter than this many characters. Default 4096 (≈1024 tokens). */
|
|
67
|
+
minChunkChars?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface CacheControl { type: "ephemeral" }
|
|
71
|
+
|
|
72
|
+
/** Loose ("-ish") view of the body anthropicStream builds (src/providers/stream.ts:152-160):
|
|
73
|
+
* `{ model, max_tokens, system?: string, messages: [...], tools?: [...] }`. Every field is
|
|
74
|
+
* unknown-typed so the adapter's `Record<string, unknown>` is directly assignable; this module
|
|
75
|
+
* narrows at runtime and passes through anything it does not recognize. */
|
|
76
|
+
export interface AnthropicishBody {
|
|
77
|
+
system?: unknown;
|
|
78
|
+
messages?: unknown;
|
|
79
|
+
tools?: unknown;
|
|
80
|
+
[key: string]: unknown;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const DEFAULT_MAX_BREAKPOINTS = 4;
|
|
84
|
+
export const DEFAULT_MIN_CHUNK_CHARS = 4096;
|
|
85
|
+
|
|
86
|
+
const EPHEMERAL: CacheControl = { type: "ephemeral" };
|
|
87
|
+
|
|
88
|
+
type Rec = Record<string, unknown>;
|
|
89
|
+
|
|
90
|
+
function isRec(v: unknown): v is Rec {
|
|
91
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Marker presence on a content block / tool entry (block-level only — the shapes this module emits). */
|
|
95
|
+
function hasMarker(block: unknown): boolean {
|
|
96
|
+
return isRec(block) && block["cache_control"] !== undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function blockText(block: unknown): string {
|
|
100
|
+
if (!isRec(block)) return "";
|
|
101
|
+
return typeof block["text"] === "string" ? block["text"] : "";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function systemChars(system: unknown): number {
|
|
105
|
+
if (typeof system === "string") return system.length;
|
|
106
|
+
if (Array.isArray(system)) return system.reduce((n: number, b) => n + blockText(b).length, 0);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function contentChars(content: unknown): number {
|
|
111
|
+
if (typeof content === "string") return content.length;
|
|
112
|
+
if (!Array.isArray(content)) return 0;
|
|
113
|
+
let n = 0;
|
|
114
|
+
for (const b of content) {
|
|
115
|
+
n += blockText(b).length;
|
|
116
|
+
// tool_result blocks carry their payload under "content" (string form only; arrays undercount)
|
|
117
|
+
if (isRec(b) && typeof b["content"] === "string") n += b["content"].length;
|
|
118
|
+
}
|
|
119
|
+
return n;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function messageMarkerCount(msg: unknown): number {
|
|
123
|
+
if (!isRec(msg)) return 0;
|
|
124
|
+
const content = msg["content"];
|
|
125
|
+
return Array.isArray(content) ? content.filter(hasMarker).length : 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function countExistingMarkers(system: unknown, messages: readonly unknown[] | null, tools: unknown): number {
|
|
129
|
+
let n = 0;
|
|
130
|
+
if (Array.isArray(system)) n += system.filter(hasMarker).length;
|
|
131
|
+
for (const m of messages ?? []) n += messageMarkerCount(m);
|
|
132
|
+
if (Array.isArray(tools)) n += tools.filter(hasMarker).length;
|
|
133
|
+
return n;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** A marker on this message would actually land on the wire: non-empty string content (an empty
|
|
137
|
+
* string would convert to an empty text block — HTTP 400), or a non-empty block array whose last
|
|
138
|
+
* element is an object. Mirrors Hermes _can_carry_marker (prompt_caching.py:127-154). */
|
|
139
|
+
function canCarryMarker(msg: unknown): boolean {
|
|
140
|
+
if (!isRec(msg)) return false;
|
|
141
|
+
const content = msg["content"];
|
|
142
|
+
if (typeof content === "string") return content.length > 0;
|
|
143
|
+
if (Array.isArray(content) && content.length > 0) return isRec(content[content.length - 1]);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Copy of `msg` with the ephemeral marker on its last content block; string content becomes a
|
|
148
|
+
* one-block array with the text preserved byte-for-byte (Hermes prompt_caching.py:113-121). */
|
|
149
|
+
function withMarkerOnLastBlock(msg: Rec): Rec {
|
|
150
|
+
const content = msg["content"];
|
|
151
|
+
if (typeof content === "string") {
|
|
152
|
+
return { ...msg, content: [{ type: "text", text: content, cache_control: EPHEMERAL }] };
|
|
153
|
+
}
|
|
154
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
155
|
+
const last = content[content.length - 1];
|
|
156
|
+
if (isRec(last)) {
|
|
157
|
+
const blocks = content.slice();
|
|
158
|
+
blocks[blocks.length - 1] = { ...last, cache_control: EPHEMERAL };
|
|
159
|
+
return { ...msg, content: blocks };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return msg; // callers gate on canCarryMarker(); unreachable in practice
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function applyAnthropicCacheBoundaries(body: AnthropicishBody, opts?: CacheOptions): AnthropicishBody {
|
|
166
|
+
const maxBreakpoints = opts?.maxBreakpoints ?? DEFAULT_MAX_BREAKPOINTS;
|
|
167
|
+
const minChunkChars = opts?.minChunkChars ?? DEFAULT_MIN_CHUNK_CHARS;
|
|
168
|
+
|
|
169
|
+
const system = body.system;
|
|
170
|
+
const messages: readonly unknown[] | null = Array.isArray(body.messages) ? body.messages : null;
|
|
171
|
+
|
|
172
|
+
const hasSystem = (typeof system === "string" && system.length > 0) || (Array.isArray(system) && system.length > 0);
|
|
173
|
+
const hasMessages = messages !== null && messages.length > 0;
|
|
174
|
+
if (!hasSystem && !hasMessages) return body; // policy (5): pass through untouched
|
|
175
|
+
|
|
176
|
+
let used = countExistingMarkers(system, messages, body.tools);
|
|
177
|
+
let out = body;
|
|
178
|
+
const copyOnWrite = (): AnthropicishBody => (out === body ? (out = { ...body }) : out);
|
|
179
|
+
|
|
180
|
+
// ---- policy (1): system boundary ------------------------------------------------------------
|
|
181
|
+
if (used < maxBreakpoints && hasSystem) {
|
|
182
|
+
if (typeof system === "string") {
|
|
183
|
+
if (system.length >= minChunkChars) {
|
|
184
|
+
copyOnWrite().system = [{ type: "text", text: system, cache_control: EPHEMERAL }];
|
|
185
|
+
used += 1;
|
|
186
|
+
}
|
|
187
|
+
} else if (Array.isArray(system) && !system.some(hasMarker)) {
|
|
188
|
+
const last = system[system.length - 1];
|
|
189
|
+
if (isRec(last) && systemChars(system) >= minChunkChars) {
|
|
190
|
+
const blocks = system.slice();
|
|
191
|
+
blocks[blocks.length - 1] = { ...last, cache_control: EPHEMERAL };
|
|
192
|
+
copyOnWrite().system = blocks;
|
|
193
|
+
used += 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---- policy (2): conversation-prefix boundary ------------------------------------------------
|
|
199
|
+
// Anchor = most recent message with ≥2 messages after it (index length-3); the trailing two
|
|
200
|
+
// messages are this turn's churn. Reallocate backwards past marker-incapable messages.
|
|
201
|
+
if (used < maxBreakpoints && messages !== null && messages.length >= 3) {
|
|
202
|
+
let anchor = -1;
|
|
203
|
+
for (let i = messages.length - 3; i >= 0; i -= 1) {
|
|
204
|
+
if (canCarryMarker(messages[i])) { anchor = i; break; }
|
|
205
|
+
}
|
|
206
|
+
const msg = anchor >= 0 ? messages[anchor] : undefined;
|
|
207
|
+
if (isRec(msg) && messageMarkerCount(msg) === 0) {
|
|
208
|
+
let prefixChars = systemChars(system);
|
|
209
|
+
for (let i = 0; i <= anchor; i += 1) {
|
|
210
|
+
const m = messages[i];
|
|
211
|
+
if (isRec(m)) prefixChars += contentChars(m["content"]);
|
|
212
|
+
}
|
|
213
|
+
if (prefixChars >= minChunkChars) {
|
|
214
|
+
const next = messages.slice();
|
|
215
|
+
next[anchor] = withMarkerOnLastBlock(msg);
|
|
216
|
+
copyOnWrite().messages = next;
|
|
217
|
+
used += 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** rovecode's own price table for models the models.dev snapshot does not carry — the API ALIASES a
|
|
2
|
+
* vendor sells under (DeepSeek's `deepseek-chat` / `deepseek-reasoner` are aliases of a release; the
|
|
3
|
+
* snapshot lists release ids), and xAI's retired grok-4 slugs, which still resolve but are served and
|
|
4
|
+
* billed as grok-4.3. Without these a real run on those ids is "unpriced" in /cost and `rovecode model show`.
|
|
5
|
+
*
|
|
6
|
+
* Rules: an entry here applies ONLY when neither the live models.dev layer nor the snapshot has the model
|
|
7
|
+
* (catalog.ts findIn) — a later snapshot entry wins by construction. Every entry names the vendor page the
|
|
8
|
+
* numbers were read from and the day; "fetched" means the page was read on that day, "unverified" means the
|
|
9
|
+
* page no longer documents the id and the row is the last known value. `rovecode model show` prints
|
|
10
|
+
* "priced from rovecode's own table" so nobody mistakes them for models.dev's. Prices are USD per million
|
|
11
|
+
* tokens. Output limits are given only where the vendor publishes one (the runtime caps at MAX_OUTPUT_CAP). */
|
|
12
|
+
|
|
13
|
+
export interface LocalModel {
|
|
14
|
+
context: number;
|
|
15
|
+
output?: number;
|
|
16
|
+
/** has a reasoning mode (thinking.ts sends the dial only when this is not false) */
|
|
17
|
+
reasoning: boolean;
|
|
18
|
+
toolCall: boolean;
|
|
19
|
+
image?: boolean;
|
|
20
|
+
cost: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
|
|
21
|
+
/** where the numbers were read — the reader's warrant */
|
|
22
|
+
source: string;
|
|
23
|
+
/** when, and how: "YYYY-MM-DD (fetched)" or "YYYY-MM-DD (unverified: …)" */
|
|
24
|
+
checked: string;
|
|
25
|
+
/** Take precedence over a models.dev entry for this model. Off by default and deliberately rare: the
|
|
26
|
+
* overlay exists to fill gaps, and a snapshot that gains a model should normally win. Set it ONLY when
|
|
27
|
+
* the vendor's own page was read and disagrees with the snapshot, and say so in `source` — a reader has
|
|
28
|
+
* to be able to see which number rovecode chose and why. */
|
|
29
|
+
override?: true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A vendor whose price CHANGES WITH PROMPT SIZE. models.dev has no field for this, so it lives beside the
|
|
33
|
+
* overlay and augments a row whatever its source — the snapshot keeps owning the base prices.
|
|
34
|
+
*
|
|
35
|
+
* `mode` is the part that is easy to get wrong and expensive to get wrong:
|
|
36
|
+
* - "per-request": crossing the threshold reprices THE WHOLE REQUEST, every token, at the upper rate
|
|
37
|
+
* (xAI). A 201k-token prompt costs 201k × the high rate, not 200k low + 1k high.
|
|
38
|
+
* - "marginal": only the tokens past the threshold take the upper rate. NO vendor row uses this today —
|
|
39
|
+
* it exists because the two shapes are not interchangeable and a cost function must branch on which one
|
|
40
|
+
* it has, rather than assume. Do not classify a vendor as marginal without wording that says so. */
|
|
41
|
+
export interface PriceTier {
|
|
42
|
+
thresholdTokens: number;
|
|
43
|
+
mode: "per-request" | "marginal";
|
|
44
|
+
above: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
|
|
45
|
+
source: string;
|
|
46
|
+
checked: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** xAI, docs.x.ai/developers/migration/may-15-retirement (fetched 2026-09-04): grok-4-0709, grok-4-fast-reasoning,
|
|
50
|
+
* grok-4-fast-non-reasoning, grok-4-1-fast-* and grok-3 were retired on 2026-05-15; the slugs still resolve and
|
|
51
|
+
* are SERVED BY grok-4.3 — reasoning slugs at low effort, non-reasoning ones at none — and BILLED at grok-4.3
|
|
52
|
+
* rates ($1.25 in / $2.50 out / $0.20 cached per 1M under 200k context; docs.x.ai/docs/models/grok-4.3, fetched
|
|
53
|
+
* the same day: 1M context, configurable reasoning none|low|medium|high). So a request to `grok-4` costs this. */
|
|
54
|
+
const GROK_43_REDIRECT = { context: 1_000_000, toolCall: true, image: true, cost: { input: 1.25, output: 2.5, cacheRead: 0.2 },
|
|
55
|
+
source: "docs.x.ai May-15-2026 retirement: served by grok-4.3, billed at grok-4.3 rates (under 200k context)", checked: "2026-09-04 (fetched)" } as const;
|
|
56
|
+
|
|
57
|
+
/** Tiered pricing, by provider and model id. Verified 2026-09-05 against the vendors' own pricing pages.
|
|
58
|
+
* Nothing here changes a base price — `catalog.ts` attaches the tier to whatever row it resolved, so a
|
|
59
|
+
* models.dev entry keeps its numbers and gains the threshold it has no field for. */
|
|
60
|
+
export const PRICE_TIERS: Readonly<Record<string, Readonly<Record<string, PriceTier>>>> = {
|
|
61
|
+
// xAI: "requests over 200,000 prompt tokens are billed at the higher rate FOR THE ENTIRE REQUEST"
|
|
62
|
+
xai: {
|
|
63
|
+
"grok-4.6": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 12, cacheRead: 1 },
|
|
64
|
+
source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
|
|
65
|
+
"grok-4.3": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 5, cacheRead: 0.4 },
|
|
66
|
+
source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
|
|
67
|
+
},
|
|
68
|
+
// OpenAI, developers.openai.com/api/docs/models/gpt-5.4 and /gpt-5.5: "Prompts with >272K input tokens
|
|
69
|
+
// are priced at 2x input and 1.5x output for the full session." The multipliers are ASYMMETRIC — input
|
|
70
|
+
// doubles, output goes up by half — so these are written out rather than derived from one rate.
|
|
71
|
+
// The -pro tiers are NOT given this: their own pages do not carry the sentence, and inventing a
|
|
72
|
+
// threshold for a $30/1M model is the expensive kind of guess.
|
|
73
|
+
openai: {
|
|
74
|
+
"gpt-5.4": { thresholdTokens: 272_000, mode: "per-request", above: { input: 5, output: 22.5, cacheRead: 0.5 },
|
|
75
|
+
source: "developers.openai.com/api/docs/models/gpt-5.4 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
|
|
76
|
+
"gpt-5.5": { thresholdTokens: 272_000, mode: "per-request", above: { input: 10, output: 45, cacheRead: 1 },
|
|
77
|
+
source: "developers.openai.com/api/docs/models/gpt-5.5 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
|
|
78
|
+
},
|
|
79
|
+
// Google prices by PROMPT SIZE, not by overflow: the page's own columns read "prompts <= 200k tokens"
|
|
80
|
+
// and "prompts > 200k tokens" for BOTH input and output, so a 201k prompt bills every token — and that
|
|
81
|
+
// request's output — at the upper rate. Same shape as xAI, which is why neither is "marginal".
|
|
82
|
+
google: {
|
|
83
|
+
"gemini-3.1-pro-preview": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 18 },
|
|
84
|
+
source: "ai.google.dev/gemini-api/docs/pricing — \"$4.00, prompts > 200k tokens\" / \"$18.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
|
|
85
|
+
"gemini-2.5-pro": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 15 },
|
|
86
|
+
source: "ai.google.dev/gemini-api/docs/pricing — \"$2.50, prompts > 200k tokens\" / \"$15.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
|
|
87
|
+
},
|
|
88
|
+
} as const;
|
|
89
|
+
|
|
90
|
+
/** A price that is scheduled to change, or a condition the number depends on. Not a tier and not an
|
|
91
|
+
* override — just the sentence a reader needs so today's number is not mistaken for a permanent one.
|
|
92
|
+
* Attached to a row whatever its source. */
|
|
93
|
+
export const PRICE_NOTES: Readonly<Record<string, Readonly<Record<string, string>>>> = {
|
|
94
|
+
google: {
|
|
95
|
+
"gemini-3.8-flash": "promotional pricing through 2026-12-31: ai.google.dev/gemini-api/docs/pricing says it doubles to $1.50 in / $7.50 out starting 2027-01-01 (checked 2026-09-05)",
|
|
96
|
+
},
|
|
97
|
+
mistral: {
|
|
98
|
+
// Checked against mistral.ai/pricing/api on 2026-09-05: the table publishes ONLY `-latest` aliases
|
|
99
|
+
// (mistral-medium-latest $1.5/$7.5, mistral-small-latest $0.15/$0.6, mistral-large-latest $0.5/$1.5).
|
|
100
|
+
// No dated id appears — not the snapshot's `mistral-medium-2604`, and not `mistral-medium-3-5` or
|
|
101
|
+
// `ministral-8b-2512` either. The prices match, the ids are unverifiable, so nothing is renamed here.
|
|
102
|
+
"mistral-medium-latest": "mistral.ai/pricing/api publishes only `-latest` aliases and NO maximum output for any model — the catalog's maxOutput equalling the context window is filler, not a vendor figure (checked 2026-09-05)",
|
|
103
|
+
"mistral-medium-2604": "this dated id is not on mistral.ai/pricing/api, which lists only `-latest` aliases; the price matches mistral-medium-latest but the id itself is unverified (checked 2026-09-05)",
|
|
104
|
+
},
|
|
105
|
+
groq: {
|
|
106
|
+
// Not "unverified" — UNPUBLISHED. Groq's docs table says "Contact Sales" for the Llama models and
|
|
107
|
+
// console.groq.com/pricing is a 404, so any number here came from a third party, not from Groq.
|
|
108
|
+
"llama-3.3-70b-versatile": "Groq does not publish a price for this model — its docs table says \"Contact Sales\" and there is no public pricing page; the catalog's figure is models.dev's, not the vendor's (checked 2026-09-05)",
|
|
109
|
+
},
|
|
110
|
+
anthropic: {
|
|
111
|
+
// true of every Anthropic row; recorded on the one most runs use
|
|
112
|
+
"claude-mythos-5-1": "invite-only access (platform.claude.com/docs/en/models/mythos-5-1/overview); outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
|
|
113
|
+
"claude-fable-5-1": "outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
|
|
114
|
+
"claude-opus-5": "thinking tokens bill as OUTPUT and count inside max_tokens; from Opus 4.5 / Sonnet 4.6 / Fable 5.x earlier thinking blocks are preserved and bill as INPUT on the next turn (platform.claude.com/docs, checked 2026-09-05)",
|
|
115
|
+
},
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
export const LOCAL_MODELS: Readonly<Record<string, Readonly<Record<string, LocalModel>>>> = {
|
|
119
|
+
deepseek: {
|
|
120
|
+
// api-docs.deepseek.com/quick_start/pricing, fetched 2026-09-05. These three rows OVERRIDE models.dev,
|
|
121
|
+
// which carries roughly a third of the vendor's own peak numbers for the same ids (flash 0.14/0.28 vs
|
|
122
|
+
// 0.44/1.32). The page is the seller; the snapshot is a mirror that has drifted, so the page wins here
|
|
123
|
+
// and says so. Peak rates are used: the page's own off-peak wording is self-contradictory (it labels
|
|
124
|
+
// "peak" 01:00-04:00 and 06:00-10:00 UTC, which are its cheap hours elsewhere), so rovecode quotes the
|
|
125
|
+
// HIGHER of the two — an estimate that surprises nobody with a bigger bill. Do not build discount logic
|
|
126
|
+
// on that wording until the page is unambiguous.
|
|
127
|
+
"deepseek-v4-flash": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
|
|
128
|
+
cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
|
|
129
|
+
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
130
|
+
"deepseek-v4-pro": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
|
|
131
|
+
cost: { input: 1.32, output: 3.96, cacheRead: 0.044 },
|
|
132
|
+
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
133
|
+
"deepseek-v4-flash-vision-exp": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, image: true, override: true,
|
|
134
|
+
cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
|
|
135
|
+
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
136
|
+
// The two API aliases. The pricing page lists only the v4-* ids, so nothing here is verified: the
|
|
137
|
+
// numbers are the last ones DeepSeek published for them and are marked as such rather than being
|
|
138
|
+
// quietly refreshed to a v4 price they may not be billed at.
|
|
139
|
+
"deepseek-chat": { context: 128_000, output: 8_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: the alias is not on the pricing page, which lists deepseek-v4-* only)" },
|
|
140
|
+
"deepseek-reasoner": { context: 128_000, output: 64_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: not on the pricing page; DeepSeek now exposes thinking as a MODE of the v4 models, not a separate id)" },
|
|
141
|
+
},
|
|
142
|
+
openai: {
|
|
143
|
+
// developers.openai.com/api/docs/pricing, fetched 2026-09-05. Not in the snapshot at all, so this is a
|
|
144
|
+
// gap fill, not an override.
|
|
145
|
+
"gpt-6-astra": { context: 1_050_000, output: 128_000, reasoning: true, toolCall: true, image: true,
|
|
146
|
+
cost: { input: 10, output: 50, cacheRead: 1 },
|
|
147
|
+
source: "developers.openai.com/api/docs/pricing", checked: "2026-09-05 (fetched)" },
|
|
148
|
+
},
|
|
149
|
+
xai: {
|
|
150
|
+
"grok-4": { ...GROK_43_REDIRECT, reasoning: true },
|
|
151
|
+
"grok-4-0709": { ...GROK_43_REDIRECT, reasoning: true },
|
|
152
|
+
"grok-4-fast": { ...GROK_43_REDIRECT, reasoning: true },
|
|
153
|
+
"grok-4-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
|
|
154
|
+
"grok-4-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
|
|
155
|
+
"grok-4-1-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
|
|
156
|
+
"grok-4-1-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
|
|
157
|
+
"grok-3": { ...GROK_43_REDIRECT, reasoning: false },
|
|
158
|
+
// grok-3-mini: neither on docs.x.ai/docs/models nor in the retirement list on 2026-09-04 — no row, stays unpriced
|
|
159
|
+
},
|
|
160
|
+
};
|