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
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

- The planning agent no longer re-litigates the thread's scope root on every turn. The root is chosen once, when the thread is created, and nothing in the UI moves it afterwards — so an agent that opens each reply by judging whether the directory suits the work is spending the turn on the one thing the engineer cannot act on, and has already read. The system prompt now states that the root is fixed and off the table, and a thread with any history is told that what it has already established stands and should not be restated.

- The planning pane keeps your message on screen while the backend answers it. The turn task owns the thread and only writes it on completion, so between pressing Enter and the reply landing the text existed nowhere the pane drew: cleared from the input, not yet in the transcript, with only the thinking spinner where it had been. It read as though the pane had swallowed the message, or errored. The in-flight message is now shown as a normal `you` turn until the real one is loaded back from disk — and it is dropped, not duplicated, when the reply arrives or the draft is returned to the input after a failure.

- The orchestrator's system prompt is built from the tools the active persona actually has. Every persona used to get the same three opening paragraphs — "use your tools to inspect sessions, start new ones, type into them, and wire pipes between them", the pause/kill explanation, and "for WAITING, ERROR and DEAD events you MUST report" — with a single corrective sentence appended at the very bottom, after the memory dump. In a read-only persona like `assistant` all of that describes tools it was never offered and events it never receives, and the model reached for them anyway. The capability sentence is now generated from the allowlist, the pause/kill and event paragraphs appear only when they apply, and the persona note sits with the description it qualifies instead of at the end.
- A persona no longer silently discards a setting you wrote yourself, and the most recent instruction wins. `Persona::apply` overwrote unconditionally, so with `persona = "orchestrator"` in the config an explicit `approval = "propose"` was replaced by the persona's `"auto"` and the gate you asked for never existed. Now: the persona named in the config leaves explicitly-set `[orchestrator]` keys alone (both come from the same file, and the spelled-out key is the more specific one) and says in the chat pane which of its settings it declined to apply; `/persona <name>` and the Orchestrator menu at runtime override everything, because you are asking for that persona right now. The same ordering makes a runtime Approval toggle stick until you next swap personas.
- A persona picked at runtime now survives an orchestrator restart. `start_orchestrator` re-read `[orchestrator].persona` and layered it over whatever you had chosen, so Stop/Start or "Restart To Apply Changes" silently reverted the pick — and those rows sit in the same menu as the Persona row, one keystroke away, with Restart being the action the menu itself prompts for after any other change. Switching from the menu therefore looked like it did not take, while `/persona` appeared to work only because you rarely restart right after using it. The config persona is now applied only when the user has not chosen one; "Reload Config From Disk" is still a deliberate reset back to the file.
- An unrecognized `[orchestrator] approval` value now gates instead of granting full autonomy. Only the literal `"propose"` ever turned the gate on, so `"auto_approve"`, `"approve"` or any typo silently meant "run every tool call immediately" — the failure mode you cannot see. Anything that is not `"auto"` is now treated as `"propose"`, and the bad value is reported in the chat pane at orchestrator start and by `linkshell doctor`.

- Fixed Claude permission dialogs never registering as WAITING — so the session bar stayed on RUNNING, the chat pane never surfaced the request, and the orchestrator was never woken for it. The dialog itself matched, but Claude draws its input hint (`⏵⏵ accept edits on`) *below* the box in the same repaint, and that trailing line reads as RUNNING; with no JSONL watcher reporting for the session, RUNNING was allowed to clear the pattern-detected dialog milliseconds after it appeared. RUNNING now never releases a dialog — only positive evidence that it is gone (READY/THINKING, or a watcher report) does, which is what the watcher path already did.

- oh-my-pi sessions no longer flap between THINKING and READY every couple of seconds. omp animates its spinner in an unterminated tail rather than completing lines, and the idle timeout that reverts a stalled session to READY was fed only by complete lines — so a session that was visibly working looked idle after 2s, got flipped back to THINKING by the next spinner frame, and oscillated for the whole turn. A *changed* tail now counts as output; an unchanged one (the PTY reader re-sends it once a second) still does not, so a session whose screen has genuinely stopped moving idles out as before.
- oh-my-pi sessions get a real stats watcher (`src/omp_log.rs`), reading the per-turn `usage` records omp writes to `~/.omp/agent/sessions` (honouring `OMP_HOME`) instead of scraping the screen — so tokens, cost, context, and model are the ones omp itself recorded. `/new` starts a fresh transcript file, which is why token tracking used to reset to zero mid-session; the watcher follows the roll and carries the previous transcript's totals forward. Context, being a snapshot of the live prompt rather than a total, does follow `/new` down.
- Stacked output panes now have a visible edge between them. The left bar separates side-by-side panes, but vertically split panes butted one pane's last row of output directly against the next pane's title with nothing in between. Each pane's title row now doubles as its top border: a rule runs from the end of the title to the right margin. No row of output is spent on it.
Expand Down
29 changes: 27 additions & 2 deletions docs/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ accept `~`).
is just a slow tool — its context stays coherent, which matters for local
models. While a proposal is pending the orchestrator processes nothing
else; incoming events coalesce into its next turn.
`"auto"` and `"propose"` are the only values. Anything else — `"approve"`,
`"auto_approve"`, a typo — is treated as `"propose"` and reported in the
chat pane and by `linkshell doctor`, so a misspelling can never quietly
hand the agent full autonomy.
- `auto_approve` (default `["list_sessions", "read_output", "use_skill"]`) —
tools that skip the gate. The default set is read-only, so routine
observation stays fluid and only session-mutating calls (`start_session`,
Expand Down Expand Up @@ -152,7 +156,24 @@ Local LLMs require `endpoint` and `model`; `system` and `api_key` are optional.
## `[[personas]]`

Named behavioural presets layered over `[orchestrator]`. Every field is
optional; omitted fields inherit from `[orchestrator]`. Set
optional; omitted fields inherit from `[orchestrator]`.

Precedence is "whatever you asked for most recently":

- The persona named by `[orchestrator].persona` leaves keys you wrote
explicitly under `[orchestrator]` alone — both come from the same file, and
the key you spelled out is the more specific of the two. The chat pane says
which of its settings the persona therefore did not apply.
- `/persona <name>` at runtime, and the Orchestrator menu's Persona row,
apply the persona in full: you are picking it now, so it overrides the
config file and any earlier runtime tweak.
- A runtime field change (the menu's Approval row, say) holds until the next
persona swap.
- A persona chosen at runtime survives stopping, starting and restarting the
orchestrator. "Reload Config From Disk" is the deliberate way back to
`[orchestrator].persona`.

Set
`[orchestrator].persona` to pick the one applied at startup, or switch at
runtime with `/persona <name>` (history is preserved).

Expand All @@ -165,7 +186,11 @@ persona cannot turn correctness off.
- `approval`, `auto_approve` — propose-mode gating.
- `allowed_tools` — tools present in the schema at all. `list_sessions` is always kept.
- `max_tool_iterations`, `tool_dedup_secs`, `max_context_tokens`, `event_tail_lines`.
- `note` — appended to the system prompt under a `## Persona` heading.
- `note` — added to the system prompt beside the capability description it
qualifies. The rest of that description is generated from `allowed_tools`:
a persona without `send_input` is not told it can type into sessions, and
one with `events = []` is not given the "you must report every WAITING
event" rule.

Builtins: `assistant` (reactive, read-only, propose), `monitor` (watches and
reports, writes gated), `orchestrator` (acts autonomously, tightest dedup
Expand Down
Loading
Loading