Repository navigation
Document model pricing, the agent's per-session spend caps, and final cancels - #727
DavidCockerill wants to merge 16 commits into
Conversation
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a cap counts agent.maxTokens is the per-reply output limit (#712). Also: a failed call is never counted, an unmeasurable cap on a final answer ends completed, invalid caps fail the agent's start, and auto-mode costUsd covers the final round. Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efused approval does not run later Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the 5.4 release notes HarperFast/harper#3040 is milestoned v5.4, which is main, so it ships in 5.4.0 rather than a 5.3 patch. Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t stays in the total Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xact about costUsd and capped final answers Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sts until restart, and note streaming is unpriced Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ishes Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… captured at run start Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a mid-turn cancel answers the rest of the turn Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HarperFast/harper#3040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🚀 Preview DeploymentYour preview deployment is ready! 🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-727 This preview will update automatically when you push new commits. |
There was a problem hiding this comment.
Code Review
This pull request updates the documentation for version 5.4.0, introducing model pricing, per-call cost tracking, and spend caps (maxSessionTokens and maxCostUsd) for the built-in agent. It also details updates to the operations API regarding session status, usage reporting, and cancellation behavior. The review feedback suggests using a VersionBadge to highlight the new costUsd field in the API documentation, and recommends splitting combined sentences in the tool-calling and operations guides to make fallback behaviors more distinct and readable.
| | `finishReason` | `'stop'` \| `'length'` \| `'tool_calls'` \| `'content_filter'` | Why generation stopped, normalized across backends | | ||
| | `toolCalls` | `ToolCall[]` | Tool calls the model requested, when `finishReason` is `'tool_calls'` (each `{ id, name, arguments }`, with `arguments` parsed to an object) | | ||
| | `usage` | `TokenUsage` | Token usage reported by the backend (`promptTokens`, `completionTokens`, …), when available | | ||
| | `costUsd` | `number` | From v5.4.0, the call's cost from the serving entry's [pricing](./overview#pricing); absent when that entry is unpriced or usage is incomplete. With `toolMode: 'auto'`, the final round's only, like `usage` | |
There was a problem hiding this comment.
When documenting behavior changes to an existing API surface (such as adding the new costUsd field to the GenerateResult response), we should use the <VersionBadge type="changed" version="v5.4.0" /> component to clearly highlight the change.
| | `costUsd` | `number` | From v5.4.0, the call's cost from the serving entry's [pricing](./overview#pricing); absent when that entry is unpriced or usage is incomplete. With `toolMode: 'auto'`, the final round's only, like `usage` | | |
| | `costUsd` | `number` | <VersionBadge type="changed" version="v5.4.0" /> The call's cost from the serving entry's [pricing](./overview#pricing); absent when that entry is unpriced or usage is incomplete. With `toolMode: 'auto'`, the final round's only, like `usage` | |
References
- When documenting behavior changes to an existing API surface (e.g., adding new fields to a response), use .
| | `toolHandlers` | `Record<string, ToolHandler>` | — | Dispatch table keyed by tool name. A tool declared in `tools` with no handler here is a configuration error (400) | | ||
| | `maxToolIterations` | `number` | `10` | Hard cap on model → tools → model rounds | | ||
| | `maxToolTokens` | `number` | — | Cumulative prompt+completion token cap across rounds. Best-effort: requires the backend to report usage; if it does not, Harper warns once and `maxToolIterations` remains the bound. Not supported on `generateStream()` (throws) | | ||
| | `maxCostUsd` | `number` | — | From v5.4.0, cumulative USD cap across rounds, from the [pricing](./overview#pricing) of the entry that served each round. If a round is unpriced, Harper warns once and `maxToolIterations` remains the bound; a cap of `0` still stops after the first round. This is the opposite of the built-in agent's `maxCostUsd`, which stops rather than run unpriced. Not supported on `generateStream()` (throws) | |
There was a problem hiding this comment.
The fallback behavior for unpriced rounds is combined with another concept and buried behind a semicolon. To ensure operators can easily find and understand this fallback behavior, please present it in separate, distinct sentences.
| | `maxCostUsd` | `number` | — | From v5.4.0, cumulative USD cap across rounds, from the [pricing](./overview#pricing) of the entry that served each round. If a round is unpriced, Harper warns once and `maxToolIterations` remains the bound; a cap of `0` still stops after the first round. This is the opposite of the built-in agent's `maxCostUsd`, which stops rather than run unpriced. Not supported on `generateStream()` (throws) | | |
| | `maxCostUsd` | `number` | — | From v5.4.0, cumulative USD cap across rounds, from the [pricing](./overview#pricing) of the entry that served each round. If a round is unpriced, Harper warns once and `maxToolIterations` remains the bound. A cap of `0` still stops after the first round. This is the opposite of the built-in agent's `maxCostUsd`, which stops rather than run unpriced. Not supported on `generateStream()` (throws) | |
References
- Ensure critical security warnings, such as unauthorized access risks or fallback behaviors, are presented in separate, distinct sentences rather than being combined with other concepts or buried behind semicolons, so that readers scanning the documentation can easily find them.
|
|
||
| Cancels a session's run, clears any followups it scheduled, and marks the session `aborted`. Works on a paused (`awaiting_approval`) or `idle` session as well as an actively running one. | ||
|
|
||
| From v5.4.0 the cancel also answers every pending approval, decided or not, with a `cancelled_by_operator` result, in the same write that marks the session `aborted`. An approved action that has not started by then never runs, neither in the cancelled run nor when you next prompt the session, and nothing is left awaiting a decision. One already running finishes, and its result is recorded. A cancel between a turn's tool calls answers the rest of that turn `cancelled_by_operator`. The session stays `aborted`: a run caught mid-step finishes that step without marking the session `completed`, `error` or `awaiting_approval`, a tool call it then asks to have approved is answered `cancelled_by_operator` instead, and it makes no further model call. |
There was a problem hiding this comment.
The fallback behavior for a run caught mid-step during cancellation is combined with other concepts and buried behind a colon and semicolons. Please present this critical fallback behavior in separate, distinct sentences so that readers scanning the documentation can easily find it.
| From v5.4.0 the cancel also answers every pending approval, decided or not, with a `cancelled_by_operator` result, in the same write that marks the session `aborted`. An approved action that has not started by then never runs, neither in the cancelled run nor when you next prompt the session, and nothing is left awaiting a decision. One already running finishes, and its result is recorded. A cancel between a turn's tool calls answers the rest of that turn `cancelled_by_operator`. The session stays `aborted`: a run caught mid-step finishes that step without marking the session `completed`, `error` or `awaiting_approval`, a tool call it then asks to have approved is answered `cancelled_by_operator` instead, and it makes no further model call. | |
| From v5.4.0 the cancel also answers every pending approval, decided or not, with a `cancelled_by_operator` result, in the same write that marks the session `aborted`. An approved action that has not started by then never runs, neither in the cancelled run nor when you next prompt the session, and nothing is left awaiting a decision. One already running finishes, and its result is recorded. A cancel between a turn's tool calls answers the rest of that turn `cancelled_by_operator`. The session stays `aborted`. A run caught mid-step finishes that step without marking the session `completed`, `error`, or `awaiting_approval`. A tool call it then asks to have approved is answered `cancelled_by_operator` instead, and it makes no further model call. |
References
- Ensure critical security warnings, such as unauthorized access risks or fallback behaviors, are presented in separate, distinct sentences rather than being combined with other concepts or buried behind semicolons, so that readers scanning the documentation can easily find them.
⊙ Problem
agent: carry per-call token usage onto the session and enforce a budget (HarperFast/harper#3040) makes the agent's
maxCostUsdreal, addsagent.maxSessionTokens, records per-session usage, lets amodels.generativeentry declare its pricing, makes a cancel final, and validatesmaxTurns. The reference still callsmaxCostUsd"not enforced" with a5.00default, and documents none of the rest.Refs HarperFast/harper#3040. Companion to Cap the agent's spend per session by tokens or priced cost, record its model usage, and make a cancel final (HarperFast/harper#3177).
💡 Solution
pricingshared-field row,costUsdonGenerateResult, streamed calls being unpriced, andmaxCostUsdamong thetoolMode: 'auto'options, with the reserved-options note no longer listing it.maxTurns,maxSessionTokensandmaxCostUsdbullets, theset_agent_configkey list, and a new Agent spend caps subsection: what a cap counts, when it is checked, how a stop leaves the transcript, what an unmeasurable cap does, and how to leave a stuck session.errornow covering cap stops and a capped final answer endingcompleted;agent_prompt's 409s while a cancelled run finishes or when a cancel races the prompt;usageonget_agent_sessionandlist_agent_sessions;cancel_agent_runanswering pending approvals; andset_agent_configvalidating the caps andmaxTurns.agent.maxCostUsd, and for agent cancellation and limits.The
api.mdandtool-calling.mdhunks also re-align their tables, which Prettier does when a wider row is added.✅ Verification
Every statement was checked against the core branch by review rounds (Codex graded leg, Gemini, Harper domain adjudication); the last round left only nits.
npm run format:checkpasses. The core behavior these pages describe is exercised by the core PR'sintegrationTests/server/agent-budget.test.tsand its agent unit suites.🤖 Generated by Claude Code (Anthropic Claude Opus 5.5); posted via @DavidCockerill.
Related PRs: #568 independent, #671 independent, #676 independent, #683 independent, #691 independent, #709 independent, #712 overlaps (documents
agent.maxTokens, the per-reply limit, besideagent.maxSessionTokens; theset_agent_configkey list and the 5.4 agent notes conflict), #714 independent, #717 overlaps (agent.configScopein the same agent options andset_agent_configtext, and the 5.4 agent notes), #720 overlaps (agent.maxToolResultBytesin the same agent options andset_agent_configtext, and the 5.4 agent notes), #722 independent, #724 independent, #725 independent, #715 independent, #713 independent, #697 independent, #710 independent, #707 independentComplexity: medium
Review-Coverage: authored=claude; ran=gemini,codex; adjudicated=domain; blocked=cursor-composer(not-installed); declined=cursor-grok,cursor-kimi,cursor-muse; rounds=8; full=2 @ 5c38284
Review-Attention: skim ~2m (decisions: auto-loop-unpriced-fails-open, failed-calls-uncounted, legacy-sessions-unmeasured, maxcostusd-default-removed, auto-costusd-final-round, cancel-refuses-approved-actions, cancel-finishing-409, maxturns-null-split) @ 5c38284