Metadata-Version: 2.5
Name: andon-ai
Version: 0.7.4
Summary: Andon SDK and CLI for authoring, deploying, and running workflows
Requires-Python: >=3.13
Requires-Dist: httpx>=0.28
Requires-Dist: keyring>=25.6
Requires-Dist: packaging>=24.0
Requires-Dist: pydantic>=2.0
Requires-Dist: typing-extensions>=4.15
Description-Content-Type: text/markdown

# Andon SDK and CLI (`andon-ai`)

Andon is a Python SDK for building durable, document-centric workflows with
typed steps, LLM agents, human review, and integrations. Authors define the
workflow; the Andon platform handles execution, checkpointing, files, and
operations.

Installing `andon-ai` provides the `andon` command and the `andon_dsl` Python
package. Python 3.13 or newer and [uv](https://docs.astral.sh/uv/) are required.

## Create a workspace

Start in an empty directory:

```bash
mkdir claims-workflow
cd claims-workflow
uvx andon-ai init
uv sync
```

`andon init` creates `andon.toml`, a deployable `andon/` package, a sample
workflow and test, and local project configuration. It preserves files that
already exist, so an empty directory is the supported starting point.

The generated `AGENTS.md` routes coding agents to the authoring contract and
tool catalog that match the installed SDK.

## Build an email workflow

This workflow reads unread Gmail messages, summarizes them with an agent, and
emails the digest. Save it as `andon/workflows/inbox.py`:

```python
from dataclasses import dataclass

from andon_dsl.agents import Agent, StepContext
from andon_dsl.integrations import ConnectionRef, EmailFilter, EmailMessage, Gmail
from andon_dsl.workflows import map, step, workflow


@dataclass
class InboxInput:
    recipient: str
    limit: int = 10


@dataclass
class EmailSummary:
    sender: str
    subject: str
    summary: str


GMAIL = ConnectionRef(Gmail, "gmail")


summarizer = Agent(
    model_family="small",
    input_type=EmailMessage,
    output_type=EmailSummary,
    system_instructions="Summarize inbound email clearly and concisely.",
)


@step(connections=[GMAIL])
async def read_inbox(ctx: StepContext, input: InboxInput) -> list[EmailMessage]:
    gmail = await ctx.connect(GMAIL)
    return await gmail.list_messages(
        filter=EmailFilter(is_unread=True),
        limit=input.limit,
    )


@step
async def summarize(message: EmailMessage) -> EmailSummary:
    return await summarizer(
        "Summarize this email from {{sender}}.\n"
        "Subject: {{subject}}\n\n"
        "{{body_text}}",
        inputs=message,
    )


@step(connections=[GMAIL])
async def send_digest(
    ctx: StepContext,
    recipient: str,
    summaries: list[EmailSummary],
) -> str:
    gmail = await ctx.connect(GMAIL)
    body = "\n\n".join(
        f"{item.subject} — {item.sender}\n{item.summary}" for item in summaries
    )
    return await gmail.send(
        to=[recipient],
        subject="Andon inbox digest",
        body=body or "No unread messages.",
    )


@workflow()
def process_inbox(input: InboxInput) -> str:
    messages = read_inbox(input)
    summaries = map(summarize, messages)
    return send_digest(input.recipient, summaries)
```

Declare the workflow in `andon.toml`:

```toml
[[workflows]]
name = "process_inbox"
path = "andon/workflows/inbox.py"
```

`ConnectionRef` pairs the expected integration protocol with the configured
organization connection name. Reuse the same ref in the decorator grant and
`ctx.connect(...)` so the name and return type have one source of truth.

## Core concepts

- **Workflows** are declarative graphs of steps and control-flow primitives.
  Their bodies are traced during deployment and do not run as ordinary Python
  during workflow execution.
- **Steps** are typed Python functions and the durability boundary. Successful
  results are checkpointed; external side effects should be safe to repeat if
  an interrupted attempt runs again.
- **Agents** are typed LLM-powered components declared at module scope and
  awaited inside steps. Runtime prompts are Handlebars templates over the
  agent's typed inputs.
- **Tools and toolsets** give agents explicitly selected capabilities. Andon
  provides platform tools and curated toolsets, and authors can define their
  own model-callable functions with `@tool`.
- **Connections** provide typed access to organization-configured email through
  the Gmail protocol without exposing credentials to workflow code.
- **FileRef** values represent uploaded files and generated artifacts. Keep
  documents and large intermediate outputs behind `FileRef` rather than
  passing their bytes or full text through step results.

Workflow bodies use primitives such as `map`, `parallel`, `branch`, `loop`,
`wait_for_event`, `wait_for_review`, `sleep`, and `run_workflow`. Put ordinary
Python branching, iteration, parsing, and integration glue inside steps.

## Tools and toolsets

Platform tools and curated toolsets are imported from `andon_dsl.tools` and
opted into an agent through `tools=[...]`. Authors can combine them with their
own `@tool` functions:

```python
from andon_dsl.agents import Agent
from andon_dsl.tools import tool
from andon_dsl.tools.toolsets import document_analysis


@tool
def normalize_vendor_name(name: str) -> str:
    """Return a normalized vendor name for matching."""
    return " ".join(name.lower().split())


analyst = Agent(
    tools=[*document_analysis.tools, normalize_vendor_name],
)
```

Inspect the installed SDK signatures and toolsets without authentication:

```bash
uv run andon tools --offline         # toolsets, signatures, and descriptions
uv run andon tools --offline --json  # the same catalog as structured JSON
```

## Authenticate, synchronize, deploy, and run

Authenticate in the browser. You confirm the organization before Andon creates
a 90-day installation credential. The credential lives in the operating
system's secure store; only nonsecret profile metadata is written to disk:

```bash
andon auth login
andon auth status
andon tools         # organization settings, provider options, and prices
andon tools --json  # the same live catalog as structured JSON
```

`andon auth login --with-api-key` imports a key created in Settings from a
no-echo prompt or stdin. `auth list`, `auth use`, and `auth logout` manage
organization-bound profiles. Browser credentials are revoked on logout by
default. `auth status` shows stored device and expiry metadata only when the
selected profile supplies the authenticated credential.

`ANDON_API_KEY` remains the noninteractive headless and CI override. The CLI uses
`https://app.andonai.com/` by default; `--profile` selects an organization and
`--api-url` targets another environment without sending a stored credential to
a different origin. `--no-input` and `ANDON_NON_INTERACTIVE=1` prohibit prompts
and browser launches; JSON output also implies no-input unless `--input` is
explicit.

Pull an existing workspace, or keep the unbound workspace created by `init`:

```bash
uv run andon pull
uv run andon status
```

A first pull onto a local draft merges the two trees. When the same file
differs on both sides, keep the version you want, then review
`andon workspace reconcile --dry-run` and apply its `--plan` command. This keeps
your conflict resolutions, applies nonconflicting incoming changes, and records
the remote head as your baseline.

```bash
uv run andon validate
uv run pytest
uv run andon push --message "Save review changes"
uv run andon deploy
uv run andon deployments activate <deployment-id> --dry-run
uv run andon deployments activate <deployment-id> --plan <plan-id>
uv run andon run process_inbox \
  --deployment-id <deployment-id> \
  --input-json '{"recipient":"ops@example.com","limit":10}'
uv run andon runs watch <run-id>
uv run andon runs show <run-id>
uv run andon runs show <run-id> <invocation-id>
uv run andon runs download <run-id> <invocation-id> --value input
uv run andon files download andon://runs/<run-id>/send_digest/step-output/digest.md
```

`andon validate` performs local manifest and static source validation.
`andon pull` and `andon push` track the remote workspace head in ignored
`.andon/` state. `andon deploy` safely saves changed local source, compiles it,
and creates an inactive deployment. Activation is explicit. Destructive
operations use a content-addressed dry-run plan; applying `--plan` verifies the
same local snapshot, identity, workspace head, and active deployment that were
reviewed. Use the exact `apply_command` returned by the preview so every
operation-shaping argument is repeated safely.

Use `andon workspace history`, `workspace show`, and `workspace diff` to inspect
saved source. A historical pull is detached. `workspace restore --revision` or
`--deployment` appends the selected tree as a new head; it does not rewrite
history. `pull --discard-local`, `push --replace-remote`, reconciliation,
restore, and activation require an exact reviewed plan in no-input mode.

If an inspectable domain mutation response is lost, the CLI reports
`ambiguous_outcome` and exact inspection commands. It never automatically
resubmits the mutation. Inspect the current workspace, deployment, run, or
event state and create a fresh plan before another write. Input-file upload
response loss reports an ordinary retryable `network_error` because the run
does not exist yet.

`andon run` starts a deployed workflow. Local paths supplied at typed
`FileRef` input positions are uploaded before run creation; `andon files
upload` returns a reusable ref instead. `andon runs show <run-id>` summarizes
step status and errors; add its invocation ID to inspect recorded input/output,
error and artifact refs, or page through map/loop children. Inspection shows
which complete values are available to download. `andon runs download <run-id>
<invocation-id> [destination]` saves the complete recorded output as JSON; add
`--value input` for the input. `andon files download` saves any `andon://` output
locally. Both verify the server's digest and refuse to replace an existing file
without `--overwrite`.

Use `-` as the destination for verified raw bytes on stdout, with plain-text
diagnostics on stderr; for example, `andon runs download <run-id> <invocation-id> - | jq '.jobs | length'`.
This raw output applies even with `--json`.

## Authoring reference

Use the references bundled with the installed SDK before editing a workspace:

```bash
uv run andon docs
uv run andon tools --offline
```

Here `uv run` executes a command in the workspace environment, while
`andon docs` prints the complete, version-matched workspace authoring,
synchronization, and recovery contract to the terminal. It is separate from
`andon run <workflow>`, which starts a workflow run.

The authoring contract covers workflow restrictions, primitives, durable
identity, retries, agent settings, files, reference data, testing, and other
sharp edges. The generated `andon/AGENTS.md` points coding agents to this
contract and the installed tool catalog.

## Public imports

| Package | Purpose |
|---|---|
| `andon_dsl.workflows` | Workflow and step decorators plus control-flow primitives. |
| `andon_dsl.agents` | Agent declarations, prompt content, contexts, model settings, and usage limits. |
| `andon_dsl.tools` | User-authored tools and platform tool stubs; curated bundles live in `andon_dsl.tools.toolsets`. |
| `andon_dsl.resources` | `FileRef`, document result types, reference data helpers, and schema extensions. |
| `andon_dsl.integrations` | Typed `ConnectionRef` bindings, provider protocols, and shared integration types. |
| `andon_dsl.errors` | Public authoring and execution error types. |
