Capability-set targeting + Claude Code headless provider + resilience fixes
Capability targeting (new feature): - loop --capability <SET|ALL> scopes which capability library the loop reasons against. data/capability_sets/<name>.json (same schema as capabilities.json) holds one-off targeted products; drop a file in and pass its name, no code changes. First set: agentminder (Broadcom AgentMinder, agentic-AI identity). - capabilities.set_active_set() swaps the active library; keyword pre-filter is bypassed for small targeted sets so every signal is evaluated. New 'capability-sets' command lists them; run banner shows the active set. LLM provider — the '429' was a disguised policy block: - Anthropic rejects Claude Code OAuth tokens on /v1/messages for non-Claude-Code traffic with a fake rate_limit_error (no anthropic-ratelimit-* headers). New 'claude-cli' provider runs inference through the sanctioned headless path (claude -p --append-system-prompt --model), now the default provider. - OAuth keychain token auto-refresh via headless claude when expired. Resilience: - STATE_DIR is overridable (macOS endpoint-security locked output/state via com.apple.macl); load_memory tolerates PermissionError; exa usage counter tolerates malformed files; run.py handles Ctrl-C cleanly (no traceback). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,12 @@
|
||||
The design uses a vector ANN pre-filter to cut LLM calls ~60%. For the MVP we
|
||||
approximate that with keyword overlap scoring (no embedding service required).
|
||||
The interface is the same: given a signal, return candidate capabilities.
|
||||
|
||||
CAPABILITY SETS: the loop can target a subset of the portfolio at runtime.
|
||||
- "ALL" (default): data/capabilities.json — the full portfolio.
|
||||
- "<name>": data/capability_sets/<name>.json — a one-off targeted set
|
||||
(same schema). Add new products by dropping another file in that folder and
|
||||
passing `loop --capability <name>`.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -12,10 +18,43 @@ from functools import lru_cache
|
||||
|
||||
from . import config
|
||||
|
||||
# Selected via set_active_set() before the loop runs; "ALL" = full portfolio.
|
||||
_active_set = "ALL"
|
||||
|
||||
|
||||
def available_sets() -> list[str]:
|
||||
sets_dir = config.DATA_DIR / "capability_sets"
|
||||
names = sorted(p.stem for p in sets_dir.glob("*.json")) if sets_dir.exists() else []
|
||||
return ["ALL"] + names
|
||||
|
||||
|
||||
def set_active_set(name: str) -> None:
|
||||
"""Select which capability library the loop reasons against."""
|
||||
global _active_set
|
||||
name = (name or "ALL").strip()
|
||||
if name.upper() == "ALL":
|
||||
_active_set = "ALL"
|
||||
else:
|
||||
path = config.DATA_DIR / "capability_sets" / f"{name}.json"
|
||||
if not path.exists():
|
||||
raise SystemExit(
|
||||
f"Unknown capability set '{name}'. Available: {', '.join(available_sets())}"
|
||||
)
|
||||
_active_set = name
|
||||
load_library.cache_clear()
|
||||
|
||||
|
||||
def active_set() -> str:
|
||||
return _active_set
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def load_library() -> dict:
|
||||
return json.loads((config.DATA_DIR / "capabilities.json").read_text())
|
||||
if _active_set == "ALL":
|
||||
return json.loads((config.DATA_DIR / "capabilities.json").read_text())
|
||||
return json.loads(
|
||||
(config.DATA_DIR / "capability_sets" / f"{_active_set}.json").read_text()
|
||||
)
|
||||
|
||||
|
||||
def capability_by_id(cap_id: str) -> dict | None:
|
||||
@@ -33,7 +72,13 @@ def prefilter(signal_content: str, top_k: int = 3) -> list[dict]:
|
||||
"""Return capabilities whose keywords overlap the signal, best first.
|
||||
|
||||
Stand-in for the vector ANN pre-filter in the design (section 5.2).
|
||||
In a targeted set (<=5 capabilities) the pre-filter is skipped: the whole
|
||||
point of a targeted run is to evaluate EVERY signal against the chosen
|
||||
product, and the LLM prompt stays small anyway.
|
||||
"""
|
||||
caps = load_library()["capabilities"]
|
||||
if _active_set != "ALL" and len(caps) <= 5:
|
||||
return caps
|
||||
sig_tokens = _tokens(signal_content)
|
||||
scored: list[tuple[float, dict]] = []
|
||||
for cap in load_library()["capabilities"]:
|
||||
|
||||
Reference in New Issue
Block a user