mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Problem: Studio persona settings cannot select an independent tool and module combination for each conversation, or preserve that combination across reloads. Root cause: discovery and runtime construction follow mutable shared configuration without a versioned session composition or a shared admission boundary. Fix: introduce versioned scoped definitions, retained resources and authoritative session bindings. Assemble isolated runtime selections, enforce tool ceilings, pin MCP contracts, and share the lifecycle gate with first-prompt admission. Expose independent HTTP, CLI, ACP and Studio management and selection controls, with migration, native import/export, diagnostics and read-only history recovery. Verification: composition mechanism tests, provider-wire cache tests, lifecycle rollback tests, sensitive-boundary regressions, frontend typecheck and all 5,219 frontend tests; gofmt, vet, lint and repository checks. Browser light/dark wide and 390px screenshots and real Electron backend interactions are included. Refs: #13588
71 lines
2.0 KiB
Go
71 lines
2.0 KiB
Go
package boot
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
"reasonix/internal/contract/agentcomposition"
|
|
"reasonix/internal/contract/config"
|
|
"reasonix/internal/ext/pluginpkg"
|
|
"reasonix/internal/state/agentpreset"
|
|
)
|
|
|
|
func rebaseCompositionPath(value, from, to string) string {
|
|
prefix := ""
|
|
if key, path, ok := strings.Cut(value, "="); ok && strings.HasPrefix(key, "--") {
|
|
prefix, value = key+"=", path
|
|
}
|
|
if !filepath.IsAbs(value) {
|
|
return prefix + value
|
|
}
|
|
rel, err := filepath.Rel(from, value)
|
|
if err != nil || !filepath.IsLocal(rel) {
|
|
return prefix + value
|
|
}
|
|
return prefix + filepath.Join(to, rel)
|
|
}
|
|
|
|
func (b *builder) rebaseCompositionMCP(entry config.PluginEntry) (config.PluginEntry, error) {
|
|
owner, owned := b.cfg.PluginPackageOwner(entry.Name)
|
|
if !owned {
|
|
return entry, nil
|
|
}
|
|
if !member(b.opts.Composition.Resolved.Extensions, owner) {
|
|
return entry, fmt.Errorf("%w: MCP %s requires extension %s", agentcomposition.ErrUnavailable, entry.Name, owner)
|
|
}
|
|
installed, _ := pluginpkg.LoadInstalled(b.roots.Home())
|
|
retained, err := resource[retainedExtension](b.opts.Composition, "extension")
|
|
if err != nil {
|
|
return entry, err
|
|
}
|
|
for _, saved := range retained {
|
|
if saved.Package.Installed.Name != owner {
|
|
continue
|
|
}
|
|
for _, live := range installed {
|
|
if live.Installed.Name != owner {
|
|
continue
|
|
}
|
|
root, err := agentpreset.New(b.roots.Home(), b.root).Materialize(saved.Assets)
|
|
if err != nil {
|
|
return entry, err
|
|
}
|
|
entry.Args = slices.Clone(entry.Args)
|
|
entry.Env = maps.Clone(entry.Env)
|
|
entry.Source = config.MCPSourcePluginPackage
|
|
entry.Command = rebaseCompositionPath(entry.Command, live.Package.Root, root)
|
|
for i, arg := range entry.Args {
|
|
entry.Args[i] = rebaseCompositionPath(arg, live.Package.Root, root)
|
|
}
|
|
for key, value := range entry.Env {
|
|
entry.Env[key] = rebaseCompositionPath(value, live.Package.Root, root)
|
|
}
|
|
return entry, nil
|
|
}
|
|
}
|
|
return entry, fmt.Errorf("%w: MCP package resource missing", agentcomposition.ErrUnavailable)
|
|
}
|