2026-03-16 11:10:33 +00:00
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
2026-03-17 05:56:22 +00:00
"syscall"
2026-03-16 11:10:33 +00:00
"time"
2026-03-22 03:41:07 +00:00
core "dappco.re/go/core"
2026-03-22 01:27:48 +00:00
"dappco.re/go/core/process"
2026-03-16 11:10:33 +00:00
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// DispatchInput is the input for agentic_dispatch.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// input := agentic.DispatchInput{Repo: "go-io", Task: "Fix the failing tests", Agent: "codex", Issue: 15}
2026-03-16 11:10:33 +00:00
type DispatchInput struct {
Repo string ` json:"repo" ` // Target repo (e.g. "go-io")
Org string ` json:"org,omitempty" ` // Forge org (default "core")
Task string ` json:"task" ` // What the agent should do
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
Agent string ` json:"agent,omitempty" ` // "codex" (default), "claude", "gemini"
2026-03-16 11:10:33 +00:00
Template string ` json:"template,omitempty" ` // "conventions", "security", "coding" (default)
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
PlanTemplate string ` json:"plan_template,omitempty" ` // Plan template slug
2026-03-16 11:10:33 +00:00
Variables map [ string ] string ` json:"variables,omitempty" ` // Template variable substitution
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
Persona string ` json:"persona,omitempty" ` // Persona slug
Issue int ` json:"issue,omitempty" ` // Forge issue number → workspace: task-{num}/
PR int ` json:"pr,omitempty" ` // PR number → workspace: pr-{num}/
Branch string ` json:"branch,omitempty" ` // Branch → workspace: {branch}/
Tag string ` json:"tag,omitempty" ` // Tag → workspace: {tag}/ (immutable)
2026-03-16 11:10:33 +00:00
DryRun bool ` json:"dry_run,omitempty" ` // Preview without executing
}
// DispatchOutput is the output for agentic_dispatch.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// out := agentic.DispatchOutput{Success: true, Agent: "codex", Repo: "go-io", WorkspaceDir: ".core/workspace/core/go-io/task-15"}
2026-03-16 11:10:33 +00:00
type DispatchOutput struct {
Success bool ` json:"success" `
Agent string ` json:"agent" `
Repo string ` json:"repo" `
WorkspaceDir string ` json:"workspace_dir" `
Prompt string ` json:"prompt,omitempty" `
PID int ` json:"pid,omitempty" `
OutputFile string ` json:"output_file,omitempty" `
}
func ( s * PrepSubsystem ) registerDispatchTool ( server * mcp . Server ) {
mcp . AddTool ( server , & mcp . Tool {
Name : "agentic_dispatch" ,
Description : "Dispatch a subagent (Gemini, Codex, or Claude) to work on a task. Preps a sandboxed workspace first, then spawns the agent inside it. Templates: conventions, security, coding." ,
} , s . dispatch )
}
// agentCommand returns the command and args for a given agent type.
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// Supports model variants: "gemini", "gemini:flash", "codex", "claude", "claude:haiku".
2026-03-16 11:10:33 +00:00
func agentCommand ( agent , prompt string ) ( string , [ ] string , error ) {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
parts := core . SplitN ( agent , ":" , 2 )
2026-03-16 11:10:33 +00:00
base := parts [ 0 ]
model := ""
if len ( parts ) > 1 {
model = parts [ 1 ]
}
switch base {
case "gemini" :
args := [ ] string { "-p" , prompt , "--yolo" , "--sandbox" }
if model != "" {
args = append ( args , "-m" , "gemini-2.5-" + model )
}
return "gemini" , args , nil
case "codex" :
2026-03-17 17:45:04 +00:00
if model == "review" {
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
return "codex" , [ ] string { "review" , "--base" , "HEAD~1" } , nil
2026-03-17 17:45:04 +00:00
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// Codex runs from repo/ which IS a git repo — no --skip-git-repo-check
feat: devops plugin, CLI commands, Codex dispatch fixes, AX sweep
DevOps plugin (5 skills):
- install-core-agent, repair-core-agent, merge-workspace,
update-deps, clean-workspaces
CLI commands: version, check, extract for diagnostics.
Codex dispatch: --skip-git-repo-check, removed broken
--model-reasoning-effort, --sandbox workspace-write via
--full-auto. Workspace template extracts to wsDir not srcDir.
AX sweep (Codex-generated): sanitise.go extracted from prep/plan,
mirror.go JSON parsing via encoding/json, setup/config.go URL
parsing via net/url, strings/fmt imports eliminated from setup.
CODEX.md template updated with Env/Path patterns.
Review workspace template with audit-only PROMPT.md.
Marketplace updated with devops plugin.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:30:27 +00:00
args := [ ] string {
"exec" ,
"--full-auto" ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
"-o" , "../.meta/agent-codex.log" ,
feat: devops plugin, CLI commands, Codex dispatch fixes, AX sweep
DevOps plugin (5 skills):
- install-core-agent, repair-core-agent, merge-workspace,
update-deps, clean-workspaces
CLI commands: version, check, extract for diagnostics.
Codex dispatch: --skip-git-repo-check, removed broken
--model-reasoning-effort, --sandbox workspace-write via
--full-auto. Workspace template extracts to wsDir not srcDir.
AX sweep (Codex-generated): sanitise.go extracted from prep/plan,
mirror.go JSON parsing via encoding/json, setup/config.go URL
parsing via net/url, strings/fmt imports eliminated from setup.
CODEX.md template updated with Env/Path patterns.
Review workspace template with audit-only PROMPT.md.
Marketplace updated with devops plugin.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:30:27 +00:00
prompt ,
}
if model != "" {
args = append ( args [ : 3 ] , append ( [ ] string { "--model" , model } , args [ 3 : ] ... ) ... )
}
return "codex" , args , nil
2026-03-16 11:10:33 +00:00
case "claude" :
2026-03-17 04:12:54 +00:00
args := [ ] string {
"-p" , prompt ,
"--output-format" , "text" ,
2026-03-17 17:45:04 +00:00
"--dangerously-skip-permissions" ,
2026-03-17 04:12:54 +00:00
"--no-session-persistence" ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
"--append-system-prompt" , "SANDBOX: You are restricted to the current directory only. " +
"Do NOT use absolute paths. Do NOT navigate outside this repository." ,
2026-03-17 04:12:54 +00:00
}
2026-03-16 11:10:33 +00:00
if model != "" {
args = append ( args , "--model" , model )
}
return "claude" , args , nil
2026-03-17 17:45:04 +00:00
case "coderabbit" :
args := [ ] string { "review" , "--plain" , "--base" , "HEAD~1" }
if model != "" {
args = append ( args , "--type" , model )
}
if prompt != "" {
args = append ( args , "--config" , "CLAUDE.md" )
}
return "coderabbit" , args , nil
2026-03-16 11:10:33 +00:00
case "local" :
refactor(agentic): adopt core.Env() + core.Path() across package
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 10:15:15 +00:00
script := core . JoinPath ( core . Env ( "DIR_HOME" ) , "Code" , "core" , "agent" , "scripts" , "local-agent.sh" )
2026-03-16 11:10:33 +00:00
return "bash" , [ ] string { script , prompt } , nil
default :
2026-03-22 03:41:07 +00:00
return "" , nil , core . E ( "agentCommand" , "unknown agent: " + agent , nil )
2026-03-16 11:10:33 +00:00
}
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// spawnAgent launches an agent process in the repo/ directory.
// Output is captured and written to .meta/agent-{agent}.log on completion.
func ( s * PrepSubsystem ) spawnAgent ( agent , prompt , wsDir string ) ( int , string , error ) {
2026-03-16 17:52:55 +00:00
command , args , err := agentCommand ( agent , prompt )
if err != nil {
return 0 , "" , err
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
repoDir := core . JoinPath ( wsDir , "repo" )
metaDir := core . JoinPath ( wsDir , ".meta" )
outputFile := core . JoinPath ( metaDir , core . Sprintf ( "agent-%s.log" , agent ) )
2026-03-16 17:52:55 +00:00
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// Clean up stale BLOCKED.md from previous runs
fs . Delete ( core . JoinPath ( repoDir , "BLOCKED.md" ) )
2026-03-21 16:53:55 +00:00
2026-03-16 17:52:55 +00:00
proc , err := process . StartWithOptions ( context . Background ( ) , process . RunOptions {
test(brain): add unit tests for recall, remember, messaging
Coverage: 5.3% → 92.8%. Tests cover DirectSubsystem (apiCall, remember,
recall, forget via httptest), messaging (sendMessage, inbox, conversation,
parseMessages, toInt), BrainProvider (gin handlers, routes, describe,
status), Subsystem bridge-backed handlers, and RegisterTools.
Also fixes build error in dispatch.go (removed KillGroup, Timeout,
GracePeriod fields no longer in process.RunOptions).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-21 13:40:20 +00:00
Command : command ,
Args : args ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
Dir : repoDir ,
Env : [ ] string { "TERM=dumb" , "NO_COLOR=1" , "CI=true" } ,
test(brain): add unit tests for recall, remember, messaging
Coverage: 5.3% → 92.8%. Tests cover DirectSubsystem (apiCall, remember,
recall, forget via httptest), messaging (sendMessage, inbox, conversation,
parseMessages, toInt), BrainProvider (gin handlers, routes, describe,
status), Subsystem bridge-backed handlers, and RegisterTools.
Also fixes build error in dispatch.go (removed KillGroup, Timeout,
GracePeriod fields no longer in process.RunOptions).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-21 13:40:20 +00:00
Detach : true ,
2026-03-16 17:52:55 +00:00
} )
if err != nil {
2026-03-22 03:41:07 +00:00
return 0 , "" , core . E ( "dispatch.spawnAgent" , "failed to spawn " + agent , err )
2026-03-16 17:52:55 +00:00
}
2026-03-17 17:45:04 +00:00
proc . CloseStdin ( )
2026-03-16 17:52:55 +00:00
pid := proc . Info ( ) . PID
go func ( ) {
2026-03-17 05:56:22 +00:00
ticker := time . NewTicker ( 5 * time . Second )
defer ticker . Stop ( )
for {
select {
2026-03-17 17:45:04 +00:00
case <- proc . Done ( ) :
goto done
2026-03-17 05:56:22 +00:00
case <- ticker . C :
2026-03-17 17:45:04 +00:00
if err := syscall . Kill ( pid , 0 ) ; err != nil {
goto done
2026-03-17 05:56:22 +00:00
}
}
}
2026-03-17 17:45:04 +00:00
done :
2026-03-16 17:52:55 +00:00
if output := proc . Output ( ) ; output != "" {
2026-03-22 03:41:07 +00:00
fs . Write ( outputFile , output )
2026-03-16 17:52:55 +00:00
}
2026-03-17 17:45:04 +00:00
finalStatus := "completed"
exitCode := proc . Info ( ) . ExitCode
procStatus := proc . Info ( ) . Status
2026-03-17 19:35:15 +00:00
question := ""
2026-03-17 17:45:04 +00:00
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
blockedPath := core . JoinPath ( repoDir , "BLOCKED.md" )
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if r := fs . Read ( blockedPath ) ; r . OK && core . Trim ( r . Value . ( string ) ) != "" {
2026-03-17 17:45:04 +00:00
finalStatus = "blocked"
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
question = core . Trim ( r . Value . ( string ) )
2026-03-17 17:45:04 +00:00
} else if exitCode != 0 || procStatus == "failed" || procStatus == "killed" {
finalStatus = "failed"
2026-03-17 19:35:15 +00:00
if exitCode != 0 {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
question = core . Sprintf ( "Agent exited with code %d" , exitCode )
2026-03-17 17:45:04 +00:00
}
2026-03-16 17:52:55 +00:00
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
if st , stErr := readStatus ( wsDir ) ; stErr == nil {
2026-03-17 19:35:15 +00:00
st . Status = finalStatus
st . PID = 0
st . Question = question
writeStatus ( wsDir , st )
}
refactor(agentic): adopt core.Env() + core.Path() across package
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 10:15:15 +00:00
emitCompletionEvent ( agent , core . PathBase ( wsDir ) , finalStatus )
2026-03-17 03:05:26 +00:00
2026-03-17 17:45:04 +00:00
if s . onComplete != nil {
s . onComplete . Poke ( )
}
if finalStatus == "completed" {
s . autoCreatePR ( wsDir )
s . autoVerifyAndMerge ( wsDir )
}
2026-03-17 04:19:48 +00:00
2026-03-16 17:52:55 +00:00
s . ingestFindings ( wsDir )
s . drainQueue ( )
} ( )
return pid , outputFile , nil
}
2026-03-16 11:10:33 +00:00
func ( s * PrepSubsystem ) dispatch ( ctx context . Context , req * mcp . CallToolRequest , input DispatchInput ) ( * mcp . CallToolResult , DispatchOutput , error ) {
if input . Repo == "" {
2026-03-22 03:41:07 +00:00
return nil , DispatchOutput { } , core . E ( "dispatch" , "repo is required" , nil )
2026-03-16 11:10:33 +00:00
}
if input . Task == "" {
2026-03-22 03:41:07 +00:00
return nil , DispatchOutput { } , core . E ( "dispatch" , "task is required" , nil )
2026-03-16 11:10:33 +00:00
}
if input . Org == "" {
input . Org = "core"
}
if input . Agent == "" {
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
input . Agent = "codex"
2026-03-16 11:10:33 +00:00
}
if input . Template == "" {
input . Template = "coding"
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// Step 1: Prep workspace — clone + build prompt
2026-03-16 11:10:33 +00:00
prepInput := PrepInput {
Repo : input . Repo ,
Org : input . Org ,
Issue : input . Issue ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
PR : input . PR ,
Branch : input . Branch ,
Tag : input . Tag ,
2026-03-16 11:10:33 +00:00
Task : input . Task ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
Agent : input . Agent ,
2026-03-16 11:10:33 +00:00
Template : input . Template ,
PlanTemplate : input . PlanTemplate ,
Variables : input . Variables ,
Persona : input . Persona ,
}
_ , prepOut , err := s . prepWorkspace ( ctx , req , prepInput )
if err != nil {
2026-03-22 03:41:07 +00:00
return nil , DispatchOutput { } , core . E ( "dispatch" , "prep workspace failed" , err )
2026-03-16 11:10:33 +00:00
}
wsDir := prepOut . WorkspaceDir
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
prompt := prepOut . Prompt
2026-03-16 11:10:33 +00:00
if input . DryRun {
return nil , DispatchOutput {
Success : true ,
Agent : input . Agent ,
Repo : input . Repo ,
WorkspaceDir : wsDir ,
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
Prompt : prompt ,
2026-03-16 11:10:33 +00:00
} , nil
}
// Step 2: Check per-agent concurrency limit
if ! s . canDispatchAgent ( input . Agent ) {
writeStatus ( wsDir , & WorkspaceStatus {
Status : "queued" ,
Agent : input . Agent ,
Repo : input . Repo ,
Org : input . Org ,
Task : input . Task ,
2026-03-17 04:19:48 +00:00
Branch : prepOut . Branch ,
2026-03-16 11:10:33 +00:00
StartedAt : time . Now ( ) ,
Runs : 0 ,
} )
return nil , DispatchOutput {
Success : true ,
Agent : input . Agent ,
Repo : input . Repo ,
WorkspaceDir : wsDir ,
OutputFile : "queued — waiting for a slot" ,
} , nil
}
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
// Step 3: Spawn agent in repo/ directory
pid , outputFile , err := s . spawnAgent ( input . Agent , prompt , wsDir )
2026-03-16 11:10:33 +00:00
if err != nil {
return nil , DispatchOutput { } , err
}
writeStatus ( wsDir , & WorkspaceStatus {
Status : "running" ,
Agent : input . Agent ,
Repo : input . Repo ,
Org : input . Org ,
Task : input . Task ,
2026-03-17 04:19:48 +00:00
Branch : prepOut . Branch ,
2026-03-16 11:10:33 +00:00
PID : pid ,
StartedAt : time . Now ( ) ,
Runs : 1 ,
} )
return nil , DispatchOutput {
Success : true ,
Agent : input . Agent ,
Repo : input . Repo ,
WorkspaceDir : wsDir ,
PID : pid ,
OutputFile : outputFile ,
} , nil
}