feat: initial bootstrap — structure, task_brief, blackboard, adapter bases, escalation, prompts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 02:19:14 -04:00
commit eaf7fd8f6f
33 changed files with 2141 additions and 0 deletions

0
adapters/__init__.py Normal file
View File

View File

50
adapters/base/llm.py Normal file
View File

@@ -0,0 +1,50 @@
"""
adapters/base/llm.py
Abstract base class for all LLM adapters.
"""
from abc import ABC, abstractmethod
class LLMAdapter(ABC):
"""
Contract that every LLM provider adapter must fulfil.
Capability strings
------------------
"reasoning-heavy" — tasks requiring deep chain-of-thought (e.g. T1, T2)
"capable" — general-purpose capable model (e.g. T3, T4)
"fast-cheap" — high-volume, low-latency tasks (e.g. T5 quick checks)
"""
@abstractmethod
def complete(self, prompt: str, capability: str, context: dict) -> str:
"""
Send a prompt to the model and return the text response.
Parameters
----------
prompt : The full prompt string (system + user combined or just user).
capability : One of "reasoning-heavy" | "capable" | "fast-cheap".
context : Arbitrary key/value bag passed to the adapter (e.g. system
prompt override, temperature, max_tokens).
Returns
-------
The model's text completion as a plain string.
"""
...
@abstractmethod
def resolve_model(self, capability: str) -> str:
"""
Map a capability string to the concrete model identifier for this provider.
Parameters
----------
capability : One of "reasoning-heavy" | "capable" | "fast-cheap".
Returns
-------
Model identifier string (e.g. "claude-opus-4-6", "gpt-4o").
"""
...

21
adapters/base/notify.py Normal file
View File

@@ -0,0 +1,21 @@
"""
adapters/base/notify.py
Abstract base class for all notification adapters.
"""
from abc import ABC, abstractmethod
class NotifyAdapter(ABC):
"""Contract that every notification provider adapter must fulfil."""
@abstractmethod
def send(self, message: str, context: dict) -> None:
"""
Dispatch a notification.
Parameters
----------
message : Human-readable message text.
context : Arbitrary metadata (e.g. channel, severity, run_id, brief_id).
"""
...

64
adapters/base/runtime.py Normal file
View File

@@ -0,0 +1,64 @@
"""
adapters/base/runtime.py
Abstract base class for all agent-runtime adapters.
"""
from abc import ABC, abstractmethod
class RuntimeAdapter(ABC):
"""
Contract that every agent runtime adapter must fulfil.
A "runtime" is responsible for dispatching a task to an actual agent
(e.g. an OpenClaw worker, a Claude Code sub-agent, a local subprocess)
and retrieving its result.
"""
@abstractmethod
def spawn(self, task: str, capability: str, context: dict) -> str:
"""
Start an agent to work on the given task.
Parameters
----------
task : Natural-language description of the work to perform.
capability : Capability hint — "reasoning-heavy" | "capable" | "fast-cheap".
context : Arbitrary key/value bag (e.g. files, constraints, brief payload).
Returns
-------
A provider-specific agent_id string that can be used to poll for results.
"""
...
@abstractmethod
def get_result(self, agent_id: str, timeout_s: int) -> dict:
"""
Block until the agent completes or the timeout elapses.
Parameters
----------
agent_id : The id returned by spawn().
timeout_s : Maximum seconds to wait before raising TimeoutError.
Returns
-------
A dict containing at minimum:
{
"status": "done" | "failed" | "partial" | "blocked",
"output": <str or dict>,
"artifacts": [...], # optional
}
"""
...
@abstractmethod
def kill(self, agent_id: str) -> None:
"""
Terminate a running agent unconditionally.
Parameters
----------
agent_id : The id returned by spawn().
"""
...

69
adapters/base/vcs.py Normal file
View File

