Skip to content

Outcasts Fuseforks v0.1.11

Choose a tag to compare

@github-actions github-actions released this 25 Aug 06:28

✨ What's new

💵Statistics now show approximate cost

The statistics screen has a cost estimate. Register a model's prices — input, output, cache read, cache write, and optionally the 1-hour cache write rate — and the totals gain an ≈ $ line.

Prices live on the model template, so they travel with the village. The Fetch button in the model registration dialog pulls a price table from a URL you can change or empty in System settings › Cost management. It is fetched only when you press the button — the app never reaches out on its own.

The estimate is labelled and comes with a coverage line, on purpose. Coverage is reported on two axes — how many servants have prices, and what share of tokens they account for. The first real measurement here read 5/7 servants · 99.9% of tokens: by headcount it looks like a third is missing, by tokens it looks complete. Either number alone would mislead.

Two accuracy fixes underpin this:

  • Cache writes are now counted separately across all six wires. Measurement showed that what the app had been calling "uncached input" was almost entirely cache writes (100.0% on Anthropic, 99.8% on OpenAI-compatible). Writes cost more than reads, so the earlier numbers understated input cost — on Anthropic models by about 1.6×
  • Per-servant totals are now keyed by (servant, model). Previously a servant that changed models had all of its turns folded into whichever model it used last, which put the wrong price on every earlier turn

🗨️"All conversations" in statistics is now a monthly total

Lifetime totals grow forever, and the longer you use the village the harder it becomes to read "what did last month cost." The all-conversations view now shows one closing month at a time, with ◀ ▶ to walk backwards and a toggle to go back to the lifetime total.

The closing day is set in System settings › Cost management (1–28 or end-of-month, default end-of-month) and is stored on your machine, not in the village. Days 29–31 are not offered — February has no 30th, and allowing it would require a second rule for what happens that month.

Closing day 1 is not the same as end-of-month. Day 1 gives you 7/2–8/1; end-of-month gives 8/1–8/31. Rather than explain that, the settings page shows the current period live, right under the selector.

🌊Plan review — check the plan before the wave goes out

Off by default. Existing villages behave exactly as before.

Turn on Plan review for a servant and its plan calls stop before dispatch: the plan is shown in the work-status pane, where you can edit each task's text, delete tasks, and then Dispatch or Discard.

What the mechanism buys you is not convenience — you could already ask a coordinator to propose a plan in prose and wait for your go-ahead. What that cannot give you is identity between what you saw and what runs: after your approval the model rewrites the plan arguments, so the plan you approved and the plan that executes are two different objects. Here the data you looked at is dispatched directly. The model is not on the path after approval.

The turn does not block while it waits for you. The proposal ends the turn normally, and your approval starts a new causal chain — which means the wave gets a fresh token budget and a fresh cancel scope, and the bundled results come back to the coordinator as a new turn.

⏰️Delegation wait time is configurable, and circular delegation is refused instantly

The wait for a delegated answer was a fixed 180 seconds. One clock was doing two opposite jobs — waiting long enough for a deep investigation, and breaking out of two servants delegating to each other. Deep research lost that argument: investigations that take minutes were being discarded, and the worker kept running, so the answer was produced, billed, and thrown away.

The two jobs are now separate:

  • Cycles are refused structurally. Each envelope carries the chain of servants currently waiting. If a delegation targets someone already in that chain, it is refused immediately — not queued, not waited on. The refusal explains itself and tells the model what to do instead, so it can change course in the same round
  • The clock is now a safety net, not a cycle breaker. The default is 600 seconds, adjustable from 30 to 3600 in System settings › Cost management. It is stored in the village, so it travels with it

A delegation that took 206 seconds now returns its full 2,849-character answer instead of being discarded at 180.

🛠Smaller changes

  • The editor understands Ctrl+S, Ctrl+F, and Ctrl+R. Ctrl+S saves (subject to the same conditions as the Save button), the search panel is translated, and Ctrl+R while the editor has focus no longer reloads the window and discards what you were writing
  • A Chinese README (README_zh.md)
  • Manually entered prices now save. They were being dropped silently — a number-typed input hands the setter a number even without the .number modifier, and the string-only setter died on it. Only the empty field, which arrives as a string, was getting through
  • The log records which build touched the village — a version: app=… profile=… line at startup
  • reply: lines carry refusal=yes|no, a plain observation of whether a reply took the shape of a refusal. It has no effect on routing

⚠️What you take on

  • If you drive this village from an MCP client, add a timeout to your .mcp.json. The default wait is now 600 seconds, which is longer than the default client-side timeout — without it your client gives up before the village does
  • Do not open this village with an older build. Older versions do not know the price fields, drop them as unknown, and write the file back without them — silently. The startup line added in this release lets you tell afterwards which build touched it, but it does not prevent the loss. If you have both the MSI and the NSIS installer on one machine, they share the same village
  • The cost figure is an estimate. It is a token count multiplied by prices you entered. Tiered pricing, per-request minimums, and provider-side discounts are not modelled — the and the coverage line are there to say so
  • Turns recorded before v0.1.11 have no cache-write figure, so their cost reads low. This fades as new turns accumulate; nothing was built to correct it
  • The price table is fetched from an external URL when you press Fetch. The default host sees the request and your IP. You can point it elsewhere or clear it

✨ 変更点

💵統計におおよその金額が出ます

統計画面に金額の推定が付きました。モデルごとの単価 — 入力・出力・キャッシュ読み出し・キャッシュ書き込み、任意で 1 時間キャッシュの書き込み — を登録すると、集計に ≈ $ の行が増えます。

単価はモデルテンプレートに住むので、村を配ると一緒に付いて回ります。モデル登録ダイアログの「取得」ボタンが単価表を取りに行き、その取得先はシステム設定 › コスト管理で変更も空にもできます。押したときだけ取りに行き、アプリが自分から外部へ問い合わせることはありません。

推定であることを と被覆率で明示しています。 被覆率は 2 軸 — 単価が登録されているサーヴァントの数と、その個体が占めるトークンの割合です。最初の実測は 5/7 体・トークン 99.9% で、体数だけ見れば「3 割欠けている」、トークンだけ見れば「揃っている」と読めます。 片方だけでは嘘になります。

これを支える精度の修正が 2 つ入っています:

  • キャッシュ書き込みを 6 つのワイヤすべてで別に数えるようにしました。 実測すると、それまで「未キャッシュ入力」として扱っていたものはほぼ全部がキャッシュ書き込みでした(Anthropic で 100.0%、OpenAI 互換で 99.8%)。書き込みは読み出しより単価が高いので、それまでの数字は入力コストを過小に出しており、Anthropic のモデルでは 約 1.6 倍のずれがありました
  • 個体ごとの集計を (個体, モデル) の組で数えるようにしました。 以前はモデルを切り替えた個体の全ターンが最後に使ったモデルに畳まれており、それ以前のターンに違う単価が当たっていました

🗨️統計の「全会話」が月ごとの集計になりました

生涯の累計は増え続けるので、使い込むほど「先月いくらだったか」が読めない数字に育ちます。全会話の表示を締め月 1 か月ぶんに変え、◀ ▶ で前の月へ遡れるようにしました。生涯累計へ戻すトグルもあります。

締め日はシステム設定 › コスト管理で選びます(1〜28 日または月末。既定は月末)。保存先は端末で、村には入りません。29〜31 日を選べないのは、2 月に存在しない日を許すと「その月はどうなるか」という規則がもう 1 つ必要になるためです。

締め日 1 は月末と同じではありません。 1 日締めは 7/2〜8/1、月末締めは 8/1〜8/31 です。これを説明文で伝える代わりに、選択肢のすぐ下に「今の期間」をライブで表示しています。

🌊計画の確認 — 波を撒く前に計画を見る

既定は OFF です。既存の村の挙動は 1 ミリも変わりません。

