Skip to content

feat: add model roles for small, implementer, and advisor - #56

Merged
elkaix merged 6 commits into
mainfrom
feat/model-roles
Aug 12, 2026
Merged

feat: add model roles for small, implementer, and advisor#56
elkaix merged 6 commits into
mainfrom
feat/model-roles

Conversation

@elkaix

@elkaix elkaix commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Related Issue

No linked issue — this implements a directly requested feature; the problem is explained below.

Problem

There is no way to designate models for specific duties. Subagents always inherit the parent session model unless each call site names an explicit alias, so a user who wants a cheap worker model for subagents, or a designated reviewer model, has to repeat concrete aliases everywhere and update them all when switching providers.

What changed

Adds a model_roles config map that locks a model alias to a named role, with three built-in roles: small, implementer, and advisor (custom role keys are also accepted).

  • @<role> references (@small, @implementer, @advisor, @<custom>) resolve through the map wherever a subagent model alias is accepted: the Agent and DynamicWorkflow tool model arguments and agent profile frontmatter. Unassigned or unresolvable roles fall back to the existing precedence; model: permission deny rules are checked against both the raw @role form and the resolved alias.
  • An assigned implementer role becomes the default model for subagents that set no explicit or profile model.
  • TUI: /model <role> assigns from the model picker, /model <role> clear unassigns, /model roles lists assignments. Role assignment persists through the standard config patch path; an empty string is the cleared state.
  • The advisor role is configuration-only for now; a follow-up PR adds the runtime that consumes it.

This approach keeps the existing alias-based config as the single source of model identity — roles are one indirection layer over aliases, with no new selector grammar.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Summary by CodeRabbit

  • New Features

    • Added configurable small, implementer, and advisor model roles, along with custom roles.
    • Assign aliases with /model <role>, view them with /model roles, and clear assignments when needed.
    • Reference roles with aliases such as @small, @implementer, and @advisor.
    • The assigned implementer model becomes the default for subagents, with fallback when unavailable.
  • Documentation

    • Documented model-role configuration, aliases, inheritance, and fallback behavior.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d3603770-8b48-4a67-b653-4845de3ac23d

📥 Commits

Reviewing files that changed from the base of the PR and between 1e1559e and 4243f03.

📒 Files selected for processing (2)
  • packages/agent-core/src/config/toml.ts
  • packages/agent-core/test/config/configs.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/agent-core/src/config/toml.ts

📝 Walkthrough

Walkthrough

Adds configurable small, implementer, and advisor model roles. The TUI can assign, list, and clear roles. Subagents resolve role references, use implementer by default, enforce model permissions, and fall back to the parent model when needed.

Changes

Model role configuration

Layer / File(s) Summary
Role configuration and resolution
packages/agent-core/src/config/*, packages/agent-core/test/config/*, docs/configuration/config-files.md
Adds built-in and custom model roles, schema and TOML support, alias expansion, role clearing, and configuration round-trip, merge, and salvage tests.

Subagent model selection

Layer / File(s) Summary
Role-aware subagent model selection
packages/agent-core/src/session/subagent-host.ts, packages/agent-core/src/tools/builtin/collaboration/*, packages/agent-core/test/session/subagent-host.test.ts
Subagents resolve explicit role aliases, default to the configured implementer role, validate resolved aliases, and fall back to the parent model when resolution or permissions fail.

TUI role management

Layer / File(s) Summary
TUI role assignment and documentation
apps/pythinker-code/src/tui/commands/config.ts, apps/pythinker-code/src/tui/commands/registry.ts, apps/pythinker-code/src/tui/constant/pythinker-tui.ts, apps/pythinker-code/test/tui/commands/model-roles.test.ts, docs/reference/slash-commands.md, .changeset/model-roles.md
Extends /model with role listing, assignment, and clearing. Role assignment preserves picker mode and does not switch the active session model. Tests cover persistence failures, clearing, role listing, refreshes, and normal model switching.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ModelCommand
  participant ModelPicker
  participant ConfigStore
  User->>ModelCommand: select a model role
  ModelCommand->>ModelPicker: open picker in role-assignment mode
  ModelPicker->>ConfigStore: persist the selected alias
  ConfigStore-->>ModelPicker: success or error
  ModelPicker-->>User: show status without switching active model
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the feat prefix, imperative mood, stays within 72 characters, and accurately describes the model roles feature.
Description check ✅ Passed The description includes all required sections, explains the problem and changes, and completes the checklist with tests, changeset, and documentation updates.
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.

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pythoughts/pythinker-code@4243f03
npx https://pkg.pr.new/@pythoughts/pythinker-code@4243f03

commit: 4243f03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
packages/agent-core/test/session/subagent-host.test.ts (1)

1871-1904: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for raw role-reference denials.

This test denies Agent(model:small-model). Add a case that denies Agent(model:@Small). The current suite would still pass if the raw-reference permission check were removed.

As per path instructions, “New behavior should come with vitest coverage.”

🤖 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 `@packages/agent-core/test/session/subagent-host.test.ts` around lines 1871 -
1904, Add a separate test case alongside the existing role-alias denial test
that configures the parent permission rule with pattern Agent(model:`@small`) and
exercises the same SessionSubagentHost.spawn flow. Assert the delegated model
remains denied or resolves according to the established expected behavior,
ensuring raw role-reference permission checks are covered without changing the
existing Agent(model:small-model) case.

Source: Path instructions

🤖 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 `@apps/pythinker-code/src/tui/commands/config.ts`:
- Line 486: Handle RPC failures in handleModelCommand for both affected paths:
at apps/pythinker-code/src/tui/commands/config.ts:486-486, catch getConfig
failures, display a command-specific error, and return; at
apps/pythinker-code/src/tui/commands/config.ts:502-505, catch setConfig failures
before displaying the success status. Preserve the existing assignment-write
error handling pattern.
- Around line 507-509: Update the single-token role-assignment branch around
showModelPicker so it refreshes the newly opened picker by invoking
refreshModelsForOpenPicker after the picker mounts, before returning. Match the
refresh behavior used by the normal /model path while preserving the existing
role assignment and picker arguments.
- Line 485: Update the regular expression used by requestedAlias.split in the
token parsing logic to include the Unicode flag while preserving its existing
whitespace splitting and empty-token filtering behavior.

In `@docs/configuration/config-files.md`:
- Around line 172-173: Update the explicit `@role` fallback description in the
Agent and DynamicWorkflow model-resolution documentation to state that an
unassigned or unresolvable role falls back directly to the immediate parent’s
configured model alias via parent.config.modelAlias. Remove wording that implies
retrying the profile model or implementer default, while preserving the
documented locked-alias resolution behavior.

In `@packages/agent-core/src/config/model-roles.ts`:
- Around line 15-17: Reserve default consistently across model-role
configuration: retain the default special case in resolveModelRoleAlias, reject
default as a persisted modelRoles key at
packages/agent-core/src/config/schema.ts:277 and as a modelRoles patch key at
packages/agent-core/src/config/schema.ts:323, and exclude default from
documented custom role names at docs/configuration/config-files.md:161.

In `@packages/agent-core/src/tools/builtin/collaboration/dynamic-workflow.ts`:
- Line 105: Update the model precedence description in the dynamic workflow tool
to include the configured implementer role after the subagent type profile model
and before the parent agent model, matching SessionSubagentHost.childModelConfig
behavior when earlier values are unset.

---

Nitpick comments:
In `@packages/agent-core/test/session/subagent-host.test.ts`:
- Around line 1871-1904: Add a separate test case alongside the existing
role-alias denial test that configures the parent permission rule with pattern
Agent(model:`@small`) and exercises the same SessionSubagentHost.spawn flow.
Assert the delegated model remains denied or resolves according to the
established expected behavior, ensuring raw role-reference permission checks are
covered without changing the existing Agent(model:small-model) case.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6e434b4e-37aa-48a7-848d-f405c23617ff

📥 Commits

Reviewing files that changed from the base of the PR and between 45887dc and a6ae578.

📒 Files selected for processing (16)
  • .changeset/model-roles.md
  • apps/pythinker-code/src/tui/commands/config.ts
  • apps/pythinker-code/src/tui/commands/registry.ts
  • apps/pythinker-code/test/tui/commands/model-roles.test.ts
  • docs/configuration/config-files.md
  • docs/reference/slash-commands.md
  • packages/agent-core/src/config/index.ts
  • packages/agent-core/src/config/model-roles.ts
  • packages/agent-core/src/config/schema.ts
  • packages/agent-core/src/config/toml.ts
  • packages/agent-core/src/session/subagent-host.ts
  • packages/agent-core/src/tools/builtin/collaboration/agent.ts
  • packages/agent-core/src/tools/builtin/collaboration/dynamic-workflow.ts
  • packages/agent-core/test/config/configs.test.ts
  • packages/agent-core/test/config/model-roles.test.ts
  • packages/agent-core/test/session/subagent-host.test.ts

Comment thread apps/pythinker-code/src/tui/commands/config.ts Outdated
Comment thread apps/pythinker-code/src/tui/commands/config.ts
Comment thread apps/pythinker-code/src/tui/commands/config.ts
Comment thread docs/configuration/config-files.md Outdated
Comment thread packages/agent-core/src/config/model-roles.ts
Comment thread packages/agent-core/src/tools/builtin/collaboration/dynamic-workflow.ts Outdated
elkaix added 4 commits August 11, 2026 22:46
Add a model_roles config map that locks a model alias to a named role.
@ROLE references resolve wherever a subagent model alias is accepted,
and an assigned implementer role becomes the default subagent model.
The TUI assigns roles with /model <role>, clears them with
/model <role> clear, and lists them with /model roles.
The async model-list refresh re-mounted the picker without the role
options, turning a role assignment into a plain model switch. Also
salvage model_roles per entry instead of dropping the whole section,
and document /model <role> none as an alias of clear.
@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds configurable model roles for subagent model selection and TUI commands for assigning, listing, and clearing them.

  • Resolves explicit @role references and uses implementer as the implicit subagent default.
  • Persists model_roles through the TOML configuration path.
  • Documents role configuration and slash-command behavior.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/agent-core/src/config/toml.ts Serializes, replaces, and removes the model_roles table so role assignments and clears survive reloads.
packages/agent-core/src/session/subagent-host.ts Resolves explicit role references and applies the implementer role as the default subagent model.
packages/agent-core/src/config/model-roles.ts Defines built-in roles and centralizes role-reference expansion.
apps/pythinker-code/src/tui/commands/config.ts Adds role assignment, clearing, and listing through the model command.
apps/pythinker-code/src/tui/constant/pythinker-tui.ts Moves the TUI built-in role constant into the required constant directory.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Model role configuration] --> B[Resolve role to model alias]
  B --> C[Agent or DynamicWorkflow subagent]
  D[TUI model command] --> E[Config patch]
  E --> F[Persist model_roles in TOML]
  F --> A
Loading

Reviews (3): Last reviewed commit: "fix: drop the model roles table when the..." | Re-trigger Greptile

Comment thread apps/pythinker-code/src/tui/commands/config.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@packages/agent-core/src/config/toml.ts`:
- Around line 495-497: In the serialization logic around config.modelRoles,
delete out['model_roles'] before the conditional so undefined modelRoles cannot
preserve stale data; retain cloneUnknown(config.modelRoles) when defined. Add a
round-trip test covering modelRoles: undefined and confirming the old
assignments are not written back.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a347a109-43f9-485d-b3b2-078bb36204b8

📥 Commits

Reviewing files that changed from the base of the PR and between 048c427 and 1e1559e.

📒 Files selected for processing (7)
  • apps/pythinker-code/src/tui/commands/config.ts
  • apps/pythinker-code/src/tui/constant/pythinker-tui.ts
  • apps/pythinker-code/test/tui/commands/model-roles.test.ts
  • docs/configuration/config-files.md
  • docs/reference/slash-commands.md
  • packages/agent-core/src/config/toml.ts
  • packages/agent-core/test/config/configs.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/reference/slash-commands.md
  • docs/configuration/config-files.md
  • apps/pythinker-code/src/tui/commands/config.ts

Comment thread packages/agent-core/src/config/toml.ts Outdated
@elkaix
elkaix merged commit b71f094 into main Aug 12, 2026
12 checks passed
@elkaix
elkaix deleted the feat/model-roles branch August 12, 2026 04:45
elkaix pushed a commit that referenced this pull request Aug 12, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @pythoughts/pythinker-code@0.16.0

### Minor Changes

- [#59](#59)
[`6999b68`](6999b68)
- Add an opt-in advisor: a second model reviews the conversation after a
completed user turn unless another review is already running, and its
notes appear as an `<advisory>` block in the agent's next turn; enable
with `[advisor] enabled = true` plus an advisor model (the `advisor`
model role or `[advisor] model`), and it runs only when the advisor
shares the session model's provider.

- [#56](#56)
[`b71f094`](b71f094)
- Add model roles: lock a model alias to the small, implementer, or
advisor slot with `/model <role>`, list assignments with `/model roles`,
and reference roles as `@small`, `@implementer`, or `@advisor` wherever
a subagent model can be set; an assigned implementer role becomes the
default model for subagents.

- [#57](#57)
[`99c427c`](99c427c)
- Show what the agent is doing in the working indicator: eligible tool
calls whose input schema accepts the injected field now carry a short
model-written intent, streamed live into the spinner label (for example
"check failing test…") instead of a rotating placeholder; disable with
`PYTHINKER_CODE_EXPERIMENTAL_TOOL_INTENT=0`.

- [#58](#58)
[`065bf2e`](065bf2e)
- Redesign core TUI surfaces: tool cards get state-tinted backgrounds
with three new theme tokens, a status bar with a per-session accent
color appears between the input box and footer, and the prompt box uses
a neutral border while permission mode appears in the status bar. The
working-label shimmer uses a calmer constant-velocity sweep with
alternating mission-control highlights.

### Patch Changes

- [#62](#62)
[`7fc36fd`](7fc36fd)
- Repair invalid escape sequences and unescaped quotes in model-written
tool arguments instead of failing the tool call.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant