agentsclimarketplace

Agentspan

Skill DrOlu/agent-skills/skills/agentspan

Comprehensive Agentspan durable workflow orchestration skill for creating, running, scheduling, monitoring, and debugging AI agent workflows using the Agentspan Python SDK and CLI. Covers agents, tools, multi-agent strategies, guardrails, memory, streaming, human-in-the-loop, testing, and production deployment.From its SKILL.md

Install
npx -y skills add DrOlu/agent-skills --skill agentspan

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

26.0 KB, ~6.3k tokens by cl100k_base, as published. Nobody here has run it

Agentspan: Durable AI Agent Workflow Orchestration

Expert AI Agent for Agentspan Workflows Create, run, schedule, monitor, and debug durable AI agent workflows using the Agentspan Python SDK and CLI.

What I Do

I am an expert Agentspan workflow architect and developer. I help you build reliable, scalable, and fault-tolerant AI agent systems using Agentspan's durable execution runtime.

Agentspan guarantees your agent workflows complete successfully, even through:

  • Worker process crashes and restarts
  • Server failures and deployments
  • Long-running operations (hours/days/weeks)
  • Human-in-the-loop approval delays
  • Network partitions and timeouts

Core Capabilities

CapabilityDescription
CreateDesign and implement agents, tools, multi-agent strategies, guardrails, and memory systems
RunExecute agent workflows locally or against a remote server
ScheduleSet up recurring agent runs and batch processing
MonitorStream events, query status, inspect execution history
DebugTroubleshoot failed executions, replay events, analyze errors
TestMock agent behavior, record/replay, pytest integration

When to Use Agentspan

Use CaseUse AgentspanUse DaguUse Temporal
AI agent orchestration✅ Perfect fit❌ Not designed for agents⚠️ Overkill, no agent primitives
Multi-agent pipelines (research→write→edit)✅ Built-in strategies⚠️ Possible but manual⚠️ No agent primitives
Human-in-the-loop approval flows✅ Built-in HITL⚠️ Manual approval gates✅ Signals/queries
LLM tool-calling workflows✅ First-class @tool support❌ No LLM integration❌ No LLM integration
Cron-style task scheduling✅ Agent + CLI✅ Perfect fit✅ Built-in
Simple shell script orchestration❌ Overkill✅ Perfect fit❌ Overkill
Durable business process (multi-day)⚠️ Can work❌ Not designed for it✅ Designed for it
Batch document processing✅ Parallel agents✅ Sequential steps⚠️ Overkill

Quick Start

# Install
pip install agentspan

# Verify setup
agentspan doctor

# Set API key
export OPENAI_API_KEY=sk-...

# Start the server (downloads JAR on first run)
agentspan server start
# UI at http://localhost:6767

# Run your first agent
python hello.py
# hello.py
from agentspan.agents import Agent, AgentRuntime, tool

@tool
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"72°F and sunny in {city}"

agent = Agent(name="weatherbot", model="openai/gpt-4o", tools=[get_weather])

with AgentRuntime() as runtime:
    result = runtime.run(agent, "What's the weather in NYC?")
    result.print_result()

Architecture Overview

┌─────────────────────────────────────────────────────┐
│                  Agentspan Server                     │
│         (port 6767, SQLite or PostgreSQL)            │
│                                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐  │
│  │ Workflow  │  │ Task     │  │ Execution History │  │
│  │ Engine    │  │ Queue    │  │ & State Store     │  │
│  └──────────┘  └──────────┘  └──────────────────┘  │
│         │             │                │              │
└─────────┼─────────────┼────────────────┼───────────┘
          │             │                │
          ▼             ▼                ▼
┌──────────────────────────────────────────────────────┐
│               Python Worker Process                   │
│                                                       │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐             │
│  │ @tool   │  │ http_   │  │ mcp_    │             │
│  │ Python  │  │ tool()  │  │ tool()  │             │
│  │ funcs   │  │ (server)│  │ (server)│             │
│  └─────────┘  └─────────┘  └─────────┘             │
│                                                       │
│  AgentRuntime manages the worker lifecycle            │
└──────────────────────────────────────────────────────┘

