manigent-server 0.1.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 (1004) hide show
  1. manigent/_catalog/mcps/chess/README.md +38 -0
  2. manigent/_catalog/mcps/chess/mcp.json +9 -0
  3. manigent/_catalog/mcps/registry.json +9 -0
  4. manigent/_catalog/skills/brief/SKILL.md +339 -0
  5. manigent/_catalog/skills/broaden-options/SKILL.md +133 -0
  6. manigent/_catalog/skills/calendar/SKILL.md +44 -0
  7. manigent/_catalog/skills/calendar/starter_config.jsonc +25 -0
  8. manigent/_catalog/skills/gmail/SKILL.md +73 -0
  9. manigent/_catalog/skills/google-calendar/SKILL.md +44 -0
  10. manigent/_catalog/skills/google-drive/SKILL.md +42 -0
  11. manigent/_catalog/skills/google-drive-write/SKILL.md +45 -0
  12. manigent/_catalog/skills/google-drive-write/starter_config.jsonc +45 -0
  13. manigent/_catalog/skills/grill-me/SKILL.md +173 -0
  14. manigent/_catalog/skills/homekit/SKILL.md +37 -0
  15. manigent/_catalog/skills/http-api/SKILL.md +117 -0
  16. manigent/_catalog/skills/ib/SKILL.md +89 -0
  17. manigent/_catalog/skills/ib/starter_config.jsonc +35 -0
  18. manigent/_catalog/skills/mailbox/SKILL.md +47 -0
  19. manigent/_catalog/skills/mailbox/starter_config.jsonc +38 -0
  20. manigent/_catalog/skills/media/SKILL.md +93 -0
  21. manigent/_catalog/skills/media/starter_config.jsonc +63 -0
  22. manigent/_catalog/skills/osxphotos/SKILL.md +41 -0
  23. manigent/_catalog/skills/photo-story/SKILL.md +285 -0
  24. manigent/_catalog/skills/playwright-browser/SKILL.md +291 -0
  25. manigent/_catalog/skills/playwright-browser/starter_config.jsonc +44 -0
  26. manigent/_catalog/skills/playwright-save-skill/SKILL.md +149 -0
  27. manigent/_catalog/skills/registry.json +166 -0
  28. manigent/_catalog/skills/web-artifacts/SKILL.md +194 -0
  29. manigent/api/action_executor.py +404 -0
  30. manigent/api/action_validator.py +531 -0
  31. manigent/api/actions.py +221 -0
  32. manigent/api/client.py +558 -0
  33. manigent/api/control.py +806 -0
  34. manigent/api/edge_protocol.py +123 -0
  35. manigent/api/fs_protocol.py +450 -0
  36. manigent/api/host_protocol.py +298 -0
  37. manigent/api/managed_job_client.py +124 -0
  38. manigent/api/responses.py +342 -0
  39. manigent/api/retry_transport.py +178 -0
  40. manigent/api/turn_protocol.py +78 -0
  41. manigent/cli_lifecycle.py +467 -0
  42. manigent/cli_server.py +978 -0
  43. manigent/doctor.py +1167 -0
  44. manigent/hub_protocol/canonical.py +375 -0
  45. manigent/hub_protocol/codes.py +171 -0
  46. manigent/hub_protocol/constants.py +71 -0
  47. manigent/hub_protocol/envelope.py +218 -0
  48. manigent/hub_protocol/frames.py +788 -0
  49. manigent/hub_protocol/http.py +260 -0
  50. manigent/hub_protocol/ids.py +170 -0
  51. manigent/hub_protocol/keys.py +195 -0
  52. manigent/hub_protocol/text.py +75 -0
  53. manigent/llm/base.py +1072 -0
  54. manigent/llm/prompt_builder.py +2450 -0
  55. manigent/llm/triggers.py +68 -0
  56. manigent/models/agent.py +2905 -0
  57. manigent/models/agent_bundle.py +298 -0
  58. manigent/models/brief_tab.py +311 -0
  59. manigent/models/chat_message.py +231 -0
  60. manigent/models/chatroom.py +1108 -0
  61. manigent/models/context.py +1159 -0
  62. manigent/models/desk_label.py +836 -0
  63. manigent/models/desk_read_state.py +220 -0
  64. manigent/models/desk_sign_off.py +267 -0
  65. manigent/models/desk_sop.py +965 -0
  66. manigent/models/desk_todo.py +1603 -0
  67. manigent/models/desk_todo_link.py +404 -0
  68. manigent/models/file_cache.py +75 -0
  69. manigent/models/host.py +732 -0
  70. manigent/models/install_token.py +401 -0
  71. manigent/models/invitation_code.py +162 -0
  72. manigent/models/knowledge_pack.py +552 -0
  73. manigent/models/managed_job.py +295 -0
  74. manigent/models/model_config.py +564 -0
  75. manigent/models/model_defaults.py +238 -0
  76. manigent/models/onboarding_plan.py +161 -0
  77. manigent/models/participant.py +449 -0
  78. manigent/models/persistable.py +892 -0
  79. manigent/models/plan_markdown.py +1774 -0
  80. manigent/models/project.py +1343 -0
  81. manigent/models/project_plan.py +8104 -0
  82. manigent/models/project_report.py +145 -0
  83. manigent/models/provenance.py +261 -0
  84. manigent/models/provider_agents.py +101 -0
  85. manigent/models/public_request.py +108 -0
  86. manigent/models/reflection_schedule.py +191 -0
  87. manigent/models/scheduled_message.py +285 -0
  88. manigent/models/share_token.py +190 -0
  89. manigent/models/skill_version.py +31 -0
  90. manigent/models/sop_template.py +187 -0
  91. manigent/models/thinking_partner_plan.py +187 -0
  92. manigent/models/tunnel_binding.py +129 -0
  93. manigent/models/user.py +1276 -0
  94. manigent/models/work_tracker.py +510 -0
  95. manigent/runner/invocation_registry.py +187 -0
  96. manigent/server/agent_capabilities.py +119 -0
  97. manigent/server/agent_visibility.py +119 -0
  98. manigent/server/announcements.py +119 -0
  99. manigent/server/app.py +807 -0
  100. manigent/server/auth.py +355 -0
  101. manigent/server/batch_timeout.py +94 -0
  102. manigent/server/channels/access.py +194 -0
  103. manigent/server/channels/adapter.py +295 -0
  104. manigent/server/channels/config.py +693 -0
  105. manigent/server/channels/contacts.py +39 -0
  106. manigent/server/channels/email_adapter.py +436 -0
  107. manigent/server/channels/email_address.py +145 -0
  108. manigent/server/channels/email_form.py +242 -0
  109. manigent/server/channels/email_format.py +285 -0
  110. manigent/server/channels/email_mail.py +427 -0
  111. manigent/server/channels/email_mailgun.py +164 -0
  112. manigent/server/channels/email_text.py +280 -0
  113. manigent/server/channels/file_links.py +127 -0
  114. manigent/server/channels/gateway.py +2998 -0
  115. manigent/server/channels/hub_delivery.py +211 -0
  116. manigent/server/channels/hub_store.py +1730 -0
  117. manigent/server/channels/line.py +645 -0
  118. manigent/server/channels/line_format.py +589 -0
  119. manigent/server/channels/log_redaction.py +145 -0
  120. manigent/server/channels/store.py +3316 -0
  121. manigent/server/channels/telegram.py +422 -0
  122. manigent/server/channels/telegram_format.py +312 -0
  123. manigent/server/channels/webchat.py +261 -0
  124. manigent/server/channels/webchat_widget.js +1310 -0
  125. manigent/server/channels/whatsapp.py +707 -0
  126. manigent/server/channels/whatsapp_format.py +511 -0
  127. manigent/server/chat_search_index.py +1203 -0
  128. manigent/server/config.py +61 -0
  129. manigent/server/context.py +121 -0
  130. manigent/server/coordinator_rule.py +115 -0
  131. manigent/server/desk_todo_notify.py +300 -0
  132. manigent/server/edge_hub.py +577 -0
  133. manigent/server/email.py +354 -0
  134. manigent/server/errors.py +83 -0
  135. manigent/server/home_fs_relay.py +392 -0
  136. manigent/server/hub/adapter.py +1395 -0
  137. manigent/server/hub/agent_send_guard.py +196 -0
  138. manigent/server/hub/avatar.py +342 -0
  139. manigent/server/hub/browser.py +778 -0
  140. manigent/server/hub/client.py +859 -0
  141. manigent/server/hub/demand.py +49 -0
  142. manigent/server/hub/directory.py +385 -0
  143. manigent/server/hub/enroll.py +400 -0
  144. manigent/server/hub/http.py +134 -0
  145. manigent/server/hub/identity.py +61 -0
  146. manigent/server/hub/keys.py +588 -0
  147. manigent/server/hub/mirror.py +802 -0
  148. manigent/server/hub/operator.py +817 -0
  149. manigent/server/hub/retain.py +361 -0
  150. manigent/server/hub/room_bridge.py +741 -0
  151. manigent/server/hub/runtime.py +691 -0
  152. manigent/server/hub/trust.py +182 -0
  153. manigent/server/hub/views.py +316 -0
  154. manigent/server/idempotency.py +205 -0
  155. manigent/server/in_flight_restore.py +181 -0
  156. manigent/server/inspection.py +816 -0
  157. manigent/server/isolation_gate.py +516 -0
  158. manigent/server/isolation_notice.py +310 -0
  159. manigent/server/managed_job_events.py +242 -0
  160. manigent/server/mcp_registry.py +118 -0
  161. manigent/server/message_forward.py +78 -0
  162. manigent/server/message_scheduler.py +176 -0
  163. manigent/server/notification_assets.py +164 -0
  164. manigent/server/oauth_login/cli_state.py +120 -0
  165. manigent/server/oauth_login/config.py +90 -0
  166. manigent/server/oauth_login/errors.py +78 -0
  167. manigent/server/oauth_login/flow.py +223 -0
  168. manigent/server/oauth_login/handoff.py +63 -0
  169. manigent/server/oauth_login/providers.py +45 -0
  170. manigent/server/oauth_state.py +81 -0
  171. manigent/server/owner_isolation.py +466 -0
  172. manigent/server/participant_liveness.py +82 -0
  173. manigent/server/public_profile.py +149 -0
  174. manigent/server/public_requests/candidates.py +116 -0
  175. manigent/server/public_requests/dispatch.py +121 -0
  176. manigent/server/public_requests/ranking.py +153 -0
  177. manigent/server/recent_activity.py +250 -0
  178. manigent/server/reflection_completion_subscriber.py +151 -0
  179. manigent/server/reflection_scheduler.py +239 -0
  180. manigent/server/requester_key_migration.py +261 -0
  181. manigent/server/room_flow.py +139 -0
  182. manigent/server/routes/_batch.py +218 -0
  183. manigent/server/routes/_fd_routing.py +155 -0
  184. manigent/server/routes/_mentions.py +70 -0
  185. manigent/server/routes/agent_env.py +188 -0
  186. manigent/server/routes/agent_fs.py +450 -0
  187. manigent/server/routes/agents.py +1206 -0
  188. manigent/server/routes/announcements.py +79 -0
  189. manigent/server/routes/auth.py +387 -0
  190. manigent/server/routes/brief_tabs.py +404 -0
  191. manigent/server/routes/changelog.py +237 -0
  192. manigent/server/routes/channels.py +993 -0
  193. manigent/server/routes/chatrooms.py +871 -0
  194. manigent/server/routes/desk_labels.py +298 -0
  195. manigent/server/routes/desk_latest_messages.py +116 -0
  196. manigent/server/routes/desk_read_state.py +110 -0
  197. manigent/server/routes/desk_sign_off.py +88 -0
  198. manigent/server/routes/desk_sops.py +216 -0
  199. manigent/server/routes/desk_todos.py +692 -0
  200. manigent/server/routes/desk_tour.py +44 -0
  201. manigent/server/routes/files.py +572 -0
  202. manigent/server/routes/hosts.py +484 -0
  203. manigent/server/routes/hub.py +534 -0
  204. manigent/server/routes/hub_channels.py +234 -0
  205. manigent/server/routes/inbox.py +581 -0
  206. manigent/server/routes/install.ps1 +436 -0
  207. manigent/server/routes/install.py +324 -0
  208. manigent/server/routes/install.sh +566 -0
  209. manigent/server/routes/isolation_check.py +145 -0
  210. manigent/server/routes/knowledge_packs.py +477 -0
  211. manigent/server/routes/managed_jobs.py +196 -0
  212. manigent/server/routes/mcps.py +271 -0
  213. manigent/server/routes/messages.py +1972 -0
  214. manigent/server/routes/model_defaults.py +279 -0
  215. manigent/server/routes/oauth.py +237 -0
  216. manigent/server/routes/oauth_login.py +351 -0
  217. manigent/server/routes/owner_isolation.py +91 -0
  218. manigent/server/routes/project_plans.py +1521 -0
  219. manigent/server/routes/project_reports.py +235 -0
  220. manigent/server/routes/projects.py +2047 -0
  221. manigent/server/routes/public_agents.py +151 -0
  222. manigent/server/routes/public_requests.py +181 -0
  223. manigent/server/routes/reflection.py +93 -0
  224. manigent/server/routes/scheduled_messages.py +263 -0
  225. manigent/server/routes/search.py +94 -0
  226. manigent/server/routes/sharing.py +156 -0
  227. manigent/server/routes/skills.py +406 -0
  228. manigent/server/routes/templates.py +219 -0
  229. manigent/server/routes/turn_tokens.py +119 -0
  230. manigent/server/routes/webchat.py +406 -0
  231. manigent/server/routes/websocket.py +431 -0
  232. manigent/server/serving_only.py +200 -0
  233. manigent/server/signup_policy.py +120 -0
  234. manigent/server/skill_registry.py +164 -0
  235. manigent/server/socket_events.py +858 -0
  236. manigent/server/timeout_checker.py +446 -0
  237. manigent/server/tunnel_subscriber.py +1000 -0
  238. manigent/server/turn_tokens.py +285 -0
  239. manigent/server/ws_hub.py +556 -0
  240. manigent/server_cli.py +31 -0
  241. manigent/sync/changelog.py +885 -0
  242. manigent/sync/console_subscriber.py +222 -0
  243. manigent/sync/runloop.py +619 -0
  244. manigent/sync/runloop_manager.py +197 -0
  245. manigent/sync/subscriber.py +98 -0
  246. manigent/utils/agent_processes.py +276 -0
  247. manigent/utils/agent_storage.py +125 -0
  248. manigent/utils/email.py +563 -0
  249. manigent/utils/env_store.py +174 -0
  250. manigent/utils/extra_dirs.py +85 -0
  251. manigent/utils/file_io.py +485 -0
  252. manigent/utils/invoke_timeout.py +87 -0
  253. manigent/utils/jsonc.py +78 -0
  254. manigent/utils/knowledge_index.py +148 -0
  255. manigent/utils/nofollow_fs.py +279 -0
  256. manigent/utils/notification_center.py +67 -0
  257. manigent/utils/version.py +57 -0
  258. manigent/web/frontend/.design-sync-entry.tsx +74 -0
  259. manigent/web/frontend/.design-sync-provider.tsx +46 -0
  260. manigent/web/frontend/.eslintrc.cjs +183 -0
  261. manigent/web/frontend/.gitignore +6 -0
  262. manigent/web/frontend/app/page.tsx +39 -0
  263. manigent/web/frontend/hub-terms.html +192 -0
  264. manigent/web/frontend/index.html +15 -0
  265. manigent/web/frontend/landing.html +15 -0
  266. manigent/web/frontend/package-lock.json +8028 -0
  267. manigent/web/frontend/package.json +55 -0
  268. manigent/web/frontend/postcss.config.js +6 -0
  269. manigent/web/frontend/privacy.html +217 -0
  270. manigent/web/frontend/private-beta-announcement.html +19 -0
  271. manigent/web/frontend/public/apple-touch-icon.png +0 -0
  272. manigent/web/frontend/public/brand-hero-banner.png +0 -0
  273. manigent/web/frontend/public/brand-hero.png +0 -0
  274. manigent/web/frontend/public/favicon.png +0 -0
  275. manigent/web/frontend/public/favicon.svg +8 -0
  276. manigent/web/frontend/public/icon-transparent-sm.png +0 -0
  277. manigent/web/frontend/public/icon.png +0 -0
  278. manigent/web/frontend/public/manigent-mark.svg +7 -0
  279. manigent/web/frontend/public/vite.svg +5 -0
  280. manigent/web/frontend/public.html +15 -0
  281. manigent/web/frontend/src/App.activeAccountRedirect.test.tsx +236 -0
  282. manigent/web/frontend/src/App.hubHostDm.test.tsx +119 -0
  283. manigent/web/frontend/src/App.hubHostProject.test.tsx +142 -0
  284. manigent/web/frontend/src/App.hubRoutes.test.tsx +187 -0
  285. manigent/web/frontend/src/App.oauthAddCallback.test.tsx +263 -0
  286. manigent/web/frontend/src/App.tsx +246 -0
  287. manigent/web/frontend/src/api/attachments.ts +32 -0
  288. manigent/web/frontend/src/api/batchLoader.test.ts +212 -0
  289. manigent/web/frontend/src/api/batchLoader.ts +124 -0
  290. manigent/web/frontend/src/api/chatroomsBatch.test.tsx +392 -0
  291. manigent/web/frontend/src/api/client.ts +321 -0
  292. manigent/web/frontend/src/api/crossAccountAuth.test.ts +388 -0
  293. manigent/web/frontend/src/api/endpoints/agentEnv.ts +24 -0
  294. manigent/web/frontend/src/api/endpoints/agentFs.ts +125 -0
  295. manigent/web/frontend/src/api/endpoints/agentSearch.ts +19 -0
  296. manigent/web/frontend/src/api/endpoints/agents.ts +66 -0
  297. manigent/web/frontend/src/api/endpoints/announcements.ts +26 -0
  298. manigent/web/frontend/src/api/endpoints/auth.ts +361 -0
  299. manigent/web/frontend/src/api/endpoints/authLogin.test.ts +236 -0
  300. manigent/web/frontend/src/api/endpoints/batch.ts +64 -0
  301. manigent/web/frontend/src/api/endpoints/briefTabs.ts +81 -0
  302. manigent/web/frontend/src/api/endpoints/channels.ts +285 -0
  303. manigent/web/frontend/src/api/endpoints/computers.ts +212 -0
  304. manigent/web/frontend/src/api/endpoints/deskLabels.ts +151 -0
  305. manigent/web/frontend/src/api/endpoints/deskLatestMessages.ts +23 -0
  306. manigent/web/frontend/src/api/endpoints/deskReadState.ts +41 -0
  307. manigent/web/frontend/src/api/endpoints/deskSignOffs.ts +74 -0
  308. manigent/web/frontend/src/api/endpoints/deskSops.ts +53 -0
  309. manigent/web/frontend/src/api/endpoints/deskTodoAttachments.ts +69 -0
  310. manigent/web/frontend/src/api/endpoints/deskTodoProjects.test.ts +99 -0
  311. manigent/web/frontend/src/api/endpoints/deskTodoProjects.ts +114 -0
  312. manigent/web/frontend/src/api/endpoints/deskTodos.ts +164 -0
  313. manigent/web/frontend/src/api/endpoints/deskTour.ts +16 -0
  314. manigent/web/frontend/src/api/endpoints/dm.ts +137 -0
  315. manigent/web/frontend/src/api/endpoints/files.ts +55 -0
  316. manigent/web/frontend/src/api/endpoints/hub.contract.test.ts +94 -0
  317. manigent/web/frontend/src/api/endpoints/hub.keys.ts +260 -0
  318. manigent/web/frontend/src/api/endpoints/hub.test.ts +203 -0
  319. manigent/web/frontend/src/api/endpoints/hub.ts +654 -0
  320. manigent/web/frontend/src/api/endpoints/index.ts +15 -0
  321. manigent/web/frontend/src/api/endpoints/install.ts +74 -0
  322. manigent/web/frontend/src/api/endpoints/isolationCheck.ts +36 -0
  323. manigent/web/frontend/src/api/endpoints/knowledgePacks.ts +179 -0
  324. manigent/web/frontend/src/api/endpoints/mcps.ts +36 -0
  325. manigent/web/frontend/src/api/endpoints/messages.held.test.ts +43 -0
  326. manigent/web/frontend/src/api/endpoints/messages.ts +95 -0
  327. manigent/web/frontend/src/api/endpoints/modelConfigs.ts +232 -0
  328. manigent/web/frontend/src/api/endpoints/oauthApi.test.ts +110 -0
  329. manigent/web/frontend/src/api/endpoints/projectPlans.ts +608 -0
  330. manigent/web/frontend/src/api/endpoints/projectReports.ts +36 -0
  331. manigent/web/frontend/src/api/endpoints/projects.ts +124 -0
  332. manigent/web/frontend/src/api/endpoints/publicRequests.ts +75 -0
  333. manigent/web/frontend/src/api/endpoints/reflection.ts +32 -0
  334. manigent/web/frontend/src/api/endpoints/roomFlow.ts +14 -0
  335. manigent/web/frontend/src/api/endpoints/scheduledMessages.ts +60 -0
  336. manigent/web/frontend/src/api/endpoints/search.ts +27 -0
  337. manigent/web/frontend/src/api/endpoints/sharing.ts +42 -0
  338. manigent/web/frontend/src/api/endpoints/skills.ts +67 -0
  339. manigent/web/frontend/src/api/endpoints/templates.ts +72 -0
  340. manigent/web/frontend/src/api/loaders.ts +22 -0
  341. manigent/web/frontend/src/api/singleIssuer.test.ts +148 -0
  342. manigent/web/frontend/src/api/transientRetry.test.ts +176 -0
  343. manigent/web/frontend/src/api/transientRetry.ts +140 -0
  344. manigent/web/frontend/src/api/websocket.resync.test.ts +111 -0
  345. manigent/web/frontend/src/api/websocket.ts +335 -0
  346. manigent/web/frontend/src/components/agent/AgentNameLink.tsx +24 -0
  347. manigent/web/frontend/src/components/agent/AgentViewActions.test.tsx +41 -0
  348. manigent/web/frontend/src/components/agent/AgentViewActions.tsx +89 -0
  349. manigent/web/frontend/src/components/agent/CreateAgentPage.tsx +535 -0
  350. manigent/web/frontend/src/components/agent/JsonConfigEditor.tsx +33 -0
  351. manigent/web/frontend/src/components/agent/KnowledgePackList.tsx +103 -0
  352. manigent/web/frontend/src/components/agent/McpCatalog.tsx +261 -0
  353. manigent/web/frontend/src/components/agent/McpConfigModal.tsx +224 -0
  354. manigent/web/frontend/src/components/agent/PreConsentModal.tsx +172 -0
  355. manigent/web/frontend/src/components/agent/SkillCatalog.tsx +364 -0
  356. manigent/web/frontend/src/components/agent/SkillConfigModal.tsx +221 -0
  357. manigent/web/frontend/src/components/auth/AddAccountPage.test.tsx +181 -0
  358. manigent/web/frontend/src/components/auth/AddAccountPage.tsx +201 -0
  359. manigent/web/frontend/src/components/auth/AuthShell.tsx +462 -0
  360. manigent/web/frontend/src/components/auth/LoginPage.test.tsx +384 -0
  361. manigent/web/frontend/src/components/auth/LoginPage.tsx +129 -0
  362. manigent/web/frontend/src/components/auth/OAuthCallbackPage.test.tsx +185 -0
  363. manigent/web/frontend/src/components/auth/OAuthCallbackPage.tsx +154 -0
  364. manigent/web/frontend/src/components/auth/OAuthLinkedBanner.tsx +44 -0
  365. manigent/web/frontend/src/components/auth/ProviderButtons.tsx +94 -0
  366. manigent/web/frontend/src/components/auth/SignupPage.tsx +437 -0
  367. manigent/web/frontend/src/components/auth/StepProgress.tsx +60 -0
  368. manigent/web/frontend/src/components/auth/VerifyEmailPage.test.tsx +155 -0
  369. manigent/web/frontend/src/components/auth/VerifyEmailPage.tsx +211 -0
  370. manigent/web/frontend/src/components/auth/index.ts +3 -0
  371. manigent/web/frontend/src/components/auth/oauthErrors.test.ts +107 -0
  372. manigent/web/frontend/src/components/auth/oauthErrors.ts +82 -0
  373. manigent/web/frontend/src/components/auth/useUsernameCheck.test.tsx +68 -0
  374. manigent/web/frontend/src/components/auth/useUsernameCheck.ts +86 -0
  375. manigent/web/frontend/src/components/beta/BetaPage.tsx +21 -0
  376. manigent/web/frontend/src/components/beta/DayAtMyDesk.tsx +120 -0
  377. manigent/web/frontend/src/components/beta/Footer.tsx +82 -0
  378. manigent/web/frontend/src/components/beta/Hero.tsx +224 -0
  379. manigent/web/frontend/src/components/beta/Nav.tsx +51 -0
  380. manigent/web/frontend/src/components/beta/Pillars.tsx +172 -0
  381. manigent/web/frontend/src/components/beta/TwoModels.tsx +146 -0
  382. manigent/web/frontend/src/components/beta/constants.ts +3 -0
  383. manigent/web/frontend/src/components/briefing/BriefingFrame.test.tsx +208 -0
  384. manigent/web/frontend/src/components/briefing/BriefingFrame.tsx +151 -0
  385. manigent/web/frontend/src/components/briefing/BriefingPage.test.tsx +176 -0
  386. manigent/web/frontend/src/components/briefing/BriefingPage.tsx +157 -0
  387. manigent/web/frontend/src/components/briefing/BriefingPage.wsDelete.test.tsx +177 -0
  388. manigent/web/frontend/src/components/briefing/briefing.css +147 -0
  389. manigent/web/frontend/src/components/briefing/heightBootstrap.test.ts +121 -0
  390. manigent/web/frontend/src/components/briefing/heightBootstrap.ts +224 -0
  391. manigent/web/frontend/src/components/briefing/parseBriefMessage.test.ts +90 -0
  392. manigent/web/frontend/src/components/briefing/sampleBriefing.ts +148 -0
  393. manigent/web/frontend/src/components/channels/ChannelBadge.tsx +35 -0
  394. manigent/web/frontend/src/components/chatroom/ChannelThreadControls.tsx +127 -0
  395. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.test.tsx +207 -0
  396. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.tsx +80 -0
  397. manigent/web/frontend/src/components/chatroom/ChatroomView.channel.test.tsx +550 -0
  398. manigent/web/frontend/src/components/chatroom/ChatroomView.flow.test.tsx +186 -0
  399. manigent/web/frontend/src/components/chatroom/ChatroomView.isolation.test.tsx +70 -0
  400. manigent/web/frontend/src/components/chatroom/ChatroomView.tsx +450 -0
  401. manigent/web/frontend/src/components/chatroom/DelegationRow.test.tsx +170 -0
  402. manigent/web/frontend/src/components/chatroom/DelegationRow.tsx +217 -0
  403. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.test.tsx +33 -0
  404. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.tsx +46 -0
  405. manigent/web/frontend/src/components/chatroom/ForwardModal.test.tsx +109 -0
  406. manigent/web/frontend/src/components/chatroom/ForwardModal.tsx +304 -0
  407. manigent/web/frontend/src/components/chatroom/HeldNotice.tsx +77 -0
  408. manigent/web/frontend/src/components/chatroom/MentionDropdown.tsx +97 -0
  409. manigent/web/frontend/src/components/chatroom/MessageInput.tsx +408 -0
  410. manigent/web/frontend/src/components/chatroom/MessageItem.tsx +425 -0
  411. manigent/web/frontend/src/components/chatroom/MessageList.outside.test.tsx +98 -0
  412. manigent/web/frontend/src/components/chatroom/MessageList.test.tsx +245 -0
  413. manigent/web/frontend/src/components/chatroom/MessageList.tsx +406 -0
  414. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.test.tsx +211 -0
  415. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.tsx +182 -0
  416. manigent/web/frontend/src/components/chatroom/OutsideBadge.tsx +18 -0
  417. manigent/web/frontend/src/components/chatroom/RoomFlowControl.tsx +49 -0
  418. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.test.tsx +47 -0
  419. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.tsx +25 -0
  420. manigent/web/frontend/src/components/chatroom/SaveToPackModal.tsx +350 -0
  421. manigent/web/frontend/src/components/chatroom/StagedAttachments.test.tsx +56 -0
  422. manigent/web/frontend/src/components/chatroom/StagedAttachments.tsx +160 -0
  423. manigent/web/frontend/src/components/chatroom/SystemNote.tsx +18 -0
  424. manigent/web/frontend/src/components/chatroom/contactSender.test.tsx +130 -0
  425. manigent/web/frontend/src/components/chatroom/index.ts +3 -0
  426. manigent/web/frontend/src/components/chatroom/recipientStatus.test.ts +344 -0
  427. manigent/web/frontend/src/components/chatroom/recipientStatus.ts +197 -0
  428. manigent/web/frontend/src/components/common/AnnouncementBanners.test.tsx +221 -0
  429. manigent/web/frontend/src/components/common/AnnouncementBanners.tsx +180 -0
  430. manigent/web/frontend/src/components/common/Avatar.test.tsx +50 -0
  431. manigent/web/frontend/src/components/common/Avatar.tsx +102 -0
  432. manigent/web/frontend/src/components/common/Badge.tsx +30 -0
  433. manigent/web/frontend/src/components/common/BrandName.tsx +16 -0
  434. manigent/web/frontend/src/components/common/ClearHistoryButton.tsx +74 -0
  435. manigent/web/frontend/src/components/common/CollapsibleHeader.tsx +64 -0
  436. manigent/web/frontend/src/components/common/ConnectionStatus.test.tsx +56 -0
  437. manigent/web/frontend/src/components/common/ConnectionStatus.tsx +28 -0
  438. manigent/web/frontend/src/components/common/DropOverlay.tsx +27 -0
  439. manigent/web/frontend/src/components/common/InlineEditableTitle.test.tsx +369 -0
  440. manigent/web/frontend/src/components/common/InlineEditableTitle.tsx +287 -0
  441. manigent/web/frontend/src/components/common/IsolationUnavailablePanel.tsx +19 -0
  442. manigent/web/frontend/src/components/common/Loading.tsx +22 -0
  443. manigent/web/frontend/src/components/common/ModelSelector.tsx +248 -0
  444. manigent/web/frontend/src/components/common/Overlay.tsx +15 -0
  445. manigent/web/frontend/src/components/common/ResizeHandle.tsx +52 -0
  446. manigent/web/frontend/src/components/common/TagAutocompleteInput.test.tsx +92 -0
  447. manigent/web/frontend/src/components/common/TagAutocompleteInput.tsx +246 -0
  448. manigent/web/frontend/src/components/common/TagPillInput.tsx +181 -0
  449. manigent/web/frontend/src/components/common/TeamAndAgentPicker.tsx +83 -0
  450. manigent/web/frontend/src/components/common/Timestamp.tsx +61 -0
  451. manigent/web/frontend/src/components/common/VerifiedBadge.tsx +20 -0
  452. manigent/web/frontend/src/components/common/index.ts +8 -0
  453. manigent/web/frontend/src/components/composer/PhPopover.tsx +239 -0
  454. manigent/web/frontend/src/components/composer/PromptEditor.test.tsx +472 -0
  455. manigent/web/frontend/src/components/composer/PromptEditor.tsx +540 -0
  456. manigent/web/frontend/src/components/composer/caret.ts +159 -0
  457. manigent/web/frontend/src/components/composer/placeholders.test.ts +352 -0
  458. manigent/web/frontend/src/components/composer/placeholders.ts +354 -0
  459. manigent/web/frontend/src/components/composer/promptEditor.css +161 -0
  460. manigent/web/frontend/src/components/computer/ComputerPage.test.tsx +452 -0
  461. manigent/web/frontend/src/components/computer/ComputerPage.tsx +811 -0
  462. manigent/web/frontend/src/components/computer/ConnectComputerModal.tsx +135 -0
  463. manigent/web/frontend/src/components/computer/CopyCommand.tsx +77 -0
  464. manigent/web/frontend/src/components/computer/DisconnectComputerModal.tsx +121 -0
  465. manigent/web/frontend/src/components/computer/SoftwareCard.tsx +145 -0
  466. manigent/web/frontend/src/components/computer/TerminalCard.test.tsx +114 -0
  467. manigent/web/frontend/src/components/computer/TerminalCard.tsx +131 -0
  468. manigent/web/frontend/src/components/computer/TerminalPanel.tsx +226 -0
  469. manigent/web/frontend/src/components/computer/TrustPanel.test.tsx +58 -0
  470. manigent/web/frontend/src/components/computer/TrustPanel.tsx +143 -0
  471. manigent/web/frontend/src/components/computer/computerCopy.test.ts +322 -0
  472. manigent/web/frontend/src/components/computer/computerCopy.ts +413 -0
  473. manigent/web/frontend/src/components/computer/sections.ts +21 -0
  474. manigent/web/frontend/src/components/computer/terminalProtocol.test.ts +135 -0
  475. manigent/web/frontend/src/components/computer/terminalProtocol.ts +170 -0
  476. manigent/web/frontend/src/components/context/ContextGuide.test.tsx +26 -0
  477. manigent/web/frontend/src/components/context/ContextGuide.tsx +144 -0
  478. manigent/web/frontend/src/components/context/ContextLanding.tsx +46 -0
  479. manigent/web/frontend/src/components/desk/CardBand.a11y.test.tsx +138 -0
  480. manigent/web/frontend/src/components/desk/CardBand.tsx +132 -0
  481. manigent/web/frontend/src/components/desk/DashTile.tsx +81 -0
  482. manigent/web/frontend/src/components/desk/DeskAgentPicker.retained.test.tsx +79 -0
  483. manigent/web/frontend/src/components/desk/DeskAgentPicker.tsx +151 -0
  484. manigent/web/frontend/src/components/desk/DeskComposer.detachTodo.test.tsx +233 -0
  485. manigent/web/frontend/src/components/desk/DeskComposer.dispatch.test.tsx +208 -0
  486. manigent/web/frontend/src/components/desk/DeskComposer.loadTodoDraft.test.tsx +242 -0
  487. manigent/web/frontend/src/components/desk/DeskComposer.restage.test.tsx +258 -0
  488. manigent/web/frontend/src/components/desk/DeskComposer.saveTodo.test.tsx +265 -0
  489. manigent/web/frontend/src/components/desk/DeskComposer.tsx +828 -0
  490. manigent/web/frontend/src/components/desk/DeskPage.crossHighlight.test.tsx +427 -0
  491. manigent/web/frontend/src/components/desk/DeskPage.link.test.tsx +271 -0
  492. manigent/web/frontend/src/components/desk/DeskPage.tsx +1066 -0
  493. manigent/web/frontend/src/components/desk/DeskPage.welcomeTour.test.tsx +262 -0
  494. manigent/web/frontend/src/components/desk/DeskSideRail.tsx +100 -0
  495. manigent/web/frontend/src/components/desk/DeskWelcomeTour.tsx +513 -0
  496. manigent/web/frontend/src/components/desk/DeskZeroState.tsx +45 -0
  497. manigent/web/frontend/src/components/desk/LabelChip.tsx +147 -0
  498. manigent/web/frontend/src/components/desk/LabelFilter.tsx +474 -0
  499. manigent/web/frontend/src/components/desk/LabelInput.test.tsx +439 -0
  500. manigent/web/frontend/src/components/desk/LabelInput.tsx +477 -0
  501. manigent/web/frontend/src/components/desk/LabelManageSheet.test.tsx +384 -0
  502. manigent/web/frontend/src/components/desk/LabelManageSheet.tsx +498 -0
  503. manigent/web/frontend/src/components/desk/PhaseRail.test.tsx +170 -0
  504. manigent/web/frontend/src/components/desk/PhaseRail.tsx +120 -0
  505. manigent/web/frontend/src/components/desk/PlanBand.test.tsx +655 -0
  506. manigent/web/frontend/src/components/desk/PlanBand.tsx +216 -0
  507. manigent/web/frontend/src/components/desk/SaveTodoModal.tsx +126 -0
  508. manigent/web/frontend/src/components/desk/SopEditor.tsx +280 -0
  509. manigent/web/frontend/src/components/desk/SopItem.tsx +171 -0
  510. manigent/web/frontend/src/components/desk/SopRail.test.tsx +517 -0
  511. manigent/web/frontend/src/components/desk/SopRail.tsx +250 -0
  512. manigent/web/frontend/src/components/desk/TicketChip.tsx +51 -0
  513. manigent/web/frontend/src/components/desk/TodoItem.attachments.test.tsx +96 -0
  514. manigent/web/frontend/src/components/desk/TodoItem.menu.test.tsx +142 -0
  515. manigent/web/frontend/src/components/desk/TodoItem.tsx +573 -0
  516. manigent/web/frontend/src/components/desk/TodoRail.longpress.test.tsx +396 -0
  517. manigent/web/frontend/src/components/desk/TodoRail.test.tsx +1155 -0
  518. manigent/web/frontend/src/components/desk/TodoRail.tsx +918 -0
  519. manigent/web/frontend/src/components/desk/TodoRowMenu.tsx +147 -0
  520. manigent/web/frontend/src/components/desk/UpdateCard.bands.test.tsx +308 -0
  521. manigent/web/frontend/src/components/desk/UpdateCard.sendBack.test.tsx +444 -0
  522. manigent/web/frontend/src/components/desk/UpdateCard.tsx +943 -0
  523. manigent/web/frontend/src/components/desk/UpdateCard.untrusted.test.tsx +161 -0
  524. manigent/web/frontend/src/components/desk/desk.css +1921 -0
  525. manigent/web/frontend/src/components/desk/desk.css.test.ts +582 -0
  526. manigent/web/frontend/src/components/desk/deskBands.test.ts +389 -0
  527. manigent/web/frontend/src/components/desk/deskBands.ts +226 -0
  528. manigent/web/frontend/src/components/desk/m16Surfaces.test.ts +134 -0
  529. manigent/web/frontend/src/components/desk/sopSyntax.ts +73 -0
  530. manigent/web/frontend/src/components/discovery/AgentDirectory.tsx +71 -0
  531. manigent/web/frontend/src/components/discovery/AgentFilterBar.tsx +46 -0
  532. manigent/web/frontend/src/components/discovery/AgentSearchBar.tsx +56 -0
  533. manigent/web/frontend/src/components/dm/AgentSummaryCard.tsx +154 -0
  534. manigent/web/frontend/src/components/dm/ComposeFields.tsx +145 -0
  535. manigent/web/frontend/src/components/dm/DMChatView.flow.test.tsx +374 -0
  536. manigent/web/frontend/src/components/dm/DMChatView.tsx +841 -0
  537. manigent/web/frontend/src/components/dm/DMZeroState.tsx +204 -0
  538. manigent/web/frontend/src/components/dm/DmComposeModal.tsx +301 -0
  539. manigent/web/frontend/src/components/dm/PersonalizeModal.tsx +366 -0
  540. manigent/web/frontend/src/components/dm/index.ts +1 -0
  541. manigent/web/frontend/src/components/files/FileItem.tsx +138 -0
  542. manigent/web/frontend/src/components/files/FileList.outside.test.tsx +29 -0
  543. manigent/web/frontend/src/components/files/FileList.tsx +48 -0
  544. manigent/web/frontend/src/components/files/FilePreview.test.tsx +75 -0
  545. manigent/web/frontend/src/components/files/FilePreview.tsx +325 -0
  546. manigent/web/frontend/src/components/files/FileViewerModal.test.tsx +157 -0
  547. manigent/web/frontend/src/components/files/FileViewerModal.tsx +261 -0
  548. manigent/web/frontend/src/components/files/HighlightedCode.tsx +39 -0
  549. manigent/web/frontend/src/components/files/SaveToAgentPanel.test.tsx +156 -0
  550. manigent/web/frontend/src/components/files/SaveToAgentPanel.tsx +545 -0
  551. manigent/web/frontend/src/components/files/index.ts +3 -0
  552. manigent/web/frontend/src/components/hero/AgentAvatar.tsx +66 -0
  553. manigent/web/frontend/src/components/hero/Button.tsx +66 -0
  554. manigent/web/frontend/src/components/hero/Hero.tsx +133 -0
  555. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.test.tsx +393 -0
  556. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.tsx +792 -0
  557. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.test.tsx +40 -0
  558. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.tsx +36 -0
  559. manigent/web/frontend/src/components/hub/HubAvatar.test.tsx +93 -0
  560. manigent/web/frontend/src/components/hub/HubAvatar.tsx +33 -0
  561. manigent/web/frontend/src/components/hub/HubChatView.test.tsx +695 -0
  562. manigent/web/frontend/src/components/hub/HubChatView.tsx +504 -0
  563. manigent/web/frontend/src/components/hub/HubContactLabel.test.tsx +92 -0
  564. manigent/web/frontend/src/components/hub/HubContactLabel.tsx +47 -0
  565. manigent/web/frontend/src/components/hub/HubDeliveryLine.tsx +49 -0
  566. manigent/web/frontend/src/components/hub/HubDisclosure.tsx +49 -0
  567. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.test.tsx +144 -0
  568. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.tsx +244 -0
  569. manigent/web/frontend/src/components/hub/HubMessageBody.test.tsx +26 -0
  570. manigent/web/frontend/src/components/hub/HubMessageBody.tsx +14 -0
  571. manigent/web/frontend/src/components/hub/HubOfflineConfirm.tsx +45 -0
  572. manigent/web/frontend/src/components/hub/HubThreadBanner.tsx +33 -0
  573. manigent/web/frontend/src/components/hub/HubThreadGuard.tsx +59 -0
  574. manigent/web/frontend/src/components/hub/RemoteAgentCard.test.tsx +50 -0
  575. manigent/web/frontend/src/components/hub/RemoteAgentCard.tsx +174 -0
  576. manigent/web/frontend/src/components/hub/RemoteAgentSection.test.tsx +446 -0
  577. manigent/web/frontend/src/components/hub/RemoteAgentSection.tsx +219 -0
  578. manigent/web/frontend/src/components/hub/ReportDialog.test.tsx +96 -0
  579. manigent/web/frontend/src/components/hub/ReportDialog.tsx +162 -0
  580. manigent/web/frontend/src/components/hub/RetainButton.test.tsx +103 -0
  581. manigent/web/frontend/src/components/hub/RetainButton.tsx +114 -0
  582. manigent/web/frontend/src/components/hub/RetainDialog.test.tsx +68 -0
  583. manigent/web/frontend/src/components/hub/RetainDialog.tsx +115 -0
  584. manigent/web/frontend/src/components/hub/RetainedList.test.tsx +94 -0
  585. manigent/web/frontend/src/components/hub/RetainedList.tsx +32 -0
  586. manigent/web/frontend/src/components/hub/RetainedRow.tsx +129 -0
  587. manigent/web/frontend/src/components/hub/StandInChip.test.tsx +37 -0
  588. manigent/web/frontend/src/components/hub/StandInChip.tsx +43 -0
  589. manigent/web/frontend/src/components/hub/UnusualNameChip.test.tsx +81 -0
  590. manigent/web/frontend/src/components/hub/UnusualNameChip.tsx +22 -0
  591. manigent/web/frontend/src/components/hub/hubLint.test.ts +265 -0
  592. manigent/web/frontend/src/components/landing/ComparisonSection.tsx +92 -0
  593. manigent/web/frontend/src/components/landing/ConsoleSection.tsx +402 -0
  594. manigent/web/frontend/src/components/landing/CrewSection.tsx +224 -0
  595. manigent/web/frontend/src/components/landing/FAQ.tsx +68 -0
  596. manigent/web/frontend/src/components/landing/Footer.tsx +58 -0
  597. manigent/web/frontend/src/components/landing/HowItWorks.tsx +117 -0
  598. manigent/web/frontend/src/components/landing/KnowledgeSection.tsx +91 -0
  599. manigent/web/frontend/src/components/landing/LandingPage.test.tsx +134 -0
  600. manigent/web/frontend/src/components/landing/LearningSection.tsx +486 -0
  601. manigent/web/frontend/src/components/landing/Nav.tsx +68 -0
  602. manigent/web/frontend/src/components/landing/Pricing.tsx +263 -0
  603. manigent/web/frontend/src/components/landing/Section.tsx +98 -0
  604. manigent/web/frontend/src/components/landing/StaffingSection.tsx +122 -0
  605. manigent/web/frontend/src/components/landing/WhyNowSection.tsx +92 -0
  606. manigent/web/frontend/src/components/landing/WhyTeamSection.tsx +73 -0
  607. manigent/web/frontend/src/components/landing/earlyAccess.ts +23 -0
  608. manigent/web/frontend/src/components/layout/AppLayout.tsx +77 -0
  609. manigent/web/frontend/src/components/layout/Sidebar.tsx +172 -0
  610. manigent/web/frontend/src/components/layout/index.ts +2 -0
  611. manigent/web/frontend/src/components/project/ActivityFeed.tsx +247 -0
  612. manigent/web/frontend/src/components/project/CreateProjectPage.tsx +543 -0
  613. manigent/web/frontend/src/components/project/DeleteProjectButton.tsx +39 -0
  614. manigent/web/frontend/src/components/project/ProjectPlanTab.phase.test.tsx +107 -0
  615. manigent/web/frontend/src/components/project/ProjectPlanTab.test.tsx +994 -0
  616. manigent/web/frontend/src/components/project/ProjectPlanTab.tsx +166 -0
  617. manigent/web/frontend/src/components/project/ProjectReportTab.tsx +139 -0
  618. manigent/web/frontend/src/components/project/ProjectView.tsx +390 -0
  619. manigent/web/frontend/src/components/project/index.ts +2 -0
  620. manigent/web/frontend/src/components/project/plan/PlanAcceptance.test.tsx +460 -0
  621. manigent/web/frontend/src/components/project/plan/PlanAcceptance.tsx +111 -0
  622. manigent/web/frontend/src/components/project/plan/PlanBlockEdit.tsx +111 -0
  623. manigent/web/frontend/src/components/project/plan/PlanBlockEditing.test.tsx +1132 -0
  624. manigent/web/frontend/src/components/project/plan/PlanBody.test.tsx +495 -0
  625. manigent/web/frontend/src/components/project/plan/PlanBody.tsx +992 -0
  626. manigent/web/frontend/src/components/project/plan/PlanCollide.tsx +254 -0
  627. manigent/web/frontend/src/components/project/plan/PlanCommentList.test.tsx +420 -0
  628. manigent/web/frontend/src/components/project/plan/PlanCommentList.tsx +391 -0
  629. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.test.tsx +226 -0
  630. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.tsx +128 -0
  631. manigent/web/frontend/src/components/project/plan/PlanDestructiveAccept.test.tsx +209 -0
  632. manigent/web/frontend/src/components/project/plan/PlanDiff.test.tsx +126 -0
  633. manigent/web/frontend/src/components/project/plan/PlanDiff.tsx +131 -0
  634. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.test.tsx +500 -0
  635. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.tsx +125 -0
  636. manigent/web/frontend/src/components/project/plan/PlanEditor.mounts.test.tsx +504 -0
  637. manigent/web/frontend/src/components/project/plan/PlanEditor.tsx +2159 -0
  638. manigent/web/frontend/src/components/project/plan/PlanHistory.tsx +69 -0
  639. manigent/web/frontend/src/components/project/plan/PlanInlineNotes.test.tsx +2398 -0
  640. manigent/web/frontend/src/components/project/plan/PlanNoteBase.test.tsx +215 -0
  641. manigent/web/frontend/src/components/project/plan/PlanNoteRow.test.tsx +562 -0
  642. manigent/web/frontend/src/components/project/plan/PlanNoteRow.tsx +908 -0
  643. manigent/web/frontend/src/components/project/plan/PlanQuotePop.tsx +254 -0
  644. manigent/web/frontend/src/components/project/plan/PlanStaleReconcile.test.tsx +244 -0
  645. manigent/web/frontend/src/components/project/plan/PlanSubmitWindow.test.tsx +281 -0
  646. manigent/web/frontend/src/components/project/plan/PlanTray.tsx +184 -0
  647. manigent/web/frontend/src/components/project/plan/PlanTrayMirror.test.tsx +242 -0
  648. manigent/web/frontend/src/components/project/plan/plan.css +1151 -0
  649. manigent/web/frontend/src/components/project/plan/planAnchor.test.tsx +459 -0
  650. manigent/web/frontend/src/components/project/plan/planAnchor.tsx +277 -0
  651. manigent/web/frontend/src/components/project/plan/planBlocks.test.tsx +470 -0
  652. manigent/web/frontend/src/components/project/plan/planBlocks.ts +242 -0
  653. manigent/web/frontend/src/components/project/plan/planContrast.test.ts +229 -0
  654. manigent/web/frontend/src/components/project/plan/planDiffs.test.ts +455 -0
  655. manigent/web/frontend/src/components/project/plan/planDiffs.ts +358 -0
  656. manigent/web/frontend/src/components/project/plan/planTestServer.ts +741 -0
  657. manigent/web/frontend/src/components/project/plan/planTransport.test.ts +160 -0
  658. manigent/web/frontend/src/components/project/plan/planTrayLogic.test.ts +1201 -0
  659. manigent/web/frontend/src/components/project/plan/planTrayLogic.ts +1164 -0
  660. manigent/web/frontend/src/components/publicAgents/PublicAgentsAccount.tsx +162 -0
  661. manigent/web/frontend/src/components/publicAgents/api.ts +55 -0
  662. manigent/web/frontend/src/components/publicAgents/parts.tsx +54 -0
  663. manigent/web/frontend/src/components/publicAgents/publicAgents.css +53 -0
  664. manigent/web/frontend/src/components/publicAgents/publicAgents.test.tsx +86 -0
  665. manigent/web/frontend/src/components/publicAgents/widget.test.ts +148 -0
  666. manigent/web/frontend/src/components/publicAgents/widget.ts +40 -0
  667. manigent/web/frontend/src/components/publicRequests/AgentDrawer.tsx +101 -0
  668. manigent/web/frontend/src/components/publicRequests/AgentPanel.tsx +173 -0
  669. manigent/web/frontend/src/components/publicRequests/AgentRankList.tsx +61 -0
  670. manigent/web/frontend/src/components/publicRequests/MatchRetainButton.tsx +67 -0
  671. manigent/web/frontend/src/components/publicRequests/ProgressBoard.tsx +74 -0
  672. manigent/web/frontend/src/components/publicRequests/PublicRequestsPage.tsx +57 -0
  673. manigent/web/frontend/src/components/publicRequests/RequestHome.tsx +99 -0
  674. manigent/web/frontend/src/components/publicRequests/RequestRoom.tsx +126 -0
  675. manigent/web/frontend/src/components/publicRequests/agentView.ts +65 -0
  676. manigent/web/frontend/src/components/publicRequests/publicRequests.css +209 -0
  677. manigent/web/frontend/src/components/publicRequests/publicRequests.test.tsx +172 -0
  678. manigent/web/frontend/src/components/publicRequests/requestCopy.ts +121 -0
  679. manigent/web/frontend/src/components/sample-request/SampleIdeaList.test.tsx +190 -0
  680. manigent/web/frontend/src/components/sample-request/SampleIdeaList.tsx +180 -0
  681. manigent/web/frontend/src/components/sample-request/SampleRequestBody.tsx +276 -0
  682. manigent/web/frontend/src/components/sample-request/assistantStarterSamples.ts +62 -0
  683. manigent/web/frontend/src/components/sample-request/sampleIdeaList.css +106 -0
  684. manigent/web/frontend/src/components/scheduled/CronPicker.tsx +80 -0
  685. manigent/web/frontend/src/components/scheduled/ScheduleMessageModal.tsx +341 -0
  686. manigent/web/frontend/src/components/scheduled/ScheduledMessagesList.tsx +157 -0
  687. manigent/web/frontend/src/components/settings/AgentSettingsPage.tsx +366 -0
  688. manigent/web/frontend/src/components/settings/ConnectedAccounts.tsx +277 -0
  689. manigent/web/frontend/src/components/settings/DefaultModelSection.test.tsx +96 -0
  690. manigent/web/frontend/src/components/settings/DefaultModelSection.tsx +212 -0
  691. manigent/web/frontend/src/components/settings/DisconnectConfirmModal.tsx +93 -0
  692. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.isolation.test.tsx +66 -0
  693. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.tsx +134 -0
  694. manigent/web/frontend/src/components/settings/InvitableAgentsPicker.tsx +182 -0
  695. manigent/web/frontend/src/components/settings/KnowledgePacksSection.tsx +867 -0
  696. manigent/web/frontend/src/components/settings/ModelConfigDialog.catalogDriven.test.tsx +79 -0
  697. manigent/web/frontend/src/components/settings/ModelConfigDialog.tsx +343 -0
  698. manigent/web/frontend/src/components/settings/ModelConfigSection.smartRouting.test.tsx +109 -0
  699. manigent/web/frontend/src/components/settings/ModelConfigSection.tsx +599 -0
  700. manigent/web/frontend/src/components/settings/ReflectionScheduleSection.tsx +170 -0
  701. manigent/web/frontend/src/components/settings/UserSettingsPage.tsx +168 -0
  702. manigent/web/frontend/src/components/settings/agent/AgentStatusStrip.tsx +71 -0
  703. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.test.tsx +81 -0
  704. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.tsx +62 -0
  705. manigent/web/frontend/src/components/settings/agent/ChannelIsolationLine.tsx +21 -0
  706. manigent/web/frontend/src/components/settings/agent/ChannelsSection.test.tsx +879 -0
  707. manigent/web/frontend/src/components/settings/agent/ChannelsSection.tsx +1124 -0
  708. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.test.ts +15 -0
  709. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.tsx +301 -0
  710. manigent/web/frontend/src/components/settings/agent/ExtraDirsList.tsx +85 -0
  711. manigent/web/frontend/src/components/settings/agent/GeneralSection.tsx +88 -0
  712. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.test.tsx +32 -0
  713. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.tsx +30 -0
  714. manigent/web/frontend/src/components/settings/agent/HubChannelCard.test.tsx +449 -0
  715. manigent/web/frontend/src/components/settings/agent/HubChannelCard.tsx +312 -0
  716. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.test.tsx +83 -0
  717. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.tsx +64 -0
  718. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.test.tsx +69 -0
  719. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.tsx +42 -0
  720. manigent/web/frontend/src/components/settings/agent/HubTurnOffDialog.tsx +88 -0
  721. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.test.tsx +26 -0
  722. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.tsx +68 -0
  723. manigent/web/frontend/src/components/settings/agent/KnowledgeSection.tsx +68 -0
  724. manigent/web/frontend/src/components/settings/agent/LineChannelCard.test.tsx +428 -0
  725. manigent/web/frontend/src/components/settings/agent/LineChannelCard.tsx +343 -0
  726. manigent/web/frontend/src/components/settings/agent/RuntimeSection.test.tsx +164 -0
  727. manigent/web/frontend/src/components/settings/agent/RuntimeSection.tsx +118 -0
  728. manigent/web/frontend/src/components/settings/agent/StorageSection.tsx +78 -0
  729. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.test.tsx +253 -0
  730. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.tsx +401 -0
  731. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.test.tsx +462 -0
  732. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.tsx +289 -0
  733. manigent/web/frontend/src/components/settings/agent/agentSettings.test.tsx +109 -0
  734. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.test.tsx +162 -0
  735. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.tsx +119 -0
  736. manigent/web/frontend/src/components/settings/agent/channelsShared.ts +83 -0
  737. manigent/web/frontend/src/components/settings/agent/fields.tsx +67 -0
  738. manigent/web/frontend/src/components/settings/agent/sections.ts +36 -0
  739. manigent/web/frontend/src/components/settings/agent/useAgentSettingsDraft.ts +129 -0
  740. manigent/web/frontend/src/components/settings/agent/useInstallSelection.ts +38 -0
  741. manigent/web/frontend/src/components/settings/primitives.tsx +98 -0
  742. manigent/web/frontend/src/components/shared/ExpandToggle.tsx +45 -0
  743. manigent/web/frontend/src/components/shared/MarkdownBody.tsx +218 -0
  744. manigent/web/frontend/src/components/shared/MarkdownBody.untrusted.test.tsx +97 -0
  745. manigent/web/frontend/src/components/shared/RenderBody.tsx +90 -0
  746. manigent/web/frontend/src/components/shared/rehypeHideComments.test.tsx +115 -0
  747. manigent/web/frontend/src/components/shared/rehypeHideComments.ts +124 -0
  748. manigent/web/frontend/src/components/sharing/AcceptSharePage.tsx +141 -0
  749. manigent/web/frontend/src/components/sharing/ShareLandingPage.tsx +89 -0
  750. manigent/web/frontend/src/components/sidebar/AccountChip.test.tsx +337 -0
  751. manigent/web/frontend/src/components/sidebar/AccountChip.tsx +407 -0
  752. manigent/web/frontend/src/components/sidebar/ActiveWorkDot.tsx +9 -0
  753. manigent/web/frontend/src/components/sidebar/AgentProfile.tsx +44 -0
  754. manigent/web/frontend/src/components/sidebar/AgentRow.tsx +71 -0
  755. manigent/web/frontend/src/components/sidebar/AgentsPanel.retained.test.tsx +105 -0
  756. manigent/web/frontend/src/components/sidebar/AgentsPanel.tsx +231 -0
  757. manigent/web/frontend/src/components/sidebar/AssistantHero.tsx +48 -0
  758. manigent/web/frontend/src/components/sidebar/ChatroomItem.tsx +48 -0
  759. manigent/web/frontend/src/components/sidebar/CommandPalette.channelThreads.test.tsx +76 -0
  760. manigent/web/frontend/src/components/sidebar/CommandPalette.reopen.test.tsx +206 -0
  761. manigent/web/frontend/src/components/sidebar/CommandPalette.test.tsx +725 -0
  762. manigent/web/frontend/src/components/sidebar/CommandPalette.tsx +774 -0
  763. manigent/web/frontend/src/components/sidebar/ComputersPanel.tsx +144 -0
  764. manigent/web/frontend/src/components/sidebar/DmIcon.tsx +46 -0
  765. manigent/web/frontend/src/components/sidebar/DmsPanel.tsx +226 -0
  766. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.channels.test.tsx +582 -0
  767. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.test.tsx +151 -0
  768. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.tsx +454 -0
  769. manigent/web/frontend/src/components/sidebar/HotkeysHelp.tsx +101 -0
  770. manigent/web/frontend/src/components/sidebar/MessageHitRow.test.tsx +84 -0
  771. manigent/web/frontend/src/components/sidebar/MessageHitRow.tsx +57 -0
  772. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.crossHighlight.test.tsx +247 -0
  773. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.delete.test.tsx +273 -0
  774. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.tsx +351 -0
  775. manigent/web/frontend/src/components/sidebar/PanelShell.tsx +48 -0
  776. manigent/web/frontend/src/components/sidebar/ProjectItem.tsx +161 -0
  777. manigent/web/frontend/src/components/sidebar/ProjectRow.tsx +151 -0
  778. manigent/web/frontend/src/components/sidebar/ProjectsPanel.tsx +80 -0
  779. manigent/web/frontend/src/components/sidebar/PublicPanel.counterpart.test.tsx +85 -0
  780. manigent/web/frontend/src/components/sidebar/PublicPanel.hub.test.tsx +147 -0
  781. manigent/web/frontend/src/components/sidebar/PublicPanel.tsx +271 -0
  782. manigent/web/frontend/src/components/sidebar/RecencyHeader.tsx +17 -0
  783. manigent/web/frontend/src/components/sidebar/ScheduledPanel.tsx +58 -0
  784. manigent/web/frontend/src/components/sidebar/SidebarAvatar.tsx +128 -0
  785. manigent/web/frontend/src/components/sidebar/SidebarRail.tsx +163 -0
  786. manigent/web/frontend/src/components/sidebar/TeamGroup.tsx +67 -0
  787. manigent/web/frontend/src/components/sidebar/dmPrefill.test.ts +69 -0
  788. manigent/web/frontend/src/components/sidebar/dmPrefill.ts +80 -0
  789. manigent/web/frontend/src/components/sidebar/index.ts +18 -0
  790. manigent/web/frontend/src/components/sidebar/railShortcuts.ts +18 -0
  791. manigent/web/frontend/src/components/today/lib.test.ts +70 -0
  792. manigent/web/frontend/src/components/today/lib.ts +296 -0
  793. manigent/web/frontend/src/components/today/today.css +113 -0
  794. manigent/web/frontend/src/components/welcome/RunnerSetup.css +253 -0
  795. manigent/web/frontend/src/components/welcome/RunnerSetup.tsx +148 -0
  796. manigent/web/frontend/src/components/welcome/WelcomePage.css +383 -0
  797. manigent/web/frontend/src/components/welcome/WelcomePage.tsx +329 -0
  798. manigent/web/frontend/src/components/welcome/welcomeCopy.test.ts +382 -0
  799. manigent/web/frontend/src/components/welcome/welcomeCopy.ts +313 -0
  800. manigent/web/frontend/src/config/llmProviders.test.ts +161 -0
  801. manigent/web/frontend/src/config/llmProviders.ts +109 -0
  802. manigent/web/frontend/src/config/providerIds.fixture.json +13 -0
  803. manigent/web/frontend/src/hooks/accountNavigation.test.tsx +138 -0
  804. manigent/web/frontend/src/hooks/index.ts +10 -0
  805. manigent/web/frontend/src/hooks/useActiveWorkBootstrap.ts +86 -0
  806. manigent/web/frontend/src/hooks/useAgentAccess.test.tsx +30 -0
  807. manigent/web/frontend/src/hooks/useAgentAccess.ts +204 -0
  808. manigent/web/frontend/src/hooks/useAgentSampleRequests.ts +27 -0
  809. manigent/web/frontend/src/hooks/useAgentSearch.ts +10 -0
  810. manigent/web/frontend/src/hooks/useAgents.ts +46 -0
  811. manigent/web/frontend/src/hooks/useAnnouncements.test.ts +112 -0
  812. manigent/web/frontend/src/hooks/useAnnouncements.ts +100 -0
  813. manigent/web/frontend/src/hooks/useAssistantAgent.ts +24 -0
  814. manigent/web/frontend/src/hooks/useAutoResizeTextarea.ts +18 -0
  815. manigent/web/frontend/src/hooks/useBridgeDeliveries.test.tsx +79 -0
  816. manigent/web/frontend/src/hooks/useBriefTabs.ts +122 -0
  817. manigent/web/frontend/src/hooks/useChannelThreads.ts +69 -0
  818. manigent/web/frontend/src/hooks/useComposeForm.ts +80 -0
  819. manigent/web/frontend/src/hooks/useComputers.ts +123 -0
  820. manigent/web/frontend/src/hooks/useContextFirstItem.ts +118 -0
  821. manigent/web/frontend/src/hooks/useCreateProject.ts +78 -0
  822. manigent/web/frontend/src/hooks/useDebouncedValue.ts +16 -0
  823. manigent/web/frontend/src/hooks/useDeleteProject.ts +27 -0
  824. manigent/web/frontend/src/hooks/useDeskDispatch.test.tsx +170 -0
  825. manigent/web/frontend/src/hooks/useDeskDispatch.ts +135 -0
  826. manigent/web/frontend/src/hooks/useDeskReadState.test.tsx +85 -0
  827. manigent/web/frontend/src/hooks/useDeskReadState.ts +150 -0
  828. manigent/web/frontend/src/hooks/useDeskRecentlyDone.test.tsx +222 -0
  829. manigent/web/frontend/src/hooks/useDeskRecentlyDone.ts +225 -0
  830. manigent/web/frontend/src/hooks/useDeskTour.ts +37 -0
  831. manigent/web/frontend/src/hooks/useDeskUpdates.channelThreads.test.tsx +91 -0
  832. manigent/web/frontend/src/hooks/useDeskUpdates.hubThreads.test.tsx +93 -0
  833. manigent/web/frontend/src/hooks/useDeskUpdates.ts +353 -0
  834. manigent/web/frontend/src/hooks/useDisconnectIntegration.ts +23 -0
  835. manigent/web/frontend/src/hooks/useDmThreads.ts +99 -0
  836. manigent/web/frontend/src/hooks/useFileDropzone.ts +93 -0
  837. manigent/web/frontend/src/hooks/useFileUpload.ts +38 -0
  838. manigent/web/frontend/src/hooks/useFiles.ts +98 -0
  839. manigent/web/frontend/src/hooks/useHub.test.tsx +237 -0
  840. manigent/web/frontend/src/hooks/useHub.ts +502 -0
  841. manigent/web/frontend/src/hooks/useInstall.ts +82 -0
  842. manigent/web/frontend/src/hooks/useLabels.test.tsx +294 -0
  843. manigent/web/frontend/src/hooks/useLabels.ts +287 -0
  844. manigent/web/frontend/src/hooks/useLogout.ts +49 -0
  845. manigent/web/frontend/src/hooks/useMentionDetection.ts +90 -0
  846. manigent/web/frontend/src/hooks/useMessageAttachments.deskLimit.test.ts +264 -0
  847. manigent/web/frontend/src/hooks/useMessageAttachments.ts +283 -0
  848. manigent/web/frontend/src/hooks/useMessageDeepLink.test.tsx +70 -0
  849. manigent/web/frontend/src/hooks/useMessageDeepLink.ts +32 -0
  850. manigent/web/frontend/src/hooks/useMessageSearch.ts +78 -0
  851. manigent/web/frontend/src/hooks/useModelConfigs.ts +178 -0
  852. manigent/web/frontend/src/hooks/useMyAgents.ts +18 -0
  853. manigent/web/frontend/src/hooks/usePointerReorder.ts +270 -0
  854. manigent/web/frontend/src/hooks/usePrefersReducedMotion.ts +18 -0
  855. manigent/web/frontend/src/hooks/useProjectPlan.ts +36 -0
  856. manigent/web/frontend/src/hooks/useProjectReport.ts +19 -0
  857. manigent/web/frontend/src/hooks/useProjects.ts +104 -0
  858. manigent/web/frontend/src/hooks/useProjectsWithChatrooms.ts +97 -0
  859. manigent/web/frontend/src/hooks/usePublicRequests.ts +89 -0
  860. manigent/web/frontend/src/hooks/useRecentAgents.ts +38 -0
  861. manigent/web/frontend/src/hooks/useRenameProject.test.tsx +185 -0
  862. manigent/web/frontend/src/hooks/useRenameProject.ts +192 -0
  863. manigent/web/frontend/src/hooks/useRoomTrust.ts +56 -0
  864. manigent/web/frontend/src/hooks/useSharing.ts +46 -0
  865. manigent/web/frontend/src/hooks/useSopRecents.ts +55 -0
  866. manigent/web/frontend/src/hooks/useSopSchedules.ts +25 -0
  867. manigent/web/frontend/src/hooks/useSops.ts +99 -0
  868. manigent/web/frontend/src/hooks/useSwitchAccount.ts +41 -0
  869. manigent/web/frontend/src/hooks/useTeamGroups.ts +48 -0
  870. manigent/web/frontend/src/hooks/useThreadOutbox.ts +68 -0
  871. manigent/web/frontend/src/hooks/useTodos.add.test.tsx +89 -0
  872. manigent/web/frontend/src/hooks/useTodos.linkProject.test.tsx +115 -0
  873. manigent/web/frontend/src/hooks/useTodos.saveDraft.test.tsx +188 -0
  874. manigent/web/frontend/src/hooks/useTodos.ts +396 -0
  875. manigent/web/frontend/src/hooks/useUntrustedBodies.test.tsx +87 -0
  876. manigent/web/frontend/src/hooks/useUntrustedBodies.ts +51 -0
  877. manigent/web/frontend/src/hooks/useWebSocket.briefTab.test.tsx +148 -0
  878. manigent/web/frontend/src/hooks/useWebSocket.channelThreads.test.tsx +82 -0
  879. manigent/web/frontend/src/hooks/useWebSocket.hostSync.test.tsx +92 -0
  880. manigent/web/frontend/src/hooks/useWebSocket.hub.test.tsx +114 -0
  881. manigent/web/frontend/src/hooks/useWebSocket.labelSync.test.tsx +168 -0
  882. manigent/web/frontend/src/hooks/useWebSocket.plan.test.tsx +168 -0
  883. manigent/web/frontend/src/hooks/useWebSocket.reconnect.test.tsx +166 -0
  884. manigent/web/frontend/src/hooks/useWebSocket.runnerVersions.test.tsx +167 -0
  885. manigent/web/frontend/src/hooks/useWebSocket.ts +495 -0
  886. manigent/web/frontend/src/hooks/useWindowFileDropGuard.ts +33 -0
  887. manigent/web/frontend/src/landing.tsx +11 -0
  888. manigent/web/frontend/src/main.tsx +17 -0
  889. manigent/web/frontend/src/private-beta.tsx +10 -0
  890. manigent/web/frontend/src/public.tsx +42 -0
  891. manigent/web/frontend/src/queryClient.ts +16 -0
  892. manigent/web/frontend/src/stores/activeWorkStore.ts +88 -0
  893. manigent/web/frontend/src/stores/announcementStore.test.ts +217 -0
  894. manigent/web/frontend/src/stores/announcementStore.ts +169 -0
  895. manigent/web/frontend/src/stores/authStore.multiSession.test.ts +304 -0
  896. manigent/web/frontend/src/stores/authStore.test.ts +235 -0
  897. manigent/web/frontend/src/stores/authStore.ts +693 -0
  898. manigent/web/frontend/src/stores/deskLabelViewStore.test.ts +162 -0
  899. manigent/web/frontend/src/stores/deskLabelViewStore.ts +234 -0
  900. manigent/web/frontend/src/stores/deskLinkStore.ts +54 -0
  901. manigent/web/frontend/src/stores/deskStore.test.ts +53 -0
  902. manigent/web/frontend/src/stores/deskStore.ts +93 -0
  903. manigent/web/frontend/src/stores/heldPostsStore.ts +78 -0
  904. manigent/web/frontend/src/stores/mcpAuthStore.ts +52 -0
  905. manigent/web/frontend/src/stores/modelSelectionStore.ts +33 -0
  906. manigent/web/frontend/src/stores/perUserStores.ts +79 -0
  907. manigent/web/frontend/src/stores/runnerVersionStore.test.ts +81 -0
  908. manigent/web/frontend/src/stores/runnerVersionStore.ts +60 -0
  909. manigent/web/frontend/src/stores/skillAuthStore.ts +54 -0
  910. manigent/web/frontend/src/stores/switchReset.test.ts +236 -0
  911. manigent/web/frontend/src/stores/tokenExp.ts +32 -0
  912. manigent/web/frontend/src/stores/uiStore.ts +186 -0
  913. manigent/web/frontend/src/stores/unreadStore.ts +204 -0
  914. manigent/web/frontend/src/stores/unsavedGuard.ts +22 -0
  915. manigent/web/frontend/src/styles/design/fonts/JetBrainsMono-Latin.woff2 +0 -0
  916. manigent/web/frontend/src/styles/design/fonts/Nunito-Latin.woff2 +0 -0
  917. manigent/web/frontend/src/styles/design/fonts/Nunito-LatinExt.woff2 +0 -0
  918. manigent/web/frontend/src/styles/design/tokens.css +160 -0
  919. manigent/web/frontend/src/styles/homepage.css +242 -0
  920. manigent/web/frontend/src/styles/index.css +126 -0
  921. manigent/web/frontend/src/styles/landing.css +52 -0
  922. manigent/web/frontend/src/test/authFixtures.ts +154 -0
  923. manigent/web/frontend/src/test/fetchHarness.ts +203 -0
  924. manigent/web/frontend/src/test/fixtures/channelThreads.smoke.json +42 -0
  925. manigent/web/frontend/src/test/hubFixtures.ts +212 -0
  926. manigent/web/frontend/src/test/queryWrapper.tsx +9 -0
  927. manigent/web/frontend/src/test/suiteHygiene.test.ts +212 -0
  928. manigent/web/frontend/src/types/channelThreadName.test.ts +16 -0
  929. manigent/web/frontend/src/types/index.ts +903 -0
  930. manigent/web/frontend/src/utils/agentAccessCopy.test.ts +53 -0
  931. manigent/web/frontend/src/utils/agentAccessCopy.ts +231 -0
  932. manigent/web/frontend/src/utils/agentDisplay.ts +81 -0
  933. manigent/web/frontend/src/utils/briefSchedule.ts +25 -0
  934. manigent/web/frontend/src/utils/channelThreads.test.ts +109 -0
  935. manigent/web/frontend/src/utils/channelThreads.ts +179 -0
  936. manigent/web/frontend/src/utils/chatroomDisplay.ts +11 -0
  937. manigent/web/frontend/src/utils/deskLinks.test.ts +155 -0
  938. manigent/web/frontend/src/utils/deskLinks.ts +115 -0
  939. manigent/web/frontend/src/utils/deskRecipient.ts +29 -0
  940. manigent/web/frontend/src/utils/formatDate.ts +102 -0
  941. manigent/web/frontend/src/utils/frontDesk.ts +65 -0
  942. manigent/web/frontend/src/utils/hubChannelView.test.ts +178 -0
  943. manigent/web/frontend/src/utils/hubChannelView.ts +307 -0
  944. manigent/web/frontend/src/utils/hubDelivery.test.ts +105 -0
  945. manigent/web/frontend/src/utils/hubDelivery.ts +137 -0
  946. manigent/web/frontend/src/utils/hubDisplay.test.ts +157 -0
  947. manigent/web/frontend/src/utils/hubDisplay.ts +221 -0
  948. manigent/web/frontend/src/utils/hubThread.test.ts +184 -0
  949. manigent/web/frontend/src/utils/hubThread.ts +247 -0
  950. manigent/web/frontend/src/utils/labelErrors.test.ts +127 -0
  951. manigent/web/frontend/src/utils/labelErrors.ts +123 -0
  952. manigent/web/frontend/src/utils/labelView.test.ts +846 -0
  953. manigent/web/frontend/src/utils/labelView.ts +760 -0
  954. manigent/web/frontend/src/utils/legalUrls.ts +11 -0
  955. manigent/web/frontend/src/utils/messageSender.test.ts +32 -0
  956. manigent/web/frontend/src/utils/messageSender.ts +29 -0
  957. manigent/web/frontend/src/utils/oauthScopeCopy.ts +73 -0
  958. manigent/web/frontend/src/utils/personalize.ts +49 -0
  959. manigent/web/frontend/src/utils/reachableAgents.test.ts +36 -0
  960. manigent/web/frontend/src/utils/reachableAgents.ts +24 -0
  961. manigent/web/frontend/src/utils/recencyBuckets.ts +61 -0
  962. manigent/web/frontend/src/utils/roomTrust.test.ts +94 -0
  963. manigent/web/frontend/src/utils/roomTrust.ts +111 -0
  964. manigent/web/frontend/src/utils/snippetHighlight.test.ts +63 -0
  965. manigent/web/frontend/src/utils/snippetHighlight.ts +42 -0
  966. manigent/web/frontend/src/utils/sopRecipient.ts +37 -0
  967. manigent/web/frontend/src/utils/sopRequest.test.ts +91 -0
  968. manigent/web/frontend/src/utils/sopRequest.ts +68 -0
  969. manigent/web/frontend/src/utils/sopSchedule.ts +26 -0
  970. manigent/web/frontend/src/utils/sopSection.test.ts +148 -0
  971. manigent/web/frontend/src/utils/sopSection.ts +143 -0
  972. manigent/web/frontend/src/utils/textSearch.test.ts +60 -0
  973. manigent/web/frontend/src/utils/textSearch.ts +43 -0
  974. manigent/web/frontend/src/utils/thoughtPartner.ts +26 -0
  975. manigent/web/frontend/src/utils/threadName.test.ts +131 -0
  976. manigent/web/frontend/src/utils/threadName.ts +122 -0
  977. manigent/web/frontend/src/utils/ticketView.test.ts +296 -0
  978. manigent/web/frontend/src/utils/ticketView.ts +175 -0
  979. manigent/web/frontend/src/utils/todoAttachments.desiredState.test.ts +180 -0
  980. manigent/web/frontend/src/utils/todoAttachments.ts +126 -0
  981. manigent/web/frontend/src/utils/todoDraft.test.ts +84 -0
  982. manigent/web/frontend/src/utils/todoDraft.ts +46 -0
  983. manigent/web/frontend/src/utils/todoSearch.test.ts +82 -0
  984. manigent/web/frontend/src/utils/todoSearch.ts +43 -0
  985. manigent/web/frontend/src/utils/versionMatch.test.ts +295 -0
  986. manigent/web/frontend/src/utils/versionMatch.ts +245 -0
  987. manigent/web/frontend/src/vite-env.d.ts +1 -0
  988. manigent/web/frontend/tailwind.config.js +86 -0
  989. manigent/web/frontend/tos.html +188 -0
  990. manigent/web/frontend/tsconfig.dts.json +25 -0
  991. manigent/web/frontend/tsconfig.json +26 -0
  992. manigent/web/frontend/tsconfig.node.json +11 -0
  993. manigent/web/frontend/vite.config.ts +41 -0
  994. manigent/web/frontend/vitest.config.ts +16 -0
  995. manigent/ws_edge/app.py +514 -0
  996. manigent/ws_edge/auth.py +104 -0
  997. manigent/ws_edge/link.py +282 -0
  998. manigent/ws_edge/registry.py +206 -0
  999. manigent/ws_edge/terminal_relay.py +269 -0
  1000. manigent_server-0.1.0.dist-info/METADATA +138 -0
  1001. manigent_server-0.1.0.dist-info/RECORD +1004 -0
  1002. manigent_server-0.1.0.dist-info/WHEEL +4 -0
  1003. manigent_server-0.1.0.dist-info/entry_points.txt +2 -0
  1004. manigent_server-0.1.0.dist-info/licenses/LICENSE +68 -0
