Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,18 @@ Speed metrics are available directly on each `AssistantMessage` via `request_sta

## Session

The `session()` method returns the agent configured as an async context manager. Sessions handle:
Each call to `session()` returns a detached runtime with its own active tools,
provider and logger lifecycle, run state, skills, cache state, and outputs. Exact
built-in providers and loggers support overlapping sessions. Custom lifecycle
objects and subclasses of built-ins remain reusable across sequential sessions,
but the same configured object cannot be used by overlapping sessions; configure
a distinct instance for each concurrent owner.

A provider-free `Agent.run()` can still be called directly. It is always an
independent root run, even when called from inside an active session, and does
not inherit that session's files, skills, warnings, or finish validation.

Sessions handle:

- Tool lifecycle (setup and teardown of ToolProviders)
- File uploads to execution environment
Expand Down
6 changes: 6 additions & 0 deletions docs/extending/code_backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ The base class provides:
- `allowed_commands` - Optional regex patterns to restrict commands
- File upload/download utilities

Custom backends and subclasses of built-in backends own their configured runtime
state. They can be reused sequentially, but overlapping sessions must use
separate backend instances. Stirrup rejects overlap on the same configured
instance before entering resources. Exact built-in backends are reconstructed
privately for each session.

## Minimal Implementation

```python
Expand Down
11 changes: 11 additions & 0 deletions docs/extending/loggers.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,17 @@ async with agent.session() as session:
print(metrics_logger.get_summary())
```

A custom logger instance is reused directly and supports sequential sessions.
The same configured logger cannot serve overlapping sessions, even when it is
attached to different `Agent` objects; the second session fails before it enters
any logger or provider resources. Use one logger instance per concurrent
owner, and share an explicitly synchronized metrics or event sink between those
instances when aggregation is required. Subclasses of the built-in logger follow
the same sequential-only rule.

The exact built-in console and Slack loggers receive private per-session
lifecycle state automatically.

## Combining Loggers

```python
Expand Down
5 changes: 5 additions & 0 deletions docs/extending/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class WeatherToolProvider:
await self._client.aclose()
```

Custom providers are reusable sequentially. For overlapping sessions, construct
one provider instance per concurrent owner; sharing the same configured object
is rejected before resources are entered. This also applies to subclasses of
built-in providers.

## Tools with State

```python
Expand Down
4 changes: 4 additions & 0 deletions docs/guides/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,9 @@ cache_manager.clear_cache("abc123def456")
## Notes

- Cache key is computed from the initial prompt—same prompt = same cache
- Cache files are not shared transactionally. Within one process, while
`cache_on_interrupt=True`, a concurrent root run with the same task prompt is
rejected before cache I/O. Different prompts can run concurrently; use
`cache_on_interrupt=False` when identical tasks must overlap without caching.
- Caches are stored locally in `~/.cache/stirrup/`
- Caches are automatically cleared on successful completion (by default)
10 changes: 10 additions & 0 deletions docs/guides/sub-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ agent.to_tool(

Returns a `Tool[SubAgentParams, SubAgentMetadata]`.

The returned tool must execute within its owning parent's active session. Use
`async with parent.session() as session:` and run the parent through `session`;
a direct `await parent.run(...)` cannot delegate to sub-agents. Without an
active parent session, delegation returns an unsuccessful tool result instead
of treating the child as a root agent or saving its output to the current
working directory.

### SubAgentParams

When the parent calls the sub-agent tool, it provides:
Expand Down Expand Up @@ -317,6 +324,9 @@ parent = Agent(
- Sub-agent runs are synchronous (parent waits for completion)
- All sub-agent messages are returned to parent (may use context)
- File transfer only works with code execution environments
- A custom `ViewImageToolProvider` configured with the sub-agent's backend cannot
use `share_parent_exec_env=True`; disable sharing or use the exact built-in
view-image provider.

## Next Steps

Expand Down
24 changes: 24 additions & 0 deletions docs/guides/tool-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,23 @@ async with agent.session() as session:
# Providers are cleaned up automatically
```

A custom provider instance owns its lifecycle directly. It can be reused by
sequential sessions, including sessions created by different `Agent` objects.
Overlapping sessions cannot use the same configured custom provider instance;
Stirrup rejects the second session before entering any resources. Create a
separate provider instance for each concurrent owner. The same rule applies to
subclasses of built-in providers.

Exact built-in providers are reconstructed privately for each session, so one
`Agent` can run overlapping sessions without sharing their clients, sandboxes,
temporary directories, or exit stacks. When a custom `ViewImageToolProvider`
subclass explicitly references a configured built-in code backend, the dependent
provider/backend pair follows the custom-provider rule instead: it is reusable
sequentially, and overlap on either configured object is rejected before entry.
Such a pair cannot be used by a `share_parent_exec_env=True` sub-agent because
sharing substitutes the parent's backend; disable sharing or use the exact
built-in `ViewImageToolProvider`.

## Built-in ToolProviders

Stirrup includes several ToolProviders:
Expand Down Expand Up @@ -173,6 +190,13 @@ agent = Agent(

## Error Handling

Session teardown attempts every provider and logger cleanup and shields that
work from surrounding AnyIO cancellation. Custom-resource ownership is released
only after cleanup finishes or raises. If setup or the session body already
failed, cleanup failures are attached to that primary exception rather than
replacing it. A cleanup failure is raised directly only when there was no
earlier failure.

Handle setup/cleanup errors gracefully:

```python
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
"openai>=1.0.0",
"pillow>=10.4.0",
"pydantic>=2.0.0",
"rich>=13.0.0",
"rich>=14.1.0",
"tenacity>=5.0.0",
"trafilatura>=1.9.0",
]
Expand Down Expand Up @@ -62,7 +62,7 @@ dev = [
"mkdocstrings>=0.30.1",
"mkdocstrings-python>=1.19.0",
"pytest>=9.0.0",
"rich>=13.0.0",
"rich>=14.1.0",
"ruff>=0.14.4",
"ty>=0.0.1a32",
]
Expand Down
Loading
Loading