Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
182 commits
Select commit Hold shift + click to select a range
7729116
openhands: improve role agent prompts
JoTalbot Aug 25, 2026
2c7697c
test: cover strengthened openhands prompts
JoTalbot Aug 25, 2026
820e13d
openhands: add evidence and definition-of-done primitives
JoTalbot Aug 25, 2026
3a1063f
openhands: add bounded cross-agent task memory
JoTalbot Aug 25, 2026
0e220b3
openhands: add dynamic task-type prompt guidance
JoTalbot Aug 25, 2026
8a96527
openhands: add prompt input security firewall
JoTalbot Aug 25, 2026
2efb65d
openhands: integrate dynamic task guidance and prompt firewall
JoTalbot Aug 25, 2026
da10af7
openhands: add bounded reviewer-to-coder repair transition
JoTalbot Aug 25, 2026
37336ca
openhands: add deterministic prompt evaluation
JoTalbot Aug 25, 2026
7c58f78
openhands: add agent quality scoreboard
JoTalbot Aug 25, 2026
4169818
test: add OpenHands prompt engine contract coverage
JoTalbot Aug 25, 2026
05907b0
openhands: export prompt, memory, evidence and scoring primitives
JoTalbot Aug 25, 2026
651338d
test: add OpenHands evidence memory routing and score tests
JoTalbot Aug 25, 2026
2c0a5df
openhands: integrate task memory and reviewer repair loop
JoTalbot Aug 25, 2026
9cbdee1
openhands: add specialized micro-agent registry
JoTalbot Aug 25, 2026
2aac0dd
openhands: add conservative prompt optimization suggestions
JoTalbot Aug 25, 2026
22b0b24
openhands: expose micro-agents and prompt optimizer
JoTalbot Aug 25, 2026
599781e
test: cover micro-agent routing and prompt optimization
JoTalbot Aug 25, 2026
5b6d995
test: align OpenHands prompt contract wording
JoTalbot Aug 25, 2026
14af81a
fix(openhands): bound repair iterations and harden task models
JoTalbot Aug 25, 2026
91b4090
fix(openhands): make repair transitions gate-safe
JoTalbot Aug 25, 2026
864fdf6
fix(openhands): fail-closed gates and bound reviewer repairs
JoTalbot Aug 25, 2026
e774f00
security(openhands): sanitize task input and require explicit gate ve…
JoTalbot Aug 25, 2026
25fa1a1
test(openhands): cover prompt injection and explicit gate verdicts
JoTalbot Aug 25, 2026
5375baf
test(openhands): verify gate semantics and bounded repair loop
JoTalbot Aug 25, 2026
6246e57
test(openhands): align gate test with forward completion semantics
JoTalbot Aug 25, 2026
f31a9a4
feat(openhands): make agent scoreboard conservative and first-pass aware
JoTalbot Aug 25, 2026
44168bf
openhands: wire runtime evidence, scoreboard and prompt optimization
JoTalbot Aug 25, 2026
70b2cee
openhands: add deterministic prompt evaluation suite
JoTalbot Aug 25, 2026
7cba06b
openhands: make prompt evaluator support annotated task sections
JoTalbot Aug 25, 2026
8ac708c
openhands: fix evaluation suite against prompt evaluator API
JoTalbot Aug 25, 2026
2365ace
openhands: export evaluation suite primitives
JoTalbot Aug 25, 2026
acdc772
test(openhands): cover deterministic evaluation scenarios
JoTalbot Aug 25, 2026
2455a28
openhands: make gate approvals explicit in task state
JoTalbot Aug 25, 2026
a61ca66
openhands: register approved gates in runtime
JoTalbot Aug 25, 2026
b7d1fa2
test(openhands): verify explicit gate registration
JoTalbot Aug 25, 2026
d92cb60
feat(openhands): add conservative adaptive router
JoTalbot Aug 25, 2026
0a00171
feat(openhands): expose adaptive router
JoTalbot Aug 25, 2026
161a01d
test(openhands): cover adaptive router
JoTalbot Aug 25, 2026
89da87e
feat(openhands): add deterministic micro-agent meta review
JoTalbot Aug 25, 2026
c885d46
feat(openhands): expose meta review primitives
JoTalbot Aug 25, 2026
e47ad3f
test(openhands): cover specialist meta review
JoTalbot Aug 25, 2026
aba5261
feat(openhands): add specialist review pipeline
JoTalbot Aug 25, 2026
8210d25
test(openhands): cover specialist review pipeline
JoTalbot Aug 25, 2026
bcb176a
feat(openhands): expose specialist review pipeline
JoTalbot Aug 25, 2026
e206806
feat(openhands): integrate specialist review into orchestrator
JoTalbot Aug 25, 2026
658ddb8
fix(openhands): classify task type for specialist routing
JoTalbot Aug 25, 2026
e6382d3
test(openhands): add lifecycle integration coverage
JoTalbot Aug 25, 2026
6d9ef3b
feat(runtime): add common AgentTask and AgentResult contracts
JoTalbot Aug 25, 2026
69f8e3c
feat(runtime): expose agent contracts
JoTalbot Aug 25, 2026
b66312d
test(runtime): cover common agent contracts
JoTalbot Aug 25, 2026
8c13825
feat(runtime): add agent executor lifecycle
JoTalbot Aug 25, 2026
f6668a4
refactor(runtime): align executor lifecycle contract
JoTalbot Aug 25, 2026
43431e0
test(runtime): cover executor lifecycle
JoTalbot Aug 25, 2026
91837aa
feat(runtime): add OpenHands agent adapter
JoTalbot Aug 25, 2026
d975741
test(runtime): cover OpenHands adapter
JoTalbot Aug 25, 2026
05c725c
feat(runtime): bind OpenHands orchestrator to AgentTask
JoTalbot Aug 25, 2026
04378ad
test(runtime): cover orchestrator adapter
JoTalbot Aug 25, 2026
5b27c16
feat(runtime): add agent event bus
JoTalbot Aug 25, 2026
5d2a724
test(runtime): cover agent event bus
JoTalbot Aug 25, 2026
cabd8d2
feat(runtime): emit executor lifecycle events
JoTalbot Aug 25, 2026
b38be0c
feat(runtime): add audit event subscriber
JoTalbot Aug 25, 2026
c796c81
test(runtime): cover audit subscriber
JoTalbot Aug 25, 2026
6a3498f
feat(runtime): add event-backed task memory
JoTalbot Aug 25, 2026
1ba88b7
test(runtime): cover task memory subscriber
JoTalbot Aug 25, 2026
48a7646
feat(runtime): add policy and permission engine
JoTalbot Aug 25, 2026
0fd0b8d
test(runtime): cover policy decisions
JoTalbot Aug 25, 2026
7fab1c9
feat(runtime): enforce policy before agent execution
JoTalbot Aug 25, 2026
8f2e1ef
test(runtime): enforce policy at executor boundary
JoTalbot Aug 25, 2026
c4d7100
feat(runtime): add approval queue for sensitive actions
JoTalbot Aug 25, 2026
0e7c2e1
test(runtime): cover approval queue transitions
JoTalbot Aug 25, 2026
eab341d
feat(runtime): integrate approval queue with executor
JoTalbot Aug 25, 2026
3e8b725
test(runtime): integrate approval flow with executor
JoTalbot Aug 25, 2026
987e134
feat(runtime): add sandbox execution boundary
JoTalbot Aug 25, 2026
4c429b5
test(runtime): cover sandbox policy boundary
JoTalbot Aug 25, 2026
8b76d01
feat(runtime): route sandbox decisions through sandbox executor
JoTalbot Aug 25, 2026
c37fb44
feat(runtime): add subprocess sandbox backend
JoTalbot Aug 25, 2026
42ded17
test(runtime): cover subprocess sandbox limits
JoTalbot Aug 25, 2026
f2e1783
feat(runtime): add pluggable sandbox backend contract
JoTalbot Aug 25, 2026
d774470
test(runtime): cover sandbox backend contract
JoTalbot Aug 25, 2026
cc4a656
feat(runtime): add docker sandbox backend
JoTalbot Aug 25, 2026
8db7f33
test(runtime): cover docker sandbox command construction
JoTalbot Aug 25, 2026
e9e2d58
feat(runtime): add sandbox backend registry
JoTalbot Aug 25, 2026
fa1996d
test(runtime): cover sandbox backend registry
JoTalbot Aug 25, 2026
3710c6e
feat(runtime): add sandbox backend factory
JoTalbot Aug 25, 2026
76afc77
test(runtime): cover default sandbox backend selection
JoTalbot Aug 25, 2026
ea4082f
feat(runtime): connect sandbox executor to backend registry
JoTalbot Aug 25, 2026
abe9ca4
test(runtime): verify sandbox factory integration
JoTalbot Aug 25, 2026
53113d0
fix(runtime): remove implicit sandbox task command assumption
JoTalbot Aug 25, 2026
49d5d3b
test(runtime): add end-to-end policy approval sandbox pipeline
JoTalbot Aug 25, 2026
aae2f5f
test(runtime): add registry to backend integration coverage
JoTalbot Aug 25, 2026
8187a56
test(runtime): add docker sandbox e2e coverage
JoTalbot Aug 25, 2026
4db00fb
feat(openhands): add structured agent handoff contract
JoTalbot Aug 25, 2026
970eddc
test(openhands): cover structured agent handoff contract
JoTalbot Aug 25, 2026
3ef6316
feat(openhands): inject structured handoff contract into agent prompts
JoTalbot Aug 25, 2026
1798891
test(openhands): require handoff protocol in generated prompts
JoTalbot Aug 25, 2026
54efd6f
feat(openhands): add role quality gates for agent pipeline
JoTalbot Aug 25, 2026
56eb7dd
test(openhands): cover staged agent quality gates
JoTalbot Aug 25, 2026
0a88e24
feat(openhands): enforce handoff gates before pipeline advance
JoTalbot Aug 25, 2026
fff9b53
test(openhands): verify gate application mutates task extras only on …
JoTalbot Aug 25, 2026
ed078c5
feat(openhands): enforce quality gates in orchestrator
JoTalbot Aug 25, 2026
16d1de9
test(openhands): verify orchestrator records gates through handoff va…
JoTalbot Aug 25, 2026
3554346
feat(openhands): require verified completion evidence at gates
JoTalbot Aug 25, 2026
ba108e4
test(openhands): gate on completion report evidence and DoD
JoTalbot Aug 25, 2026
37de5e7
feat(openhands): extract verified completion reports from events
JoTalbot Aug 25, 2026
82c9c79
feat(openhands): enforce event-derived evidence before stage gates
JoTalbot Aug 25, 2026
2858018
test(openhands): verify event-derived completion evidence
JoTalbot Aug 25, 2026
628ad08
feat(openhands): reconcile handoff files with git diff and permissions
JoTalbot Aug 25, 2026
ebdaf09
test(openhands): verify handoff files against git evidence
JoTalbot Aug 25, 2026
cbb9df3
feat(openhands): enforce git file reality at quality gates
JoTalbot Aug 25, 2026
e037e46
fix(openhands): correct gate path type annotation
JoTalbot Aug 25, 2026
a271053
test(openhands): block gate on git handoff mismatch
JoTalbot Aug 25, 2026
a623778
fix(openhands): validate file evidence against role permissions
JoTalbot Aug 25, 2026
d153f1e
fix(openhands): use canonical role permissions in file gate
JoTalbot Aug 25, 2026
46799e3
feat(openhands): enforce stage git reality in orchestrator gates
JoTalbot Aug 25, 2026
8a2b44e
test(openhands): verify stage files feed git reality gate
JoTalbot Aug 25, 2026
4dadf27
feat(openhands): add tamper-evident audit event chain
JoTalbot Aug 25, 2026
03a37e7
test(openhands): verify hash-linked audit chain integrity
JoTalbot Aug 25, 2026
b0cacec
feat(openhands): integrate hash chain into audit logger
JoTalbot Aug 25, 2026
b2a0154
test(openhands): verify audit logger emits linked events
JoTalbot Aug 25, 2026
3a84e72
feat(openhands): restore audit chain from persisted events
JoTalbot Aug 25, 2026
12b1fbd
feat(openhands): restore audit chain after restart
JoTalbot Aug 25, 2026
1dc75b3
fix(openhands): restore all persisted OpenHands chain events
JoTalbot Aug 25, 2026
dc7102a
test(openhands): restore audit chain from persistent backend
JoTalbot Aug 25, 2026
f58ff5c
feat(openhands): add audit chain checkpoints and truncation detection
JoTalbot Aug 25, 2026
8614f74
test(openhands): detect audit chain truncation with checkpoints
JoTalbot Aug 25, 2026
bc28fc9
feat(openhands): persist audit chain checkpoints
JoTalbot Aug 25, 2026
588eaae
test(openhands): persist audit checkpoint metadata
JoTalbot Aug 25, 2026
3da2697
fix(openhands): restore and verify persisted audit checkpoints
JoTalbot Aug 25, 2026
f52ad22
test(openhands): fail closed on invalid persisted audit chain
JoTalbot Aug 25, 2026
d900034
feat(openhands): checkpoint critical audit events automatically
JoTalbot Aug 25, 2026
db497c6
test(openhands): auto-checkpoint critical audit events
JoTalbot Aug 25, 2026
aea4cdf
feat(openhands): bind audit checkpoints to execution identity
JoTalbot Aug 25, 2026
2564dbb
feat(openhands): persist commit and gate identity in checkpoints
JoTalbot Aug 25, 2026
bced742
test(openhands): bind critical checkpoints to gate and commit identity
JoTalbot Aug 25, 2026
1494500
feat(openhands): bind checkpoints to git diff identity
JoTalbot Aug 25, 2026
b0e865d
feat(openhands): record git diff hash in audit checkpoints
JoTalbot Aug 25, 2026
3642b9a
test(openhands): preserve git diff identity in checkpoints
JoTalbot Aug 25, 2026
335cf28
feat(openhands): compute canonical git diff identity
JoTalbot Aug 25, 2026
93a57c5
test(openhands): verify deterministic git diff identity
JoTalbot Aug 25, 2026
85a1808
feat(openhands): bind gate audit to real git commit and diff
JoTalbot Aug 25, 2026
dd8112e
test(openhands): require real git identity for gate audit
JoTalbot Aug 25, 2026
b62d044
fix(openhands): restore persisted checkpoints independently of event …
JoTalbot Aug 25, 2026
6efe3df
test(openhands): restore persisted checkpoint metadata
JoTalbot Aug 25, 2026
676cba0
feat(openhands): cryptographically bind checkpoint metadata
JoTalbot Aug 25, 2026
f6413d7
feat(openhands): persist checkpoint integrity hash
JoTalbot Aug 25, 2026
76e756e
test(openhands): reject tampered checkpoint metadata
JoTalbot Aug 25, 2026
06acaea
feat(openhands): chain checkpoints with previous checkpoint hash
JoTalbot Aug 25, 2026
98586d3
feat(openhands): persist checkpoint chain links
JoTalbot Aug 25, 2026
85d9ea0
test(openhands): verify checkpoint-to-checkpoint hash chain
JoTalbot Aug 25, 2026
3eec2f1
ci(openhands): verify audit integrity in GitHub Actions
JoTalbot Aug 25, 2026
25affde
feat(openhands): add fail-closed evidence completion gate
JoTalbot Aug 25, 2026
3ff9bc7
test(openhands): enforce fail-closed evidence gate
JoTalbot Aug 25, 2026
3fe1cb5
feat(openhands): enforce evidence gate before completion
JoTalbot Aug 25, 2026
80452d0
test(openhands): block runner completion without evidence
JoTalbot Aug 25, 2026
53d1c28
feat(openhands): bind test evidence to exact git state
JoTalbot Aug 25, 2026
0c113da
feat(openhands): bind completion evidence to tester gate identity
JoTalbot Aug 25, 2026
aea32e4
feat(openhands): bind tester evidence to git identity
JoTalbot Aug 25, 2026
ea74287
test(openhands): cover execution-bound evidence mismatches
JoTalbot Aug 25, 2026
d3d1e79
feat(openhands): require CI run provenance in completion evidence
JoTalbot Aug 25, 2026
ea86c91
test(openhands): enforce CI run and job provenance
JoTalbot Aug 25, 2026
eadbf5e
feat(openhands): add GitHub Actions CI provenance collector
JoTalbot Aug 25, 2026
307073a
fix(openhands): require every configured CI workflow to pass
JoTalbot Aug 25, 2026
5cf766c
test(openhands): verify CI provenance collector binding
JoTalbot Aug 25, 2026
fe6772b
feat(openhands): require all configured CI workflows to pass
JoTalbot Aug 25, 2026
4263c82
test(openhands): cover required CI workflow aggregate
JoTalbot Aug 25, 2026
c5b83c2
feat(openhands): export CI provenance collector
JoTalbot Aug 25, 2026
d7d90e4
feat(openhands): collect CI provenance before completion gate
JoTalbot Aug 25, 2026
c1a2000
feat(openhands): add task-aware CI policy profiles
JoTalbot Aug 25, 2026
e4d4ca8
feat(openhands): accept task-aware CI workflow policies
JoTalbot Aug 25, 2026
03a7ea3
feat(openhands): select CI policy from task type
JoTalbot Aug 25, 2026
8d8d5f1
feat(openhands): resolve CI policy from task risk and changed files
JoTalbot Aug 25, 2026
18f9372
test(openhands): cover security policy escalation from changed paths
JoTalbot Aug 25, 2026
745b9d2
feat(openhands): resolve CI policy from actual changed files
JoTalbot Aug 25, 2026
2f6c18f
feat(openhands): enforce pre-CI security agent escalation
JoTalbot Aug 25, 2026
4ac0dd3
feat(openhands): support automatic specialist spawning
JoTalbot Aug 25, 2026
161f4a6
feat(openhands): add specialist conversation spawner
JoTalbot Aug 25, 2026
c03a4a5
test(openhands): verify specialist spawner lifecycle
JoTalbot Aug 25, 2026
e72e3d6
test(openhands): strengthen specialist spawner fail-closed coverage
JoTalbot Aug 25, 2026
364f3f6
test(openhands): cover policy to specialist escalation
JoTalbot Aug 25, 2026
766d74d
fix(openhands): align file evidence tests with role-aware API
JoTalbot Aug 25, 2026
32e62bd
feat(openhands): auto-spawn required security specialist
JoTalbot Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/openhands-audit-integrity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: OpenHands Audit Integrity

on:
pull_request:
paths:
- "aios_core/openhands/**"
- "tests/test_openhands_*.py"
- ".github/workflows/openhands-audit-integrity.yml"
push:
branches:
- main
- "agent/**"
paths:
- "aios_core/openhands/**"
- "tests/test_openhands_*.py"
- ".github/workflows/openhands-audit-integrity.yml"

permissions:
contents: read

jobs:
audit-integrity:
name: OpenHands audit chain integrity
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install test dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
pip install pytest

- name: Run OpenHands audit tests
run: |
pytest -q tests/test_openhands_checkpoint_chain.py \
tests/test_openhands_checkpoint_identity.py \
tests/test_openhands_checkpoint_integrity.py \
tests/test_openhands_critical_checkpoints.py \
tests/test_openhands_git_identity_gate.py \
tests/test_openhands_audit_restore_fail_closed.py

- name: Verify Python syntax
run: python -m compileall -q aios_core/openhands
91 changes: 25 additions & 66 deletions aios_core/openhands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,37 @@
"""OpenHands-контур AIOS: оркестрация OpenHands-разговоров как специализированных агентов.

AIOS владеет оркестрацией, состоянием задач, правами и аудитом; OpenHands (Cloud API)
владеет исполнением в sandbox. Роли (Architect/Coder/Tester/Reviewer/...) — профили
разговоров, а не новые классы агентов. Подробнее: AIOS_OPENHANDS_INTEGRATION_PLAN.md.
"""

"""OpenHands-контур AIOS."""
from .agent_score import AgentScoreboard, AgentStats
from .api import router as oh_contour_router
from .ci_provenance import CIProvenance, CIProvenanceCollector
from .client import OpenHandsClient, resolve_api_key
from .errors import (
OpenHandsAPIError,
OpenHandsAuthError,
OpenHandsError,
OpenHandsStartError,
OpenHandsTimeoutError,
)
from .errors import OpenHandsAPIError, OpenHandsAuthError, OpenHandsError, OpenHandsStartError, OpenHandsTimeoutError
from .evidence import CompletionReport, DoDItem, Evidence, EvidenceKind, dod_for_role
from .evaluation_suite import EvaluationScenario, SCENARIOS, assert_evaluation_suite, run_prompt_evaluation
from .evaluator import PromptEvaluation, assert_prompt_contract, evaluate_prompt
from .github import GitHubHelper, GitOperationError, GitRunner
from .models import (
MVP_ROLES,
AgentPermissions,
AgentProfile,
AgentRole,
FailureReport,
Gate,
ReviewDecision,
TaskExtras,
)
from .memory import AgentMemoryEntry, TaskMemory
from .meta_review import MetaReview, SpecialistVerdict, aggregate_verdicts
from .micro_agents import MICRO_AGENTS, MicroAgentSpec, select_micro_agents
from .models import MVP_ROLES, AgentPermissions, AgentProfile, AgentRole, FailureReport, Gate, ReviewDecision, TaskExtras
from .permissions import PROFILES, check_paths, path_allowed, rbac_role_name, register_roles
from .profiles import build_prompt, conversation_title
from .prompt_optimizer import PromptOptimizationSuggestion, suggest_improvements
from .prompt_security import PromptSecurityResult, inspect_untrusted_input, sanitize_context
from .router import AdaptiveRouter, RouteDecision, default_route_candidates
from .runner import OHOrchestrator, RunResult
from .service import ContourService, ContourTask
from .state_machine import (
TransitionError,
allowed_transitions,
can_transition,
transition,
)
from .specialist_pipeline import SpecialistResult, SpecialistReviewPipeline, conservative_executor
from .state_machine import TransitionError, allowed_transitions, can_transition, transition
from .store import ContourStore
from .task_profiles import TaskType, classify_task, guidance_for
from .verdicts import parse_review_verdict

__all__ = [
"MVP_ROLES",
"PROFILES",
"AgentPermissions",
"AgentProfile",
"AgentRole",
"ContourService",
"ContourStore",
"ContourTask",
"FailureReport",
"Gate",
"GitHubHelper",
"GitOperationError",
"GitRunner",
"OHOrchestrator",
"OpenHandsAPIError",
"OpenHandsAuthError",
"OpenHandsClient",
"OpenHandsError",
"OpenHandsStartError",
"OpenHandsTimeoutError",
"ReviewDecision",
"RunResult",
"TaskExtras",
"TransitionError",
"allowed_transitions",
"build_prompt",
"can_transition",
"check_paths",
"conversation_title",
"oh_contour_router",
"parse_review_verdict",
"path_allowed",
"rbac_role_name",
"register_roles",
"resolve_api_key",
"transition",
"MVP_ROLES", "PROFILES", "AgentPermissions", "AgentProfile", "AgentRole", "AgentMemoryEntry", "AgentScoreboard", "AgentStats",
"AdaptiveRouter", "CIProvenance", "CIProvenanceCollector", "CompletionReport", "ContourService", "ContourStore", "ContourTask", "DoDItem", "Evidence", "EvidenceKind", "EvaluationScenario", "SCENARIOS", "FailureReport", "Gate",
"GitHubHelper", "GitOperationError", "GitRunner", "MICRO_AGENTS", "MetaReview", "MicroAgentSpec", "OHOrchestrator", "OpenHandsAPIError", "OpenHandsAuthError",
"OpenHandsClient", "OpenHandsError", "OpenHandsStartError", "OpenHandsTimeoutError", "PromptEvaluation", "PromptOptimizationSuggestion",
"PromptSecurityResult", "ReviewDecision", "RouteDecision", "RunResult", "SpecialistResult", "SpecialistReviewPipeline", "SpecialistVerdict", "TaskExtras", "TaskMemory", "TaskType", "TransitionError", "allowed_transitions",
"aggregate_verdicts", "assert_evaluation_suite", "assert_prompt_contract", "build_prompt", "can_transition", "check_paths", "classify_task", "conservative_executor", "conversation_title", "default_route_candidates", "dod_for_role", "evaluate_prompt",
"guidance_for", "inspect_untrusted_input", "oh_contour_router", "parse_review_verdict", "path_allowed", "rbac_role_name", "register_roles",
"resolve_api_key", "run_prompt_evaluation", "sanitize_context", "select_micro_agents", "suggest_improvements", "transition",
]
77 changes: 77 additions & 0 deletions aios_core/openhands/agent_score.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Observable agent quality statistics for routing and prompt evaluation."""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass
class AgentStats:
attempts: int = 0
successes: int = 0
first_pass_successes: int = 0
failures: int = 0
reviewer_rejections: int = 0
security_violations: int = 0
total_iterations: int = 0

@property
def success_rate(self) -> float:
return self.successes / self.attempts if self.attempts else 0.0

@property
def first_pass_rate(self) -> float:
return self.first_pass_successes / self.attempts if self.attempts else 0.0

@property
def avg_iterations(self) -> float:
return self.total_iterations / self.attempts if self.attempts else 0.0


@dataclass
class AgentScoreboard:
stats: dict[str, AgentStats] = field(default_factory=dict)

def record(
self,
role: str,
*,
success: bool,
iterations: int = 1,
reviewer_rejected: bool = False,
security_violation: bool = False,
) -> None:
stat = self.stats.setdefault(role, AgentStats())
stat.attempts += 1
normalized_iterations = max(1, iterations)
stat.total_iterations += normalized_iterations
if success:
stat.successes += 1
if normalized_iterations == 1 and not reviewer_rejected:
stat.first_pass_successes += 1
else:
stat.failures += 1
if reviewer_rejected:
stat.reviewer_rejections += 1
if security_violation:
stat.security_violations += 1

def score(self, role: str, *, min_attempts: int = 3) -> float:
"""Conservative score; sparse agents are not promoted over proven agents."""
stat = self.stats.get(role)
if not stat or not stat.attempts:
return 0.0
base = stat.success_rate
penalty = min(
0.5,
stat.reviewer_rejections / stat.attempts * 0.25
+ stat.security_violations / stat.attempts * 0.5,
)
iteration_penalty = min(0.25, max(0.0, stat.avg_iterations - 1.0) * 0.1)
score = max(0.0, base - penalty - iteration_penalty)
if stat.attempts < min_attempts:
score *= stat.attempts / min_attempts
return score

def rank(self, roles: list[str] | tuple[str, ...]) -> list[str]:
return sorted(roles, key=self.score, reverse=True)
91 changes: 49 additions & 42 deletions aios_core/openhands/audit.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
"""Аудит-события OpenHands-контура поверх ``aios_core.audit_logger.AuditLogger``.

