fix(ax): replace typed service lookups with Core.Service

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-31 05:43:14 +00:00
parent 998b6094ca
commit f0c903d8c3
21 changed files with 135 additions and 80 deletions

View file

@ -40,16 +40,21 @@ func TestMain_NewCoreAgent_Good(t *testing.T) {
assert.Contains(t, c.Commands(), "env")
assert.Contains(t, c.Actions(), "process.run")
_, ok := core.ServiceFor[*agentic.PrepSubsystem](c, "agentic")
assert.True(t, ok)
_, ok = core.ServiceFor[*runner.Service](c, "runner")
assert.True(t, ok)
_, ok = core.ServiceFor[*monitor.Subsystem](c, "monitor")
assert.True(t, ok)
_, ok = core.ServiceFor[*brain.DirectSubsystem](c, "brain")
assert.True(t, ok)
_, ok = core.ServiceFor[*mcp.Service](c, "mcp")
assert.True(t, ok)
service := c.Service("agentic")
assert.True(t, service.OK)
assert.IsType(t, &agentic.PrepSubsystem{}, service.Value)
service = c.Service("runner")
assert.True(t, service.OK)
assert.IsType(t, &runner.Service{}, service.Value)
service = c.Service("monitor")
assert.True(t, service.OK)
assert.IsType(t, &monitor.Subsystem{}, service.Value)
service = c.Service("brain")
assert.True(t, service.OK)
assert.IsType(t, &brain.DirectSubsystem{}, service.Value)
service = c.Service("mcp")
assert.True(t, service.OK)
assert.IsType(t, &mcp.Service{}, service.Value)
}
func TestMain_NewCoreAgentBanner_Good(t *testing.T) {

View file

@ -3,15 +3,12 @@
package main
import (
"dappco.re/go/agent/pkg/agentic"
"dappco.re/go/agent/pkg/brain"
"dappco.re/go/agent/pkg/monitor"
core "dappco.re/go/core"
"forge.lthn.ai/core/mcp/pkg/mcp"
)
// c := core.New(core.WithService(registerMCPService))
// _, ok := core.ServiceFor[*mcp.Service](c, "mcp")
// service := c.Service("mcp")
func registerMCPService(c *core.Core) core.Result {
if c == nil {
return core.Result{Value: core.E("main.registerMCPService", "core is required", nil), OK: false}
@ -19,15 +16,20 @@ func registerMCPService(c *core.Core) core.Result {
var registeredSubsystems []mcp.Subsystem
if agenticSubsystem, ok := core.ServiceFor[*agentic.PrepSubsystem](c, "agentic"); ok {
registeredSubsystems = append(registeredSubsystems, agenticSubsystem)
}
if monitorSubsystem, ok := core.ServiceFor[*monitor.Subsystem](c, "monitor"); ok {
registeredSubsystems = append(registeredSubsystems, monitorSubsystem)
}
if brainSubsystem, ok := core.ServiceFor[*brain.DirectSubsystem](c, "brain"); ok {
registeredSubsystems = append(registeredSubsystems, brainSubsystem)
appendSubsystem := func(name string) {
serviceResult := c.Service(name)
if !serviceResult.OK {
return
}
subsystem, ok := serviceResult.Value.(mcp.Subsystem)
if !ok {
return
}
registeredSubsystems = append(registeredSubsystems, subsystem)
}
appendSubsystem("agentic")
appendSubsystem("monitor")
appendSubsystem("brain")
service, err := mcp.New(mcp.Options{
Subsystems: registeredSubsystems,

View file

@ -355,10 +355,14 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, workspaceDir string) (int, str
command, args = containerCommand(command, args, repoDir, metaDir)
procSvc, ok := core.ServiceFor[*process.Service](s.Core(), "process")
if !ok {
processResult := s.Core().Service("process")
if !processResult.OK {
return 0, "", "", core.E("dispatch.spawnAgent", "process service not registered", nil)
}
procSvc, ok := processResult.Value.(*process.Service)
if !ok {
return 0, "", "", core.E("dispatch.spawnAgent", "process service has unexpected type", nil)
}
proc, err := procSvc.StartWithOptions(context.Background(), process.RunOptions{
Command: command,
Args: args,
@ -520,8 +524,10 @@ func (s *PrepSubsystem) dispatch(ctx context.Context, callRequest *mcp.CallToolR
Runs: 0,
}
writeStatusResult(workspaceDir, workspaceStatus)
if runnerSvc, ok := core.ServiceFor[workspaceTracker](s.Core(), "runner"); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
if runnerResult := s.Core().Service("runner"); runnerResult.OK {
if runnerSvc, ok := runnerResult.Value.(workspaceTracker); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
}
}
return nil, DispatchOutput{
Success: true,
@ -552,8 +558,10 @@ func (s *PrepSubsystem) dispatch(ctx context.Context, callRequest *mcp.CallToolR
}
writeStatusResult(workspaceDir, workspaceStatus)
if s.ServiceRuntime != nil {
if runnerSvc, ok := core.ServiceFor[workspaceTracker](s.Core(), "runner"); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
if runnerResult := s.Core().Service("runner"); runnerResult.OK {
if runnerSvc, ok := runnerResult.Value.(workspaceTracker); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
}
}
}

View file

@ -36,8 +36,10 @@ func (s *PrepSubsystem) HandleIPCEvents(c *core.Core, msg core.Message) core.Res
workspaceStatus.PID = pid
workspaceStatus.ProcessID = processID
writeStatusResult(workspaceDir, workspaceStatus)
if runnerSvc, ok := core.ServiceFor[workspaceTracker](c, "runner"); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
if runnerResult := c.Service("runner"); runnerResult.OK {
if runnerSvc, ok := runnerResult.Value.(workspaceTracker); ok {
runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus)
}
}
}
_ = outputFile

View file

@ -10,11 +10,7 @@ import (
// alive := agentic.ProcessAlive(c, proc.ID, proc.Info().PID)
// alive := agentic.ProcessAlive(c, "", 12345) // legacy PID fallback
func ProcessAlive(c *core.Core, processID string, pid int) bool {
if c == nil {
return false
}
service, ok := core.ServiceFor[*process.Service](c, "process")
service, ok := lookupProcessService(c)
if !ok {
return false
}
@ -40,11 +36,7 @@ func ProcessAlive(c *core.Core, processID string, pid int) bool {
// terminated := agentic.ProcessTerminate(c, proc.ID, proc.Info().PID)
func ProcessTerminate(c *core.Core, processID string, pid int) bool {
if c == nil {
return false
}
service, ok := core.ServiceFor[*process.Service](c, "process")
service, ok := lookupProcessService(c)
if !ok {
return false
}
@ -67,3 +59,15 @@ func ProcessTerminate(c *core.Core, processID string, pid int) bool {
return false
}
func lookupProcessService(c *core.Core) (*process.Service, bool) {
if c == nil {
return nil, false
}
result := c.Service("process")
if !result.OK {
return nil, false
}
service, ok := result.Value.(*process.Service)
return service, ok
}

View file

@ -13,13 +13,13 @@ type processActionHandlers struct {
service *process.Service
}
// c := core.New(core.WithService(agentic.ProcessRegister))
// processService, _ := core.ServiceFor[*process.Service](c, "process")
// c := core.New(core.WithService(agentic.ProcessRegister))
// processService := c.Service("process")
func ProcessRegister(c *core.Core) core.Result {
if c == nil {
return core.Result{Value: core.E("agentic.ProcessRegister", "core is required", nil), OK: false}
}
if _, ok := core.ServiceFor[*process.Service](c, "process"); ok {
if result := c.Service("process"); result.OK {
return core.Result{OK: true}
}

View file

@ -12,7 +12,7 @@ import (
// core.WithService(agentic.Register),
//
// )
// prep, _ := core.ServiceFor[*agentic.PrepSubsystem](c, "agentic")
// prep := c.Service("agentic")
func Register(c *core.Core) core.Result {
subsystem := NewPrep()
subsystem.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})

View file

@ -23,8 +23,10 @@ func TestRegister_ServiceRegistered_Good(t *testing.T) {
require.NotNil(t, c)
// Service auto-registered under the last segment of the package path: "agentic"
prep, ok := core.ServiceFor[*PrepSubsystem](c, "agentic")
assert.True(t, ok, "PrepSubsystem must be registered as \"agentic\"")
service := c.Service("agentic")
require.True(t, service.OK)
prep, ok := service.Value.(*PrepSubsystem)
require.True(t, ok, "PrepSubsystem must be registered as \"agentic\"")
assert.NotNil(t, prep)
}
@ -35,7 +37,9 @@ func TestRegister_CoreWired_Good(t *testing.T) {
c := core.New(core.WithService(Register))
prep, ok := core.ServiceFor[*PrepSubsystem](c, "agentic")
service := c.Service("agentic")
require.True(t, service.OK)
prep, ok := service.Value.(*PrepSubsystem)
require.True(t, ok)
// Register must wire ServiceRuntime — service needs it for Core access
assert.NotNil(t, prep.ServiceRuntime, "Register must set ServiceRuntime")

View file

@ -7,8 +7,8 @@ import (
)
// c := core.New(core.WithService(brain.Register))
// subsystem, _ := core.ServiceFor[*brain.DirectSubsystem](c, "brain")
// core.Println(subsystem.Name()) // "brain"
// subsystem := c.Service("brain")
// core.Println(subsystem.OK) // true
func Register(c *core.Core) core.Result {
subsystem := NewDirect()
return core.Result{Value: subsystem, OK: true}

View file

@ -453,13 +453,15 @@ func (m *Subsystem) checkInbox() string {
}
if m.ServiceRuntime != nil {
if notifier, ok := core.ServiceFor[channelSender](m.Core(), "mcp"); ok {
for _, inboxMessage := range inboxMessages {
notifier.ChannelSend(context.Background(), "inbox.message", map[string]any{
"from": inboxMessage.From,
"subject": inboxMessage.Subject,
"content": inboxMessage.Content,
})
if notifierResult := m.Core().Service("mcp"); notifierResult.OK {
if notifier, ok := notifierResult.Value.(channelSender); ok {
for _, inboxMessage := range inboxMessages {
notifier.ChannelSend(context.Background(), "inbox.message", map[string]any{
"from": inboxMessage.From,
"subject": inboxMessage.Subject,
"content": inboxMessage.Content,
})
}
}
}
}

View file

@ -17,7 +17,8 @@ func ExampleNew() {
func ExampleRegister() {
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
svc, ok := service.Value.(*Subsystem)
core.Println(ok)
core.Println(svc.Name())
// Output:

View file

@ -218,7 +218,9 @@ func TestMonitor_HandleAgentCompleted_Good_WithCore(t *testing.T) {
fs.EnsureDir(core.JoinPath(wsRoot, "workspace"))
c := core.New(core.WithService(Register))
mon, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
require.True(t, service.OK)
mon, ok := service.Value.(*Subsystem)
require.True(t, ok)
c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "ws-2", Status: "completed"})

View file

@ -7,8 +7,8 @@ import (
)
// c := core.New(core.WithService(monitor.Register))
// service, _ := core.ServiceFor[*monitor.Subsystem](c, "monitor")
// core.Println(service.Name()) // "monitor"
// service := c.Service("monitor")
// core.Println(service.OK) // true
func Register(c *core.Core) core.Result {
service := New(Options{})
service.ServiceRuntime = core.NewServiceRuntime(c, Options{})

View file

@ -6,7 +6,8 @@ import core "dappco.re/go/core"
func ExampleRegister_ipc() {
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
svc, ok := service.Value.(*Subsystem)
core.Println(ok)
core.Println(svc.Name())
// Output:

View file

@ -16,7 +16,9 @@ func TestRegister_Register_Good_ReturnsSubsystem(t *testing.T) {
t.Setenv("CORE_WORKSPACE", wsRoot)
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
assert.True(t, service.OK)
svc, ok := service.Value.(*Subsystem)
assert.True(t, ok)
assert.NotNil(t, svc)
}
@ -34,7 +36,9 @@ func TestRegister_Register_Good_WiresServiceRuntime(t *testing.T) {
t.Setenv("CORE_WORKSPACE", wsRoot)
c := core.New(core.WithService(Register))
svc, _ := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
require.True(t, service.OK)
svc, _ := service.Value.(*Subsystem)
assert.NotNil(t, svc.ServiceRuntime)
assert.Equal(t, c, svc.Core())
}
@ -45,7 +49,9 @@ func TestRegister_Register_Good_TracksStartedIPC(t *testing.T) {
fs.EnsureDir(core.JoinPath(wsRoot, "workspace"))
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
require.True(t, service.OK)
svc, ok := service.Value.(*Subsystem)
require.True(t, ok)
c.ACTION(messages.AgentStarted{Agent: "codex", Repo: "go-io", Workspace: "ws-reg"})
@ -61,7 +67,9 @@ func TestRegister_Register_Good_TracksCompletedIPC(t *testing.T) {
fs.EnsureDir(core.JoinPath(wsRoot, "workspace"))
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Subsystem](c, "monitor")
service := c.Service("monitor")
require.True(t, service.OK)
svc, ok := service.Value.(*Subsystem)
require.True(t, ok)
c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "ws-done", Status: "completed"})

View file

@ -237,11 +237,16 @@ func (s *Service) drainOne() bool {
type spawner interface {
SpawnFromQueue(agent, prompt, workspaceDir string) core.Result
}
agenticService, ok := core.ServiceFor[spawner](s.Core(), "agentic")
if !ok {
agenticResult := s.Core().Service("agentic")
if !agenticResult.OK {
core.Error("drainOne: agentic service not found")
continue
}
agenticService, ok := agenticResult.Value.(spawner)
if !ok {
core.Error("drainOne: agentic service has unexpected type")
continue
}
prompt := core.Concat("TASK: ", workspaceStatus.Task, "\n\nResume from where you left off. Read CODEX.md for conventions. Commit when done.")
spawnResult := agenticService.SpawnFromQueue(workspaceStatus.Agent, prompt, workspaceDir)
if !spawnResult.OK {

View file

@ -45,7 +45,7 @@ func New() *Service {
}
// c := core.New(core.WithService(runner.Register))
// service, _ := core.ServiceFor[*runner.Service](c, "runner")
// service := c.Service("runner")
func Register(coreApp *core.Core) core.Result {
service := New()
service.ServiceRuntime = core.NewServiceRuntime(coreApp, Options{})
@ -106,6 +106,18 @@ func (s *Service) OnShutdown(_ context.Context) core.Result {
// Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed",
// })
func (s *Service) HandleIPCEvents(coreApp *core.Core, msg core.Message) core.Result {
sendNotification := func(channel string, data any) {
serviceResult := coreApp.Service("mcp")
if !serviceResult.OK {
return
}
notifier, ok := serviceResult.Value.(channelSender)
if !ok {
return
}
notifier.ChannelSend(context.Background(), channel, data)
}
switch ev := msg.(type) {
case messages.AgentStarted:
baseAgentName := baseAgent(ev.Agent)
@ -127,9 +139,7 @@ func (s *Service) HandleIPCEvents(coreApp *core.Core, msg core.Message) core.Res
Running: runningCount,
Limit: limit,
}
if notifier, ok := core.ServiceFor[channelSender](coreApp, "mcp"); ok {
notifier.ChannelSend(context.Background(), "agent.status", notification)
}
sendNotification("agent.status", notification)
case messages.AgentCompleted:
if ev.Workspace != "" {
@ -166,9 +176,7 @@ func (s *Service) HandleIPCEvents(coreApp *core.Core, msg core.Message) core.Res
Running: runningCount,
Limit: limit,
}
if notifier, ok := core.ServiceFor[channelSender](coreApp, "mcp"); ok {
notifier.ChannelSend(context.Background(), "agent.status", notification)
}
sendNotification("agent.status", notification)
s.Poke()
case messages.PokeQueue:

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: EUPL-1.2
// service := core.ServiceFor[*setup.Service](core.New(core.WithService(setup.Register)), "setup")
// service := core.New(core.WithService(setup.Register)).Service("setup")
package setup
import (

View file

@ -12,13 +12,13 @@ import (
type RuntimeOptions struct{}
// c := core.New(core.WithService(setup.Register))
// service, _ := core.ServiceFor[*setup.Service](c, "setup")
// service := c.Service("setup")
type Service struct {
*core.ServiceRuntime[RuntimeOptions]
}
// c := core.New(core.WithService(setup.Register))
// service, _ := core.ServiceFor[*setup.Service](c, "setup")
// service := c.Service("setup")
func Register(c *core.Core) core.Result {
service := &Service{
ServiceRuntime: core.NewServiceRuntime(c, RuntimeOptions{}),

View file

@ -6,9 +6,10 @@ import (
core "dappco.re/go/core"
)
func ExampleRegister_serviceFor() {
func ExampleRegister_service() {
c := core.New(core.WithService(Register))
service, ok := core.ServiceFor[*Service](c, "setup")
serviceResult := c.Service("setup")
service, ok := serviceResult.Value.(*Service)
core.Println(ok)
core.Println(service != nil)
// Output:

View file

@ -14,7 +14,9 @@ import (
func TestService_Register_Good(t *testing.T) {
c := core.New(core.WithService(Register))
svc, ok := core.ServiceFor[*Service](c, "setup")
service := c.Service("setup")
assert.True(t, service.OK)
svc, ok := service.Value.(*Service)
assert.True(t, ok)
assert.NotNil(t, svc)
}