Repository navigation
Conversation
The claude runtime runs Claude Code in the fixed /data/workspace. A session that works on repositories shared through a volume (a directory of its own beside read-only bare mirrors of the repositories) needs it to work in that directory, and the directory starts empty. The runtime now reads, per turn, three dispatch headers (constants in go/api/a2a): x-kagent-working-directory, the directory the turn works in; x-kagent-mirrors-directory, a read-only directory of bare mirrors <owner>/<repository>.git; and x-kagent-changed-repositories, the repositories whose origin moved since the mirrors' last sync. A turn without a working directory runs where it always did, so nothing changes until something dispatches them. Claude Code is spawned in the dispatched directory. Before the first turn in a directory with mirrors, the runtime sets it up with a new shared package, go/harness/workspace, so the Go ADK runtime can reuse it: every mirror gets a clone with alternates at <owner>/<repository> (git clone --shared --no-checkout), its origin the mirror's origin URL without user information, no working tree and no local branch, so the agent's first git switch checks the default branch out from origin; checkout.workers, core.untrackedCache and feature.manyFiles suit a network filesystem. Then exactly the changed repositories are fetched from origin inside the dispatched turn, so the fetch leaves through the egress gateway with the turn's credential and the sandbox holds none, each under a timeout and all under one budget. CLAUDE.md notes say how the directory is laid out; .kagent/workspace.json records the setup, so a later turn neither clones nor fetches again and a resumed turn continues its process without setup. A clone or fetch that fails is the turn's first status message, naming the repository and the reason, and the turn still runs: runtime EventSinks gain Notice, which the shared A2A executor reports as a working status of the task. The mirrors belong to the volume's owner, so Claude Code's environment tells its git to trust them (safe.directory=<mirrors>/* through GIT_CONFIG_COUNT, nothing written to a file); nothing else is trusted. The gateway sets these headers once a session's actor mounts a volume source, which needs Substrate to mount an existing volume at a sub-path (agent-substrate/substrate#1637); that follows separately, and so does an e2e of a session on a volume. Tested by unit tests of the workspace package against real git (the clone layout, the fetch of changed repositories, a refused and a timed-out fetch, the budget, a set-up directory left alone, links the agent left, the trust environment), of the driver (Claude Code in the dispatched directory, the configured directory without one, the setup's notice and the trusted mirrors), and of the executor's Notice. Signed-off-by: Timo Derstappen <teemow@gmail.com>
The Go ADK runtime works in its scratch directory /tmp/kagent/<session>. A session that works on repositories shared through a volume needs its tools in the session's own directory of that volume, set up from the volume's bare mirrors, as the claude runtime now does. The executor reads the turn's dispatch per turn (x-kagent-working- directory, x-kagent-mirrors-directory, x-kagent-changed-repositories, from go/api/a2a). With a working directory, the turn's context carries it (a small go/adk/pkg/turn package), and before the first turn in a directory with mirrors the runtime sets it up with the shared go/harness/workspace package: a clone with alternates of every mirror, then a fetch of exactly the changed repositories inside the dispatched turn, under a per-repository timeout and a total budget. AGENTS.md notes say how to start work in a repository; .kagent/workspace.json makes the setup once. A resumed turn (an answer or an approval) sets up nothing, and a session with earlier turns whose directory is set up again is told so. A clone or fetch that fails is the message of the turn's first working status, naming the repository and the reason, and the turn runs. bash runs in the turn's directory and the file tools resolve relative paths there; the scratch directory stays reachable by its absolute path and a path outside both is refused. A turn without a directory keeps the scratch directory, so nothing changes until something dispatches the headers; the gateway does once a session's actor mounts a volume source, which follows separately. Tested by unit tests on the executor (no volume, a refused fetch as the first status message, a resumed turn, a returning session) and on the tools (bash and the file tools in the turn's directory, the scratch directory by its absolute path, the containment). Builds on the claude runtime's session directory (its first commit here), which adds go/harness/workspace. Signed-off-by: Timo Derstappen <teemow@gmail.com>
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.
Summary
Builds on #3157 (the
clauderuntime's session directory, which addsgo/harness/workspaceand the dispatch headers): its commit is the first here and drops out once it merges; review the second commit.The Go ADK runtime works in its scratch directory
/tmp/kagent/<session>. A session that works on repositories shared through a volume needs its tools in the session's own directory of that volume, set up from the volume's bare mirrors, as theclauderuntime does with #3157.The executor reads the turn's dispatch per turn (
x-kagent-working-directory,x-kagent-mirrors-directory,x-kagent-changed-repositories). With a working directory:go/adk/pkg/turnpackage);go/harness/workspace: a clone with alternates of every mirror, then a fetch of exactly the changed repositories inside the dispatched turn, under a per-repository timeout and a total budget;AGENTS.mdnotes say how to start work in a repository, and.kagent/workspace.jsonmakes the setup once;bashruns in the turn's directory and the file tools resolve relative paths there; the scratch directory stays reachable by its absolute path, and a path outside both is refused.A turn without a directory keeps the scratch directory, so nothing changes until something dispatches the headers.
Note: #3131 also adds a
go/adk/pkg/turnpackage (a turn's process groups); the two touch different files of the same package.Validation
bashand the file tools in the turn's directory, the scratch directory by its absolute path, containment of paths outside both (TestResolvePathContainmentwith the turn's context).go vetandgo test -raceof./adk/pkg/a2a/...,./adk/pkg/tools/...,./adk/pkg/turn/....The same change runs as a carried commit on the giantswarm/kagent-upstream fork (giantswarm#403).