fix(runner): direct spawn via ServiceFor, only mark running after success
drainOne now spawns agents directly via ServiceFor[spawner] instead of IPC SpawnQueued (which was never received by agentic). Workspace status is only set to "running" AFTER successful spawn — no more PID=0 ghosts. Also fixes workspace name resolution: uses relative path from workspace root (core/go-ai/dev) instead of PathBase (dev). Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
ac510fde19
commit
23f31953d4
2 changed files with 41 additions and 10 deletions
|
|
@ -29,15 +29,16 @@ func (s *PrepSubsystem) HandleIPCEvents(c *core.Core, msg core.Message) core.Res
|
|||
}
|
||||
|
||||
case messages.SpawnQueued:
|
||||
// Runner asks agentic to spawn a queued workspace
|
||||
// Runner asks agentic to spawn a queued workspace
|
||||
wsDir := resolveWorkspace(ev.Workspace)
|
||||
if wsDir == "" {
|
||||
break
|
||||
break
|
||||
}
|
||||
prompt := core.Concat("TASK: ", ev.Task, "\n\nResume from where you left off. Read CODEX.md for conventions. Commit when done.")
|
||||
pid, outputFile, err := s.spawnAgent(ev.Agent, prompt, wsDir)
|
||||
if err != nil {
|
||||
break
|
||||
break
|
||||
}
|
||||
// Update status with real PID
|
||||
if st, serr := ReadStatus(wsDir); serr == nil {
|
||||
|
|
@ -53,6 +54,15 @@ func (s *PrepSubsystem) HandleIPCEvents(c *core.Core, msg core.Message) core.Res
|
|||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
// SpawnFromQueue spawns an agent in a pre-prepped workspace.
|
||||
// Called by runner.Service via ServiceFor interface matching.
|
||||
//
|
||||
// pid, err := prep.SpawnFromQueue("codex", prompt, wsDir)
|
||||
func (s *PrepSubsystem) SpawnFromQueue(agent, prompt, wsDir string) (int, error) {
|
||||
pid, _, err := s.spawnAgent(agent, prompt, wsDir)
|
||||
return pid, err
|
||||
}
|
||||
|
||||
// resolveWorkspace converts a workspace name back to the full path.
|
||||
//
|
||||
// resolveWorkspace("core/go-io/task-5") → "/Users/snider/Code/.core/workspace/core/go-io/task-5"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
|
@ -261,18 +260,40 @@ func (s *Service) drainOne() bool {
|
|||
|
||||
// Ask agentic to spawn — runner doesn't own the spawn logic,
|
||||
// just the gate. Send IPC to trigger the actual spawn.
|
||||
if s.ServiceRuntime != nil {
|
||||
s.Core().ACTION(messages.SpawnQueued{
|
||||
Workspace: core.PathBase(wsDir),
|
||||
Agent: st.Agent,
|
||||
Task: st.Task,
|
||||
})
|
||||
// Workspace name is relative path from workspace root (e.g. "core/go-ai/dev")
|
||||
wsRoot := WorkspaceRoot()
|
||||
wsName := wsDir
|
||||
if len(wsDir) > len(wsRoot)+1 {
|
||||
wsName = wsDir[len(wsRoot)+1:]
|
||||
}
|
||||
core.Info("drainOne: found queued workspace", "workspace", wsName, "agent", st.Agent)
|
||||
|
||||
// Spawn directly — agentic is a Core service, use ServiceFor to get it
|
||||
if s.ServiceRuntime == nil {
|
||||
continue
|
||||
}
|
||||
type spawner interface {
|
||||
SpawnFromQueue(agent, prompt, wsDir string) (int, error)
|
||||
}
|
||||
prep, ok := core.ServiceFor[spawner](s.Core(), "agentic")
|
||||
if !ok {
|
||||
core.Error("drainOne: agentic service not found")
|
||||
continue
|
||||
}
|
||||
prompt := core.Concat("TASK: ", st.Task, "\n\nResume from where you left off. Read CODEX.md for conventions. Commit when done.")
|
||||
pid, err := prep.SpawnFromQueue(st.Agent, prompt, wsDir)
|
||||
if err != nil {
|
||||
core.Error("drainOne: spawn failed", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only mark running AFTER successful spawn
|
||||
st.Status = "running"
|
||||
st.PID = pid
|
||||
st.Runs++
|
||||
WriteStatus(wsDir, st)
|
||||
s.TrackWorkspace(core.PathBase(wsDir), st)
|
||||
s.TrackWorkspace(wsName, st)
|
||||
core.Info("drainOne: spawned", "pid", pid, "workspace", wsName)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue