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.
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.
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.
* 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
* 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.
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>
- 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.
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.
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.
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.
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.
## 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.
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.
# 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
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.
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.
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.
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.
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>
# 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 👓(⌐■_■) 👍."
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.