File size: 3,625 Bytes
fc3e70a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""Phase 17: Agent harness β€” agents propose artifacts; the system verifies before acceptance.

Agent output never bypasses FSM, IR validation, temporal lock, or verification.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Callable, Optional

from .ir import Artifact, SemanticIR
from .types import ArtifactType, FSMState, PipelineError, VerificationState
from .verifier import Verifier


# ── Agent task ────────────────────────────────────────────────────────────────

@dataclass
class AgentTask:
    task_id: str
    description: str
    semantic_constraints: list[str]
    available_operations: list[str]
    artifact_schema: dict
    verification_requirements: list[str]
    ir_ref: Optional[SemanticIR] = None


# ── Agent proposal ────────────────────────────────────────────────────────────

@dataclass
class AgentProposal:
    agent_id: str
    task_id: str
    artifact: Artifact
    rationale: str = ""


# ── Agent interface ───────────────────────────────────────────────────────────

AgentFn = Callable[[AgentTask], AgentProposal]


@dataclass
class Agent:
    agent_id: str
    fn: AgentFn
    capabilities: list[ArtifactType] = field(default_factory=list)


# ── Harness ───────────────────────────────────────────────────────────────────

class AgentHarness:
    """
    Accepts agent proposals, runs them through the verifier, accepts or rejects.
    Agents may NOT bypass any pipeline stage.
    """

    def __init__(self):
        self._agents: dict[str, Agent] = {}
        self._verifier = Verifier()
        self._accepted: list[Artifact] = []
        self._rejected: list[tuple[AgentProposal, str]] = []

    def register(self, agent: Agent) -> None:
        self._agents[agent.agent_id] = agent

    def dispatch(self, task: AgentTask, agent_id: str) -> Artifact:
        agent = self._agents.get(agent_id)
        if agent is None:
            raise PipelineError(FSMState.ARTIFACT_SELECTION, f"Unknown agent: {agent_id}")

        proposal = agent.fn(task)

        if task.ir_ref is None:
            raise PipelineError(
                FSMState.VERIFICATION,
                "Agent task has no IR reference β€” cannot verify provenance",
            )

        report = self._verifier.verify_artifact(proposal.artifact, task.ir_ref)

        if not report.passed:
            self._rejected.append((proposal, report.summary()))
            raise PipelineError(
                FSMState.VERIFICATION,
                f"Agent {agent_id} proposal rejected:\n{report.summary()}",
            )

        proposal.artifact.verification_state = VerificationState.VERIFIED
        self._accepted.append(proposal.artifact)
        return proposal.artifact

    def accepted(self) -> list[Artifact]:
        return list(self._accepted)

    def rejected(self) -> list[tuple[AgentProposal, str]]:
        return list(self._rejected)