Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions cmd/ax/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,11 @@ func runAntigravityInteractionsHarness(ctx context.Context) error {
if agent == "" {
agent = antigravityinteractions.DefaultAgent
}
stateDir := hc.StateDir
if stateDir == "" {
var err error
stateDir, err = antigravityinteractions.DefaultStateDir()
if err != nil {
return err
}
// AX owns the resume-cursor path (a harness implementation detail), so it's
// derived internally rather than read from ax.yaml.
stateDir, err := antigravityinteractions.DefaultStateDir()
if err != nil {
return err
}

cfg := antigravityinteractions.AntigravityInteractionsConfig{
Expand Down
22 changes: 13 additions & 9 deletions cmd/ax/internal/cliutil/cliutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,14 @@ func NewControllerFromConfig(ctx context.Context, cfg *Config) (*controller.Cont
if address == "" {
address = "127.0.0.1:50053"
}
// Local mode: the harness owns the Python sidecar. Empty StateDir
// means the sidecar applies its own default
antigravityHarness, err = antigravity.New(ctx, address, cfg.Harnesses.Antigravity.StateDir, true)
// Local mode: the harness owns the Python sidecar. AX owns the
// trajectory storage path (a harness implementation detail), so it's
// derived internally rather than exposed in ax.yaml.
stateDir, sErr := antigravity.DefaultStateDir()
if sErr != nil {
return nil, fmt.Errorf("antigravity harness: %w", sErr)
}
antigravityHarness, err = antigravity.New(ctx, address, stateDir, true)
if err != nil {
return nil, fmt.Errorf("antigravity harness: %w", err)
}
Expand All @@ -97,12 +102,11 @@ func NewControllerFromConfig(ctx context.Context, cfg *Config) (*controller.Cont
if agent == "" {
agent = antigravityinteractions.DefaultAgent
}
stateDir := cfg.Harnesses.AntigravityInteractions.StateDir
if stateDir == "" {
stateDir, err = antigravityinteractions.DefaultStateDir()
if err != nil {
return nil, fmt.Errorf("antigravity-interactions harness: %w", err)
}
// AX owns the resume-cursor path (a harness implementation detail), so
// it's derived internally rather than exposed in ax.yaml.
stateDir, sErr := antigravityinteractions.DefaultStateDir()
if sErr != nil {
return nil, fmt.Errorf("antigravity-interactions harness: %w", sErr)
}
antigravityInteractionsHarness, err = antigravityinteractions.New(antigravityinteractions.AntigravityInteractionsConfig{
Agent: agent,
Expand Down
8 changes: 3 additions & 5 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,14 @@ type HarnessesConfig struct {
// AntigravityHarnessConfig registers the built-in Antigravity harness.
type AntigravityHarnessConfig struct {
Default bool `yaml:"default,omitempty"`
Endpoint string `yaml:"endpoint,omitempty"` // HarnessService address
StateDir string `yaml:"state_dir,omitempty"` // Trajectory storage dir (optional; sidecar defaults to ~/.ax/antigravity/conversations)
Endpoint string `yaml:"endpoint,omitempty"` // HarnessService address
}

// AntigravityInteractionsHarnessConfig registers the built-in Antigravity
// Interactions harness (over the Vertex GenAI Interactions API).
type AntigravityInteractionsHarnessConfig struct {
Default bool `yaml:"default,omitempty"` // Default harness or not
Agent string `yaml:"agent,omitempty"` // Interactions API agent (default: antigravityinteractions.DefaultAgent)
StateDir string `yaml:"state_dir,omitempty"` // Resume-cursor directory (optional; defaults to ~/.ax/antigravityinteractions/cursors)
Default bool `yaml:"default,omitempty"` // Default harness or not
Agent string `yaml:"agent,omitempty"` // Interactions API agent (default: antigravityinteractions.DefaultAgent)
}

// SubstrateHarnessConfig registers a custom harness deployed on substrate
Expand Down
19 changes: 1 addition & 18 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ func TestValidate_InteractionsIDReserved(t *testing.T) {
func TestValidate_InteractionsValid(t *testing.T) {
c := validConfig()
c.Harnesses.AntigravityInteractions = AntigravityInteractionsHarnessConfig{
Agent: "projects/p/locations/global/agents/a",
StateDir: "interactions-state",
Agent: "projects/p/locations/global/agents/a",
}
if err := c.Validate(); err != nil {
t.Fatalf("Validate() = %v, want nil", err)
Expand All @@ -141,22 +140,6 @@ eventlog:
}
}

// TestLoadFromFile_AntigravityStateDir: yaml -> AntigravityHarnessConfig.StateDir.
func TestLoadFromFile_AntigravityStateDir(t *testing.T) {
data := `
harnesses:
antigravity:
state_dir: /custom/path
`
var cfg Config
if err := yaml.Unmarshal([]byte(data), &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if got, want := cfg.Harnesses.Antigravity.StateDir, "/custom/path"; got != want {
t.Errorf("StateDir = %q, want %q", got, want)
}
}

func TestLoadFromBytes(t *testing.T) {
cfg, err := LoadFromBytes([]byte(`
version: v1alpha
Expand Down
16 changes: 16 additions & 0 deletions internal/harness/antigravity/antigravity.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"net"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"

Expand All @@ -29,6 +30,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/google/ax/internal/config"
"github.com/google/ax/internal/harness"
"github.com/google/ax/internal/pythonsidecar"
"github.com/google/ax/proto"
Expand All @@ -40,6 +42,20 @@ import (
var _ harness.Harness = (*AntigravityHarness)(nil)
var _ harness.Execution = (*antigravityExecution)(nil)

// DefaultStateDir returns the default trajectory storage directory,
// ~/.ax/antigravity/conversations. AX owns this path so it stays a harness
// implementation detail rather than a user-facing ax.yaml knob. It lives
// outside the agent's working directory on purpose: the working directory is
// the agent's operating surface (it reads and edits files there), so AX's
// internal state is kept separate to avoid the agent seeing or clobbering it.
func DefaultStateDir() (string, error) {
axDir, err := config.AXAssetsDir()
if err != nil {
return "", err
}
return filepath.Join(axDir, "antigravity", "conversations"), nil
}

// AntigravityHarness implements the Harness interface by connecting to the
// Antigravity Python agent server over gRPC.
type AntigravityHarness struct {
Expand Down
16 changes: 16 additions & 0 deletions internal/harness/antigravity/antigravity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package antigravity
import (
"bytes"
"context"
"path/filepath"
"strings"
"testing"

Expand Down Expand Up @@ -106,3 +107,18 @@ func TestNew_AutoStartFalse_NilSidecar(t *testing.T) {
t.Errorf("expected sidecar to be nil, got %v", h.sidecar)
}
}

// TestDefaultStateDir returns ~/.ax/antigravity/conversations under the user's
// home directory.
func TestDefaultStateDir(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)

got, err := DefaultStateDir()
if err != nil {
t.Fatalf("DefaultStateDir: %v", err)
}
if want := filepath.Join(home, ".ax", "antigravity", "conversations"); got != want {
t.Errorf("DefaultStateDir() = %q, want %q", got, want)
}
}
Loading