Skip to content

feat(acp): pi-ACP model picker and model-aware thinking levels#340

Open
julianromli wants to merge 1 commit into
gnoviawan:devfrom
julianromli:feat/pi-acp-thinking-model-refine
Open

feat(acp): pi-ACP model picker and model-aware thinking levels#340
julianromli wants to merge 1 commit into
gnoviawan:devfrom
julianromli:feat/pi-acp-thinking-model-refine

Conversation

@julianromli

@julianromli julianromli commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add ACP session/set_model end-to-end (Rust command, store, Model chip + slash menu).
  • Model-aware thinking levels via a client-side resolver (acp-thinking.ts) that filters levels from model reasoning / thinkingLevelMap, with safe fallback to the agent's advertised modes when metadata is absent.
  • Hide reasoning UI when thinking is off; auto-clamp thinking level when the model changes.

Scope change (rev 2): the interim vendored pi-acp@0.0.27-termul.1 fork has been removed. It created a non-resolvable npx package spec and surfaced a vendored-bundle CodeQL finding. The client-side resolver remains as defense-in-depth and will take full effect once upstream pi-acp#51 forwards reasoning/thinkingLevelMap. Termul now launches the official pi-acp@0.0.27.

Related Issue

Relates to #339

Upstream pi-acp refinement tracked in:

Type of Change

  • feat: new feature
  • fix: bug fix
  • docs: documentation only
  • style: formatting or non-functional styling changes
  • refactor: internal cleanup with no behavior change
  • perf: performance improvement
  • test: adds or updates tests
  • build: build or dependency changes
  • ci: CI/CD workflow changes
  • chore: maintenance or tooling
  • revert: reverts a previous change

What Changed

Backend

  • Enable unstable_session_model on agent-client-protocol.
  • acp_set_session_model command; session responses include modes, models, configOptions.
  • SessionCreatedEvent includes models; serialization test asserts models is omitted when None.

Frontend

  • ModelChip in agent header; /model slash commands.
  • acp-thinking.ts filters levels from model reasoning / thinkingLevelMap; clamps on model change; returns null (trust agent modes) when model metadata is unavailable.
  • Timeline filters thought chunks when thinking is off.
  • Slash menu doc comment reflects actual section order (Skills → Commands → Models → Config).

How It Was Tested

  • bun run ci (biome)
  • bun run typecheck
  • bun run test (150 files, 1704 tests)
  • cargo test --lib acp (37 passed, 1 ignored)
  • Manual verification completed

Note: cargo clippy -- -D warnings surfaces 6 pre-existing const_is_empty errors in src-tauri/src/lib.rs (git_bash_paths::*) that exist on upstream/dev and are outside this diff. Not addressed per the repo's no-unrelated-changes rule.

Checklist

  • My PR title follows the conventional commit format used by this repo
  • I linked the related issue or explained why none exists
  • I updated docs when needed
  • I added or updated tests when needed
  • I verified the change does not introduce unrelated modifications

Summary by CodeRabbit

Release Notes

  • New Features
    • Added model selection capability for ACP agent chat sessions, allowing users to switch between available models during a conversation.
    • Introduced thinking visibility controls to toggle whether the agent's reasoning steps are displayed in the chat timeline.
    • Enhanced session state management to track and surface available model options and configurations.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds per-session model selection to Termul's ACP agent chat. The backend gains SessionStateOutcome (modes + models + config options), a SetSessionModel command, and the unstable_session_model Cargo feature. The frontend adds ModelInfo/SessionModelState types, a new acp-thinking module for thinking-level resolution and visibility gating, store integration for model state, and UI components (ModelChip, slash-menu model section, buildTimeline showThoughts option). A UX recommendation spec for the overall agent chat experience is also included.

Changes

ACP Session Model and Thinking

Layer / File(s) Summary
Backend protocol and SessionStateOutcome
src-tauri/Cargo.toml, src-tauri/src/acp/manager.rs, src-tauri/src/acp/commands.rs, src-tauri/src/acp/events.rs, src-tauri/src/lib.rs
Enables unstable_session_model Cargo feature; defines SessionStateOutcome and SetSessionModel command; updates load_session, resume_session, and new_session driver handlers to return enriched state including models; adds acp_set_session_model Tauri command and registers it in the handler.
Frontend API bridge and type definitions
src/renderer/lib/acp-api.ts, src/renderer/lib/acp-api.test.ts
Defines ModelInfo, SessionModelState; adds optional models to SessionStateOutcome and SessionCreatedEvent; changes acpLoadSession/acpResumeSession to return Promise<SessionStateOutcome>; adds acpSetSessionModel wrapper and extends acpApi facade.
Thinking level resolution and visibility helpers
src/renderer/lib/acp-thinking.ts, src/renderer/lib/acp-thinking.test.ts
New module with ThinkingLevel ordering, getSupportedThinkingLevels, clampThinkingLevel, resolveSessionModes (null for non-reasoning models), and isThinkingVisible (gates thought block rendering). Full test coverage including xhigh clamping and mode resolution.
Store session state and model updates
src/renderer/stores/acp-store.ts, src/renderer/stores/acp-store.test.ts
AcpSession gains models field; withSessionState helper merges backend state; createSession, load, and resume strategies propagate models; setSessionModel action updates models, resolves effective modes, and syncs mode to backend; _onMessageChunk filters thought chunks via isThinkingVisible; _onModeUpdate uses resolveSessionModes.
Chat UI components for model selection and thinking
src/renderer/components/chat/AgentChatPanel.tsx, src/renderer/components/chat/AgentHeader.tsx, src/renderer/components/chat/ChatInputBar.tsx, src/renderer/components/chat/SlashCommandMenu.tsx, src/renderer/components/chat/chat-timeline.ts, src/renderer/components/chat/chat-timeline.test.ts, src/renderer/components/chat/slash-menu-model.ts, src/renderer/components/chat/slash-menu-model.test.ts
AgentChatPanel computes showThoughts and wires setSessionModel; AgentHeader exports ModelChip popover selector and updates ModeChip with resolveSessionModes; ChatInputBar adds onSetSessionModel prop, renders ModelChip, and handles sessionModel slash items; chat-timeline gains showThoughts filter option; slash-menu-model adds SlashSessionModelItem and session-models section.
UX recommendation specification
plans/ux-recommendation-spec.md
Adds a draft UX spec covering baseline behavior, onboarding, launcher states, AgentChatPanel chip/thinking policies, tool/permission UX, multi-tab semantics, history/continuity, settings placement, copy guidance, implementation priority table, and pi-ACP reference flows.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Chat UI
  participant AcpStore
  participant acpApi as ACP API
  participant TauriCmd as Tauri Command
  participant DriverThread as Driver Thread

  UI->>AcpStore: setSessionModel(sessionId, modelId)
  AcpStore->>AcpStore: update session.models.currentModelId
  AcpStore->>AcpStore: resolveSessionModes(modes, models)
  AcpStore->>acpApi: setSessionModel(agentId, sessionId, modelId)
  acpApi->>TauriCmd: invoke acp_set_session_model
  TauriCmd->>DriverThread: SetSessionModel command
  DriverThread-->>TauriCmd: Ok(())
  alt effective mode changed after model switch
    AcpStore->>acpApi: setMode(newMode)
  end
  AcpStore-->>UI: session state updated

  Note over UI,AcpStore: Incoming thought chunks filtered<br/>by isThinkingVisible(modes, models, configOptions)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Poem

🐇 A chip appears, a model blooms,
The thinking levels fill the rooms!
From xhigh down to off we clamp,
No thought unchecked, no timeline damp.
The rabbit wired the store with care —
hop hop, new models everywhere! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main changes: adding pi-ACP model picker support and implementing model-aware thinking level controls.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread vendor/pi-acp-termul/dist/index.js Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src-tauri/src/acp/events.rs (1)

253-266: ⚡ Quick win

Add assertion for models field omission in serialization test.

The test session_created_omits_none_fields verifies that modes and configOptions are omitted from JSON when None, but it does not assert the same for the new models field. For completeness, add:

     assert!(value.get("modes").is_none());
+    assert!(value.get("models").is_none());
     assert!(value.get("configOptions").is_none());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/acp/events.rs` around lines 253 - 266, The test
session_created_omits_none_fields in SessionCreatedEvent serialization is
missing an assertion that the new optional models field is omitted when None;
update the test by adding an assertion similar to the existing ones (e.g.,
assert!(value.get("models").is_none())) after the other omission checks so
SessionCreatedEvent (constructed with models: None) verifies models is not
present in the serialized JSON.
src/renderer/components/chat/slash-menu-model.ts (1)

98-105: ⚡ Quick win

Update the doc comment to reflect the actual section ordering.

The comment describes the order as "Commands first, then each config option..." but the implementation renders sections in this order: Skills → Commands → Models → Config/Modes. The comment should mention both Skills (which come before Commands) and the new Models section (which comes between Commands and Config).

📝 Suggested doc comment update
 /**
  * Build ordered menu sections from the active session's ACP state.
  *
- * Order: Commands first, then each config option as its own section (preserving
+ * Order: Skills first (if present), then Commands, then Models (unstable
+ * session model API, if available), then each config option as its own section (preserving
  * the agent's array order). When `configOptions` is non-empty, the legacy
  * `modes` section is omitted entirely (precedence). When it is empty, a single
  * legacy Modes section is emitted if modes exist.
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/chat/slash-menu-model.ts` around lines 98 - 105,
Update the doc comment that currently describes section ordering: change it to
state the actual order is Skills → Commands → Models → Config (or Modes), noting
that Skills appear before Commands and the new Models section sits between
Commands and Config; preserve the existing rule that when configOptions is
non-empty the legacy Modes section is omitted and when empty a single Modes
section is emitted. Ensure the comment above the menu-section builder mentions
Skills and Models explicitly and keeps the note about preserving the agent's
array order for config options.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/renderer/assets/agent-icons/acp/agents.json`:
- Around line 650-659: The npx package string for the pi ACP agent ("id":
"pi-acp") is set to an unavailable version (distribution.npx.package =
"pi-acp@0.0.27-termul.1") which acp-registry.ts will invoke as `npx -y ...` and
fail; fix by updating distribution.npx.package to a resolvable npm version
(e.g., "pi-acp@0.0.27") or change the distribution type away from npx (use a
uvx/binary archive URL) or publish the missing "0.0.27-termul.1" tag to npm so
acp-registry.ts can successfully launch the agent.

In `@vendor/pi-acp-termul/README.md`:
- Line 154: Typo in the README: the entry for `/follow-up` currently reads
"`/follow-up` - pats to `pi` Follow-up Mode, get/set"; update that phrase to
"`/follow-up` - maps to `pi` Follow-up Mode, get/set" so the documentation
correctly says "maps to" and references the `/follow-up` command and the `pi`
Follow-up Mode.
- Line 201: Update the README text that currently reads "ACP clients don't yet
suport session history, but ACP sessions from `pi-acp` can be `/resume`d in pi
directly" by correcting the misspelling "suport" to "support" so the sentence
reads "ACP clients don't yet support session history, but ACP sessions from
`pi-acp` can be `/resume`d in pi directly"; locate the exact string in the
README and replace the single word to fix the typo.

---

Nitpick comments:
In `@src-tauri/src/acp/events.rs`:
- Around line 253-266: The test session_created_omits_none_fields in
SessionCreatedEvent serialization is missing an assertion that the new optional
models field is omitted when None; update the test by adding an assertion
similar to the existing ones (e.g., assert!(value.get("models").is_none()))
after the other omission checks so SessionCreatedEvent (constructed with models:
None) verifies models is not present in the serialized JSON.

In `@src/renderer/components/chat/slash-menu-model.ts`:
- Around line 98-105: Update the doc comment that currently describes section
ordering: change it to state the actual order is Skills → Commands → Models →
Config (or Modes), noting that Skills appear before Commands and the new Models
section sits between Commands and Config; preserve the existing rule that when
configOptions is non-empty the legacy Modes section is omitted and when empty a
single Modes section is emitted. Ensure the comment above the menu-section
builder mentions Skills and Models explicitly and keeps the note about
preserving the agent's array order for config options.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b0625827-02ba-4562-a99e-8fa1dec5cd6c

📥 Commits

Reviewing files that changed from the base of the PR and between ab81b45 and d66ffab.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • vendor/pi-acp-termul/dist/index.js is excluded by !**/dist/**
  • vendor/pi-acp-termul/dist/index.js.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (27)
  • .gitignore
  • package.json
  • plans/ux-recommendation-spec.md
  • src-tauri/Cargo.toml
  • src-tauri/src/acp/commands.rs
  • src-tauri/src/acp/events.rs
  • src-tauri/src/acp/manager.rs
  • src-tauri/src/lib.rs
  • src/renderer/assets/agent-icons/acp/agents.json
  • src/renderer/components/chat/AgentChatPanel.tsx
  • src/renderer/components/chat/AgentHeader.tsx
  • src/renderer/components/chat/ChatInputBar.tsx
  • src/renderer/components/chat/SlashCommandMenu.tsx
  • src/renderer/components/chat/chat-timeline.test.ts
  • src/renderer/components/chat/chat-timeline.ts
  • src/renderer/components/chat/slash-menu-model.test.ts
  • src/renderer/components/chat/slash-menu-model.ts
  • src/renderer/lib/acp-api.test.ts
  • src/renderer/lib/acp-api.ts
  • src/renderer/lib/acp-thinking.test.ts
  • src/renderer/lib/acp-thinking.ts
  • src/renderer/stores/acp-store.test.ts
  • src/renderer/stores/acp-store.ts
  • vendor/pi-acp-termul/LICENSE
  • vendor/pi-acp-termul/PATCHES.md
  • vendor/pi-acp-termul/README.md
  • vendor/pi-acp-termul/package.json
👮 Files not reviewed due to content moderation or server errors (7)
  • src-tauri/src/acp/manager.rs
  • src/renderer/lib/acp-api.ts
  • src/renderer/lib/acp-api.test.ts
  • src/renderer/lib/acp-thinking.ts
  • src/renderer/lib/acp-thinking.test.ts
  • src/renderer/stores/acp-store.ts
  • src/renderer/stores/acp-store.test.ts

Comment thread src/renderer/assets/agent-icons/acp/agents.json
Comment thread vendor/pi-acp-termul/README.md Outdated
Comment thread vendor/pi-acp-termul/README.md Outdated
@julianromli julianromli marked this pull request as draft June 10, 2026 22:52
@gnoviawan

gnoviawan commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Hey @julianromli — triage update 🔁

Still a draft. Currently blocked by: merge conflicts with dev, CodeQL failing, and 3 open CodeRabbit threads including 1 🔴 Critical (the pi-acp@0.0.27-termul.1 package availability check).

When you're ready to move this forward: rebase onto dev, resolve the Critical thread, and mark ready for review.

Add unstable session/set_model support, ModelChip UI, and client-side thinking resolution keyed off pi model capabilities. Vendor pi-acp 0.0.27-termul.1 with upstream patches until svkozak/pi-acp merges them. Hide reasoning blocks when thinking is off; auto-clamp level on model change.
@julianromli julianromli force-pushed the feat/pi-acp-thinking-model-refine branch from d66ffab to db6433f Compare June 15, 2026 09:05

@julianromli julianromli left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved all open threads in db6433f (rebased onto dev, vendored fork removed).

  • Critical (agents.json npx): reverted to pi-acp@0.0.27. The vendored fork is removed entirely; the client-side resolver in acp-thinking.ts now serves as defense-in-depth and returns null (trust agent modes) when model metadata is absent, so it degrades gracefully with stock pi-acp until upstream #51 ships.
  • README typos (pats to, suport): the vendored vendor/pi-acp-termul/README.md is deleted along with the fork, so these no longer apply.
  • events.rs test: added assert!(value.get("models").is_none()); to session_created_omits_none_fields.
  • slash-menu-model.ts doc comment: updated to reflect actual order (Skills -> Commands -> Models -> Config).

Local checks: bun run ci pass, bun run typecheck pass, bun run test pass (1704 tests), cargo test --lib acp pass (37 passed). Pre-existing git_bash_paths clippy errors on upstream/dev are untouched (outside this diff).

@julianromli julianromli marked this pull request as ready for review June 15, 2026 14:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plans/ux-recommendation-spec.md`:
- Around line 77-82: The fenced code blocks in the file are missing language
tags, which triggers markdownlint rule MD040. Add a language tag to all fenced
code blocks by changing the opening fence from triple backticks to triple
backticks followed by a language identifier. Use `text` as the language tag for
these examples (for example, change ``` to ```text). This fix needs to be
applied to the pi ACP — Setup example shown and to the similar code block
examples at the other locations mentioned in the same file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61dccb8e-fc60-45f3-af60-72adab2dc98d

