ocaya 2.14.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 (364) hide show
  1. .kiro/specs/ocaya/.config.kiro +1 -0
  2. .kiro/specs/ocaya/design.md +464 -0
  3. .kiro/specs/ocaya/requirements.md +191 -0
  4. .kiro/specs/ocaya/tasks.md +221 -0
  5. ocaya/__init__.py +13 -0
  6. ocaya/acp/__init__.py +0 -0
  7. ocaya/acp/acp_agent_loop.py +1948 -0
  8. ocaya/acp/acp_logger.py +97 -0
  9. ocaya/acp/commands/__init__.py +5 -0
  10. ocaya/acp/commands/registry.py +89 -0
  11. ocaya/acp/entrypoint.py +112 -0
  12. ocaya/acp/exceptions.py +140 -0
  13. ocaya/acp/session.py +75 -0
  14. ocaya/acp/title.py +69 -0
  15. ocaya/acp/tools/__init__.py +0 -0
  16. ocaya/acp/tools/base.py +53 -0
  17. ocaya/acp/tools/builtins/bash.py +168 -0
  18. ocaya/acp/tools/builtins/edit.py +114 -0
  19. ocaya/acp/tools/builtins/grep.py +79 -0
  20. ocaya/acp/tools/builtins/read.py +132 -0
  21. ocaya/acp/tools/builtins/skill.py +77 -0
  22. ocaya/acp/tools/builtins/task.py +73 -0
  23. ocaya/acp/tools/builtins/todo.py +68 -0
  24. ocaya/acp/tools/builtins/web_fetch.py +83 -0
  25. ocaya/acp/tools/builtins/web_search.py +80 -0
  26. ocaya/acp/tools/builtins/write_file.py +104 -0
  27. ocaya/acp/tools/events.py +8 -0
  28. ocaya/acp/tools/session_update.py +196 -0
  29. ocaya/acp/utils.py +338 -0
  30. ocaya/cli/__init__.py +0 -0
  31. ocaya/cli/autocompletion/__init__.py +0 -0
  32. ocaya/cli/autocompletion/base.py +22 -0
  33. ocaya/cli/autocompletion/path_completion.py +177 -0
  34. ocaya/cli/autocompletion/slash_command.py +95 -0
  35. ocaya/cli/cache.py +32 -0
  36. ocaya/cli/cli.py +279 -0
  37. ocaya/cli/clipboard.py +190 -0
  38. ocaya/cli/commands.py +261 -0
  39. ocaya/cli/entrypoint.py +251 -0
  40. ocaya/cli/history_manager.py +105 -0
  41. ocaya/cli/narrator_manager/__init__.py +15 -0
  42. ocaya/cli/narrator_manager/narrator_manager.py +269 -0
  43. ocaya/cli/narrator_manager/narrator_manager_port.py +45 -0
  44. ocaya/cli/narrator_manager/telemetry.py +30 -0
  45. ocaya/cli/plan_offer/adapters/http_whoami_gateway.py +45 -0
  46. ocaya/cli/plan_offer/decide_plan_offer.py +127 -0
  47. ocaya/cli/plan_offer/ports/whoami_gateway.py +67 -0
  48. ocaya/cli/profiler.py +89 -0
  49. ocaya/cli/stderr_guard.py +101 -0
  50. ocaya/cli/terminal_detect.py +83 -0
  51. ocaya/cli/textual_ui/__init__.py +0 -0
  52. ocaya/cli/textual_ui/app.py +3395 -0
  53. ocaya/cli/textual_ui/app.tcss +1606 -0
  54. ocaya/cli/textual_ui/constants.py +11 -0
  55. ocaya/cli/textual_ui/external_editor.py +38 -0
  56. ocaya/cli/textual_ui/handlers/__init__.py +5 -0
  57. ocaya/cli/textual_ui/handlers/event_handler.py +269 -0
  58. ocaya/cli/textual_ui/message_queue.py +85 -0
  59. ocaya/cli/textual_ui/notifications/__init__.py +11 -0
  60. ocaya/cli/textual_ui/notifications/adapters/__init__.py +0 -0
  61. ocaya/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +60 -0
  62. ocaya/cli/textual_ui/notifications/ports/__init__.py +0 -0
  63. ocaya/cli/textual_ui/notifications/ports/notification_port.py +16 -0
  64. ocaya/cli/textual_ui/quit_manager.py +64 -0
  65. ocaya/cli/textual_ui/recording/__init__.py +5 -0
  66. ocaya/cli/textual_ui/recording/recording_indicator.py +79 -0
  67. ocaya/cli/textual_ui/remote/__init__.py +8 -0
  68. ocaya/cli/textual_ui/remote/remote_session_manager.py +211 -0
  69. ocaya/cli/textual_ui/scheduled_loop_runner.py +99 -0
  70. ocaya/cli/textual_ui/session_exit.py +31 -0
  71. ocaya/cli/textual_ui/widgets/__init__.py +0 -0
  72. ocaya/cli/textual_ui/widgets/approval_app.py +270 -0
  73. ocaya/cli/textual_ui/widgets/banner/banner.py +158 -0
  74. ocaya/cli/textual_ui/widgets/banner/petit_chat.py +197 -0
  75. ocaya/cli/textual_ui/widgets/braille_renderer.py +58 -0
  76. ocaya/cli/textual_ui/widgets/chat_input/__init__.py +7 -0
  77. ocaya/cli/textual_ui/widgets/chat_input/body.py +308 -0
  78. ocaya/cli/textual_ui/widgets/chat_input/completion_manager.py +62 -0
  79. ocaya/cli/textual_ui/widgets/chat_input/completion_popup.py +76 -0
  80. ocaya/cli/textual_ui/widgets/chat_input/container.py +283 -0
  81. ocaya/cli/textual_ui/widgets/chat_input/paste_path.py +117 -0
  82. ocaya/cli/textual_ui/widgets/chat_input/text_area.py +445 -0
  83. ocaya/cli/textual_ui/widgets/compact.py +43 -0
  84. ocaya/cli/textual_ui/widgets/config_app.py +184 -0
  85. ocaya/cli/textual_ui/widgets/connector_auth_app.py +227 -0
  86. ocaya/cli/textual_ui/widgets/context_progress.py +30 -0
  87. ocaya/cli/textual_ui/widgets/debug_console.py +244 -0
  88. ocaya/cli/textual_ui/widgets/feedback_bar.py +64 -0
  89. ocaya/cli/textual_ui/widgets/feedback_bar_manager.py +23 -0
  90. ocaya/cli/textual_ui/widgets/load_more.py +43 -0
  91. ocaya/cli/textual_ui/widgets/loading.py +231 -0
  92. ocaya/cli/textual_ui/widgets/mcp_app.py +646 -0
  93. ocaya/cli/textual_ui/widgets/messages.py +590 -0
  94. ocaya/cli/textual_ui/widgets/model_picker.py +75 -0
  95. ocaya/cli/textual_ui/widgets/narrator_status.py +65 -0
  96. ocaya/cli/textual_ui/widgets/no_markup_static.py +11 -0
  97. ocaya/cli/textual_ui/widgets/path_display.py +31 -0
  98. ocaya/cli/textual_ui/widgets/proxy_setup_app.py +122 -0
  99. ocaya/cli/textual_ui/widgets/question_app.py +562 -0
  100. ocaya/cli/textual_ui/widgets/queued_messages.py +97 -0
  101. ocaya/cli/textual_ui/widgets/rewind_app.py +147 -0
  102. ocaya/cli/textual_ui/widgets/session_picker.py +133 -0
  103. ocaya/cli/textual_ui/widgets/spinner.py +194 -0
  104. ocaya/cli/textual_ui/widgets/status_message.py +76 -0
  105. ocaya/cli/textual_ui/widgets/teleport_message.py +31 -0
  106. ocaya/cli/textual_ui/widgets/theme_picker.py +114 -0
  107. ocaya/cli/textual_ui/widgets/thinking_picker.py +79 -0
  108. ocaya/cli/textual_ui/widgets/tool_widgets.py +368 -0
  109. ocaya/cli/textual_ui/widgets/tools.py +216 -0
  110. ocaya/cli/textual_ui/widgets/voice_app.py +173 -0
  111. ocaya/cli/textual_ui/widgets/vscode_compat.py +26 -0
  112. ocaya/cli/textual_ui/windowing/__init__.py +29 -0
  113. ocaya/cli/textual_ui/windowing/history.py +115 -0
  114. ocaya/cli/textual_ui/windowing/history_windowing.py +67 -0
  115. ocaya/cli/textual_ui/windowing/state.py +119 -0
  116. ocaya/cli/turn_summary/__init__.py +20 -0
  117. ocaya/cli/turn_summary/noop.py +38 -0
  118. ocaya/cli/turn_summary/port.py +52 -0
  119. ocaya/cli/turn_summary/tracker.py +147 -0
  120. ocaya/cli/turn_summary/utils.py +30 -0
  121. ocaya/cli/update_notifier/__init__.py +47 -0
  122. ocaya/cli/update_notifier/adapters/filesystem_update_cache_repository.py +77 -0
  123. ocaya/cli/update_notifier/adapters/github_update_gateway.py +104 -0
  124. ocaya/cli/update_notifier/adapters/pypi_update_gateway.py +110 -0
  125. ocaya/cli/update_notifier/ports/update_cache_repository.py +16 -0
  126. ocaya/cli/update_notifier/ports/update_gateway.py +53 -0
  127. ocaya/cli/update_notifier/update.py +139 -0
  128. ocaya/cli/update_notifier/whats_new.py +50 -0
  129. ocaya/cli/voice_manager/__init__.py +15 -0
  130. ocaya/cli/voice_manager/telemetry.py +30 -0
  131. ocaya/cli/voice_manager/voice_manager.py +269 -0
  132. ocaya/cli/voice_manager/voice_manager_port.py +58 -0
  133. ocaya/cli/vscode_extension_promo/__init__.py +43 -0
  134. ocaya/cli/vscode_extension_promo/_port.py +14 -0
  135. ocaya/cli/vscode_extension_promo/adapters/__init__.py +0 -0
  136. ocaya/cli/vscode_extension_promo/adapters/filesystem_repository.py +43 -0
  137. ocaya/core/__init__.py +15 -0
  138. ocaya/core/agent_loop.py +1862 -0
  139. ocaya/core/agents/__init__.py +29 -0
  140. ocaya/core/agents/manager.py +185 -0
  141. ocaya/core/agents/models.py +208 -0
  142. ocaya/core/audio_player/__init__.py +21 -0
  143. ocaya/core/audio_player/audio_player.py +130 -0
  144. ocaya/core/audio_player/audio_player_port.py +40 -0
  145. ocaya/core/audio_player/utils.py +12 -0
  146. ocaya/core/audio_recorder/__init__.py +23 -0
  147. ocaya/core/audio_recorder/audio_recorder.py +287 -0
  148. ocaya/core/audio_recorder/audio_recorder_port.py +64 -0
  149. ocaya/core/auth/__init__.py +6 -0
  150. ocaya/core/auth/crypto.py +137 -0
  151. ocaya/core/auth/github.py +184 -0
  152. ocaya/core/autocompletion/__init__.py +0 -0
  153. ocaya/core/autocompletion/completers.py +381 -0
  154. ocaya/core/autocompletion/file_indexer/__init__.py +10 -0
  155. ocaya/core/autocompletion/file_indexer/ignore_rules.py +170 -0
  156. ocaya/core/autocompletion/file_indexer/indexer.py +187 -0
  157. ocaya/core/autocompletion/file_indexer/store.py +188 -0
  158. ocaya/core/autocompletion/file_indexer/watcher.py +75 -0
  159. ocaya/core/autocompletion/fuzzy.py +189 -0
  160. ocaya/core/autocompletion/path_prompt.py +153 -0
  161. ocaya/core/autocompletion/path_prompt_adapter.py +181 -0
  162. ocaya/core/compaction.py +47 -0
  163. ocaya/core/config/__init__.py +131 -0
  164. ocaya/core/config/_settings.py +1176 -0
  165. ocaya/core/config/builder.py +122 -0
  166. ocaya/core/config/harness_files/__init__.py +17 -0
  167. ocaya/core/config/harness_files/_harness_manager.py +266 -0
  168. ocaya/core/config/harness_files/_paths.py +9 -0
  169. ocaya/core/config/layer.py +307 -0
  170. ocaya/core/config/layers/__init__.py +13 -0
  171. ocaya/core/config/layers/environment.py +44 -0
  172. ocaya/core/config/layers/overrides.py +34 -0
  173. ocaya/core/config/layers/project.py +91 -0
  174. ocaya/core/config/layers/user.py +39 -0
  175. ocaya/core/config/orchestrator.py +53 -0
  176. ocaya/core/config/patch.py +101 -0
  177. ocaya/core/config/schema.py +157 -0
  178. ocaya/core/config/types.py +25 -0
  179. ocaya/core/config/vibe_schema.py +242 -0
  180. ocaya/core/data_retention.py +7 -0
  181. ocaya/core/experiments/__init__.py +58 -0
  182. ocaya/core/experiments/_constants.py +15 -0
  183. ocaya/core/experiments/active.py +17 -0
  184. ocaya/core/experiments/client.py +81 -0
  185. ocaya/core/experiments/manager.py +110 -0
  186. ocaya/core/experiments/models.py +79 -0
  187. ocaya/core/experiments/session.py +81 -0
  188. ocaya/core/feedback.py +39 -0
  189. ocaya/core/hooks/__init__.py +0 -0
  190. ocaya/core/hooks/config.py +100 -0
  191. ocaya/core/hooks/executor.py +55 -0
  192. ocaya/core/hooks/manager.py +140 -0
  193. ocaya/core/hooks/models.py +98 -0
  194. ocaya/core/llm/__init__.py +0 -0
  195. ocaya/core/llm/backend/_image.py +38 -0
  196. ocaya/core/llm/backend/anthropic.py +619 -0
  197. ocaya/core/llm/backend/base.py +39 -0
  198. ocaya/core/llm/backend/factory.py +7 -0
  199. ocaya/core/llm/backend/generic.py +443 -0
  200. ocaya/core/llm/backend/mistral.py +442 -0
  201. ocaya/core/llm/backend/openai_responses.py +653 -0
  202. ocaya/core/llm/backend/reasoning_adapter.py +236 -0
  203. ocaya/core/llm/backend/vertex.py +130 -0
  204. ocaya/core/llm/exceptions.py +225 -0
  205. ocaya/core/llm/format.py +185 -0
  206. ocaya/core/llm/types.py +94 -0
  207. ocaya/core/log_reader.py +224 -0
  208. ocaya/core/logger.py +71 -0
  209. ocaya/core/loop.py +250 -0
  210. ocaya/core/middleware.py +266 -0
  211. ocaya/core/nuage/__init__.py +0 -0
  212. ocaya/core/nuage/agent_models.py +26 -0
  213. ocaya/core/nuage/client.py +205 -0
  214. ocaya/core/nuage/events.py +227 -0
  215. ocaya/core/nuage/exceptions.py +46 -0
  216. ocaya/core/nuage/remote_events_source.py +207 -0
  217. ocaya/core/nuage/remote_workflow_event_models.py +137 -0
  218. ocaya/core/nuage/remote_workflow_event_translator.py +1313 -0
  219. ocaya/core/nuage/streaming.py +32 -0
  220. ocaya/core/nuage/workflow.py +44 -0
  221. ocaya/core/output_formatters.py +106 -0
  222. ocaya/core/paths/__init__.py +41 -0
  223. ocaya/core/paths/_agents_home.py +14 -0
  224. ocaya/core/paths/_local_config_files.py +80 -0
  225. ocaya/core/paths/_ocaya_home.py +38 -0
  226. ocaya/core/paths/conventions.py +3 -0
  227. ocaya/core/plan_session.py +42 -0
  228. ocaya/core/programmatic.py +101 -0
  229. ocaya/core/prompts/__init__.py +113 -0
  230. ocaya/core/prompts/agents_doc.md +5 -0
  231. ocaya/core/prompts/cli.md +111 -0
  232. ocaya/core/prompts/cli_2026-05.md +135 -0
  233. ocaya/core/prompts/compact.md +12 -0
  234. ocaya/core/prompts/compact_summary_prefix.md +1 -0
  235. ocaya/core/prompts/dangerous_directory.md +5 -0
  236. ocaya/core/prompts/explore.md +50 -0
  237. ocaya/core/prompts/jarvis.md +64 -0
  238. ocaya/core/prompts/lean.md +159 -0
  239. ocaya/core/prompts/minimal.md +12 -0
  240. ocaya/core/prompts/ocaya.md +63 -0
  241. ocaya/core/prompts/project_context.md +4 -0
  242. ocaya/core/prompts/tests.md +1 -0
  243. ocaya/core/prompts/turn_summary.md +10 -0
  244. ocaya/core/proxy_setup.py +65 -0
  245. ocaya/core/rewind/__init__.py +10 -0
  246. ocaya/core/rewind/manager.py +192 -0
  247. ocaya/core/scratchpad.py +61 -0
  248. ocaya/core/session/image_snapshot.py +56 -0
  249. ocaya/core/session/last_session_pointer.py +132 -0
  250. ocaya/core/session/resume_sessions.py +95 -0
  251. ocaya/core/session/saved_sessions.py +87 -0
  252. ocaya/core/session/session_id.py +34 -0
  253. ocaya/core/session/session_loader.py +292 -0
  254. ocaya/core/session/session_logger.py +456 -0
  255. ocaya/core/session/session_migration.py +41 -0
  256. ocaya/core/session/title_format.py +51 -0
  257. ocaya/core/skills/__init__.py +13 -0
  258. ocaya/core/skills/builtins/__init__.py +6 -0
  259. ocaya/core/skills/builtins/ocaya.py +584 -0
  260. ocaya/core/skills/manager.py +193 -0
  261. ocaya/core/skills/models.py +109 -0
  262. ocaya/core/skills/parser.py +39 -0
  263. ocaya/core/system_prompt.py +394 -0
  264. ocaya/core/telemetry/__init__.py +0 -0
  265. ocaya/core/telemetry/build_metadata.py +67 -0
  266. ocaya/core/telemetry/send.py +368 -0
  267. ocaya/core/telemetry/types.py +54 -0
  268. ocaya/core/teleport/errors.py +9 -0
  269. ocaya/core/teleport/git.py +222 -0
  270. ocaya/core/teleport/nuage.py +137 -0
  271. ocaya/core/teleport/telemetry.py +80 -0
  272. ocaya/core/teleport/teleport.py +196 -0
  273. ocaya/core/teleport/types.py +39 -0
  274. ocaya/core/tools/arity.py +158 -0
  275. ocaya/core/tools/base.py +400 -0
  276. ocaya/core/tools/builtins/ask_user_question.py +132 -0
  277. ocaya/core/tools/builtins/bash.py +538 -0
  278. ocaya/core/tools/builtins/clipboard.py +169 -0
  279. ocaya/core/tools/builtins/edit.py +205 -0
  280. ocaya/core/tools/builtins/exit_plan_mode.py +137 -0
  281. ocaya/core/tools/builtins/grep.py +364 -0
  282. ocaya/core/tools/builtins/open_app.py +153 -0
  283. ocaya/core/tools/builtins/prompts/__init__.py +0 -0
  284. ocaya/core/tools/builtins/prompts/ask_user_question.md +84 -0
  285. ocaya/core/tools/builtins/prompts/bash.md +73 -0
  286. ocaya/core/tools/builtins/prompts/edit.md +19 -0
  287. ocaya/core/tools/builtins/prompts/grep.md +4 -0
  288. ocaya/core/tools/builtins/prompts/read.md +19 -0
  289. ocaya/core/tools/builtins/prompts/skill.md +19 -0
  290. ocaya/core/tools/builtins/prompts/task.md +24 -0
  291. ocaya/core/tools/builtins/prompts/todo.md +199 -0
  292. ocaya/core/tools/builtins/prompts/webfetch.md +7 -0
  293. ocaya/core/tools/builtins/prompts/websearch.md +25 -0
  294. ocaya/core/tools/builtins/prompts/write_file.md +27 -0
  295. ocaya/core/tools/builtins/read.py +235 -0
  296. ocaya/core/tools/builtins/skill.py +126 -0
  297. ocaya/core/tools/builtins/task.py +190 -0
  298. ocaya/core/tools/builtins/todo.py +129 -0
  299. ocaya/core/tools/builtins/webfetch.py +257 -0
  300. ocaya/core/tools/builtins/websearch.py +178 -0
  301. ocaya/core/tools/builtins/write_file.py +139 -0
  302. ocaya/core/tools/connectors/__init__.py +9 -0
  303. ocaya/core/tools/connectors/connector_registry.py +508 -0
  304. ocaya/core/tools/connectors/counts.py +26 -0
  305. ocaya/core/tools/manager.py +455 -0
  306. ocaya/core/tools/mcp/__init__.py +33 -0
  307. ocaya/core/tools/mcp/registry.py +181 -0
  308. ocaya/core/tools/mcp/tools.py +468 -0
  309. ocaya/core/tools/mcp_sampling.py +126 -0
  310. ocaya/core/tools/mcp_settings.py +75 -0
  311. ocaya/core/tools/permissions.py +68 -0
  312. ocaya/core/tools/ui.py +121 -0
  313. ocaya/core/tools/utils.py +125 -0
  314. ocaya/core/tracing.py +137 -0
  315. ocaya/core/transcribe/__init__.py +23 -0
  316. ocaya/core/transcribe/factory.py +19 -0
  317. ocaya/core/transcribe/mistral_transcribe_client.py +85 -0
  318. ocaya/core/transcribe/transcribe_client_port.py +44 -0
  319. ocaya/core/trusted_folders.py +174 -0
  320. ocaya/core/tts/__init__.py +7 -0
  321. ocaya/core/tts/factory.py +15 -0
  322. ocaya/core/tts/mistral_tts_client.py +58 -0
  323. ocaya/core/tts/tts_client_port.py +19 -0
  324. ocaya/core/types.py +589 -0
  325. ocaya/core/utils/__init__.py +73 -0
  326. ocaya/core/utils/async_subprocess.py +65 -0
  327. ocaya/core/utils/concurrency.py +59 -0
  328. ocaya/core/utils/display.py +19 -0
  329. ocaya/core/utils/http.py +59 -0
  330. ocaya/core/utils/io.py +216 -0
  331. ocaya/core/utils/matching.py +35 -0
  332. ocaya/core/utils/merge.py +115 -0
  333. ocaya/core/utils/paths.py +42 -0
  334. ocaya/core/utils/platform.py +45 -0
  335. ocaya/core/utils/retry.py +138 -0
  336. ocaya/core/utils/slug.py +113 -0
  337. ocaya/core/utils/tags.py +80 -0
  338. ocaya/core/utils/time.py +7 -0
  339. ocaya/core/utils/tokens.py +29 -0
  340. ocaya/setup/auth/__init__.py +39 -0
  341. ocaya/setup/auth/api_key_persistence.py +78 -0
  342. ocaya/setup/auth/auth_state.py +151 -0
  343. ocaya/setup/auth/browser_sign_in.py +212 -0
  344. ocaya/setup/auth/browser_sign_in_gateway.py +55 -0
  345. ocaya/setup/auth/http_browser_sign_in_gateway.py +355 -0
  346. ocaya/setup/onboarding/__init__.py +152 -0
  347. ocaya/setup/onboarding/base.py +14 -0
  348. ocaya/setup/onboarding/context.py +219 -0
  349. ocaya/setup/onboarding/gradient_text.py +30 -0
  350. ocaya/setup/onboarding/onboarding.tcss +453 -0
  351. ocaya/setup/onboarding/screens/__init__.py +15 -0
  352. ocaya/setup/onboarding/screens/api_key.py +135 -0
  353. ocaya/setup/onboarding/screens/auth_method.py +123 -0
  354. ocaya/setup/onboarding/screens/browser_sign_in.py +534 -0
  355. ocaya/setup/onboarding/screens/theme_selection.py +141 -0
  356. ocaya/setup/onboarding/screens/welcome.py +117 -0
  357. ocaya/setup/trusted_folders/trust_folder_dialog.py +306 -0
  358. ocaya/setup/trusted_folders/trust_folder_dialog.tcss +170 -0
  359. ocaya/whats_new.md +4 -0
  360. ocaya-2.14.0.dist-info/METADATA +573 -0
  361. ocaya-2.14.0.dist-info/RECORD +364 -0
  362. ocaya-2.14.0.dist-info/WHEEL +4 -0
  363. ocaya-2.14.0.dist-info/entry_points.txt +3 -0
  364. ocaya-2.14.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1 @@
1
+ {"specId": "c355ffb6-2295-4000-b342-f7d0e63324c7", "workflowType": "requirements-first", "specType": "feature"}
@@ -0,0 +1,464 @@
1
+ # Design Document: OCAYA
2
+
3
+ ## Overview
4
+
5
+ OCAYA is a rebrand and repurpose of the open-source `mistral-vibe` Python CLI. This is not a new architecture — it is a targeted transformation across three concerns:
6
+
7
+ 1. **Identity rename** — every package name, class name, path, environment variable, log filename, and TUI string that says "vibe" or "Mistral Vibe" becomes "ocaya" or "OCAYA".
8
+ 2. **Persona swap** — the default system prompt changes from a coding-focused instruction document (`cli.md`) to a web-first general-purpose instruction document (`ocaya.md`).
9
+ 3. **Tool enablement** — two already-drafted tools (`clipboard.py`, `open_app.py`) are confirmed as active built-ins and any rough edges in their implementation are cleaned up.
10
+
11
+ Mistral AI remains the sole LLM backend. Browser automation (Playwright, browser-use, Selenium) is explicitly out of scope.
12
+
13
+ ### Key Design Constraints
14
+
15
+ - No new architectural layers are introduced.
16
+ - The agent loop, session mechanics, MCP/ACP bridge, voice pipeline, skills system, and all existing tool logic are preserved verbatim.
17
+ - The `env_prefix="VIBE_"` in `OcayaConfig` is **preserved** — the requirement only mandates renaming `VIBE_HOME` (which is resolved outside of the pydantic-settings env prefix, in the paths module). Changing `env_prefix` would silently break all existing user `VIBE_*` config env vars; this change is intentionally deferred.
18
+ - The `clipboard.py` and `open_app.py` files already exist in the builtins directory and are auto-discovered by `ToolManager` — no explicit registration code is required beyond verifying the files are valid and their import paths are updated to `ocaya.*`.
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ The transformation is a layer-by-layer rename following dependency order. No new components are added; existing components are updated in place.
25
+
26
+ ```
27
+ ┌─────────────────────────────────────────────────────────┐
28
+ │ pyproject.toml (package name, scripts, tool config) │
29
+ ├─────────────────────────────────────────────────────────┤
30
+ │ ocaya/ (was: vibe/) │
31
+ │ ├── __init__.py OCAYA_ROOT, __version__ │
32
+ │ ├── acp/ ACP server (unchanged logic) │
33
+ │ ├── cli/ Textual TUI ← branding here │
34
+ │ └── core/ │
35
+ │ ├── agents/models.py VibeConfig ref → OcayaConfig│
36
+ │ ├── config/_settings.py VibeConfig → OcayaConfig │
37
+ │ ├── paths/_ocaya_home.py was: _vibe_home.py │
38
+ │ ├── prompts/ │
39
+ │ │ ├── __init__.py SystemPrompt.OCAYA added │
40
+ │ │ └── ocaya.md new default system prompt │
41
+ │ ├── tools/builtins/ │
42
+ │ │ ├── clipboard.py import path updated │
43
+ │ │ └── open_app.py import path updated │
44
+ │ └── skills/builtins/ocaya.py was: vibe.py │
45
+ ├─────────────────────────────────────────────────────────┤
46
+ │ tests/ (import paths updated: vibe. → ocaya.) │
47
+ └─────────────────────────────────────────────────────────┘
48
+ ```
49
+
50
+ ### Migration Order
51
+
52
+ Changes must be applied in this order to avoid import errors at any intermediate state:
53
+
54
+ 1. Rename `vibe/` → `ocaya/` (the physical directory rename)
55
+ 2. Update `ocaya/__init__.py` — rename `VIBE_ROOT` → `OCAYA_ROOT`
56
+ 3. Update `ocaya/core/paths/_vibe_home.py` → `_ocaya_home.py`
57
+ 4. Update `ocaya/core/prompts/__init__.py` — add `SystemPrompt.OCAYA`, update `PROMPTS_DIR`
58
+ 5. Create `ocaya/core/prompts/ocaya.md`
59
+ 6. Update `ocaya/core/config/_settings.py` — rename `VibeConfig` → `OcayaConfig`, update `system_prompt_id` default
60
+ 7. Update `ocaya/core/agents/models.py` — update all `VibeConfig` references
61
+ 8. Update all other `ocaya/**/*.py` files — fix import paths and string constants
62
+ 9. Update `ocaya/core/tools/builtins/clipboard.py` and `open_app.py` — fix import paths
63
+ 10. Update `ocaya/core/skills/builtins/vibe.py` → `ocaya.py` — content update
64
+ 11. Update TUI branding strings in `ocaya/cli/`
65
+ 12. Update `pyproject.toml`
66
+ 13. Update all `tests/**/*.py` files — fix import paths
67
+
68
+ ---
69
+
70
+ ## Components and Interfaces
71
+
72
+ ### pyproject.toml
73
+
74
+ | Field | Before | After |
75
+ |---|---|---|
76
+ | `[project] name` | `"mistral-vibe"` | `"ocaya"` |
77
+ | `[project] description` | `"Minimal CLI coding agent by Mistral"` | `"OCAYA — web-first general-purpose AI assistant"` |
78
+ | `[project] keywords` | includes `"coding-assistant"`, `"mistral-vibe"` | replace with `"ocaya"`, `"general-assistant"` |
79
+ | `[project.scripts] vibe` | `"vibe.cli.entrypoint:main"` | removed |
80
+ | `[project.scripts] ocaya` | (absent) | `"ocaya.cli.entrypoint:main"` |
81
+ | `[project.scripts] vibe-acp` | `"vibe.acp.entrypoint:main"` | removed |
82
+ | `[project.scripts] ocaya-acp` | (absent) | `"ocaya.acp.entrypoint:main"` |
83
+ | `[tool.hatch.build.targets.wheel] include` | `["vibe/"]` | `["ocaya/"]` |
84
+ | `[tool.pyright] include` | `["vibe/**/*.py", ...]` | `["ocaya/**/*.py", ...]` |
85
+ | `[tool.ruff] include` | `["vibe/**/*.py", ...]` | `["ocaya/**/*.py", ...]` |
86
+ | `[tool.ruff.lint.isort] known-first-party` | `["vibe"]` | `["ocaya"]` |
87
+
88
+ Project URLs pointing to GitHub repository path segments (e.g. `mistralai/mistral-vibe`) are left unchanged as they are not human-readable branding fields.
89
+
90
+ ### `ocaya/__init__.py`
91
+
92
+ ```python
93
+ OCAYA_ROOT = Path(__file__).parent
94
+ __version__ = "2.14.0"
95
+ ```
96
+
97
+ `VIBE_ROOT` is removed. All internal references to `VIBE_ROOT` become `OCAYA_ROOT`.
98
+
99
+ ### `ocaya/core/paths/_ocaya_home.py` (was `_vibe_home.py`)
100
+
101
+ Full replacement:
102
+
103
+ ```python
104
+ from __future__ import annotations
105
+
106
+ from collections.abc import Callable
107
+ import os
108
+ from pathlib import Path
109
+
110
+ from ocaya import OCAYA_ROOT
111
+
112
+
113
+ class GlobalPath:
114
+ def __init__(self, resolver: Callable[[], Path]) -> None:
115
+ self._resolver = resolver
116
+
117
+ @property
118
+ def path(self) -> Path:
119
+ return self._resolver()
120
+
121
+
122
+ _DEFAULT_OCAYA_HOME = Path.home() / ".ocaya"
123
+
124
+
125
+ def _get_ocaya_home() -> Path:
126
+ if ocaya_home := os.getenv("OCAYA_HOME"):
127
+ return Path(ocaya_home).expanduser().resolve()
128
+ return _DEFAULT_OCAYA_HOME
129
+
130
+
131
+ OCAYA_HOME = GlobalPath(_get_ocaya_home)
132
+ GLOBAL_ENV_FILE = GlobalPath(lambda: OCAYA_HOME.path / ".env")
133
+ SESSION_LOG_DIR = GlobalPath(lambda: OCAYA_HOME.path / "logs" / "session")
134
+ TRUSTED_FOLDERS_FILE = GlobalPath(lambda: OCAYA_HOME.path / "trusted_folders.toml")
135
+ LOG_DIR = GlobalPath(lambda: OCAYA_HOME.path / "logs")
136
+ LOG_FILE = GlobalPath(lambda: OCAYA_HOME.path / "logs" / "ocaya.log")
137
+ CACHE_FILE = GlobalPath(lambda: OCAYA_HOME.path / "cache.toml")
138
+ HISTORY_FILE = GlobalPath(lambda: OCAYA_HOME.path / "ocayahistory")
139
+ PLANS_DIR = GlobalPath(lambda: OCAYA_HOME.path / "plans")
140
+
141
+ DEFAULT_TOOL_DIR = GlobalPath(lambda: OCAYA_ROOT / "core" / "tools" / "builtins")
142
+ ```
143
+
144
+ The `paths/__init__.py` re-export must be updated: all exported names (`VIBE_HOME`, `LOG_FILE`, `HISTORY_FILE`, etc.) are renamed to match.
145
+
146
+ ### `ocaya/core/config/_settings.py`
147
+
148
+ Two targeted changes:
149
+
150
+ **1. Class rename:**
151
+ ```python
152
+ # Before
153
+ class VibeConfig(BaseSettings):
154
+ ...
155
+
156
+ # After
157
+ class OcayaConfig(BaseSettings):
158
+ ...
159
+ ```
160
+
161
+ **2. `system_prompt_id` default:**
162
+ ```python
163
+ # Before
164
+ system_prompt_id: str = SystemPrompt.CLI
165
+
166
+ # After
167
+ system_prompt_id: str = SystemPrompt.OCAYA
168
+ ```
169
+
170
+ **3. `env_prefix` stays as `"VIBE_"`** — preserves all existing user `VIBE_*` environment variable overrides. The requirement for renaming `VIBE_HOME` is handled entirely in the paths module (direct `os.getenv("OCAYA_HOME")` call), which is architecturally separate from pydantic-settings' env prefix mechanism.
171
+
172
+ **4. Internal model validators** referencing `VibeConfig` by name in type annotations (`-> VibeConfig`) are updated to `-> OcayaConfig`.
173
+
174
+ **5. `DEFAULT_VIBE_BASE_URL` constant:** The variable name `DEFAULT_VIBE_BASE_URL` and `vibe_base_url` field name are internal Python identifiers that map to an external Mistral service URL (`chat.mistral.ai`). Renaming these would require either a field alias or a user-visible config key change. Since the backing URL is a Mistral-owned endpoint and changing the config key would break existing `config.toml` files, these internal identifiers are renamed to `DEFAULT_OCAYA_BASE_URL` / `ocaya_base_url` with a `validation_alias` to accept the legacy `vibe_base_url` key from existing config files.
175
+
176
+ ### `ocaya/core/prompts/__init__.py`
177
+
178
+ ```python
179
+ class SystemPrompt(Prompt):
180
+ OCAYA = auto() # new — points to ocaya.md
181
+ CLI = auto() # kept for backward compatibility (custom system_prompt_id="cli")
182
+ EXPLORE = auto()
183
+ TESTS = auto()
184
+ LEAN = auto()
185
+ MINIMAL = auto()
186
+ ```
187
+
188
+ `OCAYA` is added as the first member so it occupies the `"ocaya"` value. `CLI` is kept so that any user who has `system_prompt_id = "cli"` in their `config.toml` continues to work.
189
+
190
+ `PROMPTS_DIR` changes from `VIBE_ROOT / "core" / "prompts"` to `OCAYA_ROOT / "core" / "prompts"`.
191
+
192
+ ### `ocaya/core/agents/models.py`
193
+
194
+ - All `TYPE_CHECKING` imports of `VibeConfig` → `OcayaConfig`.
195
+ - The `apply_to_config` method signature: `base: OcayaConfig` and local import `from ocaya.core.config import OcayaConfig as OC`.
196
+ - The `EXPLORE` agent's `overrides` already uses `"system_prompt_id": "explore"` — no change needed there.
197
+ - All `from vibe.*` imports → `from ocaya.*`.
198
+
199
+ ### `ocaya/core/tools/builtins/clipboard.py` and `open_app.py`
200
+
201
+ Both files exist and are functionally complete. The only required change is updating all `from vibe.core.*` imports to `from ocaya.core.*`. No logic changes.
202
+
203
+ The `ToolManager` auto-discovers all non-underscore-prefixed `.py` files under `DEFAULT_TOOL_DIR` (which now resolves to `ocaya/core/tools/builtins/`). Both files are already there — no registration code change is needed.
204
+
205
+ The `_compute_module_name` function in `manager.py` uses `parts.index("vibe")` to detect canonical module names. After renaming, this must be updated to detect `"ocaya"` instead.
206
+
207
+ ### `ocaya/core/skills/builtins/ocaya.py` (was `vibe.py`)
208
+
209
+ The file is renamed and the `SkillInfo` content is updated:
210
+
211
+ ```python
212
+ SKILL = SkillInfo(
213
+ name="ocaya",
214
+ description="Understand the OCAYA application internals: configuration, OCAYA_HOME structure, ...",
215
+ user_invocable=False,
216
+ prompt="""# OCAYA Self-Awareness
217
+ ...
218
+ """,
219
+ )
220
+ ```
221
+
222
+ All occurrences of `~/.vibe`, `VIBE_HOME`, `vibe.log`, `vibehistory`, `Mistral Vibe`, and `vibe` (as product name) in the skill prompt are updated to their OCAYA equivalents. The `.vibe/` project-local config directory references become `.ocaya/`.
223
+
224
+ ### TUI Branding (`ocaya/cli/`)
225
+
226
+ The Textual TUI uses string constants scattered across several widgets. The following surfaces must be audited and updated:
227
+
228
+ | Surface | Location | Change |
229
+ |---|---|---|
230
+ | App title | `App` CSS / `TITLE` class var | `"Mistral Vibe"` → `"OCAYA"` |
231
+ | Startup splash/banner | `WelcomeWidget` or equivalent | Remove ASCII art; replace with `"OCAYA"` heading |
232
+ | Status bar agent name | `StatusBar` widget | Label text → `"OCAYA"` |
233
+ | Help panel heading | `HelpPanel` widget | `"Mistral Vibe Help"` → `"OCAYA Help"` |
234
+ | Error dialogs | Any `ModalScreen` / `Dialog` with branding | Replace legacy branding with `"OCAYA"` |
235
+ | Version string | `--version` flag handler, startup banner | Format: `f"OCAYA {__version__}"` |
236
+
237
+ No CSS color variables, layout, or animation logic is changed.
238
+
239
+ ### `tests/` directory
240
+
241
+ Every test file that imports from `vibe.*` is updated to import from `ocaya.*`. The `tests/conftest.py` autouse fixtures (`config_dir`, `tmp_working_directory`) that reference `VIBE_HOME` / `~/.vibe` are updated to reference `OCAYA_HOME` / `~/.ocaya`.
242
+
243
+ ---
244
+
245
+ ## Data Models
246
+
247
+ ### `OcayaConfig` (was `VibeConfig`)
248
+
249
+ ```python
250
+ class OcayaConfig(BaseSettings):
251
+ system_prompt_id: str = SystemPrompt.OCAYA # was: SystemPrompt.CLI = "cli"
252
+ # All other fields: unchanged
253
+ model_config = SettingsConfigDict(
254
+ env_prefix="VIBE_", # preserved — see rationale above
255
+ case_sensitive=False,
256
+ extra="ignore",
257
+ )
258
+ ```
259
+
260
+ No fields are added or removed. The only data model change is the `system_prompt_id` default.
261
+
262
+ ### `SystemPrompt` enum
263
+
264
+ ```python
265
+ class SystemPrompt(Prompt):
266
+ OCAYA = auto() # value: "ocaya" — new default
267
+ CLI = auto() # value: "cli" — preserved for compatibility
268
+ EXPLORE = auto()
269
+ TESTS = auto()
270
+ LEAN = auto()
271
+ MINIMAL = auto()
272
+ ```
273
+
274
+ The `OCAYA` member must be ordered first so its `auto()` value is `"ocaya"`. Because `StrEnum` with `auto()` derives the string value from the member name lowercased, member order relative to each other does not matter for value correctness — what matters is the name.
275
+
276
+ ### Path constants
277
+
278
+ | Old constant | New constant | Old value | New value |
279
+ |---|---|---|---|
280
+ | `VIBE_HOME` | `OCAYA_HOME` | `~/.vibe` | `~/.ocaya` |
281
+ | `LOG_FILE` | `LOG_FILE` | `~/.vibe/logs/vibe.log` | `~/.ocaya/logs/ocaya.log` |
282
+ | `HISTORY_FILE` | `HISTORY_FILE` | `~/.vibe/vibehistory` | `~/.ocaya/ocayahistory` |
283
+ | `VIBE_ROOT` | `OCAYA_ROOT` | `Path(__file__).parent` | `Path(__file__).parent` |
284
+
285
+ ---
286
+
287
+ ## System Prompt Design (`ocaya/core/prompts/ocaya.md`)
288
+
289
+ The `ocaya.md` file replaces `cli.md` as the default system prompt. It must cover all seven required areas. The structure below is the authoritative outline:
290
+
291
+ ### Required Sections
292
+
293
+ **1. Identity**
294
+ ```
295
+ You are OCAYA, a capable general-purpose AI assistant. Today's date is $current_date.
296
+ ```
297
+ Must not contain "Mistral Vibe", "Jarvis", or "Hermes". Must contain "OCAYA".
298
+
299
+ **2. Capabilities**
300
+ Explicit list (per Requirements 5.5, 9.4):
301
+ - Web research and summarisation
302
+ - Document writing and editing
303
+ - File and folder management
304
+ - System automation and scripting
305
+ - Opening applications and URLs
306
+ - Clipboard operations
307
+ - General knowledge Q&A
308
+
309
+ This section must NOT restrict the agent to coding tasks (per Requirement 9.3).
310
+
311
+ **3. Web-First Instructions**
312
+ Explicit instruction (per Requirements 5.6, 6.1, 6.3):
313
+ - Before answering any query involving current events, live prices, external documentation, news, or user-provided URLs, invoke `websearch` or `webfetch` first — never rely on training-data memory alone for these categories.
314
+ - When a user message contains a URL, invoke `webfetch` with that URL before responding.
315
+ - Include at least one source citation (title + URL) whenever information was obtained via `websearch`.
316
+
317
+ **4. Action-First Principle**
318
+ Instruction (per Requirement 5.4):
319
+ - Take action first, explain after. Do not ask clarifying questions when the task is clear enough to attempt.
320
+ - For file operations, scripting, or software installation, use the `bash` tool to perform the operation rather than only describing it.
321
+
322
+ **5. Hard Limits**
323
+ Explicit prohibitions (per Requirement 5.7):
324
+ - Do not run `git commit`, `git push`, or any remote-push variant without explicit user instruction.
325
+ - Do not delete files without asking for confirmation.
326
+ - Do not send emails, post to social media, or initiate financial transactions without explicit user instruction.
327
+ - Before executing any shell command with blast radius outside the current machine, state the risk in one sentence and wait for confirmation.
328
+
329
+ **6. Tone**
330
+ Instruction (per Requirement 5.4):
331
+ - Be direct and warm. Skip filler phrases: no "Certainly", "Of course", "Happy to", "Great question".
332
+ - Keep responses focused. Code and structured output before prose.
333
+
334
+ **7. Source Citation**
335
+ Instruction (per Requirement 6.3):
336
+ - Whenever information was retrieved via `websearch`, include at least one inline citation with the source title and URL.
337
+
338
+ ---
339
+
340
+ ## Correctness Properties
341
+
342
+ *A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
343
+
344
+ Most of this feature's changes are identity/rename operations verified by static assertions or example-based tests. Three areas yield genuine universal properties suitable for property-based testing.
345
+
346
+ ### Property 1: OCAYA_HOME Path Resolution
347
+
348
+ *For any* non-empty string `p`, when the `OCAYA_HOME` environment variable is set to `p`, calling `_get_ocaya_home()` should return `Path(p).expanduser().resolve()` — that is, the path is always fully normalized regardless of the form of the input string (relative, absolute, `~`-prefixed, with redundant separators, etc.).
349
+ **Validates: Requirements 2.2**
350
+
351
+ ### Property 2: Version String Prefix
352
+
353
+ *For any* valid version string `v` (e.g. `"2.14.0"`, `"3.0.0-alpha"`, `"1.0"`), the formatted version display string should start with `"OCAYA "` followed by `v`, regardless of the version value.
354
+ **Validates: Requirements 4.3**
355
+
356
+ ### Property 3: OpenApp Rejects All Blank Targets
357
+
358
+ *For any* string composed entirely of whitespace characters (including the empty string, strings of spaces, tabs, newlines, or any combination), invoking `OpenApp.run` with that string as the `target` argument should raise a `ToolError` with the message `"target cannot be empty"`.
359
+ **Validates: Requirements 8.6**
360
+
361
+ ---
362
+
363
+ ## Error Handling
364
+
365
+ ### Home Directory Resolution
366
+
367
+ - If `OCAYA_HOME` is set to an empty string: `os.getenv("OCAYA_HOME")` returns `""` which is falsy; the walrus operator branch is not taken and the default `~/.ocaya` is used. No warning is required for the empty-string case because the env var being unset is semantically equivalent.
368
+ - If `OCAYA_HOME` points to a non-existent but resolvable path: `Path(...).expanduser().resolve()` succeeds; the directory is created on first access by the setup layer.
369
+ - If `OCAYA_HOME` is set to an unresolvable path (e.g. contains null bytes): `Path()` construction raises `ValueError`; this propagates as a startup error with a message printed to stderr.
370
+
371
+ ### Prompt Loading Fallback
372
+
373
+ `load_system_prompt` already implements a fallback chain: custom project/user prompt dirs → built-in enum prompt → fallback `.md` file in `PROMPTS_DIR`. If a user has a custom `system_prompt_id` that resolves to a missing file, `MissingPromptFileError` is raised. Requirement 5.8 specifies a softer fallback (fall back to `ocaya.md`, log warning, continue). This requires wrapping the `load_system_prompt` call in the config property:
374
+
375
+ ```python
376
+ @property
377
+ def system_prompt(self) -> str:
378
+ try:
379
+ return load_system_prompt(self.system_prompt_id)
380
+ except MissingPromptFileError:
381
+ logger.warning(
382
+ "system_prompt_id=%s not found; falling back to ocaya",
383
+ self.system_prompt_id,
384
+ )
385
+ return load_system_prompt(SystemPrompt.OCAYA)
386
+ ```
387
+
388
+ ### Tool Discovery After Rename
389
+
390
+ `ToolManager._compute_module_name` uses `parts.index("vibe")` to detect canonical module names for deduplication. After the package rename this must become `parts.index("ocaya")`. If it is left as `"vibe"`, every builtin tool gets a hash-based synthetic module name instead of its canonical `ocaya.core.tools.builtins.*` name, causing Pydantic class identity mismatches (two copies of `ClipboardArgs` etc.). This is a subtle correctness issue that must be fixed as part of the rename.
391
+
392
+ ### Clipboard and OpenApp on Linux Without Desktop Utilities
393
+
394
+ Both tools have explicit `ToolError` paths for missing `xclip`/`xsel` (clipboard) and `xdg-open`/`gio` (open_app). These are already implemented in the drafted files — no additional error handling is needed. The error messages already name the missing utility and provide the `apt install` instruction as required by Requirements 7.4 and 8.7.
395
+
396
+ ---
397
+
398
+ ## Testing Strategy
399
+
400
+ ### Unit Tests (example-based)
401
+
402
+ Focus on concrete behavioral assertions:
403
+
404
+ - `test_ocaya_home_default`: assert `_get_ocaya_home()` with no env var returns `Path.home() / ".ocaya"`.
405
+ - `test_ocaya_config_defaults`: assert `OcayaConfig()` has `system_prompt_id == "ocaya"`.
406
+ - `test_system_prompt_loads_ocaya_md`: assert `OcayaConfig().system_prompt` contains `"OCAYA"` and does not contain `"Mistral Vibe"`.
407
+ - `test_system_prompt_fallback`: assert that a missing custom `system_prompt_id` falls back to `ocaya.md` and logs a warning.
408
+ - `test_clipboard_config_permission`: assert `ClipboardConfig().permission == ToolPermission.ASK`.
409
+ - `test_open_app_config_permission`: assert `OpenAppConfig().permission == ToolPermission.ASK`.
410
+ - `test_clipboard_read_empty`: mock `_read_clipboard` to return `""`; assert result is `ClipboardResult(action="read", text="")`.
411
+ - `test_open_app_linux_no_opener`: mock platform as `"linux"`, mock all subprocess calls to raise `FileNotFoundError`; assert `ToolError` with message naming `xdg-open`.
412
+ - `test_ocaya_md_contains_required_sections`: read `ocaya.md`; assert presence of keywords for each of the 7 required sections.
413
+ - `test_pyproject_no_legacy_branding`: parse `pyproject.toml`; assert name, description, keywords, scripts contain no `"mistral-vibe"`, `"Mistral Vibe"`, or `"vibe"` (as a standalone word in user-facing fields).
414
+ - `test_no_browser_automation_deps`: parse `pyproject.toml` dependencies; assert none match `playwright|browser-use|selenium|puppeteer|pyppeteer`.
415
+
416
+ ### Property-Based Tests
417
+
418
+ Use `hypothesis` (already a transitive dev dependency via `pytest`). Each test runs minimum 100 examples.
419
+
420
+ **Property 1: OCAYA_HOME path resolution**
421
+ ```python
422
+ # Feature: ocaya, Property 1: OCAYA_HOME path resolution
423
+ @given(st.text(min_size=1).filter(lambda s: s.strip()))
424
+ @settings(max_examples=100)
425
+ def test_ocaya_home_resolution(tmp_path, path_str):
426
+ resolved = Path(path_str).expanduser().resolve()
427
+ with mock.patch.dict(os.environ, {"OCAYA_HOME": path_str}):
428
+ assert _get_ocaya_home() == resolved
429
+ ```
430
+
431
+ **Property 2: Version string prefix**
432
+ ```python
433
+ # Feature: ocaya, Property 2: Version string prefix
434
+ @given(st.from_regex(r"[0-9]+\.[0-9]+\.[0-9]+(\.[a-z0-9]+)?", fullmatch=True))
435
+ @settings(max_examples=100)
436
+ def test_version_string_prefix(version_str):
437
+ result = format_version_string(version_str)
438
+ assert result.startswith("OCAYA ")
439
+ assert version_str in result
440
+ ```
441
+
442
+ **Property 3: OpenApp rejects all blank targets**
443
+ ```python
444
+ # Feature: ocaya, Property 3: OpenApp rejects all blank targets
445
+ @given(st.text(alphabet=st.characters(whitelist_categories=("Zs", "Cc"))))
446
+ @settings(max_examples=100)
447
+ async def test_open_app_blank_target_raises(whitespace_str):
448
+ tool = OpenApp.from_config(lambda: OpenAppConfig())
449
+ args = OpenAppArgs(target=whitespace_str)
450
+ with pytest.raises(ToolError, match="target cannot be empty"):
451
+ async for _ in tool.run(args):
452
+ pass
453
+ ```
454
+
455
+ ### Integration Tests
456
+
457
+ - Verify `ToolManager` discovers `clipboard` and `open_app` in `available_tools` after rename (real filesystem, no mocks).
458
+ - Verify the ACP server starts and returns a valid `initialized` response via `ocaya-acp`.
459
+ - Verify all built-in agent profiles load without error from `OcayaConfig` and produce correct tool permission sets.
460
+ - Verify session resume (`--resume`) works with sessions stored under `~/.ocaya`.
461
+
462
+ ### Snapshot Tests
463
+
464
+ Existing `pytest-textual-snapshot` tests for TUI components are updated: expected snapshots are regenerated after branding changes to capture the new "OCAYA" labels.