Previewed as 2.0.0-rc.1 to 2.0.0-rc.4. The feature that defines 2.0 is the in-app chat: reconciling bank statements in a conversation inside Pocket CFO, with every write staged for approval, instead of driving an external agent against the API.
Added
- A Chat tab, for admins only, where a bank statement is uploaded and reconciled in a conversation with a model behind any OpenAI-compatible endpoint. The conversation fills the screen with the composer pinned at the bottom — a full-width box, an Attach button with removable file chips, Enter to send — and the Changes rail sits beside it on a wide screen and below it on a phone. The transcript tells logs from answers: the model's reasoning, tool calls and results are gray, collapsed lines between your message and the answer, which is a white card with light formatting. Several chats per user, kept on the server so they survive a reload or a redeploy; a chat can be closed, and
/infocounts the stored chats and can delete them all. Present only whenOPENAI_API_KEYis set. - Every write the model proposes is staged, not committed: the Changes rail shows it as a pending change — a file edit as its diff — and you approve them together, one commit per touched file however many tool calls produced it, or discard one by one. Every applied change has a Revert button that commits the previous content back, or removes a file the change created.
- A turn runs on the server until it finishes: the page follows it over a live event stream and picks it up again after a reload or a tab switch, the composer is disabled meanwhile, and the completion is streamed from the provider so a reasoning model may take as long as it needs — only an endpoint that sends nothing for three minutes is given up on.
- The chat asks instead of assuming: an
ask_usertool ends the turn with a question card — clickable options and, where sensible, a free-text field — and the answer resumes the turn. read_data_fileandwrite_data_file(MCP,GET/PUT /api/files/{name}, chat) servebudget.json,accounts.jsonandconfig.jsonwhole, for what the narrower tools cannot express — a moved dividend, a new category or account, a new dated rule. A write must pass the file's own validation, becomes one commit with the reason as its message, and reports the diff.derive_transaction_ids(MCP) andPOST /api/actuals/ids(REST) hand an agent the stable id of every statement line — a short hash of account, date, amount and description, suffixed-2,-3for identical lines in one call — so ids are computed by the app rather than by the agent, and a re-imported statement still dedups line for line.OPENAI_API_KEY,OPENAI_MODEL,OPENAI_BASE_URLandOPENAI_EXTRA_BODYconfigure the chat: any OpenAI-compatible endpoint, the model passed through verbatim (an OpenRouter preset works), and a JSON object merged into every request for provider-specific fields such as OpenRouter's zero-data-retention routing. The key alone is enough to boot: an unset model falls back togpt-5(openai/gpt-5on OpenRouter) with a log line.- Release candidates: a tag such as
v2.0.0-rc.1publishes the image under its own tag and:next— never:latest— is marked pre-release on GitHub, and does not notify the data repo, so a test deployment can follow:nextwhile production stays on stable tags. Therelease-itskill cuts and later promotes them.
Changed
STATE_DIRis the one writable directory the app keeps between restarts —cache/for Toggl,chat/for the chats — set to/var/data/pocketcfoby the image, so a deployment mounts one volume there and sets nothing else.TOGGL_CACHE_DIRstays as an override, so an existing deployment keeps working;CHAT_DIRoverrides the chat half.