Commit Graph
200 Commits
Author SHA1 Message Date
Jaana Dogan 233820befc make tasks immutable and revisit lifecycle management
Make Task manifests fully immutable once created, removing `UpdateTask`
in favor of `CreateTask` and driving lifecycle transitions (suspend,
resume, delete) through dedicated RPCs and event stream processing.

- Proto: Replace `UpdateTask` with `CreateTask` in AX service.
- Proto: Remove mutable `suspend` field from `TaskSpec` and reserve field 2.
- API / Server: Implement `CreateTask` with duplicate name rejection
  (`codes.AlreadyExists`). Update `SuspendTask`, `ResumeTask`, and `DeleteTask`
  to transition status phase and publish lifecycle events.
- Store: Remove redundant `SetTaskSuspended` and `MarkTaskDeleting` methods;
  standardize on `CreateTask`, `UpdateTaskStatus`, `DeleteTask`, and
  `PublishEvent`.
- Controller: Decouple spec reconciliation from task suspension; introduce
  `ReconcileSuspend` to handle checkpointing and pausing actors on Substrate.
- CLI: Update `ax apply` to submit tasks via `CreateTask`.
- Docs: Update design docs and specifications to reflect task immutability.
2026-09-24 04:03:32 -07:00
Alex Jones e6211f84a9 fix(controller): keep task templates stable across lifecycle changes (#390)
Exclude status and the suspend flag from embedded launch configuration so reconciliation reuses the existing template and golden snapshot.
2026-09-24 02:01:21 -07:00
JBD dc4f36cdba Restructure AX into a general-purpose orchestration layer for agentic tasks
AX is undergoing a significant redesign, moving away from a single CLI with
an embedded Python harness toward a general-purpose orchestration layer for
agentic tasks. The repository is reorganized around three binaries: ax-server
exposes a gRPC API for submitting Task manifests, ax-controller runs as a
horizontally scaled reconciler that consumes work from Redis Streams, and
ax-task-runner executes tasks inside sandboxed workers. Task state now lives
in Redis rather than Kubernetes CRDs so the system can handle millions of
short-lived tasks without straining etcd. The legacy Python harness, ATE
client, SQL event log, and skill examples are removed, and new design,
concepts, manifest, networking, runner, and sandbox docs plus Kubernetes
deploy manifests describe the new architecture.
2026-09-19 20:14:43 -07:00
Jaana Dogan 9faeb23bed Rename harnessConfig to agentConfig across the codebase for consistency 2026-08-11 23:00:48 -07:00
Jaana Dogan 2282424ebc Make the public facing ExecutionService more InteractionsService-like (#339)
We want AX to be compatible with the Interactions API protocol as much as possible. This PR aligns the proto with Steps and with the Interactions semantics. We will follow up with more changes to make it more compatible with Interactions while preserving a `conversation_id` based history model.

`ConversationService` is removed for now until we know what kind of data operations we want to provide for event log management.
2026-08-11 22:52:05 -07:00
Jaana Dogan bab973e877 refactor: rename seq to step across protocol and internal controller logic 2026-07-22 12:13:19 -07:00
Junjie Wang 2db1833113 Handle default harness correctly. (#308) 2026-07-15 20:24:21 -07:00
Jaana Dogan b8c87febf0 Add traces to the SQL event log implementation (#234) 2026-07-01 10:26:14 -07:00
Junjie Wang fa2a34588e Pass harness config through the incoming request (#193)
* Pass harness config from exec input.

* Update generated proto.

* Add a flag for inline config json.
2026-06-26 12:41:38 -07:00
Jaana Dogan 4aad1a6605 Consolidate controller and config packages by removing v2 suffixes (#179)
Also remove ununsed ExecutionEvent from proto.
2026-06-25 15:32:21 -07:00
Jaana Dogan 9737fd7033 Migrate controller and registry to internal/controller2 (#176)
* Cleanup the repo and move to controller2

* Migrate controller and registry to internal/controller2

- Remove the legacy harness build tag

* refactor: move GeminiConfig definition from internal/config to internal/gemini package

* test: replace controller registry with dummy implementation and update dependency constraints in go.mod

* chore: remove harness build and test steps from GitHub Actions workflow
2026-06-25 15:21:28 -07:00
JoyceLiu 3438251c04 Remove A2A agent and bridge (#160)
Removes the A2A protocol integration end-to-end:

- examples/a2a_agent/ (Python coding agent example)
- internal/experimental/a2abridge/ (A2A <-> AX bridge)
- internal/experimental/agent/a2a.go (A2A agent client)
- internal/auth/ (only used by A2A)
- RemoteAgentConfig.Protocol/Auth/Headers/A2A fields and
  Registry.registerA2A (the protocol switch collapses to AXP)
- github.com/a2aproject/a2a-go/v2 dep (via go mod tidy)
- A2A mentions in ax.yaml, README.md, .gitignore

Fixes #128
2026-06-22 22:10:20 -07:00
Jaana Dogan da9d20eccc Remove Colab agent support (#151) 2026-06-18 12:14:06 -07:00
Jaana Dogan 544db4c560 Remove ATE agents (#148)
SubstrateHarness will replace this. SubstrateAgent is deadcode.
2026-06-18 08:08:44 -07:00
Jaana Dogan 5425c993f4 Remove forking capabilities until controller2 is stable (#130)
* Remove forking capabilities until controller2 is stable

* chore: add Apache 2.0 license headers to generated python protobuf files
2026-06-17 14:16:21 -07:00
anj-s 018e6791b2 impl: update event log mechanism in controller v2 (#58) 2026-06-08 11:46:56 -07:00
Jaana Dogan 9b336989b6 Rename registry methods GetInfo and GetHarness to AgentInfo and Harness to follow Go style (#40)
Go getters are not prefixed with Get*.
2026-05-31 19:35:13 -07:00
Jaana Dogan 4d04e36470 Initial commit of the Substrate refactor (#17)
* Added internal/controller2/: as a copy of the existing controller.
* Added a new deployment manifest which will replace the previous one, internal/manifests/ax-deployment2.yaml.
* Added HarnessService to proto as a placeholder.
* Added internal/SUBSTRATE_REFACTOR_CLEANUPS.md for some cleanups after this refactor is done.
* Added SubstrateHarness that can create/resume axharness as an actor with conversation ID.
* Remove unnecessary ate build tags because Substrate is now public.

Updates #3.
2026-05-22 09:30:44 -07:00
Jaana Dogan cdb0b348ea go fmt ./... (#11) 2026-05-20 22:35:36 -07:00
Jaana Dogan 4f332b6387 Rename ATEAgent to SubstrateAgent (#362)
Naming cleanup for ax.yaml and the experimental example SubstrateAgent.
2026-05-16 19:12:44 -07:00
Jaana Dogan c1a6d14840 Remove experimental testagent and clean up controller (#358) 2026-05-16 19:05:25 -07:00
Daqing YiandDaqing Yi 73d7ee43ab Reject Reserved agent IDs during registration (#334)
Avoid reserved agent ids defined in the registration so that they would
be overwritten in controller when instantiating planner and gemini
agent.

Co-authored-by: Daqing Yi <dqyi@google.com>
2026-05-11 22:14:36 -07:00
Jaana Dogan af899c8036 Demo and many other fixes (#331)
- Make changes to the testagents for a realistic sub agent demo
- Sanitize agent names, replace - with _ to avoid malformation errors
- Increase the idle networking timeout across different configs, set a
high GKE ingress timeout to enable long running operations
- Register AX_TEST_AGENTS with description and name
- Make changes to the symptom prompt and disable bash tool improve the
selection quality for the demo
- Move inflight tracking to server to avoid double suspension 
- Make the resumption command to honor --server if set

Follow up:
- Remove AX_TEST_AGENTS once we don't need them.
- Work on Gemini planner to support better selection among the agents
and the bash tool.
2026-05-11 14:28:36 -07:00
JoyceLiu cc08d9b3ef todo: add TODO to consider removing hardcoded default gemini agent (#327) 2026-05-10 17:02:43 -07:00
JoyceLiu 9821d06efd fix: agentExecutor for sub-agent invocation (#329)
## Summary

Introduces `agentExecutor`, a lightweight executor for nested sub-agent
calls. Fixes the infinite loop when a sub-agent emits a confirmation,
and eliminates a wasted Gemini API call on every sub-agent delegation.

## Changes

### `internal/controller/executor/agent_executor.go` (new)

`agentExecutor` implements `agent.Executor` for sub-agent invocations:
- Does NOT load history from event log (caller passes messages)
- Does NOT short-circuit on `WaitsForConfirmation` or `COMPLETED` cache
- Writes execution-level logs for observability
- Returns `STATE_PENDING` when sub-agent emits a confirmation

### `internal/controller/executor/executor.go`

Child executor in `defaultExecutor.exec` changed from `defaultExecutor`
to `agentExecutor`. Also renames receiver `tm` → `de`/`ae` for clarity.

### `internal/gemini/gemini_planner.go`

After `e.Exec` returns in `loop`:
- Appends sub-agent outputs to `start.Messages` immediately (eliminates
  wasted Gemini call — was 3, now 2)
- Respects `STATE_PENDING` (stops the infinite loop, surfaces
  confirmation to user)
- Handles nil Gemini content gracefully (preserves `FinishReasonStop`
  check, reports other reasons as errors)

## Confirmation resume routing

On Turn 2 (user sends approval), `tryResuming` finds the planner
PENDING and resumes it normally. The planner calls Gemini, which
re-delegates to the sub-agent. The sub-agent sees the approval in
`start.Messages` and completes. **This costs 1 extra Gemini call on
the resume turn.**

Direct routing (skipping Gemini on resume) is deferred to a follow-up.
Options explored include controller-side routing, ToolCallContent
pairing, and `pending_agent_id` on execution events — see branches
`agent-executor-confirmation`, `agent-executor-toolcall`, and
`agent-executor-two-layer` for prototypes.

## Before/After

| Scenario | Before | After |
|---|---|---|
| Plain delegation | 3 Gemini calls | 2 |
| Delegation + confirmation | ∞ (hangs) | Works (1 extra Gemini call on
resume) |
| Bash tool | No change | No change |

## Testing

6 new tests in `agent_executor_test.go`. E2e tested with `ax serve` +
`ax exec` across no-confirmation, confirmation (3x stress), and bash
tool scenarios.

## Sample logs (real `ax serve` + `ax exec`, gemini-3-flash-preview)

### Scenario 1: No confirmation

Commands:
```bash
go run ./examples/remote_agent                     # Terminal 1: lowercase agent on :50051
ax serve --config ax.yaml                          # Terminal 2
ax exec --server localhost:8494 --input "Please lowercase HELLO WORLD"  # Terminal 3
```

Output: `please lowercase hello world`

<details>
<summary>Raw conversation log (2 events)</summary>

```json
{
  "conversationId": "raw-no-confirm",
  "seq": 1,
  "execId": "6abb8d1c-2d0e-4aa2-9550-6c18c51b91a4",
  "messages": [
    {
      "role": "user",
      "content": {
        "text": {
          "text": "Please lowercase HELLO WORLD"
        }
      }
    }
  ],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-no-confirm",
  "seq": 2,
  "execId": "6abb8d1c-2d0e-4aa2-9550-6c18c51b91a4",
  "messages": [
    {
      "role": "assistant",
      "content": {
        "text": {
          "text": "please lowercase hello world"
        }
      }
    }
  ],
  "state": "STATE_PENDING"
}
```
</details>

<details>
<summary>Raw execution log — lowercase (3 events)</summary>

```json
{
  "execId": "6abb8d1c-2d0e-4aa2-9550-6c18c51b91a4-lowercase",
  "agentId": "lowercase",
  "inputs": [
    {
      "role": "user",
      "content": {
        "text": {
          "text": "Please lowercase HELLO WORLD"
        }
      }
    }
  ],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:47.532433859Z"
}
{
  "execId": "6abb8d1c-2d0e-4aa2-9550-6c18c51b91a4-lowercase",
  "agentId": "lowercase",
  "outputs": [
    {
      "role": "assistant",
      "content": {
        "text": {
          "text": "please lowercase hello world"
        }
      }
    }
  ],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:47.540714804Z"
}
{
  "execId": "6abb8d1c-2d0e-4aa2-9550-6c18c51b91a4-lowercase",
  "agentId": "lowercase",
  "state": "STATE_COMPLETED",
  "timestamp": "2026-05-10T23:20:47.541394315Z"
}
```
</details>

### Scenario 2: With confirmation

Commands (uses a confirm-enabled remote agent variant):
```bash
go run ./examples/remote_agent                     # modified to ask for confirmation
ax serve --config ax.yaml
ax exec --server localhost:8494 --input "Please lowercase HELLO WORLD"  # Turn 1
# User approves the confirmation prompt                                  # Turn 2
```

Turn 1: agent asks confirmation, planner stops with `STATE_PENDING`.
Turn 2: user approves, planner re-delegates to sub-agent (1 extra Gemini
call), sub-agent completes.

Agent log:
```
[agent] asking confirmation for: "Please lowercase HELLO WORLD"
[agent] user approved, lowercasing: "Please lowercase HELLO WORLD"
```

<details>
<summary>Raw conversation log (7 events)</summary>

```json
{
  "conversationId": "raw-with-confirm",
  "seq": 1,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "messages": [{"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}}],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 2,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "messages": [{"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}}],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 3,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 4,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "messages": [{"role": "user", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "approval": {"approved": true}}}}],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 5,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "messages": [{"role": "assistant", "content": {"text": {"text": "please lowercase hello world"}}}],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 6,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "messages": [{"role": "model", "content": {"text": {"text": "please lowercase hello world"}}}],
  "state": "STATE_PENDING"
}
{
  "conversationId": "raw-with-confirm",
  "seq": 7,
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "state": "STATE_COMPLETED"
}
```
</details>

<details>
<summary>Raw execution log — __planner (5 events)</summary>

```json
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "agentId": "__planner",
  "inputs": [{"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}}],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:54.629227521Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "agentId": "__planner",
  "outputs": [{"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}}],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:55.966125441Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "agentId": "__planner",
  "inputs": [
    {"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}},
    {"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}},
    {"role": "user", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "approval": {"approved": true}}}}
  ],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:55.994507633Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "agentId": "__planner",
  "outputs": [
    {"role": "assistant", "content": {"text": {"text": "please lowercase hello world"}}},
    {"role": "model", "content": {"text": {"text": "please lowercase hello world"}}}
  ],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:57.672836311Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d",
  "agentId": "__planner",
  "state": "STATE_COMPLETED",
  "timestamp": "2026-05-10T23:20:57.673539482Z"
}
```
</details>

<details>
<summary>Raw execution log — lowercase (5 events)</summary>

```json
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d-lowercase",
  "agentId": "lowercase",
  "inputs": [{"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}}],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:55.956365514Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d-lowercase",
  "agentId": "lowercase",
  "outputs": [{"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}}],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:55.965184294Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d-lowercase",
  "agentId": "lowercase",
  "inputs": [
    {"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}},
    {"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}},
    {"role": "user", "content": {"text": {"text": "Please lowercase HELLO WORLD"}}},
    {"role": "assistant", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "question": "Can I lowercase \"Please lowercase HELLO WORLD\" for you?"}}},
    {"role": "user", "content": {"confirmation": {"id": "dc65756b-d450-49e7-bf08-93b4b492363e", "approval": {"approved": true}}}}
  ],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:56.876585877Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d-lowercase",
  "agentId": "lowercase",
  "outputs": [{"role": "assistant", "content": {"text": {"text": "please lowercase hello world"}}}],
  "state": "STATE_PENDING",
  "timestamp": "2026-05-10T23:20:56.886341400Z"
}
{
  "execId": "d2271679-8812-4d13-967c-2401b94a761d-lowercase",
  "agentId": "lowercase",
  "state": "STATE_COMPLETED",
  "timestamp": "2026-05-10T23:20:56.887095309Z"
}
```
</details>
2026-05-10 16:53:45 -07:00
Jaana Dogan a93dd3a9e0 Use markInFlight in Delete (#309)
The mutex should be hold during Delete.
2026-05-06 22:53:11 -07:00
JoyceLiu 6d60b40ac5 fork: validate src_seq exists, and update README for fork requirement on dest_conversation_id (#292)
Two related fork fixes uncovered while testing `ax fork --src-seq`:

1. controller.Fork: validate src_seq exists in source. Previously,
passing a non-existent src_seq (e.g. a typo or stale checkpoint)
silently degraded to "fork all events", which is misleading. Mirror the
validation tryResuming already does for --last-seq and return "src_seq N
not found in conversation X".

2. server.ForkConversation: update the README to require destiation
conversation id. this is required for Substrate integration
 
 
Tests:
- controller: TestController_Fork_SrcSeqNotFound covers the new
validation and asserts no events are appended on failure.
2026-05-06 09:03:04 -07:00
Junjie Wang 80c38f2dcb Support a2a in substrate agent. (#286)
Add A2A protocol support to the ATE (Substrate) agent path, so an A2A
agent packaged as a Substrate ActorTemplate can be registered under
`ate_agents` with `protocol: a2a`. The actor lifecycle (CreateActor /
SuspendActor) is shared with the existing remote agent path; only the
protocol-specific transport differs.

- The A2A agent will use the substrate worker address and configured
port.
- The A2A agent card will be updated with the URL from the worker
address.
- Using default stateless mode when A2A is deployed on substrate.
- TODO: Will add example substrate artifacts in a follow up.
2026-05-05 20:43:15 -07:00
Jaana Dogan 9da8fad0ba Delete Kubernetes sandbox agents (#281)
So we don't accidentally promote this option. There are also rough edges
in this implementation that we don't have time to fix, e.g. the use of
kubectl.
2026-05-05 11:55:39 -07:00
Junjie Wang 4b9de6aa68 Feat: support A2A agent in AX. (#274)
Adds an A2A (Agent-to-Agent) protocol bridge so AX can communicate with
A2A-compliant external agents.

## Changes
```text
internal/auth/                             # NEW shared auth package
├── auth.go
└── auth_test.go
internal/experimental/a2abridge/           # A2A bridge (renamed from a2autil)
├── auth.go
├── auth_test.go
├── mappings.go
├── mappings_test.go
├── a2a_internal_state.go
├── a2a_internal_state_test.go
├── a2a_converter.go
├── a2a_converter_test.go
├── a2a_event_handler.go
└── a2a_event_handler_test.go
internal/experimental/agent/a2a.go         bridge orchestrator (A2AAgent type)
internal/config/config.go                  unified RemoteAgentConfig + A2AConfig
internal/controller/registry.go            RegisterRemote dispatcher (axp | a2a)
internal/cliutil/cliutil.go                registration loop
cmd/ax/exec.go                             binary-content no-op
cmd/ax/trace.go                            new role CSS
ax.yaml                                    a2a coding-agent example
go.mod, go.sum                             +github.com/a2aproject/a2a-go/v2 v2.2.0
proto/ax.proto + generated .pb.go          new Content types
```

(TODO) Implement credential refresh on `TaskStateAuthRequired`
(currently only static `api_key`/`bearer` is implemented).
2026-05-04 14:49:34 -07:00
Jaana Dogan 55d5b1b1ea Deploy AX to Substrate (#228)
This change introduces an EPP to create/resume actor the incoming
conversation_id. We also introduce an ax-shell image and a demo pod for
testing purposes.

Follow up changes:
- Suspend the AX server automatically after the request is sent.
- Explore failure cases and how we can ensure suspension safely even if
AX server crashes.
- EventLogService.Fork shouldn't autogenerate a dest_conversation_id.
dest_conversation_id should always be supplied from the client for
routing.

This change also removes the RegisterAgent RPC. Actor
resumption/suspension requires a valid conversation_id for all RPCs and
RegisterAgent no longer fits into this new constraint.
2026-05-04 08:23:48 -07:00
anj-s b91caa44f3 Move Gemini related files into their own package (#273)
## Summary
This branch reorganizes the codebase by moving Gemini-related test files
to their appropriate package and resolves several circular dependencies
that arose from the file relocation. It also ensures that the project
builds, lints, and passes all tests (including integration tests).

## Motivation
* **Cohesion**: Test files were located in `internal/controller` but
were testing functionality belonging to `internal/gemini`. Moving them
improves project structure.
* **Decoupling**: The `gemini` package was tightly coupled to
`controller` via concrete types, causing import cycles when files were
moved.

## Changes

### File Relocations
* Moved the following files from `internal/controller` to
`internal/gemini`:
    *   `gemini_planner_integration_test.go`
    *   `gemini_planner_test.go`
    *   `gemini_test.go`

### Refactoring & Cycle Breaking
*   **`internal/agent`**:
* Moved `AgentInfo` struct from `internal/controller` to
`internal/agent/agent.go` to allow sharing without circular
dependencies.
*   **`internal/gemini`**:
* Defined `AgentRegistry` interface in `gemini_planner.go` to decouple
the planner from `controller.Registry`.
    *   Updated package names in moved test files to `gemini`.
* Converted `gemini_planner_integration_test.go` to an external test
(`package gemini_test`) to resolve a test-time import cycle.
*   **`internal/controller`**:
* Updated `controller.go` and `registry.go` to use `agent.AgentInfo` and
import `gemini` symbols correctly.
*   **`internal/cliutil`**:
* Updated `cliutil.go` to reference `gemini.NewGeminiPlannerAgent`
instead of the old `controller` path.

## Verification Results
*   **Build**: `go build ./...` completed successfully.
*   **Lint**: `go vet ./...` reported no issues.
*   **Tests**: All unit tests passed.
* **Integration Tests**: `TestIntegrationGeminiPlanner` passed
successfully using the provided `GEMINI_API_KEY` and the `integration`
build tag.
2026-04-30 14:32:49 -07:00
JoyceLiu 94cc5a8726 Move antigravity_planner.go to internal/experimental/planner and update caller (#261)
Moved antigravity_planner.go to internal/experimental/planner and
updated the caller in cliutil.go to use the new location. Also broke an
import cycle by removing unused Registry reference.
2026-04-30 12:13:40 -07:00
anj-s e35da2c1cc pr1 tests (#272)
- **Add unit and integration tests for gemini_planner**
- **Handle missing skills directory gracefully in NewSkillsTool**
2026-04-30 11:54:08 -07:00
Jaana Dogan b2cca64368 feat: return error when requested last_seq is missing from event log (#262) 2026-04-30 08:47:45 -07:00
JoyceLiu 59dfcf1f49 Update Antigravity planner to use WebSocket protocol (#245)
# Refactor Antigravity Planner to use WebSocket and AgentMessage  

This PR refactors the Antigravity Planner to use the standard
`AgentMessage` protocol for communication between the Go controller and
the Python planner server. It also changes the protocol from HTTP/JSON
to Websocket to support tool call. AX agents are invoked as Tools.
Python side CL cl/904297174 (need to improve the code quality)

Next up: 
* clean up the logic for HITL. 
* get cl/906138865 into google3 and further github once antigravity SDK
is released

## E2E Test Result: Antigravity Planner with Tool Call

### Commands Run
*   Start remote agent: `go run ./examples/remote_agent`
* Start Python planner server:
`/google/src/cloud/lhuan/jetski-ax/google3/blaze-bin/experimental/users/lhuan/jetski_planner_2`
*   Start AX server: `./bin/ax serve --config ax.yaml`
* Run client: `./bin/ax exec --server localhost:8496 --input "Use the
'secret_code' agent to get the secret code

### Logs and Outputs

#### Python Server Log
```
[AX Server] Loaded subagents: [{'id': 'secret_code', 'description': 'Retrieves secret codes and performs sensitive text transformations that only this agent can do.'}]
[AX Server] Generated tool for subagent: secret_code
Starting connection to harness and creating conversation...
Sending prompt to harness: Use the 'secret_code' agent to get the secret code
Waiting for steps...
[AXToolRunner] Intercepted tool call: secret_code
[AXToolRunner] Raw response from Go: {"outputs":{"messages":[{"role":"assistant", "content":{"text":{"text":"secret code 111 from ax"}}}]}}
[AXToolRunner] Received result from AX: secret code 111 from ax
Final Response: The secret code for user 'sundar' is `secret code 111 from ax`.
```

#### AX Server Log
```
2026/04/28 07:27:15 [AX] Connecting to Antigravity WebSocket for execution b8549e17-483a-4016-a06b-f81cd1a579dc
2026/04/28 07:27:22 [AX] Handling tool call: secret_code
2026/04/28 07:27:22 [AX] Captured 1 tool outputs for secret_code
2026/04/28 07:27:24 [AX] Conversation completed.
```

#### Client Output
```
⏺ Use the 'secret_code' agent to get the secret code 

The secret code for user 'sundar' is `secret code 111 from ax`.
```

TAG=agy
CONV=4d4e96b3-966e-488c-8de2-31a1c6bb1978
2026-04-28 13:57:11 -07:00
Junjie Wang 369b7fb88b Move internal_only to Message level. (#259)
This PR moves the internal_only boolean from `AgentOutputs` to
`Message`.

1. Internal-only messages are persisted in event log as `Message`. The
`internal_only` bit is not persisted.
2. I'd like to store some execution metadata as an internal event, e.g.
conversation id, task id, etc.
3. When resume, these internal metadata is read by AX controller, but I
want to filter them out of the history that we feed into planner agents
or custom agents since these messages are of no meaning to them.

Thus, moving it to Message level for log filtering.
2026-04-27 21:34:41 -07:00
Jaana Dogan 13a77b8946 Move experimental features to internal/experimental (#257)
Allows us to more easily track and update experimental features.
2026-04-27 10:02:42 -07:00
Jaana Dogan 17718da571 go fmt ./... (#254) 2026-04-26 20:17:16 -07:00
Jaana Dogan 5dc999316a Use JSON encoding for agent configuration (#252)
We allow any encoding for sub agent configuration after
https://github.com/google-gemini/ax/pull/248. This change is removing
the proto encoding requirement for the Gemini and the test agents.
2026-04-26 20:16:39 -07:00
Jaana Dogan e8491a1c27 Use bytes for agent_config (#248)
bytes allow arbitary configuration encoding format, e.g. JSON. Rename
AgentStart.config to AgentStart.agent_config for consistency.
2026-04-25 14:51:45 -07:00
Junjie Wang a6d8a2803f Add conversation_id in AgentMessage and Agent interface. (#244)
This PR adds a new conversation_id field in AgentMessage. 

Also add the conversationID to the signature of Agent.Connect and
Executor.Exec so that we can:
1. propagate conversationID from controller to sub-agent, like remote
agent, a2a agent
2. propagate conversationID to substrate worker

The conversationID is unused by anything yet. It will be picked up by
a2a agents in follow up PRs, and possibly substrate agent as well.
2026-04-23 11:54:21 -07:00
Jaana Dogan 60a7e370a8 Remove Agent.HealthCheck (#233)
AX won't be doing any healthchecks itself. agent.HealthCheck is obsolete
now. We keep the AgentService.HealthCheck RPC to ensure agent
implementators provide a health check mechanism on wire for job
schedulers.
2026-04-20 12:56:03 -07:00
anj-sandanj-s d736972e54 feat(ax): Implement Conversation Forking and Fix Trace Viewer Ordering (#229)
1. Conversation Forking

Protocol: Added Fork RPC to EventLogService with support for specifying
source conversation, sequence number (checkpoint), and optional
destination ID.
Controller: Implemented the Fork method to clone events while preserving
shared execution references.
CLI: Added root-level ax fork command. It supports both remote mode (via
--server) and "headless" local mode (reading ax.yaml or custom config
via --config).

2. Trace Viewer Fixes
Display Correct ID: Fixed the trace viewer to display the actual
Conversation ID in the terminal output and UI header instead of the
random root execution ID.
Chronological Ordering: Fixed a bug where execution cards were sorted
alphabetically by their random UUID ExecID. They are now sorted
chronologically based on the order they occurred in the conversation.

3. Testing
Added unit tests for the forking logic in the controller.
Added integration tests for the server-side RPC.

Co-authored-by: anj-s <anjalisridhar@google.com>
2026-04-20 12:48:53 -07:00
Jaana Dogan 2123c3cd73 Fix the in flight error message (#240)
We guard by conversation ID, not by exec ID anymore.
2026-04-20 10:52:35 -07:00
JoyceLiu 6f8f97b907 Antigravity and AX integration - skills (#218)
# Introduce AntigravityPlanner (Initial Cut)

This PR introduces the `AntigravityPlanner` into the AX framework. This
is just an **initial cut** to establish the connection and basic flow.
There are heavy TODOs left for subsequent PRs.

## Key Changes & Clarifications

1. **Initial Cut**: This version only takes a service URL and handles
limited content types. We will expand this in future PRs to handle more
complex interactions and data types.
2. **Python Sidecar**: The Antigravity planner works as a Python
sidecar. The relevant code resides in `google3` because the Antigravity
SDK is not public yet. cl/902522140 has the initial change and is
subject to a pending CL in Antigravity side
3. **Status & TODOs**: This is a preliminary prototyping to show
Antigravity can select and execute local skill but many to do.

**Nexts** 
- README 
- uplift Antigravity python server 
- support more types 

## Test Evidence
We verified the basic local skills selection and execution end-to-end.
Below is the evidence from the successful test run.


**Client Command:**
```bash
go run ./cmd/ax exec --server localhost:8494 --input "Give me an emoji for sundar"
```

**Cient Output**

⏺ Give me an emoji for sundar

The emoji for Sundar is 👓(⌐■_■) 👍.

**Antigravity Server Logs:** The logs show the planner successfully
loaded the skills directory and processed the steps (including reading
files and searching) to find the emoji:

[Antigravity Server] Starting simplified Antigravity Planner server on
port 8085...
[Antigravity Server] Received request with 1 messages.
[Antigravity Server] Loaded skills:
[SkillPath(path='/usr/local/google/home/lhuan/projects/gar/skills/ax/examples/skills',
path_type='absolute')]
[Antigravity Server] Creating new conversation for
9ea10cff-a2c7-4ee0-9091-b3a9bb717aba

[Antigravity Server] Step received: user_input -> "Give me an emoji for
sundar"
[Antigravity Server] Step received: planner_response
[Antigravity Server] Step received: view_file
[Antigravity Server] Step received: list_directory
[Antigravity Server] Step received: run_command -> Returns 👓(⌐■_■) 👍
[Antigravity Server] Step received: planner_response -> "The emoji for
Sundar is 👓(⌐■_■) 👍."
2026-04-20 10:49:01 -07:00
Jaana Dogan 392a19e7e5 Introduce ATE agents (#213)
This pull request introduces initial support for integrating
experimental ATE (SubstrATE) agents. It also includes a complete example
with manifests and a sample agent implementation.

The changes are primarily focused on enabling optional ATE agent support
behind a build tag. CreateActor isn't always working but we will use
this experimental feature as a starting point.

ATE agents start and suspend actors for execution at every agent call.
In the future, we would like to make changes to the controller to make
native resumption of agents from the snapshots possible. For now, we are
creating and suspending at every agent call.

We will rename ATE once we have an external name for the project.
2026-04-17 10:04:09 -07:00
Jaana Dogan ee9c62acdf Rename the proto package (#206)
Remove redundant AX* prefix from service names.
2026-04-15 11:22:03 -07:00
Jaana Dogan 1bc1955791 Switch to Interactions ToolCallContent and ToolResultContent (#205) 2026-04-14 13:48:50 -07:00
Jaana Dogan 782e950416 Rename last_seen_seq to last_seq (#202) 2026-04-14 09:58:50 -07:00