Organize Agentbox runtime code

Co-authored-by: codegirl007 <codegirl-007@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-08-18 06:03:59 +00:00
co-authored by codegirl007
parent f2fee5d26b
commit 4d2fb4a733
22 changed files with 1358 additions and 878 deletions
+64
View File
@@ -0,0 +1,64 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"agentbox/internal/trace"
)
func listRuns() error {
projectRoot, err := findProjectRoot()
if err != nil {
return err
}
runs, err := trace.List(projectRoot)
if err != nil {
return err
}
if len(runs) == 0 {
fmt.Println("No recorded runs.")
return nil
}
for _, run := range runs {
fmt.Printf("%s %-8s %-20s %s\n",
run.ID, run.Status, run.Agent, run.Task)
}
return nil
}
func inspect(args []string) error {
if len(args) != 1 {
return errors.New("inspect requires one run ID")
}
projectRoot, err := findProjectRoot()
if err != nil {
return err
}
runID := args[0]
run, err := trace.Read(projectRoot, runID)
if err != nil {
return err
}
steps, err := trace.ReadSteps(projectRoot, runID)
if err != nil {
return err
}
output := struct {
Run trace.Run `json:"run"`
Steps []trace.StepRecord `json:"steps"`
Directory string `json:"directory"`
}{
Run: run,
Steps: steps,
Directory: filepath.Join(projectRoot, ".agentbox", "runs", run.ID),
}
data, err := json.MarshalIndent(output, "", " ")
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}
-193
View File
@@ -1,24 +1,13 @@
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
"agentbox/internal/agent"
"agentbox/internal/appspec"
"agentbox/internal/environment/dockerx11"
"agentbox/internal/phase1"
agentRuntime "agentbox/internal/runtime"
"agentbox/internal/trace"
)
func main() {
@@ -50,188 +39,6 @@ func main() {
}
}
type runOptions struct {
path string
task string
agent string
model string
maxSteps int
}
func run(ctx context.Context, args []string) error {
options, err := parseRunOptions(args)
if err != nil {
return err
}
root, err := findRoot()
if err != nil {
return err
}
command, err := appspec.Resolve(options.path)
if err != nil {
return err
}
controller, err := selectAgent(options)
if err != nil {
return err
}
store, err := trace.New(root, options.task, options.path, controller.Name())
if err != nil {
return err
}
env := dockerx11.New(dockerx11.Config{
ProjectRoot: root,
RunID: store.ID(),
Output: os.Stdout,
})
err = agentRuntime.Run(ctx, agentRuntime.Config{
Task: options.task,
Command: command,
Agent: controller,
Env: env,
Trace: store,
MaxSteps: options.maxSteps,
Output: os.Stdout,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Run artifacts: %s\n", store.Directory())
return err
}
fmt.Printf("Replay written to:\n%s\n", store.Directory())
return nil
}
func parseRunOptions(args []string) (runOptions, error) {
options := runOptions{agent: "deterministic", model: "gpt-5", maxSteps: 20}
for index := 0; index < len(args); index++ {
arg := args[index]
var name, value string
if strings.HasPrefix(arg, "--") {
name, value, _ = strings.Cut(strings.TrimPrefix(arg, "--"), "=")
if value == "" {
index++
if index >= len(args) {
return options, fmt.Errorf("--%s requires a value", name)
}
value = args[index]
}
switch name {
case "task":
options.task = value
case "agent":
options.agent = value
case "model":
options.model = value
case "max-steps":
number, err := strconv.Atoi(value)
if err != nil || number < 1 {
return options, errors.New("--max-steps must be a positive integer")
}
options.maxSteps = number
default:
return options, fmt.Errorf("unknown flag --%s", name)
}
continue
}
if options.path != "" {
return options, errors.New("run accepts exactly one application path")
}
options.path = arg
}
if options.path == "" {
return options, errors.New("run requires an application path")
}
if options.task == "" {
return options, errors.New("run requires --task")
}
return options, nil
}
func selectAgent(options runOptions) (agent.Agent, error) {
switch options.agent {
case "deterministic":
return &agent.Deterministic{}, nil
case "openai":
return agent.NewOpenAI(agent.OpenAIConfig{
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: options.model,
})
default:
return nil, fmt.Errorf("unknown agent %q (want deterministic or openai)", options.agent)
}
}
func listRuns() error {
root, err := findRoot()
if err != nil {
return err
}
runs, err := trace.List(root)
if err != nil {
return err
}
if len(runs) == 0 {
fmt.Println("No recorded runs.")
return nil
}
for _, run := range runs {
fmt.Printf("%s %-8s %-20s %s\n",
run.ID, run.Status, run.Agent, run.Task)
}
return nil
}
func inspect(args []string) error {
if len(args) != 1 {
return errors.New("inspect requires one run ID")
}
root, err := findRoot()
if err != nil {
return err
}
run, err := trace.Read(root, args[0])
if err != nil {
return err
}
steps, err := trace.ReadSteps(root, args[0])
if err != nil {
return err
}
output := struct {
Run trace.Run `json:"run"`
Steps []trace.StepRecord `json:"steps"`
Directory string `json:"directory"`
}{
Run: run,
Steps: steps,
Directory: filepath.Join(root, ".agentbox", "runs", run.ID),
}
data, err := json.MarshalIndent(output, "", " ")
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}
func findRoot() (string, error) {
current, err := os.Getwd()
if err != nil {
return "", err
}
for {
data, readErr := os.ReadFile(filepath.Join(current, "go.mod"))
if readErr == nil && bytes.Contains(data, []byte("module agentbox")) {
return current, nil
}
parent := filepath.Dir(current)
if parent == current {
return "", errors.New("run agentbox from inside its module directory")
}
current = parent
}
}
func usage() {
fmt.Fprintln(os.Stderr, `usage:
agentbox run <path> --task "<task>" [--agent deterministic|openai] [--model <model>]
+3 -1
View File
@@ -9,7 +9,9 @@ func TestParseRunOptionsAllowsFlagsAfterPath(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if options.path != "./game" || options.task != "move right" || options.maxSteps != 7 {
if options.applicationPath != "./game" ||
options.task != "move right" ||
options.maxSteps != 7 {
t.Fatalf("options = %#v", options)
}
}
+28
View File
@@ -0,0 +1,28 @@
package main
import (
"bytes"
"errors"
"os"
"path/filepath"
)
// findProjectRoot lets commands work from any directory inside the Agentbox
// module. The module declaration is a stronger marker than a directory name.
func findProjectRoot() (string, error) {
currentDirectory, err := os.Getwd()
if err != nil {
return "", err
}
for {
goModule, readErr := os.ReadFile(filepath.Join(currentDirectory, "go.mod"))
if readErr == nil && bytes.Contains(goModule, []byte("module agentbox")) {
return currentDirectory, nil
}
parentDirectory := filepath.Dir(currentDirectory)
if parentDirectory == currentDirectory {
return "", errors.New("run agentbox from inside its module directory")
}
currentDirectory = parentDirectory
}
}
+141
View File
@@ -0,0 +1,141 @@
package main
import (
"context"
"errors"
"fmt"
"os"
"strconv"
"strings"
"agentbox/internal/agent"
"agentbox/internal/appspec"
"agentbox/internal/environment/dockerx11"
agentRuntime "agentbox/internal/runtime"
"agentbox/internal/trace"
)
type runOptions struct {
applicationPath string
task string
agentName string
modelName string
maxSteps int
}
func run(ctx context.Context, args []string) error {
options, err := parseRunOptions(args)
if err != nil {
return err
}
projectRoot, err := findProjectRoot()
if err != nil {
return err
}
applicationCommand, err := appspec.Resolve(options.applicationPath)
if err != nil {
return err
}
controller, err := newAgent(options)
if err != nil {
return err
}
traceStore, err := trace.New(
projectRoot,
options.task,
options.applicationPath,
controller.Name(),
)
if err != nil {
return err
}
desktopEnvironment := dockerx11.New(dockerx11.Config{
ProjectRoot: projectRoot,
RunID: traceStore.ID(),
Output: os.Stdout,
})
err = agentRuntime.Run(ctx, agentRuntime.Config{
Task: options.task,
Command: applicationCommand,
Controller: controller,
Environment: desktopEnvironment,
TraceStore: traceStore,
MaxSteps: options.maxSteps,
Output: os.Stdout,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Run artifacts: %s\n", traceStore.Directory())
return err
}
fmt.Printf("Replay written to:\n%s\n", traceStore.Directory())
return nil
}
// parseRunOptions accepts flags before or after the application path so the CLI
// matches the natural "agentbox run ./app --task ..." form shown in the docs.
func parseRunOptions(args []string) (runOptions, error) {
options := runOptions{
agentName: "deterministic",
modelName: "gpt-5",
maxSteps: 20,
}
for index := 0; index < len(args); index++ {
argument := args[index]
if !strings.HasPrefix(argument, "--") {
if options.applicationPath != "" {
return options, errors.New("run accepts exactly one application path")
}
options.applicationPath = argument
continue
}
flagName, flagValue, _ := strings.Cut(strings.TrimPrefix(argument, "--"), "=")
if flagValue == "" {
index++
if index >= len(args) {
return options, fmt.Errorf("--%s requires a value", flagName)
}
flagValue = args[index]
}
switch flagName {
case "task":
options.task = flagValue
case "agent":
options.agentName = flagValue
case "model":
options.modelName = flagValue
case "max-steps":
maxSteps, err := strconv.Atoi(flagValue)
if err != nil || maxSteps < 1 {
return options, errors.New("--max-steps must be a positive integer")
}
options.maxSteps = maxSteps
default:
return options, fmt.Errorf("unknown flag --%s", flagName)
}
}
if options.applicationPath == "" {
return options, errors.New("run requires an application path")
}
if options.task == "" {
return options, errors.New("run requires --task")
}
return options, nil
}
func newAgent(options runOptions) (agent.Agent, error) {
switch options.agentName {
case "deterministic":
return &agent.Deterministic{}, nil
case "openai":
return agent.NewOpenAI(agent.OpenAIConfig{
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: options.modelName,
})
default:
return nil, fmt.Errorf(
"unknown agent %q (want deterministic or openai)",
options.agentName,
)
}
}