PraisonAI 3.0.0__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 (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,587 @@
1
+ """
2
+ ACP Server implementation for PraisonAI.
3
+
4
+ This module implements the Agent Client Protocol server that allows
5
+ IDEs/editors to communicate with PraisonAI agents.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional, Union
13
+
14
+ from .config import ACPConfig
15
+ from .session import ACPSession, SessionStore
16
+
17
+ # Configure logging to stderr only (stdout reserved for JSON-RPC)
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def _setup_stderr_logging(debug: bool = False) -> None:
22
+ """Configure logging to stderr only."""
23
+ handler = logging.StreamHandler(sys.stderr)
24
+ handler.setFormatter(logging.Formatter(
25
+ "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
26
+ ))
27
+
28
+ root_logger = logging.getLogger("praisonai.acp")
29
+ root_logger.handlers.clear()
30
+ root_logger.addHandler(handler)
31
+ root_logger.setLevel(logging.DEBUG if debug else logging.INFO)
32
+
33
+
34
+ class ACPServer:
35
+ """
36
+ ACP Server that exposes PraisonAI agents to IDE clients.
37
+
38
+ Implements the Agent Client Protocol (JSON-RPC 2.0 over stdio).
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ config: Optional[ACPConfig] = None,
44
+ agent: Optional[Any] = None,
45
+ agents: Optional[Any] = None,
46
+ ):
47
+ """
48
+ Initialize ACP server.
49
+
50
+ Args:
51
+ config: ACP configuration
52
+ agent: Optional pre-configured Agent instance
53
+ agents: Optional pre-configured PraisonAIAgents instance
54
+ """
55
+ self.config = config or ACPConfig()
56
+ self._agent = agent
57
+ self._agents = agents
58
+ self._session_store = SessionStore()
59
+ self._sessions: Dict[str, ACPSession] = {}
60
+ self._client = None # ACP client connection
61
+ self._cancelled_sessions: set = set()
62
+
63
+ _setup_stderr_logging(self.config.debug)
64
+
65
+ def _get_agent(self):
66
+ """Lazy load or create agent."""
67
+ if self._agent is not None:
68
+ return self._agent
69
+
70
+ if self._agents is not None:
71
+ return self._agents
72
+
73
+ # Create default agent
74
+ try:
75
+ from praisonaiagents import Agent
76
+ self._agent = Agent(
77
+ name="PraisonAI",
78
+ instructions="You are a helpful AI coding assistant.",
79
+ model=self.config.model or "gpt-4o-mini",
80
+ )
81
+ return self._agent
82
+ except ImportError:
83
+ logger.error("praisonaiagents not installed")
84
+ raise RuntimeError("praisonaiagents package required")
85
+
86
+ def on_connect(self, client) -> None:
87
+ """Called when client connects."""
88
+ self._client = client
89
+ logger.info("Client connected")
90
+
91
+ async def initialize(
92
+ self,
93
+ protocol_version: int,
94
+ client_capabilities: Optional[Dict[str, Any]] = None,
95
+ client_info: Optional[Dict[str, Any]] = None,
96
+ **kwargs: Any,
97
+ ) -> Dict[str, Any]:
98
+ """
99
+ Handle initialize request.
100
+
101
+ Negotiate protocol version and exchange capabilities.
102
+ """
103
+ logger.info(f"Initialize: protocol_version={protocol_version}, client_info={client_info}")
104
+
105
+ # Import here to avoid circular imports and check availability
106
+ try:
107
+ from acp import PROTOCOL_VERSION
108
+ except ImportError:
109
+ PROTOCOL_VERSION = 1
110
+
111
+ return {
112
+ "protocolVersion": min(protocol_version, PROTOCOL_VERSION),
113
+ "agentCapabilities": {
114
+ "loadSession": True,
115
+ "promptCapabilities": {
116
+ "image": False,
117
+ "audio": False,
118
+ "embeddedContext": True,
119
+ },
120
+ "mcpCapabilities": {
121
+ "http": False,
122
+ "sse": False,
123
+ },
124
+ },
125
+ "agentInfo": {
126
+ "name": "praisonai",
127
+ "title": "PraisonAI",
128
+ "version": self._get_version(),
129
+ },
130
+ "authMethods": [],
131
+ }
132
+
133
+ async def authenticate(self, method_id: str, **kwargs: Any) -> Optional[Dict[str, Any]]:
134
+ """Handle authenticate request (optional)."""
135
+ logger.info(f"Authenticate: method_id={method_id}")
136
+ return {}
137
+
138
+ async def new_session(
139
+ self,
140
+ cwd: str,
141
+ mcp_servers: List[Dict[str, Any]],
142
+ **kwargs: Any,
143
+ ) -> Dict[str, Any]:
144
+ """
145
+ Create a new conversation session.
146
+
147
+ Args:
148
+ cwd: Working directory for the session
149
+ mcp_servers: List of MCP server configurations
150
+ """
151
+ logger.info(f"New session: cwd={cwd}")
152
+
153
+ workspace = Path(cwd).resolve()
154
+ session = ACPSession.create(
155
+ workspace=workspace,
156
+ agent_id=self.config.agent,
157
+ )
158
+ session.mcp_servers = mcp_servers
159
+
160
+ self._sessions[session.session_id] = session
161
+ self._session_store.save(session)
162
+
163
+ logger.info(f"Created session: {session.session_id}")
164
+
165
+ return {
166
+ "sessionId": session.session_id,
167
+ "modes": self._get_available_modes(),
168
+ }
169
+
170
+ async def load_session(
171
+ self,
172
+ cwd: str,
173
+ mcp_servers: List[Dict[str, Any]],
174
+ session_id: str,
175
+ **kwargs: Any,
176
+ ) -> Optional[Dict[str, Any]]:
177
+ """
178
+ Load an existing session.
179
+
180
+ Replays conversation history via session/update notifications.
181
+ """
182
+ logger.info(f"Load session: session_id={session_id}")
183
+
184
+ # Try to load from store
185
+ session = self._session_store.load(session_id)
186
+ if session is None:
187
+ logger.warning(f"Session not found: {session_id}")
188
+ return None
189
+
190
+ # Update workspace and MCP servers
191
+ session.workspace = Path(cwd).resolve()
192
+ session.mcp_servers = mcp_servers
193
+ session.update_activity()
194
+
195
+ self._sessions[session_id] = session
196
+
197
+ # Replay conversation history
198
+ await self._replay_session_history(session)
199
+
200
+ return {}
201
+
202
+ async def list_sessions(
203
+ self,
204
+ cursor: Optional[str] = None,
205
+ cwd: Optional[str] = None,
206
+ **kwargs: Any,
207
+ ) -> Dict[str, Any]:
208
+ """List available sessions."""
209
+ sessions = self._session_store.list_sessions()
210
+
211
+ # Filter by cwd if provided
212
+ if cwd:
213
+ cwd_path = Path(cwd).resolve()
214
+ sessions = [s for s in sessions if s.workspace == cwd_path]
215
+
216
+ return {
217
+ "sessions": [
218
+ {
219
+ "sessionId": s.session_id,
220
+ "createdAt": s.created_at,
221
+ "lastActivity": s.last_activity,
222
+ }
223
+ for s in sessions
224
+ ],
225
+ }
226
+
227
+ async def set_session_mode(
228
+ self,
229
+ mode_id: str,
230
+ session_id: str,
231
+ **kwargs: Any,
232
+ ) -> Optional[Dict[str, Any]]:
233
+ """Set the operating mode for a session."""
234
+ logger.info(f"Set session mode: session_id={session_id}, mode_id={mode_id}")
235
+
236
+ session = self._sessions.get(session_id)
237
+ if session:
238
+ session.mode = mode_id
239
+ self._session_store.save(session)
240
+
241
+ return {}
242
+
243
+ async def set_session_model(
244
+ self,
245
+ model_id: str,
246
+ session_id: str,
247
+ **kwargs: Any,
248
+ ) -> Optional[Dict[str, Any]]:
249
+ """Set the LLM model for a session."""
250
+ logger.info(f"Set session model: session_id={session_id}, model_id={model_id}")
251
+
252
+ session = self._sessions.get(session_id)
253
+ if session:
254
+ session.model = model_id
255
+ self._session_store.save(session)
256
+
257
+ return {}
258
+
259
+ async def prompt(
260
+ self,
261
+ prompt: List[Dict[str, Any]],
262
+ session_id: str,
263
+ **kwargs: Any,
264
+ ) -> Dict[str, Any]:
265
+ """
266
+ Handle user prompt.
267
+
268
+ This is the main interaction method where the user sends a message
269
+ and the agent processes it.
270
+ """
271
+ logger.info(f"Prompt: session_id={session_id}")
272
+
273
+ session = self._sessions.get(session_id)
274
+ if session is None:
275
+ logger.error(f"Session not found: {session_id}")
276
+ return {"stopReason": "refusal"}
277
+
278
+ # Check if cancelled
279
+ if session_id in self._cancelled_sessions:
280
+ self._cancelled_sessions.discard(session_id)
281
+ return {"stopReason": "cancelled"}
282
+
283
+ # Extract text from prompt blocks
284
+ user_message = self._extract_prompt_text(prompt)
285
+ session.add_message("user", user_message)
286
+
287
+ try:
288
+ # Get agent response
289
+ agent = self._get_agent()
290
+
291
+ # Send thinking update
292
+ await self._send_thought(session_id, "Processing your request...")
293
+
294
+ # Generate response
295
+ response = await self._generate_response(agent, user_message, session)
296
+
297
+ # Send response as agent message chunks
298
+ await self._send_agent_message(session_id, response)
299
+
300
+ # Save session
301
+ session.add_message("assistant", response)
302
+ self._session_store.save(session)
303
+
304
+ return {"stopReason": "end_turn"}
305
+
306
+ except asyncio.CancelledError:
307
+ return {"stopReason": "cancelled"}
308
+ except Exception as e:
309
+ logger.exception(f"Error processing prompt: {e}")
310
+ await self._send_agent_message(session_id, f"Error: {str(e)}")
311
+ return {"stopReason": "refusal"}
312
+
313
+ async def fork_session(
314
+ self,
315
+ cwd: str,
316
+ session_id: str,
317
+ mcp_servers: Optional[List[Dict[str, Any]]] = None,
318
+ **kwargs: Any,
319
+ ) -> Dict[str, Any]:
320
+ """Fork an existing session."""
321
+ logger.info(f"Fork session: session_id={session_id}")
322
+
323
+ original = self._sessions.get(session_id)
324
+ if original is None:
325
+ original = self._session_store.load(session_id)
326
+
327
+ if original is None:
328
+ # Create new session if original not found
329
+ return await self.new_session(cwd, mcp_servers or [])
330
+
331
+ # Create forked session
332
+ forked = ACPSession.create(
333
+ workspace=Path(cwd).resolve(),
334
+ agent_id=original.agent_id,
335
+ )
336
+ forked.messages = original.messages.copy()
337
+ forked.mcp_servers = mcp_servers or original.mcp_servers
338
+
339
+ self._sessions[forked.session_id] = forked
340
+ self._session_store.save(forked)
341
+
342
+ return {"sessionId": forked.session_id}
343
+
344
+ async def resume_session(
345
+ self,
346
+ cwd: str,
347
+ session_id: str,
348
+ mcp_servers: Optional[List[Dict[str, Any]]] = None,
349
+ **kwargs: Any,
350
+ ) -> Dict[str, Any]:
351
+ """Resume an existing session."""
352
+ return await self.load_session(cwd, mcp_servers or [], session_id)
353
+
354
+ async def cancel(self, session_id: str, **kwargs: Any) -> None:
355
+ """Cancel ongoing operations for a session."""
356
+ logger.info(f"Cancel: session_id={session_id}")
357
+ self._cancelled_sessions.add(session_id)
358
+
359
+ async def ext_method(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
360
+ """Handle extension method calls."""
361
+ logger.info(f"Extension method: {method}")
362
+ return {"error": f"Unknown extension method: {method}"}
363
+
364
+ async def ext_notification(self, method: str, params: Dict[str, Any]) -> None:
365
+ """Handle extension notifications."""
366
+ logger.info(f"Extension notification: {method}")
367
+
368
+ # Helper methods
369
+
370
+ def _get_version(self) -> str:
371
+ """Get PraisonAI version."""
372
+ try:
373
+ from praisonai.version import __version__
374
+ return __version__
375
+ except ImportError:
376
+ return "0.0.0"
377
+
378
+ def _get_available_modes(self) -> Optional[List[Dict[str, Any]]]:
379
+ """Get available operating modes."""
380
+ return [
381
+ {
382
+ "id": "manual",
383
+ "name": "Manual",
384
+ "description": "Requires approval for all actions",
385
+ },
386
+ {
387
+ "id": "auto",
388
+ "name": "Auto",
389
+ "description": "Auto-approve within workspace",
390
+ },
391
+ ]
392
+
393
+ def _extract_prompt_text(self, prompt: List[Dict[str, Any]]) -> str:
394
+ """Extract text content from prompt blocks."""
395
+ texts = []
396
+ for block in prompt:
397
+ if isinstance(block, dict):
398
+ block_type = block.get("type", "")
399
+ if block_type == "text":
400
+ texts.append(block.get("text", ""))
401
+ elif block_type == "resource":
402
+ resource = block.get("resource", {})
403
+ if "text" in resource:
404
+ texts.append(f"[File: {resource.get('uri', 'unknown')}]\n{resource['text']}")
405
+ return "\n".join(texts)
406
+
407
+ async def _generate_response(
408
+ self,
409
+ agent: Any,
410
+ message: str,
411
+ session: ACPSession,
412
+ ) -> str:
413
+ """Generate agent response."""
414
+ try:
415
+ # Try async chat first
416
+ if hasattr(agent, "achat"):
417
+ response = await agent.achat(message)
418
+ elif hasattr(agent, "chat"):
419
+ # Run sync chat in executor
420
+ loop = asyncio.get_event_loop()
421
+ response = await loop.run_in_executor(None, agent.chat, message)
422
+ else:
423
+ response = "Agent does not support chat interface"
424
+
425
+ return str(response) if response else ""
426
+ except Exception as e:
427
+ logger.exception(f"Error generating response: {e}")
428
+ return f"Error generating response: {str(e)}"
429
+
430
+ async def _send_agent_message(self, session_id: str, text: str) -> None:
431
+ """Send agent message chunk to client."""
432
+ if self._client is None:
433
+ return
434
+
435
+ try:
436
+ from acp import update_agent_message_text
437
+ update = update_agent_message_text(text)
438
+ await self._client.session_update(session_id=session_id, update=update)
439
+ except ImportError:
440
+ # Fallback without ACP SDK
441
+ logger.debug(f"Agent message: {text[:100]}...")
442
+
443
+ async def _send_thought(self, session_id: str, text: str) -> None:
444
+ """Send agent thought to client."""
445
+ if self._client is None:
446
+ return
447
+
448
+ try:
449
+ from acp import update_agent_thought_text
450
+ update = update_agent_thought_text(text)
451
+ await self._client.session_update(session_id=session_id, update=update)
452
+ except ImportError:
453
+ logger.debug(f"Agent thought: {text}")
454
+
455
+ async def _replay_session_history(self, session: ACPSession) -> None:
456
+ """Replay session history to client."""
457
+ if self._client is None:
458
+ return
459
+
460
+ try:
461
+ from acp import update_user_message_text, update_agent_message_text
462
+
463
+ for msg in session.messages:
464
+ role = msg.get("role", "")
465
+ content = msg.get("content", "")
466
+
467
+ if role == "user":
468
+ update = update_user_message_text(str(content))
469
+ elif role == "assistant":
470
+ update = update_agent_message_text(str(content))
471
+ else:
472
+ continue
473
+
474
+ await self._client.session_update(
475
+ session_id=session.session_id,
476
+ update=update,
477
+ )
478
+ except ImportError:
479
+ logger.debug("Skipping history replay (ACP SDK not available)")
480
+
481
+
482
+ async def _run_server(config: ACPConfig) -> None:
483
+ """Run the ACP server."""
484
+ try:
485
+ from acp import Agent as ACPAgent, run_agent
486
+ except ImportError:
487
+ logger.error(
488
+ "agent-client-protocol package not installed.\n"
489
+ "Install with: pip install praisonai[acp]\n"
490
+ "Or: pip install agent-client-protocol"
491
+ )
492
+ sys.exit(1)
493
+
494
+ server = ACPServer(config=config)
495
+
496
+ # Create ACP-compatible agent wrapper
497
+ class PraisonACPAgent(ACPAgent):
498
+ def on_connect(self, conn):
499
+ server.on_connect(conn)
500
+
501
+ async def initialize(self, protocol_version, client_capabilities=None, client_info=None, **kwargs):
502
+ return await server.initialize(protocol_version, client_capabilities, client_info, **kwargs)
503
+
504
+ async def authenticate(self, method_id, **kwargs):
505
+ return await server.authenticate(method_id, **kwargs)
506
+
507
+ async def new_session(self, cwd, mcp_servers, **kwargs):
508
+ return await server.new_session(cwd, mcp_servers, **kwargs)
509
+
510
+ async def load_session(self, cwd, mcp_servers, session_id, **kwargs):
511
+ return await server.load_session(cwd, mcp_servers, session_id, **kwargs)
512
+
513
+ async def list_sessions(self, cursor=None, cwd=None, **kwargs):
514
+ return await server.list_sessions(cursor, cwd, **kwargs)
515
+
516
+ async def set_session_mode(self, mode_id, session_id, **kwargs):
517
+ return await server.set_session_mode(mode_id, session_id, **kwargs)
518
+
519
+ async def set_session_model(self, model_id, session_id, **kwargs):
520
+ return await server.set_session_model(model_id, session_id, **kwargs)
521
+
522
+ async def prompt(self, prompt, session_id, **kwargs):
523
+ return await server.prompt(prompt, session_id, **kwargs)
524
+
525
+ async def fork_session(self, cwd, session_id, mcp_servers=None, **kwargs):
526
+ return await server.fork_session(cwd, session_id, mcp_servers, **kwargs)
527
+
528
+ async def resume_session(self, cwd, session_id, mcp_servers=None, **kwargs):
529
+ return await server.resume_session(cwd, session_id, mcp_servers, **kwargs)
530
+
531
+ async def cancel(self, session_id, **kwargs):
532
+ return await server.cancel(session_id, **kwargs)
533
+
534
+ async def ext_method(self, method, params):
535
+ return await server.ext_method(method, params)
536
+
537
+ async def ext_notification(self, method, params):
538
+ return await server.ext_notification(method, params)
539
+
540
+ logger.info("Starting PraisonAI ACP server...")
541
+ await run_agent(PraisonACPAgent(), use_unstable_protocol=True)
542
+
543
+
544
+ def serve(
545
+ workspace: Union[str, Path] = ".",
546
+ agent: str = "default",
547
+ model: Optional[str] = None,
548
+ resume: Optional[str] = None,
549
+ resume_last: bool = False,
550
+ debug: bool = False,
551
+ read_only: bool = True,
552
+ allow_write: bool = False,
553
+ allow_shell: bool = False,
554
+ approval_mode: str = "manual",
555
+ **kwargs: Any,
556
+ ) -> None:
557
+ """
558
+ Start the ACP server.
559
+
560
+ This is the main entry point for running PraisonAI as an ACP agent.
561
+
562
+ Args:
563
+ workspace: Working directory for the session
564
+ agent: Agent name or configuration
565
+ model: LLM model to use
566
+ resume: Session ID to resume
567
+ resume_last: Resume the last session
568
+ debug: Enable debug logging
569
+ read_only: Read-only mode (default True)
570
+ allow_write: Allow file writes
571
+ allow_shell: Allow shell commands
572
+ approval_mode: Approval mode (manual, auto, scoped)
573
+ """
574
+ config = ACPConfig(
575
+ workspace=Path(workspace).resolve(),
576
+ agent=agent,
577
+ model=model,
578
+ resume_session=resume,
579
+ resume_last=resume_last,
580
+ debug=debug,
581
+ read_only=read_only,
582
+ allow_write=allow_write,
583
+ allow_shell=allow_shell,
584
+ approval_mode=approval_mode,
585
+ )
586
+
587
+ asyncio.run(_run_server(config))