Skip to main content

What it enables

  • Operations-shaped scenarios (AWS Pulse, Incident Response, Lead Qualifier) that need to read live state from outside the project workspace.
  • Per-tenant credential management with an audit-friendly indirection: the database holds a reference to the secret, never the secret value itself.
  • Per-scenario tool whitelisting via the previously-unused Level.intra_tools field — a typo surfaces as a clear KeyError at level start, not a silent miss.
  • Shared HTTP plumbing (retry, per-host rate limit, structured logging) so each connector doesn’t reinvent it.

Architecture

Two-registry topology. File tools (finish, read_file, edit_file, …) stay in worker/runtime/file_tools.py; external tools live in this new registry. They’re layered together at dispatch time — file tools are always available; external tools come from Level.intra_tools. This separation is deliberate: file tools take a project workspace and mutate the tree; external tools take a ToolContext (tenant + secrets
  • HTTP) and reach out to remote services. Forcing one signature would compromise both.

Tool definition pattern

Connector packages call default_registry.register(SPEC) at import time. tools/__init__.py pulls in builtins for the side-effect, so a single from worker.worker.runtime.tools import default_registry exposes every shipped tool.

Tenant secrets

Schema: tenant_tool_secrets (added to PostgresRepo.ensure_schema). Phase 1 only resolves ref_kind='env'. EnvToolSecretProvider reads the row, looks up os.environ[row['ref_value']], JSON-parses if the payload looks like JSON, falls back to {"value": <raw>}. A 300s in-process TTL cache absorbs repeat lookups within a single run. Seed a row:
…then set AMOS_AWS_DEFAULT='{"access_key_id":"...","secret_access_key":"...","region":"ap-east-1"}'. SecretsManagerToolProvider is a stub that raises NotImplementedError loudly so a premature switch in Phase 2 is loud, not silent.

Runner integration

Two surgical edits in worker/worker/graph/scenarios/runners.py:
  1. _build_tool_list(level, registry) — combined file + external tool schema list per level. Unknown intra_tools raises at level start with the full list of registered names.
  2. _dispatch_tool — registry-aware: ask_user (interrupt) → external tool registry → file tool execute_tool fallthrough. Crashes in external handlers become ToolResult(ok=False); they never abort the loop.
Tool-call / tool-result events on the SSE bus are passed through _redact() — any string under a key matching /(secret|token|password|api[_-]?key|access[_-]?key)/i is masked to <redacted>. Belt-and-braces in case a token leaks into args. Workspace-less levels (AWS Pulse style — intra_tools set, no _project_cwd) get a scratch tempfile.TemporaryDirectory so finish still works.

Shipped connectors

AWS — 12 read-only audit tools. aws_ec2_inventory, aws_ebs_inventory, aws_cost_breakdown, aws_cost_anomalies, aws_compute_optimizer_recs, aws_s3_inventory, aws_iam_audit, aws_security_groups_audit, aws_cloudtrail_coverage, aws_nat_gateways_audit, aws_eips_audit, aws_rds_inventory. Cost Explorer / Compute Optimizer require account opt-in; OptInRequired surfaces as ToolResult(ok=False, error=…) so the loop continues. Slack — 4 tools. slack_post_message, slack_post_blocks, slack_post_threaded, slack_channel_history. Auth resolves either a bot_token (xoxb-…) for the full Web API or a webhook_url for post-only delivery; bot token wins when both are present.

Impact scope

  • Pure addition. Existing scenarios (code_build, document_writing, quick_qa, security_review) are unaffected — file tools dispatch unchanged. A legacy intra_tools: [heartbeat_l4] declaration in code_build.yaml works because we registered a no-op sentinel.
  • New Postgres table; ensure_schema() is idempotent.
  • Production wiring lives in worker/worker/main.py: builds one EnvToolSecretProvider(repo=postgres) and passes it to WorkerOrchestrator.
  • New dependencies: none. Uses existing httpx, boto3, psycopg.

Tests

Run:

Phase 2 plans

  • Real SecretsManagerToolProvider backing (currently stubbed).
  • Cross-tenant AWS via sts:AssumeRole.
  • More connectors: GitHub, PagerDuty, CRM (Attio / HubSpot), ATS (Greenhouse / Lever).
  • Admin UI for per-tenant secret seeding (currently CLI-only).