fix(ax): continue AX comment cleanup

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-30 22:30:05 +00:00
parent ce7c81a15b
commit 40a26ca28c
18 changed files with 152 additions and 393 deletions

View file

@ -14,10 +14,8 @@ type applicationCommandSet struct {
core *core.Core
}
// startupArgs applies early log flags, then returns args for c.Cli().Run().
//
// args := startupArgs()
// _ = c.Cli().Run(args...)
// args := startupArgs()
// _ = c.Cli().Run(args...)
func startupArgs() []string {
previous := flag.CommandLine
commandLine := flag.NewFlagSet("core-agent", flag.ContinueOnError)
@ -48,10 +46,8 @@ func startupArgs() []string {
return applyLogLevel(commandLine.Args())
}
// applyLogLevel strips log-level flags from args and applies the level in-order.
//
// args := applyLogLevel([]string{"version", "-q"})
// args := applyLogLevel([]string{"--debug", "mcp"})
// args := applyLogLevel([]string{"version", "-q"})
// args := applyLogLevel([]string{"--debug", "mcp"})
func applyLogLevel(args []string) []string {
var cleaned []string
for _, arg := range args {

View file

@ -10,10 +10,8 @@ import (
"forge.lthn.ai/core/mcp/pkg/mcp"
)
// registerMCPService builds the MCP service from registered AX subsystems.
//
// c := core.New(core.WithService(registerMCPService))
// _, ok := core.ServiceFor[*mcp.Service](c, "mcp")
// c := core.New(core.WithService(registerMCPService))
// _, ok := core.ServiceFor[*mcp.Service](c, "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}

View file

@ -4,10 +4,8 @@ package main
import agentpkg "dappco.re/go/agent"
// updateChannel maps the build version to the release channel.
//
// agentpkg.Version = "0.15.0"
// updateChannel() // "stable"
// agentpkg.Version = "0.15.0"
// updateChannel() // "stable"
func updateChannel() string {
switch {
case agentpkg.Version == "" || agentpkg.Version == "dev":

View file

@ -1,7 +1,6 @@
// SPDX-License-Identifier: EUPL-1.2
// Package agentic provides MCP tools for agent orchestration.
// Prepares workspaces and dispatches subagents.
// core.New(core.WithService(agentic.Register))
package agentic
import (
@ -18,15 +17,10 @@ import (
"gopkg.in/yaml.v3"
)
// AgentOptions configures the agentic service.
//
// options := agentic.AgentOptions{}
// options := agentic.AgentOptions{}
type AgentOptions struct{}
// PrepSubsystem provides agentic MCP tools for workspace orchestration.
// Agent lifecycle events are broadcast via s.Core().ACTION(messages.AgentCompleted{}).
//
// core.New(core.WithService(agentic.Register))
// core.New(core.WithService(agentic.Register))
type PrepSubsystem struct {
*core.ServiceRuntime[AgentOptions]
forge *forge.Forge
@ -47,10 +41,8 @@ type PrepSubsystem struct {
var _ coremcp.Subsystem = (*PrepSubsystem)(nil)
// NewPrep creates an agentic subsystem.
//
// subsystem := agentic.NewPrep()
// subsystem.SetCompletionNotifier(monitor)
// subsystem := agentic.NewPrep()
// subsystem.SetCompletionNotifier(monitor)
func NewPrep() *PrepSubsystem {
home := HomeDir()
@ -81,18 +73,13 @@ func NewPrep() *PrepSubsystem {
}
}
// Use core.New(core.WithService(agentic.Register)) for new code.
//
// prep.SetCore(c)
// prep.SetCore(c)
func (s *PrepSubsystem) SetCore(c *core.Core) {
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
}
// OnStartup implements core.Startable — registers named Actions, starts the queue runner,
// and registers CLI commands. The Action registry IS the capability map.
//
// c.Action("agentic.dispatch").Run(ctx, options)
// c.Actions() // ["agentic.dispatch", "agentic.prep", "agentic.status", ...]
// c.Action("agentic.dispatch").Run(ctx, options)
// c.Actions() // ["agentic.dispatch", "agentic.prep", "agentic.status", ...]
func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
c := s.Core()
@ -214,10 +201,8 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
// registerCommands is in commands.go
// OnShutdown implements core.Stoppable and freezes the queue.
//
// subsystem := agentic.NewPrep()
// _ = subsystem.OnShutdown(context.Background())
// subsystem := agentic.NewPrep()
// _ = subsystem.OnShutdown(context.Background())
func (s *PrepSubsystem) OnShutdown(ctx context.Context) core.Result {
s.frozen = true
return core.Result{OK: true}
@ -243,20 +228,16 @@ func (s *PrepSubsystem) hydrateWorkspaces() {
}
}
// TrackWorkspace registers or updates a workspace in the in-memory Registry.
//
// s.TrackWorkspace("core/go-io/task-5", st)
// s.TrackWorkspace("core/go-io/task-5", st)
func (s *PrepSubsystem) TrackWorkspace(name string, st *WorkspaceStatus) {
if s.workspaces != nil {
s.workspaces.Set(name, st)
}
}
// Workspaces returns the workspace Registry for cross-cutting queries.
//
// s.Workspaces().Names() // all workspace names
// s.Workspaces().List("core/*") // org-scoped workspaces
// s.Workspaces().Each(func(name string, st *WorkspaceStatus) { ... })
// s.Workspaces().Names() // all workspace names
// s.Workspaces().List("core/*") // org-scoped workspaces
// s.Workspaces().Each(func(name string, st *WorkspaceStatus) { ... })
func (s *PrepSubsystem) Workspaces() *core.Registry[*WorkspaceStatus] {
return s.workspaces
}
@ -268,17 +249,13 @@ func envOr(key, fallback string) string {
return fallback
}
// Name identifies the MCP subsystem.
//
// subsystem := agentic.NewPrep()
// name := subsystem.Name()
// _ = name // "agentic"
// subsystem := agentic.NewPrep()
// name := subsystem.Name()
// _ = name // "agentic"
func (s *PrepSubsystem) Name() string { return "agentic" }
// RegisterTools publishes the agentic MCP tools on the server.
//
// subsystem := agentic.NewPrep()
// subsystem.RegisterTools(server)
// subsystem := agentic.NewPrep()
// subsystem.RegisterTools(server)
func (s *PrepSubsystem) RegisterTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{
Name: "agentic_prep_workspace",
@ -306,18 +283,13 @@ func (s *PrepSubsystem) RegisterTools(server *mcp.Server) {
s.registerWatchTool(server)
}
// Shutdown satisfies mcp.SubsystemWithShutdown for clean server teardown.
//
// subsystem := agentic.NewPrep()
// _ = subsystem.Shutdown(context.Background())
// subsystem := agentic.NewPrep()
// _ = subsystem.Shutdown(context.Background())
func (s *PrepSubsystem) Shutdown(_ context.Context) error { return nil }
// --- Input/Output types ---
// PrepInput is the input for agentic_prep_workspace.
// One of Issue, PR, Branch, or Tag is required.
//
// input := agentic.PrepInput{Repo: "go-io", Issue: 15, Task: "Migrate to Core primitives"}
// input := agentic.PrepInput{Repo: "go-io", Issue: 15, Task: "Migrate to Core primitives"}
type PrepInput struct {
Repo string `json:"repo"` // required: e.g. "go-io"
Org string `json:"org,omitempty"` // default "core"
@ -334,9 +306,7 @@ type PrepInput struct {
DryRun bool `json:"dry_run,omitempty"` // preview without executing
}
// PrepOutput is the output for agentic_prep_workspace.
//
// out := agentic.PrepOutput{Success: true, WorkspaceDir: ".core/workspace/core/go-io/task-15"}
// out := agentic.PrepOutput{Success: true, WorkspaceDir: ".core/workspace/core/go-io/task-15"}
type PrepOutput struct {
Success bool `json:"success"`
WorkspaceDir string `json:"workspace_dir"`
@ -348,10 +318,8 @@ type PrepOutput struct {
Resumed bool `json:"resumed"`
}
// workspaceDir resolves the workspace path from the input identifier.
//
// dir := workspaceDir("core", "go-io", PrepInput{Issue: 15})
// // → ".core/workspace/core/go-io/task-15"
// dir := workspaceDir("core", "go-io", PrepInput{Issue: 15})
// dir == ".core/workspace/core/go-io/task-15"
func workspaceDir(org, repo string, input PrepInput) (string, error) {
r := workspaceDirResult(org, repo, input)
if !r.OK {
@ -368,10 +336,8 @@ func workspaceDir(org, repo string, input PrepInput) (string, error) {
return workspaceDir, nil
}
// workspaceDirResult resolves the workspace path and returns core.Result.
//
// r := workspaceDirResult("core", "go-io", PrepInput{Issue: 15})
// if r.OK { workspaceDir := r.Value.(string) }
// r := workspaceDirResult("core", "go-io", PrepInput{Issue: 15})
// if r.OK { workspaceDir := r.Value.(string) }
func workspaceDirResult(org, repo string, input PrepInput) core.Result {
orgName := core.ValidateName(org)
if !orgName.OK {

View file

@ -10,18 +10,14 @@ import (
"gopkg.in/yaml.v3"
)
// DispatchConfig controls agent dispatch behaviour.
//
// config := agentic.DispatchConfig{DefaultAgent: "claude", DefaultTemplate: "coding"}
// config := agentic.DispatchConfig{DefaultAgent: "claude", DefaultTemplate: "coding"}
type DispatchConfig struct {
DefaultAgent string `yaml:"default_agent"`
DefaultTemplate string `yaml:"default_template"`
WorkspaceRoot string `yaml:"workspace_root"`
}
// RateConfig controls pacing between task dispatches.
//
// rate := agentic.RateConfig{ResetUTC: "06:00", SustainedDelay: 120, BurstWindow: 2, BurstDelay: 15}
// rate := agentic.RateConfig{ResetUTC: "06:00", SustainedDelay: 120, BurstWindow: 2, BurstDelay: 15}
type RateConfig struct {
ResetUTC string `yaml:"reset_utc"` // Daily quota reset time (UTC), e.g. "06:00"
DailyLimit int `yaml:"daily_limit"` // Max requests per day (0 = unknown)
@ -31,13 +27,12 @@ type RateConfig struct {
BurstDelay int `yaml:"burst_delay"` // Delay during burst window
}
// ConcurrencyLimit supports both flat (int) and nested (map with total + per-model) formats.
// claude: 1 → Total=1, Models=nil
// codex: → Total=2, Models={"gpt-5.4": 1, "gpt-5.3-codex-spark": 1}
//
// claude: 1 → Total=1, Models=nil
// codex: → Total=2, Models={"gpt-5.4": 1, "gpt-5.3-codex-spark": 1}
// total: 2
// gpt-5.4: 1
// gpt-5.3-codex-spark: 1
// total: 2
// gpt-5.4: 1
// gpt-5.3-codex-spark: 1
type ConcurrencyLimit struct {
Total int
Models map[string]int
@ -65,9 +60,7 @@ func (c *ConcurrencyLimit) UnmarshalYAML(value *yaml.Node) error {
return nil
}
// AgentsConfig is the root of config/agents.yaml.
//
// config := agentic.AgentsConfig{Version: 1, Dispatch: agentic.DispatchConfig{DefaultAgent: "claude"}}
// config := agentic.AgentsConfig{Version: 1, Dispatch: agentic.DispatchConfig{DefaultAgent: "claude"}}
type AgentsConfig struct {
Version int `yaml:"version"`
Dispatch DispatchConfig `yaml:"dispatch"`
@ -235,9 +228,7 @@ func baseAgent(agent string) string {
return core.SplitN(agent, ":", 2)[0]
}
// canDispatchAgent checks both pool-level and per-model concurrency limits.
//
// codex: {total: 2, models: {gpt-5.4: 1}} → max 2 codex total, max 1 gpt-5.4
// codex: {total: 2, models: {gpt-5.4: 1}} → max 2 codex total, max 1 gpt-5.4
func (s *PrepSubsystem) canDispatchAgent(agent string) bool {
var concurrency map[string]ConcurrencyLimit
if s.ServiceRuntime != nil {

View file

@ -10,14 +10,8 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// statusPath := agentic.WorkspaceStatusPath("/srv/.core/workspace/core/go-io/task-5")
// blockedPath := agentic.WorkspaceBlockedPath("/srv/.core/workspace/core/go-io/task-5")
// logs := agentic.WorkspaceLogFiles("/srv/.core/workspace/core/go-io/task-5")
// WorkspaceStatus represents the current state of an agent workspace.
//
// result := ReadStatusResult(workspaceDir)
// if result.OK && result.Value.(*WorkspaceStatus).Status == "completed" { autoCreatePR(workspaceDir) }
// result := ReadStatusResult(workspaceDir)
// if result.OK && result.Value.(*WorkspaceStatus).Status == "completed" { autoCreatePR(workspaceDir) }
type WorkspaceStatus struct {
Status string `json:"status"` // running, completed, blocked, failed
Agent string `json:"agent"` // gemini, claude, codex
@ -35,12 +29,9 @@ type WorkspaceStatus struct {
PRURL string `json:"pr_url,omitempty"` // pull request URL (after PR created)
}
// WorkspaceQuery is the QUERY type for workspace state lookups.
// Returns the workspace Registry via c.QUERY(agentic.WorkspaceQuery{}).
//
// r := c.QUERY(agentic.WorkspaceQuery{})
// if r.OK { reg := r.Value.(*core.Registry[*WorkspaceStatus]) }
// r := c.QUERY(agentic.WorkspaceQuery{Name: "core/go-io/task-5"})
// r := c.QUERY(agentic.WorkspaceQuery{})
// if r.OK { reg := r.Value.(*core.Registry[*WorkspaceStatus]) }
// r := c.QUERY(agentic.WorkspaceQuery{Name: "core/go-io/task-5"})
type WorkspaceQuery struct {
Name string // specific workspace (empty = all)
Status string // filter by status (empty = all)
@ -58,10 +49,8 @@ func writeStatus(workspaceDir string, status *WorkspaceStatus) error {
return nil
}
// writeStatusResult writes status.json and returns core.Result.
//
// result := writeStatusResult("/srv/core/workspace/core/go-io/task-5", &WorkspaceStatus{Status: "running"})
// if result.OK { return }
// result := writeStatusResult("/srv/core/workspace/core/go-io/task-5", &WorkspaceStatus{Status: "running"})
// if result.OK { return }
func writeStatusResult(workspaceDir string, status *WorkspaceStatus) core.Result {
if status == nil {
return core.Result{Value: core.E("writeStatus", "status is required", nil), OK: false}
@ -80,10 +69,8 @@ func writeStatusResult(workspaceDir string, status *WorkspaceStatus) core.Result
return core.Result{OK: true}
}
// ReadStatusResult parses status.json and returns a WorkspaceStatus pointer.
//
// result := ReadStatusResult("/path/to/workspace")
// if result.OK { workspaceStatus := result.Value.(*WorkspaceStatus) }
// result := ReadStatusResult("/path/to/workspace")
// if result.OK { workspaceStatus := result.Value.(*WorkspaceStatus) }
func ReadStatusResult(workspaceDir string) core.Result {
r := fs.Read(WorkspaceStatusPath(workspaceDir))
if !r.OK {
@ -104,10 +91,8 @@ func ReadStatusResult(workspaceDir string) core.Result {
return core.Result{Value: &s, OK: true}
}
// workspaceStatusValue extracts a WorkspaceStatus from a Result.
//
// result := ReadStatusResult("/path/to/workspace")
// workspaceStatus, ok := workspaceStatusValue(result)
// result := ReadStatusResult("/path/to/workspace")
// workspaceStatus, ok := workspaceStatusValue(result)
func workspaceStatusValue(result core.Result) (*WorkspaceStatus, bool) {
workspaceStatus, ok := result.Value.(*WorkspaceStatus)
if !ok || workspaceStatus == nil {
@ -118,19 +103,14 @@ func workspaceStatusValue(result core.Result) (*WorkspaceStatus, bool) {
// --- agentic_status tool ---
// StatusInput is the input for agentic_status.
//
// input := agentic.StatusInput{Workspace: "core/go-io/task-42", Limit: 50}
// input := agentic.StatusInput{Workspace: "core/go-io/task-42", Limit: 50}
type StatusInput struct {
Workspace string `json:"workspace,omitempty"` // specific workspace name, or empty for all
Limit int `json:"limit,omitempty"` // max results (default 100)
Status string `json:"status,omitempty"` // filter: running, completed, failed, blocked
}
// StatusOutput is the output for agentic_status.
// Returns stats by default. Only blocked workspaces are listed (they need attention).
//
// out := agentic.StatusOutput{Total: 42, Running: 3, Queued: 10, Completed: 25}
// out := agentic.StatusOutput{Total: 42, Running: 3, Queued: 10, Completed: 25}
type StatusOutput struct {
Total int `json:"total"`
Running int `json:"running"`
@ -140,9 +120,7 @@ type StatusOutput struct {
Blocked []BlockedInfo `json:"blocked,omitempty"`
}
// BlockedInfo shows a workspace that needs human input.
//
// info := agentic.BlockedInfo{Name: "core/go-io/task-4", Repo: "go-io", Question: "Which API version?"}
// info := agentic.BlockedInfo{Name: "core/go-io/task-4", Repo: "go-io", Question: "Which API version?"}
type BlockedInfo struct {
Name string `json:"name"`
Repo string `json:"repo"`

View file

@ -1,9 +1,7 @@
// SPDX-License-Identifier: EUPL-1.2
// Package brain gives MCP and HTTP services the same OpenBrain capability map.
//
// subsystem := brain.New(nil)
// core.Println(subsystem.Name())
// subsystem := brain.New(nil)
// core.Println(subsystem.Name()) // "brain"
package brain
import (
@ -15,9 +13,8 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// fs provides unrestricted filesystem access for shared brain credentials.
// keyPath := core.JoinPath(home, ".claude", "brain.key")
//
// keyPath := core.JoinPath(home, ".claude", "brain.key")
// if readResult := fs.Read(keyPath); readResult.OK {
// apiKey = core.Trim(readResult.Value.(string))
// }
@ -27,40 +24,31 @@ func stringField(values map[string]any, key string) string {
return core.Sprint(values[key])
}
// errBridgeNotAvailable is returned when a tool requires the Laravel bridge
// but it has not been initialised (headless mode).
// core.E("brain", "bridge not available", nil)
var errBridgeNotAvailable = core.E("brain", "bridge not available", nil)
// Subsystem routes `brain_*` MCP tools through the shared IDE bridge.
//
// subsystem := brain.New(nil)
// core.Println(subsystem.Name()) // "brain"
// subsystem := brain.New(nil)
// core.Println(subsystem.Name()) // "brain"
type Subsystem struct {
bridge *ide.Bridge
}
// New builds the bridge-backed OpenBrain subsystem used by MCP.
//
// subsystem := brain.New(nil)
// core.Println(subsystem.Name())
// subsystem := brain.New(nil)
// core.Println(subsystem.Name())
func New(bridge *ide.Bridge) *Subsystem {
return &Subsystem{bridge: bridge}
}
// name := subsystem.Name() // "brain"
// name := subsystem.Name() // "brain"
func (s *Subsystem) Name() string { return "brain" }
// RegisterTools publishes the bridge-backed brain tools on an MCP server.
//
// subsystem := brain.New(nil)
// subsystem.RegisterTools(server)
// subsystem := brain.New(nil)
// subsystem.RegisterTools(server)
func (s *Subsystem) RegisterTools(server *mcp.Server) {
s.registerBrainTools(server)
}
// Shutdown satisfies the MCP subsystem lifecycle without extra cleanup.
//
// _ = subsystem.Shutdown(context.Background())
// _ = subsystem.Shutdown(context.Background())
func (s *Subsystem) Shutdown(_ context.Context) error {
return nil
}

View file

@ -12,10 +12,8 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// DirectSubsystem talks to OpenBrain over HTTP without the IDE bridge.
//
// subsystem := brain.NewDirect()
// core.Println(subsystem.Name()) // "brain"
// subsystem := brain.NewDirect()
// core.Println(subsystem.Name()) // "brain"
type DirectSubsystem struct {
apiURL string
apiKey string
@ -23,10 +21,8 @@ type DirectSubsystem struct {
var _ coremcp.Subsystem = (*DirectSubsystem)(nil)
// NewDirect builds the HTTP-backed OpenBrain subsystem.
//
// subsystem := brain.NewDirect()
// core.Println(subsystem.Name())
// subsystem := brain.NewDirect()
// core.Println(subsystem.Name())
func NewDirect() *DirectSubsystem {
apiURL := core.Env("CORE_BRAIN_URL")
if apiURL == "" {
@ -56,13 +52,11 @@ func NewDirect() *DirectSubsystem {
}
}
// name := subsystem.Name() // "brain"
// name := subsystem.Name() // "brain"
func (s *DirectSubsystem) Name() string { return "brain" }
// RegisterTools publishes the direct `brain_*` and `agent_*` tools on an MCP server.
//
// subsystem := brain.NewDirect()
// subsystem.RegisterTools(server)
// subsystem := brain.NewDirect()
// subsystem.RegisterTools(server)
func (s *DirectSubsystem) RegisterTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{
Name: "brain_remember",
@ -83,9 +77,7 @@ func (s *DirectSubsystem) RegisterTools(server *mcp.Server) {
s.RegisterMessagingTools(server)
}
// Shutdown satisfies the MCP subsystem lifecycle without extra cleanup.
//
// _ = subsystem.Shutdown(context.Background())
// _ = subsystem.Shutdown(context.Background())
func (s *DirectSubsystem) Shutdown(_ context.Context) error { return nil }
func brainKeyPath(home string) string {

View file

@ -10,10 +10,8 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// RegisterMessagingTools adds direct agent messaging tools to an MCP server.
//
// subsystem := brain.NewDirect()
// subsystem.RegisterMessagingTools(server)
// subsystem := brain.NewDirect()
// subsystem.RegisterMessagingTools(server)
func (s *DirectSubsystem) RegisterMessagingTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{
Name: "agent_send",
@ -33,34 +31,26 @@ func (s *DirectSubsystem) RegisterMessagingTools(server *mcp.Server) {
// Input/Output types
// SendInput is the payload for `agent_send`.
//
// brain.SendInput{To: "charon", Subject: "status update", Content: "deploy complete"}
// brain.SendInput{To: "charon", Subject: "status update", Content: "deploy complete"}
type SendInput struct {
To string `json:"to"`
Content string `json:"content"`
Subject string `json:"subject,omitempty"`
}
// SendOutput reports the stored direct message.
//
// brain.SendOutput{Success: true, ID: 42, To: "charon"}
// brain.SendOutput{Success: true, ID: 42, To: "charon"}
type SendOutput struct {
Success bool `json:"success"`
ID int `json:"id"`
To string `json:"to"`
}
// InboxInput selects which inbox `agent_inbox` should read.
//
// brain.InboxInput{Agent: "cladius"}
// brain.InboxInput{Agent: "cladius"}
type InboxInput struct {
Agent string `json:"agent,omitempty"`
}
// MessageItem is one inbox or conversation entry.
//
// brain.MessageItem{ID: 7, From: "cladius", To: "charon", Content: "all green"}
// brain.MessageItem{ID: 7, From: "cladius", To: "charon", Content: "all green"}
type MessageItem struct {
ID int `json:"id"`
From string `json:"from"`
@ -71,24 +61,18 @@ type MessageItem struct {
CreatedAt string `json:"created_at"`
}
// InboxOutput returns the latest direct messages for one agent.
//
// brain.InboxOutput{Success: true, Messages: []brain.MessageItem{{ID: 1, From: "charon", To: "cladius"}}}
// brain.InboxOutput{Success: true, Messages: []brain.MessageItem{{ID: 1, From: "charon", To: "cladius"}}}
type InboxOutput struct {
Success bool `json:"success"`
Messages []MessageItem `json:"messages"`
}
// ConversationInput selects the thread `agent_conversation` should load.
//
// brain.ConversationInput{Agent: "charon"}
// brain.ConversationInput{Agent: "charon"}
type ConversationInput struct {
Agent string `json:"agent"`
}
// ConversationOutput returns a direct message thread with another agent.
//
// brain.ConversationOutput{Success: true, Messages: []brain.MessageItem{{ID: 10, From: "cladius", To: "charon"}}}
// brain.ConversationOutput{Success: true, Messages: []brain.MessageItem{{ID: 10, From: "cladius", To: "charon"}}}
type ConversationOutput struct {
Success bool `json:"success"`
Messages []MessageItem `json:"messages"`

View file

@ -33,10 +33,8 @@ const (
statusServiceUnavailable = 503
)
// NewProvider builds the HTTP provider around the IDE bridge and WS hub.
//
// p := brain.NewProvider(bridge, hub)
// core.Println(p.BasePath())
// p := brain.NewProvider(bridge, hub)
// core.Println(p.BasePath())
func NewProvider(bridge *ide.Bridge, hub *ws.Hub) *BrainProvider {
return &BrainProvider{
bridge: bridge,
@ -47,15 +45,11 @@ func NewProvider(bridge *ide.Bridge, hub *ws.Hub) *BrainProvider {
// name := p.Name() // "brain"
func (p *BrainProvider) Name() string { return "brain" }
// BasePath shows where the provider mounts its routes.
//
// base := p.BasePath() // "/api/brain"
// base := p.BasePath() // "/api/brain"
func (p *BrainProvider) BasePath() string { return "/api/brain" }
// Channels lists the WS events emitted after brain actions complete.
//
// channels := p.Channels()
// core.Println(channels[0]) // "brain.remember.complete"
// channels := p.Channels()
// core.Println(channels[0]) // "brain.remember.complete"
func (p *BrainProvider) Channels() []string {
return []string{
"brain.remember.complete",
@ -64,10 +58,8 @@ func (p *BrainProvider) Channels() []string {
}
}
// Element describes the browser component that renders the brain panel.
//
// spec := p.Element()
// core.Println(spec.Tag) // "core-brain-panel"
// spec := p.Element()
// core.Println(spec.Tag) // "core-brain-panel"
func (p *BrainProvider) Element() provider.ElementSpec {
return provider.ElementSpec{
Tag: "core-brain-panel",
@ -75,9 +67,7 @@ func (p *BrainProvider) Element() provider.ElementSpec {
}
}
// RegisterRoutes mounts the provider handlers onto a router group.
//
// p.RegisterRoutes(router.Group("/api/brain"))
// p.RegisterRoutes(router.Group("/api/brain"))
func (p *BrainProvider) RegisterRoutes(rg *gin.RouterGroup) {
rg.POST("/remember", p.remember)
rg.POST("/recall", p.recall)
@ -86,10 +76,8 @@ func (p *BrainProvider) RegisterRoutes(rg *gin.RouterGroup) {
rg.GET("/status", p.status)
}
// Describe returns the route contract used by API discovery and docs.
//
// routes := p.Describe()
// core.Println(routes[0].Path) // "/remember"
// routes := p.Describe()
// core.Println(routes[0].Path) // "/remember"
func (p *BrainProvider) Describe() []api.RouteDescription {
return []api.RouteDescription{
{

View file

@ -13,10 +13,6 @@ import (
// -- Input/Output types -------------------------------------------------------
// RememberInput is the input for brain_remember.
//
// Usage example:
//
// input := brain.RememberInput{
// Content: "Use core.Env for system paths.",
// Type: "convention",
@ -31,10 +27,6 @@ type RememberInput struct {
ExpiresIn int `json:"expires_in,omitempty"`
}
// RememberOutput is the output for brain_remember.
//
// Usage example:
//
// output := brain.RememberOutput{
// Success: true,
// MemoryID: "mem_123",
@ -45,10 +37,6 @@ type RememberOutput struct {
Timestamp time.Time `json:"timestamp"`
}
// RecallInput is the input for brain_recall.
//
// Usage example:
//
// input := brain.RecallInput{
// Query: "core.Env conventions",
// TopK: 5,
@ -59,10 +47,6 @@ type RecallInput struct {
Filter RecallFilter `json:"filter,omitempty"`
}
// RecallFilter holds optional filter criteria for brain_recall.
//
// Usage example:
//
// filter := brain.RecallFilter{
// Project: "agent",
// Type: "convention",
@ -74,10 +58,6 @@ type RecallFilter struct {
MinConfidence float64 `json:"min_confidence,omitempty"`
}
// RecallOutput is the output for brain_recall.
//
// Usage example:
//
// output := brain.RecallOutput{
// Success: true,
// Count: 1,
@ -88,10 +68,6 @@ type RecallOutput struct {
Memories []Memory `json:"memories"`
}
// Memory is a single memory entry returned by recall or list.
//
// Usage example:
//
// memory := brain.Memory{
// ID: "mem_123",
// Type: "convention",
@ -111,10 +87,6 @@ type Memory struct {
UpdatedAt string `json:"updated_at"`
}
// ForgetInput is the input for brain_forget.
//
// Usage example:
//
// input := brain.ForgetInput{
// ID: "mem_123",
// Reason: "superseded",
@ -124,10 +96,6 @@ type ForgetInput struct {
Reason string `json:"reason,omitempty"`
}
// ForgetOutput is the output for brain_forget.
//
// Usage example:
//
// output := brain.ForgetOutput{
// Success: true,
// Forgotten: "mem_123",
@ -138,10 +106,6 @@ type ForgetOutput struct {
Timestamp time.Time `json:"timestamp"`
}
// ListInput is the input for brain_list.
//
// Usage example:
//
// input := brain.ListInput{
// Project: "agent",
// Limit: 20,
@ -153,10 +117,6 @@ type ListInput struct {
Limit int `json:"limit,omitempty"`
}
// ListOutput is the output for brain_list.
//
// Usage example:
//
// output := brain.ListOutput{
// Success: true,
// Count: 2,

View file

@ -1,26 +1,18 @@
// SPDX-License-Identifier: EUPL-1.2
// Package messages defines IPC message types for inter-service communication
// within core-agent. Services emit these via c.ACTION() and handle them via
// c.RegisterAction(). No service imports another — they share only these types.
//
// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Status: "completed"})
// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Status: "completed"})
package messages
// --- Agent Lifecycle ---
// AgentStarted is broadcast when a subagent process is spawned.
//
// c.ACTION(messages.AgentStarted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5"})
// c.ACTION(messages.AgentStarted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5"})
type AgentStarted struct {
Agent string
Repo string
Workspace string
}
// AgentCompleted is broadcast when a subagent process exits.
//
// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed"})
// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed"})
type AgentCompleted struct {
Agent string
Repo string
@ -30,9 +22,7 @@ type AgentCompleted struct {
// --- QA & PR Pipeline ---
// QAResult is broadcast after QA runs on a completed workspace.
//
// c.ACTION(messages.QAResult{Workspace: "core/go-io/task-5", Repo: "go-io", Passed: true})
// c.ACTION(messages.QAResult{Workspace: "core/go-io/task-5", Repo: "go-io", Passed: true})
type QAResult struct {
Workspace string
Repo string
@ -40,9 +30,7 @@ type QAResult struct {
Output string
}
// PRCreated is broadcast after a PR is auto-created on Forge.
//
// c.ACTION(messages.PRCreated{Repo: "go-io", Branch: "agent/fix-tests", PRURL: "https://...", PRNum: 12})
// c.ACTION(messages.PRCreated{Repo: "go-io", Branch: "agent/fix-tests", PRURL: "https://...", PRNum: 12})
type PRCreated struct {
Repo string
Branch string
@ -50,18 +38,14 @@ type PRCreated struct {
PRNum int
}
// PRMerged is broadcast after a PR is auto-verified and merged.
//
// c.ACTION(messages.PRMerged{Repo: "go-io", PRURL: "https://...", PRNum: 12})
// c.ACTION(messages.PRMerged{Repo: "go-io", PRURL: "https://...", PRNum: 12})
type PRMerged struct {
Repo string
PRURL string
PRNum int
}
// PRNeedsReview is broadcast when auto-merge fails and human attention is needed.
//
// c.ACTION(messages.PRNeedsReview{Repo: "go-io", PRNum: 12, Reason: "merge conflict"})
// c.ACTION(messages.PRNeedsReview{Repo: "go-io", PRNum: 12, Reason: "merge conflict"})
type PRNeedsReview struct {
Repo string
PRURL string
@ -71,31 +55,22 @@ type PRNeedsReview struct {
// --- Queue ---
// QueueDrained is broadcast when running=0 and queued=0 (genuinely empty).
//
// c.ACTION(messages.QueueDrained{Completed: 3})
// c.ACTION(messages.QueueDrained{Completed: 3})
type QueueDrained struct {
Completed int
}
// PokeQueue signals the runner to drain the queue immediately.
//
// c.ACTION(messages.PokeQueue{})
// c.ACTION(messages.PokeQueue{})
type PokeQueue struct{}
// SpawnQueued is sent by the runner to request agentic spawn a queued workspace.
// Runner gates (frozen + concurrency), agentic owns the actual process spawn.
//
// c.ACTION(messages.SpawnQueued{Workspace: "core/go-io/task-5", Agent: "codex", Task: "review"})
// c.ACTION(messages.SpawnQueued{Workspace: "core/go-io/task-5", Agent: "codex", Task: "review"})
type SpawnQueued struct {
Workspace string
Agent string
Task string
}
// RateLimitDetected is broadcast when fast failures trigger agent pool backoff.
//
// c.ACTION(messages.RateLimitDetected{Pool: "codex", Duration: "30m"})
// c.ACTION(messages.RateLimitDetected{Pool: "codex", Duration: "30m"})
type RateLimitDetected struct {
Pool string
Duration string
@ -103,27 +78,21 @@ type RateLimitDetected struct {
// --- Monitor Events ---
// HarvestComplete is broadcast when a workspace branch is ready for review.
//
// c.ACTION(messages.HarvestComplete{Repo: "go-io", Branch: "agent/fix-tests", Files: 5})
// c.ACTION(messages.HarvestComplete{Repo: "go-io", Branch: "agent/fix-tests", Files: 5})
type HarvestComplete struct {
Repo string
Branch string
Files int
}
// HarvestRejected is broadcast when a workspace fails safety checks (binaries, size).
//
// c.ACTION(messages.HarvestRejected{Repo: "go-io", Branch: "agent/fix-tests", Reason: "binary detected"})
// c.ACTION(messages.HarvestRejected{Repo: "go-io", Branch: "agent/fix-tests", Reason: "binary detected"})
type HarvestRejected struct {
Repo string
Branch string
Reason string
}
// InboxMessage is broadcast when a new inter-agent message arrives.
//
// c.ACTION(messages.InboxMessage{From: "charon", Subject: "status", Content: "all green"})
// c.ACTION(messages.InboxMessage{From: "charon", Subject: "status", Content: "all green"})
type InboxMessage struct {
From string
Subject string

View file

@ -1,9 +1,7 @@
// SPDX-License-Identifier: EUPL-1.2
// Package monitor keeps workspace state and inbox status visible to MCP clients.
//
// service := monitor.New(monitor.Options{Interval: 30 * time.Second})
// service.RegisterTools(server)
// service := monitor.New(monitor.Options{Interval: 30 * time.Second})
// service.RegisterTools(server)
package monitor
import (

View file

@ -1,9 +1,7 @@
// SPDX-License-Identifier: EUPL-1.2
// Package runner owns agent dispatch and workspace lifecycle.
//
// service := runner.New()
// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"})
// service := runner.New()
// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"})
package runner
import (
@ -16,17 +14,11 @@ import (
core "dappco.re/go/core"
)
// Options configures the runner service.
//
// options := runner.Options{}
// options := runner.Options{}
type Options struct{}
// Service is the agent dispatch runner.
// Manages concurrency limits, queue drain, workspace lifecycle, and frozen state.
// All dispatch requests — MCP tool, CLI, or IPC — go through this service.
//
// service := runner.New()
// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"})
// service := runner.New()
// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"})
type Service struct {
*core.ServiceRuntime[Options]
dispatchMu sync.Mutex
@ -73,13 +65,13 @@ func Register(coreApp *core.Core) core.Result {
return core.Result{Value: service, OK: true}
}
// OnStartup registers Actions and starts the queue runner.
// c.Action("runner.dispatch").Run(ctx, core.NewOptions(
//
// c.Action("runner.dispatch").Run(ctx, core.NewOptions(
// core.Option{Key: "repo", Value: "go-io"},
// core.Option{Key: "agent", Value: "codex"},
// ))
// c.Action("runner.status").Run(ctx, core.NewOptions())
// core.Option{Key: "repo", Value: "go-io"},
// core.Option{Key: "agent", Value: "codex"},
//
// ))
// c.Action("runner.status").Run(ctx, core.NewOptions())
func (s *Service) OnStartup(ctx context.Context) core.Result {
coreApp := s.Core()
@ -99,9 +91,8 @@ func (s *Service) OnStartup(ctx context.Context) core.Result {
return core.Result{OK: true}
}
// OnShutdown freezes the queue.
// result := service.OnShutdown(context.Background())
//
// result := service.OnShutdown(context.Background())
// if result.OK {
// core.Println(service.IsFrozen())
// }
@ -110,9 +101,8 @@ func (s *Service) OnShutdown(_ context.Context) core.Result {
return core.Result{OK: true}
}
// HandleIPCEvents applies runner side-effects for IPC messages.
// service.HandleIPCEvents(c, messages.PokeQueue{})
//
// service.HandleIPCEvents(c, messages.PokeQueue{})
// service.HandleIPCEvents(c, messages.AgentCompleted{
// Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed",
// })
@ -190,16 +180,12 @@ func (s *Service) HandleIPCEvents(coreApp *core.Core, msg core.Message) core.Res
return core.Result{OK: true}
}
// IsFrozen returns whether dispatch is currently frozen.
//
// if s.IsFrozen() { return "queue is frozen" }
// if s.IsFrozen() { return "queue is frozen" }
func (s *Service) IsFrozen() bool {
return s.frozen
}
// Poke signals the runner to check the queue immediately.
//
// s.Poke()
// s.Poke()
func (s *Service) Poke() {
if s.pokeCh == nil {
return
@ -210,11 +196,8 @@ func (s *Service) Poke() {
}
}
// TrackWorkspace registers or updates a workspace in the in-memory Registry.
// Accepts the runner projection directly and the agentic projection from IPC.
//
// s.TrackWorkspace("core/go-io/task-5", &WorkspaceStatus{Status: "running", Agent: "codex"})
// s.TrackWorkspace("core/go-io/task-5", &agentic.WorkspaceStatus{Status: "running", Agent: "codex"})
// s.TrackWorkspace("core/go-io/task-5", &WorkspaceStatus{Status: "running", Agent: "codex"})
// s.TrackWorkspace("core/go-io/task-5", &agentic.WorkspaceStatus{Status: "running", Agent: "codex"})
func (s *Service) TrackWorkspace(name string, status any) {
if s.workspaces == nil {
return
@ -240,17 +223,13 @@ func (s *Service) TrackWorkspace(name string, status any) {
s.workspaces.Delete(core.Concat("pending/", workspaceStatus.Repo))
}
// Workspaces returns the workspace Registry.
//
// s.Workspaces().Each(func(name string, workspaceStatus *WorkspaceStatus) { ... })
// s.Workspaces().Each(func(name string, workspaceStatus *WorkspaceStatus) { ... })
func (s *Service) Workspaces() *core.Registry[*WorkspaceStatus] {
return s.workspaces
}
// handleWorkspaceQuery answers workspace state queries from Core QUERY calls.
//
// result := c.QUERY(runner.WorkspaceQuery{Name: "core/go-io/task-42"})
// result := c.QUERY(runner.WorkspaceQuery{Status: "running"})
// result := c.QUERY(runner.WorkspaceQuery{Name: "core/go-io/task-42"})
// result := c.QUERY(runner.WorkspaceQuery{Status: "running"})
func (s *Service) handleWorkspaceQuery(_ *core.Core, query core.Query) core.Result {
workspaceQuery, ok := query.(WorkspaceQuery)
if !ok {

View file

@ -7,9 +7,7 @@ import (
"gopkg.in/yaml.v3"
)
// ConfigData supplies values when setup renders workspace templates.
//
// data := setup.ConfigData{Name: "agent", Type: "go", Repository: "core/agent"}
// data := setup.ConfigData{Name: "agent", Type: "go", Repository: "core/agent"}
type ConfigData struct {
Name string
Description string
@ -22,17 +20,13 @@ type ConfigData struct {
Env map[string]string
}
// Target describes one build target.
//
// target := setup.Target{OS: "linux", Arch: "amd64"}
// target := setup.Target{OS: "linux", Arch: "amd64"}
type Target struct {
OS string
Arch string
}
// Command defines one named command in generated config.
//
// command := setup.Command{Name: "unit", Run: "go test ./..."}
// command := setup.Command{Name: "unit", Run: "go test ./..."}
type Command struct {
Name string
Run string
@ -48,10 +42,8 @@ type configValue struct {
Value any
}
// GenerateBuildConfig renders `build.yaml` content for a detected repo type.
//
// r := setup.GenerateBuildConfig("/srv/repos/agent", setup.TypeGo)
// if r.OK { content := r.Value.(string) }
// r := setup.GenerateBuildConfig("/srv/repos/agent", setup.TypeGo)
// if r.OK { content := r.Value.(string) }
func GenerateBuildConfig(path string, projectType ProjectType) core.Result {
name := core.PathBase(path)
sections := []configSection{
@ -95,10 +87,8 @@ func GenerateBuildConfig(path string, projectType ProjectType) core.Result {
return renderConfig(core.Concat(name, " build configuration"), sections)
}
// GenerateTestConfig renders `test.yaml` content for a detected repo type.
//
// r := setup.GenerateTestConfig(setup.TypeGo)
// if r.OK { content := r.Value.(string) }
// r := setup.GenerateTestConfig(setup.TypeGo)
// if r.OK { content := r.Value.(string) }
func GenerateTestConfig(projectType ProjectType) core.Result {
var sections []configSection

View file

@ -1,18 +1,14 @@
// SPDX-License-Identifier: EUPL-1.2
// Package setup provisions `.core/` files and workspace scaffolds for a repo.
//
// service := core.ServiceFor[*setup.Service](core.New(core.WithService(setup.Register)), "setup")
// service := core.ServiceFor[*setup.Service](core.New(core.WithService(setup.Register)), "setup")
package setup
import (
core "dappco.re/go/core"
)
// ProjectType records what setup detected in a repository path.
//
// projectType := setup.Detect("/srv/repos/agent")
// if projectType == setup.TypeGo { /* generate Go defaults */ }
// projectType := setup.Detect("/srv/repos/agent")
// if projectType == setup.TypeGo { /* generate Go defaults */ }
type ProjectType string
const (
@ -23,12 +19,10 @@ const (
TypeUnknown ProjectType = "unknown"
)
// fs provides unrestricted filesystem access for setup operations.
// fs := (&core.Fs{}).NewUnrestricted()
var fs = (&core.Fs{}).NewUnrestricted()
// Detect inspects a repository path and returns the primary project type.
//
// projectType := setup.Detect("./repo")
// projectType := setup.Detect("./repo")
func Detect(path string) ProjectType {
base := absolutePath(path)
checks := []struct {
@ -48,9 +42,7 @@ func Detect(path string) ProjectType {
return TypeUnknown
}
// DetectAll returns every detected project type for a polyglot repository.
//
// types := setup.DetectAll("./repo")
// types := setup.DetectAll("./repo")
func DetectAll(path string) []ProjectType {
base := absolutePath(path)
var projectTypes []ProjectType

View file

@ -26,16 +26,12 @@ func Register(c *core.Core) core.Result {
return core.Result{Value: service, OK: true}
}
// OnStartup keeps the setup service ready for Core startup hooks.
//
// result := service.OnStartup(context.Background())
// result := service.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) core.Result {
return core.Result{OK: true}
}
// DetectGitRemote reads `origin` and returns `owner/repo` when available.
//
// remote := service.DetectGitRemote("/srv/repos/agent")
// remote := service.DetectGitRemote("/srv/repos/agent")
func (s *Service) DetectGitRemote(path string) string {
result := s.Core().Process().RunIn(context.Background(), path, "git", "remote", "get-url", "origin")
if !result.OK {

View file

@ -7,10 +7,8 @@ import (
core "dappco.re/go/core"
)
// Options controls one setup run.
//
// result := service.Run(setup.Options{Path: ".", Template: "auto", Force: true})
// if !result.OK { core.Print(nil, "%v", result.Value) }
// result := service.Run(setup.Options{Path: ".", Template: "auto", Force: true})
// if !result.OK { core.Print(nil, "%v", result.Value) }
type Options struct {
Path string // Target directory (default: cwd)
DryRun bool // Preview only, don't write
@ -18,10 +16,8 @@ type Options struct {
Template string // Workspace template or compatibility alias (default, review, security, agent, go, php, gui, auto)
}
// Run generates `.core/` files and optional workspace scaffolding for a repo.
//
// result := service.Run(setup.Options{Path: ".", Template: "auto"})
// core.Println(result.OK)
// result := service.Run(setup.Options{Path: ".", Template: "auto"})
// core.Println(result.OK)
func (s *Service) Run(options Options) core.Result {
if options.Path == "" {
options.Path = core.Env("DIR_CWD")