feat(agentic): add synchronous dispatch surface
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
2a7b8d2e25
commit
a84a610ae2
5 changed files with 49 additions and 6 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue