;
onMethodToggle: (methodId: string, checked: boolean) => void;
- onSecretIdChange: (id: string | null) => void;
+ onSecretIdChange: (methodId: string, id: string | null) => void;
secrets: SecretListItem[];
}) {
const { t } = useTranslation();
+ const showEnvVarInLabel = envMethods.length > 1;
return (
{fileMethod && (
@@ -163,18 +169,29 @@ function AuthOptions({
onCheckedChange={(checked) => onMethodToggle(fileMethod.method_id, checked)}
/>
)}
- {envMethod?.env_var && (
- onMethodToggle(envMethod.method_id, checked)}
- />
- )}
+ {envMethods
+ .filter((method) => method.env_var)
+ .map((method) => {
+ const secretId = agentEnvVars[method.method_id] ?? null;
+ const baselineSecretId = baselineAgentEnvVars[method.method_id] ?? null;
+ const isSelected = selectedIds.has(method.method_id) || Boolean(secretId);
+ const baselineSelected =
+ baselineSelectedIds.has(method.method_id) || Boolean(baselineSecretId);
+ return (
+ onSecretIdChange(method.method_id, sid)}
+ secrets={secrets}
+ onCheckedChange={(checked) => onMethodToggle(method.method_id, checked)}
+ />
+ );
+ })}
);
}
@@ -219,6 +236,7 @@ function EnvOption({
isDirty,
secretId,
baselineSecretId,
+ showEnvVarInLabel,
onSecretIdChange,
secrets,
onCheckedChange,
@@ -228,21 +246,25 @@ function EnvOption({
isDirty: boolean;
secretId: string | null;
baselineSecretId: string | null;
+ showEnvVarInLabel: boolean;
onSecretIdChange: (id: string | null) => void;
secrets: SecretListItem[];
onCheckedChange: (checked: boolean) => void;
}) {
const { t } = useTranslation();
+ const label = showEnvVarInLabel
+ ? t("executors:provideSecretForEnvVar", { envVar: method.env_var })
+ : t("executors:provideSecret");
return (
-
{t("executors:provideSecret")}
+
{label}
Set {method.env_var} via a
diff --git a/apps/web/components/settings/profile-edit/remote-credentials-card.test.tsx b/apps/web/components/settings/profile-edit/remote-credentials-card.test.tsx
index 9dd4159d43..a1e588c1ac 100644
--- a/apps/web/components/settings/profile-edit/remote-credentials-card.test.tsx
+++ b/apps/web/components/settings/profile-edit/remote-credentials-card.test.tsx
@@ -5,6 +5,7 @@ import { RemoteCredentialsCard } from "./remote-credentials-card";
import { AgentConfigOptions } from "./portable-config-bundles";
import { listRemoteCredentials } from "@/lib/api/domains/settings-api";
import { listAgentConfigBundles, type AgentConfigBundle } from "@/lib/api/domains/agent-config-api";
+import type { SecretListItem } from "@/lib/types/http-secrets";
vi.mock("@/lib/api/domains/settings-api", () => ({
listRemoteCredentials: vi.fn(),
@@ -29,12 +30,20 @@ function renderRemoteCredentialsCard(
onChange = vi.fn(),
onConfigChange = vi.fn(),
initialConfigBundleIds: string[] = [],
+ options: {
+ agentEnvVars?: Record;
+ onAgentEnvVarChange?: (methodId: string, secretId: string | null) => void;
+ secrets?: SecretListItem[];
+ } = {},
) {
render(
,
);
}
@@ -43,13 +52,20 @@ function RemoteCredentialsHarness({
onChange,
onConfigChange,
initialConfigBundleIds,
+ agentEnvVars: initialAgentEnvVars = {},
+ onAgentEnvVarChange: onEnvVarChange = () => {},
+ secrets = [],
}: {
onChange: (ids: string[]) => void;
onConfigChange: (ids: string[]) => void;
initialConfigBundleIds: string[];
+ agentEnvVars?: Record;
+ onAgentEnvVarChange?: (methodId: string, secretId: string | null) => void;
+ secrets?: SecretListItem[];
}) {
const [selectedIds, setSelectedIds] = useState([]);
const [configBundleIds, setConfigBundleIds] = useState(initialConfigBundleIds);
+ const [agentEnvVars, setAgentEnvVars] = useState(initialAgentEnvVars);
return (
{}}
- secrets={[]}
+ agentEnvVars={agentEnvVars}
+ onAgentEnvVarChange={(methodId, secretId) => {
+ setAgentEnvVars((current) => ({ ...current, [methodId]: secretId }));
+ onEnvVarChange(methodId, secretId);
+ }}
+ secrets={secrets}
gitIdentityMode="override"
onGitIdentityModeChange={() => {}}
gitUserName=""
@@ -251,6 +270,77 @@ describe("RemoteCredentialsCard auth selections", () => {
});
});
+describe("RemoteCredentialsCard with multiple env auth methods", () => {
+ const antigravityAgentName = "Antigravity";
+ const geminiMethodId = "agent:antigravity-acp:env:GEMINI_API_KEY";
+ const googleMethodId = "agent:antigravity-acp:env:GOOGLE_API_KEY";
+ const secrets: SecretListItem[] = [
+ {
+ id: "secret-gemini",
+ name: "Gemini key",
+ has_value: true,
+ created_at: "",
+ updated_at: "",
+ },
+ {
+ id: "secret-google",
+ name: "Google key",
+ has_value: true,
+ created_at: "",
+ updated_at: "",
+ },
+ ];
+
+ beforeEach(() => {
+ vi.mocked(listRemoteCredentials).mockResolvedValue({
+ auth_specs: [
+ {
+ id: "antigravity-acp",
+ display_name: antigravityAgentName,
+ methods: [
+ { method_id: geminiMethodId, type: "env", env_var: "GEMINI_API_KEY" },
+ { method_id: googleMethodId, type: "env", env_var: "GOOGLE_API_KEY" },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("lets both declared env methods be selected independently", async () => {
+ const onAuthChange = vi.fn();
+ const onEnvVarChange = vi.fn();
+ renderRemoteCredentialsCard(onAuthChange, vi.fn(), [], {
+ onAgentEnvVarChange: onEnvVarChange,
+ secrets,
+ });
+
+ fireEvent.click(await screen.findByText(antigravityAgentName));
+ const geminiOption = screen.getByRole("checkbox", {
+ name: "Provide secret for GEMINI_API_KEY",
+ });
+ const googleOption = screen.getByRole("checkbox", {
+ name: "Provide secret for GOOGLE_API_KEY",
+ });
+
+ fireEvent.click(geminiOption);
+ fireEvent.click(googleOption);
+
+ const secretSelects = await screen.findAllByRole("combobox");
+ expect(secretSelects).toHaveLength(2);
+
+ fireEvent.click(secretSelects[0]);
+ fireEvent.click(await screen.findByRole("option", { name: "Gemini key" }));
+ fireEvent.click(secretSelects[1]);
+ fireEvent.click(await screen.findByRole("option", { name: "Google key" }));
+
+ expect(geminiOption.getAttribute(DATA_STATE_ATTRIBUTE)).toBe(CHECKED_STATE);
+ expect(googleOption.getAttribute(DATA_STATE_ATTRIBUTE)).toBe(CHECKED_STATE);
+ expect(onAuthChange).toHaveBeenLastCalledWith([geminiMethodId, googleMethodId]);
+ expect(onEnvVarChange).toHaveBeenNthCalledWith(1, geminiMethodId, "secret-gemini");
+ expect(onEnvVarChange).toHaveBeenNthCalledWith(2, googleMethodId, "secret-google");
+ });
+});
+
const PORTABLE_BUNDLES: AgentConfigBundle[] = [
{
id: "claude.settings",
diff --git a/apps/web/components/settings/profile-edit/remote-credentials-card.tsx b/apps/web/components/settings/profile-edit/remote-credentials-card.tsx
index 114d65ffb7..6bc96965b8 100644
--- a/apps/web/components/settings/profile-edit/remote-credentials-card.tsx
+++ b/apps/web/components/settings/profile-edit/remote-credentials-card.tsx
@@ -14,7 +14,7 @@ import {
import { listRemoteCredentials, type RemoteAuthSpec } from "@/lib/api/domains/settings-api";
import { listAgentConfigBundles, type AgentConfigBundle } from "@/lib/api/domains/agent-config-api";
import type { SecretListItem } from "@/lib/types/http-secrets";
-import { AuthSection, getSpecMethods } from "./remote-auth-section";
+import { AuthSection } from "./remote-auth-section";
export type { GitIdentityMode, GitIdentityState } from "./git-identity-fields";
type RemoteCredentialsCardProps = {
@@ -110,30 +110,24 @@ export function RemoteCredentialsCard({
localGitIdentity={localGitIdentity}
/>
)}
- {agentSections.map(({ spec, configBundles: agentConfigBundles }) => {
- const methods = getSpecMethods(spec);
- const envMethod = methods.find((m) => m.type === "env");
- return (
-
- );
- })}
+ {agentSections.map(({ spec, configBundles: agentConfigBundles }) => (
+
+ ))}
>
) : (
diff --git a/apps/web/src/locales/en/executors.json b/apps/web/src/locales/en/executors.json
index 6ae394e343..64c306e2fd 100644
--- a/apps/web/src/locales/en/executors.json
+++ b/apps/web/src/locales/en/executors.json
@@ -175,6 +175,7 @@
"portableConfigNotFoundHint": "This is an editing hint. You can save the profile and add the file later.",
"portableConfigTitle": "Agent configuration",
"provideSecret": "Provide secret",
+ "provideSecretForEnvVar": "Provide secret for {{envVar}}",
"quickPresets": "Quick presets",
"refresh": "Refresh",
"relatedDockerContainersRemoved_one": "{{count}} related Docker container will also be removed.",
diff --git a/apps/web/src/locales/pseudo/executors.json b/apps/web/src/locales/pseudo/executors.json
index 7135965a62..2e6a3d0f8d 100644
--- a/apps/web/src/locales/pseudo/executors.json
+++ b/apps/web/src/locales/pseudo/executors.json
@@ -175,6 +175,7 @@
"portableConfigNotFoundHint": "Ţĥĩś ĩś àń ēďĩţĩńĝ ĥĩńţ. Ŷōũ ćàń śàvē ţĥē ƥŕōƒĩĺē àńď àďď ţĥē ƒĩĺē ĺàţēŕ.",
"portableConfigTitle": "Àĝēńţ ćōńƒĩĝũŕàţĩōń",
"provideSecret": "Ƥŕōvĩďē śēćŕēţ",
+ "provideSecretForEnvVar": "Ƥŕōvĩďē śēćŕēţ ƒōŕ {{envVar}}",
"quickPresets": "Qũĩćķ ƥŕēśēţś",
"refresh": "Ŕēƒŕēśĥ",
"relatedDockerContainersRemoved_one": "{{count}} ŕēĺàţēď Ďōćķēŕ ćōńţàĩńēŕ ŵĩĺĺ àĺśō ƀē ŕēḿōvēď.",
diff --git a/apps/web/src/locales/pt-pt/executors.json b/apps/web/src/locales/pt-pt/executors.json
index af90537605..bd1a90755f 100644
--- a/apps/web/src/locales/pt-pt/executors.json
+++ b/apps/web/src/locales/pt-pt/executors.json
@@ -157,6 +157,7 @@
"profileSaved": "Perfil guardado",
"profiles": "Perfis",
"provideSecret": "Indicar um segredo",
+ "provideSecretForEnvVar": "Indicar um segredo para {{envVar}}",
"quickPresets": "Predefinições rápidas",
"refresh": "Atualizar",
"relatedDockerContainersRemoved_one": "{{count}} contentor Docker relacionado também será removido.",
diff --git a/apps/web/src/locales/zh-cn/executors.json b/apps/web/src/locales/zh-cn/executors.json
index bc530eb496..61a36ad20b 100644
--- a/apps/web/src/locales/zh-cn/executors.json
+++ b/apps/web/src/locales/zh-cn/executors.json
@@ -157,6 +157,7 @@
"profileSaved": "配置已保存",
"profiles": "配置",
"provideSecret": "提供密钥",
+ "provideSecretForEnvVar": "为 {{envVar}} 提供密钥",
"quickPresets": "快速预设",
"refresh": "刷新",
"relatedDockerContainersRemoved_one": "还将移除 {{count}} 个相关 Docker 容器。",
diff --git a/apps/web/src/locales/zh-hk/executors.json b/apps/web/src/locales/zh-hk/executors.json
index 90ef6813f7..23b113113f 100644
--- a/apps/web/src/locales/zh-hk/executors.json
+++ b/apps/web/src/locales/zh-hk/executors.json
@@ -157,6 +157,7 @@
"profileSaved": "設定已儲存",
"profiles": "設定",
"provideSecret": "提供密鑰",
+ "provideSecretForEnvVar": "為 {{envVar}} 提供密鑰",
"quickPresets": "快速預設",
"refresh": "刷新",
"relatedDockerContainersRemoved_one": "還將移除 {{count}} 個相關 Docker 容器。",
diff --git a/apps/web/src/locales/zh-tw/executors.json b/apps/web/src/locales/zh-tw/executors.json
index 74a2b8e7fb..d43cb95333 100644
--- a/apps/web/src/locales/zh-tw/executors.json
+++ b/apps/web/src/locales/zh-tw/executors.json
@@ -157,6 +157,7 @@
"profileSaved": "設定已儲存",
"profiles": "設定",
"provideSecret": "提供金鑰",
+ "provideSecretForEnvVar": "為 {{envVar}} 提供金鑰",
"quickPresets": "快速預設",
"refresh": "重新整理",
"relatedDockerContainersRemoved_one": "還將移除 {{count}} 個相關 Docker 容器。",
diff --git a/docs/public/agents-and-profiles.md b/docs/public/agents-and-profiles.md
index 0a1958fa58..1f05588b3d 100644
--- a/docs/public/agents-and-profiles.md
+++ b/docs/public/agents-and-profiles.md
@@ -22,10 +22,12 @@ Open **Settings > Agents** (`/settings/agents`). Kandev scans the host on which

-The production registry currently shows Auggie, Claude, Codex, Copilot, Gemini, OpenCode, Amp, Qwen, iFlow (beta), Droid, Kilocode, Pi, Cursor, Kimi, Kiro, Qoder, Trae, `omp`, Devin, Grok, and Hermes. An entry is usable only when its executable is supported on the current platform and available to the Kandev process. Development and E2E profiles can add mock agents that are not product integrations.
+The production registry currently shows Auggie, Claude, Codex, Copilot, Gemini, OpenCode, Amp, Qwen, iFlow (beta), Droid, Kilocode, Pi, Cursor, Kimi, Kiro, Qoder, Trae, `omp`, Devin, Grok, Hermes, and Antigravity. An entry is usable only when its executable is supported on the current platform and available to the Kandev process. Development and E2E profiles can add mock agents that are not product integrations.
Hermes launches with `hermes acp`. Install the required `hermes` executable from its **Settings > Agents** card, which runs the official Hermes installer. Hermes currently supports task and workspace sessions. Office-assigned skill injection is not yet supported.
+Antigravity has no automated install: Google distributes `agy_acp_server.par` (`agy_acp_server.exe` on Windows) and its `localharness_external` or `localharness` sibling (`localharness_external.exe` or `localharness.exe` on Windows) as a signed archive through the [ACP registry](https://github.com/agentclientprotocol/registry/tree/main/antigravity-acp) rather than npm, so extract both files into one directory and add it to PATH yourself. Kandev fails discovery closed when the harness sibling is missing or not executable, so a partial extraction reports as not installed rather than as a broken session.
+
### Pi command surfaces
Pi uses separate executables for its two Kandev modes:
diff --git a/docs/specs/agents/README.md b/docs/specs/agents/README.md
index a6700cd464..9e6377f022 100644
--- a/docs/specs/agents/README.md
+++ b/docs/specs/agents/README.md
@@ -34,6 +34,7 @@ surface shared by task and Office consumers.
- [Agent Resume and Runtime Recovery](requirements/agent-resume-runtime-recovery.md)
- [Agent Rich Output](requirements/agent-rich-output.md)
+- [Google Antigravity ACP Agent](requirements/antigravity-acp-agent.md)
- [Agent Stall Recovery](requirements/agent-stall-recovery.md)
- [Collapsible Agent Blocks on the Agents Settings Page](requirements/collapsible-agent-blocks.md)
- [Cursor Subagent Metadata](requirements/cursor-subagent-metadata.md)
@@ -66,6 +67,7 @@ surface shared by task and Office consumers.
### System design
- [Agent Resume and Runtime Recovery](system-design/agent-resume-runtime-recovery.md)
+- [Google Antigravity ACP Agent](system-design/antigravity-acp-agent.md)
- [Injected Skill Naming](system-design/injected-skill-naming.md)
- [Injected Skill Naming Migration](system-design/injected-skill-naming-migration.md)
- [Dynamic Agent Routing System Design Part 1](system-design/dynamic-agent-routing-01.md)
diff --git a/docs/specs/agents/requirements/antigravity-acp-agent.md b/docs/specs/agents/requirements/antigravity-acp-agent.md
new file mode 100644
index 0000000000..704ab34dc9
--- /dev/null
+++ b/docs/specs/agents/requirements/antigravity-acp-agent.md
@@ -0,0 +1,364 @@
+---
+status: draft
+system: agents
+created: 2026-09-03
+owners:
+ - kandev
+---
+
+# Google Antigravity ACP agent requirements
+
+## Overview
+
+Google publishes a standalone ACP server for Antigravity as a signed native
+binary, separate from the `agy` CLI. Kandev needs it as a built-in agent so a
+user can select it for a task, run and resume a session over ACP, and use MCP
+servers and skills, without inventing a distribution, credential, or
+session-storage mechanism Google already owns. The agent system owns the durable
+decision: an agent identity plus its discovery, launch, config-root,
+authentication, and capability contract; transport and process supervision
+belong to the runtime. This first layer assumes the operator already installed
+the server; automated download and remote or containerized provisioning are
+named exclusions, not silence.
+
+## Terminology
+
+- **ACP server:** The executable in the ACP registry entry `antigravity-acp`
+ (`agy_acp_server.par`, `.exe` on windows). Not the `agy` CLI.
+- **Harness:** The `localharness_external` executable in the same archive, which
+ the ACP server locates and drives over gRPC.
+- **Config root:** `/antigravity-acp`, where `` is `$GEMINI_HOME`
+ when set and non-empty and `~/.gemini` otherwise. Holds settings, credentials,
+ trusted workspaces, and conversations. What Kandev declares about it is
+ REQ-AGENTS-ANTIGRAVITY-ACP-004.
+- **Advertised capabilities:** The `agentCapabilities` and `authMethods` in the
+ ACP `initialize` result.
+
+## Prior art
+
+Receipts first, because a leg with no receipt did not run.
+
+**Wiki leg: did not run, tool unavailable.** Vault
+`/Users/henry/Documents/henry/wiki`, collection `wiki`, `@henry`-pinned.
+`obsidian-wiki` and `qmd` are off PATH; the vault returns `Operation not
+permitted` to `ls` and `grep`.
+
+**Cross-product leg: did not run, tool unavailable.** The `saas-kb` MCP server
+and its `search_fsm_docs` tool are not exposed in this session.
+
+**In-repo prior art: read directly.** Kandev's built-in ACP
+agents are either managed npm runtimes on a pinned `npx` specification or
+binaries found on PATH and launched by name. Antigravity is the second kind, and
+Kandev has no managed non-npm download mechanism, which is why provisioning is
+excluded below rather than reinvented. It departs from that group three times —
+discovery checks a second file, the install script is empty, and there is no CLI
+passthrough — each argued in the design's
+[prior-art departure](../system-design/antigravity-acp-agent.md#prior-art-departure).
+
+## Evidence
+
+Values are of two kinds. **Measured** ones come from a manual probing session
+against build `agy_acp_server_20260818_01_RC01`, darwin-arm64, on 2026-09-03;
+the artifact was removed afterwards, so this pair summarizes that session rather
+than transcribing it, and re-checking one means re-downloading that build.
+**Registry** ones are read from the published `antigravity-acp/agent.json` and
+were never executed; each use says so.
+
+## Requirements
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-001: Built-in Antigravity agent identity
+
+**Intent:** Give Antigravity a stable catalog identity so a user can select it
+for a task and profile like any other built-in agent.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.1:** The agent identifier shall be
+ `antigravity-acp`, matching the `agentInfo.name` the ACP server reports.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.2:** The display name shall be `Antigravity`
+ and the catalog name shall be `Antigravity ACP Agent`.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.3:** The agent shall be enabled by default and
+ present in the default registry.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.4:** The display order shall be `22`, distinct
+ from every other built-in agent's, so the display-order sort never reaches its
+ stable-sort tiebreak here.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.5:** The agent shall return a non-empty light
+ and dark logo, and the light one for any other variant.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.6:** The agent shall declare the ACP
+ protocol and shall not be a virtual agent.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.7:** Because REQ-AGENTS-ANTIGRAVITY-ACP-007
+ contributes no install script, the description shall carry the install
+ guidance, stating all four of: the registry entry `antigravity-acp` as the
+ source; that the archive's two entries extract together into one directory;
+ that the directory be on PATH; and the platform executable name from
+ AC-AGENTS-ANTIGRAVITY-ACP-002.1. It shall embed no archive URL or build stamp,
+ both of which rotate.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.8:** The agent shall declare no agent-specific
+ permission settings; its catalog shall still expose the universal
+ auto-approve setting.
+- **AC-AGENTS-ANTIGRAVITY-ACP-001.9:** The agent shall not offer a CLI
+ passthrough mode.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-002: Discovery that fails closed on a partial install
+
+**Intent:** Report Antigravity as installed only when it can run, so a user
+never starts a session that silently loses capability mid-prompt.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.1:** Discovery shall look up exactly one
+ executable name on the PATH of the Kandev process performing discovery:
+ `agy_acp_server.exe` on windows, `agy_acp_server.par` on every other platform.
+ The name is therefore defined on every platform. Registry-sourced.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.2:** When that executable is not on PATH,
+ discovery shall report unavailable with an empty matched path and no error.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.3:** When the executable is on PATH, discovery
+ shall additionally require a harness in the directory containing it, trying
+ `localharness_external` then `localharness`, first match wins, each
+ `.exe`-suffixed on windows and unsuffixed elsewhere. A name matches a regular
+ file, with an execute bit required on non-windows hosts. A directory, dangling
+ symlink, or unstat-able name does not match; discovery continues.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.4:** When `ANTIGRAVITY_HARNESS_PATH` is set to a
+ non-empty value in the environment discovery observes, the
+ AC-AGENTS-ANTIGRAVITY-ACP-002.3 check shall be treated as satisfied without
+ inspecting the directory.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.5:** When the executable is present but
+ neither AC-AGENTS-ANTIGRAVITY-ACP-002.3 nor AC-AGENTS-ANTIGRAVITY-ACP-002.4
+ holds, the AC-AGENTS-ANTIGRAVITY-ACP-002.2 outcome shall apply unchanged.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.6:** When discovery reports the agent
+ available, the matched path shall be the path PATH lookup returned, and MCP
+ support and session resume shall both be reported as supported.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.7:** The directory inspected by
+ AC-AGENTS-ANTIGRAVITY-ACP-002.3 shall be the directory of the path PATH lookup
+ returned, without resolving symbolic links.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.8:** Discovery shall create no files, write no
+ configuration, and start no process, so repeated calls over an unchanged PATH
+ and filesystem return equal results.
+- **AC-AGENTS-ANTIGRAVITY-ACP-002.9:** When the caller's context is cancelled or
+ expires during discovery, discovery shall return that error rather than
+ reporting the agent as unavailable.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-003: One launch command across every surface
+
+**Intent:** Keep the session, runtime, and inference commands identical, so a
+session and a capability probe cannot disagree about how the agent starts.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.1:** On windows the argument vector shall be
+ exactly `agy_acp_server.exe` with no arguments; on darwin and on every platform
+ other than linux and windows, exactly `agy_acp_server.par` with no arguments.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.2:** On linux, the argument vector shall be
+ exactly `agy_acp_server.par --uid=`, matching the published registry entry for
+ both Linux platforms. Registry-sourced, not measured.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.3:** The platform selecting among
+ AC-AGENTS-ANTIGRAVITY-ACP-002.1, AC-AGENTS-ANTIGRAVITY-ACP-003.1 and
+ AC-AGENTS-ANTIGRAVITY-ACP-003.2 shall be that of the Kandev process building
+ the command — the host, not an executor's target; a remote executor on another
+ platform is a named exclusion below. Those three shall be total: a defined
+ lookup name and a non-empty vector on every platform. Availability is decided
+ by discovery alone, so no platform needs a special case here: anywhere else
+ AC-AGENTS-ANTIGRAVITY-ACP-002.2 governs the outcome.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.4:** Command construction shall be
+ deterministic: repeated calls on one platform shall produce equal vectors,
+ consulting neither PATH, the filesystem, nor the environment.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.5:** The session, runtime, and one-shot inference
+ commands shall produce the same argument vector for the same platform.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.6:** The argument vector shall not vary with
+ the selected model, permission policy, auto-approve setting, agent type, or
+ resume target.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.7:** This agent's own command construction
+ shall not add `--debug` or `--notices`.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.8:** The working directory shall be the task
+ workspace.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.9:** The runtime shall declare an empty
+ environment map and no stripped variables, so the agent inherits the execution
+ environment unmodified.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.10:** Resource limits shall be 4096 MB memory,
+ 2.0 CPU cores, and a one-hour timeout, matching other built-in ACP agents.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.11:** The runtime shall not request an
+ immediate process kill on shutdown.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.12:** One-shot inference shall be supported.
+- **AC-AGENTS-ANTIGRAVITY-ACP-003.13:** AC-AGENTS-ANTIGRAVITY-ACP-003.1 through
+ 003.7 bind this agent's own command construction, not the final launched argv:
+ the shared builder appends the profile's `cli_flags` and any sandbox command
+ prefix to every agent afterwards, and this agent shall declare no filter for
+ either.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-004: Config root, sessions, and skills
+
+**Intent:** Point Kandev at the directories the server uses, so a long task
+survives a relaunch and Kandev never writes state it does not own.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.1:** The session directory template shall be
+ `{home}/.gemini/antigravity-acp`.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.2:** Kandev shall not set, override, or read
+ `GEMINI_HOME`. The declared template is the server's default config root, so
+ it holds exactly when `GEMINI_HOME` is unset or empty in the execution
+ environment.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.3:** The agent shall declare native session resume
+ and that a session can be recovered.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.4:** Kandev shall introduce no per-launch
+ variation in the config root: one static template, no `GEMINI_HOME` set or
+ injected by Kandev, and no config-root flag. A relaunch therefore presents the
+ server the root the creating launch saw whenever the execution environment is
+ unchanged, which is the root the server scopes session lookup to.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.5:** The project skill directory shall be
+ `.agents/skills`.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.6:** The user skill directory shall be
+ `.gemini/config/skills`, the first entry of the server's global skill pair.
+ The server searches both entries, so this is a determinism choice rather than
+ a capability one, and it keeps Kandev out of the `agy` CLI's
+ `antigravity-cli/` domain.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.7:** The template nests inside the Gemini
+ agent's `{home}/.gemini`; the two shall remain distinct values, neither
+ aliased, merged, nor special-cased.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.8:** During a session, Kandev shall not
+ create, modify, or delete any file under the config root. Its only write there
+ is the user-initiated remote-auth copy in AC-AGENTS-ANTIGRAVITY-ACP-005.7,
+ which seeds a remote host before a session starts.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.9:** When `GEMINI_HOME` is set and non-empty,
+ the server resolves a config root the declared template does not describe.
+ Kandev shall not detect, read, or reconcile it; that host is a named exclusion
+ below.
+- **AC-AGENTS-ANTIGRAVITY-ACP-004.10:** The container session-directory target
+ shall be empty, so no bind mount is created for the config root and the
+ declared template has no runtime effect today, matching the other
+ native-binary ACP agents.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-005: Authentication owned by the ACP server
+
+**Intent:** Surface the server's own authentication contract instead of
+duplicating it, so an unauthenticated agent reads as "needs login", not "broken".
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.1:** Kandev shall present the authentication
+ methods the server advertises in its `initialize` result rather than a
+ hardcoded list.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.2:** Those methods shall be presented in the order
+ the server returned them, never re-sorted or re-ranked.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.3:** This agent shall declare no auth
+ classification of its own, relying on the shared ACP handling of a `-32000`
+ from a capability probe or session start, which records an
+ authentication-required state rather than a failed one. An unauthenticated
+ probe therefore records that state, not a success carrying an empty model
+ list.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.4:** This agent shall declare no stderr-derived
+ failure rule: when the server answers with a well-formed JSON-RPC error on
+ stdout, the traceback it also writes to stderr for that handled error is not an
+ additional or fatal launch failure.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.5:** Kandev shall surface the message the server
+ returns when an `authenticate` call fails because a required environment
+ variable is absent from the launch environment.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.6:** Kandev shall not mint, refresh, or
+ rewrite credentials; it only copies files the server already wrote per
+ AC-AGENTS-ANTIGRAVITY-ACP-005.7 and passes user-supplied variables.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.7:** Remote authentication shall offer a
+ file-copy method for darwin and linux carrying `settings.json`,
+ `acp_token.json` and `acp_business_token.json`, each under
+ `.gemini/antigravity-acp`, which is also the target directory.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.8:** The file-copy method shall not require every
+ listed file to exist; which token file is present depends on the
+ authentication method the user chose.
+- **AC-AGENTS-ANTIGRAVITY-ACP-005.9:** Remote authentication shall also offer
+ environment-variable methods for `GEMINI_API_KEY` and `GOOGLE_API_KEY`.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-006: Capabilities read from the agent, not inferred
+
+**Intent:** Derive behavior from what the server advertises, so an echoed
+protocol version is never taken for a capability claim.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-006.1:** This agent shall add no
+ `protocolVersion`-derived branch to the shared ACP capability path, which
+ already reads the advertised `agentCapabilities` and `authMethods`; the echoed
+ `protocolVersion` carries no capability information.
+- **AC-AGENTS-ANTIGRAVITY-ACP-006.2:** The agent shall not declare an assumed
+ HTTP or SSE MCP override, because the server advertises both.
+- **AC-AGENTS-ANTIGRAVITY-ACP-006.3:** MCP servers shall be passed through ACP
+ `session/new`, and the agent shall not declare a project-local MCP
+ configuration strategy.
+- **AC-AGENTS-ANTIGRAVITY-ACP-006.4:** The agent shall not declare that it
+ namespaces MCP tool names by server.
+- **AC-AGENTS-ANTIGRAVITY-ACP-006.5:** Billing type shall be resolved by the
+ shared default rule rather than pinned by this agent.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-007: No automated provisioning
+
+**Intent:** Contribute no install script, so a remote bootstrap never runs prose
+as a shell command or pulls a third of a gigabyte per host. Kandev executes that
+value verbatim as remote shell.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-007.1:** The install script shall be the empty
+ string.
+- **AC-AGENTS-ANTIGRAVITY-ACP-007.2:** The agent shall therefore contribute no
+ text to remote bootstrap scripts on any executor, and requesting an install job
+ shall fail with the existing empty-install-script error, running nothing.
+- **AC-AGENTS-ANTIGRAVITY-ACP-007.3:** When an executor cannot find the agent on
+ its PATH, Kandev shall surface whichever failure that executor already
+ produces and add no new preflight: SSH's existing preflight names the agent
+ and the missing binary, elsewhere the spawn failure names the binary alone.
+ Neither shall fall back to another agent, command, or a download.
+
+### REQ-AGENTS-ANTIGRAVITY-ACP-008: Concurrent sessions
+
+**Intent:** State that concurrency in the config root belongs to the ACP server,
+so two tasks run at once and no one adds a lock the server does not expect.
+
+#### Acceptance criteria
+
+- **AC-AGENTS-ANTIGRAVITY-ACP-008.1:** Two concurrent Kandev sessions using this
+ agent shall each launch an independent server process.
+- **AC-AGENTS-ANTIGRAVITY-ACP-008.2:** Kandev shall not serialize those processes
+ and shall not lock, guard, or arbitrate access to the shared config root;
+ conflicting writes there are the ACP server's responsibility.
+- **AC-AGENTS-ANTIGRAVITY-ACP-008.3:** Terminating one session shall not terminate
+ the other nor remove shared state under the config root.
+
+## Out of scope
+
+Each exclusion is a contract, not silence. The design's
+[deferred work](../system-design/antigravity-acp-agent.md#deferred-work) records
+what a follow-up would have to decide for the first three.
+
+- **Automated download and installation of the ACP server, and therefore
+ containerized, Kubernetes, SSH and Sprites provisioning.** Kandev has no
+ managed non-npm download mechanism, no image ships this binary, and
+ REQ-AGENTS-ANTIGRAVITY-ACP-007 contributes no install script, so those
+ executors report it not found.
+- **A remote executor whose platform differs from the Kandev host's.** Discovery
+ reads the host's PATH and every command surface is static, so a darwin host
+ driving a hand-provisioned linux remote ships the darwin vector and drops the
+ linux `--uid=` of AC-AGENTS-ANTIGRAVITY-ACP-003.2. Command construction takes
+ no per-executor platform input, so this layer neither detects nor corrects it.
+ REQ-AGENTS-ANTIGRAVITY-ACP-005's remote-auth methods serve the same-platform
+ remote, which is unaffected.
+- **Any non-default `GEMINI_HOME`, whether ambient or set per task.** The server
+ moves its whole config root while Kandev declares the default root and never
+ reads the variable, so session-dir seeding, the
+ AC-AGENTS-ANTIGRAVITY-ACP-005.7 copy and the AC-AGENTS-ANTIGRAVITY-ACP-004.6
+ user skill directory all address directories the server is not using: resume,
+ remote auth and user skills are undefined there, not merely degraded.
+- **Application Default Credentials for `agent-platform` remote auth.** A
+ single-variable remote-auth method cannot express `GOOGLE_CLOUD_PROJECT` plus
+ `GOOGLE_CLOUD_LOCATION` with ADC, so AC-AGENTS-ANTIGRAVITY-ACP-005.9 offers
+ only the two API-key methods and an ADC-only remote host has no auth path.
+- **A passthrough or TUI agent for the `agy` CLI.** `agy` 1.1.25 has no ACP mode,
+ is a different binary, and authenticates into `~/.gemini/antigravity-cli/`
+ rather than the config root, so binding both to one agent would make the
+ passthrough toggle silently switch credential domain and conversation store.
+- **Model catalog curation.** Models come from the existing ACP probe, which
+ needs authentication first. No static model list, no model flag.
+- **Windows and Linux verification.** Only darwin-arm64 was executed, so the
+ windows `.exe` name and the linux `--uid=` argument in
+ AC-AGENTS-ANTIGRAVITY-ACP-002.1/003.1/003.2 are registry-sourced and
+ unverified.
+- **Adding a failure reason to the discovery result.** Distinguishing "installed
+ but missing its harness" in user-facing text would change a discovery contract
+ shared by every agent, so the partial install reports plain unavailable.
diff --git a/docs/specs/agents/system-design/antigravity-acp-agent.md b/docs/specs/agents/system-design/antigravity-acp-agent.md
new file mode 100644
index 0000000000..7fc4cc0740
--- /dev/null
+++ b/docs/specs/agents/system-design/antigravity-acp-agent.md
@@ -0,0 +1,414 @@
+---
+status: draft
+system: agents
+requirements:
+ - REQ-AGENTS-ANTIGRAVITY-ACP-001
+ - REQ-AGENTS-ANTIGRAVITY-ACP-002
+ - REQ-AGENTS-ANTIGRAVITY-ACP-003
+ - REQ-AGENTS-ANTIGRAVITY-ACP-004
+ - REQ-AGENTS-ANTIGRAVITY-ACP-005
+ - REQ-AGENTS-ANTIGRAVITY-ACP-006
+ - REQ-AGENTS-ANTIGRAVITY-ACP-007
+ - REQ-AGENTS-ANTIGRAVITY-ACP-008
+---
+
+# Google Antigravity ACP agent system design
+
+## Purpose and boundaries
+
+The agent system owns the Antigravity agent's declared identity, discovery
+predicate, launch argument vector, config-root template, skill directories, and
+remote-auth methods. Google's ACP server owns its own credentials, session
+storage, harness subprocess, and model catalogue.
+
+This design does not own ACP transport, process supervision, capability
+probing, MCP wiring, or executor provisioning. It constrains only what Kandev
+declares about this agent and what Kandev may assume about the server.
+
+Six requirement criteria touch those unowned layers and are deliberately
+written as *declare-nothing-new* constraints rather than as new behavior:
+`AC-AGENTS-ANTIGRAVITY-ACP-003.13` on the shared command builder's `cli_flags`
+and sandbox-prefix append, `AC-AGENTS-ANTIGRAVITY-ACP-005.1` and `.2` on the
+advertised authentication methods and their order,
+`AC-AGENTS-ANTIGRAVITY-ACP-005.3` and `.4` on authentication and stderr, and
+`AC-AGENTS-ANTIGRAVITY-ACP-006.1` on capability derivation. Each is satisfied by
+this agent adding no agent-specific override, because the shared layer already
+behaves that way: `CommandBuilder.BuildCommand` appends `cli_flags` for every
+agent with no per-agent opt-out (see [launch surface](#launch-surface)), the
+session-start adapter caches the `initialize` result's `authMethods` and
+re-emits them in that order, `maybeEmitAuthRequired` classifies a `-32000` as
+auth-required whenever auth methods are cached, and the capability path reads
+`agentCapabilities`. Build should assert the absence of an override, not
+re-implement the shared rule. If a future change makes the shared rule
+insufficient for this agent, that is a change to the runtime's design, not this
+one.
+
+**Which layer presents the auth methods.** `AC-AGENTS-ANTIGRAVITY-ACP-005.1` and
+`.2` bind the session-start adapter: it is the layer that caches the advertised
+methods when `initialize` returns and re-emits them, in the server's order, when
+`session/new` answers `-32000`. They do not bind the capability probe: on an
+authenticated run that path does read the advertised methods, but when
+`session/new` answers `-32000` it returns before reaching that step. An
+unauthenticated probe therefore records the auth-required state of
+`AC-AGENTS-ANTIGRAVITY-ACP-005.3` carrying no method list, which is the shared
+behavior for every ACP agent rather than something this one declares. Making the
+probe carry them would change a shared cross-agent path this design does not
+own, so it is out of scope here rather than implied by `.1`.
+
+Values here are either **measured** against build
+`agy_acp_server_20260818_01_RC01`, darwin-arm64, on 2026-09-03, or **registry**
+values read from `antigravity-acp/agent.json` and never executed; each section
+says which. The probed artifact was removed after the session and no transcript
+was retained, so the measured values are a summary of that session and
+re-checking one means re-downloading that build. Only darwin-arm64 was ever
+executed: every Windows and Linux value here is registry-sourced. This is a
+record of an external contract, not a Kandev implementation plan.
+
+## Requirement mapping
+
+| Requirement | Design section |
+| --- | --- |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-001` | [Distribution artifact](#distribution-artifact) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-002` | [Harness resolution](#harness-resolution) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-003` | [Launch surface](#launch-surface) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-004` | [Persistence](#persistence) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-005` | [Security](#security) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-006` | [Data and contracts](#data-and-contracts) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-007` | [Distribution artifact](#distribution-artifact) |
+| `REQ-AGENTS-ANTIGRAVITY-ACP-008` | [Control flow](#control-flow) |
+
+## Components and responsibilities
+
+- **Kandev agent declaration.** Identity, discovery, argument vector, config
+ root, skill directories, remote-auth methods. Static metadata only.
+- **`agy_acp_server.par` / `.exe`.** Google's ACP server. A Mach-O executable
+ wrapping a packed Python archive; the `.par` suffix is part of the filename,
+ not an interpreter hint, and the file is executed directly. Speaks ACP over
+ stdio.
+- **`localharness_external`.** A sibling executable the server drives over gRPC
+ using protobuf messages (`localharness_pb2`). Located by the server itself,
+ never by Kandev.
+
+### Distribution artifact
+
+The ACP registry entry `antigravity-acp/agent.json` publishes a
+`distribution.binary` matrix, read verbatim on 2026-09-03:
+
+| Platform | `cmd` | `args` |
+| --- | --- | --- |
+| `darwin-aarch64` | `./agy_acp_server.par` | none |
+| `linux-x86_64` | `./agy_acp_server.par` | `["--uid="]` |
+| `linux-aarch64` | `./agy_acp_server.par` | `["--uid="]` |
+| `windows-x86_64` | `./agy_acp_server.exe` | none |
+| `windows-aarch64` | `./agy_acp_server.exe` | none |
+
+**The executable name carries its suffix on every platform.** There is no
+`agy_acp_server` without an extension: the archives contain `.par` (darwin,
+linux) and `.exe` (windows), and the registry invokes exactly those. Kandev's
+discovery name and argument vector therefore use the suffixed, platform-specific
+name and require no rename, symlink, or wrapper from the user. An earlier draft
+of this pair used the bare name and would have made the agent undiscoverable on
+every platform.
+
+The darwin-arm64 archive is 314,500,221 bytes,
+`sha256 f122ca7e7030a27f9649da4cf1a7d80e12c48c5f6118ff35affc34d56cbf83dd`. It
+holds exactly two archive-root entries:
+
+| Entry | Uncompressed | Mode |
+| --- | --- | --- |
+| `agy_acp_server.par` | 792,105,680 | `0755` |
+| `localharness_external` | 101,551,680 | `0555` |
+
+An extraction therefore costs roughly 894 MB. The executable is signed
+`Developer ID Application: Google LLC (EQHXZ8M8AV)` with hardened runtime and a
+2026-08-19 timestamp, so it runs on macOS without a quarantine prompt when
+fetched by a non-quarantining downloader.
+
+The archive URL carries a rotating build stamp
+(`agy_acp_server_20260818_01_RC01`) and Google publishes no checksum beside it.
+That, the size, and the fact that Kandev executes `InstallScript()` verbatim as
+remote shell are the three reasons provisioning is excluded from this layer.
+
+`agy` 1.1.25, the separate Antigravity CLI, is not an alternative entry point:
+it exposes no ACP mode in `--help` or `--helpfull`, contains no reference to the
+ACP server, and authenticates into `~/.gemini/antigravity-cli/`.
+
+### Prior-art departure
+
+Kandev's built-in ACP agents that launch a bare binary rather than `npx` are
+Cursor, Kimi, Kiro, Qoder, Trae, Devin, Grok, Hermes and Omp. Each detects one
+PATH entry and stops, and each returns a non-empty `InstallScript()` — Kimi,
+Kiro and Qoder return prose rather than runnable shell, which is the latent form
+of the hazard below, while Cursor, Devin and Hermes return real installers. This
+agent departs on three counts, each forced by measured behavior rather than
+preference.
+
+**Discovery checks a second file.** With the executable present but its harness
+absent, the server still starts and still answers `initialize`, logging only
+`Localharness not found.` to stderr. One-entry detection would therefore report
+a half-installed agent as healthy and lose capability later, inside a prompt.
+The predicate checks the sibling directly so the failure surfaces at discovery.
+See [harness resolution](#harness-resolution) for the search order this mirrors.
+
+**No CLI passthrough.** Every one of those agents also declares a passthrough
+config so the user can drop to the raw CLI under a PTY. This server has no such
+mode: `--helpfull` exposes only `--[no]debug` and `--[no]notices`, there is no
+subcommand or REPL, and the process speaks ACP over stdio and exits on stdin
+EOF. Declaring passthrough would surface a toggle that spawns a process the user
+cannot interact with, so AC-AGENTS-ANTIGRAVITY-ACP-001.9 declines it.
+
+**The install script is empty.** That value runs verbatim as remote shell on the
+SSH, Kubernetes and Sprites executors, so prose in it is a command those
+executors attempt to execute. Because the artifact is also a version-pinned
+314 MB download expanding to roughly 894 MB, the honest value is the empty
+string, and the install guidance moves to `Description()`.
+
+## Data and contracts
+
+`--helpfull` exposes exactly two flags on darwin, `--[no]debug` and
+`--[no]notices`. There is no `--uid`, `--model`, `--acp`, or port flag, which is
+why the argument vector cannot vary with model, permission policy, or resume
+target.
+
+`initialize` returns:
+
+- `agentInfo`: `{name: antigravity-acp, title: Google Antigravity, version:
+ agy_acp_server_20260818_01_RC01}`
+- `agentCapabilities`: `loadSession: true`; `promptCapabilities` `{image, audio,
+ embeddedContext}`; `mcpCapabilities` `{http: true, sse: true}`;
+ `sessionCapabilities` `{list, resume}`; `auth: {logout}`
+- `authMethods`: `oauth-personal`, `oauth-business`, `gemini-api-key`,
+ `agent-platform`
+
+The server echoes the requested `protocolVersion` verbatim and does not clamp:
+a request of `1` returned `1`, and a request of `99` returned `99`. The echoed
+value carries no information about what the server supports, which is why
+capability decisions must read `agentCapabilities` instead.
+
+## Launch surface
+
+One argument vector serves the session command, the runtime command, and the
+one-shot inference command, so no path can drift to a different one:
+
+| Platform | Argument vector | Source |
+| --- | --- | --- |
+| darwin | `agy_acp_server.par` | registry `cmd`; binary measured |
+| linux | `agy_acp_server.par --uid=` | registry `cmd` + `args`, not measured |
+| windows | `agy_acp_server.exe` | registry `cmd`, not measured |
+
+The name is the same one discovery looks up on that platform, which is what
+keeps a successful detection from producing an unlaunchable command. Selection
+is by the platform of the Kandev process building the command — the host, not an
+executor's target. That distinction is real rather than theoretical: discovery
+calls `exec.LookPath` on the host, while the SSH executor ships the built command
+verbatim to the remote, so a darwin host driving a hand-provisioned linux remote
+would send the darwin vector and drop `--uid=`. Nothing here can correct it,
+because command construction receives no per-executor platform, and the
+requirements name the mixed-platform remote as an explicit exclusion.
+
+The rules are also total: every platform yields a defined lookup name and a
+non-empty vector. There is no "build no command" outcome to express — the command
+surfaces return a value, not an error — and an empty vector would not be inert,
+because the SSH preflight skips binary verification when the built command has no
+arguments, turning a clean early failure into an opaque spawn error later.
+Availability is gated by discovery alone.
+
+Nothing else varies the declared vector. `--helpfull` exposes only `--[no]debug`
+and `--[no]notices` on darwin, so there is no model, permission, auto-approve,
+agent-type, or resume flag to vary, and this agent's own construction passes
+neither of the two that exist. The working directory is the task workspace, the
+environment map is empty with nothing stripped, and shutdown needs no forced kill
+because the server exits on stdin EOF.
+
+**Which layer the vector describes.** Everything above describes what this agent
+*constructs*, which is not the whole of what is launched.
+`CommandBuilder.BuildCommand` calls the agent's own `BuildCommand` and then
+appends the profile's configured `cli_flags` to every agent's vector, after which
+`prependCommandPrefix` prepends any sandbox launcher tokens. No agent has an
+opt-out from either, and adding one here would change a contract shared by all of
+them, which [purpose and boundaries](#purpose-and-boundaries) rules out. So
+AC-AGENTS-ANTIGRAVITY-ACP-003.7 binds this agent's own construction only: a user
+who puts `--debug` or `--notices` in their profile's `cli_flags` gets it passed,
+and that is the shared `cli_flags` feature working as designed rather than a
+defect in this agent. The same boundary is why the "exactly" in
+AC-AGENTS-ANTIGRAVITY-ACP-003.1 and `.2`, and the list in `.6`, are claims about
+the declared vector rather than the final argv.
+AC-AGENTS-ANTIGRAVITY-ACP-003.13 states that binding, so a test asserting it
+knows which layer to call.
+
+The linux `--uid=` argument is the one real risk in this table. The darwin build
+exposes no `uid` flag at all, and absl rejects an unknown flag at startup, so if
+the linux build matches darwin rather than the registry the agent fails to start
+there. That argument stays registry-sourced and unverified: the requirements
+exclude Windows and Linux verification from this layer, so confirming it with
+`--helpfull` against the linux archive is follow-up work, not a condition of
+this change.
+
+## Control flow
+
+Kandev spawns one server process per session and speaks ACP over its stdio. The
+server spawns and supervises its own harness subprocess.
+
+Concurrent sessions run independent processes over a shared config root. Kandev
+adds no lock: arbitration inside the config root is the server's own concern.
+
+Closing stdin after `initialize` terminated the process within one second with
+exit code 0, so graceful shutdown needs no forced process kill.
+
+## Failure and recovery
+
+### Harness resolution
+
+This is the design's central hazard. The server resolves its harness like this:
+
+1. If `ANTIGRAVITY_HARNESS_PATH` is already set, return immediately.
+2. Otherwise search `dirname(abspath(sys.argv[0]))`, then
+ `dirname(abspath(sys.executable))`, for `localharness_external` then
+ `localharness` (`.exe` variants on Windows), first match wins.
+3. If nothing matches, log `Localharness not found.` and continue starting.
+
+Kandev accepts a candidate only when it is a regular file. On non-Windows
+hosts, it also requires at least one execute bit. Windows mode bits do not
+control executable access, so the regular-file check applies there.
+
+`abspath` does not resolve symlinks, and step 3 is not fatal. Measured:
+
+| Layout | Result |
+| --- | --- |
+| Real path, harness sibling present | clean start, no warning |
+| Real path, harness removed | `Localharness not found.`, still starts |
+| Symlink from a harness-less PATH directory | `Localharness not found.`, still starts |
+
+In all three cases `initialize` succeeded. A user who symlinks the executable
+onto PATH, or extracts only one of the two files, therefore gets an agent that
+looks healthy at every point Kandev normally checks and loses capability later,
+inside a prompt. Kandev's discovery predicate closes that gap by checking the
+sibling directly, and treats a set `ANTIGRAVITY_HARNESS_PATH` as satisfying it
+because step 1 makes the directory irrelevant in that case.
+
+### Authentication errors
+
+`session/new` before authentication returns JSON-RPC `-32000` `"Authentication
+required"`, with a message enumerating the settings key and the environment
+variables each method needs. The server also writes a Python traceback to stderr
+for that handled error, so stderr traceback text is not evidence of a failed
+launch when a well-formed JSON-RPC error arrived on stdout.
+
+`authenticate` with `gemini-api-key` and no `GEMINI_API_KEY` in the launch
+environment returns `-32602` and does not authenticate.
+
+## Persistence
+
+`` is `$GEMINI_HOME` when set and non-empty, otherwise `~/.gemini`.
+Startup logged `Gemini home resolved to /Users/henry/.gemini (default;
+$GEMINI_HOME is unset)`; setting `GEMINI_HOME=/tmp/gemhome` moved both the
+resolved home and the settings path. Nothing is written to the config root
+before authentication succeeds.
+
+| Path | Contents |
+| --- | --- |
+| `/antigravity-acp/settings.json` | `auth.type`, `gcp.project`, `gcp.location` |
+| `/antigravity-acp/acp_token.json` | `oauth-personal` token |
+| `/antigravity-acp/acp_business_token.json` | `oauth-business` token |
+| `/antigravity-acp/trusted_workspaces.json` | trusted workspace roots |
+| `/antigravity-acp/brain/` | harness per-conversation artifacts |
+| `/antigravity-acp/conversations/` | session trajectories |
+
+Session lookup is scoped to the resolved home: an unknown identifier reports
+`Session not found in the current GEMINI_HOME`. Resume therefore depends on the
+config root being identical across relaunches, which is what makes per-task
+`GEMINI_HOME` isolation incompatible with resume.
+
+**Declared template versus resolved root.** Kandev declares one static
+`SessionDirTemplate`, `{home}/.gemini/antigravity-acp`. That string is not
+expanded against a home directory at launch. Its single production consumer is
+the container bind-mount source, where `SessionDirHostPath` strips the `{home}/`
+prefix and rebases the remainder onto `/agent-sessions//`
+— a kandev-managed directory deliberately outside every home, so host state DBs
+and session caches stay out of the container. That mount is created only when the
+agent also declares a `SessionDirTarget`, and per
+AC-AGENTS-ANTIGRAVITY-ACP-004.10 this agent declares none. For this agent the
+template is therefore a *statement about where the server keeps its config root*
+with no runtime effect today, not a path Kandev resolves.
+
+As a statement it is accurate exactly when `GEMINI_HOME` is unset or empty; it
+diverges when the environment exports a non-empty one, because the runtime
+declaration inherits the environment unmodified and so the server sees it. That
+divergence is not repairable here: the field is a static string on every agent,
+consumed by container and remote paths that resolve it against a target the
+local environment does not describe, so deriving it from a local variable would
+be wrong for exactly the executors that consume it. Kandev declares the default
+root, never reads `GEMINI_HOME`, and the requirements name any non-default
+`GEMINI_HOME` as an explicit exclusion rather than pretending the template still
+describes it. Remote credential upload has the same shape: it resolves sources
+against `os.UserHomeDir()`, so it too addresses the default root only.
+
+Skill resolution appends `/.agents/skills/` and `/.gemini/skills` for
+the project, and `/config/skills` plus `/antigravity-cli/skills`
+globally. Kandev declares the first of each pair — `.agents/skills` and
+`.gemini/config/skills` — matching the single project and user skill directory
+its injection contract supports. The server searches both entries of each pair,
+so taking the first is a determinism choice rather than a capability one; it
+also keeps Kandev out of `antigravity-cli/`, the directory the `agy` CLI owns
+and the requirements isolate. Because that global pair resolves against ``,
+the declared user skill directory carries the same `GEMINI_HOME` divergence as
+the session template: under a non-default `GEMINI_HOME` Kandev would inject user
+skills where the server does not look, which is why the requirements name it in
+that exclusion alongside session-dir seeding and the remote-auth copy.
+
+Note that `/antigravity-acp` nests inside `~/.gemini`, which is already
+the Gemini agent's session directory template. The two remain distinct values;
+neither is aliased to the other.
+
+## Security
+
+Credentials are written and read by the ACP server alone. Kandev copies token
+files for remote executors and sets environment variables, and otherwise does
+not create, modify, or delete anything under the config root.
+
+API keys cannot be supplied over the ACP wire: `gemini-api-key` reads
+`GEMINI_API_KEY` and `agent-platform` reads `GOOGLE_API_KEY` or
+`GOOGLE_CLOUD_PROJECT`/`GOOGLE_CLOUD_LOCATION` with Application Default
+Credentials, all from the launch environment. Remote auth therefore needs both a
+file-copy method and environment-variable methods.
+
+Which token file exists depends on the method the user chose, so a file-copy
+method must tolerate absent entries rather than requiring all of them.
+
+## Observability
+
+The server logs to stderr in absl format at INFO by default. The two lines worth
+recognising are `Gemini home resolved to ` at startup, which reports the
+effective config root, and `Localharness not found.`, which is the only signal
+of the partial-install state described above.
+
+`--debug` raises verbosity. Kandev does not pass it; a user debugging an install
+runs the binary directly.
+
+## Deferred work
+
+The requirements' `## Out of scope` names these exclusions; this is what a
+follow-up would have to decide.
+
+**Provisioning the artifact.** A five-platform URL matrix, where the cache lives,
+how integrity is verified given Google publishes no checksum beside the archive,
+how a rotating build-stamped URL is refreshed, and whether a ~894 MB extraction
+is per-host or shared. Until that exists no image ships the binary, so the
+containerized, Kubernetes, SSH and Sprites executors report it not found.
+
+**A variable config root.** Supporting a non-default `GEMINI_HOME` means making
+the declared template environment-derived, which the static `SessionDirTemplate`
+contract shared by every agent does not allow today. Setting it per task or
+session would additionally isolate credentials, forcing a fresh login per task,
+and would break the resume property REQ-AGENTS-ANTIGRAVITY-ACP-004 depends on.
+
+**Multi-variable remote auth.** `agent-platform` also resolves via
+`GOOGLE_CLOUD_PROJECT` plus `GOOGLE_CLOUD_LOCATION` with Application Default
+Credentials. A remote-auth method carries a single `EnvVar`, so that combination
+cannot be expressed and an ADC-only remote host has no auth path until the method
+shape grows.
+
+## Related decisions
+
+None. This design records an external vendor contract and introduces no new
+Kandev architecture boundary.