Harness foundation: INTENT, ADR-001, architecture, prototype adoption
- INTENT.md: three-layer model (blueprint/instance/harness), single shared runtime, never-become boundaries - ADR-001 (accepted): DEC-2026-002 resolution — one harness repo for all projects; instances are declarative state in consuming repos - docs/architecture.md: components, contracts (manifest, tool profiles, completion events, credential lanes), deployment shape - agent_harness/: executor-worker prototype adopted and renamed (6/6 tests green); HARNESS-WP-0001 initial workplan (7 tasks) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
87ae78c56a
commit
cfc1b75157
18 changed files with 1204 additions and 1 deletions
83
agent_harness/adapter.py
Normal file
83
agent_harness/adapter.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"""Agentic Claude Code adapter.
|
||||
|
||||
llm-connect's ClaudeCodeAdapter is a text-generation adapter (`claude
|
||||
--print`, no working directory, no tool grants). An executor run needs an
|
||||
*agentic* session: file edits and git commits inside the target repo, under
|
||||
a hard tool allow-list. This adapter subclasses it, keeping the llm-connect
|
||||
LLMAdapter interface so a hosted adapter can be swapped in later, and adds:
|
||||
|
||||
- cwd pinned to the target repo
|
||||
- --permission-mode acceptEdits
|
||||
- an allow-list identical in spirit to binky-control/scripts/rhythm-session.sh:
|
||||
read/edit tools plus local git add/commit/status/log/diff — no push, no
|
||||
network, no arbitrary shell.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from llm_connect.claude_code import ClaudeCodeAdapter
|
||||
from llm_connect.exceptions import LLMSubprocessError, LLMTimeoutError
|
||||
from llm_connect.models import LLMResponse, RunConfig
|
||||
|
||||
ALLOWED_TOOLS = (
|
||||
"Read,Write,Edit,Glob,Grep,"
|
||||
"Bash(git add:*),Bash(git commit:*),Bash(git status),"
|
||||
"Bash(git log:*),Bash(git diff:*),Bash(date:*),Bash(ls:*)"
|
||||
)
|
||||
|
||||
|
||||
class AgenticClaudeCodeAdapter(ClaudeCodeAdapter):
|
||||
def __init__(self, workdir: Path, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._workdir = workdir
|
||||
|
||||
def _build_command(self, config: RunConfig) -> list[str]:
|
||||
cmd = [
|
||||
self._cli_path,
|
||||
"--print",
|
||||
"--permission-mode",
|
||||
"acceptEdits",
|
||||
"--allowedTools",
|
||||
ALLOWED_TOOLS,
|
||||
]
|
||||
if self._model:
|
||||
cmd.extend(["--model", self._model])
|
||||
return cmd
|
||||
|
||||
def execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
|
||||
self._preflight_budget(config)
|
||||
cmd = self._build_command(config)
|
||||
timeout = config.timeout_seconds or self._config.timeout_seconds
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
input=prompt,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
cwd=self._workdir,
|
||||
)
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
raise LLMTimeoutError(
|
||||
f"claude CLI timed out after {timeout}s", cause=exc
|
||||
) from exc
|
||||
if result.returncode != 0:
|
||||
raise LLMSubprocessError(
|
||||
f"claude CLI exited with code {result.returncode}",
|
||||
return_code=result.returncode,
|
||||
stderr=result.stderr,
|
||||
)
|
||||
return LLMResponse(
|
||||
content=result.stdout,
|
||||
model=self._model or "claude-code-cli",
|
||||
usage={},
|
||||
finish_reason="stop",
|
||||
metadata={
|
||||
"provider": "claude-code-agentic",
|
||||
"cli_path": self._cli_path,
|
||||
"workdir": str(self._workdir),
|
||||
},
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue