Skip to content

v0.59.0 — what the herd is costing you

Latest

Choose a tag to compare

@ralyodio ralyodio released this 16 Aug 12:13
8c8cbfb

The other question you have with six agents running

moshcode ps answers "which one is blocked". It has never answered "what is
this costing me", and with a herd running overnight that is the question you
wake up to.

  session  engine  model          in    out   cache  cost    age
  api      claude  claude-opus-5  1.2k  27k   10.5M  $9.91~  42m
  audit    codex   gpt-5.6-sol    400   200   600    —       12m

  total  $9.91~  1.6k in · 27k out · 10.5M cached
  ~ estimated from published rates; unmarked figures are the engine's own.
⚠ no rate for gpt-5.6-sol — tokens counted, cost omitted.

Nothing is instrumented, wrapped or proxied to produce that. Every engine
already writes down what it used, because it needs the numbers itself — Claude
Code keeps a per-message usage block in ~/.claude/projects/**/*.jsonl, Codex
emits cumulative token_count events into ~/.codex/sessions/…, opencode and
privacycode store a cost they computed themselves in SQLite, and aider prints
a running session total into .aider.chat.history.md beside the code. This
reads what is on disk and lines it up against the herd.

moshcode cost                    # per session, last 24h
moshcode cost api                # one session, with its engine runs
moshcode cost --all --since 7d   # every engine session on the box, herd or not
moshcode cost --watch            # re-read every 10s — the slope is the point
moshcode cost --json             # for a script

Also moshcode herd cost, /cost in the pit, and cost() in moshscript.
--all needs no herd at all, so a foreground moshcode agents claude is
covered the same way.

A ~ is an estimate and an unmarked figure is not

opencode and aider price their own messages, and that price is reported
untouched — they know which model actually served the request. Claude Code and
Codex record tokens and nothing else, which is the honest state of things on a
subscription where the marginal request costs nothing extra, so those get
multiplied by a rate card and marked. The mark is the whole point: one of those
numbers is a measurement and the other is "what this would have cost on the
API". --json carries it as costSource.

A model nobody has priced shows its tokens and no cost, rather than a
convincing-looking $0. Only Anthropic rates ship, because a Codex or Kimi
model id (gpt-5.6-sol is what a rollout actually records) does not appear on
any public price list and inventing one would be worse than a blank. Your own
rates go in ~/.moshcode/pricing.json and win over the shipped table:

{ "gpt-5.6-sol": { "input": 1.25, "output": 10 } }

Cache tokens get a column of their own for the same reason. On a long agent
session they are most of the traffic and a tenth of the price — fold them into
in and a $3 session reads like a $60 one, which is the opposite of the point.

Attribution is engine + directory + "started before this run did". That is right
for the ordinary case of one agent per directory and can be wrong when two
sessions of the same engine share one, so --json carries the raw run list
rather than implying an answer, and a run that matches no session is its own
line instead of being absorbed into a neighbour's total. gemini, kimi, qwen,
deepseek and openagents keep no readable usage log, so they report no cost —
which is not the same as zero cost, and the report says so.

A name can publish, not just point

target proxies a name at a web server its owner already runs. That is the
right primitive and the wrong first step: most people who claim a name have
something to publish and nowhere to publish it from, so the commonest thing in
the registry was a name that never became a site.

A name can now carry a feed — paste an RSS or Atom URL next to any name you
hold and /n/<name> renders it, as a blog with dates and excerpts or as a
podcast with cover art, running times and a player. The layout is inferred from
whether the entries carry audio enclosures, and can be overridden when the guess
is wrong.

The other half is somewhere to put writing that does not exist anywhere yet:

POST /api/moshpit/sites/blue.eggs/content
{"kind":"link","title":"Worth reading","url":"https://example.com/post"}

Eight kinds — section and page for navigation, then text, link, image, gallery,
video and embed — which is the set a link aggregator has because it is the set
people actually post. It upserts on the slug, because the caller is a webhook
and anything firing over HTTP retries: a publish endpoint that makes a second
copy on every redelivery is one you cannot point automation at.

And GET /api/moshpit/log finally exposes the allocation record. "The directory
can be mirrored and served by anyone" was true of the schema and false of the
product, because nothing could read the log; now a second copy of the registry
is something a stranger can build.

moshscript catches up with the CLI

The vocabulary had drifted to 43 verbs against 54 commands, with no way for a
script to authenticate, reach the pit's aliases, or read a tool's output as
anything but an exit code. Account verbs (login, whoami, requireLogin,
logout) close that, with identity() in src/auth.mjs as the one place that
decides what "logged in" means so the CLI and a script can never disagree.

The paddle is on the game's clock

Pong and breakout tick at 60Hz and move the ball on that clock; the paddle — the
one object you are actually steering — was still being moved by the keypress
itself, one jump per key. A terminal has no key-up, only auto-repeat, so that is
two problems and neither of them is "the paddle is slow". Both paddles now run
on the same clock as everything else on the board.

Housekeeping

The ThreatCrush pack moves to 1.7.0, replacing a local scoping fix, and its PR
comment is scoped to the files a PR actually changes.