ai-kit-python 1.4.2__py3-none-any.whl

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.
Files changed (278) hide show
  1. ai_kit_python-1.4.2.dist-info/METADATA +132 -0
  2. ai_kit_python-1.4.2.dist-info/RECORD +278 -0
  3. ai_kit_python-1.4.2.dist-info/WHEEL +5 -0
  4. ai_kit_python-1.4.2.dist-info/entry_points.txt +2 -0
  5. ai_kit_python-1.4.2.dist-info/licenses/LICENSE +21 -0
  6. ai_kit_python-1.4.2.dist-info/licenses/template/ai-kit/vendor/security-audit/LICENSE +21 -0
  7. ai_kit_python-1.4.2.dist-info/licenses/template/ai-kit/vendor/strix/LICENSE +201 -0
  8. ai_kit_python-1.4.2.dist-info/top_level.txt +1 -0
  9. aikit/.agents/skills/go-work/SKILL.md +8 -0
  10. aikit/.agents/skills/laravel-work/SKILL.md +8 -0
  11. aikit/.agents/skills/moodle-work/SKILL.md +8 -0
  12. aikit/.agents/skills/project-continuity/SKILL.md +34 -0
  13. aikit/.agents/skills/python-work/SKILL.md +8 -0
  14. aikit/.dockerignore +90 -0
  15. aikit/.gitattributes +3 -0
  16. aikit/.github/workflows/check-kit.yml +70 -0
  17. aikit/.github/workflows/publish-pypi.yml +108 -0
  18. aikit/.gitignore +60 -0
  19. aikit/BOOTSTRAP_PROMPT.md +18 -0
  20. aikit/CHANGELOG.md +85 -0
  21. aikit/CLAUDE.md +3 -0
  22. aikit/CONTRIBUTING.md +46 -0
  23. aikit/Dockerfile +44 -0
  24. aikit/KIMI.md +3 -0
  25. aikit/LICENSE +21 -0
  26. aikit/MANIFEST.in +22 -0
  27. aikit/MANUS.md +3 -0
  28. aikit/Makefile +70 -0
  29. aikit/README.md +1161 -0
  30. aikit/SECURITY.md +8 -0
  31. aikit/SKILL.md +105 -0
  32. aikit/VERSION +1 -0
  33. aikit/__init__.py +1 -0
  34. aikit/ai-kit/ADAPTERS.md +5 -0
  35. aikit/ai-kit/CONTAINERS.md +5 -0
  36. aikit/ai-kit/ENGINEERING.md +5 -0
  37. aikit/ai-kit/INTENT.md +5 -0
  38. aikit/ai-kit/MODELS.md +5 -0
  39. aikit/ai-kit/SKILLS.md +5 -0
  40. aikit/ai-kit/stacks/GO.md +5 -0
  41. aikit/ai-kit/stacks/LARAVEL.md +5 -0
  42. aikit/ai-kit/stacks/MOODLE.md +5 -0
  43. aikit/ai-kit/stacks/PYTHON.md +5 -0
  44. aikit/aikit_cli.py +59 -0
  45. aikit/deploy/k8s/install-preview-job.yaml +56 -0
  46. aikit/docker/build-requirements.txt +4 -0
  47. aikit/docker/check_version.py +29 -0
  48. aikit/docker/pypi-requirements.txt +3 -0
  49. aikit/docs/ADAPTER_PATH_FIX.md +23 -0
  50. aikit/docs/BENCHMARK.md +106 -0
  51. aikit/docs/CONTEXT7.md +33 -0
  52. aikit/docs/DECISIONS.md +22 -0
  53. aikit/docs/DEVELOPMENT_PLAN.md +16 -0
  54. aikit/docs/DIALOGUE_MEMORY.md +70 -0
  55. aikit/docs/DOCKER.md +74 -0
  56. aikit/docs/DOCKER_HUB_OVERVIEW.md +311 -0
  57. aikit/docs/EVALUATION.md +30 -0
  58. aikit/docs/IMPLEMENTATION.md +134 -0
  59. aikit/docs/MANUS_REVIEW.md +46 -0
  60. aikit/docs/PYPI.md +87 -0
  61. aikit/docs/PYPI_DESCRIPTION.md +103 -0
  62. aikit/docs/SECURITY_AUDIT.md +26 -0
  63. aikit/docs/STRIX.md +28 -0
  64. aikit/docs/V0_5_0.md +27 -0
  65. aikit/docs/V0_6_4.md +25 -0
  66. aikit/docs/V1_4_0.md +73 -0
  67. aikit/docs/V1_4_1.md +55 -0
  68. aikit/docs/V1_4_2.md +33 -0
  69. aikit/docs/WEB_DESIGN.md +72 -0
  70. aikit/docs/WINDOWS_CI_FIX.md +39 -0
  71. aikit/docs/adr/0000-template.md +24 -0
  72. aikit/docs/adr/0001-adaptive-model-routing.md +30 -0
  73. aikit/docs/adr/0002-template-separation.md +24 -0
  74. aikit/docs/adr/0003-agent-registry-and-router.md +24 -0
  75. aikit/docs/adr/0004-onboarding-and-project-facts.md +24 -0
  76. aikit/docs/adr/0005-client-settings-ownership.md +30 -0
  77. aikit/docs/adr/0006-routing-evidence-and-local-measurements.md +26 -0
  78. aikit/docs/adr/0007-generated-client-configuration-and-presets.md +34 -0
  79. aikit/docs/adr/0008-unified-cli-and-fact-drift.md +20 -0
  80. aikit/docs/adr/0009-provider-review-doctor-fix-and-ci-self-install.md +20 -0
  81. aikit/docs/adr/0010-metrics-analysis-and-gemini-verification.md +20 -0
  82. aikit/docs/adr/0011-evidence-lifecycle-and-evaluation.md +20 -0
  83. aikit/docs/adr/0012-measurement-led-routing.md +31 -0
  84. aikit/docs/adr/0013-docker-cli-image.md +24 -0
  85. aikit/docs/adr/0014-local-dialogue-memory.md +22 -0
  86. aikit/docs/history/original-bundle-checks.md +11 -0
  87. aikit/docs/history/v0.1-changelog.txt +37 -0
  88. aikit/docs/history/v0.10-changelog.md +12 -0
  89. aikit/docs/history/v0.2-changelog.md +35 -0
  90. aikit/docs/history/v0.3-changelog.md +25 -0
  91. aikit/docs/history/v0.4-changelog.md +31 -0
  92. aikit/docs/history/v0.5-changelog.md +14 -0
  93. aikit/docs/history/v0.6.4-changelog.md +12 -0
  94. aikit/docs/history/v0.6.5-v0.9-changelog.md +35 -0
  95. aikit/docs/history/v1.1.0-changelog.md +12 -0
  96. aikit/docs/history/v1.2.0-changelog.md +11 -0
  97. aikit/docs/history/verification-before-measurements.md +53 -0
  98. aikit/integrations/CONVENTIONS.md +3 -0
  99. aikit/integrations/README.md +7 -0
  100. aikit/integrations/agents.json +136 -0
  101. aikit/integrations/ai-kit-check.yml +66 -0
  102. aikit/integrations/ai-kit-cline.md +4 -0
  103. aikit/integrations/ai-kit-roo.md +4 -0
  104. aikit/integrations/ai-kit-windsurf.md +4 -0
  105. aikit/integrations/ai-kit.mdc +6 -0
  106. aikit/integrations/claude-settings.data-ask.json +23 -0
  107. aikit/integrations/claude-settings.git-ask.json +10 -0
  108. aikit/integrations/copilot-instructions.md +3 -0
  109. aikit/integrations/security-audit.json +135 -0
  110. aikit/integrations/strix.json +69 -0
  111. aikit/pyproject.toml +60 -0
  112. aikit/scripts/__init__.py +1 -0
  113. aikit/scripts/adapters.py +184 -0
  114. aikit/scripts/adaptive.py +167 -0
  115. aikit/scripts/adr.py +61 -0
  116. aikit/scripts/benchmark.py +543 -0
  117. aikit/scripts/changelog.py +55 -0
  118. aikit/scripts/check_distribution.py +178 -0
  119. aikit/scripts/check_kit.py +406 -0
  120. aikit/scripts/context.py +184 -0
  121. aikit/scripts/docker_test_usage.py +55 -0
  122. aikit/scripts/doctor.py +313 -0
  123. aikit/scripts/evaluate.py +125 -0
  124. aikit/scripts/install.py +1584 -0
  125. aikit/scripts/learning.py +260 -0
  126. aikit/scripts/memory.py +344 -0
  127. aikit/scripts/memory_import.py +177 -0
  128. aikit/scripts/memory_store.py +125 -0
  129. aikit/scripts/metrics.py +351 -0
  130. aikit/scripts/router.py +399 -0
  131. aikit/template/.agents/hooks/session-start.md +3 -0
  132. aikit/template/.agents/hooks/session-start.sh +21 -0
  133. aikit/template/.agents/skills/ai-kit-bootstrap/SKILL.md +14 -0
  134. aikit/template/.agents/skills/ai-kit-review/SKILL.md +11 -0
  135. aikit/template/.agents/skills/ai-kit-sync-context/SKILL.md +12 -0
  136. aikit/template/.agents/skills/context7-docs/SKILL.md +11 -0
  137. aikit/template/.agents/skills/design-extract/SKILL.md +9 -0
  138. aikit/template/.agents/skills/dotnet-work/SKILL.md +9 -0
  139. aikit/template/.agents/skills/filament-work/SKILL.md +9 -0
  140. aikit/template/.agents/skills/frontend-work/SKILL.md +9 -0
  141. aikit/template/.agents/skills/go-work/SKILL.md +9 -0
  142. aikit/template/.agents/skills/infra-work/SKILL.md +9 -0
  143. aikit/template/.agents/skills/java-work/SKILL.md +9 -0
  144. aikit/template/.agents/skills/laravel-blade-ui/SKILL.md +9 -0
  145. aikit/template/.agents/skills/laravel-work/SKILL.md +10 -0
  146. aikit/template/.agents/skills/library-work/SKILL.md +9 -0
  147. aikit/template/.agents/skills/moodle-work/SKILL.md +9 -0
  148. aikit/template/.agents/skills/php-work/SKILL.md +9 -0
  149. aikit/template/.agents/skills/project-continuity/SKILL.md +21 -0
  150. aikit/template/.agents/skills/python-work/SKILL.md +9 -0
  151. aikit/template/.agents/skills/rust-work/SKILL.md +9 -0
  152. aikit/template/.agents/skills/security-audit/SKILL.md +16 -0
  153. aikit/template/.agents/skills/security-audit/agents/openai.yaml +2 -0
  154. aikit/template/.agents/skills/strix/SKILL.md +16 -0
  155. aikit/template/.agents/skills/strix/agents/openai.yaml +2 -0
  156. aikit/template/.agents/skills/ui-review/SKILL.md +9 -0
  157. aikit/template/.agents/skills/web-design/SKILL.md +9 -0
  158. aikit/template/AGENTS.md +16 -0
  159. aikit/template/CHANGELOG.md +6 -0
  160. aikit/template/CLAUDE.md +5 -0
  161. aikit/template/GEMINI.md +4 -0
  162. aikit/template/KIMI.md +4 -0
  163. aikit/template/MANUS.md +4 -0
  164. aikit/template/PROJECT_CONTEXT.md +35 -0
  165. aikit/template/README.md +6 -0
  166. aikit/template/WIKI.md +37 -0
  167. aikit/template/ai-kit/ADAPTERS.md +100 -0
  168. aikit/template/ai-kit/BOOTSTRAP.md +15 -0
  169. aikit/template/ai-kit/CONTAINERS.md +63 -0
  170. aikit/template/ai-kit/CONTEXT7.md +96 -0
  171. aikit/template/ai-kit/CORE.md +23 -0
  172. aikit/template/ai-kit/ENGINEERING.md +34 -0
  173. aikit/template/ai-kit/MCP.md +17 -0
  174. aikit/template/ai-kit/METRICS.md +83 -0
  175. aikit/template/ai-kit/SECURITY-AUDIT.md +21 -0
  176. aikit/template/ai-kit/SECURITY.md +34 -0
  177. aikit/template/ai-kit/SKILLS.md +37 -0
  178. aikit/template/ai-kit/STRIX.md +44 -0
  179. aikit/template/ai-kit/UPSTREAM.md +38 -0
  180. aikit/template/ai-kit/WEB-DESIGN.md +37 -0
  181. aikit/template/ai-kit/profiles/OWNER.md +19 -0
  182. aikit/template/ai-kit/project.json +14 -0
  183. aikit/template/ai-kit/router/POLICY.md +79 -0
  184. aikit/template/ai-kit/router/providers/anthropic.json +44 -0
  185. aikit/template/ai-kit/router/providers/anthropic.md +14 -0
  186. aikit/template/ai-kit/router/providers/gemini.json +13 -0
  187. aikit/template/ai-kit/router/providers/gemini.md +10 -0
  188. aikit/template/ai-kit/router/providers/kimi.json +26 -0
  189. aikit/template/ai-kit/router/providers/kimi.md +13 -0
  190. aikit/template/ai-kit/router/providers/local.json +18 -0
  191. aikit/template/ai-kit/router/providers/local.md +10 -0
  192. aikit/template/ai-kit/router/providers/openai.json +48 -0
  193. aikit/template/ai-kit/router/providers/openai.md +14 -0
  194. aikit/template/ai-kit/router/routing.json +11 -0
  195. aikit/template/ai-kit/router/selection.json +14 -0
  196. aikit/template/ai-kit/settings.json +11 -0
  197. aikit/template/ai-kit/stacks/DOTNET.md +13 -0
  198. aikit/template/ai-kit/stacks/FILAMENT.md +18 -0
  199. aikit/template/ai-kit/stacks/FRONTEND.md +9 -0
  200. aikit/template/ai-kit/stacks/GO.md +19 -0
  201. aikit/template/ai-kit/stacks/INFRA.md +12 -0
  202. aikit/template/ai-kit/stacks/JAVA.md +13 -0
  203. aikit/template/ai-kit/stacks/LARAVEL.md +15 -0
  204. aikit/template/ai-kit/stacks/LIBRARY.md +9 -0
  205. aikit/template/ai-kit/stacks/MOODLE.md +12 -0
  206. aikit/template/ai-kit/stacks/PHP.md +11 -0
  207. aikit/template/ai-kit/stacks/PYTHON.md +11 -0
  208. aikit/template/ai-kit/stacks/RUST.md +18 -0
  209. aikit/template/ai-kit/vendor/security-audit/AI-AND-LLM.md +83 -0
  210. aikit/template/ai-kit/vendor/security-audit/ATTACK-CLASSES.md +130 -0
  211. aikit/template/ai-kit/vendor/security-audit/CLIENT-SIDE.md +83 -0
  212. aikit/template/ai-kit/vendor/security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
  213. aikit/template/ai-kit/vendor/security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
  214. aikit/template/ai-kit/vendor/security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
  215. aikit/template/ai-kit/vendor/security-audit/HUNTING.md +251 -0
  216. aikit/template/ai-kit/vendor/security-audit/LICENSE +21 -0
  217. aikit/template/ai-kit/vendor/security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
  218. aikit/template/ai-kit/vendor/security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
  219. aikit/template/ai-kit/vendor/security-audit/RECONNAISSANCE.md +156 -0
  220. aikit/template/ai-kit/vendor/security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
  221. aikit/template/ai-kit/vendor/security-audit/SKILL.md +192 -0
  222. aikit/template/ai-kit/vendor/security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
  223. aikit/template/ai-kit/vendor/security-audit/VALIDATION-AND-REPORTING.md +186 -0
  224. aikit/template/ai-kit/vendor/security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
  225. aikit/template/ai-kit/vendor/security-audit/report-schema.json +461 -0
  226. aikit/template/ai-kit/vendor/security-audit/validate-coverage-ledger.cjs +872 -0
  227. aikit/template/ai-kit/vendor/security-audit/validate-coverage-ledger.test.cjs +740 -0
  228. aikit/template/ai-kit/vendor/security-audit/validate-findings.cjs +773 -0
  229. aikit/template/ai-kit/vendor/security-audit/validate-findings.test.cjs +652 -0
  230. aikit/template/ai-kit/vendor/strix/LICENSE +201 -0
  231. aikit/template/ai-kit/vendor/strix/api-security-testing/SKILL.md +61 -0
  232. aikit/template/ai-kit/vendor/strix/application-security-testing/SKILL.md +66 -0
  233. aikit/template/ai-kit/vendor/strix/ci-security-scanning-with-strix/SKILL.md +149 -0
  234. aikit/template/ai-kit/vendor/strix/find-security-vulnerabilities-in-code/SKILL.md +62 -0
  235. aikit/template/ai-kit/vendor/strix/fix-security-vulnerabilities-with-strix/SKILL.md +77 -0
  236. aikit/template/ai-kit/vendor/strix/managed-pentesting-with-strix/SKILL.md +321 -0
  237. aikit/template/ai-kit/vendor/strix/owasp-top-10-testing/SKILL.md +64 -0
  238. aikit/template/ai-kit/vendor/strix/penetration-testing-with-strix/SKILL.md +162 -0
  239. aikit/template/ai-kit/vendor/strix/web-app-penetration-testing/SKILL.md +54 -0
  240. aikit/template/ai-kit/web-design/LARAVEL.md +51 -0
  241. aikit/template/ai-kit/web-design/LIBRARIES.md +39 -0
  242. aikit/template/ai-kit/web-design/REVIEW.md +37 -0
  243. aikit/template/ai-kit/web-design/TOOLS.md +43 -0
  244. aikit/template/docs/DECISIONS.md +7 -0
  245. aikit/template/docs/adr/0000-template.md +20 -0
  246. aikit/template/docs/design/ACCEPTANCE.md +24 -0
  247. aikit/template/docs/design/BRIEF.md +26 -0
  248. aikit/template/docs/design/SYSTEM.md +30 -0
  249. aikit/templates/agent-secrets.ignore +19 -0
  250. aikit/templates/gitignore.private +160 -0
  251. aikit/templates/gitignore.team +139 -0
  252. aikit/tests/build_distribution.py +73 -0
  253. aikit/tests/docker_smoke.py +172 -0
  254. aikit/tests/fixtures/create-junction.ps1 +18 -0
  255. aikit/tests/fixtures/golden/claude-settings-local.json +58 -0
  256. aikit/tests/test_adapters.py +105 -0
  257. aikit/tests/test_aikit.py +79 -0
  258. aikit/tests/test_benchmark.py +241 -0
  259. aikit/tests/test_check_kit.py +203 -0
  260. aikit/tests/test_context.py +76 -0
  261. aikit/tests/test_context7.py +153 -0
  262. aikit/tests/test_context_selection.py +70 -0
  263. aikit/tests/test_distribution.py +181 -0
  264. aikit/tests/test_docker_image.py +63 -0
  265. aikit/tests/test_docker_test_usage.py +54 -0
  266. aikit/tests/test_doctor.py +148 -0
  267. aikit/tests/test_evaluate.py +66 -0
  268. aikit/tests/test_install.py +939 -0
  269. aikit/tests/test_learning.py +247 -0
  270. aikit/tests/test_memory.py +371 -0
  271. aikit/tests/test_metrics.py +209 -0
  272. aikit/tests/test_native.py +342 -0
  273. aikit/tests/test_onboarding.py +301 -0
  274. aikit/tests/test_router.py +336 -0
  275. aikit/tests/test_security_audit.py +237 -0
  276. aikit/tests/test_strix.py +245 -0
  277. aikit/tests/test_web_design.py +219 -0
  278. aikit/tests/wheel_smoke.py +104 -0
@@ -0,0 +1,23 @@
1
+ {
2
+ "permissions": {
3
+ "ask": [
4
+ "Bash(rm -rf *)",
5
+ "Bash(rm -fr *)",
6
+ "Bash(git reset --hard*)",
7
+ "Bash(git clean *)",
8
+ "Bash(docker system prune*)",
9
+ "Bash(docker volume rm *)",
10
+ "Bash(docker volume prune*)"
11
+ ]
12
+ },
13
+ "stacks": {
14
+ "LARAVEL": [
15
+ "Bash(php artisan migrate*)",
16
+ "Bash(php artisan db:wipe*)",
17
+ "Bash(php artisan db:seed*)"
18
+ ],
19
+ "MOODLE": [
20
+ "Bash(php admin/cli/upgrade.php*)"
21
+ ]
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "ask": [
4
+ "Bash(git commit)",
5
+ "Bash(git commit *)",
6
+ "Bash(git push)",
7
+ "Bash(git push *)"
8
+ ]
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ # Project instructions
2
+
3
+ Read AGENTS.md, ai-kit/CORE.md, settings, and current PROJECT_CONTEXT before edits; use WIKI for applicable rules.
@@ -0,0 +1,135 @@
1
+ {
2
+ "schema": 1,
3
+ "repository": "https://github.com/cloudflare/security-audit-skill",
4
+ "revision": "c1c8a8c1471069fb0e188eeaff69b8e8db6564a8",
5
+ "source_directory": "skills/security-audit",
6
+ "license": "MIT",
7
+ "files": {
8
+ "LICENSE": {
9
+ "sha256": "e598e694aa506650c7192d5ea3be0e50aca0d356ea00551c53429f0b01eb6391",
10
+ "git_blob": "6dbc9ecb3a5b9080e95b962869e8c7ab16cfdc20",
11
+ "git_mode": "100644",
12
+ "size": 1078
13
+ },
14
+ "AI-AND-LLM.md": {
15
+ "sha256": "7335a95c43554eadefcf03ece639291bb0e038c398a179299e3887ffe06bd2ef",
16
+ "git_blob": "02ba9039eca4cec811fefc77bce0d7349b379aae",
17
+ "git_mode": "100644",
18
+ "size": 10617
19
+ },
20
+ "ATTACK-CLASSES.md": {
21
+ "sha256": "3ee4f00c6e8c9dcf6dd02dd5ac2a4ceb322e2085f3e9fbe79cf0c6330bc7ef83",
22
+ "git_blob": "18a2beb4dbb554fb52b0a0293b8bd9108207f8fa",
23
+ "git_mode": "100644",
24
+ "size": 15695
25
+ },
26
+ "CLIENT-SIDE.md": {
27
+ "sha256": "613f6b98f86083e045525c5b60154ffdab122bac35f1e05eb17488e6e453370c",
28
+ "git_blob": "8ba673e9e7e3d02f5518aa0219bcbeec7957531b",
29
+ "git_mode": "100644",
30
+ "size": 8594
31
+ },
32
+ "CLOUD-AND-DEPLOYMENT.md": {
33
+ "sha256": "3c80f3088be22345e07b057c056d6ed99fe2b0304f794398964f7ccd5a496ca3",
34
+ "git_blob": "5569f76c7740744aa3f60aaa89eaefe275f2e6c9",
35
+ "git_mode": "100644",
36
+ "size": 8291
37
+ },
38
+ "DATA-ISOLATION-AND-LIFECYCLE.md": {
39
+ "sha256": "307dcb3e24d292715c4a041190e7bb135646a37eb6187619ae01d000a634a4ad",
40
+ "git_blob": "0cba1fa463b62c9b0caba710a201e29c1eecd0ec",
41
+ "git_mode": "100644",
42
+ "size": 7798
43
+ },
44
+ "DESKTOP-MOBILE-AND-LOCAL-IPC.md": {
45
+ "sha256": "58eae8ee0ca6a46611c501ec5a3ab47fdff88dd7db0d8d74789bbf8bb19d8cb2",
46
+ "git_blob": "d6230098651bff6348d3868c11f566bfb1306e7f",
47
+ "git_mode": "100644",
48
+ "size": 8739
49
+ },
50
+ "HUNTING.md": {
51
+ "sha256": "71c121decea1322c47c718f6e110f1a892980bb4266bd7ac55ef043a213ade09",
52
+ "git_blob": "377d4dcfbe88a049379448cd7892717185401597",
53
+ "git_mode": "100644",
54
+ "size": 22446
55
+ },
56
+ "MEMORY-SAFETY-AND-BINARY.md": {
57
+ "sha256": "7c8b0dccfb35315cca2455509674d49632b2fe37e1f3aac16c1a496a7e9924a7",
58
+ "git_blob": "86ff4920752661fc4a31a35ca4f962ace5dd5f48",
59
+ "git_mode": "100644",
60
+ "size": 10513
61
+ },
62
+ "PROTOCOLS-RPC-AND-MESSAGING.md": {
63
+ "sha256": "8b73816fa5b3697c9c40977f0c836b40f01d6e81929ff2720fe83ce089e9a619",
64
+ "git_blob": "b04ab88f852224838fdc9683d9fcccd858b0778a",
65
+ "git_mode": "100644",
66
+ "size": 7483
67
+ },
68
+ "RECONNAISSANCE.md": {
69
+ "sha256": "02ca887aca84de8ae137f1891bd2f0a54f28d3fa6da92a27f54734382b3381a2",
70
+ "git_blob": "1a170a93ab76b93dc5ac268a5b690beb423a8aa9",
71
+ "git_mode": "100644",
72
+ "size": 15902
73
+ },
74
+ "report-schema.json": {
75
+ "sha256": "6575c9de4a62255699ea052ebf9a49ebfb373a32891bcd89af6f55cc84b35519",
76
+ "git_blob": "55815a3687a45fff63d8ebf20068611bd6ad6d9a",
77
+ "git_mode": "100644",
78
+ "size": 14494
79
+ },
80
+ "RESOURCE-EXHAUSTION-AND-AVAILABILITY.md": {
81
+ "sha256": "4326fab5c92a0b4c420a70196254040105cd794026562130b59bf1a6db3e4baa",
82
+ "git_blob": "6cbbd1e2cfa482068c8279c2f7da9db43d3f54b8",
83
+ "git_mode": "100644",
84
+ "size": 7476
85
+ },
86
+ "SKILL.md": {
87
+ "sha256": "5e3e96a1e438d8f35fef0a1e38f02d4f00e2bdf910401b7dfe00e6779c6dac85",
88
+ "git_blob": "92178dad304d3f63e37582a297026a7874e12c62",
89
+ "git_mode": "100644",
90
+ "size": 22026
91
+ },
92
+ "SUPPLY-CHAIN-AND-RELEASE.md": {
93
+ "sha256": "f372e919940e335468ba1ae09f99026c3986a345f7fe26f8068f132e199f52a5",
94
+ "git_blob": "bf96aed0803bc07471483ab129dea86759c3e305",
95
+ "git_mode": "100644",
96
+ "size": 7258
97
+ },
98
+ "validate-coverage-ledger.cjs": {
99
+ "sha256": "2eb280bd2e33c002916f86db9b29f9edfedb6454e5db973c766290465b868ac8",
100
+ "git_blob": "0ef58657bef18b26a92321df6c8e3d31bc8e6659",
101
+ "git_mode": "100755",
102
+ "size": 33522
103
+ },
104
+ "validate-coverage-ledger.test.cjs": {
105
+ "sha256": "cfb9df26b03469a4279a70e7664a2a5434dd6f8c2c204e91f9b0aeb9dd71e648",
106
+ "git_blob": "b1444f57e1b021a61175733b8c356171861c8701",
107
+ "git_mode": "100644",
108
+ "size": 28483
109
+ },
110
+ "validate-findings.cjs": {
111
+ "sha256": "e85f232e36bfac0f866f244da50376692d6b45c690beadcb00608b0e6c8b5283",
112
+ "git_blob": "2843feceddb7e30bf10d6e4f7bbe6640e4c57799",
113
+ "git_mode": "100755",
114
+ "size": 28806
115
+ },
116
+ "validate-findings.test.cjs": {
117
+ "sha256": "b92001117a92c4161d43777a0ba2fa182a8a4d97f2304cc8c0cee5331497b73b",
118
+ "git_blob": "8d245b226bbe2fdf28b46b3203b99c2824039d2d",
119
+ "git_mode": "100644",
120
+ "size": 26384
121
+ },
122
+ "VALIDATION-AND-REPORTING.md": {
123
+ "sha256": "b892470cb89bb31f33403e050d4bdfbe2cdaef940ade1496fa65e32b2e129449",
124
+ "git_blob": "5e200d7387e665e53e0e4190fa4a5814c334c507",
125
+ "git_mode": "100644",
126
+ "size": 17843
127
+ },
128
+ "WEB-PROTOCOL-AND-AUTH.md": {
129
+ "sha256": "4f3066dffa23c5635859187be9b96c32f2aff29a688186b91ff0c63e47d62b5c",
130
+ "git_blob": "1a099fa575cf52a61e9a627f91b61015120fa517",
131
+ "git_mode": "100644",
132
+ "size": 12300
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "schema": 1,
3
+ "repository": "https://github.com/usestrix/strix",
4
+ "revision": "f7a32ae62d94872631bc8415073868d02a6ffdaf",
5
+ "source_directory": "skills",
6
+ "license": "Apache-2.0",
7
+ "files": {
8
+ "LICENSE": {
9
+ "sha256": "7db9697134251e314bd8d39257fe170c98b3e2ad9b2ed67e97acaf9613e7b9e3",
10
+ "git_blob": "65c4e8f58a00552cc29f6bd1dcaf1c4ef10012b8",
11
+ "git_mode": "100644",
12
+ "size": 11345
13
+ },
14
+ "api-security-testing/SKILL.md": {
15
+ "sha256": "bd04c90d87be2353ebefee33363af0818746345e25c80ed36f63fcdd2b25509c",
16
+ "git_blob": "d21a25da6167f79c3caff745abc142b2118c2691",
17
+ "git_mode": "100644",
18
+ "size": 6331
19
+ },
20
+ "application-security-testing/SKILL.md": {
21
+ "sha256": "b3041c800a9a1d34e04164c5f700b6d12bbc237f098d760c09ec076c92d2a654",
22
+ "git_blob": "0c27bd5df4a346abfc1e81efc62f49b066637f52",
23
+ "git_mode": "100644",
24
+ "size": 4557
25
+ },
26
+ "ci-security-scanning-with-strix/SKILL.md": {
27
+ "sha256": "f8e1dd61721dc302bd64155f80a2a3b16a5bf5bca598ae23d98aad8fefebea91",
28
+ "git_blob": "69ae726776b8c1dbc667f18a9f972a66e4656c4e",
29
+ "git_mode": "100644",
30
+ "size": 9019
31
+ },
32
+ "find-security-vulnerabilities-in-code/SKILL.md": {
33
+ "sha256": "e7c25f1a0aea437d011d96dd306736b644597b07aa5a6dbc5588eefaab248e0a",
34
+ "git_blob": "b1829e9cba9db38d6d76e9fa274da3caa188ac75",
35
+ "git_mode": "100644",
36
+ "size": 4386
37
+ },
38
+ "fix-security-vulnerabilities-with-strix/SKILL.md": {
39
+ "sha256": "bb1b351fd60c91e4875207f3fe4f35774ff5194261a00aea9f4752e459f24a9f",
40
+ "git_blob": "6912cb542b3e5c46e99fa196cd29039de9a8e574",
41
+ "git_mode": "100644",
42
+ "size": 5997
43
+ },
44
+ "managed-pentesting-with-strix/SKILL.md": {
45
+ "sha256": "45e1432ea897e59e67a5102513a4b9aac7372a5666d68d620ee867bbfa8b290b",
46
+ "git_blob": "246aa882580962a89aaef2ad89d5d3c6e150a793",
47
+ "git_mode": "100644",
48
+ "size": 23639
49
+ },
50
+ "owasp-top-10-testing/SKILL.md": {
51
+ "sha256": "02b1cbe60d40c44493309418c7824f1f681d9f32bf0299ca246892c46cb130af",
52
+ "git_blob": "328e217f7e0ab0fe43f448de49c1b2fb2897a6a7",
53
+ "git_mode": "100644",
54
+ "size": 6245
55
+ },
56
+ "penetration-testing-with-strix/SKILL.md": {
57
+ "sha256": "8051d93f1ccc96c7268ca7b3d3d589c56997c620164773617ceb53af8832692c",
58
+ "git_blob": "afd56cea3920da1f5092f90244c3db7519b8b0ae",
59
+ "git_mode": "100644",
60
+ "size": 10131
61
+ },
62
+ "web-app-penetration-testing/SKILL.md": {
63
+ "sha256": "2e8df0b9233971c496d0c0c7660808fc717c08d4165455a0423229045389608a",
64
+ "git_blob": "99331fa8bb83315a3bce4449592bb77504421d30",
65
+ "git_mode": "100644",
66
+ "size": 4320
67
+ }
68
+ }
69
+ }
aikit/pyproject.toml ADDED
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ai-kit-python"
7
+ description = "Offline installer and portable instructions that give AI coding agents project memory, stack rules, guardrails, and adaptive model routing."
8
+ readme = {file = "docs/PYPI_DESCRIPTION.md", content-type = "text/markdown"}
9
+ requires-python = ">=3.10"
10
+ authors = [{name = "Igor Sazonov"}]
11
+ license = "MIT AND Apache-2.0"
12
+ license-files = [
13
+ "LICENSE",
14
+ "template/ai-kit/vendor/security-audit/LICENSE",
15
+ "template/ai-kit/vendor/strix/LICENSE",
16
+ ]
17
+ keywords = ["ai-agents", "coding-assistant", "context-engineering", "agent-skills", "project-memory", "developer-tools"]
18
+ classifiers = [
19
+ "Environment :: Console",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Programming Language :: Python :: 3.14",
28
+ "Topic :: Software Development",
29
+ ]
30
+ dynamic = ["version"]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/tigusigalpa/ai-kit"
34
+ Documentation = "https://github.com/tigusigalpa/ai-kit#readme"
35
+ Source = "https://github.com/tigusigalpa/ai-kit"
36
+ Issues = "https://github.com/tigusigalpa/ai-kit/issues"
37
+ Changelog = "https://github.com/tigusigalpa/ai-kit/blob/main/CHANGELOG.md"
38
+
39
+ [project.scripts]
40
+ aikit = "aikit.aikit_cli:main"
41
+
42
+ [tool.setuptools]
43
+ packages = ["aikit", "aikit.scripts"]
44
+ package-dir = {aikit = ".", "aikit.scripts" = "scripts"}
45
+ include-package-data = true
46
+
47
+ [tool.setuptools.package-data]
48
+ aikit = ["VERSION", "LICENSE", "MANIFEST.in", "pyproject.toml", "Makefile", "Dockerfile", ".dockerignore", ".gitignore", ".gitattributes", "*.md", "*.json", "ai-kit/*.md", "ai-kit/router/*.md", "ai-kit/router/providers/*.md", "ai-kit/stacks/*.md", "ai-kit/profiles/*.md", "docs/**/*", "integrations/**/*", "template/**/*", "template/.agents/**/*", "templates/**/*", ".github/**/*", ".agents/**/*", "docker/**/*", "deploy/**/*", "tests/*.py", "tests/fixtures/**/*"]
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = {file = ["VERSION"]}
52
+
53
+ [tool.setuptools.exclude-package-data]
54
+ aikit = [
55
+ "AGENTS.md", "PROJECT_CONTEXT.md", "WIKI.md", "auth.json", ".netrc", ".pypirc", ".env", ".env.*",
56
+ "ai-kit/.metrics/*", "ai-kit/.memory/*", "ai-kit/.upstream-cache/*", "ai-kit/.design/*", "docs/.private/*",
57
+ "**/.env", "**/.env.*", "**/auth.json", "**/.netrc", "**/.pypirc",
58
+ "*.pem", "*.p12", "*.pfx", "id_rsa*", "id_ed25519*",
59
+ "**/*.pem", "**/*.p12", "**/*.pfx", "**/id_rsa*", "**/id_ed25519*",
60
+ ]
@@ -0,0 +1 @@
1
+ """AI-KIT command implementations."""
@@ -0,0 +1,184 @@
1
+ """Review client adapters and retire only unchanged AI-KIT-owned adapter files."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ from pathlib import Path
7
+ import sys
8
+
9
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
10
+ import install
11
+
12
+
13
+ STATE_PATH = "ai-kit/.install-state.json"
14
+
15
+
16
+ def target_root(target: Path) -> Path:
17
+ target = target.absolute()
18
+ if install.is_link(target):
19
+ raise ValueError("Refusing a linked project root")
20
+ target = target.resolve()
21
+ if not target.is_dir():
22
+ raise ValueError("Project must be an existing directory")
23
+ return target
24
+
25
+
26
+ def installed_state(target: Path, registry: dict) -> dict:
27
+ path = install.safe_path(target, STATE_PATH)
28
+ if not path.is_file():
29
+ raise ValueError("No installer state found; is AI-KIT installed in this project?")
30
+ state = install.read_json(path, {})
31
+ # Read-only audits must reach their obstruction report; lexical/link boundaries stay strict.
32
+ install.validate_state(state, target, registry["names"], allow_obstructions=True)
33
+ return state
34
+
35
+
36
+ def report(*, root: Path = install.ROOT, project: Path | None = None) -> dict:
37
+ registry = install.load_agent_registry(root)
38
+ state = None
39
+ detected: dict[str, list[str]] = {}
40
+ target = None
41
+ if project is not None:
42
+ target = target_root(project)
43
+ state = installed_state(target, registry)
44
+ detected = install.detect_agents(target, registry)
45
+ clients = []
46
+ for name in sorted(registry["names"]):
47
+ metadata = registry["metadata"][name]
48
+ managed = []
49
+ blocked = []
50
+ selected = None
51
+ if state is not None:
52
+ selected = name in state["agents"]
53
+ for relative in sorted(state["files"]):
54
+ if install.adapter_owner(relative, registry) != name:
55
+ continue
56
+ try:
57
+ exists = install.safe_path(target, relative).is_file()
58
+ except (ValueError, NotADirectoryError):
59
+ blocked.append(relative)
60
+ continue
61
+ if exists:
62
+ managed.append(relative)
63
+ clients.append({"client": name, "documentation_status": metadata["status"],
64
+ "verified_documentation_date": metadata["verified_documentation_date"],
65
+ "sources": metadata["sources"], "activation": metadata["activation"],
66
+ "selected": selected, "detected": detected.get(name, []), "managed_paths": managed,
67
+ "blocked_paths": blocked})
68
+ return {"project": str(target) if target is not None else None, "clients": clients,
69
+ "note": "Documentation status does not prove native client activation."}
70
+
71
+
72
+ def retirement_plan(project: Path, agent: str, *, root: Path = install.ROOT) -> dict:
73
+ target = target_root(project)
74
+ registry = install.load_agent_registry(root)
75
+ if agent not in registry["names"]:
76
+ raise ValueError("Unknown client: " + agent)
77
+ state = installed_state(target, registry)
78
+ if agent in state["agents"] and len(state["agents"]) == 1:
79
+ raise ValueError("Cannot retire the last selected client; install a replacement selection first")
80
+ records = state["files"]
81
+ removals, conflicts, absent = [], [], []
82
+ for relative in sorted(records):
83
+ if install.adapter_owner(relative, registry) != agent:
84
+ continue
85
+ try:
86
+ path = install.safe_path(target, relative)
87
+ except (ValueError, NotADirectoryError):
88
+ conflicts.append(relative)
89
+ continue
90
+ if not path.exists():
91
+ absent.append(relative)
92
+ continue
93
+ if not path.is_file():
94
+ conflicts.append(relative)
95
+ continue
96
+ data = path.read_bytes()
97
+ record = records[relative]
98
+ if record.get("local_adaptation") or install.digest(data) != record.get("installed_hash"):
99
+ conflicts.append(relative)
100
+ continue
101
+ removals.append({"path": relative, "old": data,
102
+ "backup": "ai-kit/.upstream-cache/retirements/" + install.digest(data) + "/" + relative})
103
+ next_state = dict(state)
104
+ next_files = dict(records)
105
+ for item in removals:
106
+ next_files.pop(item["path"], None)
107
+ for relative in absent:
108
+ next_files.pop(relative, None)
109
+ next_state["files"] = next_files
110
+ next_state["agents"] = [name for name in state["agents"] if name != agent]
111
+ state_path = install.safe_path(target, STATE_PATH)
112
+ old_state = state_path.read_bytes()
113
+ state_data = (json.dumps(next_state, indent=2, sort_keys=True) + "\n").encode("utf-8")
114
+ return {"target": str(target), "agent": agent, "removals": removals, "conflicts": conflicts,
115
+ "absent": absent, "state_path": STATE_PATH, "state_old": old_state, "state_data": state_data,
116
+ "warnings": ((["Selected client will be deselected only after this explicit retirement."]
117
+ if agent in state["agents"] else []) +
118
+ (["Modified or non-file adapter paths require manual retirement."] if conflicts else []))}
119
+
120
+
121
+ def apply_retirement(plan: dict) -> bool:
122
+ if plan["conflicts"]:
123
+ return False
124
+ target = Path(plan["target"])
125
+ state_path = install.safe_path(target, plan["state_path"])
126
+ if state_path.read_bytes() != plan["state_old"]:
127
+ raise ValueError("Installer state changed after preview")
128
+ for item in plan["removals"]:
129
+ path = install.safe_path(target, item["path"])
130
+ actual = path.read_bytes() if path.exists() and path.is_file() else None
131
+ if actual != item["old"]:
132
+ raise ValueError("Adapter changed after preview: " + item["path"])
133
+ completed = []
134
+ try:
135
+ for item in plan["removals"]:
136
+ path = install.safe_path(target, item["path"])
137
+ backup = install.safe_path(target, item["backup"])
138
+ if backup.exists():
139
+ if backup.read_bytes() != item["old"]:
140
+ raise ValueError("Existing retirement backup differs: " + item["backup"])
141
+ else:
142
+ install.atomic_write(backup, item["old"])
143
+ path.unlink()
144
+ completed.append(item)
145
+ install.atomic_write(state_path, plan["state_data"])
146
+ except (OSError, ValueError):
147
+ for item in reversed(completed):
148
+ path = install.safe_path(target, item["path"])
149
+ if not path.exists():
150
+ install.atomic_write(path, item["old"])
151
+ raise
152
+ return True
153
+
154
+
155
+ def main(argv: list[str] | None = None) -> int:
156
+ parser = argparse.ArgumentParser(description=__doc__)
157
+ sub = parser.add_subparsers(dest="command", required=True)
158
+ report_parser = sub.add_parser("report", help="List client documentation status and installed adapter evidence")
159
+ report_parser.add_argument("--project", type=Path)
160
+ retire = sub.add_parser("retire", help="Preview or remove unchanged tracked files and explicitly deselect one client")
161
+ retire.add_argument("project", type=Path)
162
+ retire.add_argument("--agent", required=True)
163
+ retire.add_argument("--apply", action="store_true")
164
+ args = parser.parse_args(argv)
165
+ try:
166
+ if args.command == "report":
167
+ print(json.dumps(report(project=args.project), indent=2))
168
+ return 0
169
+ plan = retirement_plan(args.project, args.agent)
170
+ print(json.dumps({"target": plan["target"], "agent": plan["agent"], "preview": not args.apply,
171
+ "removals": [item["path"] for item in plan["removals"]],
172
+ "backups": [item["backup"] for item in plan["removals"]],
173
+ "conflicts": plan["conflicts"], "absent": plan["absent"],
174
+ "warnings": plan["warnings"]}, indent=2))
175
+ if args.apply:
176
+ apply_retirement(plan)
177
+ return 2 if plan["conflicts"] else 0
178
+ except (OSError, ValueError, UnicodeError, json.JSONDecodeError) as exc:
179
+ print(f"Adapter operation refused: {exc}", file=sys.stderr)
180
+ return 1
181
+
182
+
183
+ if __name__ == "__main__":
184
+ raise SystemExit(main())
@@ -0,0 +1,167 @@
1
+ """Optional routing modes, pins and measured recommendations, without a switching runtime."""
2
+ from __future__ import annotations
3
+
4
+ import copy
5
+ import json
6
+ from pathlib import Path
7
+ import sys
8
+
9
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
10
+ import install
11
+ import learning
12
+ import metrics
13
+ import router
14
+
15
+ MODES = ("manual", "fixed", "recommended", "adaptive")
16
+ PROFILES = ("economy", "balanced", "quality")
17
+
18
+
19
+ def defaults() -> dict:
20
+ return {"schema": 1, "mode": "recommended", "profile": "balanced", "project_pin": None,
21
+ "task_pins": {}, "minimum_cases": 20, "quality_floor": 0.8,
22
+ "context_bytes": {"economy": 16000, "balanced": 32000, "quality": 64000},
23
+ "orchestration": {"planner": "work", "executor": "work", "reviewer": "work", "escalation": "escalation"}}
24
+
25
+
26
+ def validate_pin(value: object) -> dict:
27
+ if not isinstance(value, dict) or set(value) != {"provider", "model", "effort"}:
28
+ raise ValueError("A pin needs provider, model and effort (null when unknown/not controlled)")
29
+ for key in ("provider", "model"):
30
+ metrics._identifier(value[key], key)
31
+ if value["effort"] is not None:
32
+ metrics._identifier(value["effort"], "effort")
33
+ return value
34
+
35
+
36
+ def validate_config(value: object) -> dict:
37
+ base = defaults()
38
+ if not isinstance(value, dict) or set(value) - set(base):
39
+ raise ValueError("Invalid routing configuration or unknown fields")
40
+ base.update(copy.deepcopy(value))
41
+ if type(base["schema"]) is not int or base["schema"] != 1:
42
+ raise ValueError("Expected routing schema 1")
43
+ if not isinstance(base["mode"], str) or not isinstance(base["profile"], str) or base["mode"] not in MODES or base["profile"] not in PROFILES:
44
+ raise ValueError("Unknown routing mode/profile")
45
+ if base["project_pin"] is not None:
46
+ validate_pin(base["project_pin"])
47
+ if not isinstance(base["task_pins"], dict):
48
+ raise ValueError("task_pins must be an object")
49
+ for key, pin in base["task_pins"].items():
50
+ metrics._identifier(key, "task pin ID")
51
+ validate_pin(pin)
52
+ if type(base["minimum_cases"]) is not int or base["minimum_cases"] < 5:
53
+ raise ValueError("Minimum sample must be at least five distinct cases")
54
+ if type(base["quality_floor"]) not in {int, float} or not 0.5 <= base["quality_floor"] <= 1:
55
+ raise ValueError("Quality floor must be 0.5-1")
56
+ if (not isinstance(base["context_bytes"], dict) or set(base["context_bytes"]) != set(PROFILES) or
57
+ any(type(v) is not int or v < 1 for v in base["context_bytes"].values())):
58
+ raise ValueError("Every profile needs a positive context byte budget")
59
+ roles = base["orchestration"]
60
+ if (not isinstance(roles, dict) or set(roles) != {"planner", "executor", "reviewer", "escalation"} or
61
+ any(v not in router.ROLES for v in roles.values())):
62
+ raise ValueError("Orchestration roles must map to configured routing roles")
63
+ return base
64
+
65
+
66
+ def load_config(project: Path | None = None, root: Path = router.ROOT) -> dict:
67
+ path = (install.safe_path(metrics._target(project), "ai-kit/router/routing.json") if project else
68
+ root / "template/ai-kit/router/routing.json")
69
+ return validate_config(json.loads(path.read_text(encoding="utf-8")) if path.exists() else {})
70
+
71
+
72
+ def recommend_route(text: str, selection: dict, providers: dict, *, config: dict | None = None,
73
+ records: list[dict] | None = None, mode: str | None = None, profile: str | None = None,
74
+ task_id: str | None = None, stack: str = "unknown", capabilities: list[str] | None = None,
75
+ **overrides) -> dict:
76
+ config = validate_config(config or {})
77
+ mode, profile = mode or config["mode"], profile or config["profile"]
78
+ if mode not in MODES or profile not in PROFILES:
79
+ raise ValueError("Unknown routing mode/profile")
80
+ if stack not in metrics.STACKS:
81
+ raise ValueError("Unknown stack")
82
+ if task_id is not None:
83
+ metrics._identifier(task_id, "task_id")
84
+ capabilities = capabilities or []
85
+ for capability in capabilities:
86
+ metrics._identifier(capability, "capability")
87
+ pin = config["task_pins"].get(task_id, config["project_pin"])
88
+ source = "task pin" if task_id in config["task_pins"] else "project pin"
89
+ explicit = any(overrides.get(k) is not None for k in ("provider", "model", "effort"))
90
+ call = dict(overrides)
91
+ effective_pin = copy.deepcopy(pin)
92
+ if pin:
93
+ if overrides.get("provider") is not None and overrides["provider"] != pin["provider"]:
94
+ effective_pin.pop("model", None)
95
+ effective_pin.pop("effort", None)
96
+ elif overrides.get("model") is not None and overrides["model"] != pin["model"]:
97
+ effective_pin.pop("effort", None)
98
+ call = {**effective_pin, **{k: v for k, v in overrides.items() if v is not None}}
99
+ selection = copy.deepcopy(selection)
100
+ for name in router.ROLES:
101
+ spec = selection["roles"].get(name)
102
+ if isinstance(spec, dict):
103
+ spec.pop("effort", None)
104
+ result = router.route_result(text, selection, providers, **call)
105
+ if mode in {"manual", "fixed"} and not pin and overrides.get("model") is None:
106
+ raise ValueError("Manual/fixed mode requires a project/task pin or explicit model")
107
+ if mode in {"manual", "fixed"} and not result["resolved"]:
108
+ raise ValueError("Pinned model is unresolved")
109
+ if pin and "effort" in effective_pin and overrides.get("effort") is None:
110
+ # Null pin effort explicitly means no effort control; the ladder cannot override it.
111
+ result["effort"] = effective_pin["effort"]
112
+ result["effort_source"] = source
113
+ configured_effort = router._role_selection(result["role"], selection).get("effort") is not None
114
+ if not pin and not explicit and not configured_effort:
115
+ supported = router._supported_efforts(providers.get(result["provider"], {}), result["model"])
116
+ ladder = ("none", "minimal", "low", "medium", "high", "xhigh", "max")
117
+ if profile == "quality" and result["effort"] in ladder and "high" in supported and ladder.index(result["effort"]) < ladder.index("high"):
118
+ result["effort"], result["effort_source"] = "high", "quality profile"
119
+ elif profile == "economy" and result["level"] < 3 and result["effort"] == "medium" and "low" in supported:
120
+ result["effort"], result["effort_source"] = "low", "economy profile"
121
+ learned = None
122
+ if mode == "adaptive" and not pin and not explicit and not configured_effort:
123
+ classified = router.classify_task(text, operation=overrides.get("operation"),
124
+ risks=overrides.get("risks", ()), components=overrides.get("components"))
125
+ task_class = overrides.get("operation") or classified["signals"][0].split(": ")[-1]
126
+ available = {selection.get("default_provider")}
127
+ available.update(router._role_selection(name, selection).get("provider") for name in router.ROLES)
128
+ if any(selection["local_models"].values()):
129
+ available.update(name for name, catalogue in providers.items() if catalogue.get("user_supplied_models"))
130
+ available.discard(None)
131
+ learned = learning.recommend(records or [], result, stack=stack, task_class=task_class,
132
+ level=result["level"], capabilities=capabilities,
133
+ minimum=config["minimum_cases"],
134
+ quality_floor=max(config["quality_floor"], 0.95 if profile == "quality" else 0.5),
135
+ profile=profile, allowed_providers=available)
136
+ if learned:
137
+ catalogue = providers.get(learned["provider"], {})
138
+ declared = list(catalogue.get("roles", {}).values())
139
+ supported = set(next((s.get("capabilities", []) for s in declared if s.get("model") == learned["model"]), []))
140
+ known_models = {s.get("model") for s in declared} | (set(selection["local_models"].values()) if catalogue.get("user_supplied_models") else set())
141
+ efforts = router._supported_efforts(catalogue, learned["model"])
142
+ if (catalogue and catalogue.get("verification_status") != "pending" and learned["model"] in known_models and
143
+ (learned["effort"] is None or learned["effort"] in efforts) and set(capabilities) <= supported):
144
+ proposed = router.route_result(text, selection, providers, **{
145
+ **overrides, **{k: learned[k] for k in ("provider", "model", "effort")}})
146
+ result = proposed
147
+ result["effort"] = learned["effort"]
148
+ result["effort_source"] = "matched measurements"
149
+ else:
150
+ learned = None
151
+ result.update({"routing_mode": mode, "optimization_profile": profile,
152
+ "context_budget_bytes": config["context_bytes"][profile],
153
+ "independent_review": profile == "quality" or result["level"] >= 4,
154
+ "escalation_policy": "diagnosed failure; independent review before acceptance" if profile == "quality" else
155
+ "bounded rework before diagnosed escalation; preserve quality floor" if profile == "economy" else
156
+ "diagnosed capability shortfall; include recovery cost",
157
+ "recommendation_source": "explicit" if explicit else source if pin else
158
+ "matched measurements" if learned else "capability ladder",
159
+ "measurement_evidence": learned, "execution_confirmed": False})
160
+ declared = providers.get(result["provider"], {}).get("roles", {}).values()
161
+ known = set(next((s.get("capabilities", []) for s in declared if s.get("model") == result["model"]), []))
162
+ result["capabilities_verified"] = set(capabilities) <= known
163
+ if not result["capabilities_verified"]:
164
+ result["adjustments"].append("Required capabilities are not declared for this model; verify before execution.")
165
+ if mode == "adaptive" and not learned and not pin and not explicit:
166
+ result["adjustments"].append("Insufficient comparable measured evidence; retain the capability recommendation.")
167
+ return result