You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/compose/reports/sticky-agent-mode.md
+32-12Lines changed: 32 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ status: experimental
4
4
specs: []
5
5
plans: []
6
6
branch: exp/sticky-agent-mode
7
-
commits: e418785e..fe72d4e2
7
+
commits: e418785e..6760950e
8
8
---
9
9
10
10
# Sticky Agent Mode — Final Report
@@ -13,7 +13,11 @@ commits: e418785e..fe72d4e2
13
13
14
14
An experimental mode-locking system where agent selection is permanent for the duration of a session. Once a session has content (any user message), the user cannot switch to a different mode group. Build and Plan form a single free-switch group; all other agents (Compose, etc.) are isolated — once you're in Compose, you stay in Compose until `/new`.
15
15
16
-
Alongside this, compose skills are migrated from `hidden: true` to permission-based scoping. The compose agent's permission allows `compose:*` skills; all other agents deny them. This eliminates the hardcoded `composeSkillsBlock()` injection — compose skills appear in the normal system prompt skill listing when the compose agent is active.
16
+
Three supporting changes enable a clean per-mode experience:
17
+
18
+
1.**Permission-based skill scoping** — compose skills use `deny`/`allow` permissions instead of `hidden: true`
19
+
2.**Plan tools scoped to build/plan** — `plan_enter`/`plan_exit` denied by default, allowed only for build/plan (keeping compose's tool list clean)
20
+
3.**Removed `composeSkillsBlock()` injection** — compose skills appear naturally in the system prompt via `available(agent)`
17
21
18
22
## Architecture
19
23
@@ -23,10 +27,11 @@ Alongside this, compose skills are migrated from `hidden: true` to permission-ba
23
27
24
28
-`agentStore.sessionHasMessages` — reactive boolean derived from `!!lastUserMessage()`
25
29
-`FREE_SWITCH_GROUP = ["build", "plan"]` — agents that can freely switch between each other
26
-
-`canSwitchTo(target)` — returns true if: no messages yet, OR both current and target are in the same group
30
+
-`canSwitchTo(target)` — returns true if: no messages yet, OR target is self, OR both current and target are in the same group
27
31
-`set(name)` — unguarded, for system/programmatic use (session restore, plan tools, CLI)
28
-
-`userSwitch(name)` — guarded, for user actions (dialog, voice). Shows toast when blocked
32
+
-`userSwitch(name)` — guarded, for user actions (dialog, voice). Shows contextual toast when blocked
29
33
-`move(direction)` — cycles through agents, skipping blocked ones. Toast only when no valid target exists
- Plan agent: `plan_enter: "allow"`, `plan_exit: "allow"`
64
+
- Both agents see BOTH tools (symmetric, no list mutation on build↔plan switch)
53
65
54
-
- All 14 compose skills: `hidden: true` removed from frontmatter
66
+
**No registry.ts changes needed** — `llm.ts:resolveTools()` already uses `Permission.disabled()` to strip denied tools before sending them to the model. The permission rules in agent.ts are sufficient.
55
67
56
68
### Compose Prompt Cleanup
57
69
@@ -63,7 +75,7 @@ Single reactive effect — no manual lock/unlock. Naturally handles `/new` (empt
- "Compose Skills Visibility" section simplified: skills are in the normal listing
66
-
- Subagent guidance: "distill instructions" instead of "pass skill lists"
78
+
- Subagent guidance: "distill instructions into prompts"
67
79
68
80
### Design Decisions
69
81
@@ -73,34 +85,42 @@ Single reactive effect — no manual lock/unlock. Naturally handles `/new` (empt
73
85
74
86
**`move()` skips blocked agents:** Tab cycles within the allowed group instead of stopping at the first blocked agent. Toast only shows when the entire group has been exhausted (e.g., compose mode with no other compose-group agents).
75
87
76
-
**Permission in defaults (deny) + compose override (allow):** Future agents automatically inherit the `compose:*` deny. Only the compose agent explicitly opts in. This is more future-proof than denying on each non-compose agent individually.
88
+
**Self-switch always allowed:**`canSwitchTo` returns true when `current === target`. Prevents false toast on no-op switches (e.g., compose user selecting compose in `/agents` dialog).
89
+
90
+
**Contextual toast messages:** Two variants — "只能在 build, plan 之间切换" when in the group but target is outside, "进入 compose 模式后无法切换" when isolated with no valid targets.
91
+
92
+
**Plan tools: symmetric allow in build/plan, deny in defaults:** Future agents automatically inherit the deny. Build and plan both see both tools (no list mutation within the group). This is NOT a revert of #1207 — #1207 made plan tools visible to ALL agents; this scopes them to the build/plan group only, possible because sticky mode prevents cross-group switching.
77
93
78
-
**Subagent distillation model:** Compose skills instruct the orchestrator to distill guidance into concrete subagent prompts, rather than passing `<available_skills>` blocks. Subagents are leaf workers — they don't need orchestration skills.
94
+
**Permission in defaults (deny) + specific agent override (allow):** Used for both skills (`compose:*`) and tools (`plan_enter`/`plan_exit`). Future agents automatically inherit all deny rules. Only the relevant agent explicitly opts in.
79
95
80
96
## Usage
81
97
82
98
This is an experimental branch (`exp/sticky-agent-mode`). Behavior:
83
99
84
100
-**New session:** Mode selector works normally (Tab cycles all agents)
85
101
-**After first message:** Mode is locked to the current group
86
-
- Build/Plan: can Tab between them freely
102
+
- Build/Plan: Tab cycles between them. Toast: "只能在 build, plan 之间切换" if trying `/agents` to compose
0 commit comments