Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ All notable changes to `@fusengine/harness`. Format: [Keep a Changelog](https://

## [Unreleased]

## [0.1.103] - 2026-10-08

### Added

- **Motion G1 stills approval gate** (`src/policy/motion/gates.ts`, `src/runtime/motion/`, `src/runtime/handle.ts`) — the new `motion` hook scope (`hook <host> motion`) for fuse-motion denies draft renders without owner approval bound to the SHA-256 of `.motion/stills/contact.png`.
- **Measured multi-host motion support** (`src/runtime/motion/host.ts`, `src/runtime/motion/reply.ts`, `src/runtime/motion/files.ts`, `src/runtime/motion/index.ts`, `src/runtime/motion/pre.ts`, `src/runtime/motion/post.ts`, `src/runtime/motion/prompt.ts`) — native payloads against `dist/cli/bin.mjs` on 2026-10-08 passed draft refusal, store-write refusal, prompt approval recording and approved render in a new session for claude-code, codex, cursor, kimi, gemini-cli, cline and hermes, with native deny responses. Cursor blocks writes in `preToolUse`, not post-write `afterFileEdit` (response `{}`); opencode remains untested. These measurements do not establish live host application, G2, critic lifecycle or budget parity.
- **Motion G2 draft approval gate** (`src/policy/motion/gates.ts`, `command.ts`) — master renders and ffmpeg writes to declared master outputs require approval of the draft's exact SHA-256; an artifact changed since approval is denied as stale and needs re-approval.
- **Owner-only motion approvals** (`src/policy/motion/approve-prompt.ts`, `approvals.ts`, `src/runtime/motion/prompt.ts`) — an exact `MOTION-APPROVE <stage> <code>` entered through the host's native prompt event grants only a matching pending request and current artifact hash; sub-agent payloads and automated envelopes cannot approve. An explicit refusal cancels the session's pending requests.
- **Motion self-approval guard** (`src/policy/motion/self-approval.ts`, `src/policy/guards/protected-path.ts`) — agent writes to `~/.fuse-harness/motion/` and `~/.fuse-harness/motion-keys/` are denied through write tools and Bash; both paths are also protected by the shared path guard **for every hook scope** (the only behavior change outside `motion`; reads stay allowed).
- **Signed approvals** (`src/policy/motion/auth-key.ts`, `auth-approval.ts`, `state-witness.ts`) — approvals are HMAC-SHA256 signed with a per-project `0600` key in `~/.fuse-harness/motion-keys/`; unsigned records are ignored, and keys or approvals the harness's own registry never recorded (planted before first observation) are not trusted.
- **Motion state verification** (`src/policy/motion/state.ts`, `state-files.ts`, `src/runtime/motion/post.ts`, `index.ts`) — after every tool call and at Stop, a draft/master not produced by an authorized render in that same call is moved to `.motion/quarantine/` (never deleted); key, store or `project.json` changed outside the harness invalidates every approval of the project. Artifacts found at first observation stay in place but never count as approved; an authorized render's partial output is left alone while it runs.
- **Motion detach guard** (`src/policy/motion/guard-detach.ts`) — in a motion project, Bash commands that detach a process (`nohup`, `setsid`, `disown`, trailing `&`, `at`, `launchctl`) are denied so writes cannot be deferred past the state check.
- **Motion approval forgery guard** (`src/policy/motion/forgery.ts`) — Bash commands and CronCreate, ScheduleWakeup, RemoteTrigger or SendMessage inputs carrying `MOTION-APPROVE` are denied, preventing agents from relaying the approval phrase through scheduled or cross-session prompts.
- **Sensitive motion-source write scan** (`src/policy/motion/sensitive.ts`, `src/runtime/motion/pre.ts`) — checks the full Write/Edit/MultiEdit content under the motion source directory for terms from `~/.fuse-harness/motion/banned.txt` or currency amounts (shell positional parameters such as `$1` are not amounts); detected values are denied and reported masked.
- **Motion critic sandbox** (`src/policy/motion/critic.ts`, `critic-flag.ts`, `src/runtime/motion/lifecycle.ts`) — read-only checks for `motion-critic` deny write tools, limit Read to media or the review pack, and limit Bash to ffprobe/ffmpeg/ls/stat; ffmpeg output arguments must stay in the review directory, and shell redirections must stay there or target `/dev/null`. SubagentStart activates the checks and SubagentStop clears them by agent id.
- **Motion render budget** (`src/policy/motion/budget.ts`, `src/runtime/motion/pre.ts`, `post.ts`, `host.ts`) — reserves a render at PreToolUse, counts it once at PostToolUse and releases it on tool failure, with wall time per stage; correlation uses each host's native call id (Kimi `tool_call_id` included), session-scoped and collision-free. `FUSE_MOTION_MAX_MASTER_RENDERS` denies further master renders when its configured cap is reached (unset or invalid means no cap). A corrupt budget is never repaired automatically. Known limit: a render cancelled with no terminal event keeps its reservation (no time-based expiry).
- **Motion approval-store failure handling** (`src/policy/motion/store.ts`, `gates.ts`, `src/runtime/motion/index.ts`) — missing or corrupt approval state denies gated renders, and failure to save a pending request never turns that deny into an allow; other internal exceptions fail open with a diagnostic on stderr.

### Changed

- **Internal hook payload helpers extracted** (`src/runtime/handle-payload.ts`, `src/util/record-object.ts`) — `rawEventName` and `cursorRawPayloadProjection`, with their parsing helper, moved out of `handle.ts` without changing their behavior; `segments`, `tokenize` and `REFUSAL_RE` are exported unchanged for reuse by `motion`.

### Fixed

- **CONFIRM multi-pending, visible ack** (`src/runtime/confirm/` — `codex-confirm-state.ts` (new), `confirm-outcome.ts` (new), `confirm-feedback.ts` (new), `codex-confirm.ts`, `confirm-pending.ts`, `confirm-state.ts`, `confirm-submit.ts`, `confirm-gate.ts` — `src/runtime/handle.ts`) — a session now keeps up to 10 pending CONFIRM codes (oldest evicted): a new denial no longer erases a code already given, an armed token survives the denial of an unrelated command, a pending code NEVER expires and survives arming on the shared path (an expired token re-arms by retyping the code), and a refusal drops only the armed tokens there (codex clears its pending too, as before); the legacy single-slot state files are still read. A prompt that is exactly `CONFIRM <code>` is acked on kimi/codex (`accepté pour : … (valable 5 min)` / `inconnu — codes en attente : …`); an incidental code inside a longer prompt arms silently, byte-identical output. Tests: `confirm-multi-pending`, `confirm-multi-state`, `confirm-feedback`, `confirm-codex-legacy`.

## [0.1.102] - 2026-10-05

### Fixed
Expand Down
Loading
Loading