📥 Commits

Reviewing files that changed from the base of the PR and between d66ffab and db6433f.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • plans/ux-recommendation-spec.md
  • src-tauri/Cargo.toml
  • src-tauri/src/acp/commands.rs
  • src-tauri/src/acp/events.rs
  • src-tauri/src/acp/manager.rs
  • src-tauri/src/lib.rs
  • src/renderer/components/chat/AgentChatPanel.tsx
  • src/renderer/components/chat/AgentHeader.tsx
  • src/renderer/components/chat/ChatInputBar.tsx
  • src/renderer/components/chat/SlashCommandMenu.tsx
  • src/renderer/components/chat/chat-timeline.test.ts
  • src/renderer/components/chat/chat-timeline.ts
  • src/renderer/components/chat/slash-menu-model.test.ts
  • src/renderer/components/chat/slash-menu-model.ts
  • src/renderer/lib/acp-api.test.ts
  • src/renderer/lib/acp-api.ts
  • src/renderer/lib/acp-thinking.test.ts
  • src/renderer/lib/acp-thinking.ts
  • src/renderer/stores/acp-store.test.ts
  • src/renderer/stores/acp-store.ts
✅ Files skipped from review due to trivial changes (1)
  • src-tauri/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/renderer/components/chat/chat-timeline.test.ts
  • src/renderer/components/chat/slash-menu-model.test.ts
  • src/renderer/components/chat/SlashCommandMenu.tsx
  • src/renderer/lib/acp-api.test.ts
  • src-tauri/src/acp/events.rs
  • src/renderer/components/chat/AgentChatPanel.tsx
  • src-tauri/src/acp/commands.rs
  • src/renderer/components/chat/slash-menu-model.ts
  • src/renderer/components/chat/AgentHeader.tsx
  • src/renderer/lib/acp-thinking.test.ts
  • src/renderer/components/chat/ChatInputBar.tsx
  • src/renderer/stores/acp-store.ts
  • src-tauri/src/acp/manager.rs
  • src/renderer/stores/acp-store.test.ts
  • src/renderer/lib/acp-thinking.ts
  • src/renderer/lib/acp-api.ts

Comment on lines +77 to +82
```
pi ACP — Setup (2/3)
☑ Enabled in settings
☐ API key / login configured [Open setup guide]
☐ Ready to chat [Test connection]
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language tag to these fenced examples.

These bare fences trigger markdownlint MD040. text is sufficient here and keeps the spec lint-clean.

Also applies to: 153-155, 178-180, 199-205

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plans/ux-recommendation-spec.md` around lines 77 - 82, The fenced code blocks
in the file are missing language tags, which triggers markdownlint rule MD040.
Add a language tag to all fenced code blocks by changing the opening fence from
triple backticks to triple backticks followed by a language identifier. Use
`text` as the language tag for these examples (for example, change ``` to
```text). This fix needs to be applied to the pi ACP — Setup example shown and
to the similar code block examples at the other locations mentioned in the same
file.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants