feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package runner
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-29 21:27:32 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"dappco.re/go/agent/pkg/agentic"
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
core "dappco.re/go/core"
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-30 22:54:19 +00:00
|
|
|
// fs := agentic.LocalFs()
|
2026-03-29 21:27:32 +00:00
|
|
|
var fs = agentic.LocalFs()
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
|
2026-03-30 16:24:06 +00:00
|
|
|
func runnerWorkspaceStatusFromAgentic(status *agentic.WorkspaceStatus) *WorkspaceStatus {
|
|
|
|
|
if status == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return &WorkspaceStatus{
|
|
|
|
|
Status: status.Status,
|
|
|
|
|
Agent: status.Agent,
|
|
|
|
|
Repo: status.Repo,
|
|
|
|
|
Org: status.Org,
|
|
|
|
|
Task: status.Task,
|
|
|
|
|
Branch: status.Branch,
|
|
|
|
|
PID: status.PID,
|
|
|
|
|
Question: status.Question,
|
|
|
|
|
PRURL: status.PRURL,
|
|
|
|
|
StartedAt: status.StartedAt,
|
|
|
|
|
Runs: status.Runs,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func agenticWorkspaceStatusFromRunner(status *WorkspaceStatus) *agentic.WorkspaceStatus {
|
|
|
|
|
if status == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return &agentic.WorkspaceStatus{
|
|
|
|
|
Status: status.Status,
|
|
|
|
|
Agent: status.Agent,
|
|
|
|
|
Repo: status.Repo,
|
|
|
|
|
Org: status.Org,
|
|
|
|
|
Task: status.Task,
|
|
|
|
|
Branch: status.Branch,
|
|
|
|
|
PID: status.PID,
|
|
|
|
|
Question: status.Question,
|
|
|
|
|
PRURL: status.PRURL,
|
|
|
|
|
StartedAt: status.StartedAt,
|
|
|
|
|
Runs: status.Runs,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 21:58:24 +00:00
|
|
|
// root := runner.WorkspaceRoot()
|
|
|
|
|
// core.Println(root) // "~/Code/.core/workspace"
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
func WorkspaceRoot() string {
|
2026-03-29 21:27:32 +00:00
|
|
|
return agentic.WorkspaceRoot()
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 21:58:24 +00:00
|
|
|
// root := runner.CoreRoot()
|
|
|
|
|
// core.Println(root) // "~/Code/.core"
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
func CoreRoot() string {
|
2026-03-29 21:27:32 +00:00
|
|
|
return agentic.CoreRoot()
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 21:58:24 +00:00
|
|
|
// result := ReadStatusResult("/srv/core/workspace/core/go-io/task-5")
|
|
|
|
|
// if result.OK { workspaceStatus := result.Value.(*WorkspaceStatus) }
|
2026-03-30 21:22:54 +00:00
|
|
|
func ReadStatusResult(workspaceDir string) core.Result {
|
|
|
|
|
statusResult := agentic.ReadStatusResult(workspaceDir)
|
2026-03-30 21:11:06 +00:00
|
|
|
if !statusResult.OK {
|
|
|
|
|
err, _ := statusResult.Value.(error)
|
2026-03-30 19:22:49 +00:00
|
|
|
if err == nil {
|
|
|
|
|
return core.Result{Value: core.E("runner.ReadStatusResult", "failed to read status", nil), OK: false}
|
|
|
|
|
}
|
2026-03-30 16:24:06 +00:00
|
|
|
return core.Result{Value: core.E("runner.ReadStatusResult", "failed to read status", err), OK: false}
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|
2026-03-29 21:27:32 +00:00
|
|
|
|
2026-03-30 21:11:06 +00:00
|
|
|
agenticStatus, ok := statusResult.Value.(*agentic.WorkspaceStatus)
|
2026-03-30 20:58:12 +00:00
|
|
|
if !ok || agenticStatus == nil {
|
2026-03-30 19:22:49 +00:00
|
|
|
return core.Result{Value: core.E("runner.ReadStatusResult", "invalid status payload", nil), OK: false}
|
|
|
|
|
}
|
2026-03-30 20:58:12 +00:00
|
|
|
workspaceStatus := runnerWorkspaceStatusFromAgentic(agenticStatus)
|
|
|
|
|
if workspaceStatus == nil {
|
2026-03-30 16:24:06 +00:00
|
|
|
return core.Result{Value: core.E("runner.ReadStatusResult", "invalid status payload", nil), OK: false}
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|
2026-03-30 20:58:12 +00:00
|
|
|
return core.Result{Value: workspaceStatus, OK: true}
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 21:58:24 +00:00
|
|
|
// result := runner.WriteStatus("/srv/core/workspace/core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"})
|
|
|
|
|
// core.Println(result.OK)
|
2026-03-30 21:22:54 +00:00
|
|
|
func WriteStatus(workspaceDir string, status *WorkspaceStatus) core.Result {
|
2026-03-30 16:33:14 +00:00
|
|
|
if status == nil {
|
|
|
|
|
return core.Result{Value: core.E("runner.WriteStatus", "status is required", nil), OK: false}
|
2026-03-29 21:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 16:33:14 +00:00
|
|
|
agenticStatus := agenticWorkspaceStatusFromRunner(status)
|
|
|
|
|
if agenticStatus == nil {
|
|
|
|
|
return core.Result{Value: core.E("runner.WriteStatus", "status conversion failed", nil), OK: false}
|
|
|
|
|
}
|
|
|
|
|
agenticStatus.UpdatedAt = time.Now()
|
2026-03-30 21:22:54 +00:00
|
|
|
if writeResult := fs.WriteAtomic(agentic.WorkspaceStatusPath(workspaceDir), core.JSONMarshalString(agenticStatus)); !writeResult.OK {
|
2026-03-30 21:11:06 +00:00
|
|
|
err, _ := writeResult.Value.(error)
|
2026-03-30 16:33:14 +00:00
|
|
|
if err == nil {
|
|
|
|
|
return core.Result{Value: core.E("runner.WriteStatus", "failed to write status", nil), OK: false}
|
|
|
|
|
}
|
|
|
|
|
return core.Result{Value: core.E("runner.WriteStatus", "failed to write status", err), OK: false}
|
2026-03-29 21:27:32 +00:00
|
|
|
}
|
2026-03-30 16:33:14 +00:00
|
|
|
return core.Result{OK: true}
|
feat(runner): extract dispatch runner into independent Core service
Moves concurrency, queue drain, workspace lifecycle, and frozen state
from agentic/prep into pkg/runner/ — a standalone Core service that
communicates via IPC Actions only.
- runner.Register wires Actions: dispatch, status, start, stop, kill, poke
- runner.HandleIPCEvents catches AgentCompleted → ChannelPush + queue poke
- Agentic dispatch asks runner for permission via c.Action("runner.dispatch")
- Dispatch mutex moved to struct-level sync.Mutex (fixes core.Lock init race)
- Registry-based concurrency counting replaces disk scanning
- TrackWorkspace called on both queued and running status writes
- SpawnQueued message added for runner→agentic spawn requests
- ChannelPush message in core/mcp enables any service to push channel events
- 51 new tests covering runner service, queue, and config parsing
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 11:00:47 +00:00
|
|
|
}
|