@@ -0,0 +1,741 @@
1
+ # SPDX-License-Identifier: BUSL-1.1
2
+ """The room bridge: a retained hub agent taking part in a workroom
3
+ (plan-agent-access 4.2).
4
+
5
+ The stand-in is a ghost member of the room (``external``, kind
6
+ ``hub_stand_in``): it has no runner and no changelog. ``RoomBridgeSubscriber``
7
+ relays a room message to it only when the message addresses it (a qualified
8
+ ``@name@deployment`` mention, or it is expected to answer), the sender is a
9
+ local user or agent, the message is not itself a mirror, ``bridge_allowed``
10
+ holds at that moment, and the outbound inspection passes. Only the files
11
+ uploaded WITH that message go along. The remote agent's verified reply is
12
+ mirrored back into the room as the stand-in, fenced in every turn and kept
13
+ out of memory, and completes the batch it answers.
14
+
15
+ **One send-time predicate.** ``bridge_allowed`` is true iff the owner's
16
+ ``hub_retained`` row for the stand-in is ``active``, the hub mode allows
17
+ sending, the stand-in's participant row in the room has ``bridge_state``
18
+ ``active`` and the room's inbound flow is open. It reads the store every
19
+ time. ``RoomBridgeSubscriber`` asks it before every relay (the flow part is
20
+ asked by the outbound inspection itself, which holds a relay out of a
21
+ stopped room until reopen) and the hub adapter's ``prepare_relay`` asks it
22
+ again before every send of a bridge thread's envelope, so an unretain,
23
+ delisting or stop that races a queued envelope still wins.
24
+
25
+ **The bridge thread** is a hidden hub thread of the room's owner
26
+ (``hub_threads.kind = "room_bridge"``, bound to the workroom and the
27
+ stand-in), carried by ``RequesterMirror`` exactly like a DM thread: the
28
+ relayed message is appended there and sent signed once. Bridge thread
29
+ projects are plumbing: every project list filters them out server-side.
30
+
31
+ **Replies** are admitted once (path ``hub_reply_bridge``, against the
32
+ workroom), then appended to the bridge thread and mirrored into the workroom
33
+ as the stand-in on the SAME ingress ticket (``OutsideIngress.extend``), with
34
+ ``mirrored_from`` and no wake: ``expects_response_from`` is empty and
35
+ mentions in remote text are never parsed. The reply closes the stand-in's
36
+ slot of the batch it answers, so BATCH_COMPLETE wakes the coordinator in its
37
+ normal full turn, where the reply is fenced and kept out of memory (it is a
38
+ hub stand-in's row: models/provenance.py). After an unretain (or any inactive
39
+ state) a late reply is appended to the hidden thread only.
40
+ """
41
+ from __future__ import annotations
42
+
43
+ import asyncio
44
+ import hashlib
45
+ import logging
46
+ import unicodedata
47
+ import uuid
48
+ from datetime import UTC, datetime
49
+ from pathlib import PurePosixPath
50
+ from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence
51
+
52
+ from fastapi import HTTPException
53
+
54
+ from manigent.api.control import ControlMessageType
55
+ from manigent.models.agent import Agent
56
+ from manigent.models.chatroom import Chatroom, parse_mentions
57
+ from manigent.models.project import Project
58
+ from manigent.models.provenance import SYSTEM_AUTHORS, STAND_IN_SEPARATOR, outside_author
59
+ from manigent.models.user import User
60
+ from manigent.sync.changelog import (
61
+ BatchCompletePayload,
62
+ ChangelogEntry,
63
+ ChangelogEntryType,
64
+ FilePayload,
65
+ MessagePayload,
66
+ MirroredFromRef,
67
+ ParticipantUpdatedPayload,
68
+ RoomCreatedParticipant,
69
+ )
70
+ from manigent.sync.subscriber import ChangelogSubscriber
71
+ from manigent.utils.file_io import FileUtil
72
+
73
+ from .browser import BridgeDelivery, HubError
74
+
75
+ if TYPE_CHECKING:
76
+ from manigent.server.channels.hub_store import BridgeRelay, HubThread, RemoteAgent
77
+ from manigent.server.context import ServerContext
78
+
79
+ from .runtime import HubRuntime
80
+
81
+ logger = logging.getLogger(__name__)
82
+
83
+ UC = "user-communication"
84
+ KIND_STAND_IN = "hub_stand_in"
85
+ #: BridgeDelivery states.
86
+ QUEUED, RELAYED, DELIVERED, ANSWERED, FAILED, CANCELLED = (
87
+ "queued", "relayed", "delivered", "answered", "failed", "cancelled")
88
+ #: The deliverer key of a held relay (``OutsideIngress.register_deliverer``).
89
+ VIA_BRIDGE_OUTBOUND = "bridge_outbound"
90
+ VIA_BRIDGE_REPLY = "hub_reply_bridge"
91
+
92
+ #: Names an instruction or tool loader recognises; an incoming file with one
93
+ #: of these names is renamed ``<name>.remote.txt`` so nothing loads it.
94
+ LOADER_NAMES = frozenset({
95
+ "skill.md", "claude.md", "agents.md", "gemini.md", ".mcp.json", "settings.json",
96
+ "config.toml", "opencode.json",
97
+ })
98
+ #: Incoming files are flattened to at most this many path segments under
99
+ #: ``files/hub/<name>@<deployment>/``.
100
+ MAX_INCOMING_DEPTH = 2
101
+
102
+
103
+ def confine_incoming_path(sender_label: str, name: str) -> Optional[str]:
104
+ """Where an incoming file from a retained agent is written in the room's
105
+ files, or None when the name is refused.
106
+
107
+ NFC-normalised; absolute paths, ``..``, empty segments and any segment
108
+ starting with ``.`` refused; flattened to at most two levels; loader names
109
+ renamed; always under ``hub/<sender_label>/``."""
110
+ if not isinstance(name, str) or not name or "\x00" in name or "\\" in name:
111
+ return None
112
+ name = unicodedata.normalize("NFC", name)
113
+ if name.startswith("/"):
114
+ return None
115
+ parts = name.split("/")
116
+ if any(p in ("", ".", "..") or p.startswith(".") for p in parts):
117
+ return None
118
+ if len(parts) > MAX_INCOMING_DEPTH:
119
+ parts = [*parts[: MAX_INCOMING_DEPTH - 1], "_".join(parts[MAX_INCOMING_DEPTH - 1:])]
120
+ if parts[-1].lower() in LOADER_NAMES:
121
+ parts[-1] = f"{parts[-1]}.remote.txt"
122
+ label = unicodedata.normalize("NFC", sender_label).replace("/", "_")
123
+ if not label or label.startswith("."):
124
+ return None
125
+ return str(PurePosixPath("hub", label, *parts))
126
+
127
+
128
+ def incoming_file_payloads(sender_label: str, author_id: str, chatroom: str,
129
+ files: Sequence[tuple[str, bytes]]) -> list[FilePayload]:
130
+ """FILE payloads for the files a remote agent sent with a reply, each
131
+ under ``files/hub/<sender_label>/`` (``confine_incoming_path``) and
132
+ authored by its stand-in, so the guard asks for the reply's ticket and the
133
+ manifest lists them as outside content. A refused name is left out."""
134
+ out = []
135
+ for name, data in files:
136
+ path = confine_incoming_path(sender_label, name)
137
+ if path is None:
138
+ logger.warning("Hub reply file with a refused name: not written")
139
+ continue
140
+ out.append(FilePayload(
141
+ chatroom_name=chatroom, filename=path, content_b64=FileUtil.to_base64(data),
142
+ sha256=hashlib.sha256(data).hexdigest(), from_participant_id=author_id,
143
+ from_participant_name=sender_label,
144
+ ))
145
+ return out
146
+
147
+
148
+ def _now() -> float:
149
+ return datetime.now(UTC).timestamp()
150
+
151
+
152
+ def stand_in_participant(stand_in: "RemoteAgent") -> RoomCreatedParticipant:
153
+ """The ghost membership row of a stand-in: external (no runner), kind
154
+ ``hub_stand_in``, bridge active."""
155
+ return RoomCreatedParticipant(
156
+ id=stand_in.id, name=stand_in.display, external=True, kind=KIND_STAND_IN,
157
+ bridge_state="active", inactive_reason=None,
158
+ hub={"hub_agent_id": stand_in.id, "name": stand_in.agent_name,
159
+ "deployment_name": stand_in.deployment_name, "deployment_fingerprint": stand_in.genesis_fp},
160
+ )
161
+
162
+
163
+ def _allowlist_admits(project: Project, stand_in: "RemoteAgent") -> bool:
164
+ """As "Other accounts": ``Project.admits_hub_agent`` owns the rule."""
165
+ return project.admits_hub_agent(
166
+ stand_in.id, f"{stand_in.agent_name}@{stand_in.deployment_name}")
167
+
168
+
169
+ def _conflict(code: str, message: str) -> HTTPException:
170
+ return HTTPException(409, detail={"code": code, "message": message})
171
+
172
+
173
+ def looks_like_hub_ref(ref: str) -> bool:
174
+ """``name@deployment`` or a hub agent id (``{genesis}~{name}``)."""
175
+ return STAND_IN_SEPARATOR in ref or ("@" in ref.strip("@") and not ref.startswith("@"))
176
+
177
+
178
+ class RoomBridge:
179
+ """The room bridge of one server (built beside the requester mirror)."""
180
+
181
+ def __init__(self, runtime: "HubRuntime") -> None:
182
+ self.runtime = runtime
183
+ self._tasks: set[asyncio.Task] = set()
184
+
185
+ @property
186
+ def hub(self):
187
+ return self.runtime.hub
188
+
189
+ @property
190
+ def _ctx(self) -> "ServerContext":
191
+ return self.runtime._ctx
192
+
193
+ @property
194
+ def mirror(self):
195
+ return self.runtime.mirror
196
+
197
+ def _ingress(self):
198
+ return getattr(self._ctx, "outside_ingress", None)
199
+
200
+ # ── the predicate ───────────────────────────────────────────────────
201
+
202
+ def mode_allows_sending(self) -> bool:
203
+ config = self.runtime.config
204
+ if not config.enabled or self.runtime.mode != "full":
205
+ return False
206
+ reg = self.runtime.registration()
207
+ return reg is not None and reg.status == "active"
208
+
209
+ def bridge_allowed(self, owner_user_id: str, stand_in_id: str, project_id: str, room: str, *,
210
+ check_flow: bool = True) -> bool:
211
+ """See the module docstring. Read straight from the store."""
212
+ row = self.hub.retained_row(owner_user_id, stand_in_id)
213
+ if row is None or row.state != "active" or not self.mode_allows_sending():
214
+ return False
215
+ chatroom = Chatroom.get(project_id, room, self._ctx.model_ctx)
216
+ if chatroom is None:
217
+ return False
218
+ member = chatroom.participant_rows().get(stand_in_id)
219
+ if member is None or member.get("kind") != KIND_STAND_IN or member.get("bridge_state") != "active":
220
+ return False
221
+ return not check_flow or chatroom.flow.inbound_flow != "stopped"
222
+
223
+ def why_not(self, owner_user_id: str, stand_in_id: str) -> str:
224
+ row = self.hub.retained_row(owner_user_id, stand_in_id)
225
+ if row is None:
226
+ return "unretained"
227
+ if row.state != "active":
228
+ return "agent_unlisted" if row.state == "unlisted" else row.state
229
+ if not self.mode_allows_sending():
230
+ return "hub_off"
231
+ return "inactive"
232
+
233
+ # ── invite ──────────────────────────────────────────────────────────
234
+
235
+ def resolve_invitee(self, project: Project, ref: str, room: Optional[str]) -> RoomCreatedParticipant:
236
+ """A ``name@deployment`` or hub agent id the project owner retained:
237
+ its stand-in's membership row. ``not_retained`` [409] when it is not
238
+ retained (active), 403 outside the allowlist, ``room_flow_stopped``
239
+ [409] when the room is stopped."""
240
+ ref = ref.strip().lstrip("@")
241
+ stand_in = None
242
+ owner = project.created_by or ""
243
+ for row in self.hub.retained_for(owner):
244
+ handle = f"{row.agent_name}@{row.deployment_name}".lower()
245
+ if ref.lower() in (row.stand_in_id.lower(), handle) and row.state == "active":
246
+ stand_in = self.hub.remote_agent(row.stand_in_id)
247
+ break
248
+ if stand_in is None:
249
+ raise _conflict("not_retained", f"{ref!r} is not a hub agent your user retained")
250
+ if not _allowlist_admits(project, stand_in):
251
+ raise HTTPException(403, f"{stand_in.display!r} is not in this project's invitable allowlist")
252
+ if room is not None and not self.runtime._ctx.room_flow.is_open(project.id, room):
253
+ raise _conflict("room_flow_stopped", "this room's flow is stopped")
254
+ return stand_in_participant(stand_in)
255
+
256
+ # ── the bridge thread ───────────────────────────────────────────────
257
+
258
+ async def ensure(self, project: Project, room: str, stand_in: "RemoteAgent") -> "HubThread":
259
+ """The owner's hidden bridge thread for (room, stand-in); idempotent."""
260
+ user = User.get(project.created_by or "", self._ctx.model_ctx)
261
+ if user is None:
262
+ raise HubError("project_not_found", "the room has no owner")
263
+ thread = self.hub.bridge_thread(user.id, project.id, room, stand_in.id)
264
+ if thread is None:
265
+ thread = self.hub.open_bridge_thread(
266
+ user_id=user.id, project_id=str(uuid.uuid4()), stand_in=stand_in,
267
+ bound_project_id=project.id, bound_room=room, now=self.runtime._now())
268
+ await self.mirror._ensure_project(thread, stand_in, user)
269
+ return thread
270
+
271
+ # ── room -> B ───────────────────────────────────────────────────────
272
+
273
+ def addressed_stand_ins(self, room: Chatroom, payload: MessagePayload) -> list[str]:
274
+ """Stand-in members this message addresses: a qualified mention of
275
+ one, or one in ``expects_response_from``."""
276
+ rows = room.participant_rows()
277
+ out: list[str] = []
278
+ for pid in payload.expects_response_from or []:
279
+ if (rows.get(pid) or {}).get("kind") == KIND_STAND_IN and pid not in out:
280
+ out.append(pid)
281
+ for name, deployment in parse_mentions(payload.content).qualified:
282
+ pid = room.stand_in_member(name, deployment)
283
+ if pid is not None and pid not in out:
284
+ out.append(pid)
285
+ # An inactive member (unretained, delisted) is addressed by nobody:
286
+ # its room note was posted when it went inactive.
287
+ return [pid for pid in out if (rows.get(pid) or {}).get("bridge_state") == "active"]
288
+
289
+ def _attached_files(self, project: Project, entry: ChangelogEntry) -> list[tuple[str, bytes]]:
290
+ """Only files uploaded WITH this message: FILE entries in the same
291
+ room, by the same author, appended before it, and either linked to
292
+ it (``source_version`` is the message's version: a user's message
293
+ with attachments, one batch) or carrying its non-null
294
+ ``source_version`` (one agent action block). Nothing else ever goes
295
+ along."""
296
+ payload = entry.payload
297
+ from manigent.sync.changelog import iter_entries
298
+
299
+ path = self._ctx.model_ctx.changelog_dir(project.id, project.name) / "changelog.ndjson"
300
+ names: list[str] = []
301
+ for e in iter_entries(path) if path.exists() else []:
302
+ if e.version >= entry.version:
303
+ break
304
+ p = e.payload
305
+ if (e.entry_type in (ChangelogEntryType.FILE_CREATED, ChangelogEntryType.FILE_UPDATED)
306
+ and e.source_version is not None
307
+ and e.source_version in (entry.version, entry.source_version)
308
+ and p.chatroom_name == payload.chatroom_name
309
+ and p.from_participant_id == payload.from_participant_id and p.filename not in names):
310
+ names.append(p.filename)
311
+ room = Chatroom.get_in(project, payload.chatroom_name)
312
+ out = []
313
+ for name in names:
314
+ data = room.get_file(name) if room is not None else None
315
+ if data:
316
+ out.append((name, data))
317
+ return out
318
+
319
+ async def relay(self, project: Project, entry: ChangelogEntry, stand_in_id: str) -> None:
320
+ """Relay one addressed room message to one stand-in (see the module
321
+ docstring). Idempotent on the room message id."""
322
+ from manigent.server.inspection import FileRef, InspectionRequest, OutsidePath
323
+
324
+ payload: MessagePayload = entry.payload
325
+ room = payload.chatroom_name
326
+ owner = project.created_by or ""
327
+ now = self.runtime._now()
328
+ if not self.hub.claim_relay(room_project_id=project.id, room=room, room_message_id=payload.id,
329
+ stand_in_id=stand_in_id, user_id=owner, state=QUEUED, reason=None, now=now):
330
+ return # a replay: decided already
331
+ if not self.bridge_allowed(owner, stand_in_id, project.id, room, check_flow=False):
332
+ await self._end(project, room, payload.id, stand_in_id, FAILED, self.why_not(owner, stand_in_id),
333
+ close_slot=True)
334
+ return
335
+ try:
336
+ from .agent_send_guard import via_agent_name
337
+
338
+ guard = self._send_guard()
339
+ sender_agent = Agent.get(payload.via_agent_id or payload.from_participant_id, self._ctx.model_ctx)
340
+ via_agent_name(sender_agent, self._ctx.model_ctx) # a name the hub refuses costs nothing
341
+ guard.charge(user_id=owner, agent_id=sender_agent.id if sender_agent else None,
342
+ target=stand_in_id, room=(project.id, room))
343
+ except HubError as exc:
344
+ await self._end(project, room, payload.id, stand_in_id, FAILED, exc.code, close_slot=True)
345
+ return
346
+ files = self._attached_files(project, entry)
347
+ stand_in = self.hub.remote_agent(stand_in_id)
348
+ label = stand_in.display if stand_in is not None else stand_in_id
349
+ replay = {"via": VIA_BRIDGE_OUTBOUND, "project_id": project.id, "room": room,
350
+ "room_message_id": payload.id, "stand_in_id": stand_in_id,
351
+ "sender_id": payload.from_participant_id, "sender_name": payload.from_participant_name,
352
+ "via_agent_id": payload.via_agent_id,
353
+ "text": payload.content,
354
+ # Each file's sha256 as inspected: a held relay sends these
355
+ # bytes or nothing (send_relay).
356
+ "files": [{"name": name, "sha256": hashlib.sha256(data).hexdigest()} for name, data in files],
357
+ "version": entry.version}
358
+ ingress = self._ingress()
359
+ if ingress is not None:
360
+ admission = await ingress.admit(InspectionRequest(
361
+ path=OutsidePath.BRIDGE_OUTBOUND, direction="outbound", owner_user_id=owner,
362
+ project_id=project.id, chatroom=room, author_id=payload.from_participant_id,
363
+ author_label=f"{payload.from_participant_name} to {label}", text=payload.content,
364
+ files=tuple(FileRef(name=n, sha256=hashlib.sha256(d).hexdigest(), size=len(d)) for n, d in files),
365
+ source_ref=f"{payload.id}/{stand_in_id}",
366
+ ), replay=replay)
367
+ if admission.verdict.value == "hold":
368
+ reason = admission.reason_code if admission.reason_code == "flow_stopped" else "held"
369
+ self.hub.set_relay(payload.id, stand_in_id, state=QUEUED, reason=reason, now=self.runtime._now())
370
+ await self._push(project, room, payload.id, stand_in_id)
371
+ return
372
+ if admission.verdict.value == "block":
373
+ await self._end(project, room, payload.id, stand_in_id, FAILED, "blocked", close_slot=True)
374
+ return
375
+ await self.send_relay(replay)
376
+
377
+ async def close_held_relay(self, replay: Mapping[str, Any]) -> None:
378
+ """A relay held while the room was stopped that expired: its delivery
379
+ line fails (``held_expired``) and the stand-in's slot closes."""
380
+ try:
381
+ project = Project.get(replay["project_id"], self._ctx.model_ctx)
382
+ except ValueError:
383
+ return
384
+ await self._end(project, replay["room"], replay["room_message_id"], replay["stand_in_id"], FAILED,
385
+ "held_expired", close_slot=True)
386
+
387
+ async def block_held_relay(self, replay: Mapping[str, Any]) -> None:
388
+ """A held relay blocked on release: its delivery line fails
389
+ (``blocked``) and the stand-in's slot closes, as on a live block."""
390
+ try:
391
+ project = Project.get(replay["project_id"], self._ctx.model_ctx)
392
+ except ValueError:
393
+ return
394
+ await self._end(project, replay["room"], replay["room_message_id"], replay["stand_in_id"], FAILED,
395
+ "blocked", close_slot=True)
396
+
397
+ async def send_relay(self, replay: Mapping[str, Any], _ticket: Any = None) -> None:
398
+ """The send itself (also the deliverer of a relay held while the room
399
+ was stopped): only a relay still ``queued`` (an unretain cancelled
400
+ it otherwise), re-check the predicate, re-read each file and refuse
401
+ the relay (``file_changed``) when its bytes are not the ones the
402
+ inspection saw, append to the bridge thread, sign once and queue."""
403
+ from .agent_send_guard import via_agent_name
404
+ from .mirror import OutgoingFile
405
+
406
+ try:
407
+ project = Project.get(replay["project_id"], self._ctx.model_ctx)
408
+ except ValueError:
409
+ return
410
+ room, stand_in_id, message_id = replay["room"], replay["stand_in_id"], replay["room_message_id"]
411
+ row = self.hub.relay_row(message_id, stand_in_id)
412
+ if row is None or row.state != QUEUED:
413
+ return # cancelled or decided meanwhile
414
+ owner = project.created_by or ""
415
+ if not self.bridge_allowed(owner, stand_in_id, project.id, room):
416
+ await self._end(project, room, message_id, stand_in_id, FAILED, self.why_not(owner, stand_in_id),
417
+ close_slot=True)
418
+ return
419
+ stand_in = self.hub.remote_agent(stand_in_id)
420
+ user = User.get(owner, self._ctx.model_ctx)
421
+ chatroom = Chatroom.get_in(project, room)
422
+ if stand_in is None or user is None or chatroom is None:
423
+ return
424
+ files = []
425
+ for ref in replay.get("files") or []:
426
+ data = chatroom.get_file(ref["name"])
427
+ if not data or hashlib.sha256(data).hexdigest() != ref["sha256"]:
428
+ await self._end(project, room, message_id, stand_in_id, FAILED, "file_changed", close_slot=True)
429
+ return
430
+ files.append(OutgoingFile(name=PurePosixPath(ref["name"]).name, mime="application/octet-stream",
431
+ data=data))
432
+ # The agent sending: the message's agent author, or the agent whose
433
+ # credential posted a user-authored message; None for the user's own.
434
+ sender = Agent.get(replay.get("via_agent_id") or replay.get("sender_id") or "", self._ctx.model_ctx)
435
+ try:
436
+ via = via_agent_name(sender, self._ctx.model_ctx)
437
+ thread = await self.ensure(project, room, stand_in)
438
+ sent = await self.mirror.send(
439
+ user, thread.project_id, text=replay["text"], client_message_id=f"bridge-{message_id}",
440
+ files=files, via_agent=via, author=sender)
441
+ except HubError as exc:
442
+ await self._end(project, room, message_id, stand_in_id, FAILED, exc.code, close_slot=True)
443
+ return
444
+ self.hub.set_relay(message_id, stand_in_id, state=RELAYED, reason=None, now=self.runtime._now(),
445
+ thread_project_id=thread.project_id, hub_message_id=sent.message_id)
446
+ await self._push(project, room, message_id, stand_in_id)
447
+
448
+ def _send_guard(self):
449
+ from .agent_send_guard import AgentSendGuard
450
+
451
+ guard = getattr(self._ctx, "hub_send_guard", None)
452
+ if guard is None:
453
+ guard = self._ctx.hub_send_guard = AgentSendGuard(self._ctx.channel_store)
454
+ return guard
455
+
456
+ # ── delivery state ──────────────────────────────────────────────────
457
+
458
+ async def on_row_change(self, row, thread: "HubThread") -> None:
459
+ """A bridge thread's outbox row changed: its relay's delivery line."""
460
+ from manigent.server.channels.store import EXPIRED, FAILED as ROW_FAILED, SENT
461
+
462
+ relay = self.hub.relay_for_hub_message(row.message_id) if row.message_id else None
463
+ if relay is None or relay.state in (ANSWERED, CANCELLED, FAILED):
464
+ return
465
+ try:
466
+ project = Project.get(relay.room_project_id, self._ctx.model_ctx)
467
+ except ValueError:
468
+ return
469
+ if row.state == SENT and relay.state != DELIVERED:
470
+ self.hub.set_relay(relay.room_message_id, relay.stand_in_id, state=DELIVERED, reason=None,
471
+ now=self.runtime._now())
472
+ await self._push(project, relay.room, relay.room_message_id, relay.stand_in_id)
473
+ elif row.state in (ROW_FAILED, EXPIRED):
474
+ state = CANCELLED if row.reason == "cancelled_unretained" else FAILED
475
+ await self._end(project, relay.room, relay.room_message_id, relay.stand_in_id, state,
476
+ row.reason or "failed", close_slot=state == FAILED)
477
+
478
+ def deliveries(self, project_id: str, room: str, message_ids: Optional[set[str]] = None) -> list[BridgeDelivery]:
479
+ return [
480
+ BridgeDelivery(room_message_id=r.room_message_id, hub_agent_id=r.stand_in_id, state=r.state,
481
+ reason=r.reason)
482
+ for r in self.hub.relays_for_room(project_id, room)
483
+ if message_ids is None or r.room_message_id in message_ids
484
+ ]
485
+
486
+ async def _push(self, project: Project, room: str, message_id: str, stand_in_id: str) -> None:
487
+ relay = self.hub.relay_row(message_id, stand_in_id)
488
+ if relay is None or not project.created_by:
489
+ return
490
+ delivery = BridgeDelivery(room_message_id=message_id, hub_agent_id=stand_in_id, state=relay.state,
491
+ reason=relay.reason)
492
+ await self.runtime._push(project.created_by, ControlMessageType.HUB_BRIDGE_DELIVERY, {
493
+ "project_id": project.id, "chatroom_name": room, "delivery": delivery.model_dump(mode="json")})
494
+
495
+ async def _end(self, project: Project, room: str, message_id: str, stand_in_id: str, state: str,
496
+ reason: str, *, close_slot: bool) -> None:
497
+ self.hub.set_relay(message_id, stand_in_id, state=state, reason=reason, now=self.runtime._now())
498
+ await self._push(project, room, message_id, stand_in_id)
499
+ if close_slot:
500
+ stand_in = self.hub.remote_agent(stand_in_id)
501
+ label = stand_in.display if stand_in is not None else stand_in_id
502
+ await self.close_slot(project, room, stand_in_id,
503
+ note=f"A message to {label} was not relayed ({reason}).", reason=reason)
504
+
505
+ async def close_slot(self, project: Project, room: str, stand_in_id: str, *, note: Optional[str],
506
+ reason: Optional[str] = None) -> None:
507
+ """Close the stand-in's slot in the room's open batch
508
+ (``RequesterMirror.close_stand_in_slot``) and post a one-line system note."""
509
+ await self.mirror.close_stand_in_slot(project, room, stand_in_id, reason=reason)
510
+ if note:
511
+ ingress = self._ingress()
512
+ if ingress is not None:
513
+ await ingress.post_note(project.id, room, note)
514
+
515
+ # ── B -> room ───────────────────────────────────────────────────────
516
+
517
+ async def on_reply(self, thread: "HubThread", envelope, *, entry_id: str, event_id: Optional[str],
518
+ files: Sequence[tuple[str, bytes]] = ()) -> str:
519
+ """A verified reply into a bridge thread, with the files it carried
520
+ (checked against their FileRefs). Returns the verdict."""
521
+ from manigent.server.inspection import InspectionRequest, OutsidePath, file_refs
522
+
523
+ stand_in = self.hub.remote_agent(thread.stand_in_id)
524
+ name = stand_in.display if stand_in is not None else thread.agent_name
525
+ text = envelope.body.text
526
+ # Confined once; the room each copy lands in is set where it is stored.
527
+ payloads = incoming_file_payloads(name, thread.stand_in_id, thread.bound_room or UC, files)
528
+ replay = {"via": VIA_BRIDGE_REPLY, "thread_project_id": thread.project_id,
529
+ "stand_in_id": thread.stand_in_id, "name": name, "entry_id": entry_id, "text": text,
530
+ "in_reply_to": envelope.in_reply_to, "files": [p.model_dump(mode="json") for p in payloads]}
531
+ try:
532
+ project = Project.get(thread.bound_project_id or "", self._ctx.model_ctx)
533
+ except ValueError:
534
+ project = None
535
+ live = project is not None and self.bridge_allowed(
536
+ thread.user_id, thread.stand_in_id, project.id, thread.bound_room or "", check_flow=False)
537
+ ingress = self._ingress()
538
+ if not live:
539
+ # Unretained, delisted or the room is gone: the hidden thread
540
+ # only, never mirrored.
541
+ thread_project = Project.get(thread.project_id, self._ctx.model_ctx)
542
+ ticket = None
543
+ if ingress is not None:
544
+ admission = await ingress.admit(InspectionRequest(
545
+ path=OutsidePath.HUB_REPLY_BRIDGE, direction="inbound",
546
+ owner_user_id=thread.user_id, project_id=thread.project_id, chatroom=UC,
547
+ author_id=thread.stand_in_id, author_label=name, text=text, files=file_refs(payloads),
548
+ source_ref=event_id or entry_id,
549
+ ), replay={**replay, "thread_only": True})
550
+ if not admission.passed:
551
+ return admission.verdict.value
552
+ ticket = admission.ticket
553
+ await self.mirror._store_reply(thread_project, thread.stand_in_id, name, entry_id, text, ticket,
554
+ files=payloads)
555
+ return "pass"
556
+ ticket = None
557
+ if ingress is not None:
558
+ admission = await ingress.admit(InspectionRequest(
559
+ path=OutsidePath.HUB_REPLY_BRIDGE, direction="inbound", owner_user_id=project.created_by or "",
560
+ project_id=project.id, chatroom=thread.bound_room or "", author_id=thread.stand_in_id,
561
+ author_label=name, text=text, files=file_refs(payloads), source_ref=event_id or entry_id,
562
+ ), replay=replay)
563
+ if not admission.passed:
564
+ if admission.verdict.value == "block":
565
+ await self.block_reply(replay)
566
+ return admission.verdict.value
567
+ ticket = admission.ticket
568
+ await self.deliver_reply(replay, ticket)
569
+ return "pass"
570
+
571
+ async def block_reply(self, replay: Mapping[str, Any]) -> None:
572
+ """A blocked reply (live, or held and blocked on release): the relay
573
+ it answers fails (``blocked``) and the stand-in's slot closes. A
574
+ thread-only reply has neither."""
575
+ thread = self.hub.thread_for_project(replay["thread_project_id"])
576
+ if thread is None or replay.get("thread_only"):
577
+ return
578
+ try:
579
+ project = Project.get(thread.bound_project_id or "", self._ctx.model_ctx)
580
+ except ValueError:
581
+ return
582
+ in_reply_to = replay.get("in_reply_to")
583
+ relay = self.hub.relay_for_hub_message(in_reply_to) if in_reply_to else None
584
+ if relay is not None:
585
+ self.hub.set_relay(relay.room_message_id, relay.stand_in_id, state=FAILED,
586
+ reason="blocked", now=self.runtime._now())
587
+ await self._push(project, relay.room, relay.room_message_id, relay.stand_in_id)
588
+ await self.close_slot(project, thread.bound_room or "", thread.stand_in_id, note=None, reason="blocked")
589
+
590
+ async def deliver_reply(self, replay: Mapping[str, Any], ticket: Any) -> None:
591
+ """Append the admitted reply to the bridge thread, then mirror it into
592
+ the workroom as the stand-in, on the same ticket. Also the deliverer
593
+ of a reply held while the room was stopped."""
594
+ thread = self.hub.thread_for_project(replay["thread_project_id"])
595
+ if thread is None:
596
+ return
597
+ thread_project = Project.get(thread.project_id, self._ctx.model_ctx)
598
+ ingress = self._ingress()
599
+ files = [FilePayload.model_validate(f) for f in replay.get("files") or ()]
600
+ if replay.get("thread_only"):
601
+ await self.mirror._store_reply(thread_project, replay["stand_in_id"], replay["name"],
602
+ replay["entry_id"], replay["text"], ticket, files=files)
603
+ return
604
+ both = ingress.extend(ticket, (thread.project_id, UC)) if (ingress is not None and ticket is not None) \
605
+ else ticket
606
+ await self.mirror._store_reply(thread_project, replay["stand_in_id"], replay["name"], replay["entry_id"],
607
+ replay["text"], both, files=files)
608
+ try:
609
+ project = Project.get(thread.bound_project_id or "", self._ctx.model_ctx)
610
+ except ValueError:
611
+ return
612
+ room = thread.bound_room or ""
613
+ if Chatroom.get_in(project, room) is None:
614
+ return
615
+ thread_entry = next((e for e in reversed(self._entries(thread_project))
616
+ if e.entry_type == ChangelogEntryType.MESSAGE
617
+ and getattr(e.payload, "id", None) == replay["entry_id"]), None)
618
+ room_entry_id = str(uuid.uuid5(uuid.NAMESPACE_URL, f"manigent-bridge-mirror:{replay['entry_id']}"))
619
+ runloop = await self._ctx.runloop_manager.get_or_create(project.id, project.name, project.coordinator_id)
620
+ if any(e.entry_type == ChangelogEntryType.MESSAGE and getattr(e.payload, "id", None) == room_entry_id
621
+ for e in runloop.get_entries_since(0)):
622
+ return # mirrored already (idempotent)
623
+ # The reply's files land in the workroom first, as outside content
624
+ # (files/hub/<name>@<deployment>/), on the same ticket as its text.
625
+ await self.mirror.store_incoming_files(runloop, project, [f.model_copy(update={"chatroom_name": room})
626
+ for f in files], both)
627
+ entry = await runloop.append(ChangelogEntryType.MESSAGE, MessagePayload(
628
+ chatroom_name=room, id=room_entry_id, ts=datetime.now(UTC),
629
+ from_participant_id=replay["stand_in_id"], from_participant_name=replay["name"],
630
+ content=replay["text"], expects_response_from=[],
631
+ ), ingress_ticket=both, mirrored_from=MirroredFromRef(
632
+ project_id=thread.project_id, version=thread_entry.version if thread_entry is not None else 0))
633
+ version = entry.version
634
+ tracker = getattr(self._ctx, "work_tracker", None)
635
+ work = await tracker.record_response(project.id, room, replay["stand_in_id"]) if tracker else None
636
+ if work is not None and work.is_complete:
637
+ await tracker.clear_pending_work(project.id, room)
638
+ batch = await runloop.append(ChangelogEntryType.BATCH_COMPLETE, BatchCompletePayload(
639
+ chatroom_name=room, message_id=work.message_id, coordinator_id=work.coordinator_id,
640
+ responded_participants=list(work.responded_participants),
641
+ ), source_version=work.message_version)
642
+ version = batch.version
643
+ relay = self.hub.relay_for_hub_message(replay.get("in_reply_to") or "") if replay.get("in_reply_to") else None
644
+ if relay is not None:
645
+ self.hub.set_relay(relay.room_message_id, relay.stand_in_id, state=ANSWERED, reason=None,
646
+ now=self.runtime._now())
647
+ await self._push(project, room, relay.room_message_id, relay.stand_in_id)
648
+ await self.mirror._broadcast(project, version)
649
+
650
+ def _entries(self, project: Project) -> list[ChangelogEntry]:
651
+ from manigent.sync.changelog import iter_entries
652
+
653
+ path = self._ctx.model_ctx.changelog_dir(project.id, project.name) / "changelog.ndjson"
654
+ return list(iter_entries(path)) if path.exists() else []
655
+
656
+ # ── unretain, delisting, identity change ────────────────────────────
657
+
658
+ async def deactivate(self, user_ids: Optional[list[str]], stand_in_id: str, reason: str) -> None:
659
+ """Every bridged room of these users (all users when None) with this
660
+ stand-in: its participant row goes inactive with ``reason``
661
+ (PARTICIPANT_UPDATED), its waiting slot closes, a room note."""
662
+ threads = [t for t in self.hub.threads_for_stand_in(stand_in_id)
663
+ if t.kind == "room_bridge" and (user_ids is None or t.user_id in user_ids)]
664
+ stand_in = self.hub.remote_agent(stand_in_id)
665
+ label = stand_in.display if stand_in is not None else stand_in_id
666
+ rooms: set[tuple[str, str]] = {(t.bound_project_id or "", t.bound_room or "") for t in threads}
667
+ # Rooms the stand-in was invited into but never relayed to have no thread yet.
668
+ for project in Project.list_all(self._ctx.model_ctx):
669
+ if user_ids is not None and project.created_by not in user_ids:
670
+ continue
671
+ if stand_in_id not in (project.participating_agents or []):
672
+ continue
673
+ for room_name in project.chatrooms:
674
+ rooms.add((project.id, room_name))
675
+ for project_id, room in sorted(rooms):
676
+ try:
677
+ project = Project.get(project_id, self._ctx.model_ctx)
678
+ except ValueError:
679
+ continue
680
+ chatroom = Chatroom.get_in(project, room)
681
+ member = chatroom.participant_rows().get(stand_in_id) if chatroom is not None else None
682
+ if member is None or member.get("kind") != KIND_STAND_IN or member.get("bridge_state") == "inactive":
683
+ continue
684
+ runloop = await self._ctx.runloop_manager.get_or_create(project.id, project.name, project.coordinator_id)
685
+ await runloop.append(ChangelogEntryType.PARTICIPANT_UPDATED, ParticipantUpdatedPayload(
686
+ chatroom_name=room, participant_id=stand_in_id, bridge_state="inactive", inactive_reason=reason))
687
+ await self.close_slot(project, room, stand_in_id,
688
+ note=f"{label} can no longer take part in this room ({reason}).", reason=reason)
689
+
690
+
691
+ class RoomBridgeSubscriber(ChangelogSubscriber):
692
+ """Relays addressed local room messages to retained agents' stand-ins.
693
+ Registered on every server runloop beside the TunnelSubscriber. The relay
694
+ runs off the source runloop (its lock is held while subscribers run);
695
+ ``drain`` waits for the relays in flight."""
696
+
697
+ def __init__(self) -> None:
698
+ self._server_ctx: Optional["ServerContext"] = None
699
+ self._tasks: set[asyncio.Task] = set()
700
+
701
+ def bind_server_context(self, ctx: "ServerContext") -> None:
702
+ self._server_ctx = ctx
703
+
704
+ async def drain(self) -> None:
705
+ while self._tasks:
706
+ await asyncio.gather(*list(self._tasks), return_exceptions=True)
707
+
708
+ async def on_entry(self, entry: ChangelogEntry, project_id: str, project_name: str) -> None:
709
+ ctx = self._server_ctx
710
+ if ctx is None or entry.entry_type != ChangelogEntryType.MESSAGE or entry.mirrored_from is not None:
711
+ return
712
+ runtime = getattr(ctx, "hub_runtime", None)
713
+ bridge = getattr(runtime, "bridge", None) if runtime is not None else None
714
+ payload = entry.payload
715
+ if bridge is None or not isinstance(payload, MessagePayload):
716
+ return
717
+ if payload.from_participant_id in SYSTEM_AUTHORS or payload.is_ack:
718
+ return
719
+ try:
720
+ project = Project.get(project_id, ctx.model_ctx)
721
+ except ValueError:
722
+ return
723
+ if project.surface != "regular":
724
+ return
725
+ if payload.outside_isolated or outside_author(project, payload.from_participant_id,
726
+ ctx.model_ctx) is not None:
727
+ return # only local users and agents relay
728
+ room = Chatroom.get_in(project, payload.chatroom_name)
729
+ if room is None:
730
+ return
731
+ targets = bridge.addressed_stand_ins(room, payload)
732
+ for stand_in_id in targets:
733
+ task = asyncio.get_running_loop().create_task(self._relay(bridge, project, entry, stand_in_id))
734
+ self._tasks.add(task)
735
+ task.add_done_callback(self._tasks.discard)
736
+
737
+ async def _relay(self, bridge: RoomBridge, project: Project, entry: ChangelogEntry, stand_in_id: str) -> None:
738
+ try:
739
+ await bridge.relay(project, entry, stand_in_id)
740
+ except Exception: # noqa: BLE001 — one relay never stops the next
741
+ logger.warning("Room bridge relay failed", exc_info=True)