Все значения проходят маскирование секретов до записи: в лог не попадают
passwords, tokens, API keys, private keys, cookies (Этап 17 master-плана).
"""

"""OpenHands audit with secret masking and cryptographically linked checkpoints."""
import re
from typing import Any
from uuid import uuid4

from aios_core.audit_logger import AuditLogger

from .audit_chain import AuditChain, ChainCheckpoint
from .models import AgentRole

EVENT_PREFIX = "openhands"

# Ключи, значения которых маскируются всегда.
_SENSITIVE_KEY = re.compile(
r"(password|passwd|secret|token|api[_-]?key|private[_-]?key|cookie|credential|authorization)",
re.IGNORECASE,
)
# Значения, похожие на секреты: длинные base64/hex-строки (≥20 символов).
CHECKPOINT_ACTION = "audit_checkpoint"
CRITICAL_ACTIONS = frozenset({"gate_pass", "gate_block", "handoff", "security_review"})
_SENSITIVE_KEY = re.compile(r"(password|passwd|secret|token|api[_-]?key|private[_-]?key|cookie|credential|authorization)", re.IGNORECASE)
_SENSITIVE_VALUE = re.compile(r"\b[A-Za-z0-9+/=_-]{20,}\b")

MASK = "***"


def mask_secrets(obj: Any) -> Any:
"""Рекурсивно замаскировать секреты в dict/list/str перед записью в лог."""
if isinstance(obj, dict):
masked = {}
for key, value in obj.items():
if _SENSITIVE_KEY.search(str(key)):
masked[key] = MASK
else:
masked[key] = mask_secrets(value)
return masked
return {key: MASK if _SENSITIVE_KEY.search(str(key)) else mask_secrets(value) for key, value in obj.items()}
if isinstance(obj, (list, tuple)):
return [mask_secrets(item) for item in obj]
if isinstance(obj, str):
Expand All @@ -42,37 +27,59 @@ def mask_secrets(obj: Any) -> Any:


class OHAuditLogger:
"""Обёртка над AuditLogger: контурный тип события + маскирование секретов."""
"""OpenHands audit facade with durable, cryptographically linked checkpoints."""

def __init__(self, logger: AuditLogger | None = None) -> None:
def __init__(self, logger: AuditLogger | None = None, chain: AuditChain | None = None) -> None:
self._logger = logger or AuditLogger()
if chain is not None:
self._chain = chain
else:
persisted = [event for event in self._logger.query(limit=100000) if str(event.get("type", "")).startswith(f"{EVENT_PREFIX}.")]
self._chain = AuditChain.from_persisted(persisted)

def log(
self,
action: str,
task_id: str,
agent: AgentRole | str,
**fields: Any,
) -> dict:
"""Записать событие контура (тип ``openhands.<action>``) с маскированием."""
def log(self, action: str, task_id: str, agent: AgentRole | str, **fields: Any) -> dict:
role = agent.value if isinstance(agent, AgentRole) else str(agent)
event = {
"type": f"{EVENT_PREFIX}.{action}",
"task_id": task_id,
"agent": role,
**fields,
}
return self._logger.record(mask_secrets(event))
event_id = uuid4().hex
event = mask_secrets({"type": f"{EVENT_PREFIX}.{action}", "task_id": task_id, "agent": role, **fields})
chain_event = self._chain.append(event_id, event)
event.update({"event_id": event_id, "parent_event_id": chain_event.parent_event_id, "event_hash": chain_event.event_hash})
result = self._logger.record(event)
if action in CRITICAL_ACTIONS:
self.checkpoint(task_id, agent, gate_decision=fields.get("decision"), commit_sha=fields.get("commit_sha"), diff_hash=fields.get("diff_hash"))
return result

def log_transition(self, task_id: str, agent: AgentRole | str, src: str, dst: str, **fields: Any) -> dict:
"""Событие смены статуса задачи."""
return self.log("transition", task_id, agent, src=src, dst=dst, **fields)

def log_decision(self, task_id: str, agent: AgentRole | str, decision: str, **fields: Any) -> dict:
"""Событие решения (gate, review, retry, fail)."""
return self.log("decision", task_id, agent, decision=decision, **fields)

def checkpoint(self, task_id: str = "system", agent: AgentRole | str = "system", *, gate_decision: str | None = None, commit_sha: str | None = None, diff_hash: str | None = None) -> ChainCheckpoint:
checkpoint = self._chain.checkpoint(task_id=task_id, agent=agent.value if isinstance(agent, AgentRole) else str(agent), gate_decision=gate_decision, commit_sha=commit_sha, diff_hash=diff_hash)
role = agent.value if isinstance(agent, AgentRole) else str(agent)
event = {
"type": f"{EVENT_PREFIX}.{CHECKPOINT_ACTION}",
"task_id": task_id,
"agent": role,
"sequence": checkpoint.sequence,
"last_event_id": checkpoint.last_event_id,
"root_hash": checkpoint.root_hash,
"gate_decision": checkpoint.gate_decision,
"commit_sha": checkpoint.commit_sha,
"diff_hash": checkpoint.diff_hash,
"previous_checkpoint_hash": checkpoint.previous_checkpoint_hash,
"checkpoint_hash": checkpoint.checkpoint_hash,
}
self._logger.record(event)
return checkpoint

def verify_chain(self) -> bool:
return self._chain.verify()

@property
def chain(self) -> AuditChain:
return self._chain

@property
def backend(self) -> AuditLogger:
"""Нижележащий AuditLogger (для query/stats)."""
return self._logger
Loading
Loading