jvagent 0.1.1__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 (624) hide show
  1. jvagent/__init__.py +35 -0
  2. jvagent/__main__.py +6 -0
  3. jvagent/_logging_compat.py +70 -0
  4. jvagent/_mimetypes_compat.py +53 -0
  5. jvagent/action/AGENTS.md +1 -0
  6. jvagent/action/CLAUDE.md +145 -0
  7. jvagent/action/README.md +34 -0
  8. jvagent/action/__init__.py +33 -0
  9. jvagent/action/access_control/README.md +230 -0
  10. jvagent/action/access_control/__init__.py +10 -0
  11. jvagent/action/access_control/access_control_action.py +452 -0
  12. jvagent/action/access_control/endpoints.py +602 -0
  13. jvagent/action/access_control/info.yaml +17 -0
  14. jvagent/action/actions.py +728 -0
  15. jvagent/action/agent_utils/README.md +3 -0
  16. jvagent/action/agent_utils/__init__.py +10 -0
  17. jvagent/action/agent_utils/agent_utils.py +112 -0
  18. jvagent/action/agent_utils/endpoints.py +59 -0
  19. jvagent/action/agent_utils/info.yaml +16 -0
  20. jvagent/action/avatar_action/README.md +20 -0
  21. jvagent/action/avatar_action/__init__.py +10 -0
  22. jvagent/action/avatar_action/avatar_action.py +228 -0
  23. jvagent/action/avatar_action/endpoints.py +275 -0
  24. jvagent/action/avatar_action/info.yaml +14 -0
  25. jvagent/action/base.py +1297 -0
  26. jvagent/action/channels/__init__.py +1 -0
  27. jvagent/action/channels/media.py +159 -0
  28. jvagent/action/code_execution/__init__.py +8 -0
  29. jvagent/action/code_execution/code_execution_action.py +203 -0
  30. jvagent/action/code_execution/executor.py +223 -0
  31. jvagent/action/code_execution/info.yaml +20 -0
  32. jvagent/action/email_action/README.md +35 -0
  33. jvagent/action/email_action/__init__.py +6 -0
  34. jvagent/action/email_action/canonical_send_builder.py +191 -0
  35. jvagent/action/email_action/email_action.py +685 -0
  36. jvagent/action/email_action/email_adapter.py +222 -0
  37. jvagent/action/email_action/email_filter.py +100 -0
  38. jvagent/action/email_action/email_payload.py +74 -0
  39. jvagent/action/email_action/email_utterance.py +68 -0
  40. jvagent/action/email_action/email_webhook_helpers.py +172 -0
  41. jvagent/action/email_action/endpoints.py +536 -0
  42. jvagent/action/email_action/gmail_inbox.py +153 -0
  43. jvagent/action/email_action/inbound/__init__.py +7 -0
  44. jvagent/action/email_action/inbound/gmail.py +172 -0
  45. jvagent/action/email_action/inbound/outlook.py +125 -0
  46. jvagent/action/email_action/inbound/sendgrid.py +181 -0
  47. jvagent/action/email_action/inbound/text_utils.py +11 -0
  48. jvagent/action/email_action/info.yaml +21 -0
  49. jvagent/action/email_action/modules/__init__.py +15 -0
  50. jvagent/action/email_action/modules/base.py +42 -0
  51. jvagent/action/email_action/modules/gmail.py +151 -0
  52. jvagent/action/email_action/modules/outlook.py +135 -0
  53. jvagent/action/email_action/modules/sendgrid.py +294 -0
  54. jvagent/action/email_action/outlook_inbox.py +149 -0
  55. jvagent/action/email_action/utils/__init__.py +1 -0
  56. jvagent/action/email_action/webhook_auth.py +12 -0
  57. jvagent/action/endpoints.py +587 -0
  58. jvagent/action/facebook_action/README.md +142 -0
  59. jvagent/action/facebook_action/__init__.py +9 -0
  60. jvagent/action/facebook_action/endpoints.py +803 -0
  61. jvagent/action/facebook_action/facebook_action.py +844 -0
  62. jvagent/action/facebook_action/facebook_api.py +807 -0
  63. jvagent/action/facebook_action/info.yaml +16 -0
  64. jvagent/action/facebook_action/messenger_adapter.py +121 -0
  65. jvagent/action/facebook_action/messenger_filter.py +27 -0
  66. jvagent/action/facebook_action/messenger_message_coalescer.py +161 -0
  67. jvagent/action/facebook_action/messenger_voice_filter.py +58 -0
  68. jvagent/action/facebook_action/messenger_webhook_helpers.py +551 -0
  69. jvagent/action/facebook_action/webhook_auth.py +12 -0
  70. jvagent/action/file_interface/__init__.py +5 -0
  71. jvagent/action/file_interface/_core.py +271 -0
  72. jvagent/action/file_interface/file_interface_action.py +187 -0
  73. jvagent/action/file_interface/info.yaml +19 -0
  74. jvagent/action/google/__init__.py +5 -0
  75. jvagent/action/google/endpoints.py +385 -0
  76. jvagent/action/google/google_action.py +382 -0
  77. jvagent/action/google/google_calendar_action/README.md +86 -0
  78. jvagent/action/google/google_calendar_action/__init__.py +6 -0
  79. jvagent/action/google/google_calendar_action/endpoints.py +237 -0
  80. jvagent/action/google/google_calendar_action/google_calendar_action.py +121 -0
  81. jvagent/action/google/google_calendar_action/info.yaml +22 -0
  82. jvagent/action/google/google_docs_action/__init__.py +6 -0
  83. jvagent/action/google/google_docs_action/endpoints.py +135 -0
  84. jvagent/action/google/google_docs_action/google_docs_action.py +330 -0
  85. jvagent/action/google/google_docs_action/info.yaml +24 -0
  86. jvagent/action/google/google_drive_action/README.md +240 -0
  87. jvagent/action/google/google_drive_action/__init__.py +6 -0
  88. jvagent/action/google/google_drive_action/endpoints.py +408 -0
  89. jvagent/action/google/google_drive_action/google_drive_action.py +421 -0
  90. jvagent/action/google/google_drive_action/info.yaml +24 -0
  91. jvagent/action/google/google_gmail_action/README.md +64 -0
  92. jvagent/action/google/google_gmail_action/__init__.py +6 -0
  93. jvagent/action/google/google_gmail_action/endpoints.py +170 -0
  94. jvagent/action/google/google_gmail_action/google_gmail_action.py +138 -0
  95. jvagent/action/google/google_gmail_action/info.yaml +23 -0
  96. jvagent/action/google/google_sheets_action/README.md +88 -0
  97. jvagent/action/google/google_sheets_action/__init__.py +6 -0
  98. jvagent/action/google/google_sheets_action/endpoints.py +1098 -0
  99. jvagent/action/google/google_sheets_action/google_sheets_action.py +1264 -0
  100. jvagent/action/google/google_sheets_action/info.yaml +22 -0
  101. jvagent/action/google/google_token.py +61 -0
  102. jvagent/action/handoff_interact_action/README.md +13 -0
  103. jvagent/action/handoff_interact_action/__init__.py +5 -0
  104. jvagent/action/handoff_interact_action/handoff_interact_action.py +454 -0
  105. jvagent/action/handoff_interact_action/info.yaml +37 -0
  106. jvagent/action/interact/AGENTS.md +1 -0
  107. jvagent/action/interact/CLAUDE.md +139 -0
  108. jvagent/action/interact/README.md +453 -0
  109. jvagent/action/interact/__init__.py +12 -0
  110. jvagent/action/interact/base.py +543 -0
  111. jvagent/action/interact/conversation_lock_manager.py +51 -0
  112. jvagent/action/interact/endpoints.py +929 -0
  113. jvagent/action/interact/interact_walker.py +1013 -0
  114. jvagent/action/interact/rate_limiter.py +328 -0
  115. jvagent/action/interact/response_builder.py +233 -0
  116. jvagent/action/interact/session_token.py +406 -0
  117. jvagent/action/interact/utils/__init__.py +1 -0
  118. jvagent/action/interact/utils/uploads.py +225 -0
  119. jvagent/action/interact/webhook_pipeline.py +394 -0
  120. jvagent/action/interview/AGENTS.md +1 -0
  121. jvagent/action/interview/CLAUDE.md +119 -0
  122. jvagent/action/interview/README.md +507 -0
  123. jvagent/action/interview/SKILL.md +100 -0
  124. jvagent/action/interview/__init__.py +24 -0
  125. jvagent/action/interview/_validate_contract.py +65 -0
  126. jvagent/action/interview/activation_seed.py +146 -0
  127. jvagent/action/interview/directive_compose.py +175 -0
  128. jvagent/action/interview/docs/extending.md +553 -0
  129. jvagent/action/interview/docs/frontmatter-schema.md +165 -0
  130. jvagent/action/interview/docs/multi-turn-flow.md +239 -0
  131. jvagent/action/interview/docs/skill_custom_instructions.md +102 -0
  132. jvagent/action/interview/docs/thin-harness.md +92 -0
  133. jvagent/action/interview/docs/troubleshooting.md +204 -0
  134. jvagent/action/interview/engine.py +1852 -0
  135. jvagent/action/interview/examples/example_account_gating/README.md +57 -0
  136. jvagent/action/interview/examples/example_account_gating/example_booking_interview/SKILL.md +65 -0
  137. jvagent/action/interview/examples/example_account_gating/example_signin_interview/SKILL.md +37 -0
  138. jvagent/action/interview/examples/example_account_gating/use-cases/booking-gated-no-session.yaml +34 -0
  139. jvagent/action/interview/examples/example_account_gating/use-cases/booking-with-session.yaml +32 -0
  140. jvagent/action/interview/examples/example_for_each_interview/SKILL.md +57 -0
  141. jvagent/action/interview/examples/example_for_each_interview/scripts/custom_tools.py +59 -0
  142. jvagent/action/interview/examples/example_interview/SKILL.md +95 -0
  143. jvagent/action/interview/examples/example_interview/scripts/custom_tools.py +331 -0
  144. jvagent/action/interview/flow.py +477 -0
  145. jvagent/action/interview/for_each.py +398 -0
  146. jvagent/action/interview/hooks.py +709 -0
  147. jvagent/action/interview/info.yaml +23 -0
  148. jvagent/action/interview/interview_action.py +372 -0
  149. jvagent/action/interview/procedure.py +33 -0
  150. jvagent/action/interview/session.py +168 -0
  151. jvagent/action/interview/spec.py +403 -0
  152. jvagent/action/interview/tasks.py +133 -0
  153. jvagent/action/interview/tools.py +220 -0
  154. jvagent/action/interview/validators.py +246 -0
  155. jvagent/action/intro/README.md +62 -0
  156. jvagent/action/intro/__init__.py +5 -0
  157. jvagent/action/intro/info.yaml +26 -0
  158. jvagent/action/intro/intro_interact_action.py +132 -0
  159. jvagent/action/leadgen/README.md +42 -0
  160. jvagent/action/leadgen/SKILL.md +58 -0
  161. jvagent/action/leadgen/__init__.py +12 -0
  162. jvagent/action/leadgen/_validate_contract.py +46 -0
  163. jvagent/action/leadgen/docs/extending.md +41 -0
  164. jvagent/action/leadgen/docs/frontmatter-schema.md +104 -0
  165. jvagent/action/leadgen/docs/thin-harness.md +25 -0
  166. jvagent/action/leadgen/docs/troubleshooting.md +12 -0
  167. jvagent/action/leadgen/engine.py +413 -0
  168. jvagent/action/leadgen/examples/example_leadgen/SKILL.md +80 -0
  169. jvagent/action/leadgen/examples/example_leadgen/scripts/custom_tools.py +21 -0
  170. jvagent/action/leadgen/hooks.py +97 -0
  171. jvagent/action/leadgen/info.yaml +15 -0
  172. jvagent/action/leadgen/leadgen_action.py +148 -0
  173. jvagent/action/leadgen/procedure.py +21 -0
  174. jvagent/action/leadgen/spec.py +240 -0
  175. jvagent/action/leadgen/store.py +255 -0
  176. jvagent/action/leadgen/sync.py +166 -0
  177. jvagent/action/leadgen/tools.py +178 -0
  178. jvagent/action/leadgen/validators.py +97 -0
  179. jvagent/action/loader/__init__.py +14 -0
  180. jvagent/action/loader/action_loader.py +1300 -0
  181. jvagent/action/loader/core_discovery.py +129 -0
  182. jvagent/action/loader/factory.py +44 -0
  183. jvagent/action/loader/importer.py +153 -0
  184. jvagent/action/loader/info_yaml.py +46 -0
  185. jvagent/action/loader/metadata.py +128 -0
  186. jvagent/action/loader/module_loading.py +167 -0
  187. jvagent/action/manifest.py +298 -0
  188. jvagent/action/mcp/README.md +142 -0
  189. jvagent/action/mcp/__init__.py +6 -0
  190. jvagent/action/mcp/client.py +215 -0
  191. jvagent/action/mcp/fs_server_factory.py +112 -0
  192. jvagent/action/mcp/info.yaml +20 -0
  193. jvagent/action/mcp/jvspatial_fs_server.py +308 -0
  194. jvagent/action/mcp/mcp_action.py +878 -0
  195. jvagent/action/mcp/prompts.py +35 -0
  196. jvagent/action/mcp/result.py +25 -0
  197. jvagent/action/microsoft/README.md +111 -0
  198. jvagent/action/microsoft/__init__.py +3 -0
  199. jvagent/action/microsoft/endpoints.py +333 -0
  200. jvagent/action/microsoft/microsoft_action.py +347 -0
  201. jvagent/action/microsoft/microsoft_excel_action/README.md +58 -0
  202. jvagent/action/microsoft/microsoft_excel_action/__init__.py +4 -0
  203. jvagent/action/microsoft/microsoft_excel_action/endpoints.py +187 -0
  204. jvagent/action/microsoft/microsoft_excel_action/info.yaml +18 -0
  205. jvagent/action/microsoft/microsoft_excel_action/microsoft_excel_action.py +583 -0
  206. jvagent/action/microsoft/microsoft_onedrive_action/README.md +44 -0
  207. jvagent/action/microsoft/microsoft_onedrive_action/__init__.py +4 -0
  208. jvagent/action/microsoft/microsoft_onedrive_action/endpoints.py +260 -0
  209. jvagent/action/microsoft/microsoft_onedrive_action/info.yaml +17 -0
  210. jvagent/action/microsoft/microsoft_onedrive_action/microsoft_onedrive_action.py +293 -0
  211. jvagent/action/microsoft/microsoft_outlook_calendar_action/README.md +38 -0
  212. jvagent/action/microsoft/microsoft_outlook_calendar_action/__init__.py +4 -0
  213. jvagent/action/microsoft/microsoft_outlook_calendar_action/endpoints.py +168 -0
  214. jvagent/action/microsoft/microsoft_outlook_calendar_action/info.yaml +17 -0
  215. jvagent/action/microsoft/microsoft_outlook_calendar_action/microsoft_outlook_calendar_action.py +163 -0
  216. jvagent/action/microsoft/microsoft_outlook_mail_action/README.md +43 -0
  217. jvagent/action/microsoft/microsoft_outlook_mail_action/__init__.py +4 -0
  218. jvagent/action/microsoft/microsoft_outlook_mail_action/endpoints.py +161 -0
  219. jvagent/action/microsoft/microsoft_outlook_mail_action/info.yaml +17 -0
  220. jvagent/action/microsoft/microsoft_outlook_mail_action/microsoft_outlook_mail_action.py +253 -0
  221. jvagent/action/microsoft/microsoft_token.py +60 -0
  222. jvagent/action/model/README.md +783 -0
  223. jvagent/action/model/__init__.py +72 -0
  224. jvagent/action/model/base.py +482 -0
  225. jvagent/action/model/context.py +259 -0
  226. jvagent/action/model/cost_estimator.py +108 -0
  227. jvagent/action/model/embedding/__init__.py +24 -0
  228. jvagent/action/model/embedding/base.py +152 -0
  229. jvagent/action/model/embedding/endpoints.py +459 -0
  230. jvagent/action/model/embedding/generic/__init__.py +5 -0
  231. jvagent/action/model/embedding/generic/generic.py +191 -0
  232. jvagent/action/model/embedding/generic/info.yaml +34 -0
  233. jvagent/action/model/embedding/huggingface/__init__.py +5 -0
  234. jvagent/action/model/embedding/huggingface/huggingface.py +132 -0
  235. jvagent/action/model/embedding/huggingface/info.yaml +34 -0
  236. jvagent/action/model/embedding/ollama/__init__.py +5 -0
  237. jvagent/action/model/embedding/ollama/info.yaml +22 -0
  238. jvagent/action/model/embedding/ollama/ollama.py +117 -0
  239. jvagent/action/model/embedding/openai/__init__.py +5 -0
  240. jvagent/action/model/embedding/openai/info.yaml +35 -0
  241. jvagent/action/model/embedding/openai/openai.py +180 -0
  242. jvagent/action/model/embedding/openrouter/__init__.py +5 -0
  243. jvagent/action/model/embedding/openrouter/info.yaml +34 -0
  244. jvagent/action/model/embedding/openrouter/openrouter.py +136 -0
  245. jvagent/action/model/language/__init__.py +39 -0
  246. jvagent/action/model/language/anthropic/__init__.py +5 -0
  247. jvagent/action/model/language/anthropic/anthropic.py +625 -0
  248. jvagent/action/model/language/anthropic/info.yaml +22 -0
  249. jvagent/action/model/language/base.py +1238 -0
  250. jvagent/action/model/language/endpoints.py +498 -0
  251. jvagent/action/model/language/groq/__init__.py +5 -0
  252. jvagent/action/model/language/groq/groq.py +100 -0
  253. jvagent/action/model/language/groq/info.yaml +37 -0
  254. jvagent/action/model/language/ollama/__init__.py +5 -0
  255. jvagent/action/model/language/ollama/info.yaml +22 -0
  256. jvagent/action/model/language/ollama/ollama.py +475 -0
  257. jvagent/action/model/language/openai/__init__.py +5 -0
  258. jvagent/action/model/language/openai/info.yaml +35 -0
  259. jvagent/action/model/language/openai/openai.py +702 -0
  260. jvagent/action/model/language/openrouter/__init__.py +5 -0
  261. jvagent/action/model/language/openrouter/info.yaml +34 -0
  262. jvagent/action/model/language/openrouter/openrouter.py +222 -0
  263. jvagent/action/model/language/templates.py +255 -0
  264. jvagent/action/model/language/tools.py +405 -0
  265. jvagent/action/model/ollama_endpoint.py +15 -0
  266. jvagent/action/model/utils/__init__.py +1 -0
  267. jvagent/action/model/utils/message_format.py +87 -0
  268. jvagent/action/model/utils/token_estimation.py +201 -0
  269. jvagent/action/oauth/__init__.py +29 -0
  270. jvagent/action/oauth/audit.py +92 -0
  271. jvagent/action/oauth/state.py +162 -0
  272. jvagent/action/oauth/token_crypto.py +219 -0
  273. jvagent/action/orchestrator/__init__.py +21 -0
  274. jvagent/action/orchestrator/access.py +66 -0
  275. jvagent/action/orchestrator/catalog.py +236 -0
  276. jvagent/action/orchestrator/constants.py +37 -0
  277. jvagent/action/orchestrator/continuation.py +326 -0
  278. jvagent/action/orchestrator/core_tools.py +298 -0
  279. jvagent/action/orchestrator/egress.py +127 -0
  280. jvagent/action/orchestrator/info.yaml +33 -0
  281. jvagent/action/orchestrator/loop.py +867 -0
  282. jvagent/action/orchestrator/loop_helpers.py +16 -0
  283. jvagent/action/orchestrator/orchestrator_interact_action.py +2790 -0
  284. jvagent/action/orchestrator/preconditions.py +74 -0
  285. jvagent/action/orchestrator/proactive_tools.py +73 -0
  286. jvagent/action/orchestrator/prompts.py +252 -0
  287. jvagent/action/orchestrator/skill_providers.py +59 -0
  288. jvagent/action/orchestrator/skill_tasks.py +1011 -0
  289. jvagent/action/orchestrator/skills.py +204 -0
  290. jvagent/action/orchestrator/task_runners.py +95 -0
  291. jvagent/action/orchestrator/tools.py +164 -0
  292. jvagent/action/orchestrator/uploads.py +291 -0
  293. jvagent/action/orchestrator/walk_path.py +48 -0
  294. jvagent/action/pageindex/README.md +228 -0
  295. jvagent/action/pageindex/__init__.py +50 -0
  296. jvagent/action/pageindex/adapter.py +265 -0
  297. jvagent/action/pageindex/config.py +439 -0
  298. jvagent/action/pageindex/core/README.md +16 -0
  299. jvagent/action/pageindex/core/__init__.py +2 -0
  300. jvagent/action/pageindex/core/config.yaml +9 -0
  301. jvagent/action/pageindex/core/page_index.py +1389 -0
  302. jvagent/action/pageindex/core/page_index_md.py +419 -0
  303. jvagent/action/pageindex/core/utils.py +722 -0
  304. jvagent/action/pageindex/docling_convert.py +252 -0
  305. jvagent/action/pageindex/document_walker.py +105 -0
  306. jvagent/action/pageindex/documents.py +1307 -0
  307. jvagent/action/pageindex/endpoints.py +2241 -0
  308. jvagent/action/pageindex/jvforge_assimilate.py +363 -0
  309. jvagent/action/pageindex/jvforge_routing.py +34 -0
  310. jvagent/action/pageindex/lexical_index.py +326 -0
  311. jvagent/action/pageindex/llm_bridge.py +148 -0
  312. jvagent/action/pageindex/llm_override.py +59 -0
  313. jvagent/action/pageindex/markdown_pages.py +108 -0
  314. jvagent/action/pageindex/md_tree_enriched.py +667 -0
  315. jvagent/action/pageindex/models.py +200 -0
  316. jvagent/action/pageindex/pageindex_action/__init__.py +13 -0
  317. jvagent/action/pageindex/pageindex_action/info.yaml +30 -0
  318. jvagent/action/pageindex/pageindex_action/pageindex_action.py +949 -0
  319. jvagent/action/pageindex/pageindex_action/runtime_config.py +133 -0
  320. jvagent/action/pageindex/pageindex_google_drive_sync_action/README.md +195 -0
  321. jvagent/action/pageindex/pageindex_google_drive_sync_action/__init__.py +21 -0
  322. jvagent/action/pageindex/pageindex_google_drive_sync_action/drive_ingest_filter.py +55 -0
  323. jvagent/action/pageindex/pageindex_google_drive_sync_action/endpoints.py +693 -0
  324. jvagent/action/pageindex/pageindex_google_drive_sync_action/google_drive_documents.py +52 -0
  325. jvagent/action/pageindex/pageindex_google_drive_sync_action/info.yaml +30 -0
  326. jvagent/action/pageindex/pageindex_google_drive_sync_action/pageindex_google_drive_sync_action.py +1629 -0
  327. jvagent/action/pageindex/pageindex_google_drive_sync_action/webhook_auth.py +12 -0
  328. jvagent/action/pageindex/prompts.py +39 -0
  329. jvagent/action/pageindex/ranking.py +70 -0
  330. jvagent/action/pageindex/retrieval.py +888 -0
  331. jvagent/action/pageindex/tokenizer.py +127 -0
  332. jvagent/action/pageindex/url_guard.py +133 -0
  333. jvagent/action/pageindex/webhook_auth.py +24 -0
  334. jvagent/action/parameters.py +356 -0
  335. jvagent/action/plugin_contracts.py +32 -0
  336. jvagent/action/postiz_action/README.md +72 -0
  337. jvagent/action/postiz_action/__init__.py +4 -0
  338. jvagent/action/postiz_action/endpoints.py +74 -0
  339. jvagent/action/postiz_action/info.yaml +17 -0
  340. jvagent/action/postiz_action/postiz_action.py +285 -0
  341. jvagent/action/reply/__init__.py +16 -0
  342. jvagent/action/reply/endpoints.py +202 -0
  343. jvagent/action/reply/info.yaml +19 -0
  344. jvagent/action/reply/reply_action.py +803 -0
  345. jvagent/action/response/README.md +664 -0
  346. jvagent/action/response/__init__.py +28 -0
  347. jvagent/action/response/channel_adapter.py +105 -0
  348. jvagent/action/response/channel_filter.py +121 -0
  349. jvagent/action/response/chunking.py +153 -0
  350. jvagent/action/response/message.py +127 -0
  351. jvagent/action/response/response_bus.py +1066 -0
  352. jvagent/action/response/streaming.py +120 -0
  353. jvagent/action/response/thought_formatting.py +44 -0
  354. jvagent/action/sentdm_broadcast/README.md +193 -0
  355. jvagent/action/sentdm_broadcast/__init__.py +11 -0
  356. jvagent/action/sentdm_broadcast/broadcast_tester.py +843 -0
  357. jvagent/action/sentdm_broadcast/docs/openapi.json +3159 -0
  358. jvagent/action/sentdm_broadcast/docs/openapi.yaml +2691 -0
  359. jvagent/action/sentdm_broadcast/docs/postman.json +2074 -0
  360. jvagent/action/sentdm_broadcast/endpoints.py +551 -0
  361. jvagent/action/sentdm_broadcast/info.yaml +17 -0
  362. jvagent/action/sentdm_broadcast/models.py +135 -0
  363. jvagent/action/sentdm_broadcast/sentdm_broadcast_action.py +1545 -0
  364. jvagent/action/sentdm_broadcast/webhook_auth.py +12 -0
  365. jvagent/action/sentdm_broadcast/webhook_debug.py +62 -0
  366. jvagent/action/skill_hub/__init__.py +5 -0
  367. jvagent/action/skill_hub/_installer.py +294 -0
  368. jvagent/action/skill_hub/_skills_cli.py +268 -0
  369. jvagent/action/skill_hub/info.yaml +22 -0
  370. jvagent/action/skill_hub/skill_hub_action.py +484 -0
  371. jvagent/action/skill_spec/__init__.py +39 -0
  372. jvagent/action/skill_spec/base.py +129 -0
  373. jvagent/action/skill_spec/contract.py +133 -0
  374. jvagent/action/skill_spec/docs/thin-harness.md +20 -0
  375. jvagent/action/skill_spec/registry.py +68 -0
  376. jvagent/action/skill_spec/task_lock.py +15 -0
  377. jvagent/action/spreadsheet/__init__.py +1 -0
  378. jvagent/action/spreadsheet/range_utils.py +45 -0
  379. jvagent/action/streaming.py +18 -0
  380. jvagent/action/stt_action/README.md +106 -0
  381. jvagent/action/stt_action/__init__.py +12 -0
  382. jvagent/action/stt_action/base.py +87 -0
  383. jvagent/action/stt_action/deepgram/__init__.py +3 -0
  384. jvagent/action/stt_action/deepgram/deepgram.py +229 -0
  385. jvagent/action/stt_action/deepgram/info.yaml +17 -0
  386. jvagent/action/stt_action/endpoints.py +108 -0
  387. jvagent/action/task_creation_interact_action/__init__.py +7 -0
  388. jvagent/action/task_creation_interact_action/info.yaml +17 -0
  389. jvagent/action/task_creation_interact_action/prompts.py +55 -0
  390. jvagent/action/task_creation_interact_action/task_creation_interact_action.py +326 -0
  391. jvagent/action/task_monitor/__init__.py +6 -0
  392. jvagent/action/task_monitor/endpoints.py +120 -0
  393. jvagent/action/task_monitor/finalize.py +117 -0
  394. jvagent/action/task_monitor/info.yaml +16 -0
  395. jvagent/action/task_monitor/task_monitor.py +464 -0
  396. jvagent/action/task_trigger_interact_action/__init__.py +7 -0
  397. jvagent/action/task_trigger_interact_action/info.yaml +17 -0
  398. jvagent/action/task_trigger_interact_action/task_trigger_interact_action.py +84 -0
  399. jvagent/action/tts_action/README.md +130 -0
  400. jvagent/action/tts_action/__init__.py +12 -0
  401. jvagent/action/tts_action/base.py +80 -0
  402. jvagent/action/tts_action/elevenlabs/__init__.py +3 -0
  403. jvagent/action/tts_action/elevenlabs/elevenlabs.py +215 -0
  404. jvagent/action/tts_action/elevenlabs/info.yaml +17 -0
  405. jvagent/action/tts_action/endpoints.py +178 -0
  406. jvagent/action/utils/__init__.py +17 -0
  407. jvagent/action/utils/endpoint_helpers.py +31 -0
  408. jvagent/action/utils/meta_webhook.py +23 -0
  409. jvagent/action/utils/webhook_reconcile.py +121 -0
  410. jvagent/action/utils/webhook_system_user.py +68 -0
  411. jvagent/action/vectorstore/__init__.py +18 -0
  412. jvagent/action/vectorstore/base.py +378 -0
  413. jvagent/action/vectorstore/endpoints.py +466 -0
  414. jvagent/action/vectorstore/typesense/__init__.py +5 -0
  415. jvagent/action/vectorstore/typesense/info.yaml +34 -0
  416. jvagent/action/vectorstore/typesense/typesense.py +703 -0
  417. jvagent/action/video_generation/README.md +37 -0
  418. jvagent/action/video_generation/__init__.py +1 -0
  419. jvagent/action/video_generation/heygen.py +558 -0
  420. jvagent/action/video_generation/info.yaml +17 -0
  421. jvagent/action/vision/__init__.py +5 -0
  422. jvagent/action/vision/info.yaml +23 -0
  423. jvagent/action/vision/multimodal.py +138 -0
  424. jvagent/action/vision/prompts.py +11 -0
  425. jvagent/action/vision/vision_action.py +144 -0
  426. jvagent/action/web_fetch/__init__.py +5 -0
  427. jvagent/action/web_fetch/info.yaml +22 -0
  428. jvagent/action/web_fetch/web_fetch_action.py +202 -0
  429. jvagent/action/web_search/README.md +133 -0
  430. jvagent/action/web_search/__init__.py +20 -0
  431. jvagent/action/web_search/base.py +62 -0
  432. jvagent/action/web_search/brave/__init__.py +3 -0
  433. jvagent/action/web_search/brave/brave.py +131 -0
  434. jvagent/action/web_search/brave/info.yaml +17 -0
  435. jvagent/action/web_search/prompts.py +8 -0
  436. jvagent/action/web_search/serpapi/__init__.py +3 -0
  437. jvagent/action/web_search/serpapi/info.yaml +18 -0
  438. jvagent/action/web_search/serpapi/serpapi.py +116 -0
  439. jvagent/action/web_search/serper/__init__.py +3 -0
  440. jvagent/action/web_search/serper/info.yaml +16 -0
  441. jvagent/action/web_search/serper/serper.py +134 -0
  442. jvagent/action/whatsapp/README.md +698 -0
  443. jvagent/action/whatsapp/__init__.py +13 -0
  444. jvagent/action/whatsapp/endpoints.py +1378 -0
  445. jvagent/action/whatsapp/info.yaml +19 -0
  446. jvagent/action/whatsapp/modules/base.py +643 -0
  447. jvagent/action/whatsapp/modules/meta_api.py +907 -0
  448. jvagent/action/whatsapp/modules/registry.py +41 -0
  449. jvagent/action/whatsapp/modules/ultramsg.py +592 -0
  450. jvagent/action/whatsapp/modules/wppconnect.py +421 -0
  451. jvagent/action/whatsapp/modules/wwebjs_api.py +965 -0
  452. jvagent/action/whatsapp/utils/__init__.py +1 -0
  453. jvagent/action/whatsapp/utils/conversation_lock_manager.py +5 -0
  454. jvagent/action/whatsapp/utils/endpoint_helpers.py +652 -0
  455. jvagent/action/whatsapp/utils/media_batch_manager.py +756 -0
  456. jvagent/action/whatsapp/utils/meta_audio.py +59 -0
  457. jvagent/action/whatsapp/utils/meta_verify_token.py +22 -0
  458. jvagent/action/whatsapp/utils/meta_webhook_dedup.py +65 -0
  459. jvagent/action/whatsapp/utils/meta_webhook_verify.py +153 -0
  460. jvagent/action/whatsapp/utils/typing_state_manager.py +49 -0
  461. jvagent/action/whatsapp/webhook_auth.py +12 -0
  462. jvagent/action/whatsapp/whatsapp_action.py +1122 -0
  463. jvagent/action/whatsapp/whatsapp_adapter.py +300 -0
  464. jvagent/action/whatsapp/whatsapp_filter.py +61 -0
  465. jvagent/action/whatsapp/whatsapp_voice_filter.py +68 -0
  466. jvagent/bundle/README.md +176 -0
  467. jvagent/bundle/__init__.py +5 -0
  468. jvagent/bundle/bundler.py +74 -0
  469. jvagent/bundle/dockerfile_generator.py +196 -0
  470. jvagent/cli/AGENTS.md +1 -0
  471. jvagent/cli/CLAUDE.md +138 -0
  472. jvagent/cli/__init__.py +5 -0
  473. jvagent/cli/__main__.py +6 -0
  474. jvagent/cli/agent_commands.py +363 -0
  475. jvagent/cli/app_commands.py +220 -0
  476. jvagent/cli/bootstrap.py +244 -0
  477. jvagent/cli/chat.py +77 -0
  478. jvagent/cli/commands.py +40 -0
  479. jvagent/cli/main.py +249 -0
  480. jvagent/cli/server.py +616 -0
  481. jvagent/cli/server_config.py +618 -0
  482. jvagent/cli/skill_commands.py +540 -0
  483. jvagent/cli/validate.py +184 -0
  484. jvagent/core/AGENTS.md +1 -0
  485. jvagent/core/CLAUDE.md +123 -0
  486. jvagent/core/__init__.py +40 -0
  487. jvagent/core/agent.py +444 -0
  488. jvagent/core/agent_loader.py +761 -0
  489. jvagent/core/agent_yaml_validator.py +163 -0
  490. jvagent/core/agents.py +302 -0
  491. jvagent/core/app.py +599 -0
  492. jvagent/core/app_context.py +39 -0
  493. jvagent/core/app_loader.py +569 -0
  494. jvagent/core/app_yaml_validator.py +323 -0
  495. jvagent/core/bootstrap_logger.py +82 -0
  496. jvagent/core/bootstrap_update_mode.py +77 -0
  497. jvagent/core/cache.py +398 -0
  498. jvagent/core/callback.py +380 -0
  499. jvagent/core/channel.py +27 -0
  500. jvagent/core/config.py +631 -0
  501. jvagent/core/dependency_installer.py +212 -0
  502. jvagent/core/embed_endpoints.py +42 -0
  503. jvagent/core/endpoints/__init__.py +54 -0
  504. jvagent/core/endpoints/agents.py +263 -0
  505. jvagent/core/endpoints/app.py +56 -0
  506. jvagent/core/endpoints/conversation.py +107 -0
  507. jvagent/core/endpoints/graph_repair.py +262 -0
  508. jvagent/core/endpoints/status.py +30 -0
  509. jvagent/core/env_resolver.py +119 -0
  510. jvagent/core/errors.py +190 -0
  511. jvagent/core/graph_repair.py +524 -0
  512. jvagent/core/graph_repair_handlers.py +339 -0
  513. jvagent/core/graph_repair_job.py +1469 -0
  514. jvagent/core/index_bootstrap.py +179 -0
  515. jvagent/core/jvspatial_compat.py +50 -0
  516. jvagent/core/observability.py +40 -0
  517. jvagent/core/profiling.py +382 -0
  518. jvagent/core/public_url.py +9 -0
  519. jvagent/core/repair_phases/__init__.py +27 -0
  520. jvagent/core/repair_phases/engine.py +5 -0
  521. jvagent/core/repair_phases/memory.py +205 -0
  522. jvagent/core/repair_phases/types.py +43 -0
  523. jvagent/core/repair_scratch.py +201 -0
  524. jvagent/core/repair_state.py +388 -0
  525. jvagent/core/sandbox.py +271 -0
  526. jvagent/core/scheduler_bootstrap.py +169 -0
  527. jvagent/core/startup.py +172 -0
  528. jvagent/core/yaml_validation_utils.py +59 -0
  529. jvagent/embed/__init__.py +66 -0
  530. jvagent/embed/bootstrap.py +295 -0
  531. jvagent/embed/interact.py +793 -0
  532. jvagent/env.py +49 -0
  533. jvagent/logging/AGENTS.md +1 -0
  534. jvagent/logging/CLAUDE.md +96 -0
  535. jvagent/logging/__init__.py +21 -0
  536. jvagent/logging/endpoints.py +113 -0
  537. jvagent/logging/service.py +20 -0
  538. jvagent/memory/AGENTS.md +1 -0
  539. jvagent/memory/CLAUDE.md +130 -0
  540. jvagent/memory/README.md +92 -0
  541. jvagent/memory/__init__.py +27 -0
  542. jvagent/memory/artifact.py +98 -0
  543. jvagent/memory/conversation.py +1231 -0
  544. jvagent/memory/distributed_conversation_lock.py +306 -0
  545. jvagent/memory/endpoints.py +547 -0
  546. jvagent/memory/evidence_log.py +205 -0
  547. jvagent/memory/interaction.py +776 -0
  548. jvagent/memory/lock_manager.py +143 -0
  549. jvagent/memory/manager.py +1182 -0
  550. jvagent/memory/services/__init__.py +9 -0
  551. jvagent/memory/task_eligibility.py +161 -0
  552. jvagent/memory/task_graph.py +108 -0
  553. jvagent/memory/task_payload.py +37 -0
  554. jvagent/memory/task_proactive.py +105 -0
  555. jvagent/memory/task_store.py +1149 -0
  556. jvagent/memory/user.py +329 -0
  557. jvagent/scaffold/__init__.py +18 -0
  558. jvagent/scaffold/builtin_profiles/__init__.py +1 -0
  559. jvagent/scaffold/builtin_profiles/conversational.yaml +6 -0
  560. jvagent/scaffold/builtin_profiles/minimal.yaml +34 -0
  561. jvagent/scaffold/builtin_profiles/orchestrator.yaml +89 -0
  562. jvagent/scaffold/builtin_profiles/research.yaml +6 -0
  563. jvagent/scaffold/builtin_profiles/whatsapp_voice.yaml +19 -0
  564. jvagent/scaffold/interact_action.py +218 -0
  565. jvagent/scaffold/operations.py +585 -0
  566. jvagent/scaffold/profile_resolve.py +186 -0
  567. jvagent/scaffold/profile_stub.py +33 -0
  568. jvagent/scaffold/resource_io.py +46 -0
  569. jvagent/scaffold/skill_resolve.py +692 -0
  570. jvagent/scaffold/sop_extend.py +704 -0
  571. jvagent/scaffold/static/__init__.py +1 -0
  572. jvagent/scaffold/static/env.example.txt +196 -0
  573. jvagent/scaffold/yaml_io.py +104 -0
  574. jvagent/schemas/use-case-v1.schema.json +224 -0
  575. jvagent/skills/README.md +297 -0
  576. jvagent/skills/__init__.py +1 -0
  577. jvagent/skills/answer/SKILL.md +80 -0
  578. jvagent/skills/pageindex_search_with_references/SKILL.md +97 -0
  579. jvagent/skills/pdf_generation/SKILL.md +63 -0
  580. jvagent/skills/pdf_generation/resources/requirements.txt +19 -0
  581. jvagent/skills/pdf_generation/scripts/render_pdf.py +118 -0
  582. jvagent/skills/research/SKILL.md +41 -0
  583. jvagent/skills/research/__init__.py +1 -0
  584. jvagent/skills/triage/SKILL.md +49 -0
  585. jvagent/skills/triage/scripts/prioritize.py +59 -0
  586. jvagent/stress_seed_graph.py +455 -0
  587. jvagent/testing/__init__.py +9 -0
  588. jvagent/testing/use_case_loader.py +59 -0
  589. jvagent/tooling/__init__.py +19 -0
  590. jvagent/tooling/signature_schema.py +180 -0
  591. jvagent/tooling/tool.py +85 -0
  592. jvagent/tooling/tool_decorator.py +215 -0
  593. jvagent/tooling/tool_executor.py +251 -0
  594. jvagent/tooling/tool_observability.py +50 -0
  595. jvagent/tooling/tool_registry.py +104 -0
  596. jvagent/tooling/tool_result.py +39 -0
  597. jvagent/tooling/tool_schema_validator.py +116 -0
  598. jvagent/tooling/tool_serializer.py +24 -0
  599. jvagent/version.py +3 -0
  600. jvagent/webui/__init__.py +11 -0
  601. jvagent/webui/dist/assets/ChatInterface-C61tS6kJ.js +267 -0
  602. jvagent/webui/dist/assets/ChatInterface-CpKl2eGN.css +1 -0
  603. jvagent/webui/dist/assets/ChatRedirect-bolUSsJG.js +1 -0
  604. jvagent/webui/dist/assets/DebugInteractions-BGGM2vNQ.js +47 -0
  605. jvagent/webui/dist/assets/Login-Bep8PiER.js +1 -0
  606. jvagent/webui/dist/assets/main-DSMdJ_OX.css +1 -0
  607. jvagent/webui/dist/assets/main-yV_cwtCQ.js +13 -0
  608. jvagent/webui/dist/assets/useAgents-CdIk_6vr.js +1 -0
  609. jvagent/webui/dist/assets/useAuth-DoodL4QV.js +1 -0
  610. jvagent/webui/dist/assets/vendor-cytoscape-CUqq0XTU.js +331 -0
  611. jvagent/webui/dist/assets/vendor-d3-DznK3MmS.js +5 -0
  612. jvagent/webui/dist/assets/vendor-dagre-bridge-B2IYeVxy.js +6 -0
  613. jvagent/webui/dist/assets/vendor-graphviz-C9sQj0WI.js +3 -0
  614. jvagent/webui/dist/assets/vendor-markdown-B9moJQqy.js +14 -0
  615. jvagent/webui/dist/assets/vendor-react-CEqf_TvG.js +60 -0
  616. jvagent/webui/dist/index.html +34 -0
  617. jvagent/webui/server.py +183 -0
  618. jvagent-0.1.1.dist-info/METADATA +380 -0
  619. jvagent-0.1.1.dist-info/RECORD +624 -0
  620. jvagent-0.1.1.dist-info/WHEEL +5 -0
  621. jvagent-0.1.1.dist-info/entry_points.txt +2 -0
  622. jvagent-0.1.1.dist-info/licenses/AUTHORS +15 -0
  623. jvagent-0.1.1.dist-info/licenses/LICENSE +21 -0
  624. jvagent-0.1.1.dist-info/top_level.txt +1 -0
