Capability Upgrade — Architecture Design (Phase 2 output)
Status: approved-by-author / implemented incrementally. Companion to the audit
reports in docs/phase1-audit/.
The capability model described here (declared vs available, capability
records, requires/produces composition) is also the substrate the future
browser-native web agent builds on: prepared seam
tools/browser/capabilities.py (stable browser.* names, fail-closed
availability) + design doc docs/browser-agent-design.md.
Governing principle, forced by the audit: extend existing seams, build zero parallel systems. Every wishlist item below names the existing substrate it extends.
The loop the AI should run
Operator Goal
↓
Assessment State ← tools/assessment_state.py aggregates plan + recon +
↓ creds + audit + hypotheses (owned) into one snapshot
Planner / Task Graph ← AttackPlan/AttackStep extended into a real DAG
↓ (depends_on already serialized; add ready_steps(),
Capability Selection per-step status/hypothesis/priority/failure fields)
↓ ← capability_record() metadata + applicability_explain()
MCP / Attack Module (score + reasons + penalties), find_producers() for
↓ dynamic composition off requires/produces attrs
Structured Result ← ModuleResult extended: failure_class, retryable,
↓ confidence, produced_artifacts, follow_ups
Evidence Store ← existing: exploit_audit.jsonl + workspace artifacts
↓ + plans/<ip>_assessment.json (hypotheses, notes)
State Update ← update_task / record_hypothesis MCP tools mutate the
↓ plan + assessment JSON (allowlist-gated)
Reflection / Next ← failure_taxonomy.classify_failure() → recovery action
decides retry-with-params / prereq / switch / stop
Component map (wishlist § → substrate → change)
-
Capability model (§1) — new class attrs on
AttackModule(requires,produces,read_only,cost,phase_hint), all defaulted[]/safe. New methodcapability_record()returns the full machine- readable record.to_json()stays byte-identical (tests key-pin it).find_producers(kind)added toregistry.pyfor composition discovery. -
Applicability explanation (§6) —
applicability_explain(ctx)returnsApplicabilityReport(score, reasons, penalties);applicability(ctx)is re-implemented asapplicability_explain(ctx).scoreso scoring weights, 0–100 cap, and the ICS zero-gate stay bit-identical. -
ModuleContext (§2) — additive defaulted fields:
sessions,findings,hypotheses,evidence_refs,access_achieved,privilege_level,phase. Orchestrator ctx builders (612/2042) andrun_attack_modulestart passing what they already hold inAttackState. Other constructors default. -
Structured results (§7) —
ModuleResultgainsfailure_class,retryable,confidence,produced_artifacts,follow_ups,unlocked_capabilities(all empty/None defaults;to_dictdrops empties as today;to_resultreads them back). Adapter contract untouched. -
Failure taxonomy (§5) — NEW single source:
tools/failure_taxonomy.py(FailureClassenum,classify_failure(),RecoveryActionenum,recovery_for()+ hints).RetryEngine.should_retry, the exploit loop's replan prompts, and the reflection taxonomy express on top of it. The three existing classifiers keep their public APIs (tests depend). -
Task graph (§3) — extend
AttackStepwithhypothesis,priority,status(pending/running/done/failed/blocked/cancelled),attempt_count,failure_class,failure_reason,capability,expected_evidence,confidence,created_from. ExtendAttackPlanwithready_steps(),next_step(),add_step(),cancel_step(),fail_step()over the existingdepends_onedges.to_json/from_jsonstay tolerant;session_manager.SessionState.planembedding keeps loading. -
Assessment state store (§2/§11/§16) — NEW
tools/assessment_state.py:AssessmentState(goal, phase, hypotheses, notes, per-run) persisted to<workspace>/plans/<ip>_assessment.json;aggregate_state()reads the existing stores (plan JSON, recon_result.json, cred vault summary, audit JSONL rollup) and merges with owned hypotheses/notes. Raw output stays in artifacts; the snapshot carries only compact refs (evidence IDs =exploit_audit:<target>:<attempt_id>convention already used). -
AI-facing MCP tools (§16) — NEW
tools/mcp_tools/assessment_state.pyregistering six tools (double-registration rule: registry__all__+ server register call + docs):get_assessment_state(target_ip)RA-gated;query_capabilities(scope, service)+get_capability_details(name)audit-gated RO;get_evidence(target_ip, limit, tool)RA-gated (reuses mitre filtering);record_hypothesis(target_ip, ...)+update_task(target_ip, ...)RA-gated (state files are LLM-writable → handler re-validates, the run_campaign_step:2516 precedent). Return-block convention:ASSESSMENT_STATE: .../CAPABILITIES: ...UPPER_SNAKE + KEY: value lines,BLOCKED:/ERROR:prefixes only. -
Agent loop integration (§4/§22) — new tools added to the universal tool set (never phase-hidden); new prompt section (kwarg +
""-when-off, the established pattern) on hypothesis-driven workflow;_blocked_replan_promptgains the classified failure + recovery hint; decision log hook at the outcome-normalization point. All asserted substrings/markers preserved. -
Decision log (§17) — NEW
tools/decision_log.py, one append-onlydecision_log.jsonlper run dir:{round, tool, reason, failure_class, outcome, success, evidence_refs}. Never raw CoT — concise fields only. -
Model-role routing (§13) —
ModelRouter.get_client_for_role(role, fallback=None)readingmodels.roles.<role>→ alias →get_client(reverse-lookup already handles ids). Default: every role resolves to the default alias — byte-identical behavior today. Role call sites opt-in. -
Orchestrator upgrades (§5/§9) —
RetryEngine.should_retrydelegates to the taxonomy (permanent classes → no retry;prerequisite_missing→ emit a prerequisite task resolved viafind_producers);_module_context/ executor ctx gain access/priv/sessions fromAttackState;max_pivot_depth: 0and the scope-gate check are untouched. -
Module metadata sweep (§19) — set
requires/produces/read_only/cost/phase_hinton the ~75 classes (adds only; no behavior change). The composition chains from the attack-modules audit §Q4 become explicit. -
Config (§23) — new
agentblock (budgets/toggles, all defaulting to today's behavior) +models.rolesblock;CONFIG_SCHEMAentries (auto- whitelist),config.yamlmirror, defensive.getat every consumption site (config_cli merges no defaults). -
Simulations (§18) —
tests/test_task_graph_simulations.py: scripted scenario harness driving ready_steps + failure classification + recovery over mocked module runs (the 9 scenarios from the prompt).
Explicitly NOT done (recorded for the summary)
- No new MCP tool rewrites of write/run_python_file (helper-code safety already = static body scan + allowlist + verify_poc self-heal; we expose the repair budget via config).
- No rewrite of
_DEFAULT_AGENT_MAP/ agent naming (test-locked). - No Flow B file edits, ever. No target-lock weakening: every new
target-touching tool uses the same
@require_allowlist/ audit machinery.
Backwards-compat guardrails observed (from audit §risks)
byte-identical to_json() / applicability scoring / status-string looseness;
_sync_patchable_symbols extended if new patch seams appear; markers
(COMPACTED_CONTEXT etc.) untouched; _EXC_GROUP_CATCH in all MCP-adjacent
async code; PUBLIC_USAGE_FIELDS append-only; new dataclass fields always
defaulted; monkeypatch shims (mcp_exploit_server._get_model_router,
_run_with_pgrp_timeout, _consultation_count) reused, never bypassed.