Skip to content

v2.14.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 00:25
· 29 commits to main since this release
97b1ac8

[2.14.0] - 2026-10-15

Added

  • Value origin holds a call that sends, pays or posts to a recipient, payee or endpoint the agent only ever saw in content it read. With "value_origin": true in ~/.vaara/claude-code/config.json, the Claude Code hook remembers the email addresses, IBANs and URL hosts in external tool results (web fetch and search, MCP tools, and files read outside the project and outside the agent's own configuration directories) and in the user's prompts, the latter through a new UserPromptSubmit hook (vaara hook user-prompt-submit, registered by vaara init and the plugin). It stores only HMAC-SHA256 digests, under a per-install key. A call to a tool whose name sends, pays or posts (send_email, create_payment, postMessage) is held for a human when a recipient, payee or endpoint argument carries a value seen only in external results. The reason reads value origin: iban first seen in the result of mcp__mail__read_email (action ...), and the decision record carries value_origin, mapping each held argument to the tool and the action ids it was seen in. Values the user typed, values listed in trusted_values, and values never seen pass; authority_args declares more arguments per tool. InterceptionPipeline.intercept takes value_origin for other integrations, and an approval of the same call still replays. Off by default: replayed over 34,601 calls in 256 sessions recorded by the maintainer, it held 92 of 99 mail sends, all of them intended, whose recipients came from web pages, local files and a mail allowlist tool's results.
  • A decision that completes a sequence pattern names the earlier actions that matched. The decision record carries sequence_steps, mapping each matched pattern to the action ids of its earlier steps, oldest first, so an assessor can go from the decision straight to the actions that changed it. This also holds when each call runs in a fresh process, as a host hook does: the session's earlier actions are replayed from the trail with their ids. The scorer receives the call's action_id in its context, and seed_session accepts (tool_name, action_name, parameters, action_id) entries alongside the existing three-field form. A record with no matched sequence gains no key.
  • vaara trail export-ocsf (--trail FILE.jsonl | --db AUDIT.db) --out FILE.jsonl [--hostname NAME] writes each decision on a trail as an OCSF 1.9.0 API Activity event (class 6003) with the security_control and ai_operation profiles, one per line, for SIEMs and data lakes that ingest OCSF. The tool name is api.operation and the agent is ai_agent, with the session as ai_agent.instance_uid. allow maps to action Allowed and disposition Allowed, deny to Denied and Blocked with the recorded ground as status_detail, and escalate to action Other "Escalate" with disposition Delayed. severity_id follows the risk score, policy.uid is the decision's policy_digest, and metadata.uid and unmapped.vaara.record_hash name the trail record. The events carry no attestation_list: an OCSF attestation signs the OCSF event itself, and integrity stays with the Vaara receipt and trail the event names.
  • The trail_decision_v0 vectors include a decision written after its action request under a described engine state, so its receipt carries argumentsCommitment and policyDigest, with companion files a third party checks without the trail: the arguments disclosure that opens the commitment, one with changed arguments that does not, and the engine state behind the digest. companions.json indexes them, and _check_independent.py recomputes the commitment and the digest from the standard library, rfc8785 and cryptography alone. expected.json still lists receipts only.
  • Every decision names the decision state it was made under. The pipeline describes what it decides by (Vaara version, enforce or shadow, the scorer's thresholds, sequence patterns and loaded policy, and session authority settings) as a vaara.engine-policy/v0 object, and each decision record carries policy_digest, the SHA-256 of its RFC 8785 form; the first decision after the state changes also carries the state as policy_state, so the trail explains its own digests. Decision receipts carry the digest as policyDigest (SPEC 5.10). Learned state such as calibration is not part of it. A state that cannot be described leaves the record without the keys and never blocks a decision.
  • Decision receipts name the arguments a decision was made over without carrying them. Each action request on the trail records a fresh 16-byte salt and arguments_commitment, the SHA-256 of a domain tag, the salt and the RFC 8785 form of the recorded arguments, and the decision's receipt carries it as argumentsCommitment (SPEC 5.10). vaara receipt disclose-arguments RECEIPT [--db TRAIL] [--out FILE] writes the arguments and salt that open one receipt, read from its trail, and vaara receipt verify-arguments RECEIPT --disclosure FILE checks the receipt's signature and that the disclosure opens its commitment, without the trail. Each request has its own salt, so a disclosure opens one decision and no other, and a guessed argument cannot be confirmed from a receipt alone.

Fixed

  • A deny on a scorer verdict outside the decision vocabulary was recorded with the scorer's own reason only, so a refusal spelled in another vocabulary and no answer at all read the same on the trail. The reason now starts unrecognised decision 'VALUE', with the value as received cut at 64 characters, or no decision, followed by the scorer's reason.
  • The default gate refused a write to six git hooks and nothing else that makes git run a program. It now refuses every hook git runs (commit-msg, post-rewrite, reference-transaction, fsmonitor-watchman and the rest), a write to .git/config, .git/config.worktree, ~/.gitconfig or ~/.config/git/config, and a shell command that sets a key which runs a program (core.fsmonitor, core.pager, core.editor, core.sshCommand, core.hooksPath, diff.external, sequence.editor, pager.<cmd>, diff, merge and filter drivers) through git config or git -c, or writes into .git/config or .git/hooks. Each of these runs on ordinary commands such as git status and git diff. The values cat, less, more, true, false, : and /dev/null pass, so git -c core.pager=cat log and git -c core.hooksPath=/dev/null commit are not held, and reading or unsetting a key is not held. VAARA_ALLOW_GIT_EXEC_CONFIG=1 lifts both new rules. Replayed over 80,708 distinct calls recorded on the maintainer's trails, the rules refused four, each a script that carries these commands as text.
  • The vaara-governance plugin's bundled deny matcher, which runs when no vaara binary is on PATH, matched path rules against Windows paths with their backslashes, so a write to C:\Users\h\.ssh\authorized_keys or to the harness settings file passed every path rule. It now folds \ to / on file_path, notebook_path and uri, as the package matcher does.
  • A sequence pattern that raised a call's risk without holding it was recorded only as the sequence_pattern signal value, so the trail could not say which pattern had matched. The decision reason now ends ; sequence NAME matched for each pattern that matched; a pattern that holds the call is named once, as before.
  • vaara trail export-incident counted the Article 73 reporting deadline from the trigger record's time. Article 73 counts from the provider's awareness, which comes after the event once a causal link is established or reasonably likely. The incident metadata takes an optional awareness_timestamp (ISO 8601 with a zone, or epoch seconds), the deadline runs from it, and the report's timeline carries awareness_timestamp and deadline_basis (awareness or trigger_record). Without it the trigger time is used as before. An awareness time earlier than the trigger record is refused.