Tools for developing and optimizing side effect free background agents
Project description
WINK (Weak Incentives) - Agent Reference
Dense technical guide for AI coding agents. WINK is a Python 3.12+ library for building deterministic, side-effect-free background agents with typed prompts, immutable sessions, and provider-agnostic adapters.
Core Philosophy
The prompt is the agent. Prompts are hierarchical documents where sections bundle instructions and tools together. No separate tool registry; capabilities live in the prompt definition.
Event-driven state. All mutations flow through pure reducers processing typed events. State is immutable and inspectable via snapshots.
Provider-agnostic. Same agent definition works across OpenAI, LiteLLM, and Claude Agent SDK via adapter abstraction.
Guiding Principles
Definition vs Harness
WINK separates what you own from what the runtime provides:
Agent Definition (you own and iterate):
- Prompt - A structured decision procedure, not a loose string
- Tools - The capability surface; the only place side effects occur
- Policies - Enforceable invariants constraining tool use and state
- Feedback - "Are we done?" checks preventing premature termination
Execution Harness (runtime-owned):
- Planning/act loop driving tool calls
- Sandboxing and permissions (filesystem, shell, network)
- Retries, throttling, crash recovery
- Deadlines, budgets, operational guardrails
The harness keeps changing (and increasingly comes from vendor runtimes), but your agent definition should not. WINK makes the definition a first-class artifact you can version, review, test, and port across runtimes.
The Prompt is the Agent
Most frameworks treat prompts as afterthoughts—templates glued to separately registered tool lists. WINK inverts this: you define an agent as a single hierarchical document where each section bundles its own instructions and tools.
PromptTemplate[ReviewResponse]
├── MarkdownSection (guidance)
├── WorkspaceDigestSection ← auto-generated codebase summary
├── MarkdownSection (reference docs, progressive disclosure)
├── PlanningToolsSection ← contributes planning_* tools
│ └── (nested planning docs)
├── VfsToolsSection ← contributes ls/read_file/write_file/...
│ └── (nested filesystem docs)
└── MarkdownSection (user request)
Why this matters:
-
Co-location - Instructions and tools live together. The section that explains filesystem navigation provides the
read_filetool. Documentation can't drift from implementation. -
Progressive disclosure - Nest child sections to reveal advanced capabilities when relevant. The LLM sees numbered, hierarchical headings.
-
Dynamic scoping - Each section has an
enabledpredicate. Disable a section and its entire subtree—tools included—disappears from the prompt. -
Typed all the way down - Sections are parameterized with dataclasses. Placeholders are validated at construction time. Tools declare typed params and results.
Policies Over Workflows
Prefer declarative policies over prescriptive workflows.
A workflow encodes how to accomplish a goal—a predetermined sequence that fractures when encountering unexpected situations. A policy encodes what the goal requires—constraints the agent must satisfy while remaining free to find any valid path.
Workflow (brittle): Policy (flexible):
1. Read the file - File must be read before overwriting
2. Parse the AST - Tests must pass before deployment
3. Generate patch - Sensitive ops require confirmation
4. Write file
5. Run tests
When workflow step 3 fails, the agent is stuck. When a policy check fails, the agent can reason about alternatives that satisfy the constraint.
Key policy characteristics:
- Declarative - State what must be true, not how to make it true
- Composable - Policies combine via conjunction (all must allow)
- Fail-closed - When uncertain, deny; let the agent adapt
- Observable - Explain denials to enable self-correction
Transactional Tools
Tool calls are atomic transactions. When a tool fails:
- Session state rolls back to pre-call state
- Filesystem changes revert
- Error result returned to LLM with guidance
Failed tools don't leave partial state. This enables aggressive retry and recovery strategies.
One Sentence Summary
"You write the agent definition (prompt, tools, policies, feedback); the runtime owns the harness (planning loop, sandboxing, orchestration). WINK keeps the definition portable while runtimes evolve."
Accessing Documentation
After installing WINK, use wink docs to access bundled documentation:
wink docs --reference # This file (API reference)
wink docs --guide # Usage guide with tutorials
wink docs --specs # All specification documents
wink docs --changelog # Release history
# Combine flags for multiple sections
wink docs --reference --specs
# Pipe to clipboard or other tools
wink docs --specs | pbcopy
wink docs --guide | llm "Summarize key concepts"
Available documentation:
--reference- Dense API reference (this file)--guide- Step-by-step usage guide with examples--specs- Design specifications (adapters, sessions, tools, etc.)--changelog- Version history and breaking changes
Module Map
weakincentives # Top-level exports
weakincentives.prompt # Prompt authoring, sections, tools
weakincentives.prompt.overrides # Hash-based prompt iteration
weakincentives.runtime # Session, events, lifecycle, mailbox
weakincentives.runtime.session # Slice ops, reducers, snapshots
weakincentives.runtime.events # Dispatcher, event types
weakincentives.runtime.mailbox # Message queues
weakincentives.adapters # Provider base, config, throttling
weakincentives.adapters.openai # OpenAIAdapter
weakincentives.adapters.litellm # LiteLLMAdapter
weakincentives.adapters.claude_agent_sdk # ClaudeAgentSDKAdapter
weakincentives.contrib.tools # VFS, planning, asteval, podman
weakincentives.contrib.optimizers # WorkspaceDigestOptimizer
weakincentives.contrib.mailbox # RedisMailbox
weakincentives.resources # Dependency injection
weakincentives.filesystem # Filesystem protocol
weakincentives.evals # Evaluation framework
weakincentives.serde # Dataclass serialization
weakincentives.dbc # Design-by-contract decorators
weakincentives.formal # TLA+ specification embedding
weakincentives.skills # Agent Skills support
weakincentives.types # JSON type aliases
Import Cheatsheet
Essential Imports
# Top-level exports
from weakincentives import (
Prompt,
MarkdownSection,
Tool,
ToolContext,
ToolResult,
Budget,
BudgetTracker,
Deadline,
)
# Prompt system
from weakincentives.prompt import (
PromptTemplate,
Section,
SectionVisibility,
ToolExample,
RenderedPrompt,
)
# Runtime
from weakincentives.runtime import (
Session,
InProcessDispatcher,
MainLoop,
MainLoopConfig,
MainLoopRequest,
PromptExecuted,
ToolInvoked,
Snapshot,
append_all,
replace_latest,
upsert_by,
)
# Adapters
from weakincentives.adapters.openai import OpenAIAdapter
from weakincentives.adapters.litellm import LiteLLMAdapter
from weakincentives.adapters import PromptResponse
# Contrib tools
from weakincentives.contrib.tools import (
PlanningToolsSection,
PlanningStrategy,
Plan,
PlanStep,
VfsToolsSection,
HostMount,
VfsPath,
WorkspaceDigestSection,
WorkspaceDigest,
AstevalSection,
PodmanSandboxSection,
PodmanSandboxConfig,
)
# Serde
from weakincentives.serde import dump, parse, schema, clone
# Resources
from weakincentives.resources import Binding, Scope, ResourceRegistry
# Tool policies
from weakincentives.prompt import (
ReadBeforeWritePolicy,
SequentialDependencyPolicy,
PolicyDecision,
PolicyState,
ToolPolicy,
)
# Feedback providers
from weakincentives.prompt import (
DeadlineFeedback,
Feedback,
FeedbackProvider,
FeedbackProviderConfig,
FeedbackTrigger,
)
Claude Agent SDK
from weakincentives.adapters.claude_agent_sdk import (
ClaudeAgentSDKAdapter,
ClaudeAgentSDKClientConfig,
ClaudeAgentWorkspaceSection,
HostMount, # Different from contrib.tools.HostMount
IsolationConfig,
NetworkPolicy,
SandboxConfig,
# Task completion
TaskCompletionChecker,
TaskCompletionContext,
TaskCompletionResult,
PlanBasedChecker,
CompositeChecker,
)
Minimal Working Example
from dataclasses import dataclass
from weakincentives import Prompt, MarkdownSection
from weakincentives.prompt import PromptTemplate
from weakincentives.adapters.openai import OpenAIAdapter
from weakincentives.runtime import Session
@dataclass(slots=True, frozen=True)
class TaskParams:
objective: str
@dataclass(slots=True, frozen=True)
class TaskResult:
summary: str
steps: list[str]
template = PromptTemplate[TaskResult](
ns="myapp",
key="task-agent",
name="task_agent",
sections=(
MarkdownSection[TaskParams](
title="Task",
key="task",
template="Complete: ${objective}",
),
),
)
session = Session()
adapter = OpenAIAdapter(model="gpt-4o-mini")
prompt = Prompt(template).bind(TaskParams(objective="Review the auth module"))
response = adapter.evaluate(prompt, session=session)
result: TaskResult = response.output
Core Patterns
1. Prompt Construction
PromptTemplate is the immutable blueprint. Prompt wraps it with bindings.
from dataclasses import dataclass
from weakincentives import Prompt
from weakincentives.prompt import PromptTemplate, MarkdownSection
@dataclass(slots=True, frozen=True)
class OutputType:
answer: str
# Template: defines structure, typed output
template = PromptTemplate[OutputType](
ns="namespace", # Required: grouping
key="prompt-key", # Required: unique identifier
name="prompt_name", # Optional: display name
sections=(MarkdownSection(title="Task", key="task", template="Do something"),),
)
# Prompt: wraps template, binds parameters
prompt = Prompt(template)
# Render to inspect
rendered = prompt.render()
print(rendered.text) # Markdown content
print(rendered.tools) # Tool tuple
2. Sections
All sections inherit from Section. Most common: MarkdownSection.
from dataclasses import dataclass
from weakincentives.prompt import MarkdownSection, SectionVisibility
@dataclass(slots=True, frozen=True)
class ReviewParams:
focus: str
section = MarkdownSection[ReviewParams](
title="Review Guidelines", # Rendered as heading
key="review-guidelines", # Unique within prompt
template="Focus on: ${focus}", # Template.substitute syntax
default_params=ReviewParams(focus="correctness"),
tools=(), # Tools attached to section
children=(), # Nested sections
visibility=SectionVisibility.FULL, # Or SUMMARY for progressive disclosure
summary="Guidelines available.", # Shown when visibility=SUMMARY
accepts_overrides=True, # Allow prompt overrides
)
Placeholder syntax: ${field_name} from the params dataclass.
Children: Nest sections for hierarchy. Heading levels auto-increment.
3. Tools
Tools are typed with params and result dataclasses.
from dataclasses import dataclass, field
from weakincentives import Tool, ToolContext, ToolResult
@dataclass(slots=True, frozen=True)
class SearchParams:
query: str = field(metadata={"description": "Search query"})
limit: int = field(default=10, metadata={"description": "Max results"})
@dataclass(slots=True, frozen=True)
class SearchResult:
snippets: tuple[str, ...]
def render(self) -> str:
return "\n".join(f"- {s}" for s in self.snippets)
def search_handler(
params: SearchParams,
*,
context: ToolContext,
) -> ToolResult[SearchResult]:
# Access session state
# plan = context.session[Plan].latest()
# Access resources
fs = context.filesystem # Shorthand for context.resources.get_optional(Filesystem)
# Check deadline
if context.deadline and context.deadline.remaining().total_seconds() < 5:
return ToolResult.error("Deadline too close")
# Do work...
snippets = ("result1", "result2")
# Return typed result
return ToolResult.ok(SearchResult(snippets=snippets), message="Found 2 results")
# Create tool
search_tool = Tool[SearchParams, SearchResult](
name="search",
description="Search for content",
handler=search_handler,
)
ToolResult constructors:
ToolResult.ok(value, message="...")- Success with typed valueToolResult.error("message")- Failure, value=None
4. Tool Examples
Provide representative invocations for documentation and few-shot learning.
from dataclasses import dataclass, field
from weakincentives import Tool, ToolContext, ToolResult
from weakincentives.prompt import ToolExample
@dataclass(slots=True, frozen=True)
class LookupParams:
entity_id: str = field(metadata={"description": "ID to fetch"})
@dataclass(slots=True, frozen=True)
class LookupResult:
entity_id: str
url: str
def lookup_handler(
params: LookupParams, *, context: ToolContext
) -> ToolResult[LookupResult]:
result = LookupResult(entity_id=params.entity_id, url="https://example.com/...")
return ToolResult.ok(result, message=f"Fetched {result.entity_id}")
lookup_tool = Tool[LookupParams, LookupResult](
name="lookup_entity",
description="Fetch information for an entity ID.",
handler=lookup_handler,
examples=(
ToolExample(
description="Basic lookup",
input=LookupParams(entity_id="abc-123"),
output=LookupResult(entity_id="abc-123", url="https://example.com/abc-123"),
),
),
)
5. Sessions
Redux-style immutable state container with typed slices.
from weakincentives.runtime import Session, InProcessDispatcher, Snapshot
from weakincentives.runtime import replace_latest, append_all
from weakincentives.contrib.tools import Plan
# Create session
session = Session() # Creates InProcessDispatcher internally
# Query state
plan = session[Plan].latest() # Most recent or None
all_plans = session[Plan].all() # All values as tuple
active = session[Plan].where(lambda p: p.status == "active")
exists = session[Plan].exists() # Boolean
# Mutations (all dispatch events internally)
# session[Plan].seed(initial_plan) # Initialize/replace slice
# session[Plan].append(new_plan) # Append via default reducer
session[Plan].clear() # Clear all
# Snapshots
snapshot = session.snapshot()
session.restore(snapshot)
Built-in reducers (from weakincentives.runtime):
append_all- Always append (default)replace_latest- Keep only most recentupsert_by(key_fn)- Replace by keyreplace_latest_by(key_fn)- Latest per key
6. Adapters
Provider-agnostic evaluation interface.
from weakincentives.adapters.openai import OpenAIAdapter
from weakincentives.adapters.litellm import LiteLLMAdapter
from weakincentives.adapters import PromptResponse
from weakincentives.errors import DeadlineExceededError
# Basic
adapter = OpenAIAdapter(model="gpt-4o")
# LiteLLM (multi-provider)
adapter = LiteLLMAdapter(model="claude-3-sonnet-20240229")
# Evaluate
response = adapter.evaluate(
prompt,
session=session,
deadline=deadline, # Optional
budget=budget, # Optional
budget_tracker=tracker, # Optional
)
# Response fields
response.output # Parsed dataclass (OutputType)
response.text # Raw text
response.prompt_name # Prompt identifier
7. Claude Agent SDK Adapter
Native Claude Code capabilities with hermetic isolation.
import os
from weakincentives.adapters.claude_agent_sdk import (
ClaudeAgentSDKAdapter,
ClaudeAgentSDKClientConfig,
ClaudeAgentWorkspaceSection,
HostMount,
IsolationConfig,
NetworkPolicy,
SandboxConfig,
)
from weakincentives.runtime import Session
session = Session()
# Create workspace (materializes files to temp dir)
workspace = ClaudeAgentWorkspaceSection(
session=session,
mounts=(
HostMount(
host_path="/path/to/project",
mount_path="project",
include_glob=("*.py", "*.md"),
exclude_glob=("__pycache__/*",),
max_bytes=5_000_000,
),
),
allowed_host_roots=("/path/to",),
)
# Configure isolation
adapter = ClaudeAgentSDKAdapter(
model="claude-sonnet-4-5-20250929",
client_config=ClaudeAgentSDKClientConfig(
permission_mode="bypassPermissions", # Auto-approve tools
cwd=str(workspace.temp_dir),
isolation=IsolationConfig(
api_key=os.environ["ANTHROPIC_API_KEY"],
network_policy=NetworkPolicy.no_network(), # API only
sandbox=SandboxConfig(enabled=True),
),
),
)
# Use and cleanup
# response = adapter.evaluate(prompt, session=session)
workspace.cleanup()
Isolation modes:
NetworkPolicy.no_network()- API access onlyNetworkPolicy(allowed_domains=("docs.python.org",))- Specific domainsSandboxConfig(enabled=True)- OS-level sandboxing
8. Tool Policies
Enforce sequential dependencies between tool invocations.
from weakincentives.prompt import (
ReadBeforeWritePolicy,
SequentialDependencyPolicy,
PolicyDecision,
)
# Read-before-write: must read existing files before overwriting
rbw_policy = ReadBeforeWritePolicy(
read_tools=frozenset({"read_file"}),
write_tools=frozenset({"write_file", "edit_file"}),
)
# Sequential dependency: enforce tool ordering
seq_policy = SequentialDependencyPolicy(
dependencies={
"deploy": frozenset({"test", "build"}), # deploy requires test AND build
"build": frozenset({"lint"}), # build requires lint
}
)
Policy behavior:
ReadBeforeWritePolicy: New files can be created freely; existing files must be read first. Tracks read paths in sessionPolicyStateslice.SequentialDependencyPolicy: Tool B requires tool A to have succeeded. Tracks invoked tools in sessionPolicyStateslice.
9. Feedback Providers
Deliver ongoing progress feedback during unattended execution.
from weakincentives.prompt import (
DeadlineFeedback,
FeedbackProviderConfig,
FeedbackTrigger,
)
# Built-in: deadline feedback (warns about remaining time)
deadline_config = FeedbackProviderConfig(
provider=DeadlineFeedback(warning_threshold_seconds=120),
trigger=FeedbackTrigger(every_n_seconds=30), # Check every 30 seconds
)
Trigger conditions (OR'd together):
every_n_calls- Run after N tool calls since last feedbackevery_n_seconds- Run after N seconds elapsed
10. Task Completion Checkers
Verify agents complete all tasks before stopping. Critical for unattended agents.
from weakincentives.adapters.claude_agent_sdk import (
ClaudeAgentSDKAdapter,
ClaudeAgentSDKClientConfig,
PlanBasedChecker,
CompositeChecker,
)
from weakincentives.contrib.tools import Plan
# Plan-based: ensure all plan steps are "done"
adapter = ClaudeAgentSDKAdapter(
client_config=ClaudeAgentSDKClientConfig(
task_completion_checker=PlanBasedChecker(plan_type=Plan),
),
)
Hook integration (Claude Agent SDK):
- PostToolUse Hook: After
StructuredOutput, checker verifies completion. If incomplete, adds feedback to encourage continuation. - Stop Hook: Before allowing stop, checker verifies. If incomplete, signals
needsMoreTurns: True.
11. Contrib Tools
VFS (Virtual Filesystem):
from pathlib import Path
from weakincentives.contrib.tools import VfsToolsSection, HostMount, VfsPath
from weakincentives.runtime import Session
session = Session()
vfs = VfsToolsSection(
session=session,
mounts=(
HostMount(
host_path="./repo",
mount_path=VfsPath(("workspace",)),
include_glob=("*.py",),
exclude_glob=("*.pyc",),
max_bytes=600_000,
),
),
allowed_host_roots=(Path("."),),
)
# Tools: ls, read_file, write_file, edit_file, glob, grep, rm
Planning:
from weakincentives.contrib.tools import (
PlanningToolsSection,
PlanningStrategy,
Plan,
)
from weakincentives.runtime import Session
session = Session()
planning = PlanningToolsSection(
session=session,
strategy=PlanningStrategy.PLAN_ACT_REFLECT,
)
# Tools: planning_setup_plan, planning_add_step, planning_update_step, planning_read_plan
# Query plan state
plan = session[Plan].latest()
if plan:
for step in plan.steps:
print(f"[{step.status}] {step.title}")
12. Resources
Dependency injection with scoped lifecycles.
from weakincentives.resources import Binding, Scope, ResourceRegistry
class Config:
pass
class HTTPClient:
pass
# Build registry
registry = ResourceRegistry.of(
Binding(Config, lambda r: Config()),
Binding(HTTPClient, lambda r: HTTPClient()),
)
Scopes:
Scope.SINGLETON- Once per context (default)Scope.TOOL_CALL- Fresh per tool invocationScope.PROTOTYPE- Fresh every resolution
13. Serialization
from dataclasses import dataclass
from weakincentives.serde import dump, parse, schema, clone
@dataclass(frozen=True)
class MyData:
value: int
data = MyData(value=42)
# Serialize dataclass to dict
d = dump(data)
# Parse dict to dataclass
obj = parse(MyData, d)
# JSON schema
json_schema = schema(MyData)
# Deep clone
copy = clone(data)
14. Design-by-Contract
from weakincentives.dbc import require, ensure, invariant, pure
@require(lambda x: x > 0)
@ensure(lambda result: result >= 0)
def compute(x: int) -> int:
return x * 2
@invariant(lambda self: self.count >= 0)
class Counter:
count: int = 0
@pure # Validates no side effects
def hash_value(x: str) -> int:
return hash(x)
Best Practices
Agent Design
- Plan first: Use
PlanningToolsSectionto structure work before acting - Verify completion: Enable
TaskCompletionCheckerfor unattended agents - Set budgets: Always configure
Budgetwith token limits - Use deadlines: Set wall-clock limits via
Deadline - Provide feedback: Configure
FeedbackProviderfor long-running tasks
Tool Implementation
- Type everything: Use
@dataclass(slots=True, frozen=True)for params/results - Document params: Add
metadata={"description": "..."}to all fields - Handle failures gracefully: Return
ToolResult.error(), don't raise - Check deadlines: Early-exit if
context.deadline.remaining()is low - Access resources properly: Use
context.resources.get(Protocol)
Session Management
- Snapshot before risky operations:
session.snapshot()enables rollback - Use typed slices: Query via
session[Type].latest(), not raw access - Dispatch events: Never mutate state directly; use
session.dispatch() - Register reducers early: Call
session[Type].register()before dispatching
Prompt Authoring
- Keep sections focused: One concern per section
- Use progressive disclosure: Set
visibility=SUMMARYfor verbose content - Attach tools to relevant sections: Tools should be near their instructions
- Apply policies at appropriate level: Section-level for local constraints, prompt-level for global ones
Decision Trees
Which Adapter?
Need Claude Code native tools? → ClaudeAgentSDKAdapter
Need multi-provider support? → LiteLLMAdapter
OpenAI only? → OpenAIAdapter
Which Workspace Tool?
Claude Agent SDK mode? → ClaudeAgentWorkspaceSection
Need shell execution? → PodmanSandboxSection
Standard file ops only? → VfsToolsSection
Which Reducer?
Recording every event? → append_all (default)
Only latest value matters? → replace_latest
Keyed upsert (like cache)? → upsert_by(key_fn)
Complex state transitions? → @reducer decorator on dataclass
Session vs Resource State?
Agent state (plans, results)? → Session slices
Runtime deps (HTTP, DB)? → ResourceRegistry
Filesystem state? → Filesystem via resources
Common Pitfalls
- Forgetting
slots=True, frozen=Trueon dataclasses - breaks serde - Missing
${}in templates - use${field}not{field} - Tool handler signature - must be
(params, *, context: ToolContext) - ToolResult return - use
.ok()or.error(), not raw constructor - Session mutations - all go through
dispatch(), use accessor methods - Resource access outside context - use
with prompt.resources:block - Duplicate tool names - raises
PromptValidationError - Hash mismatch in overrides - stale overrides silently filtered
Event Types
from weakincentives.runtime import (
PromptRendered, # After render, before provider call
PromptExecuted, # After all tools and parsing
ToolInvoked, # After each tool handler
TokenUsage, # Token consumption data
)
Error Hierarchy
WinkError # Base for all WINK errors
├── DeadlineExceededError # Wall-clock limit hit
├── BudgetExceededError # Token limit breached
├── ToolValidationError # Tool params invalid
├── PromptError # Prompt system errors
│ ├── PromptValidationError # Construction failures
│ ├── PromptRenderError # Render failures
│ ├── OutputParseError # Structured output invalid
│ └── VisibilityExpansionRequired # Progressive disclosure request
├── SnapshotRestoreError # Snapshot restore failed
└── TransactionError # Transaction failed
Development Commands
uv sync && ./install-hooks.sh # Setup
make format # ruff format (88-char)
make lint # ruff check --preview
make typecheck # ty + pyright strict
make test # pytest, 100% coverage required
make check # ALL checks - run before commit
make bandit # Security scan
make deptry # Dependency analysis
make pip-audit # Vulnerability scan
File Layout
src/weakincentives/
├── adapters/ # OpenAI, LiteLLM, Claude Agent SDK
│ └── claude_agent_sdk/
├── cli/ # wink CLI
├── contrib/
│ ├── tools/ # Planning, VFS, asteval, podman, workspace digest
│ ├── optimizers/ # WorkspaceDigestOptimizer
│ └── mailbox/ # RedisMailbox
├── dataclasses/ # FrozenDataclass utilities
├── dbc/ # @require, @ensure, @invariant, @pure
├── debug/ # Log collector, session inspection
├── evals/ # Evaluation framework
├── filesystem/ # Filesystem protocol
├── formal/ # TLA+ embedding
├── optimizers/ # Optimizer framework
├── prompt/ # Sections, tools, rendering, overrides
│ └── overrides/ # LocalPromptOverridesStore
├── resources/ # DI with Binding, Scope
├── runtime/
│ ├── events/ # Dispatcher, event types
│ ├── mailbox/ # Message queue protocol
│ └── session/ # Session, slices, reducers
│ └── slices/ # MemorySlice, JsonlSlice
├── serde/ # dump, parse, schema, clone
├── skills/ # Agent Skills support
└── types/ # JSONValue, type aliases
Key Specs
Read before modifying related code:
| Spec | Topic |
|---|---|
specs/PROMPTS.md |
Prompt system, composition, overrides |
specs/SESSIONS.md |
Session lifecycle, events, budgets |
specs/TOOLS.md |
Tool registration, planning tools, policies |
specs/FEEDBACK_PROVIDERS.md |
Trajectory feedback, stall detection |
specs/TASK_COMPLETION.md |
Task completion verification |
specs/ADAPTERS.md |
Provider adapters, throttling |
specs/CLAUDE_AGENT_SDK.md |
SDK adapter, isolation, MCP |
specs/WORKSPACE.md |
VFS, Podman, asteval |
specs/DBC.md |
Design-by-contract patterns |
specs/RESOURCE_REGISTRY.md |
Dependency injection |
specs/MAIN_LOOP.md |
MainLoop orchestration |
specs/MAILBOX.md |
Message queue abstraction |
Quick Reference
PromptTemplate
PromptTemplate[OutputT](
ns: str, # Namespace (required)
key: str, # Unique key (required)
name: str | None, # Display name
sections: tuple[Section], # Ordered sections
)
MarkdownSection
MarkdownSection[ParamsT](
title: str, # Heading text
key: str, # Unique key
template: str, # ${field} syntax
default_params: ParamsT,
tools: tuple[Tool],
children: tuple[Section],
visibility: SectionVisibility,
summary: str, # For SUMMARY visibility
enabled: Callable[[ParamsT], bool],
accepts_overrides: bool,
)
Tool
Tool[ParamsT, ResultT](
name: str, # ^[a-z0-9_-]{1,64}$
description: str, # 1-200 chars
handler: ToolHandler,
examples: tuple[ToolExample],
accepts_overrides: bool,
)
# Handler signature
def handler(params: ParamsT, *, context: ToolContext) -> ToolResult[ResultT]
ToolContext
context.session # Session
context.deadline # Deadline | None
context.budget_tracker # BudgetTracker | None
context.resources # ScopedResourceContext (from prompt)
context.filesystem # Filesystem | None (shorthand)
context.prompt # PromptProtocol
context.rendered_prompt # RenderedPromptProtocol | None
context.adapter # ProviderAdapterProtocol
Session
session[T].latest() # T | None
session[T].all() # tuple[T, ...]
session[T].where(pred) # tuple[T, ...]
session[T].exists() # bool
session[T].seed(value) # Initialize slice
session[T].append(value) # Dispatch to reducers
session[T].clear() # Clear slice
session[T].register(E, reducer) # Register reducer
session.dispatch(event) # Broadcast dispatch
session.snapshot() # Snapshot
session.restore(snap) # Restore from snapshot
Budget
Budget(
deadline: Deadline | None,
max_total_tokens: int | None,
max_input_tokens: int | None,
max_output_tokens: int | None,
)
tracker = BudgetTracker(budget)
tracker.record_cumulative(eval_id, usage)
tracker.check() # Raises BudgetExceededError
Example: Complete Agent
See code_reviewer_example.py for production patterns:
- Structured output types
- VFS/Planning tool sections
- MainLoop implementation
- Event subscription
- Prompt overrides
- Claude Agent SDK mode
Alpha Status
All APIs may change without backward compatibility. No deprecation warnings; unused code is deleted completely.
License
Apache License 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file weakincentives-0.20.0.tar.gz.
File metadata
- Download URL: weakincentives-0.20.0.tar.gz
- Upload date:
- Size: 8.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c71c4f43d161fccb79790d0564cac5e6231c7a241db0453edc545c32b141a017
|
|
| MD5 |
47c865f92f8a779b44f8aa398d85516a
|
|
| BLAKE2b-256 |
21ac927506f7033f51c804a99732034339e92b5d32e78694be3c87bc21f636ba
|
Provenance
The following attestation bundles were made for weakincentives-0.20.0.tar.gz:
Publisher:
release.yml on weakincentives/weakincentives
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weakincentives-0.20.0.tar.gz -
Subject digest:
c71c4f43d161fccb79790d0564cac5e6231c7a241db0453edc545c32b141a017 - Sigstore transparency entry: 832014837
- Sigstore integration time:
-
Permalink:
weakincentives/weakincentives@2bd0a4ca068abc139273af8953791e4dd0f3dec4 -
Branch / Tag:
refs/tags/v0.20.0 - Owner: https://github.com/weakincentives
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2bd0a4ca068abc139273af8953791e4dd0f3dec4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file weakincentives-0.20.0-py3-none-any.whl.
File metadata
- Download URL: weakincentives-0.20.0-py3-none-any.whl
- Upload date:
- Size: 647.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfec873a2d2e9a16bff5a150cd693db0dcde448e5d2e0f9d69db36bd0541b64e
|
|
| MD5 |
eb5298e5ea67de334fe70d7af38575ba
|
|
| BLAKE2b-256 |
c9c6bc0c33dbdadc5df00a248fe74011b7ac864787ae552334e795ec83455660
|
Provenance
The following attestation bundles were made for weakincentives-0.20.0-py3-none-any.whl:
Publisher:
release.yml on weakincentives/weakincentives
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weakincentives-0.20.0-py3-none-any.whl -
Subject digest:
bfec873a2d2e9a16bff5a150cd693db0dcde448e5d2e0f9d69db36bd0541b64e - Sigstore transparency entry: 832014841
- Sigstore integration time:
-
Permalink:
weakincentives/weakincentives@2bd0a4ca068abc139273af8953791e4dd0f3dec4 -
Branch / Tag:
refs/tags/v0.20.0 - Owner: https://github.com/weakincentives
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2bd0a4ca068abc139273af8953791e4dd0f3dec4 -
Trigger Event:
release
-
Statement type: