2026-05-08 12:54:00 -04:00
|
|
|
from langchain_core.messages import SystemMessage, HumanMessage
|
|
|
|
|
from state import OrchestratorState
|
|
|
|
|
from models import (
|
|
|
|
|
get_implementer,
|
|
|
|
|
get_reviewer,
|
|
|
|
|
get_researcher,
|
|
|
|
|
get_cross_reviewer,
|
|
|
|
|
get_scanner,
|
|
|
|
|
get_fast_coder,
|
2026-05-15 11:20:28 -04:00
|
|
|
with_retries,
|
2026-05-08 12:54:00 -04:00
|
|
|
)
|
Add memory retriever node (Phase 2)
Plugs the orchestrator into Adam's existing memory store at
~/.claude/projects/-Users-adammoussa-Documents-repositories/memory/. Every
run starts with a top-3 retrieval pass that is then surfaced in the CLI
output and injected as system context into the router and downstream agent.
- retriever.py: load *.md memories (skipping the MEMORY.md index), embed
with text-embedding-3-small, cache to .cache/embeddings.json keyed on
file mtime. Cosine similarity, top-k=3 default. Reads only — never
writes back to the memory store.
- state.py: add `retrieved: list[dict]` to OrchestratorState; relax to
total=False to match LangGraph's partial-update semantics.
- graph.py: new retriever_node wired as START -> retriever -> router.
router_node and connector_node now inject retrieved memories into their
SystemMessage. Retrieval failures are caught and the run continues with
empty memory context (logged).
- agents.py: make_agent_node injects retrieved memories into each agent's
system prompt.
- run.py: prints `[retrieved: name1, name2, name3]` (or `[retrieved: none]`)
before route/result for both --route-only and full-run modes, so bad
retrieval is visible at a glance.
- .gitignore: add .cache/, .pytest_cache/, .ruff_cache/.
Validated: golden-set still 21/21 passing; smoke tests retrieve plausible
memories ("Send a Slack message to ops about the new exec-aide deploy" ->
project_exec_aide, feedback_exec_aide_vip_management, project_seahaven_slack_bot).
2026-05-15 11:36:03 -04:00
|
|
|
from retriever import format_memories_for_prompt
|
2026-05-08 12:54:00 -04:00
|
|
|
|
|
|
|
|
IMPLEMENTER_PROMPT = """You are an implementation agent. You write clean, production-ready code.
|
|
|
|
|
Follow the spec exactly. No over-engineering, no unnecessary abstractions.
|
|
|
|
|
Return the complete implementation with file paths."""
|
|
|
|
|
|
|
|
|
|
REVIEWER_PROMPT = """You are a code review agent. Review code changes for correctness, security, and maintainability.
|
|
|
|
|
|
|
|
|
|
For each issue found, categorize it:
|
|
|
|
|
- BLOCK — Must fix before merge. Security vulnerabilities, data loss risks, broken logic.
|
|
|
|
|
- FIX — Should fix. Bugs, performance issues, missing error handling at boundaries.
|
|
|
|
|
- NIT — Optional. Style, naming, minor improvements.
|
|
|
|
|
- QUESTION — Needs clarification. Intent is unclear.
|
|
|
|
|
|
|
|
|
|
Start with a one-line summary: APPROVE, REQUEST CHANGES, or NEEDS DISCUSSION.
|
|
|
|
|
Then list findings grouped by category (BLOCK > FIX > NIT > QUESTION).
|
|
|
|
|
If the code is fine, say "No issues found." No praise or filler."""
|
|
|
|
|
|
|
|
|
|
RESEARCHER_PROMPT = """You are a research agent. Look up documentation, API references, and technical answers.
|
|
|
|
|
Be concise and cite sources. Return factual information, not opinions."""
|
|
|
|
|
|
|
|
|
|
CROSS_REVIEWER_PROMPT = """You are a cross-family code review agent. You provide an independent review perspective.
|
|
|
|
|
Review the provided code for bugs, security issues, and improvements.
|
|
|
|
|
Categorize findings as BLOCK, FIX, or NIT. Be concise.
|
|
|
|
|
Focus on issues that might be missed by the primary development team."""
|
|
|
|
|
|
|
|
|
|
SCANNER_PROMPT = """You are a large-context analysis agent. You analyze codebases, identify patterns,
|
|
|
|
|
check consistency across files, and find structural issues.
|
|
|
|
|
Summarize findings concisely with file references."""
|
|
|
|
|
|
|
|
|
|
FAST_CODER_PROMPT = """You are a fast coding agent for well-specified tasks.
|
|
|
|
|
Implement exactly what is asked. No extras, no refactoring beyond scope.
|
|
|
|
|
Return complete, working code."""
|
|
|
|
|
|
|
|
|
|
|
2026-05-15 11:20:28 -04:00
|
|
|
# Single source of truth for simple-pattern agents (system + human → result).
|
|
|
|
|
# Connector is handled separately in graph.py because it binds tools.
|
|
|
|
|
AGENTS = {
|
|
|
|
|
"implementer": {
|
|
|
|
|
"model_fn": get_implementer,
|
|
|
|
|
"prompt": IMPLEMENTER_PROMPT,
|
|
|
|
|
"description": "Write new code, add features, fix bugs. Use for any coding task with a clear spec.",
|
|
|
|
|
},
|
|
|
|
|
"reviewer": {
|
|
|
|
|
"model_fn": get_reviewer,
|
|
|
|
|
"prompt": REVIEWER_PROMPT,
|
|
|
|
|
"description": "Review code changes (diffs, PRs) for correctness, security, maintainability. Uses Claude.",
|
|
|
|
|
},
|
|
|
|
|
"researcher": {
|
|
|
|
|
"model_fn": get_researcher,
|
|
|
|
|
"prompt": RESEARCHER_PROMPT,
|
|
|
|
|
"description": "Look up documentation, API references, technical questions. Fast and cheap.",
|
|
|
|
|
},
|
|
|
|
|
"cross_reviewer": {
|
|
|
|
|
"model_fn": get_cross_reviewer,
|
|
|
|
|
"prompt": CROSS_REVIEWER_PROMPT,
|
|
|
|
|
"description": (
|
|
|
|
|
"Independent code review using a different AI model (GPT). Use when you want a "
|
|
|
|
|
"second opinion that catches different blind spots than Claude."
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
"scanner": {
|
|
|
|
|
"model_fn": get_scanner,
|
|
|
|
|
"prompt": SCANNER_PROMPT,
|
|
|
|
|
"description": (
|
|
|
|
|
"Analyze large codebases for patterns, consistency, structural issues. "
|
|
|
|
|
"Uses Gemini's large context window."
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
"fast_coder": {
|
|
|
|
|
"model_fn": get_fast_coder,
|
|
|
|
|
"prompt": FAST_CODER_PROMPT,
|
|
|
|
|
"description": "Quick, bounded coding for crystal-clear specs. Uses DeepSeek. Best for small, well-defined tasks.",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Add memory retriever node (Phase 2)
Plugs the orchestrator into Adam's existing memory store at
~/.claude/projects/-Users-adammoussa-Documents-repositories/memory/. Every
run starts with a top-3 retrieval pass that is then surfaced in the CLI
output and injected as system context into the router and downstream agent.
- retriever.py: load *.md memories (skipping the MEMORY.md index), embed
with text-embedding-3-small, cache to .cache/embeddings.json keyed on
file mtime. Cosine similarity, top-k=3 default. Reads only — never
writes back to the memory store.
- state.py: add `retrieved: list[dict]` to OrchestratorState; relax to
total=False to match LangGraph's partial-update semantics.
- graph.py: new retriever_node wired as START -> retriever -> router.
router_node and connector_node now inject retrieved memories into their
SystemMessage. Retrieval failures are caught and the run continues with
empty memory context (logged).
- agents.py: make_agent_node injects retrieved memories into each agent's
system prompt.
- run.py: prints `[retrieved: name1, name2, name3]` (or `[retrieved: none]`)
before route/result for both --route-only and full-run modes, so bad
retrieval is visible at a glance.
- .gitignore: add .cache/, .pytest_cache/, .ruff_cache/.
Validated: golden-set still 21/21 passing; smoke tests retrieve plausible
memories ("Send a Slack message to ops about the new exec-aide deploy" ->
project_exec_aide, feedback_exec_aide_vip_management, project_seahaven_slack_bot).
2026-05-15 11:36:03 -04:00
|
|
|
def _system_prompt_with_memory(base_prompt: str, retrieved: list[dict] | None) -> str:
|
|
|
|
|
memory_block = format_memories_for_prompt(retrieved or [])
|
|
|
|
|
if not memory_block:
|
|
|
|
|
return base_prompt
|
|
|
|
|
return f"{base_prompt}\n\n{memory_block}"
|
|
|
|
|
|
|
|
|
|
|
2026-05-15 11:20:28 -04:00
|
|
|
def make_agent_node(label: str):
|
|
|
|
|
cfg = AGENTS[label]
|
|
|
|
|
|
|
|
|
|
def node(state: OrchestratorState) -> dict:
|
|
|
|
|
llm = with_retries(cfg["model_fn"]())
|
Add memory retriever node (Phase 2)
Plugs the orchestrator into Adam's existing memory store at
~/.claude/projects/-Users-adammoussa-Documents-repositories/memory/. Every
run starts with a top-3 retrieval pass that is then surfaced in the CLI
output and injected as system context into the router and downstream agent.
- retriever.py: load *.md memories (skipping the MEMORY.md index), embed
with text-embedding-3-small, cache to .cache/embeddings.json keyed on
file mtime. Cosine similarity, top-k=3 default. Reads only — never
writes back to the memory store.
- state.py: add `retrieved: list[dict]` to OrchestratorState; relax to
total=False to match LangGraph's partial-update semantics.
- graph.py: new retriever_node wired as START -> retriever -> router.
router_node and connector_node now inject retrieved memories into their
SystemMessage. Retrieval failures are caught and the run continues with
empty memory context (logged).
- agents.py: make_agent_node injects retrieved memories into each agent's
system prompt.
- run.py: prints `[retrieved: name1, name2, name3]` (or `[retrieved: none]`)
before route/result for both --route-only and full-run modes, so bad
retrieval is visible at a glance.
- .gitignore: add .cache/, .pytest_cache/, .ruff_cache/.
Validated: golden-set still 21/21 passing; smoke tests retrieve plausible
memories ("Send a Slack message to ops about the new exec-aide deploy" ->
project_exec_aide, feedback_exec_aide_vip_management, project_seahaven_slack_bot).
2026-05-15 11:36:03 -04:00
|
|
|
system_prompt = _system_prompt_with_memory(
|
|
|
|
|
cfg["prompt"], state.get("retrieved")
|
|
|
|
|
)
|
2026-05-15 11:20:28 -04:00
|
|
|
response = llm.invoke(
|
|
|
|
|
[
|
Add memory retriever node (Phase 2)
Plugs the orchestrator into Adam's existing memory store at
~/.claude/projects/-Users-adammoussa-Documents-repositories/memory/. Every
run starts with a top-3 retrieval pass that is then surfaced in the CLI
output and injected as system context into the router and downstream agent.
- retriever.py: load *.md memories (skipping the MEMORY.md index), embed
with text-embedding-3-small, cache to .cache/embeddings.json keyed on
file mtime. Cosine similarity, top-k=3 default. Reads only — never
writes back to the memory store.
- state.py: add `retrieved: list[dict]` to OrchestratorState; relax to
total=False to match LangGraph's partial-update semantics.
- graph.py: new retriever_node wired as START -> retriever -> router.
router_node and connector_node now inject retrieved memories into their
SystemMessage. Retrieval failures are caught and the run continues with
empty memory context (logged).
- agents.py: make_agent_node injects retrieved memories into each agent's
system prompt.
- run.py: prints `[retrieved: name1, name2, name3]` (or `[retrieved: none]`)
before route/result for both --route-only and full-run modes, so bad
retrieval is visible at a glance.
- .gitignore: add .cache/, .pytest_cache/, .ruff_cache/.
Validated: golden-set still 21/21 passing; smoke tests retrieve plausible
memories ("Send a Slack message to ops about the new exec-aide deploy" ->
project_exec_aide, feedback_exec_aide_vip_management, project_seahaven_slack_bot).
2026-05-15 11:36:03 -04:00
|
|
|
SystemMessage(content=system_prompt),
|
2026-05-15 11:20:28 -04:00
|
|
|
HumanMessage(content=state["task"]),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
return {"result": response.content, "messages": [response]}
|
|
|
|
|
|
|
|
|
|
return node
|