サーヴァントの設定で「計画の確認」を ON にすると、その個体の plan は配送の手前で止まります。計画が作業状況ペインに提示され、各タスクの本文を書き換え、不要なものを削ってから「実行」または「破棄」を選べます。

この機構が買っているのは手間の削減ではありません — 「まず計画を文章で出して、承認を待ってから撒け」と条例に書くことは以前からできました。それでは得られないのが「見た計画と走る計画が同じものである」ことです。承認の後にモデルが plan の引数を書き直すので、承認した計画と実行される計画が別物になりえます。この機構では人が見たデータがそのまま配送され、承認後の経路に LLM は居ません

ターンの中で人を待ちません。 提示でターンは正常に終わり、承認が新しい因果の起点になります。つまり波には新しいトークン予算と打ち切りの範囲が与えられ、束ねた結果は新しいターンとして進行役へ届きます。

⏰️委譲の待ち時間を設定できるようになり、循環する委譲は即座に拒否されます

委譲した答えを待つ時間は 180 秒の固定値でした。1 本の時計が逆向きの 2 つの仕事を担っていたことになります — 深い調査を待ち切ることと、互いに委譲し合う 2 体から抜け出すことです。負けていたのは調査の側で、分単位の調査が捨てられ、しかも相手のターンは止まらないので、答えは作られ、課金され、届かずに捨てられていました。

2 つの仕事を分けました:

  • 輪は構造で拒否します。 依頼の封筒が「いま答えを待っているサーヴァントの連鎖」を運び、その連鎖に居る相手へ委譲しようとすると即座に拒否されます(受信箱に積まれず、待ちも発生しません)。拒否には理由と次の手が書かれているので、モデルは同じ周で進路を変えられます
  • 時計は保険へ格下げしました。 既定は 600 秒で、システム設定 › コスト管理から 30〜3600 秒の範囲で変えられます。保存先は村なので、配ると一緒に付いて回ります

206 秒かかった委譲が、180 秒で捨てられずに 2,849 字の答えを返すようになりました。

🛠細かい変更

  • エディタが Ctrl+S / Ctrl+F / Ctrl+R を理解します。 Ctrl+S で保存でき(条件は保存ボタンと同じ)、検索パネルが日本語になり、エディタにフォーカスがある間の Ctrl+R は画面を再読み込みして書きかけを消すことがなくなりました
  • 中国語の README を追加しましたREADME_zh.md
  • 手で入力した単価が保存されるようになりました。 それまで黙って捨てられていました — type="number" の入力欄は .number 修飾子が無くても数値を渡すため、文字列前提の処理がそこで死んでおり、文字列で届く空欄だけが通っていました
  • どの版が村を触ったかがログに残ります — 起動時に version: app=… profile=… の 1 行を出します
  • reply: の行に refusal=yes|no が付きます。 返信が断りの形をしていたかどうかの観測で、配送には一切影響しません

⚠️利用者が負う条件

  • MCP クライアントからこの村を使っている場合は、.mcp.jsontimeout を書き足してください。 既定の待ち時間が 600 秒になり、クライアント側の既定より長くなったため、書かないとクライアントが先に諦めます
  • この村を古い版で開かないでください。 古い版は単価の欄を知らないため未知のフィールドとして捨て、そのまま書き戻して消します。警告は出ません。 この版で足した起動行のおかげで後からどの版が触ったかは読めますが、消えること自体は防げません。MSI と NSIS のインストーラを同じ端末に両方入れている場合、2 つは同じ村を共有します
  • 金額はあくまで推定です。 トークン数に入力された単価を掛けたもので、段階課金・要求ごとの最低額・提供元の割引は考慮していません。 と被覆率の行はそれを言うために付いています
  • v0.1.11 より前に記録されたターンにはキャッシュ書き込みの数字がないため、金額が低めに出ます。新しいターンが積まれるにつれ薄まります。これを補正する機構は作っていません
  • 単価表は「取得」を押したときに外部の URL から取得します。 既定の配信元には要求の事実と IP が残ります。取得先は変更も空にもできます