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
+ Message Queue for Interactive Mode.
3
+
4
+ Provides FIFO message queuing while the AI agent is processing,
5
+ inspired by Claude Code, Windsurf Cascade, Cursor, and Gemini CLI.
6
+
7
+ Features:
8
+ - Queue messages while agent is processing
9
+ - Auto-process queue when agent becomes idle
10
+ - Visual indicators for queue status
11
+ - Thread-safe operations
12
+ """
13
+
14
+ import threading
15
+ import time
16
+ from dataclasses import dataclass, field
17
+ from enum import Enum
18
+ from typing import Callable, List, Optional
19
+
20
+
21
+ # ============================================================================
22
+ # Enums
23
+ # ============================================================================
24
+
25
+ class ProcessingState(Enum):
26
+ """State of the agent processing."""
27
+ IDLE = "idle"
28
+ PROCESSING = "processing"
29
+ WAITING_APPROVAL = "waiting_approval"
30
+
31
+
32
+ class MessagePriority(Enum):
33
+ """Priority levels for queued messages."""
34
+ LOW = "low"
35
+ NORMAL = "normal"
36
+ HIGH = "high"
37
+
38
+
39
+ # ============================================================================
40
+ # Data Classes
41
+ # ============================================================================
42
+
43
+ @dataclass
44
+ class QueuedMessage:
45
+ """A message in the queue with metadata."""
46
+ content: str
47
+ timestamp: float = field(default_factory=time.time)
48
+ priority: MessagePriority = MessagePriority.NORMAL
49
+
50
+
51
+ # ============================================================================
52
+ # MessageQueue - Thread-safe FIFO queue
53
+ # ============================================================================
54
+
55
+ class MessageQueue:
56
+ """
57
+ Thread-safe FIFO message queue.
58
+
59
+ Provides basic queue operations with thread safety for concurrent access.
60
+ """
61
+
62
+ def __init__(self):
63
+ self._messages: List[str] = []
64
+ self._lock = threading.Lock()
65
+
66
+ @property
67
+ def is_empty(self) -> bool:
68
+ """Check if queue is empty."""
69
+ with self._lock:
70
+ return len(self._messages) == 0
71
+
72
+ @property
73
+ def count(self) -> int:
74
+ """Get number of messages in queue."""
75
+ with self._lock:
76
+ return len(self._messages)
77
+
78
+ def add(self, message: str) -> bool:
79
+ """
80
+ Add a message to the end of the queue.
81
+
82
+ Args:
83
+ message: The message to add
84
+
85
+ Returns:
86
+ True if message was added, False if empty/whitespace
87
+ """
88
+ trimmed = message.strip() if message else ""
89
+ if not trimmed:
90
+ return False
91
+
92
+ with self._lock:
93
+ self._messages.append(trimmed)
94
+ return True
95
+
96
+ def pop(self) -> Optional[str]:
97
+ """
98
+ Remove and return the first message (FIFO).
99
+
100
+ Returns:
101
+ The first message, or None if queue is empty
102
+ """
103
+ with self._lock:
104
+ if self._messages:
105
+ return self._messages.pop(0)
106
+ return None
107
+
108
+ def peek(self) -> Optional[str]:
109
+ """
110
+ View the first message without removing it.
111
+
112
+ Returns:
113
+ The first message, or None if queue is empty
114
+ """
115
+ with self._lock:
116
+ if self._messages:
117
+ return self._messages[0]
118
+ return None
119
+
120
+ def clear(self) -> None:
121
+ """Remove all messages from the queue."""
122
+ with self._lock:
123
+ self._messages.clear()
124
+
125
+ def get_all(self) -> List[str]:
126
+ """
127
+ Get all messages as a list (does not remove them).
128
+
129
+ Returns:
130
+ Copy of all messages in queue order
131
+ """
132
+ with self._lock:
133
+ return self._messages.copy()
134
+
135
+ def remove_at(self, index: int) -> Optional[str]:
136
+ """
137
+ Remove message at specific index.
138
+
139
+ Args:
140
+ index: The index to remove (0-based)
141
+
142
+ Returns:
143
+ The removed message, or None if index is invalid
144
+ """
145
+ with self._lock:
146
+ if 0 <= index < len(self._messages):
147
+ return self._messages.pop(index)
148
+ return None
149
+
150
+
151
+ # ============================================================================
152
+ # StateManager - Processing state management
153
+ # ============================================================================
154
+
155
+ class StateManager:
156
+ """
157
+ Manages the processing state with optional callbacks.
158
+
159
+ Tracks whether the agent is idle, processing, or waiting for approval.
160
+ """
161
+
162
+ def __init__(self, on_state_change: Optional[Callable[[ProcessingState, ProcessingState], None]] = None):
163
+ self._state = ProcessingState.IDLE
164
+ self._lock = threading.Lock()
165
+ self._on_state_change = on_state_change
166
+
167
+ @property
168
+ def current_state(self) -> ProcessingState:
169
+ """Get current processing state."""
170
+ with self._lock:
171
+ return self._state
172
+
173
+ @property
174
+ def is_idle(self) -> bool:
175
+ """Check if currently idle."""
176
+ return self.current_state == ProcessingState.IDLE
177
+
178
+ @property
179
+ def is_processing(self) -> bool:
180
+ """Check if currently processing."""
181
+ return self.current_state == ProcessingState.PROCESSING
182
+
183
+ def set_state(self, new_state: ProcessingState) -> None:
184
+ """
185
+ Set the processing state.
186
+
187
+ Args:
188
+ new_state: The new state to set
189
+ """
190
+ with self._lock:
191
+ old_state = self._state
192
+ self._state = new_state
193
+
194
+ # Call callback outside lock to prevent deadlock
195
+ if self._on_state_change and old_state != new_state:
196
+ self._on_state_change(old_state, new_state)
197
+
198
+
199
+ # ============================================================================
200
+ # QueueDisplay - Visual formatting
201
+ # ============================================================================
202
+
203
+ class QueueDisplay:
204
+ """
205
+ Formats queue and status for display.
206
+
207
+ Provides visual indicators for queue status, inspired by Codex CLI.
208
+ """
209
+
210
+ def __init__(
211
+ self,
212
+ queue: MessageQueue,
213
+ state_manager: Optional[StateManager] = None,
214
+ max_message_length: int = 50
215
+ ):
216
+ self._queue = queue
217
+ self._state_manager = state_manager
218
+ self._max_length = max_message_length
219
+
220
+ def format_queue(self) -> str:
221
+ """
222
+ Format queued messages for display.
223
+
224
+ Returns:
225
+ Formatted string with queued messages
226
+ """
227
+ messages = self._queue.get_all()
228
+ if not messages:
229
+ return ""
230
+
231
+ lines = []
232
+ for msg in messages:
233
+ # Truncate long messages
234
+ display_msg = msg
235
+ if len(msg) > self._max_length:
236
+ display_msg = msg[:self._max_length - 3] + "..."
237
+ lines.append(f" ↳ {display_msg}")
238
+
239
+ return "\n".join(lines)
240
+
241
+ def format_status(self) -> str:
242
+ """
243
+ Format current processing status.
244
+
245
+ Returns:
246
+ Status string (e.g., "⏳ Processing..." or "")
247
+ """
248
+ if not self._state_manager:
249
+ return ""
250
+
251
+ state = self._state_manager.current_state
252
+ if state == ProcessingState.PROCESSING:
253
+ return "⏳ Processing..."
254
+ elif state == ProcessingState.WAITING_APPROVAL:
255
+ return "🔒 Waiting for approval..."
256
+ return ""
257
+
258
+ def format_queue_count(self) -> str:
259
+ """
260
+ Format queue count indicator.
261
+
262
+ Returns:
263
+ Count string (e.g., "📋 Queued (2)")
264
+ """
265
+ count = self._queue.count
266
+ if count == 0:
267
+ return ""
268
+ return f"📋 Queued ({count})"
269
+
270
+
271
+ # ============================================================================
272
+ # MessageQueueHandler - Main handler class
273
+ # ============================================================================
274
+
275
+ class MessageQueueHandler:
276
+ """
277
+ Main handler for message queue functionality.
278
+
279
+ Coordinates queue, state, and processing.
280
+ """
281
+
282
+ def __init__(
283
+ self,
284
+ processor: Optional[Callable[[str], str]] = None,
285
+ on_response: Optional[Callable[[str], None]] = None
286
+ ):
287
+ self._processor = processor
288
+ self._on_response = on_response
289
+ self._queue = MessageQueue()
290
+ self._state_manager = StateManager(on_state_change=self._on_state_change)
291
+ self._processing_lock = threading.Lock()
292
+
293
+ @property
294
+ def queue(self) -> MessageQueue:
295
+ """Get the message queue."""
296
+ return self._queue
297
+
298
+ @property
299
+ def state_manager(self) -> StateManager:
300
+ """Get the state manager."""
301
+ return self._state_manager
302
+
303
+ def submit(self, message: str) -> bool:
304
+ """
305
+ Submit a message for processing.
306
+
307
+ If idle, processes immediately. If processing, queues the message.
308
+
309
+ Args:
310
+ message: The message to submit
311
+
312
+ Returns:
313
+ True if processed/queued, False if empty message
314
+ """
315
+ trimmed = message.strip() if message else ""
316
+ if not trimmed:
317
+ return False
318
+
319
+ if self._state_manager.is_idle:
320
+ # Process immediately
321
+ self._process_message(trimmed)
322
+ else:
323
+ # Queue for later
324
+ self._queue.add(trimmed)
325
+
326
+ return True
327
+
328
+ def on_processing_complete(self) -> None:
329
+ """
330
+ Called when current processing completes.
331
+
332
+ Sets state to idle and processes next queued message if any.
333
+ """
334
+ self._state_manager.set_state(ProcessingState.IDLE)
335
+ self._process_next_in_queue()
336
+
337
+ def get_status(self) -> dict:
338
+ """
339
+ Get current status.
340
+
341
+ Returns:
342
+ Dict with queue_count and state
343
+ """
344
+ return {
345
+ 'queue_count': self._queue.count,
346
+ 'state': self._state_manager.current_state.value,
347
+ 'messages': self._queue.get_all()
348
+ }
349
+
350
+ def clear_queue(self) -> None:
351
+ """Clear all queued messages."""
352
+ self._queue.clear()
353
+
354
+ def _process_message(self, message: str) -> None:
355
+ """Process a single message."""
356
+ if not self._processor:
357
+ return
358
+
359
+ with self._processing_lock:
360
+ self._state_manager.set_state(ProcessingState.PROCESSING)
361
+ try:
362
+ response = self._processor(message)
363
+ if self._on_response and response:
364
+ self._on_response(response)
365
+ finally:
366
+ self._state_manager.set_state(ProcessingState.IDLE)
367
+
368
+ def _process_next_in_queue(self) -> None:
369
+ """Process the next message in queue if any."""
370
+ next_msg = self._queue.pop()
371
+ if next_msg:
372
+ self._process_message(next_msg)
373
+
374
+ def _on_state_change(self, old_state: ProcessingState, new_state: ProcessingState) -> None:
375
+ """Handle state changes."""
376
+ # Auto-process queue when becoming idle
377
+ if new_state == ProcessingState.IDLE and old_state == ProcessingState.PROCESSING:
378
+ # Don't auto-process here to avoid recursion
379
+ # The on_processing_complete method handles this
380
+ pass
381
+
382
+
383
+ # ============================================================================
384
+ # AsyncProcessor - Background processing
385
+ # ============================================================================
386
+
387
+ class AsyncProcessor:
388
+ """
389
+ Runs work functions in background threads.
390
+
391
+ Allows agent processing to happen asynchronously while
392
+ the main thread remains responsive for user input.
393
+ """
394
+
395
+ def __init__(
396
+ self,
397
+ on_complete: Optional[Callable[[str], None]] = None,
398
+ on_status: Optional[Callable[[str], None]] = None,
399
+ on_error: Optional[Callable[[Exception], None]] = None
400
+ ):
401
+ self._on_complete = on_complete
402
+ self._on_status = on_status
403
+ self._on_error = on_error
404
+ self._thread: Optional[threading.Thread] = None
405
+ self._running = False
406
+ self._lock = threading.Lock()
407
+
408
+ @property
409
+ def is_running(self) -> bool:
410
+ """Check if currently processing."""
411
+ with self._lock:
412
+ return self._running
413
+
414
+ def start(self, work_fn: Callable[[], str]) -> None:
415
+ """
416
+ Start processing work in background thread.
417
+
418
+ Args:
419
+ work_fn: Function to execute (should return result string)
420
+ """
421
+ def _worker():
422
+ try:
423
+ with self._lock:
424
+ self._running = True
425
+
426
+ result = work_fn()
427
+
428
+ with self._lock:
429
+ self._running = False
430
+
431
+ if self._on_complete:
432
+ self._on_complete(result)
433
+ except Exception as e:
434
+ with self._lock:
435
+ self._running = False
436
+ if self._on_error:
437
+ self._on_error(e)
438
+
439
+ self._thread = threading.Thread(target=_worker, daemon=True)
440
+ self._thread.start()
441
+
442
+ def update_status(self, status: str) -> None:
443
+ """Update status (can be called from work function)."""
444
+ if self._on_status:
445
+ self._on_status(status)
446
+
447
+
448
+ # ============================================================================
449
+ # LiveStatusDisplay - Real-time status display
450
+ # ============================================================================
451
+
452
+ class LiveStatusDisplay:
453
+ """
454
+ Tracks and displays real-time status during processing.
455
+
456
+ Shows tool calls, command executions, and current status.
457
+ """
458
+
459
+ def __init__(self):
460
+ self._status = ""
461
+ self._tool_calls: List[dict] = []
462
+ self._commands: List[str] = []
463
+ self._lock = threading.Lock()
464
+
465
+ @property
466
+ def current_status(self) -> str:
467
+ """Get current status message."""
468
+ with self._lock:
469
+ return self._status
470
+
471
+ @property
472
+ def tool_calls(self) -> List[dict]:
473
+ """Get list of tool calls."""
474
+ with self._lock:
475
+ return self._tool_calls.copy()
476
+
477
+ @property
478
+ def commands(self) -> List[str]:
479
+ """Get list of commands executed."""
480
+ with self._lock:
481
+ return self._commands.copy()
482
+
483
+ def update_status(self, status: str) -> None:
484
+ """Update current status message."""
485
+ with self._lock:
486
+ self._status = status
487
+
488
+ def add_tool_call(self, name: str, args: dict) -> None:
489
+ """Add a tool call to the display."""
490
+ with self._lock:
491
+ self._tool_calls.append({
492
+ 'name': name,
493
+ 'args': args,
494
+ 'timestamp': time.time()
495
+ })
496
+
497
+ def add_command(self, command: str) -> None:
498
+ """Add a command execution to the display."""
499
+ with self._lock:
500
+ self._commands.append(command)
501
+
502
+ def format(self) -> str:
503
+ """Format current status for display."""
504
+ lines = []
505
+
506
+ with self._lock:
507
+ if self._status:
508
+ lines.append(f"⏳ {self._status}")
509
+
510
+ for tool in self._tool_calls[-3:]: # Show last 3 tools
511
+ lines.append(f" 🔧 {tool['name']}")
512
+
513
+ for cmd in self._commands[-2:]: # Show last 2 commands
514
+ display_cmd = cmd[:40] + "..." if len(cmd) > 40 else cmd
515
+ lines.append(f" 💻 {display_cmd}")
516
+
517
+ return "\n".join(lines)
518
+
519
+ def clear(self) -> None:
520
+ """Clear all status."""
521
+ with self._lock:
522
+ self._status = ""
523
+ self._tool_calls.clear()
524
+ self._commands.clear()
525
+
526
+
527
+ # ============================================================================
528
+ # NonBlockingInput - Async input handling
529
+ # ============================================================================
530
+
531
+ class NonBlockingInput:
532
+ """
533
+ Handles user input asynchronously.
534
+
535
+ Allows users to type new messages while processing is ongoing.
536
+ """
537
+
538
+ def __init__(self):
539
+ self._queue = MessageQueue()
540
+
541
+ @property
542
+ def has_pending(self) -> bool:
543
+ """Check if there are pending inputs."""
544
+ return not self._queue.is_empty
545
+
546
+ @property
547
+ def pending_count(self) -> int:
548
+ """Get number of pending inputs."""
549
+ return self._queue.count
550
+
551
+ def submit(self, message: str) -> bool:
552
+ """Submit a new input message."""
553
+ return self._queue.add(message)
554
+
555
+ def pop(self) -> Optional[str]:
556
+ """Get next pending input."""
557
+ return self._queue.pop()
558
+
559
+ def peek(self) -> Optional[str]:
560
+ """View next pending input without removing."""
561
+ return self._queue.peek()
562
+
563
+ def get_all(self) -> List[str]:
564
+ """Get all pending inputs."""
565
+ return self._queue.get_all()
566
+
567
+ def clear(self) -> None:
568
+ """Clear all pending inputs."""
569
+ self._queue.clear()
570
+
571
+
572
+ # ============================================================================
573
+ # Exports
574
+ # ============================================================================
575
+
576
+ __all__ = [
577
+ 'ProcessingState',
578
+ 'MessagePriority',
579
+ 'QueuedMessage',
580
+ 'MessageQueue',
581
+ 'StateManager',
582
+ 'QueueDisplay',
583
+ 'MessageQueueHandler',
584
+ 'AsyncProcessor',
585
+ 'LiveStatusDisplay',
586
+ 'NonBlockingInput',
587
+ ]