merge: sync upstream google/ax v0.2.0 + absorb WorkspaceSystemInstruction#10
Merged
Merged
Conversation
google#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>
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.
…util (google#242) Refactor: Move newControllerFromConfig to shared package internal/cliutil
This PR optimizes the building of execution traces in cmd/ax/trace.go by iterating over the already ordered execIDs list and looking up events in a map. This avoids a redundant sorting step and reduces complexity from O(N^2) to O(N). A unit test was also added to verify order preservation.
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.
bytes allow arbitary configuration encoding format, e.g. JSON. Rename AgentStart.config to AgentStart.agent_config for consistency.
We allow any encoding for sub agent configuration after google-gemini/ax#248. This change is removing the proto encoding requirement for the Gemini and the test agents.
AX is primarily a server project. The Serve section is accidentally burried undernath other sections.
- ConversationId is added in the reply agent message of the remote agent example. Without conversation id the message gets rejected. (The ate agent and k8s sandbox agent have it added in the previous PR).
Allows us to more easily track and update experimental features.
Add content types for different media types. These new types will be used by the A2A bridge. Mirror the exact content types from Interactions with a slight difference: - Added two MimeType in DocumentContent for A2A bridge: `TYPE_JSON`, `TYPE_PYTHON`. The current enum in Interactions has a small set of values. We may update this in the future to make them consistent.
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.
# 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
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Also add a TODO about the current bug and why we are not yet fixing it.
Gemini planner is overwhelmingly choosing these skills. New comers to the project will fork the repo and observe this behavior. Also most people will bring skills through the global directories or SKILLS_DIR.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- **Add unit and integration tests for gemini_planner** - **Handle missing skills directory gracefully in NewSkillsTool**
…te caller (google#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.
* Harden Antigravity Interactions harness HTTP client
Fix a substrate suspend/resume failure and tighten the config surface:
- Disable HTTP keep-alives on the harness's default client. On substrate the
actor is suspended after a turn and resumed for the next (with a new routable
IP), which leaves any pooled keep-alive connection stale; reusing it made the
next turn's request fail. Opening a fresh connection per request avoids that
at the cost of an extra handshake.
- Remove the public HTTPClient override from AntigravityInteractionsConfig.
External callers don't configure the harness's transport, and exposing it
risked silently reintroducing the keep-alive bug. New now always owns its
(keep-alive-disabled) client; tests inject a fake transport via an unexported
newWithHTTPClient seam.
- Reorder AntigravityInteractionsConfig fields to list required (Agent,
StateDir) before optional, with clearer doc comments.
Build and package tests pass.
* Explain why the HTTP transport is cloned, not newly created
Address review feedback: document that cloning DefaultTransport (rather than a
bare &http.Transport{}) is intentional -- it inherits the production defaults
(env proxy, dial/TLS/handshake timeouts, HTTP/2) and only overrides keep-alives.
A fresh empty transport would silently drop those.
…le#288) parseStreamedTurn only handled interaction.* and step.* SSE events; a server-emitted "error" event fell through the switch and was silently dropped. The turn then returned as completed-but-empty, which: - made a failure (e.g. INVALID_ARGUMENT for a malformed client tool result) look like a blank "no response" turn, and - persisted the failing interaction's id as a poisoned resume cursor. Add an "error" case that returns a real error, plus a serverErrorMessage helper that extracts message/status from the error payload. Now a turn-level server failure aborts the turn with a descriptive error instead of being hidden. Adds tests for the error-event path and serverErrorMessage formatting.
* Run antigravity interactions as actor. * Add instructions for users. * Remove a comment. * Fix readme format.
…ume (google#289) The Python sidecar previously kept a per-conversation Agent instance in an in-process dict (self._agents) with a lock, and relied on that cache to preserve conversation history across turns. This tied conversation state to a single sidecar process lifetime: any restart, crash, or multi-replica scaling would silently drop history. Delete the cache and use the Antigravity SDK's own per-conversation persistence instead. LocalAgentConfig accepts (conversation_id, save_dir); the SDK's localharness persists trajectory state to {save_dir}/{sdk_conv_id}.db. Each turn now: 1. Constructs a fresh Agent scoped to a per-AX-conversation save_dir (~/.ax/antigravity/conversations/{ax_conv_id}/), so each AX conversation gets its own directory with at most one .db file. 2. If a .db exists there (from a prior turn), passes its stem as the SDK's conversation_id to trigger resume. The SDK's conversation_id is resume-only -- passing a non-existent id errors out -- hence the discover-then-pass pattern. 3. Runs the turn inside an async-with Agent(...) block, disposing at the end. No process-level state. Mirrors internal/harness/antigravityinteractions/DefaultStateDir() convention (state under ~/.ax/, out of the agent's cwd/operating surface). A TODO references issues google#269 and google#203 for the eventual controller-injected save_dir via harness_config. Test replaces test_grpc_connect_agent_reused (which asserted cache behavior) with test_grpc_connect_agent_per_turn_with_save_dir, which asserts per-turn Agent construction and deterministic per-conv save_dir naming. Verified end-to-end with ax exec --conversation <id>: turn 2 correctly recalls a name introduced in turn 1 across a fresh sidecar process, with only one .db file per conversation on disk. Different --conversation ids remain isolated.
…#293) * Update install script to use GOOGLE_CLOUD_PROJECT instead of PROJECT_ID Fixes google#290. * Use GOOGLE_CLOUD_PROJECT variable and remove PROJECT_ID usage Fixes google#290.
PR google#244 normalized thought summary text with a trailing '\n' in harness_server.py but harness_server_test.py wasn't updated. test_grpc_connect_success and test_grpc_connect_buffering assert against the pre-normalization strings and fail. The drift went unnoticed because the Python tests aren't wired into CI (.github/workflows/go.yml only runs go test ./...). Wiring pytest into CI is the follow-up.
* antigravity: plumb sidecar state_dir from ax.yaml
Sidecar's trajectory storage directory was hardcoded in Python. This
plumbs it through as a yaml config that users can override, matching
the pattern already used for the Interactions harness's state_dir.
Flow:
- ax.yaml: harnesses.antigravity.state_dir (optional; empty = default)
- config.go: AntigravityHarnessConfig.StateDir field (yaml surface only,
no default logic on the Go side)
- cliutil.go: passes yaml value as-is (empty string when unset)
- antigravity Go client: appends --state-dir arg only when non-empty
- Python sidecar: argparse --state-dir with default
~/.ax/antigravity/conversations. Default is a transitional fallback
for substrate mode (where ActorTemplate doesn't inject the flag yet).
Removable once substrate can set the field via ActorTemplate.
Servicer's state_dir is required (no fallback) -- production path always
receives it from argparse; tests pass tmp_path.
Verified end-to-end with ax exec:
- No yaml state_dir -> ~/.ax/antigravity/conversations/{conv_id}/
- yaml state_dir: X -> X/{conv_id}/
- Different conversations remain isolated across both paths.
Closes part of the local-mode gap in issue google#269 (harness_config
contract design); a follow-up will do the same for substrate via
ActorTemplate.
* antigravity: address PR review comments (P1/P2/P3)
- P1: two Python tests referenced tmp_path without declaring the fixture;
add tmp_path to their signatures.
- P2: state_dir CLI arg was not tilde-expanded, so ~/.ax/... became a
literal ~ dir. Add .expanduser().
- P3: no Go-side test asserted --state-dir forwarding. Extract
buildSidecarArgs() as a small pure helper and add table test for the
empty/non-empty cases, plus a yaml parse test for
AntigravityHarnessConfig.StateDir.
* antigravity: inline sidecar args build instead of separate helper
Follow-up to review feedback: buildSidecarArgs was overkill for a 3-line
conditional. Move it back into New() inline. Also drops the associated
TestBuildSidecarArgs (the arg-forwarding rule is now covered only by
end-to-end verification).
The 17 tests in python/antigravity/harness_server_test.py have never run in CI. .github/workflows/go.yml only runs 'go test ./...' and the Makefile 'test' target likewise only runs Go. That let PR google#244 land with 2 stale assertions unnoticed (fixed in google#291). Adds: - .github/workflows/python.yml -- dedicated Python workflow (kept separate from the Go-only go.yml so each can evolve independently). - Makefile 'test-python' target for local parity. Test deps are inlined in the workflow rather than added as a separate requirements-test.txt -- the diff is 2 packages ('pytest>=7.0', 'pytest-timeout>=2.0'), not worth its own file. Version floors match the upstream google-antigravity SDK's dev extras so this doesn't drift as pytest evolves. --timeout=30 --timeout-method=thread guards against hung gRPC servers so a stuck test can't eat the whole workflow budget.
* antigravity: parse and validate request harness_config Parse HarnessStart.harness_config (JSON-in-bytes) and overlay it onto the server's default LocalAgentConfig before each turn. - Blocks a request from overriding AX-owned persistence fields (save_dir, conversation_id) via _AX_MANAGED_CONFIG_FIELDS; these are injected last so a request can't redirect trajectory storage. - Reconstructs the config (not model_copy) so the SDK re-validates the overlaid values and surfaces its own error; invalid config fails the turn with INVALID_ARGUMENT instead of crashing. - save_dir derives from the injected state_dir (google#292) as state_dir / conversation_id. * antigravity: address harness_config review comments - Inline _parse_harness_config into _build_config_for so the parsed dict stays a local intermediate; the method's only boundary type is the validated LocalAgentConfig (no dict[str, object] across a helper boundary). Addresses the "introduce a type for the config" comment. - Make per-conv save_dir test assertions portable: compare against str(tmp_path / conversation_id) instead of hardcoding "/conv-1", and drop the now-vestigial Path.home monkeypatch (save_dir derives from the injected state_dir since google#292). Addresses the Windows path separator comment. _AX_MANAGED_CONFIG_FIELDS keeps guarding both conversation_id and save_dir on purpose: harness_config is a separate client-controlled surface from the request's conversation_id, so blocking it prevents a request from redirecting another conversation's trajectory storage.
LocalAgentConfig uses pydantic's default extra="ignore", so an
unrecognized field in a request's harness_config -- e.g. a typo like
"system_instruction" for "system_instructions" -- was silently dropped
and the caller believed the override took effect when it did not.
Add _reject_disallowed_fields(overrides), which factors two key checks
into one helper and raises HarnessConfigError (mapped to
INVALID_ARGUMENT) naming the offending field(s):
- fields that come from outside harness_config (_NON_HARNESS_CONFIG_
FIELDS: conversation_id from the runtime request, save_dir from the
server's state_dir), and
- unknown top-level fields.
Validation is best-effort and top-level only: nested-key and value/type
validation is delegated to the SDK's own LocalAgentConfig validation at
construction time.
state_dir is an AGY SDK implementation detail (where trajectory / resume-cursor storage lives), not something AX users should configure. Remove the yaml surface for both built-in harnesses and derive the path internally from config.AXAssetsDir(). - config: drop StateDir from AntigravityHarnessConfig and AntigravityInteractionsHarnessConfig. - antigravity: add DefaultStateDir() -> ~/.ax/antigravity/conversations, mirroring antigravityinteractions.DefaultStateDir(). cliutil now passes this into antigravity.New instead of the yaml value. - cliutil: interactions harness always uses DefaultStateDir(); drop the yaml read + fallback. The internal APIs (antigravity.New's stateDir arg, the required AntigravityInteractionsConfig.StateDir field, and the Python sidecar's --state-dir default) are unchanged -- only the user-facing ax.yaml knob is removed. Addresses rakyll's follow-up on google#292.
…oogle#300) Materialize agentskills.io skills from the Gemini Enterprise Skill Registry (Vertex AI v1beta1) into on-disk folders before the harness starts, so the built-in harnesses can use registry-hosted skills on the local `ax exec` / `ax serve` path. - internal/skills/geminienterprise: harness-agnostic package that reads config.SkillsConfig, drives the registry client (ListSkills / GetSkill / GetSkillRevision / skills:retrieve), safe-unzips payloads to <target_dir>/<skill-id>/, and reports what it wrote. First-wins on duplicate ids with a warning; fail-safe (a registry error never blocks harness startup). - config: top-level `skills.registries[]` (harness-agnostic -- each actor runs a single harness that consumes the materialized folder). Per-registry selection (skills / query / all), required target_dir, and a validated "exactly one selection mode" rule. Also wires the interactions harness's system_instruction from ax.yaml. - cliutil: materializes skills once, up front, at controller construction; for the interactions harness (no SKILLS_DIR concept) it appends a discovery pointer to the system instruction. Scope: local path only; the substrate/pod path does not yet read ax.yaml. Verified end-to-end against a live registry (by-id and by-query).
The exec example used `--harness coding`, but no "coding" harness exists; runHarness only accepts "antigravity" and "antigravity-interactions" and errors otherwise, so the command as written fails. Point the example at the real "antigravity" harness.
* Honor view_file line range and cap result size
execViewFile read the whole file and ignored the agent's StartLine/EndLine,
so viewing a large file returned its entire content as the tool result --
a ~900KB blob for a 3k-line CSV stalled the following turn.
Implement the Antigravity view_file contract (1-indexed inclusive line range
with slice-notation windowing) plus defensive caps:
- StartLine/EndLine window (neither=first N lines; start-only=next N forward;
end-only=previous N backward; both=precise range, capped to N).
- viewFileMaxLines / viewFileMaxBytes caps so a large file can never blob.
- ContentOffset honored as the read position within the windowed content.
The result payload is just {"content": ...}; the view_file result schema is
defined server-side, so no extra fields are added.
Adds intArg/intArgOK helpers and tests for windowing, byte cap, offset read,
and range honoring.
* view_file: UTF-8-safe byte cap + pagination metadata
Address review feedback on the byte cap:
- applyByteWindow backs the cut off to the last complete UTF-8 rune so a
multi-byte character straddling viewFileMaxBytes is never split (raw byte
slicing could produce invalid UTF-8, corrupting the last char and JSON
serialization).
- execViewFile returns the metadata the server needs to distinguish a
complete read from a paginated/byte-truncated one: content, start_line/
end_line (0-indexed inclusive served range), content_offset,
line_range_bytes (total bytes of the line range pre-byte-cap), and
num_lines/num_bytes. The server detects truncation via
content_offset+len(content) < line_range_bytes and prompts the model to
resume from that offset.
A paired server change reads these fields instead of hardcoding
start_line=0/end_line=last.
Adds tests for UTF-8 boundary capping, pagination metadata, and resume.
) * docs(readme): refresh Antigravity setup, auth, and CLI usage Re-land the README refresh that was approved as google#310 but never reached main (it merged into google#309's branch, which was then squash-merged, orphaning this content). - Document the built-in Antigravity harness: local execution needs python3 and pip on PATH; AX auto-starts the Python sidecar and installs the pinned SDK dependencies on first use. - Promote Authentication to its own top-level section (Google AI Studio and Vertex AI / ADC). - Refresh Quick Start / Usage examples, document the --harness-config and --harness-config-json flags, and add a per-request configuration example. * docs(readme): use gemini-3.5-flash in per-request config example Update the per-request harness config example from gemini-2.5-pro to a current model, matching the model naming used in examples/skills.
…strate (google#322) runAntigravityHarness (the `ax harness antigravity` path used by the substrate ActorTemplate) forked the Python sidecar with only --host/--port. The sidecar then fell back to its own default state dir, so on a substrate actor per-conversation save_dir landed on a path AX does not control, which breaks SDK-native resume across actor Run/Restore and snapshotting. Derive the trajectory dir via antigravity.DefaultStateDir() and pass it as --state-dir, mirroring the local path in antigravity.New and the interactions harness in runAntigravityInteractionsHarness (both own the path internally rather than taking it from ax.yaml/ActorTemplate). Fixes google#321
0.1.7 reads GenAI credential env vars natively. Bump first; the sidecar override is removed in a follow-up (google#325). Behavior unchanged here since explicit override kwargs still take precedence. Ref google#325
google-antigravity 0.1.7's _connect_websocket retries both "localhost" and "127.0.0.1" when dialing localharness (local_connection.py:993), so the sidecar no longer needs to patch /etc/hosts for substrate/gVisor environments where localhost may not resolve. Ref google#325 antigravity: guard conversation_id for safe save_dir use (google#312) harness_server uses the caller-supplied conversation_id directly as a per-conversation storage directory name (save_dir = state_dir / conversation_id) before the Antigravity harness ever sees it, so a value like "../escape" could traverse outside state_dir. Reject an empty id or one containing a path separator or a "." / ".." component at the boundary in _run_turn, returning an INVALID_ARGUMENT end frame. This is intentionally only a path-safety guard, not the id-format contract. The Antigravity harness owns that: it validates the forwarded cascade_id (length and character set) per cl/948016352. Keeping the format rule in one place avoids the two layers drifting. chore: remove stale docs, gitignore, and Makefile entries (google#329) - CONTRIBUTING.md: drop 'make run-remote' (no such Makefile target) - .gitignore: remove orphaned entries (local/remote_agent, sandbox-router, tasklog, axepp, google-cloud-sdk, test-sandbox-config.yaml) - Makefile: add missing deps, clean-logs to .PHONY antigravity: remove vertex env-var override (superseded by AGY 0.1.7) (google#331) * antigravity: remove vertex env-var override (superseded by AGY 0.1.7) google-antigravity 0.1.7 reads GOOGLE_GENAI_USE_VERTEXAI / GOOGLE_GENAI_USE_ENTERPRISE + GOOGLE_CLOUD_{PROJECT,LOCATION} natively, so the sidecar no longer needs _vertex_kwargs_from_env to thread them into LocalAgentConfig. - Delete _vertex_kwargs_from_env + VertexKwargs. - _build_default_config: bare LocalAgentConfig(system_instructions=...). - _has_credentials: read project/location from env (SDK hydrates them onto VertexEndpoint, not LocalAgentConfig). - Update tests accordingly. Requires the 0.1.7 bump. Credential resolution is startup-only and does not touch the save_dir/state_dir resume path. Ref google#325 * antigravity: drop stale _has_credentials docstring note about vertex env override Interactions: persist interactions harness with durable dir and resume from it (google#327) interactions: make WorkDir authoritative for run_command (google#335) The interactions harness executed run_command relative to the process's ambient working directory, which is not reliably the agent's workspace: with no Cwd argument the command ran wherever the process cwd happened to be, and a model-supplied Cwd (including "/") was honored blindly. As a result tool calls often ran from "/" instead of the configured workspace (google#332). Make the workspace directory authoritative in the executor rather than relying on ambient process state: - Add AntigravityInteractionsConfig.WorkDir (defaults from AX_HARNESS_WORKDIR). - run_command now resolves its directory via resolveRunDir: no Cwd -> WorkDir; relative Cwd -> joined under WorkDir; absolute Cwd -> honored as-is. Empty WorkDir preserves the previous process-cwd behavior. - Add WorkspaceSystemInstruction to orient the agent about its working directory so it emits workspace-relative paths, and wire it in cmd/ax. Fixes google#332 Remove the binary (google#334) * Remove the binary * chore: add e2e directory to .gitignore * refactor: migrate binary file check to a dedicated workflow file * fix: restrict binary file check to added, copied, or modified files
Establishes a real shared merge-base with upstream so future syncs are clean `git merge` operations (previously the fork's rewritten history put the merge-base back in Feb 2026, making every comparison subject- rather than git-based). Resolution strategy, backed by a prior content audit showing the only upstream content agentfleet/main lacked was WorkspaceSystemInstruction: - All non-workspace conflicts resolved to "ours" (agentfleet). Those files were already a content superset of upstream for feature purposes; taking ours keeps the exact code af currently runs, so this merge is a behavioral no-op there and only records the lineage. - Absorb upstream WorkspaceSystemInstruction: add workspace.go and wire it into both system-instruction assembly points (cmd/ax/harness.go and cliutil.go), sourcing workDir from AX_HARNESS_WORKDIR. The upstream WorkDir config field is intentionally not taken — the fork already makes execution authoritative via os.Chdir (setHarnessWorkDir + a per-turn re-chdir in the interactions executor). - Drop upstream's check-binaries.yml that the merge re-added: agentfleet relocated that guard into go.yml (fork google#329). Net effect vs agentfleet/main: only the WorkspaceSystemInstruction absorption (4 files). Build, vet, and full test suite pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Yc8cbm8fuV5f7LWZYpyiTw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A real
git mergeof upstreamgoogle/ax(v0.2.0,a34bbeb) intoagentfleet/main, resolving to agentfleet's current state plus the one missing upstream feature (WorkspaceSystemInstruction).Why merge (not a hand-written commit)
The fork's history is rewritten relative to upstream, so the merge-base sat back in Feb 2026 — every "what's ours vs upstream" comparison had to be done by matching commit subjects, which is fragile (upstream and fork even collide on PR numbers). This merge commit makes upstream's tip (
a34bbeb) the shared base, so:git merge-base agentfleet/main origin/mainis nowa34bbeb(was Feb 2).git mergeoperations over just the new delta.Resolution (backed by content audit)
A prior audit showed the only upstream content
agentfleet/mainlacked wasWorkspaceSystemInstruction. So:ours. Those files were already a feature-superset of upstream; taking ours keeps the exact codeafruns today → this merge is a behavioral no-op there, it only records lineage.WorkspaceSystemInstruction: addworkspace.go, wire it into both system-instruction assembly points (cmd/ax/harness.go,cliutil.go), sourcingworkDirfromAX_HARNESS_WORKDIR. Upstream'sWorkDirconfig field is deliberately not taken — the fork already makes execution authoritative viaos.Chdir(setHarnessWorkDir+ per-turn re-chdir).check-binaries.ymlthat the merge re-added — agentfleet relocated that guard intogo.yml(fork chore: remove stale docs, gitignore, and Makefile entries google/ax#329).Net effect
git diff agentfleet/main= only the 4 workspace files (+105/-2).git diff origin/main= agentfleet's full fork divergence (expected).Test
go build ./...✅ ·go vet ./...✅ ·go test ./...✅ (13 pkgs, incl. newworkspace_test.go)Follow-ups
fork/main→origin/mainclean mirror (pending force-push).agentfleet/go.modreplace pina84892e→ this branch's merge tip.🤖 Generated with Claude Code
https://claude.ai/code/session_01Yc8cbm8fuV5f7LWZYpyiTw