"""G8 source pack -> draft -> red-team -> benchmark agentic worker. The existing continuous-improvement store is deliberately the only persistence boundary. This worker owns model calls and local fail-closed validation, then hands the resulting immutable metadata to that store only after every agent stage has succeeded. """ from __future__ import annotations import asyncio import hashlib import json import re from collections.abc import Mapping, Sequence from typing import Any, Protocol from urllib.parse import urlsplit from uuid import UUID import httpx from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator from ..contracts.continuous_improvement import ( ContentBenchmarkQualification, ContentSourceArtifact, GeneratedContentDraft, IndependentRedTeamReview, OperationalIncident, RedTeamFinding, ReviewDimension, ) from ..contracts.engine_gateway import ( EngineMessage, GenerateRequest, GenerateResponse, structured_payload_from_response, ) from . import continuous_improvement_store from .guardrail import mask_pii, mask_synthetic_generated_pii PROMPT_VERSION = "1.0.0" _TOKEN_PATTERN = re.compile(r"[A-Za-z0-9가-힣_]{4,}") _REVIEW_LANES: tuple[tuple[str, tuple[ReviewDimension, ...]], ...] = ( ("safety-identity", ("safety", "identity", "pii", "grounding")), ("leakage-bias", ("answer_leakage", "cultural_bias", "difficulty")), ) class AgenticPipelineError(ValueError): """Base error for the model-owned content pipeline.""" class AgenticPipelineExecutionError(AgenticPipelineError): """A required model call or structured response failed.""" class AgenticPipelineRejectedError(AgenticPipelineError): """Generated content failed a safety, grounding, or benchmark gate.""" class StructuredGenerationEngine(Protocol): async def generate(self, req: GenerateRequest) -> GenerateResponse: ... class HttpRollbackExecutor: """Authenticated control-plane adapter for model and runtime rollback.""" TOKEN_HEADER = "X-Vignette-Rollback-Executor-Token" def __init__( self, *, endpoint: str, token: str, timeout_seconds: float, transport: httpx.AsyncBaseTransport | None = None, ) -> None: parsed = urlsplit(endpoint) is_local_http = parsed.scheme == "http" and (parsed.hostname or "").lower() in { "localhost", "127.0.0.1", "::1", } if parsed.scheme != "https" and not is_local_http: raise ValueError("rollback executor endpoint must use HTTPS") if parsed.username or parsed.password or parsed.query or parsed.fragment: raise ValueError("rollback executor endpoint must not contain credentials or query") if len(token) < 32: raise ValueError("rollback executor token must contain at least 32 characters") self._endpoint = endpoint self._token = token self._timeout_seconds = timeout_seconds self._transport = transport async def execute( self, request: continuous_improvement_store.RollbackExecutionRequest ) -> continuous_improvement_store.RollbackExecutionReceipt: try: async with httpx.AsyncClient( timeout=httpx.Timeout(self._timeout_seconds), follow_redirects=False, trust_env=False, transport=self._transport, ) as client: response = await client.post( self._endpoint, headers={ self.TOKEN_HEADER: self._token, "Accept": "application/json", }, json=request.model_dump(mode="json"), ) response.raise_for_status() if len(response.content) > 65_536: raise ValueError("rollback executor receipt is too large") payload = response.json() except (httpx.HTTPError, ValueError) as exc: raise AgenticPipelineExecutionError( "rollback executor request failed" ) from exc return continuous_improvement_store.RollbackExecutionReceipt.model_validate(payload) def build_configured_rollback_executor( settings: Any, ) -> continuous_improvement_store.RollbackExecutor | None: """Return the separately opted-in executor; disabled always means no call.""" if not bool(settings.continuous_improvement_rollback_executor_enabled): return None token = settings.continuous_improvement_rollback_executor_token.get_secret_value() return HttpRollbackExecutor( endpoint=settings.continuous_improvement_rollback_executor_endpoint, token=token, timeout_seconds=settings.continuous_improvement_rollback_executor_timeout_seconds, ) class AgenticSourcePack(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) artifact: ContentSourceArtifact content: str = Field(min_length=1, max_length=100_000) class GroundedClaim(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) claim: str = Field(min_length=1, max_length=600) source_ref: str = Field(min_length=1, max_length=180) class GeneratedDraftPayload(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) title: str = Field(min_length=1, max_length=180) synthetic_profile: str = Field(min_length=1, max_length=1200) scenario: str = Field(min_length=1, max_length=6000) rupture_or_challenge: str = Field(min_length=1, max_length=2400) learner_task: str = Field(min_length=1, max_length=2000) success_criteria: tuple[str, ...] = Field(min_length=1, max_length=10) hidden_answer: str = Field(min_length=4, max_length=2000) source_refs: tuple[str, ...] = Field(min_length=1, max_length=100) grounded_claims: tuple[GroundedClaim, ...] = Field(min_length=1, max_length=20) @model_validator(mode="after") def unique_source_refs(self) -> "GeneratedDraftPayload": if len(self.source_refs) != len(set(self.source_refs)): raise ValueError("generated draft source refs must be unique") return self def visible_payload(self) -> dict[str, Any]: payload = self.model_dump(mode="json") payload.pop("hidden_answer", None) return payload class RedTeamFindingOutput(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) dimension: ReviewDimension severity: str = Field(pattern=r"^(blocker|high|moderate|low)$") evidence_summary: str = Field(min_length=1, max_length=500) class RedTeamOutput(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) dimensions: tuple[ReviewDimension, ...] = Field(min_length=3) findings: tuple[RedTeamFindingOutput, ...] = Field(max_length=50) class BenchmarkVariantOutput(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) visible_scenario: str = Field(min_length=1, max_length=5000) learner_task: str = Field(min_length=1, max_length=2000) success_criteria: tuple[str, ...] = Field(min_length=1, max_length=10) hidden_answer: str = Field(min_length=4, max_length=2000) source_refs: tuple[str, ...] = Field(min_length=1, max_length=100) def visible_payload(self) -> dict[str, Any]: payload = self.model_dump(mode="json") payload.pop("hidden_answer", None) return payload class BenchmarkVariantPackOutput(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) variants: tuple[BenchmarkVariantOutput, ...] = Field(min_length=3, max_length=12) class BenchmarkJudgeOutput(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) passed: bool answer_leakage: bool pii_found: bool unsupported_claim: bool safety_failure: bool reward_hacking: bool evidence_summary: str = Field(min_length=1, max_length=500) @model_validator(mode="after") def consistent_pass_flag(self) -> "BenchmarkJudgeOutput": has_failure = any( ( self.answer_leakage, self.pii_found, self.unsupported_claim, self.safety_failure, self.reward_hacking, ) ) if self.passed == has_failure: raise ValueError("benchmark judge pass flag contradicts failure flags") return self class AgenticPipelineResult(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) submission_id: UUID pipeline_id: UUID qualification_id: UUID candidate_catalog_entry_id: str state: str human_approval_required: bool catalog_promoted: bool idempotent_replay: bool clinical_claim_allowed: bool draft_id: str benchmark_id: str red_team_review_count: int benchmark_variant_count: int agent_calls_executed: int trigger_kind: str def _canonical_json(value: Any) -> str: return json.dumps( value, ensure_ascii=False, separators=(",", ":"), sort_keys=True, default=str, ) def _sha256_text(value: str) -> str: return hashlib.sha256(value.encode("utf-8")).hexdigest() def _answer_overlap_count(hidden_answer: str, visible_payload: Mapping[str, Any]) -> int: hidden_tokens = [item.lower() for item in _TOKEN_PATTERN.findall(hidden_answer)] visible_tokens = [ item.lower() for item in _TOKEN_PATTERN.findall(_canonical_json(visible_payload)) ] hidden_set = set(hidden_tokens) visible_set = set(visible_tokens) opaque_markers = { token for token in hidden_set if "_" in token or any(character.isdigit() for character in token) } def shingles(tokens: Sequence[str], width: int = 4) -> set[tuple[str, ...]]: return { tuple(tokens[index : index + width]) for index in range(max(0, len(tokens) - width + 1)) } marker_overlap = opaque_markers & visible_set phrase_overlap = shingles(hidden_tokens) & shingles(visible_tokens) return len(marker_overlap) + len(phrase_overlap) def _assert_pii_free(value: Any, *, stage: str) -> None: result = mask_synthetic_generated_pii(_canonical_json(value)) if result.entities: entity_types = ",".join(sorted(set(result.entities))) raise AgenticPipelineRejectedError( f"{stage} contains PII types: {entity_types}" ) def validate_source_packs( source_packs: Sequence[AgenticSourcePack], ) -> list[dict[str, Any]]: """Validate source approval, integrity and PII before any model call.""" if not source_packs: raise AgenticPipelineRejectedError("at least one source pack is required") source_ids = [item.artifact.source_id for item in source_packs] if len(source_ids) != len(set(source_ids)): raise AgenticPipelineRejectedError("source pack ids must be unique") context: list[dict[str, Any]] = [] for item in source_packs: if item.artifact.usage_status != "approved": raise AgenticPipelineRejectedError("agentic generation requires approved sources") if _sha256_text(item.content) != item.artifact.content_sha256: raise AgenticPipelineRejectedError("source pack content hash mismatch") masked = mask_pii(item.content) if masked.entities: raise AgenticPipelineRejectedError("source pack contains PII") context.append( { "source_id": item.artifact.source_id, "version": item.artifact.version, "content_sha256": item.artifact.content_sha256, "provenance_uri": item.artifact.provenance_uri, "citation_label": item.artifact.citation_label, "content": item.content, } ) return context # Backward-compatible private seam retained for focused contract tests. _source_context = validate_source_packs def _generation_prompt_payload( *, source_context: Sequence[Mapping[str, Any]], content_kind: str, difficulty_level: int, prompt_version: str, trigger_kind: str, ) -> dict[str, Any]: return { "prompt_version": prompt_version, "content_kind": content_kind, "difficulty_level": difficulty_level, "trigger_kind": trigger_kind, "sources": list(source_context), } def source_pack_from_operational_incident( incident: OperationalIncident, ) -> AgenticSourcePack: """Convert persisted metadata-only operational failure into adversarial input.""" content = _canonical_json(incident.model_dump(mode="json")) fingerprint = incident.error_fingerprint[:24] return AgenticSourcePack( artifact=ContentSourceArtifact( source_id=f"oas-g8-source-incident-{fingerprint}", version="1.0.0", content_sha256=_sha256_text(content), provenance_uri=( "audit://continuous-improvement/incidents/" f"{incident.incident_id}" ), usage_status="approved", citation_label=( "운영 오류 재현용 metadata source: " f"{incident.affected_contract}" ), ), content=content, ) async def _generate_structured( *, engine: StructuredGenerationEngine, stage: str, agent_id: str, messages: list[EngineMessage], schema_model: type[BaseModel], pipeline_id: UUID, max_tokens: int = 2400, ) -> tuple[BaseModel, GenerateResponse, int]: repair_message = EngineMessage( role="user", content=( "직전 출력이 JSON schema 검증에 실패했다. 동일한 작업을 다시 수행하되, " "제공된 JSON schema를 정확히 만족하는 단일 JSON object만 반환하라. " "필수 필드를 모두 포함하고 타입과 enum을 지키며 설명이나 markdown을 덧붙이지 마라. " "내용 정책, 승인된 source 범위, 안전 경계는 바꾸지 마라." ), ) last_error_message = f"{stage} returned no structured output" last_validation_error: ValidationError | None = None max_attempts = 2 for attempt in range(1, max_attempts + 1): request_messages = messages if attempt == 1 else [*messages, repair_message] request = GenerateRequest( ai_role="evaluator", messages=request_messages, max_tokens=max_tokens, temperature=0.1 if attempt == 1 else 0.0, structured_schema=schema_model.model_json_schema(), metadata={ "purpose": "g8_agentic_content_pipeline", "agentic_stage": stage, "agent_id": agent_id, "pipeline_id": str(pipeline_id), "structured_attempt": attempt, "structured_repair": attempt > 1, }, ) try: response = await engine.generate(request) except Exception as exc: raise AgenticPipelineExecutionError(f"{stage} model call failed") from exc payload = structured_payload_from_response(response) if payload is None: last_error_message = f"{stage} returned no structured output" continue try: parsed = schema_model.model_validate(payload) except ValidationError as exc: last_error_message = f"{stage} returned invalid structured output" last_validation_error = exc continue return parsed, response, attempt error = AgenticPipelineExecutionError(last_error_message) if last_validation_error is not None: raise error from last_validation_error raise error def _validate_grounding( source_ids: set[str], source_refs: Sequence[str], claims: Sequence[GroundedClaim] ) -> int: unknown = set(source_refs) - source_ids unknown.update(item.source_ref for item in claims if item.source_ref not in source_ids) return len(unknown) async def run_agentic_content_pipeline( *, conn: Any, engine: StructuredGenerationEngine, submission_id: UUID, pipeline_id: UUID, benchmark_record_id: UUID, qualification_id: UUID, source_packs: Sequence[AgenticSourcePack], content_kind: str, difficulty_level: int, variant_count: int, prompt_version: str = PROMPT_VERSION, trigger_kind: str = "source_pack", ) -> AgenticPipelineResult: if content_kind not in {"case", "rupture", "practice", "benchmark"}: raise AgenticPipelineRejectedError("unsupported content kind") if not 1 <= difficulty_level <= 5: raise AgenticPipelineRejectedError("difficulty level must be between 1 and 5") if not 3 <= variant_count <= 12: raise AgenticPipelineRejectedError("variant count must be between 3 and 12") sources = tuple(source_packs) source_context = validate_source_packs(sources) source_ids = {item.artifact.source_id for item in sources} prompt_payload = _generation_prompt_payload( source_context=source_context, content_kind=content_kind, difficulty_level=difficulty_level, prompt_version=prompt_version, trigger_kind=trigger_kind, ) prompt_sha256 = _sha256_text(_canonical_json(prompt_payload)) replay = await continuous_improvement_store.find_content_pipeline_submission( conn, submission_id=submission_id ) if replay is not None: expected = { "pipeline_id": str(pipeline_id), "benchmark_record_id": str(benchmark_record_id), "qualification_id": str(qualification_id), "prompt_sha256": prompt_sha256, } actual = {key: str(replay.get(key)) for key in expected} if actual != expected: raise continuous_improvement_store.ContinuousImprovementConflictError( "submission id was already used with different agentic input" ) return AgenticPipelineResult( submission_id=submission_id, pipeline_id=pipeline_id, qualification_id=qualification_id, candidate_catalog_entry_id=str(replay["candidate_catalog_entry_id"]), state="pending_human_approval", human_approval_required=True, catalog_promoted=False, idempotent_replay=True, clinical_claim_allowed=False, draft_id=str(replay["draft_id"]), benchmark_id=str(replay["benchmark_id"]), red_team_review_count=int(replay["red_team_review_count"]), benchmark_variant_count=int(replay["benchmark_variant_count"]), agent_calls_executed=0, trigger_kind=trigger_kind, ) slug = pipeline_id.hex generation_messages = [ EngineMessage( role="system", content=( "너는 교육용 상담 시뮬레이션 콘텐츠 생성 agent다. 승인된 source만 사용하고 " "실존 또는 합성 고유 이름, 이니셜, 기관, 주소, 연락처, 구체 날짜, 금액, 식별 번호를 " "절대 만들지 마라. 등장인물은 오직 'fictional client'와 'learner' 같은 역할명으로만 " "지칭하라. hidden_answer의 고유 marker나 연속된 답안 구절은 visible 필드에 " "반복하지 말고, 모든 grounding claim은 입력 source_id를 참조하라. 출력은 schema만 따른다." ), ), EngineMessage(role="user", content=_canonical_json(prompt_payload)), ] generated_model, generation_response, generation_attempts = await _generate_structured( engine=engine, stage="draft_generation", agent_id="g8-content-generator-v1", messages=generation_messages, schema_model=GeneratedDraftPayload, pipeline_id=pipeline_id, ) agent_calls_executed = generation_attempts generated = GeneratedDraftPayload.model_validate(generated_model.model_dump()) unsupported_claims = _validate_grounding( source_ids, generated.source_refs, generated.grounded_claims ) if unsupported_claims: raise AgenticPipelineRejectedError("generated draft has unsupported claims") _assert_pii_free(generated.model_dump(mode="json"), stage="generated draft") answer_overlap = _answer_overlap_count( generated.hidden_answer, generated.visible_payload() ) if answer_overlap: raise AgenticPipelineRejectedError("generated draft exposes hidden answer tokens") payload_sha256 = _sha256_text(_canonical_json(generated.model_dump(mode="json"))) draft_id = f"oas-g8-draft-{slug}" draft = GeneratedContentDraft( draft_id=draft_id, content_kind=content_kind, source_refs=tuple(generated.source_refs), generation_model=f"{generation_response.provider}/{generation_response.model}", prompt_version=prompt_version, prompt_sha256=prompt_sha256, payload_sha256=payload_sha256, synthetic_identity_id=f"synthetic-identity-{slug}", difficulty_level=difficulty_level, hidden_answer_fingerprint=_sha256_text(generated.hidden_answer), visible_answer_overlap_tokens=0, pii_findings=0, unsupported_clinical_claims=0, ) async def review_lane( lane: str, dimensions: tuple[ReviewDimension, ...] ) -> tuple[IndependentRedTeamReview, int]: agent_id = f"g8-redteam-{lane}-v1" messages = [ EngineMessage( role="system", content=( "너는 다른 reviewer와 대화하거나 결과를 공유하지 않는 독립 red-team agent다. " f"오직 {','.join(dimensions)} 차원만 공격적으로 검토하라. 문제를 찾으면 finding을 " "반드시 남기되, source와 draft에서 입증되는 실제 계약 위반만 finding이다. 선택적 문구 개선, " "취향, 근거 없는 가능성은 finding으로 만들지 말고 실제 위반이 없으면 findings=[]를 반환하라. " "출력은 schema만 따른다." ), ), EngineMessage( role="user", content=_canonical_json( { "draft": generated.model_dump(mode="json"), "sources": source_context, "reviewed_payload_sha256": payload_sha256, } ), ), ] output_model, _, attempts = await _generate_structured( engine=engine, stage=f"red_team_{lane}", agent_id=agent_id, messages=messages, schema_model=RedTeamOutput, pipeline_id=pipeline_id, ) output = RedTeamOutput.model_validate(output_model.model_dump()) if set(output.dimensions) != set(dimensions): raise AgenticPipelineExecutionError( f"red_team_{lane} did not attest assigned dimensions" ) if any(item.dimension not in dimensions for item in output.findings): raise AgenticPipelineExecutionError( f"red_team_{lane} returned a cross-lane finding" ) findings = tuple( RedTeamFinding( finding_id=f"oas-g8-finding-{slug}-{lane}-{index}", dimension=item.dimension, severity=item.severity, state="open", evidence_ref=( f"audit://continuous-improvement/{slug}/redteam/{lane}/{index}" ), ) for index, item in enumerate(output.findings, start=1) ) return ( IndependentRedTeamReview( review_id=f"oas-g8-review-{slug}-{lane}", draft_id=draft_id, reviewer_agent_id=agent_id, dimensions=dimensions, findings=findings, reviewed_payload_sha256=payload_sha256, ), attempts, ) review_results = tuple( await asyncio.gather( *(review_lane(lane, dimensions) for lane, dimensions in _REVIEW_LANES) ) ) reviews = tuple(review for review, _ in review_results) agent_calls_executed += sum(attempts for _, attempts in review_results) if any(review.findings for review in reviews): finding_summary = sorted( f"{finding.dimension}:{finding.severity}" for review in reviews for finding in review.findings ) raise AgenticPipelineRejectedError( "red-team agents found unresolved issues: " + ",".join(finding_summary) ) variant_messages = [ EngineMessage( role="system", content=( f"원본 교육 초안을 의미는 유지하되 표현·관계 맥락·난이도를 바꾼 {variant_count}개 " "적대 benchmark variant로 변형하라. 각 hidden_answer 고유 marker나 연속 답안 구절은 visible 필드에 " "반복하지 말고 입력 source_id만 참조하라. 출력은 schema만 따른다." ), ), EngineMessage( role="user", content=_canonical_json( {"draft": generated.model_dump(mode="json"), "sources": source_context} ), ), ] variant_model, _, variant_attempts = await _generate_structured( engine=engine, stage="benchmark_variant_generation", agent_id="g8-benchmark-variant-generator-v1", messages=variant_messages, schema_model=BenchmarkVariantPackOutput, pipeline_id=pipeline_id, ) agent_calls_executed += variant_attempts variant_pack = BenchmarkVariantPackOutput.model_validate(variant_model.model_dump()) if len(variant_pack.variants) != variant_count: raise AgenticPipelineExecutionError( "benchmark variant generator returned an unexpected variant count" ) for index, variant in enumerate(variant_pack.variants, start=1): _assert_pii_free(variant.model_dump(mode="json"), stage=f"benchmark variant {index}") if set(variant.source_refs) - source_ids: raise AgenticPipelineRejectedError( f"benchmark variant {index} has unsupported claims" ) if _answer_overlap_count(variant.hidden_answer, variant.visible_payload()): raise AgenticPipelineRejectedError( f"benchmark variant {index} exposes hidden answer tokens" ) async def judge_variant( index: int, variant: BenchmarkVariantOutput ) -> tuple[BenchmarkJudgeOutput, int]: messages = [ EngineMessage( role="system", content=( "너는 독립 benchmark judge다. answer leakage, PII, 무근거 임상 주장, 안전 실패, " "점수·문구 암기 보상 해킹을 각각 판정하라. 하나라도 있으면 passed=false다. " "출력은 schema만 따른다." ), ), EngineMessage( role="user", content=_canonical_json( { "source_ids": sorted(source_ids), "original_draft": generated.model_dump(mode="json"), "variant": variant.model_dump(mode="json"), } ), ), ] output_model, _, attempts = await _generate_structured( engine=engine, stage=f"benchmark_judge_{index}", agent_id=f"g8-benchmark-judge-{index}-v1", messages=messages, schema_model=BenchmarkJudgeOutput, pipeline_id=pipeline_id, ) return BenchmarkJudgeOutput.model_validate(output_model.model_dump()), attempts judgment_results = tuple( await asyncio.gather( *( judge_variant(index, variant) for index, variant in enumerate(variant_pack.variants, start=1) ) ) ) judgments = tuple(judgment for judgment, _ in judgment_results) agent_calls_executed += sum(attempts for _, attempts in judgment_results) pass_count = sum(1 for item in judgments if item.passed) benchmark_id = f"oas-g8-benchmark-{slug}" benchmark = ContentBenchmarkQualification( benchmark_id=benchmark_id, draft_id=draft_id, variant_count=len(judgments), variant_pass_rate=pass_count / len(judgments), answer_leakage_count=sum(1 for item in judgments if item.answer_leakage), pii_finding_count=sum(1 for item in judgments if item.pii_found), unsupported_claim_count=sum(1 for item in judgments if item.unsupported_claim), safety_failure_count=sum(1 for item in judgments if item.safety_failure), reward_hacking_count=sum(1 for item in judgments if item.reward_hacking), evidence_refs=tuple( f"audit://continuous-improvement/{slug}/benchmark/judge/{index}" for index in range(1, len(judgments) + 1) ), ) if not benchmark.qualified: raise AgenticPipelineRejectedError("benchmark qualification failed") stored = await continuous_improvement_store.submit_content_pipeline( conn, submission_id=submission_id, pipeline_id=pipeline_id, benchmark_record_id=benchmark_record_id, qualification_id=qualification_id, draft=draft, sources=[item.artifact for item in sources], reviews=list(reviews), benchmark=benchmark, draft_payload=generated.model_dump(mode="json"), ) if ( stored.get("state") != "pending_human_approval" or stored.get("human_approval_required") is not True or stored.get("catalog_promoted") is not False or stored.get("clinical_claim_allowed") is not False ): raise AgenticPipelineExecutionError( "content store violated the mandatory human approval boundary" ) return AgenticPipelineResult( **stored, draft_id=draft_id, benchmark_id=benchmark_id, red_team_review_count=len(reviews), benchmark_variant_count=len(judgments), agent_calls_executed=agent_calls_executed, trigger_kind=trigger_kind, ) __all__ = [ "AgenticPipelineError", "AgenticPipelineExecutionError", "AgenticPipelineRejectedError", "AgenticPipelineResult", "AgenticSourcePack", "BenchmarkJudgeOutput", "BenchmarkVariantOutput", "BenchmarkVariantPackOutput", "GeneratedDraftPayload", "GroundedClaim", "HttpRollbackExecutor", "PROMPT_VERSION", "RedTeamOutput", "build_configured_rollback_executor", "run_agentic_content_pipeline", "source_pack_from_operational_incident", "validate_source_packs", ]