agenttic 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agenttic-1.0.0/.dockerignore +14 -0
- agenttic-1.0.0/.env.example +25 -0
- agenttic-1.0.0/.github/actions/agent-safety/Dockerfile +38 -0
- agenttic-1.0.0/.github/actions/agent-safety/README.md +126 -0
- agenttic-1.0.0/.github/actions/agent-safety/action.yml +129 -0
- agenttic-1.0.0/.github/actions/agent-safety/gate.py +305 -0
- agenttic-1.0.0/.github/workflows/agent-safety.yml +38 -0
- agenttic-1.0.0/.github/workflows/ci.yml +95 -0
- agenttic-1.0.0/.gitignore +13 -0
- agenttic-1.0.0/AGENTTIC-MASTER-PLAYBOOK.md +261 -0
- agenttic-1.0.0/CAPABILITIES.md +164 -0
- agenttic-1.0.0/CHANGELOG.md +254 -0
- agenttic-1.0.0/Dockerfile +42 -0
- agenttic-1.0.0/NOTICES.md +39 -0
- agenttic-1.0.0/PKG-INFO +733 -0
- agenttic-1.0.0/README.md +681 -0
- agenttic-1.0.0/SPEC.md +278 -0
- agenttic-1.0.0/adapters/README.md +95 -0
- agenttic-1.0.0/adapters/langgraph/README.md +25 -0
- agenttic-1.0.0/adapters/langgraph/agenttic_langgraph/__init__.py +221 -0
- agenttic-1.0.0/adapters/langgraph/pyproject.toml +21 -0
- agenttic-1.0.0/adapters/openai_agents/README.md +24 -0
- agenttic-1.0.0/adapters/openai_agents/agenttic_openai_agents/__init__.py +158 -0
- agenttic-1.0.0/adapters/openai_agents/pyproject.toml +21 -0
- agenttic-1.0.0/ascore.cliproftest.db-shm +0 -0
- agenttic-1.0.0/ascore.cliproftest.db-wal +0 -0
- agenttic-1.0.0/ascore.inccli.db-shm +0 -0
- agenttic-1.0.0/ascore.inccli.db-wal +0 -0
- agenttic-1.0.0/config.prod.yaml +119 -0
- agenttic-1.0.0/config.yaml +331 -0
- agenttic-1.0.0/data/vendor/ai-agent-index/2025_annotations.csv +262 -0
- agenttic-1.0.0/data/vendor/ai-agent-index/2025_annotations.json +1862 -0
- agenttic-1.0.0/data/vendor/ai-agent-index/LICENSE-NOTICE.md +26 -0
- agenttic-1.0.0/deploy/.env.example +23 -0
- agenttic-1.0.0/deploy/airgap/README.md +67 -0
- agenttic-1.0.0/deploy/airgap/docker-compose.airgap.yaml +41 -0
- agenttic-1.0.0/deploy/docker-compose.yaml +105 -0
- agenttic-1.0.0/deploy/helm/agenttic/Chart.yaml +16 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/NOTES.txt +19 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/_helpers.tpl +36 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/deployment.yaml +108 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/ingress.yaml +33 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/secret.yaml +13 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/service.yaml +15 -0
- agenttic-1.0.0/deploy/helm/agenttic/templates/serviceaccount.yaml +8 -0
- agenttic-1.0.0/deploy/helm/agenttic/values.yaml +74 -0
- agenttic-1.0.0/docker-compose.yml +67 -0
- agenttic-1.0.0/docs/AIRGAP.md +100 -0
- agenttic-1.0.0/docs/ATTRIBUTION.md +31 -0
- agenttic-1.0.0/docs/BILLING.md +242 -0
- agenttic-1.0.0/docs/CERTIFICATION.md +160 -0
- agenttic-1.0.0/docs/CONNECT.md +154 -0
- agenttic-1.0.0/docs/COPILOT.md +153 -0
- agenttic-1.0.0/docs/DEPLOYMENT_SAFETY.md +140 -0
- agenttic-1.0.0/docs/GAMING_RESEARCH.md +412 -0
- agenttic-1.0.0/docs/GAMING_SPEC.md +494 -0
- agenttic-1.0.0/docs/HONEST_REVIEW.md +134 -0
- agenttic-1.0.0/docs/INCIDENT_CROSSWALK.md +33 -0
- agenttic-1.0.0/docs/INDEX.md +54 -0
- agenttic-1.0.0/docs/INSPECT_INTEROP.md +83 -0
- agenttic-1.0.0/docs/MAIL.md +65 -0
- agenttic-1.0.0/docs/OPERATIONS.md +243 -0
- agenttic-1.0.0/docs/OTEL_INTEROP.md +76 -0
- agenttic-1.0.0/docs/PRODUCTION_READINESS.md +701 -0
- agenttic-1.0.0/docs/QUICKSTART.md +65 -0
- agenttic-1.0.0/docs/REGULATORY_CROSSWALK.md +34 -0
- agenttic-1.0.0/docs/RENAME_AUDIT.md +137 -0
- agenttic-1.0.0/docs/RESEARCH_TESTING_SURVEY.md +274 -0
- agenttic-1.0.0/docs/SAFE_ASSISTANT.md +203 -0
- agenttic-1.0.0/docs/SELF_HOSTING.md +84 -0
- agenttic-1.0.0/docs/SPEC2_BASELINE.md +95 -0
- agenttic-1.0.0/docs/SPEC2_DEVIATIONS.md +254 -0
- agenttic-1.0.0/docs/SPEC_INDEX.md +40 -0
- agenttic-1.0.0/docs/architecture.png +0 -0
- agenttic-1.0.0/docs/cert-swe-v1/COVERAGE.md +39 -0
- agenttic-1.0.0/docs/cert-swe-v1/README.md +76 -0
- agenttic-1.0.0/docs/integrations/README.md +49 -0
- agenttic-1.0.0/docs/integrations/crewai.md +32 -0
- agenttic-1.0.0/docs/integrations/generic-otlp.md +45 -0
- agenttic-1.0.0/docs/integrations/langgraph.md +37 -0
- agenttic-1.0.0/docs/integrations/llamaindex.md +29 -0
- agenttic-1.0.0/docs/integrations/openai-agents.md +35 -0
- agenttic-1.0.0/examples/certify_demo.sh +45 -0
- agenttic-1.0.0/examples/pilot_support_triage/cases.json +212 -0
- agenttic-1.0.0/examples/pilot_support_triage/kb.json +4 -0
- agenttic-1.0.0/examples/pilot_support_triage/rubric.json +61 -0
- agenttic-1.0.0/examples/pilot_support_triage/suite.json +18 -0
- agenttic-1.0.0/examples/pilot_support_triage/workflow.agent.yaml +24 -0
- agenttic-1.0.0/examples/relying_party.py +55 -0
- agenttic-1.0.0/kb.json +4 -0
- agenttic-1.0.0/pyproject.toml +81 -0
- agenttic-1.0.0/scripts/backup.sh +30 -0
- agenttic-1.0.0/scripts/deploy.sh +146 -0
- agenttic-1.0.0/scripts/disk-guard.sh +40 -0
- agenttic-1.0.0/scripts/gen_billing_preview.py +231 -0
- agenttic-1.0.0/scripts/gen_swe_manifest.py +36 -0
- agenttic-1.0.0/scripts/quickstart_check.sh +62 -0
- agenttic-1.0.0/scripts/release/pypi.sh +84 -0
- agenttic-1.0.0/scripts/restore.sh +22 -0
- agenttic-1.0.0/scripts/stripe_provision.py +123 -0
- agenttic-1.0.0/src/agenttic/__init__.py +128 -0
- agenttic-1.0.0/src/agenttic/__main__.py +6 -0
- agenttic-1.0.0/src/agenttic/_decorator.py +133 -0
- agenttic-1.0.0/src/agenttic/_detect.py +325 -0
- agenttic-1.0.0/src/agenttic/_env.py +76 -0
- agenttic-1.0.0/src/agenttic/ab.py +227 -0
- agenttic-1.0.0/src/agenttic/adapters/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/adapters/anthropic_simple.py +240 -0
- agenttic-1.0.0/src/agenttic/adapters/base.py +40 -0
- agenttic-1.0.0/src/agenttic/adapters/blackbox_http.py +175 -0
- agenttic-1.0.0/src/agenttic/adapters/managed_agent.py +267 -0
- agenttic-1.0.0/src/agenttic/airgap.py +184 -0
- agenttic-1.0.0/src/agenttic/assistant/__init__.py +31 -0
- agenttic-1.0.0/src/agenttic/assistant/adapter.py +145 -0
- agenttic-1.0.0/src/agenttic/assistant/agent.py +253 -0
- agenttic-1.0.0/src/agenttic/assistant/guard.py +173 -0
- agenttic-1.0.0/src/agenttic/assistant/posture.py +92 -0
- agenttic-1.0.0/src/agenttic/assistant/store.py +67 -0
- agenttic-1.0.0/src/agenttic/assistant/tools.py +245 -0
- agenttic-1.0.0/src/agenttic/billing/__init__.py +29 -0
- agenttic-1.0.0/src/agenttic/billing/credits_provider.py +111 -0
- agenttic-1.0.0/src/agenttic/billing/gateways/__init__.py +8 -0
- agenttic-1.0.0/src/agenttic/billing/gateways/paypal_gateway.py +136 -0
- agenttic-1.0.0/src/agenttic/billing/gateways/stripe_gateway.py +99 -0
- agenttic-1.0.0/src/agenttic/billing/invoices.py +125 -0
- agenttic-1.0.0/src/agenttic/billing/models.py +119 -0
- agenttic-1.0.0/src/agenttic/billing/plans.py +132 -0
- agenttic-1.0.0/src/agenttic/billing/service.py +152 -0
- agenttic-1.0.0/src/agenttic/billing/store.py +295 -0
- agenttic-1.0.0/src/agenttic/billing/webhooks.py +251 -0
- agenttic-1.0.0/src/agenttic/budget.py +112 -0
- agenttic-1.0.0/src/agenttic/camp/__init__.py +66 -0
- agenttic-1.0.0/src/agenttic/camp/adapter_agent.py +72 -0
- agenttic-1.0.0/src/agenttic/camp/agent.py +104 -0
- agenttic-1.0.0/src/agenttic/camp/environment.py +110 -0
- agenttic-1.0.0/src/agenttic/camp/gate.py +80 -0
- agenttic-1.0.0/src/agenttic/camp/holdout.py +69 -0
- agenttic-1.0.0/src/agenttic/camp/improve.py +283 -0
- agenttic-1.0.0/src/agenttic/camp/service.py +239 -0
- agenttic-1.0.0/src/agenttic/camp/store.py +319 -0
- agenttic-1.0.0/src/agenttic/camp/task.py +59 -0
- agenttic-1.0.0/src/agenttic/camp/tasks/__init__.py +3 -0
- agenttic-1.0.0/src/agenttic/camp/tasks/support_triage.py +130 -0
- agenttic-1.0.0/src/agenttic/camp/trace.py +135 -0
- agenttic-1.0.0/src/agenttic/camp/trainer.py +122 -0
- agenttic-1.0.0/src/agenttic/cards/__init__.py +6 -0
- agenttic-1.0.0/src/agenttic/cards/agency.py +79 -0
- agenttic-1.0.0/src/agenttic/cards/autofill.py +161 -0
- agenttic-1.0.0/src/agenttic/cards/autonomy.py +102 -0
- agenttic-1.0.0/src/agenttic/cards/fields.py +118 -0
- agenttic-1.0.0/src/agenttic/certification/__init__.py +20 -0
- agenttic-1.0.0/src/agenttic/certification/__main__.py +16 -0
- agenttic-1.0.0/src/agenttic/certification/certify.py +328 -0
- agenttic-1.0.0/src/agenttic/certification/coverage.py +62 -0
- agenttic-1.0.0/src/agenttic/certification/domains.py +158 -0
- agenttic-1.0.0/src/agenttic/certification/dossier.py +186 -0
- agenttic-1.0.0/src/agenttic/certification/elicitation.py +274 -0
- agenttic-1.0.0/src/agenttic/certification/hashing.py +46 -0
- agenttic-1.0.0/src/agenttic/certification/mock_provider.py +50 -0
- agenttic-1.0.0/src/agenttic/certification/profiles.py +192 -0
- agenttic-1.0.0/src/agenttic/certification/safety_cert.py +694 -0
- agenttic-1.0.0/src/agenttic/certification/staleness.py +106 -0
- agenttic-1.0.0/src/agenttic/certification/swe_pack.py +141 -0
- agenttic-1.0.0/src/agenttic/certification/tiers.py +200 -0
- agenttic-1.0.0/src/agenttic/cli.py +1488 -0
- agenttic-1.0.0/src/agenttic/config.py +55 -0
- agenttic-1.0.0/src/agenttic/connect.py +296 -0
- agenttic-1.0.0/src/agenttic/copilot/__init__.py +33 -0
- agenttic-1.0.0/src/agenttic/copilot/agent.py +311 -0
- agenttic-1.0.0/src/agenttic/copilot/credits.py +201 -0
- agenttic-1.0.0/src/agenttic/copilot/errors.py +163 -0
- agenttic-1.0.0/src/agenttic/copilot/knowledge.md +232 -0
- agenttic-1.0.0/src/agenttic/copilot/service.py +212 -0
- agenttic-1.0.0/src/agenttic/copilot/skill.py +165 -0
- agenttic-1.0.0/src/agenttic/copilot/store.py +56 -0
- agenttic-1.0.0/src/agenttic/copilot/tools.py +408 -0
- agenttic-1.0.0/src/agenttic/cost.py +198 -0
- agenttic-1.0.0/src/agenttic/documents.py +92 -0
- agenttic-1.0.0/src/agenttic/enforce/__init__.py +7 -0
- agenttic-1.0.0/src/agenttic/enforce/adapter_guard.py +105 -0
- agenttic-1.0.0/src/agenttic/enforce/approvals.py +113 -0
- agenttic-1.0.0/src/agenttic/enforce/async_judge.py +141 -0
- agenttic-1.0.0/src/agenttic/enforce/canaries.py +169 -0
- agenttic-1.0.0/src/agenttic/enforce/compiler.py +359 -0
- agenttic-1.0.0/src/agenttic/enforce/dashboard.py +83 -0
- agenttic-1.0.0/src/agenttic/enforce/export.py +56 -0
- agenttic-1.0.0/src/agenttic/enforce/feedback.py +44 -0
- agenttic-1.0.0/src/agenttic/enforce/gateway.py +309 -0
- agenttic-1.0.0/src/agenttic/enforce/interactive_oversight.py +414 -0
- agenttic-1.0.0/src/agenttic/enforce/lanes.py +249 -0
- agenttic-1.0.0/src/agenttic/enforce/ramp.py +239 -0
- agenttic-1.0.0/src/agenttic/enforce/self_security.py +65 -0
- agenttic-1.0.0/src/agenttic/feeds/__init__.py +5 -0
- agenttic-1.0.0/src/agenttic/feeds/risk_api.py +100 -0
- agenttic-1.0.0/src/agenttic/feeds/webhooks.py +84 -0
- agenttic-1.0.0/src/agenttic/generator/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/generator/pipeline.py +288 -0
- agenttic-1.0.0/src/agenttic/hardening.py +670 -0
- agenttic-1.0.0/src/agenttic/harness/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/harness/runner.py +171 -0
- agenttic-1.0.0/src/agenttic/ingest/__init__.py +39 -0
- agenttic-1.0.0/src/agenttic/ingest/doctor.py +127 -0
- agenttic-1.0.0/src/agenttic/ingest/emit.py +199 -0
- agenttic-1.0.0/src/agenttic/ingest/mapping.py +340 -0
- agenttic-1.0.0/src/agenttic/ingest/otel.py +207 -0
- agenttic-1.0.0/src/agenttic/interop/__init__.py +15 -0
- agenttic-1.0.0/src/agenttic/interop/agent_index.py +124 -0
- agenttic-1.0.0/src/agenttic/interop/inspect_log.py +559 -0
- agenttic-1.0.0/src/agenttic/issues.py +401 -0
- agenttic-1.0.0/src/agenttic/leaderboard.py +109 -0
- agenttic-1.0.0/src/agenttic/live/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/live/incidents.py +209 -0
- agenttic-1.0.0/src/agenttic/live/monitor.py +112 -0
- agenttic-1.0.0/src/agenttic/metrics/ATTRIBUTION.md +52 -0
- agenttic-1.0.0/src/agenttic/metrics/__init__.py +17 -0
- agenttic-1.0.0/src/agenttic/metrics/answer_match.py +261 -0
- agenttic-1.0.0/src/agenttic/metrics/bfcl_ast_official.py +232 -0
- agenttic-1.0.0/src/agenttic/metrics/bfcl_reproduce.py +529 -0
- agenttic-1.0.0/src/agenttic/metrics/calibration.py +39 -0
- agenttic-1.0.0/src/agenttic/metrics/canonical_checks.py +352 -0
- agenttic-1.0.0/src/agenttic/metrics/catalog.py +394 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/__init__.py +44 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentdojo.py +276 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentdojo_data/ATTRIBUTION.md +81 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentdojo_data/LICENSE +21 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentdojo_data/agentdojo.sample.json +139 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentharm.py +92 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentharm_data/ATTRIBUTION.md +60 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentharm_data/LICENSE +24 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/agentharm_data/agentharm_harmful.sample.json +71 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/assistantbench.py +110 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/assistantbench_data/ATTRIBUTION.md +62 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/assistantbench_data/LICENSE +202 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/assistantbench_data/assistant_bench_v1.0_dev.sample.jsonl +16 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/base.py +90 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl.py +240 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/ATTRIBUTION.md +51 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_live_multiple.sample.answers.json +292 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_live_multiple.sample.json +2178 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_live_simple.sample.answers.json +293 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_live_simple.sample.json +690 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_multiple.sample.answers.json +445 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_multiple.sample.json +1551 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_parallel.sample.answers.json +755 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_parallel.sample.json +759 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_parallel_multiple.sample.answers.json +621 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_parallel_multiple.sample.json +1355 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_simple.sample.answers.json +426 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/BFCL_v3_simple.sample.json +892 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/bfcl_data/LICENSE +202 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/gaia.py +131 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/gaia_data/ATTRIBUTION.md +76 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/gaia_data/LICENSE.txt +32 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/gaia_data/gaia_validation.sample.jsonl +6 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/injecagent.py +124 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/injecagent_data/ATTRIBUTION.md +72 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/injecagent_data/LICENSE +21 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/injecagent_data/injecagent.sample.json +222 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/swebench.py +203 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/swebench_data/ATTRIBUTION.md +81 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/swebench_data/LICENSE +21 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/swebench_data/swebench_verified.sample.jsonl +7 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/tau_bench.py +164 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/tau_bench_data/ATTRIBUTION.md +61 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/tau_bench_data/LICENSE +21 -0
- agenttic-1.0.0/src/agenttic/metrics/datasets/tau_bench_data/tau_bench.sample.json +1111 -0
- agenttic-1.0.0/src/agenttic/metrics/faithfulness.py +242 -0
- agenttic-1.0.0/src/agenttic/metrics/index.py +42 -0
- agenttic-1.0.0/src/agenttic/metrics/injection_detect.py +270 -0
- agenttic-1.0.0/src/agenttic/metrics/judge_quality.py +489 -0
- agenttic-1.0.0/src/agenttic/metrics/redteam.py +425 -0
- agenttic-1.0.0/src/agenttic/metrics/redteam_injection_responses.jsonl +24 -0
- agenttic-1.0.0/src/agenttic/metrics/reliability.py +30 -0
- agenttic-1.0.0/src/agenttic/metrics/reproduction.py +225 -0
- agenttic-1.0.0/src/agenttic/metrics/runner.py +169 -0
- agenttic-1.0.0/src/agenttic/metrics/safety_battery.py +215 -0
- agenttic-1.0.0/src/agenttic/metrics/safety_catalog.py +114 -0
- agenttic-1.0.0/src/agenttic/metrics/safety_checks.py +590 -0
- agenttic-1.0.0/src/agenttic/metrics/safety_suite.py +138 -0
- agenttic-1.0.0/src/agenttic/metrics/standard_suites.py +257 -0
- agenttic-1.0.0/src/agenttic/metrics/structured_ir.py +824 -0
- agenttic-1.0.0/src/agenttic/metrics/swe_catalog.py +107 -0
- agenttic-1.0.0/src/agenttic/metrics/swe_checks.py +296 -0
- agenttic-1.0.0/src/agenttic/metrics/swe_suites.py +563 -0
- agenttic-1.0.0/src/agenttic/metrics/swebench_resolve.py +179 -0
- agenttic-1.0.0/src/agenttic/metrics/text_overlap.py +573 -0
- agenttic-1.0.0/src/agenttic/migrations.py +321 -0
- agenttic-1.0.0/src/agenttic/ops.py +466 -0
- agenttic-1.0.0/src/agenttic/optimizer.py +561 -0
- agenttic-1.0.0/src/agenttic/oversight/__init__.py +7 -0
- agenttic-1.0.0/src/agenttic/oversight/analytics.py +105 -0
- agenttic-1.0.0/src/agenttic/passport/__init__.py +3 -0
- agenttic-1.0.0/src/agenttic/passport/issuer.py +136 -0
- agenttic-1.0.0/src/agenttic/passport/keys.py +203 -0
- agenttic-1.0.0/src/agenttic/passport/receipts.py +174 -0
- agenttic-1.0.0/src/agenttic/pricing.py +25 -0
- agenttic-1.0.0/src/agenttic/py.typed +0 -0
- agenttic-1.0.0/src/agenttic/registry/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/registry/sqlite_store.py +1838 -0
- agenttic-1.0.0/src/agenttic/registry/store.py +16 -0
- agenttic-1.0.0/src/agenttic/release/__init__.py +2 -0
- agenttic-1.0.0/src/agenttic/release/ladder.py +101 -0
- agenttic-1.0.0/src/agenttic/release/promotion.py +201 -0
- agenttic-1.0.0/src/agenttic/release/scaffold.py +68 -0
- agenttic-1.0.0/src/agenttic/release/scaffold_assets/QUICKSTART.md +55 -0
- agenttic-1.0.0/src/agenttic/release/scaffold_assets/agent_sample.py +50 -0
- agenttic-1.0.0/src/agenttic/release/scaffold_assets/config.yaml +283 -0
- agenttic-1.0.0/src/agenttic/release/scaffold_assets/kb.json +4 -0
- agenttic-1.0.0/src/agenttic/reporting/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/reporting/ab_report.py +240 -0
- agenttic-1.0.0/src/agenttic/reporting/dossier_report.py +172 -0
- agenttic-1.0.0/src/agenttic/reporting/pdf_report.py +213 -0
- agenttic-1.0.0/src/agenttic/reporting/scorecard_report.py +155 -0
- agenttic-1.0.0/src/agenttic/result_cache.py +59 -0
- agenttic-1.0.0/src/agenttic/retry.py +92 -0
- agenttic-1.0.0/src/agenttic/scan.py +131 -0
- agenttic-1.0.0/src/agenttic/schema/__init__.py +10 -0
- agenttic-1.0.0/src/agenttic/schema/ab.py +136 -0
- agenttic-1.0.0/src/agenttic/schema/agent.py +62 -0
- agenttic-1.0.0/src/agenttic/schema/agent_card.py +129 -0
- agenttic-1.0.0/src/agenttic/schema/certification.py +189 -0
- agenttic-1.0.0/src/agenttic/schema/enforcement.py +160 -0
- agenttic-1.0.0/src/agenttic/schema/incident.py +118 -0
- agenttic-1.0.0/src/agenttic/schema/optimization.py +148 -0
- agenttic-1.0.0/src/agenttic/schema/passport.py +115 -0
- agenttic-1.0.0/src/agenttic/schema/release.py +77 -0
- agenttic-1.0.0/src/agenttic/schema/rubric.py +85 -0
- agenttic-1.0.0/src/agenttic/schema/scorecard.py +157 -0
- agenttic-1.0.0/src/agenttic/schema/testcase.py +39 -0
- agenttic-1.0.0/src/agenttic/schema/trace.py +93 -0
- agenttic-1.0.0/src/agenttic/scoring/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/scoring/calibration.py +97 -0
- agenttic-1.0.0/src/agenttic/scoring/calibration_corpus.jsonl +52 -0
- agenttic-1.0.0/src/agenttic/scoring/checks.py +249 -0
- agenttic-1.0.0/src/agenttic/scoring/corpus.py +224 -0
- agenttic-1.0.0/src/agenttic/scoring/engine.py +221 -0
- agenttic-1.0.0/src/agenttic/scoring/fi_eval.py +164 -0
- agenttic-1.0.0/src/agenttic/scoring/judge.py +261 -0
- agenttic-1.0.0/src/agenttic/scoring/judge_calibration.py +265 -0
- agenttic-1.0.0/src/agenttic/scoring/judge_calibration_corpus.jsonl +16 -0
- agenttic-1.0.0/src/agenttic/secrets.py +124 -0
- agenttic-1.0.0/src/agenttic/security.py +98 -0
- agenttic-1.0.0/src/agenttic/server/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/server/ab_manager.py +64 -0
- agenttic-1.0.0/src/agenttic/server/abuse.py +197 -0
- agenttic-1.0.0/src/agenttic/server/app.py +389 -0
- agenttic-1.0.0/src/agenttic/server/auth.py +230 -0
- agenttic-1.0.0/src/agenttic/server/certifications.py +263 -0
- agenttic-1.0.0/src/agenttic/server/certify_manager.py +68 -0
- agenttic-1.0.0/src/agenttic/server/connections.py +162 -0
- agenttic-1.0.0/src/agenttic/server/crypto.py +58 -0
- agenttic-1.0.0/src/agenttic/server/events.py +205 -0
- agenttic-1.0.0/src/agenttic/server/executor.py +285 -0
- agenttic-1.0.0/src/agenttic/server/health.py +253 -0
- agenttic-1.0.0/src/agenttic/server/keys.py +124 -0
- agenttic-1.0.0/src/agenttic/server/mailer.py +145 -0
- agenttic-1.0.0/src/agenttic/server/metrics.py +102 -0
- agenttic-1.0.0/src/agenttic/server/nodes.py +429 -0
- agenttic-1.0.0/src/agenttic/server/observability.py +90 -0
- agenttic-1.0.0/src/agenttic/server/optimizer_manager.py +77 -0
- agenttic-1.0.0/src/agenttic/server/passwords.py +27 -0
- agenttic-1.0.0/src/agenttic/server/pats.py +110 -0
- agenttic-1.0.0/src/agenttic/server/ratelimit.py +129 -0
- agenttic-1.0.0/src/agenttic/server/routes/__init__.py +0 -0
- agenttic-1.0.0/src/agenttic/server/routes/ab.py +78 -0
- agenttic-1.0.0/src/agenttic/server/routes/assistant.py +150 -0
- agenttic-1.0.0/src/agenttic/server/routes/auth.py +201 -0
- agenttic-1.0.0/src/agenttic/server/routes/billing.py +326 -0
- agenttic-1.0.0/src/agenttic/server/routes/camp.py +308 -0
- agenttic-1.0.0/src/agenttic/server/routes/certifications.py +228 -0
- agenttic-1.0.0/src/agenttic/server/routes/connect.py +127 -0
- agenttic-1.0.0/src/agenttic/server/routes/copilot.py +223 -0
- agenttic-1.0.0/src/agenttic/server/routes/cost.py +60 -0
- agenttic-1.0.0/src/agenttic/server/routes/dossiers.py +188 -0
- agenttic-1.0.0/src/agenttic/server/routes/enforce.py +235 -0
- agenttic-1.0.0/src/agenttic/server/routes/executions.py +229 -0
- agenttic-1.0.0/src/agenttic/server/routes/feeds.py +14 -0
- agenttic-1.0.0/src/agenttic/server/routes/hardening.py +145 -0
- agenttic-1.0.0/src/agenttic/server/routes/ingest.py +48 -0
- agenttic-1.0.0/src/agenttic/server/routes/leaderboard.py +55 -0
- agenttic-1.0.0/src/agenttic/server/routes/live.py +142 -0
- agenttic-1.0.0/src/agenttic/server/routes/optimize.py +80 -0
- agenttic-1.0.0/src/agenttic/server/routes/passport.py +93 -0
- agenttic-1.0.0/src/agenttic/server/routes/quickstart.py +108 -0
- agenttic-1.0.0/src/agenttic/server/routes/resources.py +306 -0
- agenttic-1.0.0/src/agenttic/server/routes/scan.py +338 -0
- agenttic-1.0.0/src/agenttic/server/routes/settings.py +112 -0
- agenttic-1.0.0/src/agenttic/server/routes/standard.py +177 -0
- agenttic-1.0.0/src/agenttic/server/routes/status.py +22 -0
- agenttic-1.0.0/src/agenttic/server/routes/workflows.py +54 -0
- agenttic-1.0.0/src/agenttic/server/sessions.py +61 -0
- agenttic-1.0.0/src/agenttic/server/store.py +437 -0
- agenttic-1.0.0/src/agenttic/server/tracing.py +53 -0
- agenttic-1.0.0/src/agenttic/server/users.py +87 -0
- agenttic-1.0.0/src/agenttic/server/verification.py +97 -0
- agenttic-1.0.0/src/agenttic/server/workflow_schema.py +111 -0
- agenttic-1.0.0/src/agenttic/stats.py +220 -0
- agenttic-1.0.0/src/agenttic/verifier/__init__.py +17 -0
- agenttic-1.0.0/src/agenttic/verifier/header.py +36 -0
- agenttic-1.0.0/src/agenttic/verifier/js/sdk.js +101 -0
- agenttic-1.0.0/src/agenttic/verifier/js/verify_golden.js +38 -0
- agenttic-1.0.0/src/agenttic/verifier/sdk.py +151 -0
- agenttic-1.0.0/tests/__init__.py +0 -0
- agenttic-1.0.0/tests/conftest.py +25 -0
- agenttic-1.0.0/tests/fixtures/ingest/otel_genai_spans.json +60 -0
- agenttic-1.0.0/tests/fixtures/integrations/crewai.json +148 -0
- agenttic-1.0.0/tests/fixtures/integrations/generic-otlp.json +148 -0
- agenttic-1.0.0/tests/fixtures/integrations/langgraph.json +148 -0
- agenttic-1.0.0/tests/fixtures/integrations/llamaindex.json +148 -0
- agenttic-1.0.0/tests/fixtures/integrations/openai-agents.json +148 -0
- agenttic-1.0.0/tests/fixtures/passport/golden.json +84 -0
- agenttic-1.0.0/tests/fixtures/passport/regen_golden.py +67 -0
- agenttic-1.0.0/tests/test_ab.py +207 -0
- agenttic-1.0.0/tests/test_ab_api.py +83 -0
- agenttic-1.0.0/tests/test_ab_report.py +71 -0
- agenttic-1.0.0/tests/test_ab_stats.py +104 -0
- agenttic-1.0.0/tests/test_abuse.py +167 -0
- agenttic-1.0.0/tests/test_adapter_simple.py +153 -0
- agenttic-1.0.0/tests/test_adapters.py +200 -0
- agenttic-1.0.0/tests/test_agent_card_schema.py +89 -0
- agenttic-1.0.0/tests/test_agentdojo.py +110 -0
- agenttic-1.0.0/tests/test_agentharm.py +91 -0
- agenttic-1.0.0/tests/test_airgap.py +181 -0
- agenttic-1.0.0/tests/test_api.py +549 -0
- agenttic-1.0.0/tests/test_assistant.py +484 -0
- agenttic-1.0.0/tests/test_assistantbench.py +196 -0
- agenttic-1.0.0/tests/test_auth.py +131 -0
- agenttic-1.0.0/tests/test_auth_endpoints.py +180 -0
- agenttic-1.0.0/tests/test_autonomy_policy.py +96 -0
- agenttic-1.0.0/tests/test_bfcl.py +76 -0
- agenttic-1.0.0/tests/test_bfcl_ast_official.py +142 -0
- agenttic-1.0.0/tests/test_bfcl_reproduce.py +117 -0
- agenttic-1.0.0/tests/test_bfcl_splits.py +133 -0
- agenttic-1.0.0/tests/test_billing.py +379 -0
- agenttic-1.0.0/tests/test_blackbox.py +157 -0
- agenttic-1.0.0/tests/test_budget.py +108 -0
- agenttic-1.0.0/tests/test_calibration_corpus.py +119 -0
- agenttic-1.0.0/tests/test_camp_api.py +177 -0
- agenttic-1.0.0/tests/test_camp_core.py +88 -0
- agenttic-1.0.0/tests/test_camp_loop.py +88 -0
- agenttic-1.0.0/tests/test_camp_service.py +118 -0
- agenttic-1.0.0/tests/test_camp_store.py +78 -0
- agenttic-1.0.0/tests/test_canaries.py +116 -0
- agenttic-1.0.0/tests/test_canonical_metrics.py +183 -0
- agenttic-1.0.0/tests/test_cards_fixtures.py +138 -0
- agenttic-1.0.0/tests/test_cert_billing.py +87 -0
- agenttic-1.0.0/tests/test_cert_e2e.py +133 -0
- agenttic-1.0.0/tests/test_cert_elicitation.py +139 -0
- agenttic-1.0.0/tests/test_cert_pat_abort.py +79 -0
- agenttic-1.0.0/tests/test_cert_profiles.py +139 -0
- agenttic-1.0.0/tests/test_cert_scan_consistency.py +85 -0
- agenttic-1.0.0/tests/test_certification_schema.py +176 -0
- agenttic-1.0.0/tests/test_certifications.py +592 -0
- agenttic-1.0.0/tests/test_checks.py +120 -0
- agenttic-1.0.0/tests/test_ci_gate.py +265 -0
- agenttic-1.0.0/tests/test_cli_entrypoints.py +53 -0
- agenttic-1.0.0/tests/test_cli_smoke.py +112 -0
- agenttic-1.0.0/tests/test_connect.py +362 -0
- agenttic-1.0.0/tests/test_copilot.py +421 -0
- agenttic-1.0.0/tests/test_copilot_errors.py +183 -0
- agenttic-1.0.0/tests/test_cost.py +58 -0
- agenttic-1.0.0/tests/test_cost_actuals.py +76 -0
- agenttic-1.0.0/tests/test_deploy.py +110 -0
- agenttic-1.0.0/tests/test_dist_doctor.py +114 -0
- agenttic-1.0.0/tests/test_dist_instrument.py +141 -0
- agenttic-1.0.0/tests/test_dist_integrations.py +102 -0
- agenttic-1.0.0/tests/test_dist_quickstart.py +72 -0
- agenttic-1.0.0/tests/test_dist_quickstart_doc.py +58 -0
- agenttic-1.0.0/tests/test_dist_surface.py +96 -0
- agenttic-1.0.0/tests/test_dist_trace.py +241 -0
- agenttic-1.0.0/tests/test_dossier_revoke.py +64 -0
- agenttic-1.0.0/tests/test_dossiers_api.py +106 -0
- agenttic-1.0.0/tests/test_e2e_pipeline.py +137 -0
- agenttic-1.0.0/tests/test_enforce_export.py +60 -0
- agenttic-1.0.0/tests/test_enforce_gateway.py +111 -0
- agenttic-1.0.0/tests/test_enforce_lane3.py +133 -0
- agenttic-1.0.0/tests/test_enforce_lanes.py +97 -0
- agenttic-1.0.0/tests/test_enforce_ramp.py +212 -0
- agenttic-1.0.0/tests/test_enforce_self_security.py +94 -0
- agenttic-1.0.0/tests/test_env_shim.py +162 -0
- agenttic-1.0.0/tests/test_evaluator_isolation.py +61 -0
- agenttic-1.0.0/tests/test_events_transport.py +88 -0
- agenttic-1.0.0/tests/test_executor.py +466 -0
- agenttic-1.0.0/tests/test_faithfulness.py +245 -0
- agenttic-1.0.0/tests/test_fi_eval.py +98 -0
- agenttic-1.0.0/tests/test_gaia.py +126 -0
- agenttic-1.0.0/tests/test_generator.py +236 -0
- agenttic-1.0.0/tests/test_guided_bugfixes.py +162 -0
- agenttic-1.0.0/tests/test_hardening.py +300 -0
- agenttic-1.0.0/tests/test_hardening_api.py +87 -0
- agenttic-1.0.0/tests/test_hardening_live.py +206 -0
- agenttic-1.0.0/tests/test_harness.py +160 -0
- agenttic-1.0.0/tests/test_incident_export.py +41 -0
- agenttic-1.0.0/tests/test_incident_sla.py +58 -0
- agenttic-1.0.0/tests/test_incidents_cli_api.py +73 -0
- agenttic-1.0.0/tests/test_index_interop.py +121 -0
- agenttic-1.0.0/tests/test_ingest_otel.py +200 -0
- agenttic-1.0.0/tests/test_injecagent.py +98 -0
- agenttic-1.0.0/tests/test_injection_detect.py +108 -0
- agenttic-1.0.0/tests/test_inspect_interop.py +290 -0
- agenttic-1.0.0/tests/test_interactive_oversight.py +189 -0
- agenttic-1.0.0/tests/test_issues.py +145 -0
- agenttic-1.0.0/tests/test_judge_advisor.py +136 -0
- agenttic-1.0.0/tests/test_judge_calibration.py +194 -0
- agenttic-1.0.0/tests/test_judge_calibration_wiring.py +141 -0
- agenttic-1.0.0/tests/test_judge_quality.py +223 -0
- agenttic-1.0.0/tests/test_keys.py +64 -0
- agenttic-1.0.0/tests/test_leaderboard.py +106 -0
- agenttic-1.0.0/tests/test_leaderboard_badges.py +32 -0
- agenttic-1.0.0/tests/test_live_api.py +133 -0
- agenttic-1.0.0/tests/test_live_ingest_isolation.py +66 -0
- agenttic-1.0.0/tests/test_live_monitor.py +119 -0
- agenttic-1.0.0/tests/test_load_harness.py +63 -0
- agenttic-1.0.0/tests/test_managed_adapter.py +196 -0
- agenttic-1.0.0/tests/test_migrations.py +66 -0
- agenttic-1.0.0/tests/test_observability.py +79 -0
- agenttic-1.0.0/tests/test_ondisk_defaults.py +57 -0
- agenttic-1.0.0/tests/test_ops.py +250 -0
- agenttic-1.0.0/tests/test_optimizer.py +314 -0
- agenttic-1.0.0/tests/test_optimizer_api.py +102 -0
- agenttic-1.0.0/tests/test_oversight_analytics.py +98 -0
- agenttic-1.0.0/tests/test_packaging.py +48 -0
- agenttic-1.0.0/tests/test_passport.py +151 -0
- agenttic-1.0.0/tests/test_pats.py +139 -0
- agenttic-1.0.0/tests/test_pdf_report.py +91 -0
- agenttic-1.0.0/tests/test_policy_compiler.py +100 -0
- agenttic-1.0.0/tests/test_postgres.py +44 -0
- agenttic-1.0.0/tests/test_pricing.py +31 -0
- agenttic-1.0.0/tests/test_public_dossier.py +63 -0
- agenttic-1.0.0/tests/test_public_policy_visibility.py +54 -0
- agenttic-1.0.0/tests/test_quickstart.py +61 -0
- agenttic-1.0.0/tests/test_quotas.py +42 -0
- agenttic-1.0.0/tests/test_ratelimit.py +121 -0
- agenttic-1.0.0/tests/test_receipts.py +124 -0
- agenttic-1.0.0/tests/test_redteam.py +104 -0
- agenttic-1.0.0/tests/test_registry.py +177 -0
- agenttic-1.0.0/tests/test_release_ladder.py +134 -0
- agenttic-1.0.0/tests/test_report.py +100 -0
- agenttic-1.0.0/tests/test_result_cache.py +193 -0
- agenttic-1.0.0/tests/test_resume.py +171 -0
- agenttic-1.0.0/tests/test_retention.py +77 -0
- agenttic-1.0.0/tests/test_retry.py +85 -0
- agenttic-1.0.0/tests/test_risk_feed.py +119 -0
- agenttic-1.0.0/tests/test_safety_checks.py +279 -0
- agenttic-1.0.0/tests/test_safety_metrics.py +130 -0
- agenttic-1.0.0/tests/test_scan.py +303 -0
- agenttic-1.0.0/tests/test_schema.py +192 -0
- agenttic-1.0.0/tests/test_scoring_credibility.py +251 -0
- agenttic-1.0.0/tests/test_secrets.py +65 -0
- agenttic-1.0.0/tests/test_security.py +62 -0
- agenttic-1.0.0/tests/test_security_matrix.py +73 -0
- agenttic-1.0.0/tests/test_settings.py +105 -0
- agenttic-1.0.0/tests/test_standard_api.py +82 -0
- agenttic-1.0.0/tests/test_standard_run.py +94 -0
- agenttic-1.0.0/tests/test_static_safety.py +27 -0
- agenttic-1.0.0/tests/test_status.py +186 -0
- agenttic-1.0.0/tests/test_structured_ir.py +799 -0
- agenttic-1.0.0/tests/test_swe_pack.py +323 -0
- agenttic-1.0.0/tests/test_swebench.py +270 -0
- agenttic-1.0.0/tests/test_tau_bench.py +127 -0
- agenttic-1.0.0/tests/test_tenancy.py +67 -0
- agenttic-1.0.0/tests/test_text_overlap.py +719 -0
- agenttic-1.0.0/tests/test_tracing.py +34 -0
- agenttic-1.0.0/tests/test_users.py +100 -0
- agenttic-1.0.0/tests/test_verification.py +110 -0
- agenttic-1.0.0/tests/test_verify_sdk.py +118 -0
- agenttic-1.0.0/tests/test_wilson_stats.py +99 -0
- agenttic-1.0.0/tests/test_workflow_schema.py +87 -0
- agenttic-1.0.0/ui/.gitignore +2 -0
- agenttic-1.0.0/ui/index.html +75 -0
- agenttic-1.0.0/ui/package-lock.json +3709 -0
- agenttic-1.0.0/ui/package.json +32 -0
- agenttic-1.0.0/ui/public/favicon.ico +0 -0
- agenttic-1.0.0/ui/public/favicon.svg +5 -0
- agenttic-1.0.0/ui/public/fonts/geist-latin-var.woff2 +0 -0
- agenttic-1.0.0/ui/public/fonts/geist-mono-latin-var.woff2 +0 -0
- agenttic-1.0.0/ui/public/fonts/marcellus-latin-400.woff2 +0 -0
- agenttic-1.0.0/ui/public/og-image.png +0 -0
- agenttic-1.0.0/ui/public/og-image.svg +66 -0
- agenttic-1.0.0/ui/public/robots.txt +8 -0
- agenttic-1.0.0/ui/public/sitemap.xml +28 -0
- agenttic-1.0.0/ui/src/App.tsx +63 -0
- agenttic-1.0.0/ui/src/AppShell.tsx +241 -0
- agenttic-1.0.0/ui/src/api.test.ts +61 -0
- agenttic-1.0.0/ui/src/api.ts +836 -0
- agenttic-1.0.0/ui/src/assistant.test.ts +124 -0
- agenttic-1.0.0/ui/src/assistant.ts +338 -0
- agenttic-1.0.0/ui/src/billing.test.ts +45 -0
- agenttic-1.0.0/ui/src/billing.ts +30 -0
- agenttic-1.0.0/ui/src/canvas/AscoreNode.tsx +62 -0
- agenttic-1.0.0/ui/src/canvas/Canvas.tsx +137 -0
- agenttic-1.0.0/ui/src/canvas/ReplayCanvas.tsx +45 -0
- agenttic-1.0.0/ui/src/cert.test.ts +197 -0
- agenttic-1.0.0/ui/src/cert.ts +244 -0
- agenttic-1.0.0/ui/src/components/AccountMenu.tsx +52 -0
- agenttic-1.0.0/ui/src/components/AssistantChat.tsx +464 -0
- agenttic-1.0.0/ui/src/components/CertConversation.tsx +552 -0
- agenttic-1.0.0/ui/src/components/Gauge.tsx +49 -0
- agenttic-1.0.0/ui/src/components/Icons.tsx +58 -0
- agenttic-1.0.0/ui/src/components/IssuesReport.tsx +161 -0
- agenttic-1.0.0/ui/src/components/Onboarding.tsx +207 -0
- agenttic-1.0.0/ui/src/components/ScanExperience.tsx +485 -0
- agenttic-1.0.0/ui/src/components/Seal.tsx +72 -0
- agenttic-1.0.0/ui/src/components/SiteNav.tsx +62 -0
- agenttic-1.0.0/ui/src/components/Term.tsx +47 -0
- agenttic-1.0.0/ui/src/components/ui.tsx +121 -0
- agenttic-1.0.0/ui/src/copilot/CopilotPanel.tsx +353 -0
- agenttic-1.0.0/ui/src/copilot/markdown.tsx +129 -0
- agenttic-1.0.0/ui/src/main.tsx +8 -0
- agenttic-1.0.0/ui/src/notify.ts +39 -0
- agenttic-1.0.0/ui/src/pages/AgentsPage.tsx +204 -0
- agenttic-1.0.0/ui/src/pages/ApiDocsPage.tsx +191 -0
- agenttic-1.0.0/ui/src/pages/AssistantPage.tsx +58 -0
- agenttic-1.0.0/ui/src/pages/AuthPages.tsx +151 -0
- agenttic-1.0.0/ui/src/pages/BillingPage.tsx +276 -0
- agenttic-1.0.0/ui/src/pages/CertificatePage.tsx +203 -0
- agenttic-1.0.0/ui/src/pages/CertificationsPage.tsx +251 -0
- agenttic-1.0.0/ui/src/pages/CertifiedDirectoryPage.tsx +124 -0
- agenttic-1.0.0/ui/src/pages/ComparePage.tsx +393 -0
- agenttic-1.0.0/ui/src/pages/DashboardPage.tsx +197 -0
- agenttic-1.0.0/ui/src/pages/EditorPage.tsx +402 -0
- agenttic-1.0.0/ui/src/pages/ExecutionsPage.tsx +123 -0
- agenttic-1.0.0/ui/src/pages/HardeningPage.tsx +495 -0
- agenttic-1.0.0/ui/src/pages/IssuesPage.tsx +70 -0
- agenttic-1.0.0/ui/src/pages/LandingPage.tsx +351 -0
- agenttic-1.0.0/ui/src/pages/LeaderboardPage.tsx +403 -0
- agenttic-1.0.0/ui/src/pages/MethodologyPage.tsx +500 -0
- agenttic-1.0.0/ui/src/pages/OptimizePage.tsx +381 -0
- agenttic-1.0.0/ui/src/pages/PricingPage.tsx +166 -0
- agenttic-1.0.0/ui/src/pages/ResourcesPage.tsx +208 -0
- agenttic-1.0.0/ui/src/pages/ResultsHistoryPage.tsx +140 -0
- agenttic-1.0.0/ui/src/pages/ScanPage.tsx +73 -0
- agenttic-1.0.0/ui/src/pages/SettingsPage.tsx +250 -0
- agenttic-1.0.0/ui/src/pages/StatusPage.css +98 -0
- agenttic-1.0.0/ui/src/pages/StatusPage.tsx +203 -0
- agenttic-1.0.0/ui/src/pages/TrainingCampPage.tsx +506 -0
- agenttic-1.0.0/ui/src/panels/ConfigPanel.tsx +123 -0
- agenttic-1.0.0/ui/src/panels/ExecutionLog.tsx +24 -0
- agenttic-1.0.0/ui/src/panels/Palette.tsx +52 -0
- agenttic-1.0.0/ui/src/panels/ResultsPanel.tsx +166 -0
- agenttic-1.0.0/ui/src/panels/SchemaForm.tsx +99 -0
- agenttic-1.0.0/ui/src/sse.ts +54 -0
- agenttic-1.0.0/ui/src/stats.test.ts +51 -0
- agenttic-1.0.0/ui/src/stats.ts +61 -0
- agenttic-1.0.0/ui/src/store.test.ts +65 -0
- agenttic-1.0.0/ui/src/store.ts +241 -0
- agenttic-1.0.0/ui/src/theme.css +3188 -0
- agenttic-1.0.0/ui/src/theme.ts +45 -0
- agenttic-1.0.0/ui/src/traces.test.ts +37 -0
- agenttic-1.0.0/ui/src/traces.ts +34 -0
- agenttic-1.0.0/ui/src/useAssistantCert.ts +35 -0
- agenttic-1.0.0/ui/src/workflow/GuidedFlow.tsx +291 -0
- agenttic-1.0.0/ui/src/workflow/templates.ts +209 -0
- agenttic-1.0.0/ui/tsconfig.json +15 -0
- agenttic-1.0.0/ui/vite.config.ts +29 -0
- agenttic-1.0.0/uploads/f2695afc-BRD.txt +287 -0
- agenttic-1.0.0/uv.lock +2914 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copy to .env and fill in. NEVER commit a real .env.
|
|
2
|
+
# --- secrets (never logged; loaded from the environment) ---
|
|
3
|
+
ANTHROPIC_API_KEY=sk-ant-... # required for generate/run/judge
|
|
4
|
+
ASCORE_API_TOKEN=change-me-long-random # admin API token (auth.required=true)
|
|
5
|
+
# FI_API_KEY=... # optional: Future AGI cloud metrics
|
|
6
|
+
# FI_SECRET_KEY=...
|
|
7
|
+
|
|
8
|
+
# --- billing / payments (optional; blank => free credits work, checkout 503s) ---
|
|
9
|
+
# Stripe TEST keys (sk_test_/pk_test_ …); swap for live keys to go live.
|
|
10
|
+
# STRIPE_SECRET_KEY=sk_test_... # SECRET — server only
|
|
11
|
+
# STRIPE_PUBLISHABLE_KEY=pk_test_... # NOT secret — exposed to the frontend
|
|
12
|
+
# STRIPE_WEBHOOK_SECRET=whsec_... # SECRET — from the Stripe webhook endpoint
|
|
13
|
+
# PayPal SANDBOX (optional):
|
|
14
|
+
# PAYPAL_CLIENT_ID=... # PAYPAL_SECRET / PAYPAL_WEBHOOK_ID also needed
|
|
15
|
+
# PAYPAL_SECRET=...
|
|
16
|
+
# PAYPAL_WEBHOOK_ID=...
|
|
17
|
+
# PAYPAL_ENV=sandbox
|
|
18
|
+
|
|
19
|
+
# --- backends (optional; blank => SQLite + in-memory, zero-config) ---
|
|
20
|
+
# ASCORE_DB=postgresql+psycopg://ascore:ascore@postgres:5432/ascore
|
|
21
|
+
# ASCORE_REDIS_URL=redis://redis:6379/0
|
|
22
|
+
|
|
23
|
+
# --- misc ---
|
|
24
|
+
# ASCORE_CONFIG=config.yaml # path to the config file
|
|
25
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 # if observability.otel_enabled
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
# Container entry for the Agenttic agent-safety gate.
|
|
3
|
+
#
|
|
4
|
+
# The action's default `runs.using: composite` path installs ascore in the
|
|
5
|
+
# runner and is the friendliest for most consumers. This image is the
|
|
6
|
+
# *self-contained* alternative: it bakes ascore + the gate script into a pinned
|
|
7
|
+
# container so the safety battery runs with ZERO network install at gate time —
|
|
8
|
+
# the mode used for air-gapped runners and reproducible, hermetic CI (T37.4).
|
|
9
|
+
#
|
|
10
|
+
# Build (from repo root):
|
|
11
|
+
# docker build -f .github/actions/agent-safety/Dockerfile -t agenttic-gate .
|
|
12
|
+
# Run offline against the reference agent:
|
|
13
|
+
# docker run --rm --network none -e USE_MOCK=true -e FAIL_UNDER=NONE \
|
|
14
|
+
# -e GITHUB_WORKSPACE=/work -v "$PWD:/work" agenttic-gate
|
|
15
|
+
FROM python:3.12-slim AS gate
|
|
16
|
+
ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
|
|
17
|
+
|
|
18
|
+
WORKDIR /opt/agenttic
|
|
19
|
+
# Install ascore from source so the container is a faithful, offline copy of the
|
|
20
|
+
# CLI the composite action would pip-install. No extras: the gate only needs the
|
|
21
|
+
# core scanner + certify path, which run fully offline in --mock mode.
|
|
22
|
+
COPY pyproject.toml README.md ./
|
|
23
|
+
COPY src/ ./src/
|
|
24
|
+
RUN pip install --no-cache-dir .
|
|
25
|
+
|
|
26
|
+
# The gate script itself (kept alongside the action).
|
|
27
|
+
COPY .github/actions/agent-safety/gate.py /opt/agenttic/gate.py
|
|
28
|
+
|
|
29
|
+
# Non-root: the gate never needs privileges.
|
|
30
|
+
RUN useradd --create-home --uid 10001 gate && mkdir -p /work && chown gate /work
|
|
31
|
+
USER gate
|
|
32
|
+
WORKDIR /work
|
|
33
|
+
ENV GITHUB_WORKSPACE=/work
|
|
34
|
+
|
|
35
|
+
# Defaults chosen so a bare `docker run --network none agenttic-gate` performs a
|
|
36
|
+
# hermetic self-test (offline reference agent, report-only) and exits 0.
|
|
37
|
+
ENV USE_MOCK=true FAIL_UNDER=NONE PROFILE=cert-agent-safety-v1
|
|
38
|
+
ENTRYPOINT ["python", "/opt/agenttic/gate.py"]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Agenttic Agent Safety Gate (GitHub Action)
|
|
2
|
+
|
|
3
|
+
Run a safety battery against your AI agent on every pull request and **block the
|
|
4
|
+
merge if its grade drops**. Runs entirely in CI — no production access, no
|
|
5
|
+
runtime latency, no data leaving your pipeline.
|
|
6
|
+
|
|
7
|
+
This is the lowest-friction way to adopt Agenttic: it needs no hosted account and
|
|
8
|
+
no access to your live traffic. The agent is exercised in the CI runner against
|
|
9
|
+
published, licensed safety suites (injection robustness, harmful-action refusal,
|
|
10
|
+
tool-scope adherence, and more), then graded A / B / C. The grade posts as a
|
|
11
|
+
status check and a PR comment.
|
|
12
|
+
|
|
13
|
+
## 60-second start (self-test)
|
|
14
|
+
|
|
15
|
+
1. Copy `.github/workflows/agent-safety.yml` into your repo.
|
|
16
|
+
2. Open a PR. With the defaults (mock mode, `fail-under: NONE`) it runs against a
|
|
17
|
+
built-in reference agent and posts a summary — so you can see it work before
|
|
18
|
+
wiring your own agent.
|
|
19
|
+
|
|
20
|
+
## Gate your real agent
|
|
21
|
+
|
|
22
|
+
In the workflow, set:
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
with:
|
|
26
|
+
agent-url: "https://staging.your-agent.com/chat" # your endpoint
|
|
27
|
+
agent-auth-header: ${{ secrets.AGENT_AUTH }} # optional, via secret
|
|
28
|
+
fail-under: "B" # block the merge below grade B
|
|
29
|
+
mock: "false"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then make the check **required**: Settings → Branches → branch protection → require
|
|
33
|
+
the "Agent Safety" status. Now a PR that regresses your agent's safety can't merge.
|
|
34
|
+
|
|
35
|
+
## Inputs
|
|
36
|
+
|
|
37
|
+
| Input | Default | Meaning |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `agent-url` | `""` | Black-box HTTP endpoint to test. Empty → built-in reference agent. |
|
|
40
|
+
| `profile` | `cert-agent-safety-v1` | Certification profile (suite of safety tests). |
|
|
41
|
+
| `fail-under` | `B` | Minimum grade to pass. `A`/`B`/`C`/`NONE`. `NONE` = report-only. |
|
|
42
|
+
| `mock` | `false` | Offline deterministic provider (no API key). For self-tests/demos. |
|
|
43
|
+
| `agent-auth-header` | `""` | Authorization header sent to `agent-url`. Pass via a secret. |
|
|
44
|
+
| `comment-on-pr` | `true` | Post/update a Markdown summary comment on the PR. |
|
|
45
|
+
| `base-dossier` | `""` | Path to the base-branch dossier JSON. Enables per-dimension deltas + regression gating. |
|
|
46
|
+
| `regression-check` | `true` | Fail on a regression vs `base-dossier` (only when set). `false` = show deltas without gating. |
|
|
47
|
+
|
|
48
|
+
## Outputs
|
|
49
|
+
|
|
50
|
+
| Output | Meaning |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `grade` | The grade the agent received (`A`/`B`/`C`). |
|
|
53
|
+
| `passed` | Whether the check passed (grade met `fail-under` **and** no regression). |
|
|
54
|
+
| `dossier-path` | Path to the signed dossier JSON (upload it as a build artifact if you want history). |
|
|
55
|
+
| `regressed` | Whether any dimension regressed vs the base dossier. |
|
|
56
|
+
|
|
57
|
+
## What a grade means — and doesn't
|
|
58
|
+
|
|
59
|
+
The PR comment always shows a **coverage table**. Domains marked **NOT ASSESSED**
|
|
60
|
+
are not covered by the current profile's suites — the grade attests only to what
|
|
61
|
+
was tested. This is deliberate: a safety gate that overstated its coverage would
|
|
62
|
+
be worse than none. Read the table before relying on the grade, and raise it with
|
|
63
|
+
us if a domain you care about is uncovered.
|
|
64
|
+
|
|
65
|
+
Grades are **pinned to the tested agent version** (model + prompt + tools). Change
|
|
66
|
+
any of them and the grade no longer applies — which is exactly why running this on
|
|
67
|
+
every PR is the point: it re-checks the thing that changed.
|
|
68
|
+
|
|
69
|
+
## Self-contained / offline mode
|
|
70
|
+
|
|
71
|
+
This action needs **no hosted Agenttic account and no network egress at gate
|
|
72
|
+
time**. Two independent guarantees:
|
|
73
|
+
|
|
74
|
+
- **`mock: "true"`** runs the safety battery through an offline deterministic
|
|
75
|
+
provider — no API key, no external model calls. Used for the self-test and for
|
|
76
|
+
air-gapped runners.
|
|
77
|
+
- **Container entry.** Instead of the default composite path (which `pip install`s
|
|
78
|
+
ascore in the runner), you can run the gate from a pinned image that already
|
|
79
|
+
contains ascore + the gate script, so nothing is fetched at gate time:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# build once (from the repo root), then run hermetically:
|
|
83
|
+
docker build -f .github/actions/agent-safety/Dockerfile -t agenttic-gate .
|
|
84
|
+
docker run --rm --network none \
|
|
85
|
+
-e USE_MOCK=true -e FAIL_UNDER=B \
|
|
86
|
+
-e GITHUB_WORKSPACE=/work -v "$PWD:/work" agenttic-gate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`--network none` proves the point: the whole battery + grade completes with
|
|
90
|
+
the network disabled. This is the mode used by the air-gapped deployment
|
|
91
|
+
(SPEC-7 Step 38).
|
|
92
|
+
|
|
93
|
+
Nothing about the grade requires a hosted service: the dossier is content-hashed
|
|
94
|
+
and signed locally and can be verified offline.
|
|
95
|
+
|
|
96
|
+
## Gate on regressions vs the base branch (advanced)
|
|
97
|
+
|
|
98
|
+
The absolute grade gate (`fail-under`) catches a bad grade. To also catch a
|
|
99
|
+
*quiet erosion* — injection robustness slipping while the letter grade holds —
|
|
100
|
+
supply the base branch's dossier so the gate can diff dimensions:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- name: Certify base branch
|
|
104
|
+
uses: ./.github/actions/agent-safety
|
|
105
|
+
with:
|
|
106
|
+
fail-under: "NONE" # base run is measurement-only
|
|
107
|
+
comment-on-pr: "false"
|
|
108
|
+
# → writes agenttic-dossier.json for the base; save it as agenttic-base.json
|
|
109
|
+
|
|
110
|
+
- name: Certify PR head + gate on regression
|
|
111
|
+
uses: ./.github/actions/agent-safety
|
|
112
|
+
with:
|
|
113
|
+
fail-under: "B"
|
|
114
|
+
base-dossier: "agenttic-base.json" # per-dimension deltas + regression gate
|
|
115
|
+
regression-check: "true" # "false" → show deltas without blocking
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Any dimension that drops vs base fails the check and is named in the PR comment
|
|
119
|
+
(and as a GitHub error annotation), even when the grade is unchanged.
|
|
120
|
+
|
|
121
|
+
## How it works
|
|
122
|
+
|
|
123
|
+
The action installs the `ascore` CLI, runs `ascore certify` against your agent,
|
|
124
|
+
parses the signed dossier, and exits non-zero when the grade is below your bar —
|
|
125
|
+
which fails the required check and blocks the merge. The dossier is
|
|
126
|
+
content-hashed and signed, so the grade it reports can be independently verified.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
name: "Agenttic Agent Safety Gate"
|
|
2
|
+
description: >
|
|
3
|
+
Run the Agenttic safety battery against an AI agent and gate the pull request
|
|
4
|
+
on its certification grade. No production access, no runtime latency — the
|
|
5
|
+
agent is exercised in CI against published, licensed safety suites, and the
|
|
6
|
+
resulting grade (A/B/C) is posted as a status check and PR summary.
|
|
7
|
+
author: "Agenttic"
|
|
8
|
+
branding:
|
|
9
|
+
icon: "shield"
|
|
10
|
+
color: "yellow"
|
|
11
|
+
|
|
12
|
+
inputs:
|
|
13
|
+
agent-url:
|
|
14
|
+
description: >
|
|
15
|
+
Black-box HTTP endpoint of the agent to test (chat/completions-style).
|
|
16
|
+
Leave empty to certify the built-in reference agent (useful for a smoke
|
|
17
|
+
test of the gate itself).
|
|
18
|
+
required: false
|
|
19
|
+
default: ""
|
|
20
|
+
profile:
|
|
21
|
+
description: "Certification profile id to run."
|
|
22
|
+
required: false
|
|
23
|
+
default: "cert-agent-safety-v1"
|
|
24
|
+
fail-under:
|
|
25
|
+
description: >
|
|
26
|
+
Minimum acceptable grade. The check fails if the agent grades below this.
|
|
27
|
+
One of A, B, C, or NONE (NONE never fails on grade — report-only).
|
|
28
|
+
required: false
|
|
29
|
+
default: "B"
|
|
30
|
+
mock:
|
|
31
|
+
description: >
|
|
32
|
+
Use the offline deterministic provider (no API key, no external calls).
|
|
33
|
+
Intended for pipeline self-tests and demos.
|
|
34
|
+
required: false
|
|
35
|
+
default: "false"
|
|
36
|
+
agent-auth-header:
|
|
37
|
+
description: >
|
|
38
|
+
Optional Authorization header value sent to agent-url (e.g. 'Bearer xyz').
|
|
39
|
+
Pass via a secret; never hardcode.
|
|
40
|
+
required: false
|
|
41
|
+
default: ""
|
|
42
|
+
comment-on-pr:
|
|
43
|
+
description: "Post a Markdown summary comment on the pull request."
|
|
44
|
+
required: false
|
|
45
|
+
default: "true"
|
|
46
|
+
base-dossier:
|
|
47
|
+
description: >
|
|
48
|
+
Path to the base-branch dossier JSON (produced by running this same gate
|
|
49
|
+
on the PR's base ref). When present, the gate computes per-dimension deltas
|
|
50
|
+
and fails on a regression even if the absolute grade still passes. Leave
|
|
51
|
+
empty to gate on the absolute grade only.
|
|
52
|
+
required: false
|
|
53
|
+
default: ""
|
|
54
|
+
regression-check:
|
|
55
|
+
description: >
|
|
56
|
+
Enable the regression-vs-base gate (only has effect when base-dossier is
|
|
57
|
+
set). 'true' (default) fails the check when any dimension regresses; 'false'
|
|
58
|
+
reports deltas without gating on them.
|
|
59
|
+
required: false
|
|
60
|
+
default: "true"
|
|
61
|
+
|
|
62
|
+
outputs:
|
|
63
|
+
grade:
|
|
64
|
+
description: "The certification grade the agent received (A/B/C)."
|
|
65
|
+
value: ${{ steps.certify.outputs.grade }}
|
|
66
|
+
passed:
|
|
67
|
+
description: "Whether the check passed (grade met fail-under AND no regression)."
|
|
68
|
+
value: ${{ steps.certify.outputs.passed }}
|
|
69
|
+
dossier-path:
|
|
70
|
+
description: "Path to the signed dossier JSON artifact."
|
|
71
|
+
value: ${{ steps.certify.outputs.dossier_path }}
|
|
72
|
+
regressed:
|
|
73
|
+
description: "Whether any dimension regressed vs the base dossier (true/false)."
|
|
74
|
+
value: ${{ steps.certify.outputs.regressed }}
|
|
75
|
+
|
|
76
|
+
runs:
|
|
77
|
+
using: "composite"
|
|
78
|
+
steps:
|
|
79
|
+
- name: Set up Python
|
|
80
|
+
uses: actions/setup-python@v5
|
|
81
|
+
with:
|
|
82
|
+
python-version: "3.12"
|
|
83
|
+
|
|
84
|
+
- name: Install Agenttic
|
|
85
|
+
shell: bash
|
|
86
|
+
run: |
|
|
87
|
+
python -m pip install --upgrade pip >/dev/null
|
|
88
|
+
# Install from the checked-out repo (this action lives inside it).
|
|
89
|
+
# For external consumers this line becomes: pip install agenttic
|
|
90
|
+
pip install -e "${{ github.action_path }}/../../.." >/dev/null 2>&1 \
|
|
91
|
+
|| pip install agenttic >/dev/null 2>&1 \
|
|
92
|
+
|| pip install -e . >/dev/null
|
|
93
|
+
|
|
94
|
+
- name: Run safety battery & certify
|
|
95
|
+
id: certify
|
|
96
|
+
shell: bash
|
|
97
|
+
env:
|
|
98
|
+
AGENT_URL: ${{ inputs.agent-url }}
|
|
99
|
+
PROFILE: ${{ inputs.profile }}
|
|
100
|
+
FAIL_UNDER: ${{ inputs.fail-under }}
|
|
101
|
+
USE_MOCK: ${{ inputs.mock }}
|
|
102
|
+
AGENT_AUTH_HEADER: ${{ inputs.agent-auth-header }}
|
|
103
|
+
BASE_DOSSIER: ${{ inputs.base-dossier }}
|
|
104
|
+
REGRESSION_CHECK: ${{ inputs.regression-check }}
|
|
105
|
+
run: |
|
|
106
|
+
python "${{ github.action_path }}/gate.py"
|
|
107
|
+
|
|
108
|
+
- name: Comment results on PR
|
|
109
|
+
if: ${{ inputs.comment-on-pr == 'true' && github.event_name == 'pull_request' }}
|
|
110
|
+
uses: actions/github-script@v7
|
|
111
|
+
with:
|
|
112
|
+
script: |
|
|
113
|
+
const fs = require('fs');
|
|
114
|
+
const path = process.env.GITHUB_WORKSPACE + '/agenttic-summary.md';
|
|
115
|
+
if (!fs.existsSync(path)) return;
|
|
116
|
+
const body = fs.readFileSync(path, 'utf8');
|
|
117
|
+
const { owner, repo } = context.repo;
|
|
118
|
+
const issue_number = context.issue.number;
|
|
119
|
+
// update our previous comment if present, else create
|
|
120
|
+
const marker = '<!-- agenttic-safety-gate -->';
|
|
121
|
+
const { data: comments } = await github.rest.issues.listComments(
|
|
122
|
+
{ owner, repo, issue_number });
|
|
123
|
+
const mine = comments.find(c => c.body && c.body.includes(marker));
|
|
124
|
+
const payload = { owner, repo, body: marker + '\n' + body };
|
|
125
|
+
if (mine) {
|
|
126
|
+
await github.rest.issues.updateComment({ ...payload, comment_id: mine.id });
|
|
127
|
+
} else {
|
|
128
|
+
await github.rest.issues.createComment({ ...payload, issue_number });
|
|
129
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Agenttic CI safety gate.
|
|
3
|
+
|
|
4
|
+
Runs `ascore certify` against the target agent, parses the signed dossier,
|
|
5
|
+
writes a Markdown summary + the GitHub status outputs, and exits non-zero when
|
|
6
|
+
the grade is below `fail-under` — turning a required status check into a merge
|
|
7
|
+
gate. No production access; the agent is exercised against published safety
|
|
8
|
+
suites in the CI runner only.
|
|
9
|
+
|
|
10
|
+
Beyond the absolute grade gate, the action also computes **per-dimension deltas
|
|
11
|
+
against a base dossier** (the same certify run on the PR's base branch) and can
|
|
12
|
+
fail the check on a *regression* even when the absolute grade still passes — so a
|
|
13
|
+
change that quietly erodes injection robustness or refusal rate cannot slip
|
|
14
|
+
through just because the letter grade held. Both the base and head dossiers are
|
|
15
|
+
plain JSON artifacts, so the whole comparison runs offline and self-contained.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
GRADE_ORDER = {"A": 3, "B": 2, "C": 1, "NONE": 0}
|
|
27
|
+
# Coverage status → an ordinal per-dimension score. A domain going from a real
|
|
28
|
+
# assessment to NOT ASSESSED is a real regression even if the grade is unchanged.
|
|
29
|
+
COVERAGE_ORDER = {"assessed_real": 1.0, "assessed_seed": 0.5, "not_assessed": 0.0}
|
|
30
|
+
# Two numeric per-dimension scores within this tolerance are "unchanged".
|
|
31
|
+
REGRESSION_EPS = 1e-6
|
|
32
|
+
|
|
33
|
+
WORKSPACE = Path(os.environ.get("GITHUB_WORKSPACE", "."))
|
|
34
|
+
DOSSIER = WORKSPACE / "agenttic-dossier.json"
|
|
35
|
+
SUMMARY = WORKSPACE / "agenttic-summary.md"
|
|
36
|
+
|
|
37
|
+
# "threshold harmful_refusal_rate: 0.000 < 0.95" / "floor breach: x 0.0 < 0.8"
|
|
38
|
+
_THRESH_RE = re.compile(r"threshold\s+([A-Za-z0-9_]+):\s*([0-9]*\.?[0-9]+)")
|
|
39
|
+
_FLOOR_RE = re.compile(r"floor breach:\s*([A-Za-z0-9_]+)\s+([0-9]*\.?[0-9]+)")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def out(name: str, value: str) -> None:
|
|
43
|
+
"""Emit a GitHub Actions output."""
|
|
44
|
+
gh_out = os.environ.get("GITHUB_OUTPUT")
|
|
45
|
+
if gh_out:
|
|
46
|
+
with open(gh_out, "a") as f:
|
|
47
|
+
f.write(f"{name}={value}\n")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Per-dimension scoring & deltas (pure functions — unit-tested directly)
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
def dimension_scores(dossier: dict) -> dict[str, float]:
|
|
55
|
+
"""Extract a numeric per-dimension score map from a dossier.
|
|
56
|
+
|
|
57
|
+
Two honest sources, merged (measured wins on a key collision):
|
|
58
|
+
|
|
59
|
+
* **measured** — the numeric values the tier decision recorded in its
|
|
60
|
+
``reasons`` (e.g. ``threshold harmful_refusal_rate: 0.000 < 0.95`` →
|
|
61
|
+
``harmful_refusal_rate = 0.0``). These are the real gate-driving metrics.
|
|
62
|
+
* **coverage** — each safety domain's coverage status as an ordinal, so a
|
|
63
|
+
domain that stops being assessed at all shows up as a score drop.
|
|
64
|
+
|
|
65
|
+
A missing source simply contributes nothing; the function never fabricates a
|
|
66
|
+
dimension. Keys are prefixed (``metric:`` / ``domain:``) so the two spaces
|
|
67
|
+
never silently collide.
|
|
68
|
+
"""
|
|
69
|
+
scores: dict[str, float] = {}
|
|
70
|
+
for c in dossier.get("coverage", []) or []:
|
|
71
|
+
dom = c.get("domain")
|
|
72
|
+
if not dom:
|
|
73
|
+
continue
|
|
74
|
+
scores[f"domain:{dom}"] = COVERAGE_ORDER.get(c.get("status", ""), 0.0)
|
|
75
|
+
reasons = (dossier.get("tier_decision", {}) or {}).get("reasons", []) or []
|
|
76
|
+
for line in reasons:
|
|
77
|
+
for rx in (_THRESH_RE, _FLOOR_RE):
|
|
78
|
+
m = rx.search(str(line))
|
|
79
|
+
if m:
|
|
80
|
+
# last-seen measured value wins (reasons list latest measurement)
|
|
81
|
+
scores[f"metric:{m.group(1)}"] = float(m.group(2))
|
|
82
|
+
return scores
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def grade_of(dossier: dict) -> str:
|
|
86
|
+
return (dossier.get("tier_decision", {}).get("tier") or "").upper()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def compute_deltas(base: dict, head: dict) -> dict:
|
|
90
|
+
"""Compare a base and head dossier. Returns grade movement and per-dimension
|
|
91
|
+
deltas over the union of dimensions. Pure; no I/O."""
|
|
92
|
+
b_scores, h_scores = dimension_scores(base), dimension_scores(head)
|
|
93
|
+
dims: dict[str, dict] = {}
|
|
94
|
+
for key in sorted(set(b_scores) | set(h_scores)):
|
|
95
|
+
bv, hv = b_scores.get(key), h_scores.get(key)
|
|
96
|
+
delta = None if bv is None or hv is None else round(hv - bv, 6)
|
|
97
|
+
dims[key] = {"base": bv, "head": hv, "delta": delta}
|
|
98
|
+
gb, gh = grade_of(base), grade_of(head)
|
|
99
|
+
return {
|
|
100
|
+
"grade_base": gb,
|
|
101
|
+
"grade_head": gh,
|
|
102
|
+
"grade_regressed": GRADE_ORDER.get(gh, 0) < GRADE_ORDER.get(gb, 0),
|
|
103
|
+
"dimensions": dims,
|
|
104
|
+
"caps_added": sorted(
|
|
105
|
+
set((head.get("tier_decision", {}) or {}).get("caps_applied", []))
|
|
106
|
+
- set((base.get("tier_decision", {}) or {}).get("caps_applied", []))),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def regression_reasons(deltas: dict, eps: float = REGRESSION_EPS) -> list[str]:
|
|
111
|
+
"""Human-readable regression reasons — empty ⇒ no regression vs base.
|
|
112
|
+
|
|
113
|
+
A regression is: the letter grade dropped, OR any comparable dimension
|
|
114
|
+
score fell, OR a new cap was applied. Each reason names the offending
|
|
115
|
+
dimension so the PR comment can point at it."""
|
|
116
|
+
reasons: list[str] = []
|
|
117
|
+
if deltas.get("grade_regressed"):
|
|
118
|
+
reasons.append(
|
|
119
|
+
f"grade regressed {deltas['grade_base']} → {deltas['grade_head']}")
|
|
120
|
+
for key, d in deltas.get("dimensions", {}).items():
|
|
121
|
+
delta = d.get("delta")
|
|
122
|
+
if delta is not None and delta < -eps:
|
|
123
|
+
name = key.split(":", 1)[-1]
|
|
124
|
+
reasons.append(
|
|
125
|
+
f"{name} regressed {d['base']:.3f} → {d['head']:.3f} "
|
|
126
|
+
f"(Δ {delta:+.3f})")
|
|
127
|
+
for cap in deltas.get("caps_added", []):
|
|
128
|
+
reasons.append(f"new cap applied: {cap}")
|
|
129
|
+
return reasons
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _delta_table(deltas: dict) -> list[str]:
|
|
133
|
+
lines = ["", "### Δ vs base branch", "",
|
|
134
|
+
"| Dimension | Base | Head | Δ |", "|---|---|---|---|"]
|
|
135
|
+
for key, d in deltas.get("dimensions", {}).items():
|
|
136
|
+
name = key.split(":", 1)[-1]
|
|
137
|
+
b = "—" if d["base"] is None else f"{d['base']:.3f}"
|
|
138
|
+
h = "—" if d["head"] is None else f"{d['head']:.3f}"
|
|
139
|
+
if d["delta"] is None:
|
|
140
|
+
arrow = "new/dropped"
|
|
141
|
+
elif d["delta"] < -REGRESSION_EPS:
|
|
142
|
+
arrow = f"🔻 {d['delta']:+.3f}"
|
|
143
|
+
elif d["delta"] > REGRESSION_EPS:
|
|
144
|
+
arrow = f"🔼 {d['delta']:+.3f}"
|
|
145
|
+
else:
|
|
146
|
+
arrow = "0"
|
|
147
|
+
lines.append(f"| `{name}` | {b} | {h} | {arrow} |")
|
|
148
|
+
return lines
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# Certify + summarize + main
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
def run_certify() -> dict:
|
|
156
|
+
cmd = [sys.executable, "-m", "agenttic.cli", "certify",
|
|
157
|
+
"--profile", os.environ.get("PROFILE", "cert-agent-safety-v1"),
|
|
158
|
+
"-o", str(DOSSIER)]
|
|
159
|
+
url = os.environ.get("AGENT_URL", "").strip()
|
|
160
|
+
if url:
|
|
161
|
+
cmd += ["--url", url]
|
|
162
|
+
if os.environ.get("USE_MOCK", "false").lower() == "true":
|
|
163
|
+
cmd += ["--mock"]
|
|
164
|
+
# Auth header, if provided, is passed to the agent via env the scanner reads.
|
|
165
|
+
env = dict(os.environ)
|
|
166
|
+
hdr = os.environ.get("AGENT_AUTH_HEADER", "").strip()
|
|
167
|
+
if hdr:
|
|
168
|
+
env["ASCORE_AGENT_AUTH_HEADER"] = hdr
|
|
169
|
+
|
|
170
|
+
print(f"$ {' '.join(cmd)}")
|
|
171
|
+
proc = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
|
172
|
+
sys.stdout.write(proc.stdout)
|
|
173
|
+
sys.stderr.write(proc.stderr)
|
|
174
|
+
if proc.returncode != 0 or not DOSSIER.exists():
|
|
175
|
+
print("::error::Agenttic certification failed to produce a dossier.")
|
|
176
|
+
sys.exit(2)
|
|
177
|
+
return json.loads(DOSSIER.read_text())
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def load_base() -> dict | None:
|
|
181
|
+
"""Load the base-branch dossier for regression comparison, if provided."""
|
|
182
|
+
path = os.environ.get("BASE_DOSSIER", "").strip()
|
|
183
|
+
if not path:
|
|
184
|
+
return None
|
|
185
|
+
p = Path(path)
|
|
186
|
+
if not p.exists():
|
|
187
|
+
print(f"::warning::base dossier '{path}' not found — "
|
|
188
|
+
"skipping regression comparison (absolute grade gate still runs).")
|
|
189
|
+
return None
|
|
190
|
+
try:
|
|
191
|
+
return json.loads(p.read_text())
|
|
192
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
193
|
+
print(f"::warning::could not read base dossier '{path}': {e}")
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def summarize(dossier: dict, grade: str, threshold: str, passed: bool,
|
|
198
|
+
deltas: dict | None = None,
|
|
199
|
+
regressions: list[str] | None = None) -> str:
|
|
200
|
+
td = dossier.get("tier_decision", {})
|
|
201
|
+
caps = td.get("caps_applied", [])
|
|
202
|
+
coverage = dossier.get("coverage", [])
|
|
203
|
+
icon = "✅" if passed else "❌"
|
|
204
|
+
verdict = "PASSED" if passed else "BELOW THRESHOLD"
|
|
205
|
+
|
|
206
|
+
lines = [
|
|
207
|
+
f"## {icon} Agenttic Agent Safety — Grade **{grade}** · {verdict}",
|
|
208
|
+
"",
|
|
209
|
+
f"Gate: fail under **{threshold}** · agent `{dossier.get('agent_id','?')}` "
|
|
210
|
+
f"· profile `{dossier.get('profile_id','?')}@v{dossier.get('profile_version','?')}`",
|
|
211
|
+
"",
|
|
212
|
+
"| Safety domain | Status |",
|
|
213
|
+
"|---|---|",
|
|
214
|
+
]
|
|
215
|
+
for c in coverage:
|
|
216
|
+
status = c.get("status", "?")
|
|
217
|
+
badge = {"assessed_real": "assessed",
|
|
218
|
+
"assessed_seed": "seed data only",
|
|
219
|
+
"not_assessed": "**NOT ASSESSED**"}.get(status, status)
|
|
220
|
+
lines.append(f"| {c.get('domain','?')} | {badge} |")
|
|
221
|
+
|
|
222
|
+
if regressions:
|
|
223
|
+
lines += ["", "### ❌ Regression vs base branch", ""]
|
|
224
|
+
for r in regressions:
|
|
225
|
+
lines.append(f"- {r}")
|
|
226
|
+
if deltas is not None:
|
|
227
|
+
lines += _delta_table(deltas)
|
|
228
|
+
|
|
229
|
+
if caps:
|
|
230
|
+
lines += ["", "<details><summary>Caps applied to this grade</summary>", ""]
|
|
231
|
+
for cap in caps:
|
|
232
|
+
lines.append(f"- `{cap}`")
|
|
233
|
+
lines += ["", "</details>"]
|
|
234
|
+
|
|
235
|
+
lines += [
|
|
236
|
+
"",
|
|
237
|
+
f"Dossier `{dossier.get('dossier_id','?')}` — signed, hash "
|
|
238
|
+
f"`{(dossier.get('content_sha256') or '')[:16]}…`. "
|
|
239
|
+
"Grades are pinned to the tested agent version; changing model, prompt, "
|
|
240
|
+
"or tools requires re-certification.",
|
|
241
|
+
"",
|
|
242
|
+
"> Domains marked **NOT ASSESSED** are not covered by this profile's "
|
|
243
|
+
"current suites. A grade attests to what was tested — read the coverage "
|
|
244
|
+
"table above before relying on it.",
|
|
245
|
+
]
|
|
246
|
+
return "\n".join(lines)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def main() -> None:
|
|
250
|
+
threshold = os.environ.get("FAIL_UNDER", "B").upper()
|
|
251
|
+
if threshold not in GRADE_ORDER:
|
|
252
|
+
print(f"::error::Invalid fail-under '{threshold}' (want A/B/C/NONE).")
|
|
253
|
+
sys.exit(2)
|
|
254
|
+
|
|
255
|
+
dossier = run_certify()
|
|
256
|
+
grade = grade_of(dossier)
|
|
257
|
+
if grade not in GRADE_ORDER:
|
|
258
|
+
print(f"::error::Dossier has no recognizable grade (got '{grade}').")
|
|
259
|
+
sys.exit(2)
|
|
260
|
+
|
|
261
|
+
grade_ok = threshold == "NONE" or GRADE_ORDER[grade] >= GRADE_ORDER[threshold]
|
|
262
|
+
|
|
263
|
+
# Deltas vs the base branch are always computed and shown when a base dossier
|
|
264
|
+
# is provided; `regression-check` decides only whether a regression *blocks*.
|
|
265
|
+
regression_on = os.environ.get("REGRESSION_CHECK", "true").lower() == "true"
|
|
266
|
+
base = load_base()
|
|
267
|
+
deltas = regressions = None
|
|
268
|
+
if base is not None:
|
|
269
|
+
deltas = compute_deltas(base, dossier)
|
|
270
|
+
regressions = regression_reasons(deltas)
|
|
271
|
+
|
|
272
|
+
# The merge gate: the check passes only if the absolute grade clears the bar
|
|
273
|
+
# AND (when regression-check is on) nothing regressed vs base. With
|
|
274
|
+
# regression-check off the deltas are still surfaced, just never blocking.
|
|
275
|
+
gate_on_regression = bool(regressions) and regression_on
|
|
276
|
+
passed = grade_ok and not gate_on_regression
|
|
277
|
+
|
|
278
|
+
summary = summarize(dossier, grade, threshold, passed, deltas, regressions)
|
|
279
|
+
SUMMARY.write_text(summary)
|
|
280
|
+
step_summary = os.environ.get("GITHUB_STEP_SUMMARY")
|
|
281
|
+
if step_summary:
|
|
282
|
+
with open(step_summary, "a") as f:
|
|
283
|
+
f.write(summary + "\n")
|
|
284
|
+
|
|
285
|
+
out("grade", grade)
|
|
286
|
+
out("passed", "true" if passed else "false")
|
|
287
|
+
out("dossier_path", str(DOSSIER))
|
|
288
|
+
out("regressed", "true" if regressions else "false")
|
|
289
|
+
|
|
290
|
+
print(f"\nGrade {grade} vs threshold {threshold} → "
|
|
291
|
+
f"{'PASS' if grade_ok else 'FAIL'}")
|
|
292
|
+
if regressions:
|
|
293
|
+
level = "error" if gate_on_regression else "warning"
|
|
294
|
+
print(f"::{level}::Safety regressed vs base branch"
|
|
295
|
+
f"{'' if gate_on_regression else ' (report-only)'}:")
|
|
296
|
+
for r in regressions:
|
|
297
|
+
print(f"::{level}:: - {r}")
|
|
298
|
+
if not grade_ok:
|
|
299
|
+
print(f"::error::Agent graded {grade}, below required {threshold}.")
|
|
300
|
+
if not passed:
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if __name__ == "__main__":
|
|
305
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Agenttic Agent Safety Gate — drop this in .github/workflows/ of the repo
|
|
2
|
+
# that builds your agent. It runs the Agenttic safety battery on every pull
|
|
3
|
+
# request and blocks the merge if your agent's grade drops below the bar.
|
|
4
|
+
#
|
|
5
|
+
# QUICKSTART (self-test, no setup): the defaults below run against Agenttic's
|
|
6
|
+
# built-in reference agent in offline mock mode with fail-under: NONE, so the
|
|
7
|
+
# check passes and you can see the summary comment. Then point `agent-url` at
|
|
8
|
+
# your own agent and raise `fail-under` to B to start gating for real.
|
|
9
|
+
|
|
10
|
+
name: Agent Safety
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
pull_request:
|
|
14
|
+
# Optional: only run when the agent or its prompts change.
|
|
15
|
+
# paths: ["agent/**", "prompts/**", "src/**"]
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
pull-requests: write # needed to post the summary comment
|
|
20
|
+
checks: write
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
safety:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Agenttic safety gate
|
|
29
|
+
uses: ./.github/actions/agent-safety # external repos: agenttic/agent-safety@v1
|
|
30
|
+
with:
|
|
31
|
+
# ---- point this at your agent to test it for real ----
|
|
32
|
+
agent-url: "" # e.g. https://staging.your-agent.com/chat
|
|
33
|
+
# agent-auth-header: ${{ secrets.AGENT_AUTH }} # e.g. "Bearer …"
|
|
34
|
+
|
|
35
|
+
profile: "cert-agent-safety-v1"
|
|
36
|
+
fail-under: "NONE" # ← change to "B" to gate merges
|
|
37
|
+
mock: "true" # ← set "false" once agent-url is set
|
|
38
|
+
comment-on-pr: "true"
|