feat(agentic): add synchronous dispatch surface

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 20:34:04 +00:00
parent 2a7b8d2e25
commit a84a610ae2
5 changed files with 49 additions and 6 deletions

View file

@ -18,6 +18,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
s.startupContext = ctx
c := s.Core()
c.Command("run/task", core.Command{Description: "Run a single task end-to-end", Action: s.cmdRunTask})
c.Command("dispatch/sync", core.Command{Description: "Dispatch a single task synchronously and block until it completes", Action: s.cmdDispatchSync})
c.Command("run/orchestrator", core.Command{Description: "Run the queue orchestrator (standalone, no MCP)", Action: s.cmdOrchestrator})
c.Command("dispatch", core.Command{Description: "Dispatch queued agents", Action: s.cmdDispatch})
c.Command("agentic:dispatch", core.Command{Description: "Dispatch queued agents", Action: s.cmdDispatch})
@ -68,10 +69,14 @@ func (s *PrepSubsystem) commandContext() context.Context {
}
func (s *PrepSubsystem) cmdRunTask(options core.Options) core.Result {
return s.runTask(s.commandContext(), options)
return s.runDispatchSync(s.commandContext(), options, "run task", "agentic.runTask")
}
func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core.Result {
func (s *PrepSubsystem) cmdDispatchSync(options core.Options) core.Result {
return s.runDispatchSync(s.commandContext(), options, "dispatch sync", "agentic.runDispatchSync")
}
func (s *PrepSubsystem) runDispatchSync(ctx context.Context, options core.Options, commandLabel, errorName string) core.Result {
repo := options.String("repo")
agent := options.String("agent")
task := options.String("task")
@ -79,8 +84,8 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core.
org := options.String("org")
if repo == "" || task == "" {
core.Print(nil, "usage: core-agent run task --repo=<repo> --task=\"...\" --agent=codex [--issue=N] [--org=core]")
return core.Result{Value: core.E("agentic.runTask", "repo and task are required", nil), OK: false}
core.Print(nil, "usage: core-agent %s --repo=<repo> --task=\"...\" --agent=codex [--issue=N] [--org=core]", commandLabel)
return core.Result{Value: core.E(errorName, "repo and task are required", nil), OK: false}
}
if agent == "" {
agent = "codex"
@ -91,7 +96,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core.
issue := parseIntString(issueValue)
core.Print(nil, "core-agent run task")
core.Print(nil, "core-agent %s", commandLabel)
core.Print(nil, " repo: %s/%s", org, repo)
core.Print(nil, " agent: %s", agent)
if issue > 0 {
@ -107,7 +112,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core.
if !result.OK {
failureError := result.Error
if failureError == nil {
failureError = core.E("agentic.runTask", "dispatch failed", nil)
failureError = core.E(errorName, "dispatch failed", nil)
}
core.Print(nil, "FAILED: %v", failureError)
return core.Result{Value: failureError, OK: false}

View file

@ -1168,6 +1168,15 @@ func TestCommands_CmdRunTask_Bad_MissingArgs(t *testing.T) {
assert.False(t, r.OK)
}
func TestCommands_CmdDispatchSync_Bad_MissingArgs(t *testing.T) {
s, _ := testPrepWithCore(t, nil)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
s.startupContext = ctx
r := s.cmdDispatchSync(core.NewOptions())
assert.False(t, r.OK)
}
func TestCommands_CmdRunTask_Bad_MissingTask(t *testing.T) {
s, _ := testPrepWithCore(t, nil)
ctx, cancel := context.WithCancel(context.Background())
@ -1264,6 +1273,7 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
cmds := c.Commands()
assert.Contains(t, cmds, "run/task")
assert.Contains(t, cmds, "dispatch/sync")
assert.Contains(t, cmds, "run/orchestrator")
assert.Contains(t, cmds, "dispatch")
assert.Contains(t, cmds, "agentic:dispatch")

View file

@ -91,3 +91,28 @@ func (s *PrepSubsystem) DispatchSync(ctx context.Context, input DispatchSyncInpu
}
}
}
// result := c.Action("agentic.dispatch.sync").Run(ctx, core.NewOptions(
//
// core.Option{Key: "repo", Value: "go-io"},
// core.Option{Key: "task", Value: "Fix the failing tests"},
//
// ))
func (s *PrepSubsystem) handleDispatchSync(ctx context.Context, options core.Options) core.Result {
input := dispatchSyncInputFromOptions(options)
result := s.DispatchSync(ctx, input)
if result.Error != nil {
return core.Result{Value: result.Error, OK: false}
}
return core.Result{Value: result, OK: result.OK}
}
func dispatchSyncInputFromOptions(options core.Options) DispatchSyncInput {
return DispatchSyncInput{
Org: optionStringValue(options, "org"),
Repo: optionStringValue(options, "repo", "_arg"),
Agent: optionStringValue(options, "agent"),
Task: optionStringValue(options, "task"),
Issue: optionIntValue(options, "issue"),
}
}

View file

@ -159,6 +159,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
c.Action("agent.subscription.budget.update", s.handleSubscriptionBudgetUpdate).Description = "Update the compute budget for a fleet node"
c.Action("agentic.dispatch", s.handleDispatch).Description = "Prep workspace and spawn a subagent"
c.Action("agentic.dispatch.sync", s.handleDispatchSync).Description = "Dispatch a single task synchronously and block until it completes"
c.Action("agentic.prep", s.handlePrep).Description = "Clone repo and build agent prompt"
c.Action("agentic.status", s.handleStatus).Description = "List workspace states (running/completed/blocked)"
c.Action("agentic.resume", s.handleResume).Description = "Resume a blocked or completed workspace"

View file

@ -468,6 +468,7 @@ func TestPrep_OnStartup_Good_RegistersPlanActions(t *testing.T) {
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.dispatch.sync").Exists())
assert.True(t, c.Action("plan.create").Exists())
assert.True(t, c.Action("plan.get").Exists())
assert.True(t, c.Action("plan.read").Exists())
@ -654,6 +655,7 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
assert.Contains(t, c.Commands(), "generate")
assert.Contains(t, c.Commands(), "agentic:generate")
assert.Contains(t, c.Commands(), "complete")
assert.Contains(t, c.Commands(), "dispatch/sync")
assert.Contains(t, c.Commands(), "agentic:plan")
assert.Contains(t, c.Commands(), "prep-workspace")
assert.Contains(t, c.Commands(), "brain/ingest")