Skip to content

v0.5.4

Choose a tag to compare

@github-actions github-actions released this 21 May 11:16
· 603 commits to main since this release

Upgrade notes

Breaking changes

Upgrade notes

v0.5.4 extends the Odoo template library with read-write operator agents, hardens how those agents reference Odoo records, and irons out several chat-reliability edges. It also picks up Next.js security patches and OpenClaw 2026.5.7.

Upgrade with the standard flow:

cd /opt/pinchy
sed -i 's/PINCHY_VERSION=v0.5.3/PINCHY_VERSION=v0.5.4/' .env
docker compose pull && docker compose up -d && docker image prune -f

No database migration, no docker-compose.yml change, and no env-var changes are required.

**Odoo users: re-sync your schema after upgrade.** v0.5.4 adds six new agent templates (Bookkeeper, Project Manager, HR Operator, Warehouse Operator, Production Operator, Approval Manager) whose `requiredModels` reference Odoo models that previous Pinchy versions didn't probe (e.g. `account.account`, `account.tax`, `res.users`, `res.currency`, `hr.expense.sheet`, `project.project`, `mrp.production`, `ir.attachment`). Existing connections still hold the old narrow schema cache, so the new templates' **Create** buttons stay disabled even though your Odoo instance exposes the models. Open **Settings β†’ Integrations β†’ Odoo β†’ β‹― β†’ Sync now** once after the upgrade to refresh the cache.

This is also required for existing Bookkeeper-style agents created before
the FK-lookup fix: without account.account in the cache, the agent
cannot select the right ledger account when posting a vendor bill.

Bookkeeper / Odoo schema discovery (auto-migrated)

The odoo_schema tool has been replaced by two narrower tools: odoo_list_models and odoo_describe_model. The new odoo_describe_model returns a compact field map (default ~40 most-common fields per model) and accepts fields: ["…"], limit, and verbose: true arguments. This cuts typical schema-discovery context cost from ~18 kB per model to ~1 kB, which unblocks vision-capable models like ollama-cloud/gemini-3-flash-preview for invoice-processing flows.

No action required. A startup migration rewrites every agent's allowed_tools to use the new names, and the read-write Odoo operator templates (Bookkeeper, Warehouse Operator, HR Operator, etc.) ship with updated guidance for the agent.

For existing agents created before v0.5.4, the old odoo_schema tool name is kept as a deprecated alias. The AGENTS.md files those agents store in their workspace still contain literal odoo_schema references β€” the alias keeps them working without any manual file editing, and routes the call through the new compact code path. You can safely ignore the alias; it will be removed in v0.6.x.

Six new Odoo agent templates

The agent picker now includes a Bookkeeper template and five read-write Odoo operator counterparts to the existing read-only Analysts:

Analyst (read-only) Operator (read + create + write)
Project Tracker Project Manager
HR Analyst HR Operator
Inventory Scout Warehouse Operator
Manufacturing Planner Production Operator
β€” Approval Manager (cross-module)

Each operator template follows a safety pattern: mandatory user confirmation before any side-effecting write, duplicate-check on every create, no delete on any model (cancellations are write transitions, never hard deletes), and read-only access to records that belong to a different role. The new Bookkeeper template covers the "book this receipt" workflow with a draft-first, dedupe-first, one-shot-create flow.

The Approval Manager works on Odoo Community as well as Enterprise β€” the approval.request and approval.category models are flagged optional, so the Create button stays enabled on Community installs and the agent discovers availability at runtime via odoo_schema.

Vision capability is now standard for read-write Odoo templates

Every read-write Odoo operator template now declares the vision capability in its model hint. Previously this had to be set manually after agent creation, which often landed users on unstable Vision+Tools models. A new drift invariant in the test suite enforces this for any future write-capable Odoo template.

Hardened Odoo relation references

When an Odoo tool returns a record, related-record references are now emitted as opaque, encrypted tokens (pinchy_ref:v1:…) rather than raw (id, label) tuples. The pinchy-odoo plugin transparently resolves these tokens on subsequent calls, so an agent can no longer fabricate or substitute a numeric ID it never saw. Existing agents pick up the new behavior automatically; no configuration change is required.

Self-refs on odoo_create and odoo_read (_pinchy_ref)

Every record the plugin returns now carries a _pinchy_ref field β€” an opaque, encrypted token that identifies the record itself (alongside the raw integer id). odoo_create returns {id, _pinchy_ref} instead of just {id}, and every record returned by odoo_read carries one.

This closes a release-blocker gap surfaced on staging in late v0.5.3 testing: the odoo_attach_file tool only accepts opaque references for targetRef, but odoo_create's old {id}-only response left agents no path to a valid token for a record they had just created. They would fabricate strings like "account.move,37", which the plugin correctly rejected with "Invalid integration reference" β€” making odoo_create β†’ odoo_attach_file chains (the entire Bookkeeper "book this bill and attach the PDF" flow) effectively unreachable.

The field is named _pinchy_ref rather than ref to avoid shadowing Odoo's own ref field, which exists on account.move, account.payment, and others as a string holding payment-reference identifiers ("INV/2026/0001" etc.). The underscore prefix signals "Pinchy-added, not Odoo data" so the LLM's view of actual Odoo records stays lossless.

Tools that already accept raw integer IDs (odoo_write, odoo_delete) are unchanged. Existing agents pick up the new behavior automatically; the operator-template AGENTS.md files have been updated to teach agents about the _pinchy_ref field, so old agents continue to work via the shared instruction shipped in their template prompt.

Odoo file attachment (new odoo_attach_file tool)

Read-write Odoo operator templates can now attach uploaded files to Odoo records as ir.attachment. The Bookkeeper template uses this to link receipt images to the corresponding vendor bill; the Warehouse Operator can attach delivery notes to pickings; the HR Operator can attach medical certificates to leave requests; and so on for the Project Manager, Production Operator, and Approval Manager.

The new odoo_attach_file tool is automatically included in the read-write permission tier alongside odoo_create and odoo_write. When you create any read-write Odoo agent, the tool is pre-enabled β€” no manual permission change is required.

After upgrading: existing Odoo connections need a schema re-sync to pick up ir.attachment. Open Settings β†’ Integrations β†’ Odoo β†’ β‹― β†’ Sync now. Until you sync, the Create buttons for operator templates that reference ir.attachment will remain disabled.