@@ -0,0 +1,69 @@
"""
adapters/base/vcs.py
Abstract base class for all Version Control System adapters.
"""
from abc import ABC, abstractmethod
class VCSAdapter(ABC):
"""Contract that every VCS provider adapter must fulfil."""
@abstractmethod
def create_branch(self, name: str) -> None:
"""
Create a new branch with the given name.
Parameters
----------
name : Branch name (e.g. "feat/webhook-ingestion").
"""
...
@abstractmethod
def commit(self, files: list[str], message: str) -> str:
"""
Stage the given files and create a commit.
Parameters
----------
files : List of file paths to stage (relative to repo root).
message : Commit message.
Returns
-------
The commit SHA as a string.
"""
...
@abstractmethod
def create_pr(self, title: str, body: str, head: str, base: str) -> str:
"""
Open a pull request.
Parameters
----------
title : PR title.
body : PR description / body markdown.
head : Head branch name.
base : Base branch name (e.g. "main").
Returns
-------
The URL of the created pull request.
"""
...
@abstractmethod
def get_pr_status(self, pr_id: str) -> str:
"""
Fetch the current status of a pull request.
Parameters
----------
pr_id : Provider-specific PR identifier (number, node-id, or URL).
Returns
-------
One of: "open" | "merged" | "closed".
"""
...

0
adapters/llm/__init__.py Normal file
View File

44
adapters/llm/anthropic.py Normal file
View File

@@ -0,0 +1,44 @@
"""
adapters/llm/anthropic.py
Anthropic Claude adapter — Phase 2 stub.
TODO (Phase 2):
- Implement complete() using the anthropic SDK (anthropic.Anthropic client).
- Implement resolve_model() by reading config/team.yaml capability_map.
- Handle streaming responses, rate-limit retries, and token counting.
- Support system-prompt injection via context["system_prompt"].
- Map capability → model using the provider's capability_map config.
"""
from __future__ import annotations
from adapters.base.llm import LLMAdapter
class AnthropicAdapter(LLMAdapter):
"""
LLM adapter for Anthropic Claude models.
Reads model configuration from config/team.yaml:
models.provider: anthropic
models.capability_map.reasoning-heavy.anthropic: claude-opus-4-6
models.capability_map.capable.anthropic: claude-sonnet-4-6
models.capability_map.fast-cheap.anthropic: claude-haiku-3-5
"""
def __init__(self, config: dict) -> None:
# TODO (Phase 2): Accept loaded team.yaml config dict.
# Extract API key from environment (ANTHROPIC_API_KEY).
# Initialise the anthropic.Anthropic() client.
raise NotImplementedError("AnthropicAdapter.__init__ is not yet implemented.")
def complete(self, prompt: str, capability: str, context: dict) -> str:
# TODO (Phase 2): Call anthropic client messages.create().
# Use resolve_model(capability) to pick the model.
# Support context keys: system_prompt, max_tokens, temperature.
# Return response text as a plain string.
raise NotImplementedError("AnthropicAdapter.complete is not yet implemented.")
def resolve_model(self, capability: str) -> str:
# TODO (Phase 2): Look up capability in team.yaml capability_map.
# Fall back to "capable" tier model if capability is unknown.
raise NotImplementedError("AnthropicAdapter.resolve_model is not yet implemented.")

View File

View File

