cred update in quickstart, sample agent check before agent run, agent has welcome msg

This commit is contained in:
bryan
2026-02-10 11:27:17 -08:00
parent a12163d63f
commit 21e7554cdb
13 changed files with 137 additions and 18 deletions
+2 -1
View File
@@ -669,6 +669,7 @@ AskUserQuestion(questions=[{
|------|---------------|
| `config.py` | `AgentMetadata.name` — the display name shown in TUI agent selection |
| `config.py` | `AgentMetadata.description` — agent description |
| `config.py` | `AgentMetadata.intro_message` — greeting shown to user when TUI loads |
| `agent.py` | Module docstring (line 1) |
| `agent.py` | `class OldNameAgent:``class NewNameAgent:` |
| `agent.py` | `GraphSpec(id="old-name-graph")``GraphSpec(id="new-name-graph")` — shown in TUI status bar |
@@ -735,7 +736,7 @@ mcp__agent-builder__export_graph()
**THEN write the Python package files** using the exported data. Create these files in `exports/AGENT_NAME/`:
1. `config.py` - Runtime configuration with model settings
1. `config.py` - Runtime configuration with model settings and `AgentMetadata` (including `intro_message` — the greeting shown when TUI loads)
2. `nodes/__init__.py` - All NodeSpec definitions
3. `agent.py` - Goal, edges, graph config, and agent class
4. `__init__.py` - Package exports
@@ -16,6 +16,11 @@ class AgentMetadata:
"multi-source search, quality evaluation, and synthesis - with TUI conversation "
"at key checkpoints for user guidance and feedback."
)
intro_message: str = (
"Hi! I'm your deep research assistant. Tell me a topic and I'll investigate it "
"thoroughly — searching multiple sources, evaluating quality, and synthesizing "
"a comprehensive report. What would you like me to research?"
)
metadata = AgentMetadata()
+9 -3
View File
@@ -460,9 +460,14 @@ result: HealthCheckResult = check_credential_health("hubspot", token_value)
The local encrypted store requires `HIVE_CREDENTIAL_KEY` to encrypt/decrypt credentials.
- If the user doesn't have one, `EncryptedFileStorage` will auto-generate one and log it
- The user MUST persist this key (e.g., in `~/.bashrc` or a secrets manager)
- The user MUST persist this key (e.g., in `~/.bashrc`/`~/.zshrc` or a secrets manager)
- Without this key, stored credentials cannot be decrypted
- This is the ONLY secret that should live in `~/.bashrc` or environment config
**Shell config rule:** Only TWO keys belong in shell config (`~/.zshrc`/`~/.bashrc`):
- `HIVE_CREDENTIAL_KEY` — encryption key for the credential store
- `ADEN_API_KEY` — Aden platform auth key (needed before the store can sync)
All other API keys (Brave, Google, HubSpot, etc.) must go in the encrypted store only. **Never offer to add them to shell config.**
If `HIVE_CREDENTIAL_KEY` is not set:
@@ -475,6 +480,7 @@ If `HIVE_CREDENTIAL_KEY` is not set:
- **NEVER** log, print, or echo credential values in tool output
- **NEVER** store credentials in plaintext files, git-tracked files, or agent configs
- **NEVER** hardcode credentials in source code
- **NEVER** offer to save API keys to shell config (`~/.zshrc`/`~/.bashrc`) — the **only** keys that belong in shell config are `HIVE_CREDENTIAL_KEY` and `ADEN_API_KEY`. All other credentials (Brave, Google, HubSpot, GitHub, Resend, etc.) go in the encrypted store only.
- **ALWAYS** use `SecretStr` from Pydantic when handling credential values in Python
- **ALWAYS** use the local encrypted store (`~/.hive/credentials`) for persistence
- **ALWAYS** run health checks before storing credentials (when possible)
@@ -605,7 +611,7 @@ All credentials are now configured:
│ │
│ 1. RUN YOUR AGENT: │
│ │
PYTHONPATH=core:exports python -m research-agent tui
hive tui
│ │
│ 2. IF YOU ENCOUNTER ISSUES, USE THE DEBUGGER: │
│ │
+8
View File
@@ -336,6 +336,7 @@ def cmd_run(args: argparse.Namespace) -> int:
"""Run an exported agent."""
import logging
from framework.credentials.models import CredentialError
from framework.runner import AgentRunner
# Set logging level (quiet by default for cleaner output)
@@ -376,6 +377,9 @@ def cmd_run(args: argparse.Namespace) -> int:
model=args.model,
enable_tui=True,
)
except CredentialError as e:
print(f"\n{e}", file=sys.stderr)
return
except Exception as e:
print(f"Error loading agent: {e}")
return
@@ -1136,6 +1140,7 @@ def cmd_tui(args: argparse.Namespace) -> int:
"""Browse agents and launch the interactive TUI dashboard."""
import logging
from framework.credentials.models import CredentialError
from framework.runner import AgentRunner
from framework.tui.app import AdenTUI
@@ -1187,6 +1192,9 @@ def cmd_tui(args: argparse.Namespace) -> int:
model=args.model,
enable_tui=True,
)
except CredentialError as e:
print(f"\n{e}", file=sys.stderr)
return
except Exception as e:
print(f"Error loading agent: {e}")
return
+85
View File
@@ -272,6 +272,7 @@ class AgentRunner:
storage_path: Path | None = None,
model: str | None = None,
enable_tui: bool = False,
intro_message: str = "",
):
"""
Initialize the runner (use AgentRunner.load() instead).
@@ -284,6 +285,7 @@ class AgentRunner:
storage_path: Path for runtime storage (defaults to temp)
model: Model to use (reads from agent config or ~/.hive/configuration.json if None)
enable_tui: If True, forces use of AgentRuntime with EventBus
intro_message: Optional greeting shown to user on TUI load
"""
self.agent_path = agent_path
self.graph = graph
@@ -291,6 +293,7 @@ class AgentRunner:
self.mock_mode = mock_mode
self.model = model or self._resolve_default_model()
self.enable_tui = enable_tui
self.intro_message = intro_message
# Set up storage
if storage_path:
@@ -319,6 +322,10 @@ class AgentRunner:
self._agent_runtime: AgentRuntime | None = None
self._uses_async_entry_points = self.graph.has_async_entry_points()
# Validate credentials before spawning MCP servers.
# Fails fast with actionable guidance — no MCP noise on screen.
self._validate_credentials()
# Auto-discover tools from tools.py
tools_path = agent_path / "tools.py"
if tools_path.exists():
@@ -329,6 +336,74 @@ class AgentRunner:
if mcp_config_path.exists():
self._load_mcp_servers_from_config(mcp_config_path)
def _validate_credentials(self) -> None:
"""Check that required credentials are available before spawning MCP servers.
Raises CredentialError with actionable guidance if any are missing.
Uses graph node specs + CREDENTIAL_SPECS no tool registry needed.
"""
required_tools: set[str] = set()
for node in self.graph.nodes:
if node.tools:
required_tools.update(node.tools)
if not required_tools:
return
try:
from aden_tools.credentials import CREDENTIAL_SPECS
from framework.credentials import CredentialStore
from framework.credentials.storage import (
CompositeStorage,
EncryptedFileStorage,
EnvVarStorage,
)
except ImportError:
return # aden_tools not installed, skip check
# Build credential store (same logic as validate())
env_mapping = {
(spec.credential_id or name): spec.env_var for name, spec in CREDENTIAL_SPECS.items()
}
storages: list = [EnvVarStorage(env_mapping=env_mapping)]
if os.environ.get("HIVE_CREDENTIAL_KEY"):
storages.insert(0, EncryptedFileStorage())
if len(storages) == 1:
storage = storages[0]
else:
storage = CompositeStorage(primary=storages[0], fallbacks=storages[1:])
store = CredentialStore(storage=storage)
# Build tool→credential mapping and check
tool_to_cred: dict[str, str] = {}
for cred_name, spec in CREDENTIAL_SPECS.items():
for tool_name in spec.tools:
tool_to_cred[tool_name] = cred_name
missing: list[str] = []
checked: set[str] = set()
for tool_name in sorted(required_tools):
cred_name = tool_to_cred.get(tool_name)
if cred_name is None or cred_name in checked:
continue
checked.add(cred_name)
spec = CREDENTIAL_SPECS[cred_name]
cred_id = spec.credential_id or cred_name
if spec.required and not store.is_available(cred_id):
affected = sorted(t for t in required_tools if t in spec.tools)
entry = f" {spec.env_var} for {', '.join(affected)}"
if spec.help_url:
entry += f"\n Get it at: {spec.help_url}"
missing.append(entry)
if missing:
from framework.credentials.models import CredentialError
lines = ["Missing required credentials:\n"]
lines.extend(missing)
lines.append("\nTo fix: run /hive-credentials in Claude Code.")
raise CredentialError("\n".join(lines))
@staticmethod
def _import_agent_module(agent_path: Path):
"""Import an agent package from its directory path.
@@ -420,6 +495,12 @@ class AgentRunner:
hive_config = get_hive_config()
max_tokens = hive_config.get("llm", {}).get("max_tokens", DEFAULT_MAX_TOKENS)
# Read intro_message from agent metadata (shown on TUI load)
agent_metadata = getattr(agent_module, "metadata", None)
intro_message = ""
if agent_metadata and hasattr(agent_metadata, "intro_message"):
intro_message = agent_metadata.intro_message
# Build GraphSpec from module-level variables
graph = GraphSpec(
id=f"{agent_path.name}-graph",
@@ -442,6 +523,7 @@ class AgentRunner:
storage_path=storage_path,
model=model,
enable_tui=enable_tui,
intro_message=intro_message,
)
# Fallback: load from agent.json (legacy JSON-based agents)
@@ -762,6 +844,9 @@ class AgentRunner:
checkpoint_config=checkpoint_config,
)
# Pass intro_message through for TUI display
self._agent_runtime.intro_message = self.intro_message
async def run(
self,
input_data: dict | None = None,
+9 -4
View File
@@ -638,10 +638,15 @@ class ChatRepl(Vertical):
# Check for resumable sessions
self._check_and_show_resumable_sessions()
history.write(
"[dim]Quick start: /sessions to see previous sessions, "
"/pause to pause execution[/dim]\n"
)
# Show agent intro message if available
intro = getattr(self.runtime, "intro_message", "")
if intro:
history.write(f"[bold blue]Agent:[/bold blue] {intro}\n")
else:
history.write(
"[dim]Quick start: /sessions to see previous sessions, "
"/pause to pause execution[/dim]\n"
)
def _check_and_show_resumable_sessions(self) -> None:
"""Check for non-terminated sessions and prompt user."""
@@ -241,9 +241,7 @@ class DeepResearchAgent:
session_state=session_state,
)
async def run(
self, context: dict, session_state=None
) -> ExecutionResult:
async def run(self, context: dict, session_state=None) -> ExecutionResult:
"""Run the agent (convenience method for single execution)."""
await self.start()
try:
@@ -16,6 +16,11 @@ class AgentMetadata:
"multi-source search, quality evaluation, and synthesis - with TUI conversation "
"at key checkpoints for user guidance and feedback."
)
intro_message: str = (
"Hi! I'm your deep research assistant. Tell me a topic and I'll investigate it "
"thoroughly — searching multiple sources, evaluating quality, and synthesizing "
"a comprehensive report. What would you like me to research?"
)
metadata = AgentMetadata()
@@ -225,9 +225,7 @@ class TechNewsReporterAgent:
session_state=session_state,
)
async def run(
self, context: dict, session_state=None
) -> ExecutionResult:
async def run(self, context: dict, session_state=None) -> ExecutionResult:
"""Run the agent (convenience method for single execution)."""
await self.start()
try:
@@ -16,6 +16,11 @@ class AgentMetadata:
"summarize key stories, and produce a well-organized report "
"for the user to read."
)
intro_message: str = (
"Hi! I'm your tech news reporter. I'll search the web for the latest technology "
"and AI news, then put together a clear summary for you. What topic or area "
"should I cover?"
)
metadata = AgentMetadata()
+1 -3
View File
@@ -240,9 +240,7 @@ class TwitterOutreachAgent:
session_state=session_state,
)
async def run(
self, context: dict, session_state=None
) -> ExecutionResult:
async def run(self, context: dict, session_state=None) -> ExecutionResult:
"""Run the agent (convenience method for single execution)."""
await self.start()
try:
@@ -15,6 +15,11 @@ class AgentMetadata:
"Reads a target's Twitter/X profile, crafts a personalized outreach email "
"referencing their specific activity, and sends it after user approval."
)
intro_message: str = (
"Hi! I can help you with personalized Twitter outreach. Give me a Twitter/X "
"handle and I'll analyze their profile, then craft a tailored outreach email "
"for your approval."
)
metadata = AgentMetadata()
+1 -1
View File
@@ -878,7 +878,7 @@ if [ -n "$HIVE_CREDENTIAL_KEY" ]; then
# Initialize the metadata index
if [ ! -f "$HIVE_CRED_DIR/metadata/index.json" ]; then
echo '{}' > "$HIVE_CRED_DIR/metadata/index.json"
echo '{"credentials": {}, "version": "1.0"}' > "$HIVE_CRED_DIR/metadata/index.json"
fi
echo -e "${GREEN} ✓ Credential store initialized at ~/.hive/credentials/${NC}"