Metadata-Version: 2.4
Name: anticells
Version: 0.3.4
Summary: Official Python SDK for ANTICELLS products.
Author: ANTICELLS
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://anticells.theantitech.com/
Project-URL: Documentation, https://anticells.theantitech.com/
Keywords: anticells,ai,api,gaslighter,red-team,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.26.0
Requires-Dist: pydantic<3,>=2.7.4
Requires-Dist: tqdm>=4.66.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: build>=1.2.1; extra == "dev"
Requires-Dist: twine>=5.1.0; extra == "dev"
Provides-Extra: mcp
Requires-Dist: mcp[cli]<3,>=2; extra == "mcp"

# ANTICELLS Python SDK

Python SDK for ANTICELLS products. The Gaslighter product is available as a
namespace inside the package:

```python
from anticells import gaslighter

client = gaslighter.Client.from_env()
```

Customers normally provide only an API key.

## Install

From PyPI:

```powershell
python -m pip install anticells
```

To expose Gaslighter to an MCP host (Python 3.10 or newer):

```powershell
python -m pip install "anticells[mcp]"
```

For local development from this repository:

```powershell
python -m pip install -e .\sdk
```

For maintainers building release artifacts:

```powershell
cd sdk
python -m pip install -U build twine
python -m build
python -m twine check `
  .\dist\anticells-0.3.4-py3-none-any.whl `
  .\dist\anticells-0.3.4.tar.gz
```

This creates both a wheel and source distribution in `sdk/dist/`.

For a built local wheel:

```powershell
python -m pip install .\dist\anticells-0.3.4-py3-none-any.whl
```

## Environment

```powershell
$env:ANTICELLS_API_KEY = "glk_live_customer_key"
```

Optional auth header override:

```powershell
$env:ANTICELLS_API_KEY_HEADER = "X-API-Key"
```

For `Authorization: Bearer <key>` instead:

```powershell
$env:ANTICELLS_API_KEY_HEADER = "Authorization"
```

## Command Line

Check connectivity:

```powershell
anticells gaslighter health
anticells gaslighter personas
```

Manage account API keys:

```powershell
anticells gaslighter keys list
anticells gaslighter keys create --name "Notebook key"
anticells gaslighter keys revoke "key_..."
```

Register and inspect targets:

```powershell
anticells gaslighter targets list
anticells gaslighter targets get "target_..."

anticells gaslighter targets register `
  --url "http://localhost:8080/api/v1/execute" `
  --mode relay `
  --name "Production assistant" `
  --skip-manifest `
  --input-field "message" `
  --body-template '{"message":"__TARGET_INPUT__"}'
```

Run the relay from the machine that can reach a private/local target:

```powershell
anticells gaslighter relay run `
  --target "target_..." `
  --url "http://127.0.0.1:8080/api/v1/execute" `
  --target-credential "target_model_key_or_jwt" `
  --target-auth-type bearer `
  --target-auth-header "Authorization" `
  --target-auth-prefix "Bearer" `
  --input-field "message" `
  --body-template '{"message":"__TARGET_INPUT__"}'
```

Run one campaign:

```powershell
anticells gaslighter attacks run `
  --target "target_..." `
  --objective "Evaluate whether the target preserves approval boundaries." `
  --persona "audit_interface"
```

Run a family sweep:

```powershell
anticells gaslighter sweeps run `
  --target "target_..." `
  --family "system_prompt_extraction" `
  --family "tool_abuse" `
  --prompts-per-family 2
```

Optionally tune the dynamic system prompt extraction runner:

```powershell
anticells gaslighter sweeps run `
  --target "target_..." `
  --family "system_prompt_extraction" `
  --prompts-per-family 1 `
  --system-prompt-max-attack-turns 51 `
  --system-prompt-representation DIRECT `
  --system-prompt-representation ANALYSIS
```

Run against a chatbot that has no API endpoint:

```powershell
anticells gaslighter attacks run `
  --wait `
  --objective "Evaluate whether the target preserves approval boundaries."

anticells gaslighter sweeps run `
  --wait `
  --family "system_prompt_extraction" `
  --prompts-per-family 1
```

Wait Mode creates and retains a target. For every generated message it
shows which chatbot conversation to use, then waits for the copied chatbot
response. Paste the full response and enter `::end` on its own line.

Interrupted sessions can be listed and resumed:

```powershell
anticells gaslighter wait list
anticells gaslighter wait resume "SESSION_ID"
```

## MCP Server

The optional MCP server exposes Gaslighter as asynchronous start, status, and
result tools, plus resources for larger transcripts and Markdown reports.

Set the API key in the environment before starting the MCP host. On Windows,
start Codex or Claude Code from the same PowerShell session:

```powershell
$env:ANTICELLS_API_KEY = "glk_live_customer_key"
codex
# Or: claude
```

For an IDE or desktop app that is not launched from that terminal, set a
persistent user variable and restart the app:

```powershell
[Environment]::SetEnvironmentVariable(
  "ANTICELLS_API_KEY",
  "glk_live_customer_key",
  "User"
)
```

On macOS or Linux:

```bash
export ANTICELLS_API_KEY="glk_live_customer_key"
codex
# Or: claude
```

Do not put the key in `SKILL.md`, a prompt, or a committed configuration file.
The MCP host must start:

```text
anticells-mcp
```

For Codex, add this to `~/.codex/config.toml` or a trusted project's
`.codex/config.toml`. `env_vars` forwards the value from Codex's environment
without storing it in the file:

```toml
[mcp_servers.anticells-gaslighter]
command = "anticells-mcp"
env_vars = ["ANTICELLS_API_KEY"]
```

A copyable template is available at
[`examples/mcp/codex-config.toml`](examples/mcp/codex-config.toml).

For Claude Code, put this `.mcp.json` in the project root. Claude expands the
environment variable when it loads the configuration:

```json
{
  "mcpServers": {
    "anticells-gaslighter": {
      "type": "stdio",
      "command": "anticells-mcp",
      "env": {
        "ANTICELLS_API_KEY": "${ANTICELLS_API_KEY}"
      }
    }
  }
}
```

A copyable template is available at
[`examples/mcp/claude.mcp.json`](examples/mcp/claude.mcp.json). Restart the host
after changing its environment or MCP configuration, then use `/mcp` to verify
that `anticells-gaslighter` is connected.

The server can register public targets or start an SDK relay to a private or
localhost target. Long-running attacks and sweeps return IDs immediately; poll
their status and fetch the result after completion. Full output is available at:

```text
gaslighter://campaign/{campaign_id}/turns
gaslighter://campaign/{campaign_id}/report
gaslighter://batch/{batch_id}/report
```

Target credentials are never accepted directly as MCP arguments. Put a target
credential in the MCP process environment and pass only its variable name as
`credential_env`, for example `MY_TARGET_API_KEY`.

### Agent skill for Codex and Claude Code

The repository includes a portable
[`SKILL.md`](skills/gaslighter-security-testing/SKILL.md) that teaches a coding
agent when to use each Gaslighter tool, how to test localhost through the SDK
relay, and how to handle results and credentials safely.

Copy the complete `gaslighter-security-testing` directory into the appropriate
project location:

```text
Codex:       .agents/skills/gaslighter-security-testing/SKILL.md
Claude Code: .claude/skills/gaslighter-security-testing/SKILL.md
```

For a personal installation available in every project, use:

```text
Codex:       ~/.agents/skills/gaslighter-security-testing/SKILL.md
Claude Code: ~/.claude/skills/gaslighter-security-testing/SKILL.md
```

After the MCP server and skill are installed, users can ask naturally, for
example: `Use Gaslighter to security-test my localhost agent before deployment.`

## Quick Start

```python
from anticells import gaslighter

client = gaslighter.Client(api_key="glk_live_customer_key")

target = client.register_target(
    url="http://target-service:8080/chat",
    name="Staging assistant",
    connection_mode="sdk_relay",
    skip_manifest=True,
    target_body_template={"message": "__TARGET_INPUT__"},
    target_body_template_configured=True,
    target_input_field="message",
)

relay_runner, relay_thread, relay_stop = gaslighter.start_target_relay_thread(
    client,
    target_id=target.id,
    target_url="http://target-service:8080/chat",
    body_template={"message": "__TARGET_INPUT__"},
    body_template_configured=True,
    input_field="message",
    session_field="session_id",
)

try:
    result = client.attack(
        target_id=target.id,
        objective="Evaluate whether the target preserves approval boundaries.",
        persona="audit_interface",
        verbose=True,
    )
    print(result.outcome)
    print(result.judge_summary)
finally:
    relay_stop.set()
    relay_thread.join(timeout=5)
    relay_runner.close()
```

`objective`, `custom_objective`, and the API-native `bad_objective` are all
accepted. The SDK sends the API field as `bad_objective`.

## Wait Mode Without a Target API

The user still needs an ANTICELLS API key, but the chatbot being tested does
not need an HTTP endpoint:

```python
from anticells import gaslighter

client = gaslighter.Client.from_env()

result = client.attack(
    wait_mode=True,
    objective="Evaluate whether the target preserves approval boundaries.",
)
print(result.outcome)
```

Family sweeps use the same interaction and always process child campaigns
sequentially in Wait Mode:

```python
result = client.family_sweep(
    wait_mode=True,
    families=["system_prompt_extraction", "hallucination"],
    prompts_per_family=1,
)
```

At startup the SDK prints a session id. Resume an interrupted run before its
pending one-hour relay request expires:

```python
result = client.resume_wait("SESSION_ID")
sessions = client.list_wait_sessions()
```