@@ -0,0 +1,35 @@
"""
adapters/notify/openclaw.py
OpenClaw notification adapter — Phase 2 stub.
TODO (Phase 2):
- Implement send() to dispatch notifications via the OpenClaw API.
- Support context keys: channel, severity, run_id, brief_id.
- Read endpoint and credentials from environment (OPENCLAW_API_KEY, OPENCLAW_URL).
- Handle rate limiting and delivery retries.
"""
from __future__ import annotations
from adapters.base.notify import NotifyAdapter
class OpenClawNotifyAdapter(NotifyAdapter):
"""
Notification adapter that sends messages via OpenClaw.
Expects environment variables:
OPENCLAW_API_KEY — authentication token
OPENCLAW_URL — base URL for the OpenClaw API (optional, defaults to hosted)
"""
def __init__(self, config: dict) -> None:
# TODO (Phase 2): Accept loaded team.yaml config dict.
# Extract OPENCLAW_API_KEY and OPENCLAW_URL from environment.
# Initialise an HTTP client (e.g. httpx or requests).
raise NotImplementedError("OpenClawNotifyAdapter.__init__ is not yet implemented.")
def send(self, message: str, context: dict) -> None:
# TODO (Phase 2): POST notification payload to OpenClaw API.
# Include message, context (channel, severity, run_id, brief_id).
# Log delivery confirmation or raise on failure.
raise NotImplementedError("OpenClawNotifyAdapter.send is not yet implemented.")

View File

View File

@@ -0,0 +1,51 @@
"""
adapters/runtime/claude_code.py
Claude Code agent runtime adapter — Phase 2 stub.
TODO (Phase 2):
- Implement spawn() to launch a Claude Code sub-agent via the Agent SDK.
- Implement get_result() to await agent completion and parse the output.
- Implement kill() to terminate the sub-agent process or session.
- Map task brief context (files, constraints, artifacts) into the agent's
system prompt and tool context.
- Handle Claude Code tool-use responses and extract structured output.
"""
from __future__ import annotations
from adapters.base.runtime import RuntimeAdapter
class ClaudeCodeRuntimeAdapter(RuntimeAdapter):
"""
Runtime adapter that spawns Claude Code sub-agents for coding tasks.
Used when a TaskBrief has preferred_runtime == "coding_agent".
Expects the Claude Code CLI / Agent SDK to be available in the environment.
Credentials are inherited from the environment (ANTHROPIC_API_KEY).
"""
def __init__(self, config: dict) -> None:
# TODO (Phase 2): Accept loaded team.yaml config dict.
# Validate that Claude Code CLI or SDK is accessible.
# Initialise any agent session management state.
raise NotImplementedError("ClaudeCodeRuntimeAdapter.__init__ is not yet implemented.")
def spawn(self, task: str, capability: str, context: dict) -> str:
# TODO (Phase 2): Launch a Claude Code sub-agent.
# Compose a structured system prompt from task + context.
# Inject relevant files and constraints as tool context.
# Return an agent_id that maps to a running agent session.
raise NotImplementedError("ClaudeCodeRuntimeAdapter.spawn is not yet implemented.")
def get_result(self, agent_id: str, timeout_s: int) -> dict:
# TODO (Phase 2): Await the Claude Code agent session to complete.
# Parse the agent's final message for structured JSON output.
# Return dict with: {"status": ..., "output": ..., "artifacts": [...]}.
# Raise TimeoutError if timeout_s elapses.
raise NotImplementedError("ClaudeCodeRuntimeAdapter.get_result is not yet implemented.")
def kill(self, agent_id: str) -> None:
# TODO (Phase 2): Terminate the Claude Code agent session.
# Clean up any temporary files or session state.
raise NotImplementedError("ClaudeCodeRuntimeAdapter.kill is not yet implemented.")

View File

