Files
SivanCola 073079d675 feat(composition): load preset resources on demand
Problem: selecting a custom preset captures unused skill templates and
extension packages and waits for optional MCP startup.

Root cause: resource snapshot creation also served as the runtime permission
model, making selection cost scale with installed resource payload.

Fix: add live capability-grant bindings while preserving legacy snapshots.
Resolve current skill/MCP/extension/hook/command authority at use, defer
optional startup, retain stable provider contracts, and fence new event
and archive formats from older readers. Use lightweight status summaries
and per-instance extension call leases for safe concurrent replacement.

Verification: root and Desktop suites, full boot/builtin suites, affected
storage/controller/frontend tests, lifecycle race checks, frontend interaction
tests and typecheck, lint/vet/check, actionlint, and Windows cross-compilation.
Twenty-sample real switch measurements include build, persist and publication;
64 MiB unused templates remain outside the binding and stay below the local
500 ms P95 target. Native Windows execution is delegated to hosted CI.
2026-10-20 18:49:01 +08:00

58 lines
1.6 KiB
Go

package usecap
import (
"reasonix/internal/contract/agentcomposition"
"reasonix/internal/contract/tool"
"reasonix/internal/ext/plugin"
)
func (r *MCPCapabilityRuntime) ConfigureComposition(d agentcomposition.Definition) {
r.dispatchMu.Lock()
defer r.dispatchMu.Unlock()
policy := agentcomposition.Clone(d)
r.composition = &policy
r.mu.Lock()
defer r.mu.Unlock()
for name, server := range r.servers {
if !r.compositionAllows(server.spec) {
delete(r.servers, name)
r.state.clearServer(name)
}
}
}
func (r *MCPCapabilityRuntime) compositionAllows(spec plugin.Spec) bool {
return r.composition == nil || r.composition.MCP.Allows(spec.Name) && (spec.Package == "" || r.composition.Extensions.Allows(spec.Package))
}
func (r *MCPCapabilityRuntime) BindRegisteredTool(target tool.Tool) tool.Tool {
if bound, ok := target.(*runtimeBoundMCPTool); ok && bound.proxy.runtime == r {
return target
}
meta, ok := target.(tool.MCPMetadata)
if !ok {
return target
}
return &runtimeBoundMCPTool{proxy: r.NewFrontend(nil, nil), target: target, server: meta.MCPServerName(), authorized: tool.IsMCPServerAuthorized(target)}
}
func mcpToolMatchesRuntime(target tool.Tool, spec plugin.Spec) bool {
for {
bound, ok := target.(*runtimeBoundMCPTool)
if !ok {
return plugin.MCPToolMatchesSpec(target, spec)
}
target = bound.target
}
}
// FreezeComposition keeps settings edits from widening this session's catalog.
func (r *MCPCapabilityRuntime) FreezeComposition() {
if r == nil {
return
}
r.dispatchMu.Lock()
defer r.dispatchMu.Unlock()
r.compositionFrozen = true
}