The tool rejects filenames containing path separators or leading dots, and caps attachments at 25 MB (matching Odoo's default web.max_file_upload_size). These guardrails prevent a prompt-injection-influenced agent from referencing files outside the agent's uploads directory.

Background chats

Open chats keep running in the background while you navigate within Pinchy β€” agents no longer reset when you switch to another part of the app. A pulse dot in the sidebar marks each agent with an active run; if the last turn errored, the dot turns red so you see at a glance which chats need attention. (#199)

Chat reliability fixes

  • Browser sleep recovery. Chats now reattach cleanly when a tab returns from background sleep instead of getting stuck on a stale stream.
  • Dead-key composer freeze. Typing characters that involve dead keys (e.g. ^ + e β†’ Γͺ) no longer desyncs the composer input.
  • Payload rejection visibility. When OpenClaw rejects a payload mid-stream, the chat now surfaces a clear error status instead of leaving the spinner running.
  • Silent stream-end retry. A run that finishes without producing any assistant text β€” typically a cold-path tool call that timed out inside OpenClaw's embedded layer β€” now shows the standard error bubble with a Retry button instead of an indistinguishable "successful empty reply". Each occurrence writes a throttled chat.silent_stream audit entry so admins get an operational signal.
  • Gemini 3 thought_signature errors are now named and explained. When the upstream provider drops Gemini 3's required thought_signature field on a tool-call replay turn (upstream openclaw/openclaw#72879), Pinchy now shows a dedicated transient upstream issue bubble that names the cause and tells the user that Retry usually clears it on the next try. The bubble deliberately offers no "Switch model" link β€” the model is fine. Each occurrence writes a throttled agent.upstream_format_error audit entry so admins can track frequency from the audit page rather than grepping gateway logs. The upstream fix for the native Google path lands in OpenClaw 2026.5.18; v0.5.4 is still pinned at 2026.5.7 and ships with the transient-issue bubble as the user-facing mitigation. The OpenClaw bump is scheduled for the next Pinchy release. (#338)

Permissions tab no longer reports false-positive dirty state

If an Odoo or Email connection was configured on an agent, the Settings β†’ Agent β†’ Permissions tab kept showing "Unsaved changes" and a Save & Restart button after a successful save. The dirty-state check now re-syncs against the persisted state once the post-save refetch lands, so a clean save shows as clean.

New /api/version endpoint + OCI image labels

You can now confirm which Pinchy and OpenClaw versions are actually running without opening the UI. GET /api/version returns { pinchyVersion, openclawVersion, build, nodeEnv } and is exempt from Domain Lock so it works from monitoring tools and the host shell. Docker images now also carry standard OCI labels (org.opencontainers.image.version, org.opencontainers.image.revision, …), so docker inspect ghcr.io/heypinchy/pinchy:v0.5.4 reports the version and git SHA the image was built from.

The upgrading guide's "Verify" step is updated to use /api/version instead of opening http://localhost:7777 directly β€” the old instruction returned 403 on Domain-Lock-enabled deployments.

Startup warning when Domain Lock is set but BETTER_AUTH_URL is unset

Better Auth's own baseURL autodetection does not read Pinchy's Domain Lock value (verified on a staging v0.5.4 install where Better Auth still logged Base URL could not be determined). On a Domain-Locked deployment, that gap silently sends password-reset and email-verification links pointing at the wrong host.

Pinchy now logs a clear ⚠ Domain Lock is configured (<your-domain>) but BETTER_AUTH_URL is unset… warning on every startup of such a deployment, with the exact env-var assignment to copy-paste into your .env. The existing BETTER_AUTH_URL is set line is retained for the positive case. If you are running with Domain Lock today, check your pinchy container logs after this upgrade and add BETTER_AUTH_URL=https://<your-domain> to /opt/pinchy/.env if the warning appears. (#352)

Runtime and dependency updates

  • OpenClaw 2026.5.7 (was 2026.5.3) β€” picked up via Dockerfile.openclaw and the bundled openclaw-node 0.9.0.
  • Next.js 16.2.6 (was 16.2.4) β€” addresses GHSA-wfc6-r584-vfw7 and GHSA-26hh-7cqf-hhc6.
  • ws 8.20.1 (was 8.20.0) β€” addresses GHSA-58qx-3vcg-4xpx (uninitialized memory disclosure).

What's Changed

  • fix(openclaw-config): surface persistent EACCES on openclaw.json read (#314) by @clemenshelm in #329
  • fix(chat): surface silent OpenClaw stream-end as retry-able error by @clemenshelm in #326
  • chore(deps): bump openclaw 4.27 β†’ 5.3 + openclaw-node 0.7.0 β†’ 0.8.0 by @clemenshelm in #279
  • fix(agent-settings): clear Permissions dirty state on save when an integration is configured by @clemenshelm in #337
  • chore(deps): bump openclaw 5.3β†’5.7, sync eslint-config-next 16.2.6 by @clemenshelm in #335
  • [codex] fix payload rejection chat status by @clemenshelm in #331
  • fix(drizzle): rebuild corrupted snapshot chain for migrations 0025–0030 by @clemenshelm in #341
  • [codex] Fix dead-key composer input freeze by @clemenshelm in #349
  • Harden Odoo relation references for agent writes by @clemenshelm in #348
  • [codex] Harden chat recovery after browser sleep by @clemenshelm in #347
  • feat(templates): Odoo operator suite (Bookkeeper + 5 operators) + vision standard by @clemenshelm in #332
  • docs(upgrading): prep v0.5.4 release notes + freeze v0.5.3 section by @clemenshelm in #350
  • feat: /api/version + OCI labels, fix Domain Lock verification trap (v0.5.4 pre-release) by @clemenshelm in #351
  • fix(odoo-sync): probe all template-required models + drift-guard test by @clemenshelm in #353
  • feat(odoo): add odoo_attach_file tool + fix vision resolver for read-write operators by @clemenshelm in #356
  • [codex] Sync missing Odoo permission models by @clemenshelm in #346
  • fix(plugins): add contracts.tools to 5 manifests + 3-layer test coverage for OC 5.3 by @clemenshelm in #358
  • fix(pinchy-odoo): strip quote-wrapped keys from create/write values by @clemenshelm in #359
  • fix(e2e/web): drain OC config.apply rate-limit before dispatch probe by @clemenshelm in #361
  • fix(pinchy-odoo): recurse into nested records when stripping quote-wrapped keys by @clemenshelm in #360
  • docs(upgrading): finish v0.5.4 release notes (Background chats + attach_file security) by @clemenshelm in #357
  • feat(pinchy-odoo): split odoo_schema into odoo_list_models + odoo_describe_model with compact response by @clemenshelm in #362
  • fix(pinchy-odoo): re-add odoo_schema as deprecated alias by @clemenshelm in #365
  • fix(pinchy-odoo): auto-provision integration-ref encryption key by @clemenshelm in #366
  • fix(odoo-templates): grant read on FK-lookup models for 6 read-write operators by @clemenshelm in #367
  • fix(pinchy-odoo): emit _pinchy_ref on every record so odoo_create chains into odoo_attach_file by @clemenshelm in #379
  • fix(pinchy-odoo): declare inner items for filters tuple so OpenAI accepts schema by @clemenshelm in #380
  • fix(markdown-editor): align caret with click on inline-code and heading-heavy content by @clemenshelm in #381
  • feat: switch default model tier from fast to balanced by @clemenshelm in #378
  • fix(odoo-bookkeeper): document gross-to-net + mandate post-create verification by @clemenshelm in #382
  • docs: add 'Instructions vs. Memory' guidance for agent authors by @clemenshelm in #387
  • docs(upgrading): prune dangling images after every upgrade (#370) by @clemenshelm in #388
  • fix(model-resolver): block *-preview models for tools+vision (#344) by @clemenshelm in #389
  • test(use-ws-runtime): consolidate parallel test suites by @clemenshelm in #390
  • feat(auth): warn when Domain Lock is set but BETTER_AUTH_URL is unset by @clemenshelm in #394
  • feat(providers): reject non-allowlist Ollama URLs at save time (#296) by @clemenshelm in #391
  • ci(integration): run suite against production Pinchy image (#196) by @clemenshelm in #395
  • feat: audit agent memory file changes (#345) by @clemenshelm in #405
  • feat: support .docx uploads in chat composer and pinchy-files by @clemenshelm in #398
  • feat(pinchy-docs): expose public docs URL so agents can cite it by @clemenshelm in #393
  • feat(pinchy-files): workspace read/write β€” pinchy_write tool, implicit pinchy_ls/pinchy_read by @clemenshelm in #384
  • ci: CI speedup β€” concurrency, caches, build-once-run-many, CodeQL by @clemenshelm in #401
  • feat(pinchy-odoo): disambiguate id vs default_code in tool surface (#377) by @clemenshelm in #402
  • feat(chat): detect Gemini 3 thought_signature errors and explain in UI by @clemenshelm in #400
  • refactor(providers): clickable docs hint + dedicated unit tests (#296) by @clemenshelm in #403
  • fix(telegram): notify restart state on channels.telegram mutation by @clemenshelm in #396
  • fix(audit): keep detail.success/error consistent with outcome on isError by @clemenshelm in #409
  • docs(v0.5.4): finalise pre-release notes and CHANGELOG pointer by @clemenshelm in #410
  • fix(audit): derive outcome from result.details.error too (#404 root) by @clemenshelm in #412
  • fix(composer): preserve cursor when editing mid-text (v0.5.4 regression) by @clemenshelm in #413
  • fix(composer): remove bespoke onChange wrapper, restore mid-text editing by @clemenshelm in #414

Full Changelog: v0.5.3...v0.5.4