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
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Initialize CodeQL
uses: github/codeql-action/init@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
languages: javascript-typescript
- name: Analyze
uses: github/codeql-action/analyze@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

## Unreleased

## 0.10.0 — 2026-09-06

- Fix the plan-mode guard failing open on the first command of a session. OpenCode runs `command.execute.before` before any chat hook for the turn and its `Session` record carries no `agent` field, so the session-record fallback introduced in 0.9.0 could never learn the agent for a `/goal <objective>` sent as a fresh session's first turn: the goal started live and the routed text told the model to begin work. The routed turn itself carries the agent, so `chat.message` now re-evaluates the planning-only restriction for the goal that command created and, when the agent is restricted, holds the goal (`plan agent active`, budget preserved), rewrites the turn as a read-only control turn, and blocks tools for it. Reproduced, and verified fixed, against live OpenCode 1.18.25 and 1.18.29 over the HTTP `command` API with `agent: "plan"` and a `goal` command that does not pin an agent; the case where an earlier turn had already reported the agent was unaffected. Note that OpenCode runs a command under its configured `agent` when one is set (`cmd.agent ?? input.agent`), so with the README's `"agent": "build"` the `/goal` turn executes as `build` and a hold can only come from a previously reported planning-only agent; the README's plan-mode section now spells this out. Pinned by a new mutation-contract entry.
- Fix the `sessionTitleStatus` indicator leaving a stale `▶` running line on the session after the goal completed. Completion archives the goal, and the title sync returned early with no live goal, so the last running render stayed until `/goal clear`. A completed goal now renders as `✅ <objective> · N turns · <elapsed> · <tokens>`; `/goal clear` still restores the captured original title. Reproduced against live OpenCode 1.18.25 and 1.18.29.
- Treat a fenced ```` ```span``` ```` in `/goal` arguments as literal objective text: double-dash tokens inside it are no longer parsed as goal flags (previously `/goal run pytest --maxfail=1` rejected the command with `Unsupported flag: --maxfail`), a fence is never consumed as a flag value, and the backticks are stripped from the stored objective. Ported from [@NiklasTR](https://github.com/NiklasTR)'s fork.
- Add a cost cap: the `maxCostUsd` plugin option and `--max-cost` per-goal flag pause a goal, with the usual final wrap-up prompt, once the cumulative API cost OpenCode reports for the session reaches the cap (`stopReason: max cost reached ($X.XX)`). `/goal status` shows `Cost budget: $spent/$cap`, the continuation `<progress_budget>` carries `cost_remaining_usd`, the near-limit warning fires within 10% of the cap, and the `goal_set`/`set_goal` tools accept `maxCostUsd`. An unknown provider cost never trips the cap and a single response may overshoot it; `/goal resume` opens a fresh window. Idea and first implementation by [@NiklasTR](https://github.com/NiklasTR).
- Add `agentGoalAuthority`. The default `"full"` is unchanged; `"status"` keeps the agent-facing tools to reporting: `goal_set`/`set_goal` refuse to replace an active goal (returned as an `agent_authority` failure envelope on the canonical tool), `update_goal` refuses objective changes, and `clear_goal` refuses to clear, so a model can no longer rewrite the objective the user gave it. Completing, blocking, pausing, resuming, and creating a goal when none is live remain allowed. Prompted by [@felores](https://github.com/felores)'s fork, which removed those abilities outright.
- Make the `simultaneous stale observers` lease test deterministic: attach its settle handler before the barriers so an early loser's rejection is never reported as unhandled, and never wait at a barrier for an acquirer that already settled. This was the known-flaky Windows CI test; it also failed locally under load.
- Update the bundled `zod` dependency from 4.4.3 to 4.5.4 and the CodeQL action to v4.37.9 (supersedes dependabot #60 and #69).
- Rewrite the install docs around how OpenCode actually loads plugins. `npm install` was never needed; the config entry is what installs the package. More importantly, OpenCode resolves an unpinned `"opencode-goal-plugin"` to `@latest` once, caches it under `~/.cache/opencode/packages/opencode-goal-plugin@latest/`, and never re-resolves while that directory exists, so users who followed the old instructions are frozen on whichever version they first installed. The README now pins the version, documents the upgrade path (bump the pin, or delete the cached directory), mentions `opencode plugin <name>@<version>`, and notes that a `file://` checkout needs its `node_modules`. `npx opencode-goal-plugin` (the bundled verify script) warns when OpenCode's unpinned cache lags the package. Also documents `ledgerFilePath` and adds an OpenCode 1.18.29 compatibility row from a deterministic-provider canary.

## 0.9.0 — 2026-08-29

- Add plan-mode safety: a goal set while a planning-only agent is active is recorded but held instead of starting, with stop reason `plan agent active`, its budget preserved, and a read-only control turn that tells the model not to begin work. Auto-continue already paused when the session switched to Plan; this closes the creation path, where the goal previously started and ran the loop before any idle occurred. The active agent is read from the host's execution context with a fallback to the session record, which matters because `command.execute.before` runs before `chat.message`/`chat.params` — without the fallback the restriction failed open on the first command in a session. Configurable via `restrictedAgents` (default `["plan"]`) and `allowGoalExecutionFromPlan` (default `false`); the default-on behavior is pinned by the mutation contract. Note the restriction stops the goal loop, not the single routed command turn, which OpenCode does not fully intercept.
Expand Down
Loading