jvagent/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ """jvagent - Agentive Platform built on jvspatial.
2
+
3
+ Two supported usage modes:
4
+
5
+ * **Standalone server** (CLI) — ``jvagent run`` boots jvagent's own
6
+ jvspatial ``Server`` and HTTP surface. The default for new apps.
7
+ * **Embedded library** — host an existing jvspatial app (e.g. another
8
+ product also built on jvspatial) calls :mod:`jvagent.embed` to mount
9
+ jvagent's runtime in-process, sharing the host's database and
10
+ authentication. See :mod:`jvagent.embed` for the supported surface.
11
+
12
+ Public, semver-tracked exports live here. Anything reached via
13
+ ``jvagent.<internal_module>`` is implementation detail and may break
14
+ between minor versions.
15
+ """
16
+
17
+ # Install the Logger subclass that accepts ``details=`` as a kwarg before
18
+ # any submodule's ``logging.getLogger(__name__)`` call runs. See
19
+ # ``jvagent/_logging_compat.py`` and AUDIT-INDEX §2.2.
20
+ from jvagent._logging_compat import install as _install_logging_compat
21
+
22
+ _install_logging_compat()
23
+
24
+ # Register markdown extensions in ``mimetypes`` so jvspatial's storage MIME
25
+ # allow-list accepts ``.md`` saves on hosts without libmagic or an OS mime
26
+ # entry for markdown. Must run before any file-save path. See
27
+ # ``jvagent/_mimetypes_compat.py``.
28
+ from jvagent._mimetypes_compat import install as _install_mimetypes_compat # noqa: E402
29
+
30
+ _install_mimetypes_compat()
31
+
32
+ from jvagent import embed # noqa: E402 -- must follow logging shim install
33
+ from jvagent.version import __version__ # noqa: E402
34
+
35
+ __all__ = ["__version__", "embed"]
jvagent/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Allow running jvagent as a module: python -m jvagent"""
2
+
3
+ from jvagent.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,70 @@
1
+ """Logger compatibility shim.
2
+
3
+ jvagent calls ``logger.error(..., details={...})`` extensively across the
4
+ codebase. Stdlib :class:`logging.Logger` does not accept ``details`` as a
5
+ kwarg; it raises ``TypeError: Logger._log() got an unexpected keyword
6
+ argument 'details'``. This shim installs a :class:`Logger` subclass that
7
+ recognizes ``details=`` and routes it to ``extra["details"]`` so that
8
+ :class:`jvspatial.logging.handler.DBLogHandler` picks it up under its
9
+ documented ``extra`` contract.
10
+
11
+ Install BEFORE any ``logging.getLogger(...)`` call in jvagent — see
12
+ ``jvagent/__init__.py``.
13
+ """
14
+
15
+ import logging
16
+ from collections.abc import Mapping
17
+ from typing import Any
18
+
19
+
20
+ class JvAgentLogger(logging.Logger):
21
+ """Stdlib-compatible :class:`Logger` that also accepts a ``details`` kwarg.
22
+
23
+ Any ``details=`` passed to ``logger.error/warning/info/debug/critical``
24
+ (and the underlying ``_log``) is merged into the ``extra`` dict under the
25
+ ``"details"`` key. Caller-supplied ``extra["details"]`` wins on collision.
26
+ All other kwargs pass through unchanged.
27
+ """
28
+
29
+ def _log(
30
+ self,
31
+ level: int,
32
+ msg: object,
33
+ args: Any,
34
+ exc_info: Any = None,
35
+ extra: Any = None,
36
+ stack_info: bool = False,
37
+ stacklevel: int = 1,
38
+ details: Any = None,
39
+ ) -> None:
40
+ if details is not None:
41
+ if extra is None:
42
+ extra = {"details": details}
43
+ elif isinstance(extra, Mapping):
44
+ if "details" not in extra:
45
+ extra = {**extra, "details": details}
46
+ super()._log(
47
+ level,
48
+ msg,
49
+ args,
50
+ exc_info=exc_info,
51
+ extra=extra,
52
+ stack_info=stack_info,
53
+ stacklevel=stacklevel,
54
+ )
55
+
56
+
57
+ def install() -> None:
58
+ """Install :class:`JvAgentLogger` as the default Logger class.
59
+
60
+ Idempotent. Must be called before any ``logging.getLogger(__name__)``
61
+ call inside jvagent — typically from ``jvagent/__init__.py`` at the very
62
+ top of the module.
63
+
64
+ Loggers created BEFORE this call keep their original class. That's fine:
65
+ jvagent's own modules are imported after ``jvagent/__init__.py`` runs,
66
+ so their module-level ``logging.getLogger(__name__)`` returns instances
67
+ of this subclass.
68
+ """
69
+ if not issubclass(logging.getLoggerClass(), JvAgentLogger):
70
+ logging.setLoggerClass(JvAgentLogger)
@@ -0,0 +1,53 @@
1
+ """Deterministic MIME-type registration for text formats jvagent emits.
2
+
3
+ jvspatial's local storage validator detects a file's MIME type via
4
+ ``python-magic`` when available, otherwise it falls back to the stdlib
5
+ ``mimetypes`` module (extension-based). When *neither* knows an extension the
6
+ type resolves to ``application/octet-stream``, which is not in jvspatial's
7
+ allow-list — so the save is rejected with::
8
+
9
+ File type 'application/octet-stream' is not in allowed types
10
+
11
+ Markdown is the common casualty: ``mimetypes`` only maps ``.md`` →
12
+ ``text/markdown`` when the host OS mime database (e.g. ``/etc/mime.types``)
13
+ happens to include it. On hosts without that entry and without ``libmagic``
14
+ installed, every ``.md`` an agent writes (research reports, notes, …) fails to
15
+ save even though ``text/markdown`` *is* in jvspatial's allow-list.
16
+
17
+ We close the gap deterministically by registering the markdown extensions in
18
+ the ``mimetypes`` module at import time, before any file-save path runs. Only
19
+ extensions whose target type is already in jvspatial's default allow-list are
20
+ registered here — this changes detection, never the policy.
21
+
22
+ This mirrors the early ``_logging_compat`` install in :mod:`jvagent` and must
23
+ run before any submodule performs a save.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import mimetypes
29
+
30
+ # (mime_type, extension) pairs. Each ``mime_type`` MUST already be present in
31
+ # jvspatial's ``FileValidator.DEFAULT_ALLOWED_MIME_TYPES`` — we only fix
32
+ # *detection* of well-known text formats, never the allow-list itself.
33
+ _EXTRA_TYPES = (
34
+ ("text/markdown", ".md"),
35
+ ("text/markdown", ".markdown"),
36
+ )
37
+
38
+ _installed = False
39
+
40
+
41
+ def install() -> None:
42
+ """Register markdown extensions in ``mimetypes`` (idempotent)."""
43
+ global _installed
44
+ if _installed:
45
+ return
46
+ # ``mimetypes.add_type`` lazily inits the module's maps on first use, so an
47
+ # explicit init is unnecessary; add_type is safe to call repeatedly.
48
+ for mime_type, ext in _EXTRA_TYPES:
49
+ mimetypes.add_type(mime_type, ext)
50
+ _installed = True
51
+
52
+
53
+ __all__ = ["install"]
@@ -0,0 +1 @@
1
+ See [CLAUDE.md](CLAUDE.md) — same agent guide, alternate filename for non-Claude AI agents.
@@ -0,0 +1,145 @@
1
+ # jvagent/action/ — Agent Guide
2
+
3
+ > Local guide for the action plugin layer. Cross-link: root [`/CLAUDE.md`](../../CLAUDE.md), [`/.planning/reference/action-authoring.md`](../../.planning/reference/action-authoring.md), [`/.planning/reference/actions-catalog.md`](../../.planning/reference/actions-catalog.md).
4
+
5
+ ---
6
+
7
+ ## 1. What this directory owns
8
+
9
+ The plugin-loadable extension surface of jvagent:
10
+
11
+ - **`Action` base** ([`base.py:48`](base.py)) — Node subclass with lifecycle hooks, attribute config, endpoint registration, child-cascade delete, tool exposure to the Executive's Skills center.
12
+ - **`InteractAction`** ([`interact/base.py:32`](interact/base.py)) — see `interact/CLAUDE.md`.
13
+ - **Specialized bases**: `BaseModelAction`, `LanguageModelAction`, `BaseWebSearchAction`, `BaseSTTAction`, `BaseTTSAction`, `VectorStore`.
14
+ - **Concrete plugins** organized by topic: language models, response/bus, Orchestrator, memory-related, channel adapters, productivity integrations, tasks. Catalog in [`/.planning/reference/actions-catalog.md`](../../.planning/reference/actions-catalog.md).
15
+ - **Loader/registry** in `loader/`.
16
+ - **Plugin contracts** in `plugin_contracts.py`.
17
+
18
+ ---
19
+
20
+ ## 2. Key files
21
+
22
+ | File | Purpose |
23
+ |---|---|
24
+ | `base.py:48` | `Action` base class (canonical) |
25
+ | `base.py:272` | `get_tools()` — default discovers `@tool`-decorated methods via `collect_tools` |
26
+ | `jvagent/tooling/tool_decorator.py` | `@tool` decorator + `collect_tools` — preferred way to publish tools |
27
+ | `jvagent/tooling/signature_schema.py` | signature → JSON Schema deriver used by `@tool` |
28
+ | `base.py:180` | `get_capabilities()` — for ReplyAction prompt aggregation |
29
+ | `base.py:225` | `delete(cascade=True)` — walks outgoing edges and cascade-deletes children |
30
+ | `base.py:256-348` | Lifecycle hook contracts (`on_register`, `on_reload`, `post_register`, `on_startup`, `on_enable`, `on_disable`, `on_deregister`, `pulse`, `healthcheck`) |
31
+ | `base.py:354-460` | Endpoint discovery + unregistration (relies on `/actions/{action_id}/` path prefix) |
32
+ | `base.py:462-542` | Module unload safety (skips core + shared modules) |
33
+ | `base.py:710-852` | `get_action()` / `get_action_by_base_class()` / `get_model_action()` — cross-action lookup |
34
+ | `base.py:881-944` | Package metadata accessors (namespace, version, type) |
35
+ | `base.py:946-1100` | File storage helpers (action-scoped paths) |
36
+ | `interact/base.py:32` | `InteractAction` (see `interact/CLAUDE.md`) |
37
+ | `actions.py` | `Actions` manager node |
38
+ | `endpoints.py` | Top-level action HTTP routes (~9 routes) |
39
+ | `loader/` | Action loader, registry, plugin discovery |
40
+ | `plugin_contracts.py` | Plugin protocol definitions |
41
+ | `streaming.py` | Streaming response helpers |
42
+
43
+ ---
44
+
45
+ ## 3. Contracts (don't break)
46
+
47
+ 1. **`Action` subclasses MUST set `archetype` in `info.yaml`** to match the Python class name. The loader uses it.
48
+ 2. **Action endpoints MUST live under `/actions/{action_id}/...`** ([`base.py:373`](base.py)). Deregister scans this prefix; non-conforming endpoints leak after `on_deregister`.
49
+ 3. **`get_action()` is `O(1)`; `get_action_by_base_class()` is `O(n)`.** Don't use the latter in hot paths.
50
+ 4. **Lifecycle hooks MUST not swallow exceptions** ([`base.py:569+`](base.py)). The framework's `enable()`/`disable()`/`reload()` wrappers log errors automatically with the action context — silencing them hides bugs.
51
+ 5. **`Action.metadata` is owned by the loader.** Mutations to it are not persisted across restarts. Use `attribute(...)` fields for persistent state.
52
+ 6. **Child Nodes attached via outgoing edges are cascade-deleted** when the action is deleted ([`base.py:225`](base.py)). Always connect via `await self.connect(child, direction="out")`.
53
+ 7. **`is_singleton` default is `True`** ([`base.py:221`](base.py)). Override `config.singleton: false` in `info.yaml` if multiple instances per agent are allowed.
54
+ 8. **Thin harness** — Actions expose capabilities via `get_tools()`; they must not classify user intent, inject prep steering, auto-store extracted values, or inline multi-step workflows. Put judgment in skill SOPs and domain logic in skill extensions. See [`docs/thin-harness.md`](../../docs/thin-harness.md).
55
+ 9. **Prefer the `@tool` decorator over hand-built `Tool()`** — decorate an `async def` method with `@tool` ([`jvagent/tooling/tool_decorator.py`](../tooling/tool_decorator.py)) and the base `get_tools()` auto-publishes it. Name = `{action_name}__{method}` (override with `@tool(name=...)`); description = method docstring's first paragraph; JSON Schema = signature (use `Annotated[T, "desc"]` for per-arg docs). Hand-built `Tool()` and `get_tools()` overrides still work; only override when a tool can't be a decorated method (combine with `collect_tools(self)`).
56
+
57
+ ---
58
+
59
+ ## 4. The four-file pattern
60
+
61
+ Every action package MUST contain:
62
+
63
+ ```
64
+ {namespace}/{action_name}/
65
+ ├── __init__.py # exports class + imports endpoints (for @endpoint registration)
66
+ ├── {action_name}.py # Action subclass
67
+ ├── endpoints.py # @endpoint-decorated routes
68
+ └── info.yaml # package metadata
69
+ ```
70
+
71
+ Skeleton and full templates: [`/.planning/reference/action-authoring.md`](../../.planning/reference/action-authoring.md).
72
+
73
+ ---
74
+
75
+ ## 5. Cross-action lookup table
76
+
77
+ | Need | Method | Cost |
78
+ |---|---|---|
79
+ | One specific class | `await self.get_action(MyActionClass)` | O(1) (cached index) |
80
+ | Class by name string | `await self.get_action("MyActionClass")` | O(1) |
81
+ | Any subclass of a base | `await self.get_action_by_base_class(Base)` | O(n) isinstance scan |
82
+ | Any LM provider | `await self.get_model_action(required=True)` | O(1) if `model_action_type` set, else falls back to base scan |
83
+ | The agent | `await self.get_agent()` | Cached |
84
+ | The App | `await self.get_app()` | Cached singleton |
85
+
86
+ ---
87
+
88
+ ## 6. Tests
89
+
90
+ - `tests/action/{name}/` per-action tests.
91
+ - `tests/action/test_action_loader.py` — plugin loading.
92
+ - `tests/action/test_action_endpoints.py` — endpoint discovery.
93
+ - `tests/action/test_plugin_system.py` — plugin contracts.
94
+ - `tests/test_tool_schema_audit.py` — tool schema sanity.
95
+
96
+ ```bash
97
+ pytest tests/action/ -v
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 7. Adding a new Action
103
+
104
+ The detailed walkthrough lives at [`/.planning/reference/action-authoring.md`](../../.planning/reference/action-authoring.md) and the runbook at [`/.planning/runbooks/add-action.md`](../../.planning/runbooks/add-action.md). Short version:
105
+
106
+ 1. Pick base class (Action / InteractAction / specialized).
107
+ 2. Choose namespace (`jvagent/` for core, otherwise `contrib/` or `custom/`).
108
+ 3. Create the 4-file directory.
109
+ 4. Define `attribute(...)` fields; implement lifecycle hooks + `execute` (if InteractAction). Publish tools by decorating methods with `@tool` (preferred over overriding `get_tools()`).
110
+ 5. Wire endpoints under `/actions/{action_id}/...`.
111
+ 6. Add tests under `tests/action/{name}/`.
112
+ 7. Update [`actions-catalog.md`](../../.planning/reference/actions-catalog.md).
113
+
114
+ ---
115
+
116
+ ## 8. Traps specific to action/
117
+
118
+ | Trap | Fix |
119
+ |---|---|
120
+ | Missing `from . import endpoints` in `__init__.py` | Add it; otherwise routes don't register |
121
+ | Class name doesn't match `archetype` in info.yaml | Loader silently skips the package |
122
+ | Heavy work in `__init__` | Use lifecycle hooks (`on_register`, `on_enable`) instead |
123
+ | Forgetting `@compound_index` when adding a queried field | Slow queries at scale |
124
+ | Writing to `self.metadata` | Not persisted; use `attribute(...)` |
125
+ | Endpoints not under `/actions/{action_id}/` | Deregister leaks them |
126
+ | Recursive `await self.get_action(MyAction)` calls | OK (cache returns same instance) but expensive isinstance walks aren't |
127
+
128
+ ---
129
+
130
+ ## 9. Subdirectory pointers
131
+
132
+ | Subdir | Local guide |
133
+ |---|---|
134
+ | `interact/` | [`interact/CLAUDE.md`](interact/CLAUDE.md) |
135
+ | `interview/` | [`interview/CLAUDE.md`](interview/CLAUDE.md) |
136
+ | `orchestrator/` | (see [`/.planning/adr/0012-skill-executive-architecture.md`](../../.planning/adr/0012-skill-executive-architecture.md)) |
137
+ | All other action dirs | Per-package `info.yaml` + class docstring |
138
+
139
+ ---
140
+
141
+ ## 10. Out of scope here
142
+
143
+ - Walker mechanics: see `interact/CLAUDE.md`.
144
+ - Executive prompt/loop specifics: see [`/docs/ORCHESTRATOR.md`](../../docs/ORCHESTRATOR.md).
145
+ - Memory graph: see `jvagent/memory/CLAUDE.md`.
@@ -0,0 +1,34 @@
1
+ # jvagent.action — package index
2
+
3
+ Action plugins live under `jvagent/action/{name}/`. Each package owns its Action class,
4
+ `info.yaml`, endpoints, and action-local `utils/`.
5
+
6
+ ## Shared modules (import these across action families)
7
+
8
+ | Module | Purpose |
9
+ |--------|---------|
10
+ | [`utils/`](utils/) | Multi-consumer helpers: typed endpoint loader, Meta HMAC verify, webhook system user, reconcile |
11
+ | [`interact/`](interact/) | InteractAction base, walker, channel-neutral [`webhook_pipeline`](interact/webhook_pipeline.py) |
12
+ | [`oauth/`](oauth/) | OAuth state node, token crypto, audit logging |
13
+ | [`spreadsheet/`](spreadsheet/) | Vendor-neutral A1 range helpers (Sheets + Excel) |
14
+ | [`skill_spec/`](skill_spec/) | Skill contracts, registries, shared task-lock types |
15
+ | [`model/`](model/) | Language model actions and context |
16
+ | [`manifest`](manifest.py), [`parameters`](parameters.py) | App/action configuration |
17
+
18
+ ## Modularization contract
19
+
20
+ See [`.planning/reference/action-authoring.md` §15](../.planning/reference/action-authoring.md)
21
+ and [action-modularization-audit.md](../.planning/reference/action-modularization-audit.md).
22
+
23
+ **Rules (summary):**
24
+
25
+ 1. No imports from sibling action packages except through the shared modules above.
26
+ 2. Code moves to a shared path only when **2+ unrelated action families** consume it.
27
+ 3. No persisted `Node`/`Object` types under `action/utils/`.
28
+ 4. One canonical import path — no re-export shims.
29
+ 5. Channel webhook finalization uses `interact.webhook_pipeline` (public API).
30
+
31
+ ## Upload ownership
32
+
33
+ - **`interact/utils/uploads.py`** — upload parsing constants and helpers (canonical).
34
+ - **`orchestrator/uploads.py`** — orchestrator ingestion reflex only; imports from interact.
@@ -0,0 +1,33 @@
1
+ """Action system for jvagent.
2
+
3
+ This module provides the action management system including:
4
+ - Actions: Manager node for action registration and discovery
5
+ - Action: Base action class for all pluggable actions with CRUD endpoints
6
+ - ActionLoader: Dynamic action discovery and loading
7
+ """
8
+
9
+ from jvagent.action.actions import Actions
10
+ from jvagent.action.base import Action
11
+
12
+
13
+ def __getattr__(name: str):
14
+ """Lazy-import heavy loader objects or submodules to avoid circular imports."""
15
+ if name in ("ActionLoader", "ActionMetadata"):
16
+ from jvagent.action.loader import ActionLoader, ActionMetadata
17
+
18
+ return ActionLoader if name == "ActionLoader" else ActionMetadata
19
+
20
+ if name == "interact":
21
+ import importlib
22
+
23
+ return importlib.import_module("jvagent.action.interact")
24
+
25
+ if name == "endpoints":
26
+ import importlib
27
+
28
+ return importlib.import_module("jvagent.action.endpoints")
29
+
30
+ raise AttributeError(name)
31
+
32
+
33
+ __all__ = ["Actions", "Action", "ActionLoader", "ActionMetadata"]
@@ -0,0 +1,230 @@
1
+ # Access Control Action
2
+
3
+ Role-based access control with user_id-based permissions for secure agent operations.
4
+
5
+ ## Overview
6
+
7
+ The Access Control Action provides user-based permission management and channel/resource permissions for jvagent applications. Identity is keyed by `user_id` only (no sessions or conversations). It supports allow/deny rules for users and user groups, per-action restriction in the InteractWalker, and programmatic + REST APIs for runtime updates. User groups are scoped by action label (e.g. `default`, `PersonaAction`), allowing different group memberships per action with automatic fallback and merge resolution.
8
+
9
+ ## Features
10
+
11
+ - **User-only identity**: All permissions key off `user_id`
12
+ - **Channel/Resource permissions**: Fine-grained access by channel and action (InteractAction class name)
13
+ - **User groups**: Assign users to groups scoped by action label for permission inheritance; action-specific groups inherit from the `default` scope
14
+ - **Allow/Deny rules**: Deny rules checked first; allow rules grant access
15
+ - **default_deny**: When true, deny when no rule matches; when false, allow
16
+ - **action_aliases**: Map short names to action class names in config
17
+ - **enforce**: When false, all `has_action_access` checks succeed (policy off). Prefer this over disabling the graph node when you want the action to remain discoverable.
18
+ - **allow_anonymous**: When false (default), empty/missing `user_id` is denied whenever policy applies. Set true only if anonymous interact is intentional.
19
+ - **Exceptions**: Actions exempt from permission checks
20
+ - **Programmatic API**: Add/remove users, groups, and permission rules
21
+ - **Admin REST endpoints**: All agent-scoped via `agent_id` (singleton per agent)
22
+
23
+ ## Agent YAML Configuration
24
+
25
+ Configure directly under `context` (not under `config`). This ensures attributes are
26
+ populated correctly when running on Lambda and other serverless environments.
27
+
28
+ ```yaml
29
+ actions:
30
+ - action: jvagent/access_control_action
31
+ context:
32
+ enabled: true
33
+ enforce: true
34
+ allow_anonymous: false
35
+ description: "Per-user/group access control for interact actions"
36
+ default_deny: false
37
+ action_aliases:
38
+ persona: PersonaAction
39
+ report: ReportInterviewSkill
40
+ permissions:
41
+ default:
42
+ any:
43
+ deny: []
44
+ allow: [{ group: all, enabled: true }]
45
+ PersonaAction:
46
+ deny: []
47
+ allow: [admins]
48
+ whatsapp:
49
+ any:
50
+ deny: []
51
+ allow: [{ user: "5926431530", enabled: true }]
52
+ user_groups:
53
+ default:
54
+ admins: [user_abc123, user_def456]
55
+ support_team: [user_ghi789]
56
+ PersonaAction:
57
+ reviewers: [user_xyz]
58
+ exceptions:
59
+ - ReplyAction
60
+ - IntroInteractAction
61
+ ```
62
+
63
+ ## Permission Structure
64
+
65
+ ```
66
+ channel -> action_label -> allow/deny -> rules
67
+ ```
68
+
69
+ - **Channel**: "default" (web), "whatsapp", etc.
70
+ - **Action label**: InteractAction class name (e.g. "PersonaAction", "ReportInterviewSkill") or "any"
71
+ - **Allow/Deny**: Deny rules checked first; allow rules grant access
72
+ - **Rules**: `{ user: "user_id" }` or `{ group: "group_name" }`; `group: "all"` matches everyone. Groups resolve per action label with `default` fallback.
73
+
74
+ ## Programmatic API
75
+
76
+ ```python
77
+ # User groups (scoped by action_label, defaults to "default")
78
+ await action.add_user_group("support", ["u1", "u2"]) # default scope
79
+ await action.add_user_group("reviewers", ["u3"], action_label="PersonaAction") # action-scoped
80
+ await action.add_user_to_group("admins", "user_abc") # default scope
81
+ await action.add_users_to_group("admins", ["u1", "u2"]) # default scope
82
+ await action.add_user_to_group("reviewers", "u3", action_label="PersonaAction")
83
+ await action.remove_user_from_group("admins", "user_abc") # default scope
84
+ await action.remove_user_group("support") # default scope
85
+ await action.remove_user_group("reviewers", action_label="PersonaAction")
86
+ groups = action.get_user_groups() # full nested dict
87
+ groups = action.get_user_groups(action_label="PersonaAction") # merged (action + default)
88
+
89
+ # Direct user access (allow/deny)
90
+ await action.add_user_to_allow("default", "PersonaAction", "user_xyz")
91
+ await action.add_user_to_deny("default", "DeleteAction", "user_bad")
92
+ await action.remove_user_from_permission("default", "X", "user_id", from_allow=True)
93
+
94
+ # Group-based rules
95
+ await action.add_group_to_allow("default", "PersonaAction", "admins")
96
+ await action.add_group_to_deny("default", "DeleteAction", "guests")
97
+ await action.remove_group_from_permission("default", "X", "group", from_allow=True)
98
+
99
+ # Check access
100
+ has_access = await action.has_action_access(
101
+ user_id="user_abc",
102
+ action_label="PersonaAction",
103
+ channel="default",
104
+ )
105
+
106
+ # Bulk config (includes default_deny, action_aliases, enforce, allow_anonymous)
107
+ config = action.export_config()
108
+ await action.import_config(config, purge=True)
109
+ ```
110
+
111
+ ## REST API (Admin Only)
112
+
113
+ All endpoints require `auth=True` and `roles=["admin"]`. AccessControlAction is a
114
+ singleton per agent, so all endpoints are agent-scoped via `agent_id`.
115
+
116
+ ### Endpoints (`/agents/{agent_id}/access_control/`)
117
+
118
+ | Method | Path | Purpose |
119
+ |--------|------|---------|
120
+ | GET | `/config?format=json\|yaml` | Export config |
121
+ | PUT | `/config` | Replace permissions |
122
+ | PATCH | `/config` | Merge permissions |
123
+ | POST | `/check` | Check access (user_id, action_label, channel) |
124
+ | POST | `/user_groups` | Create group (body: `{name, user_ids?, action_label?}`) |
125
+ | GET | `/user_groups?action_label=` | List groups (scoped or full nested) |
126
+ | POST | `/user_groups/{group}/users` | Add users to group (body: `{user_ids, action_label?}`) |
127
+ | DELETE | `/user_groups/{group}/users` | Remove users from group (body: `{user_ids, action_label?}`) |
128
+ | DELETE | `/user_groups/{group}` | Remove group (body: `{action_label?}`) |
129
+ | POST | `/permissions` | Add user/group to allow or deny |
130
+ | DELETE | `/permissions` | Remove user/group from allow or deny |
131
+
132
+ ### Config Endpoint (PUT/PATCH)
133
+
134
+ Resolves AccessControlAction from `agent_id` (no start_node). Request body:
135
+
136
+ ```json
137
+ {
138
+ "permissions": {
139
+ "default": {
140
+ "any": { "deny": [], "allow": [{ "group": "all", "enabled": true }] },
141
+ "ReportInterviewSkill": { "deny": [{ "group": "all" }], "allow": [] }
142
+ },
143
+ "whatsapp": {
144
+ "any": { "deny": [], "allow": [{ "group": "all", "enabled": true }] },
145
+ "ReportInterviewSkill": { "deny": [], "allow": [{ "group": "all", "enabled": true }] }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ - **PUT**: Replaces permissions entirely. Preserves `user_groups` and `exceptions` (and other non-permissions fields on the node). Use `export_config` / `import_config` in code to replace `default_deny`, `action_aliases`, `enforce`, or `allow_anonymous` in one shot.
152
+ - **PATCH**: Merges permissions at channel level.
153
+
154
+ ## User Groups
155
+
156
+ User groups are organized by **action label scope**, allowing different group memberships per action:
157
+
158
+ ```yaml
159
+ user_groups:
160
+ default: # Fallback scope — used when the action label has no entry
161
+ admins: [user_abc123, user_def456]
162
+ support_team: [user_ghi789]
163
+ PersonaAction: # Action-specific scope
164
+ reviewers: [user_xyz]
165
+ ReportInterviewSkill:
166
+ analysts: [user_abc123]
167
+ ```
168
+
169
+ ### Resolution
170
+
171
+ When `_matches_rule` evaluates a `{ group: "admins" }` rule for action label `PersonaAction`:
172
+
173
+ 1. Look up `user_groups["PersonaAction"]["admins"]`
174
+ 2. Fall back to `user_groups["default"]["admins"]` if missing
175
+ 3. **Merge**: action-specific and default groups are merged (action-specific wins on conflicts)
176
+
177
+ This means a user in `default.admins` is also considered an admin for `PersonaAction` unless `PersonaAction.admins` overrides it. The special group names `"all"` and `"any"` always match without looking up `user_groups`.
178
+
179
+ ### Legacy Flat Format
180
+
181
+ The original `user_groups` format was flat: `{ admins: [user_abc] }`. This is auto-migrated to `{ default: { admins: [user_abc] } }` by `_migrate_user_groups()` during `import_config`. Both the programmatic API and REST endpoints accept the flat format for backward compatibility.
182
+
183
+ ### Programmatic API (action_label parameter)
184
+
185
+ All group methods accept an optional `action_label` parameter (default `"default"`):
186
+
187
+ | Method | action_label behavior |
188
+ |--------|---------------------|
189
+ | `add_user_group(name, user_ids, action_label)` | Creates group under the given scope |
190
+ | `add_user_to_group(group, user_id, action_label)` | Adds user to group in the given scope |
191
+ | `add_users_to_group(group, user_ids, action_label)` | Adds users to group in the given scope |
192
+ | `remove_user_from_group(group, user_id, action_label)` | Removes user from group in the given scope |
193
+ | `remove_user_group(name, action_label)` | Deletes group from the given scope |
194
+ | `get_user_groups(action_label)` | No argument → full nested dict; with label → merged dict for that scope |
195
+
196
+ ### REST API (action_label parameter)
197
+
198
+ All `user_groups` endpoints accept an `action_label` field in the request body (or query param for GET), defaulting to `"default"`:
199
+
200
+ | Endpoint | action_label source |
201
+ |----------|-------------------|
202
+ | `POST /user_groups` | Body: `{name, user_ids?, action_label?}` |
203
+ | `GET /user_groups?action_label=` | Query param (omit for full nested structure) |
204
+ | `POST /user_groups/{group}/users` | Body: `{user_ids, action_label?}` |
205
+ | `DELETE /user_groups/{group}/users` | Body: `{user_ids, action_label?}` |
206
+ | `DELETE /user_groups/{group}` | Body: `{action_label?}` |
207
+
208
+ ## Integration
209
+
210
+ - **Policy applies** when the graph node is `enabled` and `enforce` is true (`policy_applies()`). If there is **no** `AccessControlAction` on the agent, interact proceeds without access checks (open by default).
211
+ - **Session gate**: After `memory.get_session()` resolves `user_id`, the walker checks resource `interact` before creating an interaction. Configure `default` / `whatsapp` / … channel keys for `interact` alongside per–class-name keys.
212
+ - **InteractWalker**: Before each foreground `InteractAction` runs, access is checked using that action’s class name. Deferred (`run_in_background`) actions are checked when queued and again before `execute` in the background runner.
213
+ - **HTTP `/interact`**: Entry access is enforced inside the walker (not on the raw request parameter alone), so `session_id`-only clients are evaluated after session resolution.
214
+ - **WhatsApp webhook**: Uses `sender` (phone) as `user_id` for channel `whatsapp`; denials emit structured logs (`access_control_denied`).
215
+ - **Duplicates**: If more than one `AccessControlAction` exists for an agent, an error is logged and the first match is used—fix the graph to a single node.
216
+
217
+ ## Migration (breaking)
218
+
219
+ - **`user_groups` is now nested by action label**: The flat format `{ admins: [...] }` is auto-migrated to `{ default: { admins: [...] } }`. Existing YAML configs with flat `user_groups` continue to work via `_migrate_user_groups()`, but new configs should use the nested format.
220
+ - Empty `user_id` no longer bypasses checks when policy applies; set `allow_anonymous: true` only if you need that behavior.
221
+ - Turning off enforcement: set `enforce: false` (or disable the graph node); do not rely on old "empty user always allowed" behavior.
222
+ - Background interact actions are now subject to the same rules as foreground actions.
223
+ - Export/import round-trips `default_deny`, `action_aliases`, `enforce`, and `allow_anonymous`; merge import deduplicates `exceptions` entries.
224
+ - All group-related API methods now accept an `action_label` parameter (default `"default"`). Code calling these methods without the parameter is unaffected.
225
+
226
+ ## Channel Adapters
227
+
228
+ Channels that receive requests before user resolution (e.g. WhatsApp with `sender` phone) should either:
229
+ - Resolve to `user_id` via `memory.get_session()` before the access check, or
230
+ - Use the channel-specific stable ID (e.g. phone number) as `user_id` in config.
@@ -0,0 +1,10 @@
1
+ """Access Control action package.
2
+
3
+ This module provides role-based access control for agent operations.
4
+ """
5
+
6
+ # Import endpoints to register them
7
+ from . import endpoints # noqa: F401
8
+ from .access_control_action import AccessControlAction
9
+
10
+ __all__ = ["AccessControlAction"]