From 86260d2ae77cefe89335ed7e428a30645ebb2895 Mon Sep 17 00:00:00 2001 From: aaron Date: Tue, 20 Oct 2026 22:49:43 -0700 Subject: [PATCH 1/2] Stop tracking internal notes and personal tooling .claude/, CLAUDE.md, BACKLOG.md and DOCUMENTATION.md stay on disk but are ignored, ahead of open-sourcing the repo. Co-Authored-By: Claude Sonnet 5.5 --- .claude/settings.json | 33 --- .claude/skills/release/SKILL.md | 94 ------- .gitignore | 6 + BACKLOG.md | 188 -------------- CLAUDE.md | 83 ------- DOCUMENTATION.md | 425 -------------------------------- 6 files changed, 6 insertions(+), 823 deletions(-) delete mode 100644 .claude/settings.json delete mode 100644 .claude/skills/release/SKILL.md delete mode 100644 BACKLOG.md delete mode 100644 CLAUDE.md delete mode 100644 DOCUMENTATION.md diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 7693088..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(git fetch *)", - "Bash(git status *)", - "Bash(git log *)", - "Bash(git diff *)", - "Bash(gh run list *)", - "Bash(gh run view *)", - "Bash(gh pr view *)", - "Bash(gh pr list *)", - "Bash(gh release view *)", - "Bash(curl -s https://pypi.org/*)", - "Bash(curl -s https://test.pypi.org/*)", - "Bash(conda run -n agentloop ruff *)", - "Bash(conda run -n agentloop mypy *)", - "Bash(conda run -n agentloop python -m pytest *)", - "Bash(conda run -n agentloop env PYTHON=python bash scripts/check.sh *)", - "Bash(bash scripts/check.sh *)" - ], - "deny": [ - "Bash(git push --tags *)", - "Bash(git push * --tags *)", - "Bash(git push --force *)", - "Bash(git push * --force *)", - "Bash(git push -f *)", - "Bash(git push * -f *)", - "Bash(twine upload *)", - "Bash(python -m twine upload *)", - "Bash(python3 -m twine upload *)" - ] - } -} diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md deleted file mode 100644 index 36d2485..0000000 --- a/.claude/skills/release/SKILL.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: release -description: Cut a PyPI release of based-models-agentloop by pushing a version tag. Use only when the user invokes /release. -disable-model-invocation: true ---- - -# Release based-models-agentloop to PyPI - -Distribution `based-models-agentloop`, import name `agentloop`, repo `based-models/agent-loop`. - -Pushing a `vX.Y.Z` tag **publishes to PyPI immediately** (Trusted Publishing, no approval -gate), and a PyPI version number can **never be reused**, even after deletion. Everything -before the tag is preparation; the tag is the point of no return. The explanation lives in `docs/guides/releasing.md`; read it if anything here is unclear -and do not duplicate it. - -**Environment.** The sandbox `python3` is 3.9 and cannot build or install this project. Anything -that runs Python (`scripts/check.sh`, `scripts/test-wheel.sh`, `scripts/verify-release.sh`) goes -through the user's Terminal panel in the conda env: `conda run -n agentloop env PYTHON=python -bash scripts/`, output redirected to the scratchpad and read back. - -## 1. Preconditions (stop and report on any failure; do not fix silently) -Run read-only checks first: -- On `main`, up to date with `origin/main` (`git fetch`, then compare). Unrelated local - edits that do not belong in the release: leave them alone and say so. -- CI on the release commit is green: `gh run list --branch main --limit 3`. -- Version `X.Y.Z` is identical in `pyproject.toml` and `src/agentloop/__init__.py` - (`__version__`). The release workflow rejects a mismatch, but catch it earlier. -- `CHANGELOG.md` has a `## [X.Y.Z]` heading for exactly this version. -- No `vX.Y.Z` tag exists locally or on origin (`git ls-remote --tags origin`), and - `https://pypi.org/pypi/based-models-agentloop/X.Y.Z/json` is a 404 (that version is unpublished). -- `bash scripts/check.sh` passes on `main` (ruff, mypy, pytest **with the 88% coverage gate** that - the release workflow enforces, and the bare-import check). -- README or doc changes meant for the PyPI page are **merged to `main`**, because the tag - publishes what is on `main`. - -If the version is not bumped yet, offer to do the bump on a branch `release-X.Y.Z` and open a -PR (never push version bumps straight to `main`): set `X.Y.Z` in `pyproject.toml` and -`src/agentloop/__init__.py`, and add a `## [X.Y.Z]` heading directly above the existing -`[Unreleased]` entries (leaving `[Unreleased]` empty). Pick the number by SemVer; while `0.x`, a -minor bump may change the public API. - -## 2. Dry run on TestPyPI (recommended; do it from the bump branch, before merging) -TestPyPI versions are immutable too, and the previous version is usually already there, so a -dry run from `main` (still at the old version) fails with a false "file already exists". -- `https://test.pypi.org/pypi/based-models-agentloop/X.Y.Z/json` must be a 404 (else choose a - new version). -- Dispatch from the bump branch: `gh workflow run release.yml --ref release-X.Y.Z`. If the - permission layer refuses (it publishes a build), do not work around it: ask the user to run - Actions -> Release -> Run workflow with that branch. -- Afterwards confirm the run's `build` and `publish-testpypi` jobs succeeded, TestPyPI lists the - wheel and sdist for `X.Y.Z`, and **real PyPI still does not**. This also exercises `release.yml` - with whatever action versions are pinned, which ordinary CI never does. - -## 3. Confirm before tagging -Tell the user exactly what will happen and wait for a clear yes in this conversation: -> Tagging `vX.Y.Z` on `agent-loop` at commit `` will publish `based-models-agentloop X.Y.Z` to PyPI -> immediately. That version can never be re-uploaded. Proceed? - -A yes to an earlier question, or to a different release, does not count. - -## 4. Tag (only after the yes) -```bash -git tag vX.Y.Z && git push origin vX.Y.Z # one tag, by name; never `git push --tags` -``` - -## 5. Watch, then verify -- Find the run: `gh run list --workflow release.yml --limit 1`, then wait until it completes (a - background watch, not a sleep loop). -- Verify from the real index with one command, after the user agrees (it installs from PyPI): - `bash scripts/verify-release.sh X.Y.Z`. It checks the run's jobs, that PyPI's simple index lists - both files, the GitHub Release assets (wheel, sdist, `SHA256SUMS`), and a clean-venv install - outside the repo. Run it with the conda env's Python (`PYTHON=...`). -- PyPI's **JSON endpoint lags** the index by minutes and can still show the old "latest" after a - successful publish. Judge by the simple index or `pypi.org/pypi//X.Y.Z/json`, not by - `info.version` on the project JSON. - -## 6. If something fails -- Fails before `publish-pypi` uploads (tests, version check, missing pending publisher): - nothing was published and the version is not used. Fix, then delete and re-push the tag - **only if the user agrees**, since re-pushing re-triggers a publish. -- `publish-pypi` succeeded but the release is bad: **do not** move or delete the tag. - Recommend yanking `X.Y.Z` on PyPI (the user does this in the PyPI web UI) and releasing - `X.Y.(Z+1)`. - -## Never -- Tag without the explicit confirmation in step 3. -- `git push --tags`, force-push a tag, or reuse a version number. -- Add API tokens or `twine upload`: publishing is only via the workflow. -- Release from anything but an up-to-date `main`. - -## Related package -`based-models-agentloop-tools` (repo `agent-loop-tools`) depends on this one from PyPI. After -releasing a new agent-loop version, the tools package can adopt it: its dependency range lives -in its `pyproject.toml`, and its CI fails until the needed version exists on PyPI. diff --git a/.gitignore b/.gitignore index e9621cc..5769ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,9 @@ venv/ # personal editor settings (the interpreter choice is per-developer) .vscode/ + +# Internal notes and personal tooling: kept locally, not published +.claude/ +CLAUDE.md +BACKLOG.md +DOCUMENTATION.md diff --git a/BACKLOG.md b/BACKLOG.md deleted file mode 100644 index ba64aa9..0000000 --- a/BACKLOG.md +++ /dev/null @@ -1,188 +0,0 @@ -# Backlog - -Work that was deliberately deferred, with enough context to pick it up cold. Ordered by -priority, highest first. Last reviewed 2026-10-18 against `failure-telemetry` (`f0abed9`, -PR 11), which holds the unreleased `[Unreleased]` changelog entries. - -**Release blockers** are marked **[blocks next release]**. They are defects in, or -decisions about, work that is in `[Unreleased]` and not yet on PyPI. Fixing them after -the release means shipping a public API that later has to change, or docs that are wrong -on day one. - ---- - -## Must land before the next release (0.6.0) - -Nothing open. Items 1–6 landed: 1–4 (Anthropic transport classification, the Anthropic -`http_client` type and ownership, `phase` / `request_sent` on body errors, the API reference -pages) in PR 11; 5 (a failed turn's tokens are `None`, not `0`) and 6 (Dependabot PR 10 and -the `main` merge) after it. The numbers below are kept so existing references stay valid. - -Left over from item 2, optional: `pyproject.toml` still allows `anthropic>=1.0`, which -includes SDKs built on httpx rather than httpx2. `AnthropicClient` reads the expected -client class from the installed SDK, so either works, but the type hints and docs name -`httpx2.Client`. Raise the floor to the first httpx2-based SDK once that version is known. - ---- - -## High priority - -Items 7–12 (truncated-call retry cost, empty truncated turns, made-up tool names in telemetry, -tool-handler timeouts, `OTelObserver` redaction, and the stale 0.1-era docs) landed on -`failure-telemetry`. The numbers stay so references remain valid. - -Left over: - -- **Empty `END_TURN` (item 8).** An empty non-truncated turn is still `ResponseInvalid` and is - retried verbatim. Decide whether it should end the conversation with empty text or be retried - once with a nudge. -- **Hung tool threads (item 10).** A timed-out handler's thread is abandoned, not killed. A tool - that holds a lock or connection keeps it. `ToolSpec.timeout_seconds` defaults to `None`. -- **Shared redaction helpers (item 11).** `events.py` and `tracing.py` each implement - redact-then-clip; move them to one module so they cannot drift. -- **Hosted-tools guide examples (item 12).** The `agentloop-tools` parts of - `docs/guides/hosted-tools.md` (`native_web_search`, `DomainPolicy`) were written from this - backlog and not run here. -- **Companion package version compatibility.** `based-models-agentloop-tools` 0.1.0 pins - `based-models-agentloop>=0.4,<0.5`, while this checkout is 0.5.0. Update the companion - package's dependency range after verifying it against 0.5.0 so both can be installed - together by pip. -- **Public documentation hosting.** Add the `docs/` content to the website, then replace - the PyPI README's documentation placeholder with a link to the published pages. - ---- - -## Medium priority - -Items 13, 15, 16, 17 and 18 landed on `feature/medium-priority` (PR pending). Item 14 was checked live -(below). Left over: - -### 13. Resuming a transcript that ends mid-turn: the paused case - -Unanswered tool calls are now run before the first model call, and `check_invariants()` runs -once up front. Still open: a turn that paused (`StopReason.PAUSED`) and then hit a provider -error. The new `_Run` starts with `continuing=False`, so the continuation becomes a second -consecutive assistant message instead of extending the first. `Message` would have to record -that the turn is open, for example in `Origin`. - -### 14. Responses reasoning replay without `include` (checked live, 2026-10-18) - -Confirmed for `o4-mini`: a tool-using turn with `store: false` and no `include` returned a -`reasoning` item with a non-empty `encrypted_content`, and replaying the transcript with it was -accepted. The `_openai_responses.py` docstring is right for that model; no `include` is needed. -Not covered: `gpt-5.5` (the `.env` model) produced no reasoning item at its default effort -(0 reasoning tokens), so it did not exercise replay. Recheck if a request option for reasoning -effort is added. Item 21's fixture for a Responses reasoning item is still unrecorded. - -### 17. Model-aware provider `Quirks`: what is left - -`providers/openai.py` now picks `Quirks` by model-id prefix (`MODEL_QUIRKS`, `quirks_for_model`), -and `build_openai_client(quirks=...)` overrides it. The table holds only the o-series -(`o1`, `o3`, `o4`). Not done: other OpenAI families that reject parameters (add rows as they -are confirmed live), and OpenRouter and Modal, which stay provider-wide because their models -vary too much for one table. - -### 18. Tracing setup warnings: what is left - -A second `configure_tracing()` and a traced run with no provider now log a warning. It warns -rather than raises, and the check uses `ProxyTracerProvider` / `NoOpTracerProvider` from the -OpenTelemetry API. It is untested against older SDK versions than the one in the dev -environment. - ---- - -## Low priority - -### 19. Inconsistent error type for missing credentials - -`config.py` raises `AuthError` (a `ProviderError`, implying the provider was contacted) for -a missing `OPENROUTER_API_KEY` / `OPENAI_API_KEY`. For a missing `MODAL_ENDPOINT_URL` it -raises `ConfigurationError`. Nothing was sent in either case. Raising `ConfigurationError` -for both matches its docstring ("before any provider is touched"). This is a behaviour -change for anyone catching `AuthError`, so it needs a CHANGELOG entry. - -### 20. `Retry-After` longer than `max_delay` guarantees a wasted attempt - -`RetryingClient._delay_for` caps a server's `Retry-After` at `max_delay` (30s), retries -early, and is likely to be rate-limited again. When `retry_after > max_delay`, it would be -better to stop and raise at once, with `retry_after` on the error, so the caller can -reschedule. HTTP-date `Retry-After` values are ignored (`parse_retry_after`). - -### 21. Recorded wire-body fixtures - -`tests/fixtures/` does not exist. The adapters' inline tests cover the known quirks. Add -real captured bodies as they turn up: error-in-200, a plain-text 401, missing usage -details, an `httpx2` connection failure, and a Responses reasoning item (item 14). - -### 22. Client-side web search (implemented in agentloop-tools) - -**Status:** not implemented. Recorded here because it is the counterpart to this -package's hosted tools. - -Native search, where the provider runs the search, shipped in 0.2. Client-side search is -the other explicit option: the app's own code calls a search API, so it works on every -provider, its results can be logged, and each call goes through `before_tool`. - -**What implementing it takes** (a `[web]` extra in agentloop-tools): -1. `web_search(backend, *, http, name="web_search", policy: DomainPolicy | None = None, - max_results=5) -> ToolSpec`. - - A strict `{query, max_results}` schema, and numbered hits (`[n] title / url / - snippet`). - - A backend failure raises `ToolError`. - - Drop every hit where `policy.permits(hit.url)` is false BEFORE formatting. This is the - only place a domain policy becomes a guarantee. - - Reuse `DomainPolicy`'s matching rather than re-implementing it. - - `name=` lets it sit beside hosted `web_search`. -2. A `SearchBackend` protocol and `SearchHit(title, url, snippet, published)`, with one - concrete backend (Brave, Tavily or Exa). It receives the app's HTTP client and key and - never reads the environment. -3. `web_fetch(http, *, max_bytes, allowed_domains=()) -> ToolSpec`. - - http(s) only. - - An SSRF guard that refuses loopback, private, link-local and cloud-metadata addresses - AFTER DNS resolution. - - A content-type check, and stdlib `html.parser` HTML to text. - - `offset` paging with a "N more lines" hint, and a byte cap. -4. Tests using `httpx.MockTransport` only, including the SSRF guard against `127.0.0.1`, - `169.254.169.254` and a name that resolves to a private address. - -### 23. `OpenAICompatClient` guesses whether a supplied client chose its own timeout - -`chose_own = http_client.timeout != httpx.Timeout(5.0)`: a caller who deliberately sets -exactly 5 seconds gets the adapter's timeout instead. An explicit `timeout=` argument (item -16) would make the guess unnecessary. - -### 24. Web search over Chat Completions (`gpt-5-search-api`) - -**Status:** not implemented. The Completions codec refuses `hosted_tools` with a -`BadRequestError` that points at `api="responses"`. On Chat Completions, OpenAI's search is -a dedicated model driven by `web_search_options`, which returns `url_citation` -annotations. - -**What implementing it takes:** -- Map `HostedTool("web_search")` to `web_search_options` when the model is a search model. -- Parse `message.annotations` into `TextPart.citations`. -- Set the codec's `hosted_tools` per model (item 17). -- Domain filters have no equivalent there, so they must raise. - -Only matters for a deployment limited to Completions. - -### 25. `async` support (`AsyncLLMClient`, `Agent.arun()`) - -**Status:** deliberately not built. `client.complete()` spends its time blocked on a socket, -and both SDKs release the GIL while they wait. `agent.run` from N threads, or -`await asyncio.to_thread(agent.run, prompt)`, covers concurrency today with no changes. -`async`/`await` only pays off at four-figure concurrent conversations per process. - -**If that threshold is reached:** the canonical types and every adapter's translation -functions do no I/O and can be reused as they are. Observers need no async variants. New -async twins are needed for: -- `LLMClient`; -- `AnthropicClient` (`anthropic.AsyncAnthropic`); -- one `AsyncOpenAICompatClient` (`httpx.AsyncClient`); -- `RetryingClient` (`asyncio.sleep`); -- `run_tools` (`asyncio.gather` / `TaskGroup`, keeping the guarantee that every call gets - a result); -- `run_conversation`, `Agent.arun` / `aconverse`, and `build_async_client`. - -The real cost is doubling the provider, loop and tool test surface, plus a new dev -dependency (`pytest-asyncio` or `anyio`). diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index d119c0b..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,83 +0,0 @@ -# CLAUDE.md - -`agentloop` (PyPI: `based-models-agentloop`) is a provider-agnostic agent loop library: -reason, act, observe, repeat, with tools, retries and observability. Python 3.12+, pydantic v2. - -## Commands - -Environment: conda env `agentloop` (see `environment.yml`; editable install with `[all,dev]`). - -```bash -bash scripts/check.sh # ruff + mypy + pytest with the 88% coverage gate + bare-import check -bash scripts/check.sh --no-cov # the same without coverage, for quick loops -conda run -n agentloop python -m pytest -q tests/test_summary.py # one file -``` - -`PYTHON=` selects the Python the scripts use (default `python`). - -**In a Claude Code session the sandbox shell cannot run these.** Its `python3` is 3.9 (this -project needs 3.12) and `conda` is not usable from it. Run them through the Terminal panel -(`run_in_terminal`) as `conda run -n agentloop ...`, redirect the output to a file in the -scratchpad, and read it back. Close terminal tabs when done; there is a cap on open tabs. - -CI (`.github/workflows/ci.yml`) and the release workflow run the same checks inline; keep -`scripts/check.sh` in step with them. - -## Architecture - -The seam: **the loop emits facts only it can know; everything else is an observer.** - -- `loop.py`: iterations, limits, elapsed time, stop status, ids. No reporting policy. -- `observer.py`: the `LoopObserver` protocol. New hooks must be **optional** (`NullObserver` - no-op, fan-out in `MultiObserver`, skipped by `GuardedObserver` when absent). -- Observers that aggregate or export: `collector.py` (`SummaryCollector`), `events.py` - (JSONL event trace), `metrics.py` and `tracing.py` (OpenTelemetry), `summary.py` (the models). -- `provider.py` is the canonical request/response seam; vendor code lives only in `providers/`. -- `retry.py` stamps `attempts` / `retry_wait_seconds` onto responses and errors so retries are - counted without wiring an observer into the client. - -## Invariants (tests enforce most of these) - -- **Core stays light.** `tests/test_layering.py` allowlists every module-scope import and bans - `os`, `pathlib`, `subprocess`, `socket` and others at any depth. The library never reads the - environment or opens files; sinks write through a caller-supplied callable. A bare - `import agentloop` must not load `opentelemetry`, `httpx` or `anthropic`: OTel and metrics are - imported lazily (see `_with_tracing` / `_with_metrics` in `agent.py`). -- **Telemetry is content-free by default.** Nothing the model, a tool or a provider wrote (including - exception messages) reaches a summary, metric, span or trace unless the caller opts in, and - captured text goes through the caller's `redact` function first. Summaries carry counts, names, - statuses and error *type names* only. -- **`None` means "not reported", never zero** (`Usage`, cost, cache tokens). -- **Telemetry never becomes an execution failure.** Observer callbacks are guarded; a sink that - raises is logged and swallowed. `conversation()` is deliberately not guarded. -- Metric attributes are low-cardinality: never conversation, correlation or parent ids. - -## Conventions - -- Frozen pydantic v2 models (`ConfigDict(frozen=True)`), `from __future__ import annotations` - in every module, `collections.abc` imports, PEP 695 `type` aliases, `StrEnum` for enums. -- mypy `strict = true` over `src` **and `tests`**: annotate every test (`-> None`). -- ruff line length 100 (rules E, F, I, UP, B, SIM). pytest runs with `filterwarnings = error`. -- Sync only: the loop, clients and observers are synchronous; tools run in a thread pool, so - anything an observer shares across tool callbacks needs a lock. -- Comments explain *why*; match the surrounding density. - -## When you change public behavior - -Update, in the same PR: `CHANGELOG.md` (`[Unreleased]`), the `README.md` Observability section, -the relevant `docs/api/*` and `docs/guides/*` pages, `docs/examples/*`, and `__all__` in -`src/agentloop/__init__.py`. Note any hand-written `LoopObserver` implementations this affects. - -## Git and PRs - -- Stage only the files your task changed; the working tree often holds unrelated edits. -- `.gitignore` has `examples/`, which also matches `docs/examples/`. Existing files there are - tracked and stage normally, but a **new** file needs `git add -f`. -- Never push, merge or open a PR unless asked. Never commit to `main`; open a PR. -- Describe PRs as What / Why / Changes. - -## Releasing - -Use the `/release` skill (`.claude/skills/release/SKILL.md`; background in -`RELEASING.md`). Pushing a `vX.Y.Z` tag publishes to PyPI immediately and a version -can never be reused. Never `git push --tags`, force-push a tag or `twine upload`. diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md deleted file mode 100644 index 659af54..0000000 --- a/DOCUMENTATION.md +++ /dev/null @@ -1,425 +0,0 @@ -# Documentation review and update plan - -This checklist tracks a page-by-page review against `src/agentloop`, the provider adapters, -the public exports in `src/agentloop/__init__.py`, and the tests. Update **one Markdown page -at a time** and pause for review before starting the next page. Check a page off only after -its claims have been compared with code and every executable example on that page has been -run. Use OpenAI for agent-running examples. When credentials are unavailable, exercise the -OpenAI adapter with a local HTTP transport and record that limitation here. Use the simple -caption `Output:` for documented results. Provider setup commands can be checked locally -without making a paid request. - -## Review rules - -- [x] Inventory the documentation, package surface, provider implementations, and test suite. -- [x] Identify the available Python 3.12+ environment (`conda` environment `agentloop`). -- [ ] Use small, generic, self-contained OpenAI examples for agent runs. Reserve `FakeClient` - for pages that specifically explain offline testing. -- [ ] Replace placeholder output, invented JSON, and comments like “For example” with - verified output where stable. Do not imply that local HTTP responses are live model output. - Give each example the imports, objects, and setup it needs. -- [ ] Check package-level imports against `__all__`; use submodule imports for other names. -- [ ] After each page, run its Python examples, check links and commands as applicable, and - record the verification before pausing for review. -- [ ] After all pages, run the repository's documentation-relevant checks and a final README - review of the rendered PyPI description. - -## Getting started and navigation - -1. [ ] `docs/getting-started/hello-world.md`: revise the completed offline-first page to - lead with OpenAI and remove the `FakeClient` example; keep provider recipes accurate. -2. [x] `docs/getting-started/installation.md`: check Python version, extras, checkout install, - and lazy-import claims against `pyproject.toml` and package imports; verify commands. -3. [x] `docs/getting-started/configuration.md`: check all defaults and accepted environment - variables against `Settings.from_env`, `build_client`, and provider builders; run both - configuration examples without network calls. -4. [ ] `docs/index.md`: revise its offline-first navigation after the Hello World page is - changed to OpenAI; links were already verified. - -## Guides - -5. [x] `docs/guides/agents.md`: use OpenAI for one-shot, multi-turn, and borrowed-client - examples; check client ownership and traced-agent limits. -6. [x] `docs/guides/tools.md`: correct the `ToolCallPart` import (it is in - `agentloop.transcript`, not the package root); run examples of tool execution, - dependencies, and approval; verify result order in the tooling tests. -7. [x] `docs/guides/conversations.md`: supply the missing `agent` setup; run conversation, - manual tool-result ordering, invariant, and failure examples. -8. [x] `docs/guides/providers.md`: use OpenAI for complete examples; verify provider - constructors, transport ownership, API selection, redirects, and connection-error - descriptions against implementation and tests. -9. [x] `docs/guides/hosted-tools.md`: separate this package's `HostedTool` behavior from - optional `agentloop-tools` helpers; check options and provider support against adapters; - remove stale or unverifiable experiment claims and make the example complete. -10. [x] `docs/guides/custom-providers.md`: run the `LLMClient` example and show actual output; - verify pause, hosted-tool capability, replay, and empty-turn rules. -11. [x] `docs/guides/summaries.md`: replace the fabricated summary JSON with output from an - OpenAI adapter run; supply missing imports and objects; check status, usage, timing, - warnings, timeline, and failure semantics against summary/collector/loop code. -12. [x] `docs/guides/event-trace.md`: replace undefined `tools`/`client` with a runnable - client; capture real JSONL output; check redaction and event fields. -13. [x] `docs/guides/observers.md`: run the observer examples with explicit client and usage; - verify callback signatures, guarding, and console output. -14. [x] `docs/guides/tracing.md`: verify setup and lifecycle against `tracing.py`/`metrics.py`; - run examples with local in-memory exporters where possible, without requiring OTLP. -15. [x] `docs/guides/retries-and-errors.md`: provide a scripted failure/retry example and - real output; check error taxonomy, `request_sent`, and `RetryPolicy` fields. -16. [x] `docs/guides/transcript-storage.md`: create the missing `transcript` locally, perform - a JSONL round trip in a temporary path, and show actual output. -17. [x] `docs/guides/installing.md`: reconcile with the getting-started install page and - release metadata; check version pinning guidance and verification commands. - -## Maintainer documentation - -18. [x] `RELEASING.md`: move release instructions out of user documentation; compare every - documented command and release claim with `scripts/`, package metadata, and CI - configuration; avoid running publish steps. - -## Complete examples - -19. [x] `docs/examples/minimal-agent.md`: show a direct OpenAI agent run and verify its code - through the provider adapter. -20. [x] `docs/examples/tool-enabled-agent.md`: replace the simulated weather forecast with - a generic local tool; use OpenAI and verify the tool-call path. -21. [x] `docs/examples/custom-observer.md`: fix undefined `tools`/`client`, account for - optional token values correctly, and capture actual observer/JSONL output. -22. [x] `docs/examples/traced-agent.md`: remove dependence on an external collector for the - runnable example and verify produced spans locally. -23. [x] `docs/examples/custom-provider.md`: run the complete fixed client and show its output; - verify ownership guidance. -24. [x] `docs/examples/index.md`: check descriptions and links after examples are updated. - -## API reference - -25. [x] `docs/api/agentloop.md`: reconcile the export list with `__all__`, including - `AttemptRecord` and `FailureCount`; run the import example. -26. [x] `docs/api/agent.md`: replace the `yfinance` example with a generic OpenAI tool - example; update the documented `tool()` signature to include `timeout_seconds` and - `parallel_safe`; verify agent methods, client ownership, and output. -27. [x] `docs/api/config.md`: verify fields, defaults, constructors, optional transport, and - executable settings example. -28. [x] `docs/api/errors.md`: verify the hierarchy and structured error fields; make the - catch example self-contained and run it. -29. [x] `docs/api/loop.md`: verify defaults, status and failure behavior, and observer example; - run the low-level example. -30. [x] `docs/api/observer.md`: verify all optional hooks and callback signatures; run the - subclass example. -31. [x] `docs/api/summary.md`: verify every field and status against models; run any example. -32. [x] `docs/api/events.md`: verify constructor, event names, data policy, and sample output. -33. [x] `docs/api/metrics.md`: verify instruments and attribute names against implementation. -34. [x] `docs/api/provider.md`: verify request/response types, usage and hosted-tool semantics; - run the model example. -35. [x] `docs/api/retry.md`: verify policy defaults/fields and retry behavior; run example. -36. [x] `docs/api/tooling.md`: verify schemas, registration, execution, timeouts, and concurrency; - run the `ToolSpec` example. -37. [x] `docs/api/tracing.md`: verify OTel setup, parameters and content capture; run example. -38. [x] `docs/api/transcript.md`: add missing imports to snippets; check current part fields, - invariant rules, JSONL behavior, and actual output against `transcript.py`. -39. [x] `docs/api/providers/anthropic.md`: check constructor, options, wire translation, - hosted search, and credentials against adapter code; run translation examples offline. -40. [x] `docs/api/providers/openai.md`: check Responses/Completions behavior and constructor; - run offline translation examples. -41. [x] `docs/api/providers/openrouter.md`: check defaults, request translation and constructor; - run the provider example and an offline translation check. -42. [x] `docs/api/providers/modal.md`: check endpoint, authentication and constructor; - run offline examples. -43. [x] `docs/api/providers/index.md`: check provider table and links. -44. [x] `docs/api/index.md`: check module coverage and navigation once references are current. - -## PyPI README - -45. [x] `README.md`: check every claim and configuration default against the finished pages - and implementation; use OpenAI for agent-running examples and validate them through the - adapter; include verified output where stable. Keep the documentation placeholder until - the website includes these docs. - -## Verified pages - -Record each completed page here with the exact command or method used and the observed -output. Do not mark a page complete based on syntax inspection alone. - -- `docs/getting-started/hello-world.md`: ran the exact offline Python block with - `conda run -n agentloop python /tmp/verify_agentloop_hello.py`; output was - `Hello, world!` and `model requests: 1`. Compiled the live Python block and - constructed/closed agents with `Agent.from_env` for Anthropic, OpenAI, OpenRouter, - and Modal using local dummy settings, without making provider requests. All four - constructors succeeded with the documented default models. A live model reply - requires a real credential or deployed endpoint and is intentionally not shown - as fixed output. -- `docs/getting-started/installation.md`: ran the documented `python -c` import check - with `conda run -n agentloop`; output was `agentloop ready`. Checked Python 3.12.14, - package version 0.5.0, `requires-python`, and all extras against `pyproject.toml`. - A fresh import confirmed `agentloop` does not load `anthropic`, `httpx`, or - `opentelemetry.sdk`. A local editable-install dry run could not complete because - this existing conda environment lacks the `hatchling` build backend and network - installation is unavailable in the sandbox; installation commands are therefore - checked against package metadata, not claimed as executed. -- `docs/getting-started/configuration.md`: extracted and ran both Python blocks with - `conda run -n agentloop python /tmp/verify_agentloop_configuration.py`. They printed - `openai example-model`, `max attempts: 2`, and `openai example-model 2`, matching - the page. Compared the environment table with every key read by `Settings.from_env`; - all 14 variables are listed. Client construction and close made no provider request. -- `docs/index.md`: checked the provider list against `Settings` and the offline example - against the shipped `FakeClient`; resolved all 19 relative Markdown links with a local - link check. This navigation page contains no executable example. -- `docs/guides/agents.md`: after the OpenAI revision, extracted and ran all three Python - blocks with `conda run -n agentloop python /tmp/verify_agentloop_agents_openai.py`. - A local `httpx.MockTransport` supplied four Responses API replies; the OpenAI adapter - sent four `/v1/responses` requests, and the second conversation request contained the - earlier turn. The project `.env` contains an OpenAI key, though it is not exported to the - process by default. Loaded it without printing its value and ran the same three blocks - against live OpenAI with `conda run -n agentloop python /tmp/verify_agentloop_agents_live.py`; - all four requests succeeded. The page now shows the actual live output with the `Output:` - caption. The prior `tests/test_agent.py` run passed all 29 tests. -- `docs/guides/tools.md`: extracted and ran both complete Python blocks against live - OpenAI with `conda run -n agentloop python /tmp/verify_agentloop_tools_live.py`, loading - the project `.env` key without printing it. The arithmetic tool ran once and returned - `27 plus 15 is 42.` The public note was read from application deps; the private call - was denied before its handler ran and was counted in both `tool_calls` and - `tool_errors`. The page contains the actual output. Ran - `conda run -n agentloop python -m pytest -q tests/test_tooling.py` (25 passed), - including the executor's ordering and failure behavior. After simplifying `add`, ran - just that Python block again with - `conda run -n agentloop python /tmp/verify_agentloop_tools_add_live.py`; it returned - the same documented output. -- `docs/guides/conversations.md`: ran its OpenAI conversation block with - `conda run -n agentloop python /tmp/verify_agentloop_conversations_live.py` using the - project `.env` key without printing it; the reply and `messages: 2` output are on the - page. Ran the manual result-order and invariant blocks with - `conda run -n agentloop python /tmp/verify_agentloop_conversations_local.py`; their - output matches the page. Ran `conda run -n agentloop python -m pytest -q - tests/test_transcript.py` (37 passed). -- `docs/guides/providers.md`: extracted and ran all three Python blocks with - `conda run -n agentloop python /tmp/verify_agentloop_providers_live.py`, using the - project `.env` OpenAI key without printing it. They produced `Hello`, - `openai gpt-5.1 responses`, and `ready` with `HTTP client closed by agent: False`, - matching the page. Removed the unconfigured `truststore`/proxy snippet and checked - the transport and Responses claims against the adapters. Ran the transport, OpenAI - Responses, and OpenAI compatibility test files together (96 passed). -- `docs/guides/hosted-tools.md`: removed package-external policy details and stale - experiment claims, then ran its OpenAI Responses web-search block with - `conda run -n agentloop python /tmp/verify_agentloop_hosted_live.py` using the project - `.env` key without printing it. The provider returned one hosted search call, 12 - sources, and no citations for this answer; the output is on the page. Compared the - option table with both adapters and ran the OpenAI Responses and Anthropic test files - (83 passed). Clarified the contract with the separate `agentloop-tools` package: - `native_web_search()` returns this package's `HostedTool`. Importing its factory from - the sibling source confirmed the type and options. Moved the companion package's - version compatibility issue to `BACKLOG.md`. Corrected the execution attribution, documented - mutually exclusive domain filters and hosted/local tool name collisions, and clarified - Anthropic model support and the Chat Completions error paths. Checked those claims against - source and ran the agent, OpenAI Responses, and Anthropic test files (112 passed). -- `docs/guides/custom-providers.md`: kept the deliberately minimal custom `EchoClient` to - demonstrate the `LLMClient` protocol, combined its two code blocks into one complete - example, and ran the block in the `agentloop` conda environment. It printed `Echo: hello`, - as documented. Checked pause, hosted capability, replay, stop reasons, empty turns, and - client ownership against the provider, loop, transcript, and agent code. Ran the agent, - loop, and provider tests (69 passed). -- `docs/guides/summaries.md`: replaced fabricated JSON and incomplete snippets with live - OpenAI examples showing a selected summary, an observer callback, and an optional timeline. - Ran its Python blocks with `conda run -n agentloop python - /tmp/verify_agentloop_summaries_live.py` using the project `.env` key without printing it; - the actual output is on the page. Checked fields and failure behavior against the summary, - collector, loop, and observer code; ran summary and loop tests (94 passed). Rebuilt the page - as three independent OpenAI examples after feedback: first the result summary, then an - observer, then `SummaryOptions`. Ran all three code blocks and updated their output. -- `docs/guides/event-trace.md`: replaced undefined `tools` and `client` with an OpenAI run - that writes JSONL to a temporary file and prints selected fields from its four records. - Added a separate content-capture example with an application redactor. Ran both Python - blocks with `conda run -n agentloop python /tmp/verify_agentloop_event_trace_live.py` - using the project `.env` key without printing it; their output is on the page. Checked - event fields and redaction behavior against `events.py`; ran event trace tests (20 passed). -- `docs/guides/observers.md`: made the custom usage observer and combined built-in observer - examples complete OpenAI runs. Ran both Python blocks with `conda run -n agentloop python - /tmp/verify_agentloop_observers_live.py` using the project `.env` key without printing it; - the response count, token usage, console text, and recorded events match the page. Checked - callback signatures, optional hooks, and guarding against `observer.py`; ran agent, loop, - and summary tests (123 passed). After feedback, kept the usage observer simple and printed - the input prompt and returned answer beside the provider's input/output token counts; - reran both blocks and confirmed the displayed values. Then changed the first prompt to - the one-digit week question, printed `response.usage`, and confirmed its 19 input and 11 - output tokens against the raw OpenAI response body from the same request. Reran the updated - guide blocks; the documented output matches. -- `docs/guides/tracing.md`: replaced collector-dependent examples with complete OpenAI runs - using in-memory span and metric exporters. Ran both Python blocks with `conda run -n - agentloop python /tmp/verify_agentloop_tracing_live.py` using the project `.env` key without - printing it; the recorded span operations and metric names match the page. Checked provider - setup, content capture, lifecycle, instruments, and metric attributes against source; ran - tracing and metrics tests (41 passed). -- `docs/guides/retries-and-errors.md`: replaced incomplete snippets with one runnable example - that injects a retryable connection failure, then lets the OpenAI client answer. Ran its - Python block with `conda run -n agentloop python /tmp/verify_agentloop_retries_live.py` - using the project `.env` key without printing it; the retry log, answer, attempt count, - summary retry count, and send state match the page. Checked error types, send-state phases, - and retry policy against source; ran retry, transport error, and summary tests (115 passed). - Reworked the guide after feedback to introduce retryable errors and policy before the - example, then explain the recorded attempts and three send states. Reran the shortened - example; its output still matches. -- `docs/guides/transcript-storage.md`: replaced the undefined transcript with a complete - two-message JSONL round trip in a temporary directory. Ran the example in the `agentloop` - conda environment; its serialized lines and equality result match the page. Checked - serialization, parsing, invariant checks, and origin replay against `transcript.py`. -- `docs/guides/installing.md`: updated the version range to `0.5`, aligned extras and - Python requirements with `pyproject.toml` and the getting-started guide, and added a - runnable import check. Ran the import command outside the source tree in the `agentloop` - conda environment; its output matches the page. Parsed the example requirement to verify - its syntax. Checked the 0.5.0 release files and provenance on PyPI and the repository's - release workflow. A live GitHub release query was unavailable from this environment. -- `RELEASING.md`: moved the maintainer release procedure out of the user documentation and - removed its navigation link. Matched the tag trigger, job sequence, publisher environment - names, version checks, and release artifacts to `.github/workflows/release.yml`. Replaced - stale pending-publisher and GitHub plan claims, aligned the local checks with - `scripts/check.sh`, and corrected the `test-wheel.sh` description and yanking guidance. - Ran `conda run -n agentloop bash scripts/check.sh`: ruff and mypy passed, 586 tests passed - with 94.81% coverage, and the import check passed. Shell syntax checks passed. Attempted - `scripts/test-wheel.sh` in a fresh temporary clone; build isolation could not install - `hatchling` because PyPI network access was unavailable. No tag or publish step was run. -- `docs/examples/minimal-agent.md`: replaced the incomplete provider setup and larger - one-off script with a short OpenAI agent example. Ran the exact Python block through the - OpenAI provider in the `agentloop` conda environment; the output was `4`, as shown on the - page. Checked `Agent.from_env()`, `run()`, and context-manager ownership against `agent.py`. -- `docs/examples/tool-enabled-agent.md`: replaced the fictional weather forecast with a - small `count_letters` tool and removed the observer from the example. Ran the OpenAI agent - in the `agentloop` conda environment; it returned `5` and recorded one `count_letters` - call, matching the page. Checked decorator schema requirements, tool dispatch, and - `run_result()` summary fields against `agent.py`, `tooling.py`, and the loop. -- `docs/examples/minimal-agent.md`, `docs/examples/tool-enabled-agent.md`, and - `docs/guides/tracing.md`: added a provider-support sentence before their OpenAI install - instructions. The examples and their recorded output were unchanged. -- `docs/examples/custom-observer.md`: replaced the undefined `tools` and `client` with - complete OpenAI examples for a custom token counter and an in-memory JSONL trace. Ran - both blocks in the `agentloop` conda environment; the 19 input tokens, 11 output tokens, - and four trace events match the page. Checked callback signatures and usage field types - against `observer.py` and `provider.py`. -- `docs/examples/traced-agent.md`: replaced the external OTLP collector with an in-memory - tracer provider. Ran the OpenAI example in the `agentloop` conda environment and verified - that the `chat` span is a child of the `invoke_agent` span; the output matches the page. - Checked `trace=True` setup and provider ownership against `agent.py` and `tracing.py`. -- `docs/examples/custom-provider.md`: ran the exact `FixedClient` Python block in the - `agentloop` conda environment; it printed the documented response. Checked the - `LLMClient` protocol and borrowed-client ownership against `provider.py` and `agent.py`, - and confirmed that closing a directly constructed agent leaves a closable client open. -- `docs/examples/index.md`: updated the summaries of all five example pages and clarified - which examples call OpenAI. Checked that each relative Markdown link resolves. -- `docs/api/agentloop.md`: added the two missing root exports, `AttemptRecord` and - `FailureCount`, and checked the list against all 37 names in `__all__`. Ran the import - example in the `agentloop` conda environment and verified that a bare import leaves - `anthropic`, `httpx`, and `opentelemetry.sdk` unloaded. -- `docs/api/agent.md`: replaced the stock-price example with a small `read_note` tool that - shows model-supplied arguments and application-supplied `Deps`. Ran the exact Python block - with OpenAI in the `agentloop` conda environment; it printed `Hello from the agent loop.` - and `{'read_note': 1}`, matching the page. Updated the `tool()` signature and checked - defaults, tool validation, client ownership, and agent methods against the source. -- `docs/api/config.md`: checked every setting and default against `config.py`, made the - settings example self-contained, and showed client construction and cleanup. Ran the - exact Python block in the `agentloop` conda environment; its output matches the page. - Clarified attempt and timeout behavior, credential errors, Pydantic validation, and - ownership of an optional HTTP transport against the provider implementations. -- `docs/api/errors.md`: verified the exception hierarchy, retryable classes, budget kinds, - and provider error fields against `errors.py`, `retry.py`, and the loop. Replaced the - undefined-agent example with a real missing-credential failure from `build_client()`. - Ran the exact Python block in the `agentloop` conda environment; it raised `AuthError` - and printed the metadata shown on the page. -- `docs/api/loop.md`: checked constants, signatures, transcript updates, truncation, - deadlines, summaries, and `ListObserver` against `loop.py`. Replaced undefined-client - and empty-observer snippets with one complete low-level OpenAI example. Ran the exact - Python block in the `agentloop` conda environment; it printed `7` and the request and - response event names shown on the page. -- `docs/api/observer.md`: checked callback signatures, optional hooks, guarded callback - behavior, and tool event timing against `observer.py` and `tooling.py`. Replaced the - unconnected `RetryCounter` snippet with a complete OpenAI `ResponseCounter` example. - Ran its exact Python block in the `agentloop` conda environment; it printed `7` and one - response, matching the page. -- `docs/api/summary.md`: expanded the status table, distinguished statuses from the - `succeeded` and `budget_exhausted` properties, and documented every public field of - `ConversationSummary`, `TurnRecord`, and `ConversationContext`. Checked field and status - coverage against the live models. Ran the exact OpenAI example in the `agentloop` conda - environment; it printed `7`, `completed`, `True`, and `1`, matching the page. -- `docs/api/events.md`: verified the constructor, record keys, event names, content policy, - redaction order, and concurrency rule against `events.py`. Added a complete metadata-only - OpenAI trace example and ran its exact Python block in the `agentloop` conda environment; - the event list and `1 False` request check match the page. -- `docs/api/metrics.md`: documented all 12 instruments with their exact attribute sets and - corrected when stall and budget-warning counters record. Verified each instrument and - attribute set with a `MetricsObserver`, a complete sample `ConversationSummary`, and an - in-memory OpenTelemetry meter in the `agentloop` conda environment. Confirmed the `otel` - extra. Added a complete OpenAI example on the API page that passes a `Meter` from - `metrics.get_meter("agentloop")` to `MetricsObserver`. Ran its exact Python block; it - printed `7` and `completed 1`, matching the page. -- `docs/api/provider.md`: checked all fields of `ToolSchema`, `ModelRequest`, `Usage`, - `ModelResponse`, `AttemptRecord`, and `HostedTool` against the live Pydantic models. - Clarified empty truncated responses, retry metadata, and hosted-tool support. Replaced - the placeholder response with a complete direct OpenAI `ModelRequest` example and ran - its exact Python block in the `agentloop` conda environment; it printed `7` and - `end_turn`, matching the page. -- `docs/api/retry.md`: checked policy fields and defaults, delay calculation, observer - callback timing, delegated client properties, and attempt logging against `retry.py`. - Replaced the undefined-client snippet with an OpenAI client wrapped by a one-time - retryable failure. Ran the exact Python block in the `agentloop` conda environment; it - printed the documented retry warning, answer, attempt count, and retry count. -- `docs/api/tooling.md`: checked `ToolSpec` fields, schema use, registry behavior, - sequential and parallel execution, timeouts, and error results against `tooling.py`. - Replaced the incomplete `ping` snippet with a self-contained `ToolSpec`, `Registry`, - and `run_tools` example. Ran the exact Python block in the `agentloop` conda - environment; it printed `hello` and `False`, matching the page. -- `docs/api/tracing.md`: checked span creation, observer callbacks, content capture, - redaction and error-detail clipping, concurrency, and `configure_tracing()` against - `tracing.py`. Replaced the constructor-only snippet with an in-memory OpenTelemetry - example using a direct `OTelObserver` and OpenAI. Ran its exact Python block in the - `agentloop` conda environment; it printed `ready` and the `chat` and `invoke_agent` - span operations shown on the page. Added a separate `configure_tracing()` collector - example, ran its exact Python block with OpenAI and a temporary local OTLP receiver, - and verified that it printed `ready` and exported spans to `/v1/traces`. -- `docs/api/transcript.md`: checked content-part fields, `Origin`, message and transcript - behavior, invariants, JSONL serialization, and text extraction against `transcript.py`. - Replaced incomplete snippets with one self-contained round-trip example. Ran its exact - Python block in the `agentloop` conda environment; it printed `Hi!`, two messages, and - two JSONL lines, matching the page. -- `docs/api/providers/anthropic.md`: checked the constructor, credential handling, HTTP - client ownership, hosted web search options, and translation helpers against the - Anthropic adapter. Replaced the undefined-request snippet with a self-contained - offline `ModelRequest` example. Ran its exact Python block in the `agentloop` conda - environment; the model, prompt text, and hosted search tool type match the page. -- `docs/api/providers/openai.md`: checked the constructor, defaults, API paths, hosted - tool support, and timeout behavior against the OpenAI and shared adapters. Replaced - the undefined-request snippet with a complete example that calls both OpenAI APIs. - Ran its exact Python block in the `agentloop` conda environment; both returned - `ready`, matching the page. Offline translation checks confirmed `store: false`, - token-limit fields, and omission of `parallel_tool_calls` without tools. The live - check revealed that Chat Completions rejected that parameter without tools, so the - adapter now omits it in that case. The targeted adapter suite passed (43 tests). -- `docs/api/providers/openrouter.md`: checked its constructor, headers, timeouts, Chat - Completions format, hosted-tool support, and reasoning replay against the adapter. - Replaced the placeholder construction snippet with a complete OpenRouter request and - output. Ran its exact Python block in the `agentloop` conda environment; it printed - `openrouter completions` and `ready`, matching the page. An offline translation check - confirmed the default model, `max_tokens` field, and omission of `parallel_tool_calls` - when no tools are present. -- `docs/api/providers/modal.md`: checked the constructor, endpoint path, proxy headers, - timeout defaults, and vLLM quirks against the adapter. Replaced placeholder credentials - with a runnable client configuration example. Ran its exact Python block in the - `agentloop` conda environment; it printed the documented client API, default model, - and empty hosted-tool set. Offline checks confirmed the request path, headers, - `max_tokens` field, and absence of `parallel_tool_calls` without tools. A live request - reached the configured Modal endpoint, but it returned `no upstreams available`, so - no model output was available to document. -- `docs/api/providers/index.md`: added a provider comparison table and links to client - selection, extension, and API details. Checked all seven relative links on disk. - Constructed direct OpenAI, OpenRouter, and Modal clients offline in the `agentloop` - conda environment, and checked Anthropic's class capability; API mode and hosted - search support matched every table row. -- `docs/api/index.md`: checked the table against every top-level module in - `src/agentloop` and added `collector` and `stall` navigation. Verified all 20 relative - links on disk and confirmed that all 17 top-level modules, including the package root - and providers package, are represented. This index has no executable example. -- `README.md`: reconciled provider support, configuration defaults, tool behavior, hosted - search, summaries, tracing, and event capture with the implementation and completed - documentation pages. Replaced incomplete examples with six self-contained Python blocks - and captured their actual output in the `agentloop` conda environment. Five used live - OpenAI calls; the missing-credential example ran offline. The tool run recorded one - `add` call, and the hosted search run recorded a `HostedToolPart`. Kept the - documentation placeholder at the user's request until the website includes these docs. - Revised the reasons list to cover local tool approval, hosted search domain restrictions, - and content redaction, with each bullet at two lines or fewer. - Rendered the Markdown with `markdown_it` (four tables and 15 code blocks) and confirmed - no em dashes remain. From bed697d19984ede57a77dc75b9c027db33b070fb Mon Sep 17 00:00:00 2001 From: aaron Date: Tue, 20 Oct 2026 22:58:31 -0700 Subject: [PATCH 2/2] Fill .gitignore gaps and drop references to untracked notes Ignores .env.* (keeping .env.example); agentloop-tools gains the standard entries agent-loop already had. Two source comments no longer point at files that are not in the repo. Co-Authored-By: Claude Sonnet 5.5 --- .gitignore | 2 ++ src/agentloop/observer.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5769ff2..a2c2c41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .env +.env.* +!.env.example __pycache__/ *.egg-info/ dist/ diff --git a/src/agentloop/observer.py b/src/agentloop/observer.py index 8a9db1f..90202de 100644 --- a/src/agentloop/observer.py +++ b/src/agentloop/observer.py @@ -25,7 +25,7 @@ type BudgetKind = Literal["iterations", "deadline"] # What telemetry reports for a call to a tool that is not registered. The model wrote -# that name, so it is unbounded and not ours to export (see CLAUDE.md invariants). The +# that name, so it is unbounded and not ours to export (metric attributes stay low-cardinality). The # model itself still gets the real name back in its "Unknown tool" result. UNREGISTERED_TOOL = ""