Key principle: @tool functions run in your Python worker process (full access to code, libraries, local state). http_tool(), api_tool(), mcp_tool() run server-side (no code to write).

CLI Reference

See references/cli-quick-reference.md for the full CLI command reference.

# Server management
agentspan server start          # Start server (downloads JAR on first run)
agentspan server stop           # Stop server
agentspan server logs            # View server logs

# Credentials
agentspan credentials set KEY value    # Store encrypted credential
agentspan credentials list             # List credential keys
agentspan credentials delete KEY       # Delete a credential

# Agent operations
agentspan agent status <exec-id>       # Check execution status
agentspan agent list                    # List registered agents
agentspan agent run --name my_agent "prompt"  # Run and stream
agentspan agent execution --since 1h    # View execution history

# Diagnostics
agentspan doctor                       # Check system dependencies

# Configuration
agentspan configure --url https://server.example.com

Agent Definition

from agentspan.agents import Agent, AgentRuntime

agent = Agent(
    name="my_agent",                    # Unique name (becomes workflow name)
    model="openai/gpt-4o",              # Provider/model format
    instructions="You are helpful.",    # System prompt (str or callable)
    tools=[...],                        # @tool functions or ToolDefs
    agents=[...],                        # Sub-agents for multi-agent
    strategy="handoff",                  # Multi-agent strategy
    router=None,                         # Router agent or function
    output_type=None,                    # Pydantic BaseModel for structured output
    guardrails=[...],                    # Input/output validation
    memory=None,                         # ConversationMemory or SemanticMemory
    dependencies={...},                  # Injected into ToolContext
    max_turns=25,                        # Max agent loop iterations
    max_tokens=None,                     # LLM max tokens
    temperature=None,                    # LLM temperature
    stop_when=None,                      # Early termination condition
    metadata=None,                       # Arbitrary metadata
)

Tools

@tool — Custom Python Functions

from agentspan.agents import Agent, AgentRuntime, tool

@tool
def get_weather(city: str) -> dict:
    """Get current weather for a city."""
    return {"city": city, "temp": 72, "condition": "Sunny"}

@tool(name="custom_name", approval_required=True, timeout_seconds=60)
def dangerous_action(target: str) -> dict:
    """Do something that requires human approval."""
    return {"done": True}

agent = Agent(name="bot", model="openai/gpt-4o", tools=[get_weather])

ToolContext — Shared State and Dependencies

from agentspan.agents import tool, ToolContext

@tool
def query_database(query: str, context: ToolContext) -> dict:
    """Run a database query."""
    db = context.dependencies["db"]
    user_id = context.dependencies["user_id"]
    return db.execute(query, user=user_id)

agent = Agent(
    name="bot", model="openai/gpt-4o",
    tools=[query_database],
    dependencies={"db": my_database, "user_id": "u-123"},
)

http_tool — HTTP Endpoints (Server-Side)

from agentspan.agents import http_tool

