# AGENTS.md

Guidance for coding agents working in this repo. README.md is the CLI/TUI user guide; API.md is the Go library reference; SANDBOX.md covers sandboxing; WORKFLOWS.md is the swarm/workflow guide, with format-2 details in docs/swarm-state-model.md. Consult and update them as noted below.

## Overview

`polly` is an LLM harness: CLI + TUI (`cmd/polly`) built on a Go library (`llm`, `tools`, `messages`, `schema`, `sessions`, `skills`, `subagent`, `swarm`, `workflow`, `worktree`, `artifacts`). Module: `github.com/alexschlessinger/pollytool`, Go 1.27.

Request flow: `main` → provider router (`llm.NewMultiPass`) → `llm.NewAgent` → agent loop (each iteration `llm.Prepare` adapts the request to the model's capabilities, then `ChatCompletionStream` routes on the `provider/` prefix; each provider package under `llm/<provider>/` builds the wire request, converts messages, parses stream events through its adapter, and fetches its own model catalog and embeddings; they share the request contract in `llm/internal/contract`, which the root `llm` package re-exports, and the catalog helpers in `llm/internal/catalog`) → tool calls run in parallel via `tools.ToolRegistry` → results fed back until done or `ErrMaxIterations`.

Key types: `llm.LLM`/`Agent`/`AgentCallbacks`, `messages.ChatMessage`/`StreamEvent`, `tools.Tool`/`ToolRegistry`/`ToolError`, `schema.ToolSchema`, `sessions.Store`, `subagent.Runner`.

## Build & test

```bash
CGO_ENABLED=0 go build ./...     # no cgo: sqlite is modernc.org/sqlite
go vet ./...
go test ./...                    # tests are *_test.go colocated with code
```

CI (`.github/workflows/test.yml`) runs build + vet + tests on Linux/macOS, cross-compiles 5 GOOS/GOARCH targets including Windows, and runs `-race` on the packages listed in `.github/ci.sh`. Windows runtime tests are disabled. `.github/ci.sh [test|race|cross|all]` is the shared entry point; see [local CI](.github/local-ci/README.md) for portable Linux containers, disposable macOS VMs, and GitHub runner setup. Verify all three commands above before declaring done.

- Sandbox security tests are opt-in: `POLLYTOOL_REQUIRE_SANDBOX_TESTS=1 go test ./tools/sandbox` (macOS/Linux only).
- POSIX-only tests skip via a per-package `skipIfWindows(t)` helper (see `tools/skip_test.go`), not build tags. Use it for anything needing POSIX shell/sandboxing so the Windows CI leg passes.
- `POLLYTOOL_*` env vars configure everything (`POLLYTOOL_ANTHROPICKEY`, `MODEL`, `SANDBOX`, etc.); test-only ones: `POLLYTOOL_REQUIRE_SANDBOX_TESTS`, `POLLYTOOL_CLIPBOARD_TEST`.

## Conventions

- Commits: `area: lowercase imperative summary`, no period. Areas match package/topic: `llm`, `tools`, `sessions`, `mcp`, `repl`, `tests`, `skills`, `sandbox`, …
- Packages are flat top-level domains; one concept per file; stdlib `testing` with `t.Fatalf`, `t.TempDir()`, plain assertions.
- Errors: `fmt.Errorf` with `%w`; tool failures return `*tools.ToolError` (structured, serialized as JSON).
- Functional options for registries/clients (`With*`).
- Platform-split files (`*_darwin.go`, `*_windows.go`, `*_unix.go`, `*_other.go`) — check whether a change belongs in all variants.

## Adding things

- **Provider**: package `llm/<provider>/` exporting `NewProvider`, `ListModels` (built on `llm/internal/catalog`), and optionally `Embed` and `DefaultBaseURL`; one row in `defaultProviders()` (`llm/multipass.go`) wires them and carries every routing rule as a `providerSpec` field (base URL scoping, keyless access, host routing, catalog shape). Nothing else in `llm` names a provider. Env key `POLLYTOOL_<PROVIDER>KEY` via `getEnvVarNameForProvider`. Update API.md §Providers and README §Models.
- **Builtin tool**: implement `tools.Tool` in `tools/<name>.go` (or a declarative `tools.Func`); register in `NewToolRegistry` (`tools/registry.go`) or via `RegisterNative`. Rich output → `OutputTool`; long-running exemption → `UntimedTool`. If it spawns processes it must go through the sandbox factory; if it touches paths it must policy-check like the builtin file tools. Update README §Built-in tools.
- **Sandbox preset/config**: update SANDBOX.md (see its "How policies merge" section).

## Sandbox rules

Sandboxing is default-on for bash, shell tools, and stdio MCP servers (bubblewrap on Linux, Seatbelt on macOS); it fails closed, tool metadata cannot opt out, and credential paths are denied by default. Never add a code path that runs a child process outside the sandbox factory, and never weaken deny-list behavior without updating SANDBOX.md.

## Gotchas

- Do not commit the `polly` binary (34 MB, gitignored at repo root), the `textfx` experiment binary (`go build ./experiments/...` drops it at the repo root; gitignored there and under `experiments/textfx/`), or runtime data.
- `.gopath/` (~1.4 GB) is a local GOPATH cache, gitignored — not repo source. `.claude/worktrees/`, `.polly-shots/`, `.zvec-grep/`, `.tmux-tmp/` are scratch; work from the repo root or greps will hit duplicate trees. Agent-facing skills live in `.agents/skills/`.
- `skills.Message` is deliberately field-order-compatible with `messages.ChatMessage` to break an import cycle — do not reorder fields.
- Tool wrappers must preserve media output: use `NamespacedTool.ExecuteOutput` when wrapping; don't drop `OutputTool` semantics.
- Provider quirks are intentional: reasoning models reject `temperature`; OpenAI reasoning items are model-locked and dropped on model switch; Anthropic has a legacy vs adaptive thinking split (`legacyThinkingPrefixes`).