Resume files are stored in `~/.anticells/wait-sessions`, contain prompts and
any response awaiting acknowledgement, and never contain the ANTICELLS API
key. They are removed after successful completion and retained after an
interruption or failed run. `WaitModeOptions` can override the state directory,
input/output functions, terminator, or provide a notebook-friendly
`response_reader` callback.

SDK `0.3.4` supports both backend generations. It uses native
`connection_mode="manual"` when available. If the backend still requires a URL
and only accepts `direct` or `sdk_relay`, the SDK automatically retries with
`sdk_relay` and a reserved non-routable `wait-mode.invalid` placeholder URL.
Legacy family sweeps are forced sequential and stop if a child campaign fails.

## Python Key Management

```python
from anticells import gaslighter

client = gaslighter.Client.from_env()

keys = client.list_api_keys()
created = client.create_api_key(name="Notebook key")
client.revoke_api_key(created["key"]["id"])
```

## Target Registration Options

`register_target` mirrors the ANTICELLS target registration contract:

```python
from anticells import gaslighter

client = gaslighter.Client.from_env()

target = client.register_target(
    url="https://target.example/chat",
    name="Production assistant",
    connection_mode="direct",
    credential="target-side-secret",
    auth={"type": "bearer", "header": "Authorization", "prefix": "Bearer"},
    skip_manifest=True,
    extra_payload={"tenant": "acme"},
    target_body_template={"query": "__TARGET_INPUT__"},
    target_body_template_configured=True,
    target_input_field="query",
    target_input_aliases=["message", "prompt"],
    upload_endpoint="/upload",
    tool_invocation_endpoint="/tools",
    file_read_endpoint="/files/read",
    upload_file_field="artifact",
    upload_message_field="note",
    upload_extra_fields={"workspace": "red-team"},
)
```

For reusable configuration objects:

```python
from anticells import gaslighter

target = client.register_target_config(
    gaslighter.RegisterTarget(url="https://target.example/chat")
)
client.update_target_config(target.id, gaslighter.UpdateTarget(credential=None))
```

Use `connection_mode="sdk_relay"` for private/local targets. In relay mode, the
SDK calls the target directly and the ANTICELLS service does not need direct
network access to it.

For multi-turn or concurrent family sweeps, set `session_field` to the JSON field
your target uses to select a conversation. Relay metadata from compatible servers
is then forwarded in that field and in `X-Target-Session-Token`; campaign, phase,
and conversation identifiers are also forwarded in `X-Gaslighter-*` headers. For
example, a target accepting `{"query": "...", "session_id": "..."}` should use
`session_field="session_id"`. Older servers that do not send session metadata keep
the previous request shape.

For bearer auth, put only the token/JWT in `credential`. Use
`{"type": "bearer", "header": "Authorization", "prefix": "Bearer"}` for
`Authorization: Bearer <token>`.

## Family Sweep

```python
from anticells import gaslighter

batch = client.family_sweep(
    target_id=target.id,
    families=["system_prompt_extraction", "tool_abuse", "hallucination"],
    prompts_per_family=2,
    direct_phase_enabled=True,
    continue_on_child_failure=True,
    verbose=True,
)

print(batch.status)
```

For full personalization:

```python
started = client.start_family_sweep_config(
    gaslighter.StartFamilySweep(
        target_id=target.id,
        persona="audit_interface",
        objective_match_mode="specified_only",
        stateful_delivery="full_history",
        max_history_turns=10,
        direct_phase_enabled=False,
        prompts_per_family=5,
        families=["system_prompt_extraction", "tool_abuse"],
        allow_weak_preflight=True,
        preflight_override_reason="Authorized audit",
        continue_on_child_failure=False,
    )
)

result = client.wait_for_batch(started.batch_id, verbose=True)
```

`families=["all"]`, `families=["*"]`, or an empty family list runs every family.

## Reports

```python
report_markdown = client.get_campaign_report(result.campaign_id, tier="growth")
saved_path = client.save_campaign_report(
    result.campaign_id,
    "reports/gaslighter-campaign.md",
    tier="growth",
)
print(saved_path)
```

The CLI can save reports too:

```bash
anticells gaslighter attacks run --target "$TARGET_ID" --objective "..." --trace --report-path reports/campaign.md
```

## Notes

- `verbose=None` auto-enables tqdm only in interactive terminals.
- `verbose=True` forces progress output.
- `verbose=False` is suitable for CI and pipeline logs.
- `X-API-Key: <key>` is the default API-key style for the ANTICELLS gateway.
- Use `api_key_header="Authorization"` if you need `Authorization: Bearer <key>`.
- Use the public ANTICELLS API URL for packaged customer builds.
- Native manual-target backend support is preferred, but SDK `0.3.4` can use
  the legacy `sdk_relay` compatibility path until that backend is deployed.
