# Changelog

All notable changes to this package are recorded here, in the
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format. The package
follows [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.9.0] - 2026-10-21

### Breaking

- Agent definitions now live under `<agent dir>/squire/agents/<name>/AGENT.md`,
  and only there. The old `<agent dir>/agents/<name>/AGENT.md` location is no
  longer read, with no fallback or dual discovery, so an agent left there is
  neither listed nor launched.
- Pi 1.3.0 or later is now required. The raised floor supports structured
  per-run system-prompt sections and `before_agent_start` preparation for runs
  started by an idle custom-message result, so parent guidance and roster
  metadata are current before each new run.

### Added

- Optional parent delegation guidance: Markdown in
  `<agent dir>/squire/delegation_prompt.md` is inserted unchanged into the
  `squire_parent_delegation` system-prompt section before each new run, only
  while the `delegate` tool is active and not hidden. Missing, empty or deleted
  files add no guidance; a read failure warns with the path, removes stale
  guidance for that run, and leaves delegation working. The package ships no
  default delegation or approval policy.
- The `delegate` tool description now refreshes from the current roster before
  each new run, including a run woken by an idle delegate result. A changed
  agent description, addition or removal shows up in the run being prepared;
  unchanged metadata is not re-registered.

### Changed

- The `delegate` tool now says to keep working or end the turn rather than poll
  or send a blocking mail request merely to wait for a result, while a
  substantive follow-up remains supported.

## [0.8.1] - 2026-10-20

### Fixed

- A 429 whose text says the endpoint is unavailable, such as OpenCode Go's
  `429: Endpoint is unavailable`, no longer marks the provider as
  usage-limited. It is an outage, not a used-up quota, so `/delegate` keeps
  launching on the provider instead of refusing it for five minutes or more.
  Pi has already retried the request by then. A running delegate that hits
  it moves to its next fallback model and carries on, as it does on a usage
  limit.

## [0.8.0] - 2026-10-20

### Added

- A delegate's footer shows that it is a delegate and whether auto-exit is
  on: `👦 delegate · auto-exit on`, or
  `👦 delegate · auto-exit off`. It follows the
  setting as typing turns it off and `/auto-exit` turns it back on.

### Changed

- The parent's footer reads `👨 N delegates running` instead of
  `⇄ N running`. Each side picks its icon at random per session: 👨 or 👩
  for the parent, 👦 or 👧 for a delegate. The footer tests in
  `test/delegate.test.ts` change to match.

## [0.7.0] - 2026-10-19

### Added

- `delegate`, `delegation_status` and `delegation_close` declare an
  `outputSchema` and return matching `structuredContent`, so a codemode
  script gets objects instead of text. `delegation_status` gives each
  delegation its derived `state` (`running`, `done`, `gone`, `closed`,
  `window-closed` or `unknown`), also added to its details.
  `delegation_close` returns one shape on every path:
  `{ id, windowId, windowName, closed, alreadyClosed, killed }`; its details
  gain the fields each path lacked. The model-facing text is unchanged.
- The three tools declare MCP-style `annotations`: `delegation_status` is
  read-only and idempotent; `delegation_close` is destructive and idempotent;
  `delegate` is neither, and is the only one that reaches an open world,
  since the delegate it starts calls a remote model and runs tools
  unattended.

## [0.6.0] - 2026-10-15

### Added

- Every delegate now starts with `PI_UNATTENDED=1` in its environment, a
  general signal that no human is watching the session. Delegates run as a
  full TUI, so extensions that open a dialog, such as a handoff gate or an
  approval prompt, waited on nobody and stalled the delegate; they can read
  this variable to skip the dialog instead. The test that pins the child's
  exact environment now includes it.

## [0.5.1] - 2026-10-14

### Changed

- The `delegate` tool's description now tells the parent to keep working or
  end its turn while a delegate runs, since the result starts its next turn
  on its own; the launch message says the same in place of pointing at
  `delegation_status`. GPT parents asked their own delegates for the result
  with `session_mail_ask`, so each report arrived twice.
- The `delegation_status` description now says to call it to check on a
  delegation that seems stuck or to find its files, not to wait for a result,
  which arrives as a message on its own.

## [0.5.0] - 2026-10-13

### Changed

- A delegate is now told to put its findings in its final message and to
  message the parent before then only with a question it cannot continue
  without. Delegates mailed progress and findings that the result then
  repeated, so the parent read the same report twice.
- A delegate's result now reaches a busy parent as a steer, after its current
  tool calls and before its next model call, instead of as a follow-up once
  its run ends. Queued until then, the result arrived after the parent had
  read the envelope itself and answered, and cost a redundant turn.
  `delegation_status` now says a done result arrives as a message on its own.
  `test/delegate.test.ts` pins `steer` in place of `followUp`.

## [0.4.0] - 2026-10-13

### Added

- A delegate whose run ends on a usage limit now switches to the agent's next
  fallback that has credentials and is not marked, and continues the task in
  the same session instead of failing. The parent passes the candidates after
  the launched model as `PI_DELEGATE_FALLBACK`; an explicit `model` passes
  none. A `done` result from such a delegate carries the `Usage limit:` line,
  saying it finished on a fallback.

### Changed

- A delegate message in the chat now shows collapsed, as its first two
  lines, and expands with the tool-output toggle (Ctrl+O), in step with tool
  output. The model still gets the whole message.
- With auto-exit on, a delegate whose run fails on any provider error now
  closes its window, as after a normal completion; before, only a usage-limit
  failure did. The parent still gets the `failed` reply and the session file
  stays. Only a run the user stopped keeps the window open.
  `test/auto-exit.test.ts` and `test/harness.test.ts` change with it.
- Refusing an explicit `model` that is usage-limited now names the agent's
  models that are still open, so the caller can omit `model` and let the
  fallbacks apply.

### Fixed

- ChatGPT's "The ChatGPT user has reached their Subscription Sharing usage
  limit" error now counts as a usage limit, so it records a mark and later
  launches skip the provider. Before, it recorded nothing.

## [0.3.2] - 2026-10-09

### Added

- Agent frontmatter `extensions` lists extension paths loaded only by that
  agent's delegate, each passed to its Pi as `-e` on top of its normal
  extensions. `~` expands and relative paths resolve against the `AGENT.md`'s
  directory; a path that does not exist leaves the agent out with a warning.
- Agent frontmatter `tools` lists the delegate's visible tools, passed as
  `--tools`. Without discovery tools such as `codemode` or `tool_search`, only
  listed tools are visible; Pi can keep unlisted MCP tools registered unless
  the list includes an `mcp__` entry, and discovery tools can expose them.
  Names are not checked against the parent's tools, and `exclude-tools` still
  applies alongside.

### Fixed

- The `delegate` tool treats `null` or the string `"null"` in `model`,
  `thinking` or `label` as unset, so the agent's defaults apply. Before, a
  `null` model crashed the call and `"null"` was refused as not provider/id.

## [0.3.1] - 2026-10-03

### Changed

- With auto-exit on, a delegate whose run ends on a usage-limit error now
  closes its window, as after a normal completion. The parent still gets the
  `failed` reply and the session file stays. Other failures still leave the
  window open. `test/harness.test.ts` now fails its run with a `400` instead
  of a `429`, since a `429` is a usage limit and now exits.

## [0.3.0] - 2026-10-03

### Added

- A delegate whose run ends on a usage-limit error (a quota message, or a
  `429` that survived Pi's retries) records a mark in
  `pi-squire-limits.json` in Pi's agent directory. Every Pi session then
  refuses to launch a delegate on a marked model until the mark clears.
- A usage-limit mark clears at the reset time in the failed response's headers
  (`retry-after`, `anthropic-ratelimit-*-reset`, `x-ratelimit-reset-*`) when
  Pi reports them, ahead of the time in the error text.
- An agent's `fallback:` frontmatter (comma-separated or a YAML list of
  `provider/id`) names models to launch on, in order, when its `model` is
  usage-limited or unusable. The tool result and the command notice name the
  substitute and the skipped model; the call is refused only when every
  candidate is skipped. An explicit `model` is never substituted.
- A `failed` result from a delegate that recorded a usage-limit mark gets a
  `Usage limit:` line after `Status:`, naming the scope and clear time.
  `delegation_status` ends with a `Usage-limit marks:` block of the active
  marks, and its details gain `marks`.
- `/delegate-clear <all|provider|provider/model>` removes usage-limit marks and
  their repeat-hit history. An argument matching no active mark changes
  nothing and lists the active marks. No tool can clear marks.
- Before a launch on a paid `opencode-go` model, pi-squire reads the OpenCode
  Go quota (cached for 60 seconds). A window at 100% or `rate-limited` skips
  the model and records a `proactive` mark that clears at the window's reset
  time. A failed reading never blocks a launch: the result and notice say
  "Could not read opencode-go quota: …; launched without a proactive check.",
  and `delegation_status` shows the error in `readingErrors` until a later
  reading succeeds. `/delegate-clear` also drops the cached reading. A proactive
  mark that cannot be written does not stop the launch.
- Before a launch, pi-squire reads Claude Code's usage cache for an
  `anthropic` candidate on a Claude subscription login and skips it when its
  5-hour or 7-day window is at 100%, or its model-scoped week is and names that
  model. A stale cache is refreshed in the background with
  `claude -p /usage`, at most every 5 minutes, without delaying the launch.
- A usage-limit mark clears early when a launch considers the marked
  `opencode-go` or OAuth `anthropic` candidate and a re-check (at most every
  10 minutes per provider) shows every window under 100% and a window cycle
  that began after the mark was recorded. Headroom alone never clears a mark,
  and a model-scoped mark on `opencode-go` never clears early.
- `delegation_status` shows how long ago a running delegate last wrote its
  session file, at the end of its `state:` line (`last active 12 min ago`),
  and adds the same moment to its details as `lastActiveAt`. A long gap may
  mean the delegate is stuck but does not prove it.

### Fixed

- A tmux call that hangs now fails after 10 seconds with
  `tmux <subcommand> timed out after 10 s`, instead of hanging `delegate`,
  `delegation_status`, `delegation_close` and the background poll.

## [0.2.2] - 2026-10-02

### Fixed

- The README links `CONTRIBUTING.md` on GitHub, since it is not in the npm
  package and its relative link 404s on pi.dev.

## [0.2.1] - 2026-10-02

### Fixed

- A delegate's task now arrives as a user prompt labelled `[delegate]` instead
  of a `[mailbox]` message. Pi 1.0.0 started the injected message's turn
  without the system prompt, so a fresh delegate saw no context files, skills
  or role paragraph until after its first tool call, and could reject them as
  an injection (earendil-works/pi#5581).

## [0.2.0] - 2026-10-02

### Breaking

- A model without configured credentials is refused, by `/delegate` and the
  `delegate` tool alike, instead of failing in the delegate's window.
- A reply counts as a delegation's result only when it comes from the delegate
  (`envelope.from` equals the delegation's id); a reply to its request from any
  other sender is left to the provider as ordinary mail.
- Providers must set `envelope.from` to the sender's address.
- The `reply()` fixture in `test/poll.test.ts` now sends the delegation id as
  `from` instead of `"d"`, to match.

### Changed

- The sources moved to `src/`, so load the package directory (`pi -e .`), not
  `index.ts`. Installs through npm or git are unaffected.
- Each check (a poll tick, a `delegation_status` call, a `delegation_close`
  call) asks tmux once, however many delegations run, instead of once per
  delegation.
- The appended system prompt reaches the delegate as a path to a private file
  under the system temporary folder, so its text stays off the command line
  and out of `ps`. A `session:launch` listener now sees that path where the
  prompt text was. The argv tests in `test/delegate.test.ts` read the prompt
  from the file instead of from argv, a deliberate behaviour change.

### Fixed

- `delegation_status` details name a labelled delegation's session
  (`scout-roster`), as its text does, instead of `<agent>-<first 8 of the id>`.
- A recycled tmux window id is no longer mistaken for a delegate's window, nor
  killed: a window counts as the delegate's only when its id and its name both
  match.
- A delegate whose program exited in a window kept open (`remain-on-exit`)
  reads as gone instead of running.

### Added

- `session:launch` payloads carry the agent's name as `agent`.
- The README's Install section names its requirements: tmux 3.0 or later, Pi
  0.80.5 or later, the first installable release with `agent_settled` (tested
  with Pi 1.0.0), macOS and Linux, and
  `pi install npm:pi-session-mail` as an example `message:*` provider.
- A README "Compatibility" section names the contracts other extensions rely
  on, the below-1.0 rule that breaking one bumps the minor version, and what a
  `message:*` provider must do.

## [0.1.0] - 2026-10-02

The first public release. Before this repository the package lived in its
author's dotfiles; the changes below are against that copy.

### Added

- Published to npm as `pi-squire`.
- MIT license and package metadata, including `engines` (Node 22.19 or later).
- CI on Node 22.19 and 24: lint, typecheck, tests, and a check of the
  published file list.

### Fixed

- The published file list includes `child.ts`, which `index.ts` imports.

[Unreleased]: https://github.com/gvanderclay/pi-squire/compare/v0.9.0...HEAD
[0.9.0]: https://github.com/gvanderclay/pi-squire/compare/v0.8.1...v0.9.0
[0.8.1]: https://github.com/gvanderclay/pi-squire/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/gvanderclay/pi-squire/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/gvanderclay/pi-squire/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/gvanderclay/pi-squire/compare/v0.5.1...v0.6.0
[0.5.1]: https://github.com/gvanderclay/pi-squire/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/gvanderclay/pi-squire/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/gvanderclay/pi-squire/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/gvanderclay/pi-squire/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/gvanderclay/pi-squire/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/gvanderclay/pi-squire/compare/v0.2.2...v0.3.0
[0.2.2]: https://github.com/gvanderclay/pi-squire/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/gvanderclay/pi-squire/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/gvanderclay/pi-squire/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/gvanderclay/pi-squire/releases/tag/v0.1.0
