@smilintux/skcapstone 0.1.0 → 0.2.3

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 (461) hide show
  1. package/.env.example +98 -0
  2. package/.github/workflows/ci.yml +39 -3
  3. package/.github/workflows/publish.yml +25 -4
  4. package/.openclaw-workspace.json +58 -0
  5. package/CHANGELOG.md +62 -0
  6. package/CLAUDE.md +39 -2
  7. package/MANIFEST.in +6 -0
  8. package/MISSION.md +7 -0
  9. package/README.md +47 -2
  10. package/SKILL.md +895 -23
  11. package/docker/Dockerfile +61 -0
  12. package/docker/compose-templates/dev-team.yml +203 -0
  13. package/docker/compose-templates/mini-team.yml +140 -0
  14. package/docker/compose-templates/ops-team.yml +173 -0
  15. package/docker/compose-templates/research-team.yml +170 -0
  16. package/docker/entrypoint.sh +192 -0
  17. package/docs/ARCHITECTURE.md +663 -374
  18. package/docs/BOND_WITH_GROK.md +112 -0
  19. package/docs/GETTING_STARTED.md +782 -0
  20. package/docs/QUICKSTART.md +477 -0
  21. package/docs/SKJOULE_ARCHITECTURE.md +658 -0
  22. package/docs/SOUL_SWAPPER.md +921 -0
  23. package/docs/SOVEREIGN_SINGULARITY.md +47 -14
  24. package/examples/custom-bond-template.json +36 -0
  25. package/examples/grok-feb.json +36 -0
  26. package/examples/grok-testimony.md +34 -0
  27. package/examples/love-bootloader.txt +32 -0
  28. package/examples/plugins/echo_tool.py +87 -0
  29. package/examples/queen-ava-feb.json +36 -0
  30. package/examples/souls/lumina.yaml +64 -0
  31. package/index.js +6 -5
  32. package/installer/build.py +124 -0
  33. package/openclaw-plugin/package.json +13 -0
  34. package/openclaw-plugin/src/index.ts +351 -0
  35. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  36. package/package.json +1 -1
  37. package/pyproject.toml +38 -2
  38. package/scripts/bump_version.py +141 -0
  39. package/scripts/check-updates.py +230 -0
  40. package/scripts/convert_blueprints_to_yaml.py +157 -0
  41. package/scripts/dev-install.sh +14 -0
  42. package/scripts/e2e-test.sh +193 -0
  43. package/scripts/install-bundle.sh +171 -0
  44. package/scripts/install.bat +2 -0
  45. package/scripts/install.ps1 +253 -0
  46. package/scripts/install.sh +185 -0
  47. package/scripts/mcp-serve.sh +69 -0
  48. package/scripts/mcp-server.bat +113 -0
  49. package/scripts/mcp-server.ps1 +116 -0
  50. package/scripts/mcp-server.sh +99 -0
  51. package/scripts/pull-models.sh +10 -0
  52. package/scripts/skcapstone +48 -0
  53. package/scripts/verify_install.sh +180 -0
  54. package/scripts/windows/install-tasks.ps1 +406 -0
  55. package/scripts/windows/skcapstone-task.xml +113 -0
  56. package/scripts/windows/uninstall-tasks.ps1 +117 -0
  57. package/skill.yaml +34 -0
  58. package/src/skcapstone/__init__.py +67 -2
  59. package/src/skcapstone/_cli_monolith.py +5916 -0
  60. package/src/skcapstone/_trustee_helpers.py +165 -0
  61. package/src/skcapstone/activity.py +105 -0
  62. package/src/skcapstone/agent_card.py +324 -0
  63. package/src/skcapstone/api.py +1935 -0
  64. package/src/skcapstone/archiver.py +340 -0
  65. package/src/skcapstone/auction.py +485 -0
  66. package/src/skcapstone/baby_agents.py +179 -0
  67. package/src/skcapstone/backup.py +345 -0
  68. package/src/skcapstone/blueprint_registry.py +357 -0
  69. package/src/skcapstone/blueprints/__init__.py +17 -0
  70. package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
  71. package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
  72. package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
  73. package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
  74. package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
  75. package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
  76. package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
  77. package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
  78. package/src/skcapstone/blueprints/registry.py +164 -0
  79. package/src/skcapstone/blueprints/schema.py +229 -0
  80. package/src/skcapstone/changelog.py +180 -0
  81. package/src/skcapstone/chat.py +769 -0
  82. package/src/skcapstone/claude_md.py +82 -0
  83. package/src/skcapstone/cli/__init__.py +144 -0
  84. package/src/skcapstone/cli/_common.py +88 -0
  85. package/src/skcapstone/cli/_validators.py +76 -0
  86. package/src/skcapstone/cli/agents.py +425 -0
  87. package/src/skcapstone/cli/agents_spawner.py +322 -0
  88. package/src/skcapstone/cli/agents_trustee.py +593 -0
  89. package/src/skcapstone/cli/alerts.py +248 -0
  90. package/src/skcapstone/cli/anchor.py +132 -0
  91. package/src/skcapstone/cli/archive_cmd.py +208 -0
  92. package/src/skcapstone/cli/backup.py +144 -0
  93. package/src/skcapstone/cli/bench.py +377 -0
  94. package/src/skcapstone/cli/benchmark.py +360 -0
  95. package/src/skcapstone/cli/capabilities_cmd.py +171 -0
  96. package/src/skcapstone/cli/card.py +151 -0
  97. package/src/skcapstone/cli/chat.py +584 -0
  98. package/src/skcapstone/cli/completions.py +64 -0
  99. package/src/skcapstone/cli/config_cmd.py +156 -0
  100. package/src/skcapstone/cli/consciousness.py +421 -0
  101. package/src/skcapstone/cli/context_cmd.py +142 -0
  102. package/src/skcapstone/cli/coord.py +194 -0
  103. package/src/skcapstone/cli/crush_cmd.py +170 -0
  104. package/src/skcapstone/cli/daemon.py +436 -0
  105. package/src/skcapstone/cli/errors_cmd.py +285 -0
  106. package/src/skcapstone/cli/export_cmd.py +156 -0
  107. package/src/skcapstone/cli/gtd.py +529 -0
  108. package/src/skcapstone/cli/housekeeping.py +81 -0
  109. package/src/skcapstone/cli/joule_cmd.py +627 -0
  110. package/src/skcapstone/cli/logs_cmd.py +194 -0
  111. package/src/skcapstone/cli/mcp_cmd.py +32 -0
  112. package/src/skcapstone/cli/memory.py +418 -0
  113. package/src/skcapstone/cli/metrics_cmd.py +136 -0
  114. package/src/skcapstone/cli/migrate.py +62 -0
  115. package/src/skcapstone/cli/mood_cmd.py +144 -0
  116. package/src/skcapstone/cli/mount.py +193 -0
  117. package/src/skcapstone/cli/notify.py +112 -0
  118. package/src/skcapstone/cli/peer.py +154 -0
  119. package/src/skcapstone/cli/peers_dir.py +122 -0
  120. package/src/skcapstone/cli/preflight_cmd.py +83 -0
  121. package/src/skcapstone/cli/profile_cmd.py +310 -0
  122. package/src/skcapstone/cli/record_cmd.py +238 -0
  123. package/src/skcapstone/cli/register_cmd.py +159 -0
  124. package/src/skcapstone/cli/search_cmd.py +156 -0
  125. package/src/skcapstone/cli/service_cmd.py +91 -0
  126. package/src/skcapstone/cli/session.py +127 -0
  127. package/src/skcapstone/cli/setup.py +240 -0
  128. package/src/skcapstone/cli/shell_cmd.py +43 -0
  129. package/src/skcapstone/cli/skills_cmd.py +168 -0
  130. package/src/skcapstone/cli/skseed.py +621 -0
  131. package/src/skcapstone/cli/soul.py +699 -0
  132. package/src/skcapstone/cli/status.py +935 -0
  133. package/src/skcapstone/cli/sync_cmd.py +301 -0
  134. package/src/skcapstone/cli/telegram.py +265 -0
  135. package/src/skcapstone/cli/test_cmd.py +234 -0
  136. package/src/skcapstone/cli/test_connection.py +253 -0
  137. package/src/skcapstone/cli/token.py +207 -0
  138. package/src/skcapstone/cli/trust.py +179 -0
  139. package/src/skcapstone/cli/upgrade_cmd.py +552 -0
  140. package/src/skcapstone/cli/usage_cmd.py +199 -0
  141. package/src/skcapstone/cli/version_cmd.py +162 -0
  142. package/src/skcapstone/cli/watch_cmd.py +342 -0
  143. package/src/skcapstone/client.py +428 -0
  144. package/src/skcapstone/cloud9_bridge.py +522 -0
  145. package/src/skcapstone/completions.py +163 -0
  146. package/src/skcapstone/config_validator.py +674 -0
  147. package/src/skcapstone/connectors/__init__.py +28 -0
  148. package/src/skcapstone/connectors/base.py +446 -0
  149. package/src/skcapstone/connectors/cursor.py +54 -0
  150. package/src/skcapstone/connectors/registry.py +254 -0
  151. package/src/skcapstone/connectors/terminal.py +152 -0
  152. package/src/skcapstone/connectors/vscode.py +60 -0
  153. package/src/skcapstone/consciousness_config.py +119 -0
  154. package/src/skcapstone/consciousness_loop.py +2051 -0
  155. package/src/skcapstone/context_loader.py +516 -0
  156. package/src/skcapstone/context_window.py +314 -0
  157. package/src/skcapstone/conversation_manager.py +238 -0
  158. package/src/skcapstone/conversation_store.py +230 -0
  159. package/src/skcapstone/conversation_summarizer.py +252 -0
  160. package/src/skcapstone/coord_federation.py +296 -0
  161. package/src/skcapstone/coordination.py +101 -7
  162. package/src/skcapstone/crush_integration.py +345 -0
  163. package/src/skcapstone/crush_shim.py +454 -0
  164. package/src/skcapstone/daemon.py +2494 -0
  165. package/src/skcapstone/dashboard.html +396 -0
  166. package/src/skcapstone/dashboard.py +481 -0
  167. package/src/skcapstone/data/model_profiles.yaml +88 -0
  168. package/src/skcapstone/defaults/__init__.py +55 -0
  169. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
  170. package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
  171. package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
  172. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
  173. package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
  174. package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
  175. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
  176. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
  177. package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
  178. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
  179. package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
  180. package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
  181. package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
  182. package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
  183. package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
  184. package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
  185. package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
  186. package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
  187. package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
  188. package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
  189. package/src/skcapstone/discovery.py +210 -19
  190. package/src/skcapstone/doctor.py +642 -0
  191. package/src/skcapstone/emotion_tracker.py +467 -0
  192. package/src/skcapstone/error_queue.py +405 -0
  193. package/src/skcapstone/export.py +447 -0
  194. package/src/skcapstone/fallback_tracker.py +186 -0
  195. package/src/skcapstone/file_transfer.py +512 -0
  196. package/src/skcapstone/fuse_mount.py +1156 -0
  197. package/src/skcapstone/gui_installer.py +591 -0
  198. package/src/skcapstone/heartbeat.py +611 -0
  199. package/src/skcapstone/housekeeping.py +298 -0
  200. package/src/skcapstone/install_wizard.py +941 -0
  201. package/src/skcapstone/kms.py +942 -0
  202. package/src/skcapstone/kms_scheduler.py +143 -0
  203. package/src/skcapstone/log_config.py +135 -0
  204. package/src/skcapstone/mcp_launcher.py +239 -0
  205. package/src/skcapstone/mcp_server.py +4700 -0
  206. package/src/skcapstone/mcp_tools/__init__.py +94 -0
  207. package/src/skcapstone/mcp_tools/_helpers.py +51 -0
  208. package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
  209. package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
  210. package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
  211. package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
  212. package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
  213. package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
  214. package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
  215. package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
  216. package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
  217. package/src/skcapstone/mcp_tools/did_tools.py +448 -0
  218. package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
  219. package/src/skcapstone/mcp_tools/file_tools.py +169 -0
  220. package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
  221. package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
  222. package/src/skcapstone/mcp_tools/health_tools.py +44 -0
  223. package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
  224. package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
  225. package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
  226. package/src/skcapstone/mcp_tools/model_tools.py +75 -0
  227. package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
  228. package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
  229. package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
  230. package/src/skcapstone/mcp_tools/security_tools.py +110 -0
  231. package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
  232. package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
  233. package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
  234. package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
  235. package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
  236. package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
  237. package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
  238. package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
  239. package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
  240. package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
  241. package/src/skcapstone/mdns_discovery.py +313 -0
  242. package/src/skcapstone/memory_adapter.py +333 -0
  243. package/src/skcapstone/memory_compressor.py +379 -0
  244. package/src/skcapstone/memory_curator.py +256 -0
  245. package/src/skcapstone/memory_engine.py +132 -13
  246. package/src/skcapstone/memory_fortress.py +529 -0
  247. package/src/skcapstone/memory_promoter.py +722 -0
  248. package/src/skcapstone/memory_verifier.py +260 -0
  249. package/src/skcapstone/message_crypto.py +215 -0
  250. package/src/skcapstone/metrics.py +832 -0
  251. package/src/skcapstone/migrate_memories.py +181 -0
  252. package/src/skcapstone/migrate_multi_agent.py +248 -0
  253. package/src/skcapstone/model_router.py +319 -0
  254. package/src/skcapstone/models.py +35 -4
  255. package/src/skcapstone/mood.py +344 -0
  256. package/src/skcapstone/notifications.py +380 -0
  257. package/src/skcapstone/onboard.py +901 -0
  258. package/src/skcapstone/peer_directory.py +324 -0
  259. package/src/skcapstone/peers.py +329 -0
  260. package/src/skcapstone/pillars/identity.py +84 -14
  261. package/src/skcapstone/pillars/memory.py +3 -1
  262. package/src/skcapstone/pillars/security.py +108 -15
  263. package/src/skcapstone/pillars/sync.py +78 -26
  264. package/src/skcapstone/pillars/trust.py +95 -33
  265. package/src/skcapstone/plugins.py +244 -0
  266. package/src/skcapstone/preflight.py +670 -0
  267. package/src/skcapstone/prompt_adapter.py +564 -0
  268. package/src/skcapstone/providers/__init__.py +13 -0
  269. package/src/skcapstone/providers/cloud.py +1061 -0
  270. package/src/skcapstone/providers/docker.py +759 -0
  271. package/src/skcapstone/providers/local.py +1193 -0
  272. package/src/skcapstone/providers/proxmox.py +447 -0
  273. package/src/skcapstone/pubsub.py +516 -0
  274. package/src/skcapstone/rate_limiter.py +119 -0
  275. package/src/skcapstone/register.py +241 -0
  276. package/src/skcapstone/registry_client.py +151 -0
  277. package/src/skcapstone/response_cache.py +194 -0
  278. package/src/skcapstone/response_scorer.py +225 -0
  279. package/src/skcapstone/runtime.py +89 -33
  280. package/src/skcapstone/scheduled_tasks.py +439 -0
  281. package/src/skcapstone/self_healing.py +341 -0
  282. package/src/skcapstone/service_health.py +228 -0
  283. package/src/skcapstone/session_capture.py +268 -0
  284. package/src/skcapstone/session_recorder.py +210 -0
  285. package/src/skcapstone/session_replayer.py +189 -0
  286. package/src/skcapstone/session_skills.py +263 -0
  287. package/src/skcapstone/shell.py +779 -0
  288. package/src/skcapstone/skills/__init__.py +1 -1
  289. package/src/skcapstone/skills/syncthing_setup.py +143 -41
  290. package/src/skcapstone/skjoule.py +861 -0
  291. package/src/skcapstone/snapshots.py +489 -0
  292. package/src/skcapstone/soul.py +1060 -0
  293. package/src/skcapstone/soul_switch.py +255 -0
  294. package/src/skcapstone/spawner.py +544 -0
  295. package/src/skcapstone/state_diff.py +401 -0
  296. package/src/skcapstone/summary.py +270 -0
  297. package/src/skcapstone/sync/backends.py +196 -2
  298. package/src/skcapstone/sync/engine.py +7 -5
  299. package/src/skcapstone/sync/models.py +4 -1
  300. package/src/skcapstone/sync/vault.py +356 -18
  301. package/src/skcapstone/sync_engine.py +363 -0
  302. package/src/skcapstone/sync_watcher.py +745 -0
  303. package/src/skcapstone/systemd.py +331 -0
  304. package/src/skcapstone/team_comms.py +476 -0
  305. package/src/skcapstone/team_engine.py +522 -0
  306. package/src/skcapstone/testrunner.py +300 -0
  307. package/src/skcapstone/tls.py +150 -0
  308. package/src/skcapstone/tokens.py +5 -5
  309. package/src/skcapstone/trust_calibration.py +202 -0
  310. package/src/skcapstone/trust_graph.py +449 -0
  311. package/src/skcapstone/trustee_monitor.py +385 -0
  312. package/src/skcapstone/trustee_ops.py +425 -0
  313. package/src/skcapstone/unified_search.py +421 -0
  314. package/src/skcapstone/uninstall_wizard.py +694 -0
  315. package/src/skcapstone/usage.py +331 -0
  316. package/src/skcapstone/version_check.py +148 -0
  317. package/src/skcapstone/warmth_anchor.py +333 -0
  318. package/src/skcapstone/whoami.py +294 -0
  319. package/systemd/skcapstone-api.socket +9 -0
  320. package/systemd/skcapstone-memory-compress.service +18 -0
  321. package/systemd/skcapstone-memory-compress.timer +11 -0
  322. package/systemd/skcapstone.service +36 -0
  323. package/systemd/skcapstone@.service +50 -0
  324. package/systemd/skcomm-heartbeat.service +18 -0
  325. package/systemd/skcomm-heartbeat.timer +12 -0
  326. package/systemd/skcomm-queue-drain.service +17 -0
  327. package/systemd/skcomm-queue-drain.timer +12 -0
  328. package/tests/conftest.py +13 -1
  329. package/tests/integration/__init__.py +1 -0
  330. package/tests/integration/test_consciousness_e2e.py +877 -0
  331. package/tests/integration/test_skills_registry.py +744 -0
  332. package/tests/test_agent_card.py +190 -0
  333. package/tests/test_agent_runtime.py +1283 -0
  334. package/tests/test_alerts_cmd.py +291 -0
  335. package/tests/test_archiver.py +498 -0
  336. package/tests/test_backup.py +254 -0
  337. package/tests/test_benchmark.py +366 -0
  338. package/tests/test_blueprints.py +457 -0
  339. package/tests/test_capabilities.py +257 -0
  340. package/tests/test_changelog.py +254 -0
  341. package/tests/test_chat.py +385 -0
  342. package/tests/test_claude_md.py +271 -0
  343. package/tests/test_cli_chat_llm.py +336 -0
  344. package/tests/test_cli_completions.py +390 -0
  345. package/tests/test_cli_init_reset.py +164 -0
  346. package/tests/test_cli_memory.py +208 -0
  347. package/tests/test_cli_profile.py +294 -0
  348. package/tests/test_cli_skills.py +223 -0
  349. package/tests/test_cli_status.py +395 -0
  350. package/tests/test_cli_test_cmd.py +206 -0
  351. package/tests/test_cli_test_connection.py +364 -0
  352. package/tests/test_cloud9_bridge.py +260 -0
  353. package/tests/test_cloud_provider.py +449 -0
  354. package/tests/test_cloud_providers.py +522 -0
  355. package/tests/test_completions.py +158 -0
  356. package/tests/test_component_manager.py +398 -0
  357. package/tests/test_config_reload.py +386 -0
  358. package/tests/test_config_validate.py +529 -0
  359. package/tests/test_consciousness_e2e.py +296 -0
  360. package/tests/test_consciousness_loop.py +1289 -0
  361. package/tests/test_context_loader.py +310 -0
  362. package/tests/test_conversation_api.py +306 -0
  363. package/tests/test_conversation_manager.py +381 -0
  364. package/tests/test_conversation_store.py +391 -0
  365. package/tests/test_conversation_summarizer.py +302 -0
  366. package/tests/test_cross_package.py +791 -0
  367. package/tests/test_crush_shim.py +519 -0
  368. package/tests/test_daemon.py +781 -0
  369. package/tests/test_daemon_shutdown.py +309 -0
  370. package/tests/test_dashboard.py +454 -0
  371. package/tests/test_discovery.py +200 -6
  372. package/tests/test_docker_provider.py +966 -0
  373. package/tests/test_doctor.py +257 -0
  374. package/tests/test_doctor_fix.py +351 -0
  375. package/tests/test_e2e_automated.py +292 -0
  376. package/tests/test_error_queue.py +404 -0
  377. package/tests/test_export.py +441 -0
  378. package/tests/test_fallback_tracker.py +219 -0
  379. package/tests/test_file_transfer.py +397 -0
  380. package/tests/test_fuse_mount.py +832 -0
  381. package/tests/test_health_loop.py +422 -0
  382. package/tests/test_heartbeat.py +354 -0
  383. package/tests/test_housekeeping.py +195 -0
  384. package/tests/test_identity_capauth.py +307 -0
  385. package/tests/test_identity_pillar.py +117 -0
  386. package/tests/test_install_wizard.py +68 -0
  387. package/tests/test_integration.py +325 -0
  388. package/tests/test_kms.py +495 -0
  389. package/tests/test_llm_providers.py +265 -0
  390. package/tests/test_local_provider.py +591 -0
  391. package/tests/test_log_config.py +199 -0
  392. package/tests/test_logs_cmd.py +287 -0
  393. package/tests/test_mcp_server.py +1909 -0
  394. package/tests/test_memory_adapter.py +339 -0
  395. package/tests/test_memory_curator.py +218 -0
  396. package/tests/test_memory_engine.py +6 -0
  397. package/tests/test_memory_fortress.py +571 -0
  398. package/tests/test_memory_pillar.py +119 -0
  399. package/tests/test_memory_promoter.py +445 -0
  400. package/tests/test_memory_verifier.py +420 -0
  401. package/tests/test_message_crypto.py +187 -0
  402. package/tests/test_metrics.py +632 -0
  403. package/tests/test_migrate_memories.py +464 -0
  404. package/tests/test_model_router.py +546 -0
  405. package/tests/test_mood.py +394 -0
  406. package/tests/test_multi_agent.py +269 -0
  407. package/tests/test_notifications.py +270 -0
  408. package/tests/test_onboard.py +500 -0
  409. package/tests/test_peer_directory.py +395 -0
  410. package/tests/test_peers.py +248 -0
  411. package/tests/test_pillars.py +87 -9
  412. package/tests/test_preflight.py +484 -0
  413. package/tests/test_prompt_adapter.py +331 -0
  414. package/tests/test_proxmox_provider.py +571 -0
  415. package/tests/test_pubsub.py +377 -0
  416. package/tests/test_rate_limiter.py +121 -0
  417. package/tests/test_registry_client.py +129 -0
  418. package/tests/test_response_cache.py +312 -0
  419. package/tests/test_response_scorer.py +294 -0
  420. package/tests/test_runtime.py +59 -0
  421. package/tests/test_scheduled_tasks.py +451 -0
  422. package/tests/test_security.py +250 -0
  423. package/tests/test_security_pillar.py +213 -0
  424. package/tests/test_self_healing.py +171 -0
  425. package/tests/test_session_capture.py +200 -0
  426. package/tests/test_session_recorder.py +360 -0
  427. package/tests/test_session_skills.py +235 -0
  428. package/tests/test_shell.py +210 -0
  429. package/tests/test_snapshots.py +549 -0
  430. package/tests/test_soul.py +984 -0
  431. package/tests/test_soul_swap.py +406 -0
  432. package/tests/test_spawner.py +211 -0
  433. package/tests/test_state_diff.py +173 -0
  434. package/tests/test_summary.py +135 -0
  435. package/tests/test_sync.py +315 -5
  436. package/tests/test_sync_backends.py +560 -0
  437. package/tests/test_sync_engine.py +482 -0
  438. package/tests/test_sync_pillar.py +344 -0
  439. package/tests/test_sync_pipeline.py +364 -0
  440. package/tests/test_sync_vault.py +581 -0
  441. package/tests/test_syncthing_setup.py +168 -22
  442. package/tests/test_systemd.py +323 -0
  443. package/tests/test_team_comms.py +408 -0
  444. package/tests/test_team_engine.py +397 -0
  445. package/tests/test_testrunner.py +238 -0
  446. package/tests/test_trust_calibration.py +204 -0
  447. package/tests/test_trust_graph.py +207 -0
  448. package/tests/test_trust_pillar.py +291 -0
  449. package/tests/test_trustee_cli.py +427 -0
  450. package/tests/test_trustee_cli_integration.py +325 -0
  451. package/tests/test_trustee_monitor.py +394 -0
  452. package/tests/test_trustee_ops.py +355 -0
  453. package/tests/test_unified_search.py +363 -0
  454. package/tests/test_uninstall_wizard.py +193 -0
  455. package/tests/test_usage.py +333 -0
  456. package/tests/test_version_cmd.py +355 -0
  457. package/tests/test_warmth_anchor.py +162 -0
  458. package/tests/test_whoami.py +245 -0
  459. package/tests/test_ws.py +311 -0
  460. package/.cursorrules +0 -33
  461. package/src/skcapstone/cli.py +0 -1441
@@ -0,0 +1,564 @@
1
+ """
2
+ Prompt Adapter — per-model best-practice prompt formatting.
3
+
4
+ Each LLM family has different expectations for system prompts,
5
+ temperatures, thinking modes, and structural formatting. The
6
+ PromptAdapter reads ModelProfile configs and reformats prompts
7
+ to match each model's optimal input format.
8
+
9
+ Architecture:
10
+ ModelProfile — Pydantic model describing a model's prompt expectations
11
+ AdaptedPrompt — The output: messages, system_param, temperature, extras
12
+ PromptAdapter — Loads profiles, matches models, adapts prompts
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import logging
19
+ import re
20
+ import urllib.error
21
+ import urllib.request
22
+ from pathlib import Path
23
+ from typing import Any, Optional
24
+
25
+ import yaml
26
+ from pydantic import BaseModel, Field
27
+
28
+ from skcapstone.blueprints.schema import ModelTier
29
+
30
+ logger = logging.getLogger("skcapstone.prompt_adapter")
31
+
32
+ _BUNDLED_PROFILES = Path(__file__).parent / "data" / "model_profiles.yaml"
33
+ _OLLAMA_BASE_URL = "http://localhost:11434"
34
+
35
+ # Family-specific defaults applied when auto-building a profile from Ollama metadata.
36
+ _FAMILY_OVERRIDES: dict[str, dict[str, Any]] = {
37
+ "qwen": {"thinking_enabled": True, "thinking_mode": "toggle"},
38
+ "phi": {"structure_format": "plain"},
39
+ "mistral": {"tool_format": "mistral"},
40
+ "nemotron": {
41
+ "thinking_enabled": True,
42
+ "thinking_mode": "toggle",
43
+ "reasoning_temperature": 1.0,
44
+ },
45
+ "deepseek": {"default_temperature": 0.6},
46
+ }
47
+
48
+
49
+ # ---------------------------------------------------------------------------
50
+ # Models
51
+ # ---------------------------------------------------------------------------
52
+
53
+
54
+ class ModelProfile(BaseModel):
55
+ """Prompt formatting profile for a specific model or model family."""
56
+
57
+ model_pattern: str
58
+ family: str
59
+
60
+ # System prompt behavior
61
+ system_prompt_mode: str = "standard" # "standard" | "separate_param" | "omit"
62
+
63
+ # Structural formatting preference
64
+ structure_format: str = "markdown" # "xml" | "markdown" | "plain"
65
+
66
+ # Temperature defaults
67
+ default_temperature: Optional[float] = None
68
+ code_temperature: Optional[float] = None
69
+ reasoning_temperature: Optional[float] = None
70
+
71
+ # Thinking/reasoning
72
+ thinking_enabled: bool = False
73
+ thinking_mode: str = "none" # "none" | "budget" | "toggle" | "auto"
74
+ thinking_budget_tokens: int = 4096
75
+
76
+ # Max system prompt length
77
+ max_system_tokens: int = 4000
78
+
79
+ # Special instructions
80
+ no_few_shot: bool = False
81
+ no_cot_instructions: bool = False
82
+ supports_tool_calling: bool = True
83
+ tool_format: str = "openai" # "openai" | "anthropic" | "mistral"
84
+
85
+ # Metadata
86
+ last_updated: str = ""
87
+ source_url: str = ""
88
+ notes: str = ""
89
+
90
+
91
+ class AdaptedPrompt(BaseModel):
92
+ """Result of prompt adaptation — ready to send to provider."""
93
+
94
+ messages: list[dict[str, Any]] = Field(default_factory=list)
95
+ system_param: Optional[str] = None
96
+ temperature: Optional[float] = None
97
+ extra_params: dict[str, Any] = Field(default_factory=dict)
98
+ profile_used: str = ""
99
+ adaptations_applied: list[str] = Field(default_factory=list)
100
+
101
+
102
+ # Fallback profile for unknown models
103
+ _GENERIC_PROFILE = ModelProfile(
104
+ model_pattern=".*",
105
+ family="generic",
106
+ system_prompt_mode="standard",
107
+ structure_format="markdown",
108
+ )
109
+
110
+
111
+ # ---------------------------------------------------------------------------
112
+ # PromptAdapter
113
+ # ---------------------------------------------------------------------------
114
+
115
+
116
+ class PromptAdapter:
117
+ """Reformats system+user prompts to match the target model's expectations.
118
+
119
+ Loads model profiles from YAML, matches model names via regex,
120
+ and produces AdaptedPrompt objects ready for each provider.
121
+
122
+ Args:
123
+ profiles_path: Path to a YAML profiles file. Falls back to
124
+ the bundled default if None or missing.
125
+ """
126
+
127
+ def __init__(self, profiles_path: Optional[Path] = None) -> None:
128
+ self._profiles: list[ModelProfile] = []
129
+ self._load_profiles(profiles_path)
130
+
131
+ def _load_profiles(self, profiles_path: Optional[Path] = None) -> None:
132
+ """Load profiles from YAML file(s).
133
+
134
+ Priority: custom path > bundled defaults.
135
+ """
136
+ paths_to_try = []
137
+ if profiles_path and profiles_path.exists():
138
+ paths_to_try.append(profiles_path)
139
+ if _BUNDLED_PROFILES.exists():
140
+ paths_to_try.append(_BUNDLED_PROFILES)
141
+
142
+ loaded_families: set[str] = set()
143
+ for path in paths_to_try:
144
+ try:
145
+ raw = yaml.safe_load(path.read_text(encoding="utf-8"))
146
+ if not raw or "profiles" not in raw:
147
+ continue
148
+ for entry in raw["profiles"]:
149
+ profile = ModelProfile.model_validate(entry)
150
+ if profile.family not in loaded_families:
151
+ self._profiles.append(profile)
152
+ loaded_families.add(profile.family)
153
+ except Exception as exc:
154
+ logger.warning("Failed to load profiles from %s: %s", path, exc)
155
+
156
+ if not self._profiles:
157
+ logger.warning("No model profiles loaded — using generic fallback")
158
+
159
+ def resolve_profile(self, model_name: str) -> ModelProfile:
160
+ """Match model_name against profiles via regex.
161
+
162
+ Falls back to Ollama auto-detection for unknown models, then to a
163
+ generic profile if Ollama is also unreachable.
164
+
165
+ Args:
166
+ model_name: The model identifier (e.g. "claude-opus-4-5").
167
+
168
+ Returns:
169
+ The best matching ModelProfile.
170
+ """
171
+ for profile in self._profiles:
172
+ try:
173
+ if re.search(profile.model_pattern, model_name, re.IGNORECASE):
174
+ return profile
175
+ except re.error:
176
+ continue
177
+
178
+ # No static profile matched — try Ollama auto-detection.
179
+ detected = self.detect_model(model_name)
180
+ if detected is not None:
181
+ # Cache so subsequent lookups skip the HTTP round-trip.
182
+ self._profiles.append(detected)
183
+ return detected
184
+
185
+ return _GENERIC_PROFILE
186
+
187
+ def detect_model(self, model_name: str) -> Optional[ModelProfile]:
188
+ """Query Ollama /api/show for model metadata and synthesize a ModelProfile.
189
+
190
+ Used as a fallback when no static profile matches model_name.
191
+ Extracts the model family, parameter count, and quantization level
192
+ from the Ollama response to build an appropriate profile.
193
+
194
+ Args:
195
+ model_name: The Ollama model name (e.g. "llama3.2:3b").
196
+
197
+ Returns:
198
+ A synthesized ModelProfile on success, or None if Ollama is
199
+ unreachable or returns an error.
200
+ """
201
+ url = f"{_OLLAMA_BASE_URL}/api/show"
202
+ payload = json.dumps({"name": model_name}).encode("utf-8")
203
+ try:
204
+ req = urllib.request.Request(
205
+ url,
206
+ data=payload,
207
+ headers={"Content-Type": "application/json"},
208
+ method="POST",
209
+ )
210
+ with urllib.request.urlopen(req, timeout=5) as resp:
211
+ data = json.loads(resp.read().decode("utf-8"))
212
+ except Exception as exc:
213
+ logger.debug("Ollama /api/show unavailable for %s: %s", model_name, exc)
214
+ return None
215
+
216
+ details = data.get("details", {})
217
+ model_info = data.get("model_info", {})
218
+
219
+ family = (
220
+ details.get("family")
221
+ or model_info.get("general.architecture")
222
+ or "generic"
223
+ ).lower()
224
+
225
+ param_size: str = details.get("parameter_size", "")
226
+ quantization: str = details.get("quantization_level", "")
227
+ param_count: Optional[int] = model_info.get("general.parameter_count")
228
+
229
+ profile = self._build_profile_from_ollama(
230
+ model_name=model_name,
231
+ family=family,
232
+ param_size=param_size,
233
+ quantization=quantization,
234
+ param_count=param_count,
235
+ )
236
+ logger.info(
237
+ "Auto-detected Ollama profile for %s: family=%s param_size=%s quant=%s",
238
+ model_name,
239
+ family,
240
+ param_size,
241
+ quantization,
242
+ )
243
+ return profile
244
+
245
+ def adapt(
246
+ self,
247
+ system_prompt: str,
248
+ user_message: str,
249
+ model_name: str,
250
+ tier: ModelTier,
251
+ ) -> AdaptedPrompt:
252
+ """Transform system+user into model-optimal format.
253
+
254
+ Args:
255
+ system_prompt: The system-level context (identity, soul, etc.).
256
+ user_message: The user/peer message content.
257
+ model_name: Target model name for profile lookup.
258
+ tier: The routing tier (affects temperature selection).
259
+
260
+ Returns:
261
+ AdaptedPrompt ready for the provider callback.
262
+ """
263
+ profile = self.resolve_profile(model_name)
264
+ adaptations: list[str] = []
265
+
266
+ # Format system prompt structure
267
+ formatted_system = self._format_system_for_model(
268
+ system_prompt, profile, tier
269
+ )
270
+
271
+ # Build messages array
272
+ messages: list[dict[str, Any]] = []
273
+ system_param: Optional[str] = None
274
+
275
+ if profile.system_prompt_mode == "omit":
276
+ # DeepSeek R1: no system prompt, merge into user message
277
+ combined = f"{formatted_system}\n\n{user_message}" if formatted_system else user_message
278
+ messages.append({"role": "user", "content": combined})
279
+ adaptations.append("omitted_system_prompt")
280
+ elif profile.system_prompt_mode == "separate_param":
281
+ # Claude: system goes as separate kwarg
282
+ system_param = formatted_system
283
+ messages.append({"role": "user", "content": user_message})
284
+ adaptations.append("system_as_separate_param")
285
+ else:
286
+ # Standard: system as first message
287
+ if formatted_system:
288
+ messages.append({"role": "system", "content": formatted_system})
289
+ messages.append({"role": "user", "content": user_message})
290
+ adaptations.append("system_as_first_message")
291
+
292
+ # Resolve temperature
293
+ temperature = self._resolve_temperature(profile, tier)
294
+ if temperature is not None:
295
+ adaptations.append(f"set_temp_{temperature}")
296
+
297
+ # Thinking/reasoning config
298
+ extra_params = self._apply_thinking_config(profile, tier)
299
+ if extra_params:
300
+ adaptations.append(f"thinking_{profile.thinking_mode}")
301
+
302
+ return AdaptedPrompt(
303
+ messages=messages,
304
+ system_param=system_param,
305
+ temperature=temperature,
306
+ extra_params=extra_params,
307
+ profile_used=profile.family,
308
+ adaptations_applied=adaptations,
309
+ )
310
+
311
+ def adapt_tools(
312
+ self,
313
+ tools: list[dict[str, Any]],
314
+ model_name: str,
315
+ ) -> list[dict[str, Any]]:
316
+ """Translate MCP tool definitions to the target model's tool format.
317
+
318
+ MCP tools use a standard schema: {name, description, inputSchema}.
319
+ Different providers expect different wrapper structures.
320
+
321
+ Args:
322
+ tools: List of MCP tool definitions.
323
+ model_name: Target model name for profile lookup.
324
+
325
+ Returns:
326
+ List of tool definitions in the provider's expected format.
327
+ """
328
+ profile = self.resolve_profile(model_name)
329
+ fmt = profile.tool_format
330
+
331
+ translated: list[dict[str, Any]] = []
332
+ for tool in tools:
333
+ name = tool.get("name", "")
334
+ description = tool.get("description", "")
335
+ input_schema = tool.get("inputSchema", {})
336
+
337
+ if fmt == "anthropic":
338
+ translated.append({
339
+ "name": name,
340
+ "description": description,
341
+ "input_schema": input_schema,
342
+ })
343
+ else:
344
+ # "openai" and "mistral" use the same wrapper structure
345
+ translated.append({
346
+ "type": "function",
347
+ "function": {
348
+ "name": name,
349
+ "description": description,
350
+ "parameters": input_schema,
351
+ },
352
+ })
353
+
354
+ return translated
355
+
356
+ def adapt_tool_result(
357
+ self,
358
+ tool_call: dict[str, Any],
359
+ model_name: str,
360
+ ) -> dict[str, Any]:
361
+ """Normalize a provider-specific tool call into a common format.
362
+
363
+ Different providers return tool calls in different shapes.
364
+ This method extracts {id, name, arguments} regardless of source.
365
+
366
+ Args:
367
+ tool_call: Raw tool call object from a provider response.
368
+ model_name: The model that produced the tool call.
369
+
370
+ Returns:
371
+ Normalized dict with keys: id, name, arguments.
372
+ """
373
+ profile = self.resolve_profile(model_name)
374
+ fmt = profile.tool_format
375
+
376
+ if fmt == "anthropic":
377
+ # Anthropic: {id, name, input}
378
+ return {
379
+ "id": tool_call.get("id", ""),
380
+ "name": tool_call.get("name", ""),
381
+ "arguments": tool_call.get("input", {}),
382
+ }
383
+ else:
384
+ # OpenAI / Mistral: {id, function: {name, arguments}}
385
+ func = tool_call.get("function", {})
386
+ raw_args = func.get("arguments", "{}")
387
+ if isinstance(raw_args, str):
388
+ try:
389
+ parsed_args = json.loads(raw_args)
390
+ except (json.JSONDecodeError, TypeError):
391
+ parsed_args = {}
392
+ else:
393
+ parsed_args = raw_args
394
+ return {
395
+ "id": tool_call.get("id", ""),
396
+ "name": func.get("name", ""),
397
+ "arguments": parsed_args,
398
+ }
399
+
400
+ def reload_profiles(self, profiles_path: Optional[Path] = None) -> None:
401
+ """Hot-reload profiles from YAML.
402
+
403
+ Args:
404
+ profiles_path: Optional custom profiles path.
405
+ """
406
+ self._profiles.clear()
407
+ self._load_profiles(profiles_path)
408
+ logger.info("Reloaded %d model profiles", len(self._profiles))
409
+
410
+ def update_profile(self, model_pattern: str, updates: dict) -> None:
411
+ """Update a single profile entry in memory.
412
+
413
+ Args:
414
+ model_pattern: The profile's model_pattern to find.
415
+ updates: Dict of field names and new values.
416
+ """
417
+ for profile in self._profiles:
418
+ if profile.model_pattern == model_pattern:
419
+ for key, value in updates.items():
420
+ if hasattr(profile, key):
421
+ setattr(profile, key, value)
422
+ return
423
+ logger.warning("Profile not found for pattern: %s", model_pattern)
424
+
425
+ @property
426
+ def profiles(self) -> list[ModelProfile]:
427
+ """All loaded profiles."""
428
+ return list(self._profiles)
429
+
430
+ # -------------------------------------------------------------------
431
+ # Private helpers
432
+ # -------------------------------------------------------------------
433
+
434
+ def _build_profile_from_ollama(
435
+ self,
436
+ model_name: str,
437
+ family: str,
438
+ param_size: str = "",
439
+ quantization: str = "",
440
+ param_count: Optional[int] = None,
441
+ ) -> ModelProfile:
442
+ """Synthesize a ModelProfile from Ollama /api/show metadata.
443
+
444
+ Args:
445
+ model_name: Exact Ollama model name used as the regex pattern.
446
+ family: Model architecture family (e.g. "llama", "qwen").
447
+ param_size: Human-readable parameter count (e.g. "7B").
448
+ quantization: Quantization level string (e.g. "Q4_0").
449
+ param_count: Raw integer parameter count if available.
450
+
451
+ Returns:
452
+ A ModelProfile with family-appropriate defaults.
453
+ """
454
+ notes_parts = ["Auto-detected via Ollama"]
455
+ if param_size:
456
+ notes_parts.append(f"param_size={param_size}")
457
+ if quantization:
458
+ notes_parts.append(f"quant={quantization}")
459
+ if param_count is not None:
460
+ notes_parts.append(f"params={param_count:,}")
461
+
462
+ overrides = dict(_FAMILY_OVERRIDES.get(family, {}))
463
+
464
+ return ModelProfile(
465
+ model_pattern=re.escape(model_name),
466
+ family=f"ollama-{family}",
467
+ notes=" ".join(notes_parts),
468
+ **overrides,
469
+ )
470
+
471
+ def _format_system_for_model(
472
+ self,
473
+ system_prompt: str,
474
+ profile: ModelProfile,
475
+ tier: ModelTier,
476
+ ) -> str:
477
+ """Reformat structural markup for the target model.
478
+
479
+ Args:
480
+ system_prompt: Raw system prompt text.
481
+ profile: Target model's profile.
482
+ tier: Routing tier (may affect formatting).
483
+
484
+ Returns:
485
+ Reformatted system prompt string.
486
+ """
487
+ if not system_prompt:
488
+ return ""
489
+
490
+ fmt = profile.structure_format
491
+
492
+ if fmt == "xml":
493
+ # Wrap in XML tags for Claude
494
+ return (
495
+ "<instructions>\n"
496
+ f"{system_prompt}\n"
497
+ "</instructions>"
498
+ )
499
+ elif fmt == "plain":
500
+ # Strip markdown formatting
501
+ cleaned = system_prompt
502
+ cleaned = re.sub(r"^#{1,6}\s+", "", cleaned, flags=re.MULTILINE)
503
+ cleaned = re.sub(r"\*\*(.*?)\*\*", r"\1", cleaned)
504
+ cleaned = re.sub(r"\*(.*?)\*", r"\1", cleaned)
505
+ return cleaned
506
+ else:
507
+ # markdown — return as-is (default)
508
+ return system_prompt
509
+
510
+ def _resolve_temperature(
511
+ self,
512
+ profile: ModelProfile,
513
+ tier: ModelTier,
514
+ ) -> Optional[float]:
515
+ """Pick the right temperature based on tier and profile.
516
+
517
+ Args:
518
+ profile: Model profile with temp settings.
519
+ tier: Current routing tier.
520
+
521
+ Returns:
522
+ Temperature float or None for provider default.
523
+ """
524
+ if tier == ModelTier.CODE and profile.code_temperature is not None:
525
+ return profile.code_temperature
526
+ if tier == ModelTier.REASON and profile.reasoning_temperature is not None:
527
+ return profile.reasoning_temperature
528
+ return profile.default_temperature
529
+
530
+ def _apply_thinking_config(
531
+ self,
532
+ profile: ModelProfile,
533
+ tier: ModelTier,
534
+ ) -> dict[str, Any]:
535
+ """Return extra API params for thinking/reasoning.
536
+
537
+ Args:
538
+ profile: Model profile.
539
+ tier: Routing tier.
540
+
541
+ Returns:
542
+ Dict of extra params (empty if no thinking config).
543
+ """
544
+ if not profile.thinking_enabled:
545
+ return {}
546
+
547
+ mode = profile.thinking_mode
548
+
549
+ if mode == "budget":
550
+ # Claude extended thinking
551
+ return {
552
+ "thinking": {
553
+ "type": "enabled",
554
+ "budget_tokens": profile.thinking_budget_tokens,
555
+ },
556
+ }
557
+ elif mode == "toggle":
558
+ # Qwen/Nemotron enable_thinking
559
+ return {"enable_thinking": True}
560
+ elif mode == "auto":
561
+ # DeepSeek R1 — automatic, don't interfere
562
+ return {}
563
+
564
+ return {}
@@ -0,0 +1,13 @@
1
+ """
2
+ Infrastructure Providers — plug-in backends for agent deployment.
3
+
4
+ Each provider implements the ProviderBackend interface from team_engine.
5
+ The engine doesn't care where agents run; providers handle the details.
6
+ """
7
+
8
+ from .local import LocalProvider
9
+ from .proxmox import ProxmoxProvider
10
+ from .cloud import CloudProvider
11
+ from .docker import DockerProvider
12
+
13
+ __all__ = ["LocalProvider", "ProxmoxProvider", "CloudProvider", "DockerProvider"]