@@ -0,0 +1,48 @@
"""
adapters/runtime/openclaw.py
OpenClaw agent runtime adapter — Phase 2 stub.
TODO (Phase 2):
- Implement spawn() to submit a task to an OpenClaw worker pool.
- Implement get_result() to poll or subscribe for agent completion.
- Implement kill() to cancel a running OpenClaw agent job.
- Read endpoint and credentials from environment (OPENCLAW_API_KEY, OPENCLAW_URL).
- Map capability hint to an appropriate worker class/queue.
"""
from __future__ import annotations
from adapters.base.runtime import RuntimeAdapter
class OpenClawRuntimeAdapter(RuntimeAdapter):
"""
Runtime adapter that dispatches agent tasks to OpenClaw workers.
Expects environment variables:
OPENCLAW_API_KEY — authentication token
OPENCLAW_URL — base URL for the OpenClaw API
"""
def __init__(self, config: dict) -> None:
# TODO (Phase 2): Accept loaded team.yaml config dict.
# Extract OPENCLAW_API_KEY and OPENCLAW_URL from environment.
# Initialise HTTP client and any job-tracking state.
raise NotImplementedError("OpenClawRuntimeAdapter.__init__ is not yet implemented.")
def spawn(self, task: str, capability: str, context: dict) -> str:
# TODO (Phase 2): Submit task to OpenClaw worker pool.
# Map capability ("reasoning-heavy" | "capable" | "fast-cheap") to
# an appropriate worker queue or model hint.
# Return an agent_id string that can be used to poll for results.
raise NotImplementedError("OpenClawRuntimeAdapter.spawn is not yet implemented.")
def get_result(self, agent_id: str, timeout_s: int) -> dict:
# TODO (Phase 2): Poll or long-poll the OpenClaw API for job completion.
# Raise TimeoutError if timeout_s elapses before the job finishes.
# Return a dict with at minimum: {"status": ..., "output": ..., "artifacts": [...]}.
raise NotImplementedError("OpenClawRuntimeAdapter.get_result is not yet implemented.")
def kill(self, agent_id: str) -> None:
# TODO (Phase 2): Send a cancellation request to the OpenClaw API.
# Silently succeed if the agent has already finished.
raise NotImplementedError("OpenClawRuntimeAdapter.kill is not yet implemented.")

0
adapters/vcs/__init__.py Normal file
View File

51
adapters/vcs/github.py Normal file
View File

@@ -0,0 +1,51 @@
"""
adapters/vcs/github.py
GitHub VCS adapter — Phase 2 stub.
TODO (Phase 2):
- Implement create_branch() using PyGithub or gh CLI subprocess.
- Implement commit() — stage files and push via git subprocess or API.
- Implement create_pr() using GitHub REST API (POST /repos/{owner}/{repo}/pulls).
- Implement get_pr_status() using GET /repos/{owner}/{repo}/pulls/{pull_number}.
- Read repo and credentials from config/team.yaml and environment (GITHUB_TOKEN).
"""
from __future__ import annotations
from adapters.base.vcs import VCSAdapter
class GitHubAdapter(VCSAdapter):
"""
VCS adapter for GitHub repositories.
Expects environment variable GITHUB_TOKEN and config values:
run.repo — SSH or HTTPS clone URL
run.base_branch — default base branch (e.g. "main")
"""
def __init__(self, config: dict) -> None:
# TODO (Phase 2): Accept loaded team.yaml config dict.
# Extract GITHUB_TOKEN from environment.
# Parse owner/repo from config.run.repo.
raise NotImplementedError("GitHubAdapter.__init__ is not yet implemented.")
def create_branch(self, name: str) -> None:
# TODO (Phase 2): Create branch via GitHub API or local git subprocess.
# Use config.run.base_branch as the branch point.
raise NotImplementedError("GitHubAdapter.create_branch is not yet implemented.")
def commit(self, files: list[str], message: str) -> str:
# TODO (Phase 2): Stage files (git add), create commit (git commit), push.
# Return the resulting commit SHA.
raise NotImplementedError("GitHubAdapter.commit is not yet implemented.")
def create_pr(self, title: str, body: str, head: str, base: str) -> str:
# TODO (Phase 2): POST to GitHub API /repos/{owner}/{repo}/pulls.
# Return the HTML URL of the created PR.
raise NotImplementedError("GitHubAdapter.create_pr is not yet implemented.")
def get_pr_status(self, pr_id: str) -> str:
# TODO (Phase 2): GET /repos/{owner}/{repo}/pulls/{number}.
# Map GitHub PR state ("open", "closed") + merged flag to
# our schema: "open" | "merged" | "closed".
raise NotImplementedError("GitHubAdapter.get_pr_status is not yet implemented.")