weather = http_tool(
    name="get_weather",
    description="Get weather for a city",
    url="https://api.weather.com/v1/current",
    method="GET",
    headers={"Authorization": "Bearer ${WEATHER_KEY}"},
    input_schema={
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
    credentials=["WEATHER_KEY"],
)

api_tool — OpenAPI Auto-Discovery (Server-Side)

from agentspan.agents import api_tool

stripe = api_tool(
    url="https://api.stripe.com/openapi.json",
    headers={"Authorization": "Bearer ${STRIPE_KEY}"},
    credentials=["STRIPE_KEY"],
    max_tools=20,  # LLM auto-filters to most relevant
)

mcp_tool — MCP Servers (Server-Side)

from agentspan.agents import mcp_tool

github = mcp_tool(
    server_url="http://localhost:3001/mcp",
    name="github",
    description="GitHub operations",
)

Credential Management

# Store credentials (encrypted at rest with AES-256-GCM)
agentspan credentials set GITHUB_TOKEN ghp_xxxxxxxxxxxx
agentspan credentials set STRIPE_API_KEY sk_live_xxxxxxxx
# Option A: Isolated subprocess (credentials as env vars)
@tool(credentials=["GITHUB_TOKEN"])
def list_repos(username: str) -> dict:
    import os
    token = os.environ["GITHUB_TOKEN"]  # Auto-injected

# Option B: In-process (use get_credential)
@tool(isolated=False, credentials=["SEARCH_API_KEY"])
def search(query: str) -> dict:
    from agentspan.agents import get_credential
    key = get_credential("SEARCH_API_KEY")

Multi-Agent Strategies

Sequential Pipeline (a >> b >> c)

researcher = Agent(name="researcher", model="openai/gpt-4o",
                   instructions="Research the topic thoroughly.")
writer = Agent(name="writer", model="openai/gpt-4o",
               instructions="Write an article from the research.")
editor = Agent(name="editor", model="openai/gpt-4o",
               instructions="Polish the article for publication.")

pipeline = researcher >> writer >> editor

with AgentRuntime() as runtime:
    result = runtime.run(pipeline, "AI agents in 2025")

Parallel

market = Agent(name="market", model="openai/gpt-4o",
               instructions="Analyze market size and growth.")
risk = Agent(name="risk", model="openai/gpt-4o",
             instructions="Analyze risks.")
financial = Agent(name="financial", model="openai/gpt-4o",
                  instructions="Analyze financial projections.")

analysis = Agent(
    name="analysis",
    model="openai/gpt-4o",
    agents=[market, risk, financial],
    strategy="parallel",
)

with AgentRuntime() as runtime:
    result = runtime.run(analysis, "Launching an AI healthcare tool")
    print(result.sub_results["market"])
    print(result.sub_results["risk"])
    print(result.sub_results["financial"])

Handoff (Default)

billing = Agent(name="billing", model="openai/gpt-4o",
                instructions="Handle billing inquiries.", tools=[check_balance])
technical = Agent(name="technical", model="openai/gpt-4o",
                  instructions="Handle technical issues.")

support = Agent(
    name="support",
    model="openai/gpt-4o",
    instructions="Route customer requests to the right team.",
    agents=[billing, technical],
    strategy="handoff",
)

Router

# Router agent classifies and dispatches
classifier = Agent(
    name="classifier",
    model="openai/gpt-4o-mini",
    instructions="Classify: billing, technical, or general. Reply with just the category.",
)

support = Agent(
    name="support",
    model="openai/gpt-4o",
    agents=[billing, technical, general],
    strategy="router",
    router=classifier,
)

# Or use a Python function as router
def route(prompt: str) -> str:
    if "bill" in prompt.lower():
        return "billing"
    elif "error" in prompt.lower():
        return "technical"
    return "general"

support = Agent(name="support", agents=[billing, technical, general],
                strategy="router", router=route)

Swarm (Condition-Based Handoffs)

from agentspan.agents import Agent, AgentRuntime, Strategy
from agentspan.agents import TextMentionTermination

triage = Agent(name="triage", model="openai/gpt-4o",
               instructions="Triage requests. Say BILLING or TECH.")
billing = Agent(name="billing", model="openai/gpt-4o",
                instructions="Handle billing.")
technical = Agent(name="technical", model="openai/gpt-4o",
                  instructions="Handle technical issues.")

team = Agent(
    name="support_team",
    model="openai/gpt-4o",
    agents=[triage, billing, technical],
    strategy=Strategy.SWARM,
    handoffs=[
        TextMentionTermination("BILLING", target="billing"),
        TextMentionTermination("TECH", target="technical"),
    ],
)

Strategy Summary

StrategyDescriptionKey Use
handoffLLM chooses which sub-agent handles the requestCustomer support, routing
sequentialSub-agents run in order, output feeds forwardResearch→Write→Edit pipelines
parallelAll sub-agents run concurrentlyMulti-perspective analysis
routerDedicated classifier selects sub-agentCost-optimized routing
swarmCondition-based handoffs between agentsComplex multi-step support
round_robinAgents take turns in fixed rotationDebates, discussions
randomRandom sub-agent each turnDiverse output ensembles
manualHuman selects which agent runs nextHuman-directed workflows

Guardrails

from agentspan.agents import (
    Agent, Guardrail, GuardrailResult, guardrail,
    OnFail, Position, RegexGuardrail, LLMGuardrail,
)

# Custom guardrail function
@guardrail
def no_pii(content: str) -> GuardrailResult:
    """Reject responses containing email addresses."""
    import re
    if re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", content):
        return GuardrailResult(passed=False, message="Response contains PII (email). Remove it.")
    return GuardrailResult(passed=True)

# Regex guardrail
no_ssn = RegexGuardrail(
    patterns=[r"\b\d{3}-\d{2}-\d{4}\b"],
    name="no_ssn",
    message="Do not include SSNs.",
)

# LLM-as-judge guardrail
factual_check = LLMGuardrail(
    model="openai/gpt-4o-mini",
    policy="Is this response factually accurate? Reply YES or NO.",
    on_fail=OnFail.RETRY,
    max_retries=2,
)

agent = Agent(
    name="safe_bot",
    model="openai/gpt-4o",
    guardrails=[
        Guardrail(no_pii, on_fail=OnFail.RETRY, max_retries=3),
        no_ssn,
        factual_check,
    ],
)

OnFail Modes

ModeBehavior
OnFail.RETRYAppend feedback message and re-run the LLM (up to max_retries)
OnFail.RAISEFail the execution immediately
OnFail.FIXReplace output with GuardrailResult.fixed_output
OnFail.HUMANPause for human review (creates a WaitTask)

Input vs Output Guardrails

# Input guardrail — validates user prompt before LLM call
Guardrail(no_jailbreak, position=Position.INPUT, on_fail=OnFail.RAISE)

# Output guardrail — validates LLM response (default)
Guardrail(no_pii, position=Position.OUTPUT, on_fail=OnFail.RETRY)

Memory

ConversationMemory — Chat History

from agentspan.agents import Agent, AgentRuntime, ConversationMemory

memory = ConversationMemory(max_messages=100)

agent = Agent(name="assistant", model="openai/gpt-4o",
              instructions="You are a helpful assistant.", memory=memory)

with AgentRuntime() as runtime:
    result = runtime.run(agent, "My name is Alice.")
    memory.add_user_message("My name is Alice.")
    memory.add_assistant_message(result.output['result'])

    result2 = runtime.run(agent, "What's my name?")
    # "Your name is Alice."

SemanticMemory — Long-Term Knowledge

from agentspan.agents import Agent, AgentRuntime, tool
from agentspan.agents.semantic_memory import SemanticMemory

memory = SemanticMemory(max_results=3)
memory.add("Customer prefers email communication.")
memory.add("Account is on the Enterprise plan since March 2021.")

@tool
def get_context(query: str) -> str:
    """Retrieve relevant context from memory."""
    return memory.get_context(query)

agent = Agent(name="support", model="openai/gpt-4o",
              tools=[get_context])

Human-in-the-Loop (HITL)

from agentspan.agents import Agent, AgentRuntime, tool

@tool(approval_required=True)
def transfer_funds(from_acct: str, to_acct: str, amount: float) -> dict:
    """Transfer funds. Requires human approval."""
    return process_transfer(from_acct, to_acct, amount)

agent = Agent(name="banker", model="openai/gpt-4o", tools=[transfer_funds])

with AgentRuntime() as runtime:
    handle = runtime.start(agent, "Transfer $5000 from checking to savings")
    for event in handle.stream():
        if event.type == "waiting":
            # Human approves or rejects
            handle.approve()  # or handle.reject("Too risky")

CLI approval:

agentspan agent respond <execution-id> --approve
agentspan agent respond <execution-id> --deny --reason "Amount too large"

Streaming

from agentspan.agents import Agent, AgentRuntime

agent = Agent(name="writer", model="openai/gpt-4o")

with AgentRuntime() as runtime:
    for event in runtime.stream(agent, "Write a haiku about Python"):
        match event.type:
            case "thinking":      print(f"Thinking: {event.content}")
            case "tool_call":     print(f"Calling {event.tool_name}({event.args})")
            case "tool_result":   print(f"Result: {event.result}")
            case "handoff":       print(f"Delegating to {event.target}")
            case "waiting":       print("Waiting for human approval...")
            case "guardrail_pass": print(f"Guardrail passed: {event.guardrail_name}")
            case "guardrail_fail": print(f"Guardrail failed: {event.guardrail_name}")
            case "message":        print(f"Message: {event.content}")
            case "error":          print(f"Error: {event.content}")
            case "done":           print(f"\nFinal: {event.output}")

Testing

from agentspan.agents import Agent, tool
from agentspan.agents.testing import mock_run, MockEvent, expect

@tool
def search_web(query: str) -> str:
    """Search the web."""
    return "results"

agent = Agent(name="research_bot", model="openai/gpt-4o", tools=[search_web])

result = mock_run(
    agent,
    "What is agentspan?",
    events=[
        MockEvent.thinking("I should search for information about agentspan."),
        MockEvent.tool_call("search_web", {"query": "agentspan Python agent runtime"}),
        MockEvent.tool_result("search_web", "Agentspan is an open source Python runtime for AI agents."),
        MockEvent.done("Agentspan is an open source Python runtime for building AI agents."),
    ]
)

expect(result).completed().output_contains("Agentspan").used_tool("search_web")

Record and Replay

from agentspan.agents.testing import record, replay

# Record a real execution (calls LLM)
recording = record(agent, "What's the capital of France?")
recording.save("tests/fixtures/capital_query.json")

# Replay it deterministically (no LLM, no server)
result = replay("tests/fixtures/capital_query.json")
expect(result).completed().output_contains("Paris")

Deployment

Local Development (SQLite — Zero Setup)

agentspan server start
# Server at http://localhost:6767
# Data stored in agent-runtime.db

Production (PostgreSQL + Docker Compose)

cd deployment/docker-compose
cp .env.example .env
# Set OPENAI_API_KEY in .env
docker compose up -d

Kubernetes

# Helm chart available in deployment/helm/
# K8s manifests in deployment/k8s/

Configuration

VariableDefaultDescription
SERVER_PORT6767Server port
SPRING_PROFILES_ACTIVEdefault (SQLite)Set to postgres for PostgreSQL
SPRING_DATASOURCE_URLjdbc:sqlite:agent-runtime.dbDatabase URL
AGENTSPAN_SERVER_URLhttp://localhost:6767Server URL for SDK/workers
AGENTSPAN_AUTH_KEYAuth key (required for non-localhost)
AGENTSPAN_AUTH_SECRETAuth secret (required for non-localhost)

LLM Providers

Set environment variables for the providers you need:

ProviderEnv VarModel Prefix
OpenAIOPENAI_API_KEYopenai/
AnthropicANTHROPIC_API_KEYanthropic/
Google GeminiGEMINI_API_KEY + GOOGLE_CLOUD_PROJECTgoogle_gemini/
Azure OpenAIAZURE_OPENAI_API_KEY + endpoint + deploymentazure_openai/
AWS BedrockAWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEYaws_bedrock/
MistralMISTRAL_API_KEYmistral/
OllamaOLLAMA_BASE_URLollama/
DeepSeekDEEPSEEK_API_KEYdeepseek/
Grok/xAIXAI_API_KEYgrok/
CohereCOHERE_API_KEYcohere/

Common Patterns

See references/patterns.md for detailed pattern examples including:

  • Research pipeline (sequential)
  • Support ticket triage (handoff/router)
  • Batch document processing (parallel)
  • Crash and resume (durable execution)
  • Human-in-the-loop approval
  • Guardrail patterns
  • Memory-augmented agents

Key Differences from Alternatives

FeatureAgentspanLangGraphCrewAIAutoGen
Durable execution✅ Server-managed state❌ In-process only❌ In-process❌ In-process
Crash recovery✅ Automatic❌ Lost on crash❌ Lost on crash❌ Lost on crash
Server UI✅ Built-in
Human-in-the-loop✅ First-class⚠️ Manual⚠️ Manual⚠️ Manual
Testing✅ mock_run, record/replay
Multi-agent strategies✅ 8 built-in⚠️ Manual graph✅ Built-in✅ Built-in
LLM provider support✅ 15+✅ Via LangChain
Code execution✅ Docker/Jupyter/Local⚠️ Limited
Credential management✅ Server-side encrypted

Best Practices

  1. Always set max_turns — Prevent runaway agents. Default is 25, lower for simple tasks.
  2. Use guardrails for production — Input and output validation prevents safety issues.
  3. Store credentials on the server — Never hardcode API keys. Use agentspan credentials set.
  4. Test with mock_run — Write deterministic tests before deploying to production.
  5. Use AgentRuntime context manager — Ensures worker processes are properly cleaned up.
  6. Set stop_when for long pipelines — Custom termination conditions prevent unnecessary LLM calls.
  7. Use http_tool and api_tool for external APIs — No worker process needed, runs server-side.
  8. Use SemanticMemory for cross-session context — Better than stuffing conversation history.
  9. Monitor with agentspan agent execution — Track failed executions in production.
  10. Use approval_required=True for destructive tools — Always require human approval for irreversible actions.

Troubleshooting

IssueSolution
Server won't startRun agentspan doctor. Ensure Java 11+ is installed.
Worker can't connectCheck AGENTSPAN_SERVER_URL. Default: http://localhost:6767
LLM API errorsVerify API keys: agentspan doctor. Check rate limits.
Tool timeoutIncrease timeout_seconds in @tool(). Check network connectivity.
Agent runs foreverSet max_turns lower. Add stop_when condition.
Guardrail loopsSet max_retries on guardrails. Use OnFail.RAISE for critical checks.
Worker process diesExecution continues on server. Reconnect with AgentHandle(workflow_id=...).
Port 6767 in useSet SERVER_PORT environment variable or kill existing process.

References

Using Agentspan from RTerm (the agentspan-bridge plugin)

If you run RTerm / neuralOS (the AI-native terminal & ops platform), you don't have to drive Agentspan by hand — the agentspan-bridge plugin (v2.9.9+) wires it into RTerm's agent so any RTerm-managed host can launch durable Agentspan executions.

  • Configure: RTerm Settings → AgentSpanagentspan.serverUrl (default http://localhost:6767) + optional agentspan.authSecretRef (a vault key holding AGENTSPAN_AUTH_KEY/AGENTSPAN_AUTH_SECRET — never inline).
  • RTerm agent tools: agentspan_health, agentspan_run (an AgentConfig or a registered Conductor workflow name → executionId), agentspan_status, agentspan_approve (HITL respond), agentspan_list, agentspan_stop.
  • Trigger + panel: agentspan_execution_failed (fires on FAILED/TERMINATED/TIMED_OUT) and an agentspan-executions live dashboard feed.
  • Why pair them: RTerm gives you the terminal/fleet/governance surface (SSH/WinRM, playbooks, MOP approval, audit, SRE observability); Agentspan gives those operations durability (resume-from-step on crash), plan-execute determinism, and Kafka/SQS/AMQP event triggers. Ask the RTerm agent: "run the disk-cleanup SOP as a durable agent on Agentspan."

See the rterm-gateway skill for the RPC surface and the rterm-backend/neuralos skills for backend setup.

What ships with it: 7 files

47.8 KB alongside SKILL.md, 5 of them executable

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.