225 lines
9.0 KiB
Markdown
225 lines
9.0 KiB
Markdown
# Phase 1 Step 8d — Implementation Notes
|
|
|
|
## Status
|
|
|
|
Done.
|
|
|
|
## Plan reference
|
|
|
|
- Plan: `docs/PHASE-1-IMPLEMENTATION-PLAN.md`
|
|
- Section: "Step 8d: Scope transition rewrites — `use_role`,
|
|
`use_session`, `use_agent`, `exit_agent`"
|
|
|
|
## Summary
|
|
|
|
Added scope transition methods to `RequestContext` that build real
|
|
`ToolScope` instances via `McpFactory::acquire()`. Added
|
|
`mcp_config` and `mcp_log_path` fields to `AppState` so scope
|
|
transitions can look up MCP server specs and acquire handles. Added
|
|
`Session::new_from_ctx` and `Session::load_from_ctx` constructors
|
|
that take `&RequestContext` + `&AppConfig` instead of `&Config`.
|
|
Migrated `edit_role` (deferred from Step 8b) since `use_role` is
|
|
now available. `use_agent` is deferred to Step 8h because
|
|
`Agent::init` takes `&GlobalConfig`.
|
|
|
|
## What was changed
|
|
|
|
### Files modified (4 files)
|
|
|
|
- **`src/config/app_state.rs`** — added 2 fields:
|
|
- `mcp_config: Option<McpServersConfig>` — parsed MCP server
|
|
specs from `mcp.json`, stored at init time for scope
|
|
transitions to look up server specs by name
|
|
- `mcp_log_path: Option<PathBuf>` — log path for MCP server
|
|
stderr output, passed to `McpFactory::acquire`
|
|
|
|
- **`src/config/request_context.rs`** — added 6 methods in a new
|
|
impl block:
|
|
- `rebuild_tool_scope(&mut self, app, enabled_mcp_servers)` —
|
|
private async helper that resolves MCP server IDs, acquires
|
|
handles via `McpFactory::acquire()`, builds a fresh `Functions`
|
|
instance, appends user interaction and MCP meta functions,
|
|
assembles a `ToolScope`, and assigns it to `self.tool_scope`
|
|
- `use_role(&mut self, app, name, abort_signal)` — retrieves
|
|
the role, resolves its MCP server list, calls
|
|
`rebuild_tool_scope`, then `use_role_obj`
|
|
- `use_session(&mut self, app, session_name, abort_signal)` —
|
|
creates or loads a session via `Session::new_from_ctx` /
|
|
`Session::load_from_ctx`, rebuilds the tool scope, handles
|
|
the "carry last message" prompt, calls
|
|
`init_agent_session_variables`
|
|
- `exit_agent(&mut self, app)` — exits the session, resets the
|
|
tool scope to a fresh default (global functions + user
|
|
interaction), clears agent/supervisor/rag state
|
|
- `edit_role(&mut self, app, abort_signal)` — resolves the
|
|
current role name, calls `upsert_role` (editor), then
|
|
`use_role`
|
|
- `upsert_role(&self, app, name)` — opens the role file in the
|
|
editor (via `app.editor()`)
|
|
|
|
Updated imports: `McpRuntime`, `TEMP_SESSION_NAME`, `AbortSignal`,
|
|
`formatdoc`, `Confirm`, `remove_file`.
|
|
|
|
- **`src/config/session.rs`** — added 2 constructors:
|
|
- `Session::new_from_ctx(&RequestContext, &AppConfig, name)` —
|
|
equivalent to `Session::new(&Config, name)` but reads
|
|
`ctx.extract_role(app)` and `app.save_session`
|
|
- `Session::load_from_ctx(&RequestContext, &AppConfig, name, path)` —
|
|
equivalent to `Session::load(&Config, name, path)` but calls
|
|
`Model::retrieve_model(app, ...)` and
|
|
`ctx.retrieve_role(app, role_name)` instead of `&Config` methods
|
|
|
|
- **`src/config/bridge.rs`** — added `mcp_config: None,
|
|
mcp_log_path: None` to all 3 `AppState` construction sites in
|
|
tests
|
|
|
|
### Files NOT changed
|
|
|
|
- **`src/mcp/mod.rs`** — untouched; Step 8c's extraction is used
|
|
via `McpFactory::acquire()`
|
|
- **`src/config/mcp_factory.rs`** — untouched
|
|
- **`src/config/mod.rs`** — all `Config::use_role`,
|
|
`Config::use_session`, `Config::use_agent`,
|
|
`Config::exit_agent` stay intact for current callers
|
|
|
|
## Key decisions
|
|
|
|
### 1. `rebuild_tool_scope` replaces `McpRegistry::reinit`
|
|
|
|
The existing `Config::use_role` and `Config::use_session` both
|
|
follow the pattern: take `McpRegistry` → `McpRegistry::reinit` →
|
|
put registry back. The new `rebuild_tool_scope` replaces this with:
|
|
resolve server IDs → `McpFactory::acquire()` each → build
|
|
`ToolScope`. This is the core semantic change from the plan.
|
|
|
|
Key differences:
|
|
- `McpRegistry::reinit` does batch start/stop of servers (stops
|
|
servers not in the new set, starts missing ones). The factory
|
|
approach acquires each server independently — unused servers
|
|
are dropped when their `Arc` refcount hits zero.
|
|
- The factory's `Weak` sharing means that switching from role A
|
|
(github,slack) to role B (github,jira) shares the github
|
|
handle instead of stopping and restarting it.
|
|
|
|
### 2. `ToolCallTracker` initialized with default params
|
|
|
|
`ToolCallTracker::new(4, 10)` — 4 max repeats, 10 chain length.
|
|
These match the constants used in the existing codebase (the
|
|
tracker is used for tool-call loop detection). A future step can
|
|
make these configurable via `AppConfig` if needed.
|
|
|
|
### 3. `use_agent` deferred to Step 8h
|
|
|
|
`Config::use_agent` is a static method that takes `&GlobalConfig`
|
|
and calls `Agent::init(config, agent_name, abort_signal)`.
|
|
`Agent::init` compiles agent tools, loads RAG, resolves the model,
|
|
and does ~100 lines of setup, all against `&Config`. Migrating
|
|
`Agent::init` is a significant cross-module change that belongs
|
|
in Step 8h alongside the other agent lifecycle methods.
|
|
|
|
The plan listed `use_agent` as a target for 8d, but the
|
|
dependency on `Agent::init(&Config)` makes a clean bridge
|
|
impossible without duplicating `Agent::init`.
|
|
|
|
### 4. `abort_signal` is unused in the new methods
|
|
|
|
The existing `Config::use_role` doesn't pass `abort_signal` to
|
|
individual server starts — it's used by `abortable_run_with_spinner`
|
|
wrapping the batch `McpRegistry::reinit`. The new methods use
|
|
`McpFactory::acquire()` which doesn't take an abort signal (see
|
|
Step 8c notes). The `_abort_signal` parameter is kept in the
|
|
signature for API compatibility; Step 8f can wire it into the
|
|
factory if per-server cancellation is needed.
|
|
|
|
### 5. Session constructors parallel existing ones
|
|
|
|
`Session::new_from_ctx` and `Session::load_from_ctx` are verbatim
|
|
copies of `Session::new` and `Session::load` with `config: &Config`
|
|
replaced by `ctx: &RequestContext` + `app: &AppConfig`. The copies
|
|
are under `#[allow(dead_code)]` and will replace the originals
|
|
when callers migrate in Steps 8f-8g.
|
|
|
|
### 6. `exit_agent` rebuilds tool scope inline
|
|
|
|
`Config::exit_agent` calls `self.load_functions()` to reset the
|
|
global function declarations after exiting an agent. The new
|
|
`exit_agent` does the equivalent inline: creates a fresh
|
|
`ToolScope` with `Functions::init()` + user interaction functions.
|
|
It does NOT call `rebuild_tool_scope` because there's no MCP
|
|
server set to resolve — we're returning to the global scope.
|
|
|
|
## Deviations from plan
|
|
|
|
| Deviation | Rationale |
|
|
|---|---|
|
|
| `use_agent` deferred to Step 8h | Depends on `Agent::init(&Config)` migration |
|
|
| No `abort_signal` propagation to `McpFactory::acquire` | Step 8c decided against it; behavior matches existing code |
|
|
| No parent scope restoration test | Testing requires spawning real MCP servers; documented as Phase 5 test target |
|
|
|
|
## Verification
|
|
|
|
### Compilation
|
|
|
|
- `cargo check` — clean, zero warnings, zero errors
|
|
- `cargo clippy` — clean
|
|
|
|
### Tests
|
|
|
|
- `cargo test` — **63 passed, 0 failed** (unchanged)
|
|
|
|
## Handoff to next step
|
|
|
|
### What Step 8e can rely on
|
|
|
|
- **`RequestContext::use_role(app, name, abort_signal)`** — full
|
|
scope transition with ToolScope rebuild via McpFactory
|
|
- **`RequestContext::use_session(app, session_name, abort_signal)`** —
|
|
full scope transition with Session creation/loading
|
|
- **`RequestContext::exit_agent(app)`** — cleans up agent state
|
|
and rebuilds global ToolScope
|
|
- **`RequestContext::edit_role(app, abort_signal)`** — editor +
|
|
use_role
|
|
- **`RequestContext::upsert_role(app, name)`** — editor only
|
|
- **`Session::new_from_ctx` / `Session::load_from_ctx`** — ctx-
|
|
compatible session constructors
|
|
- **`AppState.mcp_config` / `AppState.mcp_log_path`** — MCP server
|
|
specs and log path available for scope transitions
|
|
|
|
### Method count at end of Step 8d
|
|
|
|
- `AppConfig`: 21 methods (unchanged from 8b)
|
|
- `RequestContext`: 53 methods (46 from 8b + 6 from 8d + 1 private
|
|
`rebuild_tool_scope`)
|
|
- `Session`: 2 new constructors (`new_from_ctx`, `load_from_ctx`)
|
|
- `AppState`: 2 new fields (`mcp_config`, `mcp_log_path`)
|
|
|
|
### What Step 8e should do
|
|
|
|
Migrate the Category C deferrals from Step 6:
|
|
- `compress_session`, `maybe_compress_session`
|
|
- `autoname_session`, `maybe_autoname_session`
|
|
- `use_rag`, `edit_rag_docs`, `rebuild_rag`
|
|
- `apply_prelude`
|
|
|
|
### Files to re-read at the start of Step 8e
|
|
|
|
- `docs/PHASE-1-IMPLEMENTATION-PLAN.md` — Step 8e section
|
|
- This notes file
|
|
- Step 6 notes — Category C deferral inventory
|
|
- `src/config/rag_cache.rs` — RagCache scaffolding from Step 6.5
|
|
- `src/config/mod.rs` — `compress_session`, `maybe_compress_session`,
|
|
`autoname_session`, `maybe_autoname_session`, `use_rag`,
|
|
`edit_rag_docs`, `rebuild_rag`, `apply_prelude` method bodies
|
|
|
|
## References
|
|
|
|
- Phase 1 plan: `docs/PHASE-1-IMPLEMENTATION-PLAN.md`
|
|
- Step 8b notes: `docs/implementation/PHASE-1-STEP-8b-NOTES.md`
|
|
- Step 8c notes: `docs/implementation/PHASE-1-STEP-8c-NOTES.md`
|
|
- Step 6.5 notes: `docs/implementation/PHASE-1-STEP-6.5-NOTES.md`
|
|
- Modified files:
|
|
- `src/config/request_context.rs` (6 new methods)
|
|
- `src/config/app_state.rs` (2 new fields)
|
|
- `src/config/session.rs` (2 new constructors)
|
|
- `src/config/bridge.rs` (test updates for new AppState fields)
|