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,846 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { DeskLabel } from '../api/endpoints/deskLabels'
3
+ import type { DeskTodo } from '../api/endpoints/deskTodos'
4
+ import {
5
+ CHIP_CAP,
6
+ RESIDUAL_KEY,
7
+ chipsFor,
8
+ cycleColor,
9
+ diagnoseRegistry,
10
+ displayName,
11
+ effectiveGroup,
12
+ groupTodos,
13
+ labelCounts,
14
+ matches,
15
+ nextColor,
16
+ normalizeSlug,
17
+ reconcileView,
18
+ registryWritable,
19
+ resolveLabel,
20
+ toRegistry,
21
+ unknownAxisReason,
22
+ unregisteredSlugs,
23
+ type LabelView,
24
+ type RegistryState,
25
+ } from './labelView'
26
+
27
+ /**
28
+ * THE PORT OF THE MOCK'S BEHAVIOUR CHECKS.
29
+ *
30
+ * These are the acceptance target for the whole feature, and they run as unit
31
+ * tests rather than DOM tests only because the projection layer was extracted
32
+ * pure — which was the point of extracting it.
33
+ */
34
+
35
+ /** ONE KIND. The `kind` parameter is gone with the state axis, and the fixture
36
+ * below keeps all nine rows rather than dropping four — which is the point of
37
+ * the edit rather than a convenience. An owner who minted Next / Wait For /
38
+ * Scheduled / Some Day still HAS those rows: nothing on disk is deleted, the
39
+ * server coerces a stored `state` row to a context on read, and the browser
40
+ * must render them as ordinary contexts. This fixture is that owner. */
41
+ const L = (slug: string, color: DeskLabel['color'], name = slug): DeskLabel => ({
42
+ slug,
43
+ name,
44
+ kind: 'context',
45
+ color,
46
+ created_at: '1970-01-01T00:00:00+00:00',
47
+ updated_at: '1970-01-01T00:00:00+00:00',
48
+ })
49
+
50
+ /** The mock's nine-label fixture, in its array order. The last four arrived as
51
+ * `kind: "state"` before the retirement and arrive as contexts now. */
52
+ const LABELS: DeskLabel[] = [
53
+ L('office', 'blue'),
54
+ L('home', 'green'),
55
+ L('phone', 'teal'),
56
+ L('computer', 'pink'),
57
+ L('errands', 'plum'),
58
+ L('next', 'indigo', 'Next'),
59
+ L('wait-for', 'amber', 'Wait For'),
60
+ L('scheduled', 'blue', 'Scheduled'),
61
+ L('someday', 'slate', 'Some Day'),
62
+ ]
63
+
64
+ let seq = 0
65
+ const T = (labels: string[], over: Partial<DeskTodo> = {}): DeskTodo => ({
66
+ id: `t-${++seq}`,
67
+ owner_user_id: 'u1',
68
+ text: `task ${seq}`,
69
+ // `status` is GONE from the wire, replaced by two independent fields. All
70
+ // four are typed REQUIRED, which is why every fixture in the suite moved at
71
+ // once rather than one at a time.
72
+ state: 'new',
73
+ archived: false,
74
+ association_count: 0,
75
+ project_ids: [],
76
+ created_at: '',
77
+ updated_at: '',
78
+ attachments: [],
79
+ labels,
80
+ ...over,
81
+ })
82
+
83
+ const reg = (state: RegistryState = 'ready', labels = LABELS) => toRegistry(labels, state)
84
+ const view = (over: Partial<LabelView> = {}): LabelView => ({
85
+ group: null,
86
+ filter: [],
87
+ matchAll: false,
88
+ states: [],
89
+ ...over,
90
+ })
91
+
92
+ describe('resolveLabel — the one rule the feature hangs on', () => {
93
+ it('returns the registry row when the registry is held and knows the slug', () => {
94
+ // RE-POINTED, not adjusted: `next` was the fixture's canonical STATE row.
95
+ // After the retirement the registry returns it as a context, and the row
96
+ // itself — slug, name, colour, position — is otherwise untouched. That is
97
+ // the whole shape of the retirement in one assertion.
98
+ const l = resolveLabel('next', reg())
99
+ expect(l).toEqual({
100
+ slug: 'next',
101
+ name: 'Next',
102
+ kind: 'context',
103
+ color: 'indigo',
104
+ registered: true,
105
+ })
106
+ })
107
+
108
+ it('an unknown slug on a held registry is a CONTEXT and is NOT registered', () => {
109
+ // R2: accepted on the wire, never silent. Dashed, colourless, "+ add it".
110
+ expect(resolveLabel('offce', reg())).toEqual({
111
+ slug: 'offce',
112
+ name: 'offce',
113
+ kind: 'context',
114
+ color: null,
115
+ registered: false,
116
+ })
117
+ })
118
+
119
+ it.each(['cold', 'unavailable'] as RegistryState[])(
120
+ 'off-ready (%s) every slug is kind:unknown and registered:TRUE',
121
+ (state) => {
122
+ // registered:true is the whole chip fix. We do not KNOW it isn't
123
+ // registered, so we must not say it isn't — otherwise the "not in your
124
+ // list" treatment lands on the owner's entire vocabulary at once.
125
+ for (const slug of ['office', 'next', 'offce']) {
126
+ const l = resolveLabel(slug, reg(state))
127
+ expect(l.kind).toBe('unknown')
128
+ expect(l.registered).toBe(true)
129
+ expect(l.color).toBeNull()
130
+ }
131
+ },
132
+ )
133
+
134
+ it('provisional resolves from the cached list, kinds intact', () => {
135
+ // kind is the registry's most stable field; a cached list groups
136
+ // correctly, not merely plausibly. There is one kind left, so the assertion
137
+ // is weaker than it was — but a cached row still resolving as REGISTERED is
138
+ // the half that was ever load-bearing.
139
+ expect(resolveLabel('next', reg('provisional')).kind).toBe('context')
140
+ expect(resolveLabel('next', reg('provisional')).registered).toBe(true)
141
+ })
142
+
143
+ it('an unknown label gets NO sigil — a sigil is a claim about the axis', () => {
144
+ expect(displayName(resolveLabel('office', reg()))).toBe('@office')
145
+ // WAS `!Next`. The `!` sigil retired with the axis it named: it existed to
146
+ // say "this is the other kind, press ! to reach it", and there is no other
147
+ // kind and no second key. One kind, one sigil.
148
+ expect(displayName(resolveLabel('next', reg()))).toBe('@Next')
149
+ expect(displayName(resolveLabel('office', reg('unavailable')))).toBe('office')
150
+ })
151
+ })
152
+
153
+ describe('matches — a flat OR over the chosen slugs, or AND under matchAll', () => {
154
+ const office = T(['office', 'next'])
155
+ const home = T(['home', 'next'])
156
+ const both = T(['office', 'home', 'next'])
157
+ const officeWaiting = T(['office', 'wait-for'])
158
+ const bare = T([])
159
+
160
+ it('an empty filter passes everything', () => {
161
+ expect(matches(bare, view(), reg())).toBe(true)
162
+ })
163
+
164
+ it('ORs across the chosen slugs', () => {
165
+ const v = view({ filter: ['office', 'home'] })
166
+ expect(matches(office, v, reg())).toBe(true)
167
+ expect(matches(home, v, reg())).toBe(true)
168
+ expect(matches(bare, v, reg())).toBe(false)
169
+ })
170
+
171
+ it('two slugs that used to be different KINDS now simply OR', () => {
172
+ // THIS ASSERTION TESTED AN AXIS THAT NO LONGER EXISTS. It read "ANDs across
173
+ // kinds — a context filter and a state filter both bind", and it was the
174
+ // behaviour `kind` existed to produce. With one kind the refinement is gone
175
+ // and the correct answer is a flat OR — which is what is asserted here
176
+ // rather than deleted, because "these two no longer AND" is exactly the
177
+ // fact a reader six weeks from now needs.
178
+ //
179
+ // What REPLACED the narrowing it used to buy is the state filter, which is
180
+ // a different axis entirely and is tested in `ticketView.test.ts`
181
+ // ('the state term ANDs with the label filter').
182
+ const v = view({ filter: ['office', 'next'] })
183
+ expect(matches(office, v, reg())).toBe(true)
184
+ // carries @office; under the old kinds its `!wait-for` rejected it
185
+ expect(matches(officeWaiting, v, reg())).toBe(true)
186
+ // carries @next but not @office — an OR, so it passes
187
+ expect(matches(home, v, reg())).toBe(true)
188
+ expect(matches(bare, v, reg())).toBe(false)
189
+ })
190
+
191
+ it('matchAll flips the WHOLE filter to strict AND', () => {
192
+ // Was "ONLY the context half". There is no other half.
193
+ const v = view({ filter: ['office', 'home'], matchAll: true })
194
+ expect(matches(both, v, reg())).toBe(true)
195
+ expect(matches(office, v, reg())).toBe(false)
196
+ })
197
+
198
+ it('matchAll now binds two slugs that used to be exempt from it', () => {
199
+ // The deleted assertion here was "matchAll leaves the state half as OR —
200
+ // AND across states is unsatisfiable", which was true because a to-do
201
+ // carried at most one state. `next` and `wait-for` are ordinary contexts
202
+ // now, a to-do may carry both, and AND over them is satisfiable — so the
203
+ // exemption is not merely unnecessary, it would be wrong.
204
+ //
205
+ // The unsatisfiable-AND argument DID survive, on the axis that still has
206
+ // the property: a to-do has exactly one ticket state, which is why the
207
+ // state filter has no ALL/AND toggle. Asserted in `ticketView.test.ts`.
208
+ const v = view({ filter: ['next', 'wait-for'], matchAll: true })
209
+ expect(matches(officeWaiting, v, reg())).toBe(false)
210
+ expect(matches(T(['next', 'wait-for']), v, reg())).toBe(true)
211
+ })
212
+
213
+ it('20/5 — the fixture-scale check the mock grades on, on one axis now', () => {
214
+ // 20 items carry @computer; 5 of those also carry @wait-for.
215
+ const plate = [
216
+ ...Array.from({ length: 15 }, () => T(['computer', 'next'])),
217
+ ...Array.from({ length: 5 }, () => T(['computer', 'wait-for'])),
218
+ ...Array.from({ length: 10 }, () => T(['home', 'next'])),
219
+ ]
220
+ const ctxOnly = plate.filter((t) => matches(t, view({ filter: ['computer'] }), reg()))
221
+ expect(ctxOnly).toHaveLength(20)
222
+ // WAS 5, by ANDing across the two kinds. One kind means a flat OR, so the
223
+ // second slug can only ever WIDEN — here to the 20 carrying @computer, all
224
+ // five @wait-for rows already being among them. The way back to 5 is
225
+ // matchAll, asserted below, which is a control the owner can actually see.
226
+ const across = plate.filter((t) =>
227
+ matches(t, view({ filter: ['computer', 'wait-for'] }), reg()),
228
+ )
229
+ expect(across).toHaveLength(20)
230
+ // …and the widening is real where the sets differ: @home ∪ @wait-for.
231
+ const wider = plate.filter((t) => matches(t, view({ filter: ['home', 'wait-for'] }), reg()))
232
+ expect(wider).toHaveLength(15)
233
+ const strict = plate.filter((t) =>
234
+ matches(t, view({ filter: ['computer', 'wait-for'], matchAll: true }), reg()),
235
+ )
236
+ expect(strict).toHaveLength(5)
237
+ })
238
+
239
+ it('off-ready it degrades to a FLAT OR — and does NOT become pass-everything', () => {
240
+ // The negative is the important half: with kind unreadable both partitions
241
+ // come back empty, every predicate passes, and the filter silently stops
242
+ // filtering while the chip still reads "Filter 2".
243
+ const v = view({ filter: ['office', 'next'] })
244
+ expect(matches(office, v, reg('unavailable'))).toBe(true)
245
+ expect(matches(officeWaiting, v, reg('unavailable'))).toBe(true)
246
+ expect(matches(bare, v, reg('unavailable'))).toBe(false)
247
+ expect(matches(T(['home']), v, reg('unavailable'))).toBe(false)
248
+ })
249
+
250
+ it('off-ready ignores matchAll — there is no context partition to strengthen', () => {
251
+ const v = view({ filter: ['office', 'home'], matchAll: true })
252
+ expect(matches(office, v, reg('cold'))).toBe(true)
253
+ })
254
+ })
255
+
256
+ describe('chipsFor — order, pinning and the +N cap', () => {
257
+ it('registered contexts in REGISTRY order', () => {
258
+ // WAS "state first, then registered contexts": the state arm of the
259
+ // ordering is gone, so a row is one run in registry array position. `next`
260
+ // sits at index 5 and now sorts AFTER `office` (0) and `errands` (4) rather
261
+ // than jumping the queue for being a state.
262
+ //
263
+ // The ticket chip did not take over that leading position through this
264
+ // function — it cannot reach it. `TicketChip` imports neither this module
265
+ // nor `LabelChip`, so it is never a member of `show`, is never counted
266
+ // toward CHIP_CAP, and never falls under `+N`. Its leading position is
267
+ // rendered by `TodoItem`, outside this array.
268
+ const t = T(['errands', 'office', 'next'])
269
+ const { show } = chipsFor(t, view(), reg())
270
+ expect(show.map((l) => l.slug)).toEqual(['office', 'errands', 'next'])
271
+ })
272
+
273
+ it('unregistered contexts sort LAST so the row keeps its colour rhythm', () => {
274
+ const t = T(['offce', 'office', 'next'])
275
+ const { show } = chipsFor(t, view(), reg())
276
+ expect(show.map((l) => l.slug)).toEqual(['office', 'next', 'offce'])
277
+ })
278
+
279
+ it('a filtered label PINS to the front — a filter never hides why a row matched', () => {
280
+ const t = T(['office', 'computer', 'next'])
281
+ const { show } = chipsFor(t, view({ filter: ['computer'] }), reg())
282
+ expect(show[0].slug).toBe('computer')
283
+ })
284
+
285
+ it('over the cap it shows CAP-1 and reports the rest', () => {
286
+ // "Sort out the standing desk cable mess" — five labels, the mock's
287
+ // overflow row.
288
+ const t = T(['office', 'home', 'errands', 'computer', 'someday'])
289
+ const { show, more } = chipsFor(t, view(), reg())
290
+ expect(show).toHaveLength(CHIP_CAP - 1)
291
+ expect(more).toBe(5 - (CHIP_CAP - 1))
292
+ // `someday` led this row for being a state; it is index 8 in the registry
293
+ // now, so the two survivors are the first two in array order. The CAP
294
+ // arithmetic itself is untouched — deliberately, because the ticket chip
295
+ // must never change it.
296
+ expect(show.map((l) => l.slug)).toEqual(['office', 'home'])
297
+ })
298
+
299
+ it('exactly CAP chips do NOT overflow', () => {
300
+ const { show, more } = chipsFor(T(['office', 'home', 'next']), view(), reg())
301
+ expect(show).toHaveLength(3)
302
+ expect(more).toBe(0)
303
+ })
304
+
305
+ it('a pinned chip survives the cap — it is never truncated away', () => {
306
+ const t = T(['office', 'home', 'errands', 'computer', 'someday'])
307
+ const { show, more } = chipsFor(t, view({ filter: ['computer'] }), reg())
308
+ expect(show[0].slug).toBe('computer')
309
+ expect(more).toBe(5 - (CHIP_CAP - 1))
310
+ })
311
+
312
+ it('a ticket state is NOT reachable from here, at any state', () => {
313
+ // The containment criterion, asserted where it would break first. A row
314
+ // reading Working with no labels yields NO chips and NO overflow: the chip
315
+ // it renders is produced by `TicketChip` outside this path entirely, so
316
+ // nothing here can count it, cap it, or hide it under `+N`.
317
+ const working = T([], { state: 'working', association_count: 3 })
318
+ const { show, more } = chipsFor(working, view(), reg())
319
+ expect(show).toEqual([])
320
+ expect(more).toBe(0)
321
+ // And a row AT the cap stays at the cap: the ticket chip does not consume
322
+ // one of the three slots.
323
+ const full = T(['office', 'home', 'errands'], { state: 'completed' })
324
+ expect(chipsFor(full, view(), reg()).show).toHaveLength(CHIP_CAP)
325
+ expect(chipsFor(full, view(), reg()).more).toBe(0)
326
+ })
327
+
328
+ it('expanded shows everything and reports no overflow', () => {
329
+ const t = T(['office', 'home', 'errands', 'computer', 'someday'])
330
+ const { show, more } = chipsFor(t, view(), reg(), true)
331
+ expect(show).toHaveLength(5)
332
+ expect(more).toBe(0)
333
+ })
334
+
335
+ it('off-ready the row keeps STORED order and chips do not vanish', () => {
336
+ // Without the off-ready branch the kind filters match neither value and
337
+ // every chip silently disappears from the row.
338
+ const t = T(['errands', 'office', 'next'])
339
+ const { show } = chipsFor(t, view(), reg('unavailable'))
340
+ expect(show.map((l) => l.slug)).toEqual(['errands', 'office', 'next'])
341
+ })
342
+
343
+ it('off-ready still pins the filtered chip first', () => {
344
+ const t = T(['errands', 'office', 'next'])
345
+ const { show } = chipsFor(t, view({ filter: ['next'] }), reg('unavailable'))
346
+ expect(show[0].slug).toBe('next')
347
+ })
348
+
349
+ it('a row with no labels yields no chips', () => {
350
+ expect(chipsFor(T([]), view(), reg()).show).toEqual([])
351
+ })
352
+
353
+ it('a row whose labels key is absent entirely does not throw', () => {
354
+ const legacy = { ...T([]), labels: undefined }
355
+ expect(chipsFor(legacy, view(), reg()).show).toEqual([])
356
+ })
357
+ })
358
+
359
+ describe('groupTodos — duplication, order and the residual', () => {
360
+ const plate = [
361
+ T(['office', 'next']),
362
+ T(['office', 'home', 'next']),
363
+ T(['home', 'wait-for']),
364
+ T([]),
365
+ T(['infra']),
366
+ ]
367
+
368
+ it('a to-do appears under EVERY label it carries on the axis', () => {
369
+ const groups = groupTodos(plate, 'context', reg())
370
+ const office = groups.find((g) => g.key === 'office')!
371
+ const home = groups.find((g) => g.key === 'home')!
372
+ expect(office.items).toHaveLength(2)
373
+ expect(home.items).toHaveLength(2)
374
+ // the second row is in both, and it is the SAME record
375
+ expect(office.items[1].id).toBe(home.items[0].id)
376
+ })
377
+
378
+ it('group order is registry array position', () => {
379
+ const groups = groupTodos(plate, 'context', reg())
380
+ const registered = groups.filter((g) => g.tone === '').map((g) => g.key)
381
+ // NINE groups, not five: the four rows the owner minted as states arrive as
382
+ // ordinary contexts after the retirement, so they group here too — in their
383
+ // stored registry positions, which are unchanged. Nothing was deleted from
384
+ // the registry to produce this, and nothing should be.
385
+ expect(registered).toEqual([
386
+ 'office',
387
+ 'home',
388
+ 'phone',
389
+ 'computer',
390
+ 'errands',
391
+ 'next',
392
+ 'wait-for',
393
+ 'scheduled',
394
+ 'someday',
395
+ ])
396
+ })
397
+
398
+ it('unregistered groups sort LAST and open collapsed', () => {
399
+ const groups = groupTodos(plate, 'context', reg())
400
+ const unreg = groups.filter((g) => g.tone === 'unreg')
401
+ expect(unreg.map((g) => g.key)).toEqual(['infra'])
402
+ expect(unreg[0].collapsedByDefault).toBe(true)
403
+ // …after every registered group and before the residual
404
+ const keys = groups.map((g) => g.key)
405
+ expect(keys.indexOf('infra')).toBeGreaterThan(keys.indexOf('errands'))
406
+ expect(keys.indexOf('infra')).toBeLessThan(keys.indexOf(RESIDUAL_KEY))
407
+ })
408
+
409
+ it('grouping by context puts the residual LAST and quiet', () => {
410
+ const groups = groupTodos(plate, 'context', reg())
411
+ expect(groups[groups.length - 1].key).toBe(RESIDUAL_KEY)
412
+ expect(groups[groups.length - 1].tone).toBe('resid')
413
+ })
414
+
415
+ it('there is NO "Unprocessed · no state" pile any more, on any axis', () => {
416
+ // THIS ASSERTION TESTED AN AXIS THAT NO LONGER EXISTS. It read "grouping by
417
+ // state puts the residual FIRST and nagging" and asserted a red
418
+ // `tone: 'inbox'` group named "Unprocessed · no state", holding everything
419
+ // that carried no state label.
420
+ //
421
+ // It is a DELETION rather than a re-point, and the reason is the one that
422
+ // made this a replacement instead of an adjustment: that group was computed
423
+ // from label MEMBERSHIP. Once nothing carries a state label its membership
424
+ // is the entire plate — so left in place it would swallow every to-do into
425
+ // one red pile, which is pixel-identical to the failure screen it was
426
+ // written to prevent.
427
+ //
428
+ // WHAT REPLACED IT: `groupByTicketState`, which partitions on the required
429
+ // `state` field, yields exactly three groups and no residual at all,
430
+ // because NEW IS THE INBOX. Asserted in `ticketView.test.ts`.
431
+ const groups = groupTodos(plate, 'context', reg())
432
+ expect(groups.some((g) => g.tone === ('inbox' as unknown))).toBe(false)
433
+ expect(groups.some((g) => g.name.includes('Unprocessed'))).toBe(false)
434
+ // The one residual left is the context one, and it is LAST and quiet.
435
+ const resid = groups.filter((g) => g.key === RESIDUAL_KEY)
436
+ expect(resid).toHaveLength(1)
437
+ expect(resid[0].tone).toBe('resid')
438
+ })
439
+
440
+ it('off-ready there are no groups at all — the axis is unreadable', () => {
441
+ expect(groupTodos(plate, 'context', reg('unavailable'))).toEqual([])
442
+ })
443
+
444
+ it('group → ungroup leaves the plate order byte-identical', () => {
445
+ // The strongest property of the whole shape: grouping is a pure
446
+ // projection, so there is nothing to restore because nothing changed.
447
+ const before = plate.map((t) => t.id)
448
+ groupTodos(plate, 'context', reg())
449
+ expect(plate.map((t) => t.id)).toEqual(before)
450
+ })
451
+ })
452
+
453
+ describe('labelCounts — DISTINCT, never the sum of group counts', () => {
454
+ it('counts each to-do once per slug', () => {
455
+ const plate = [T(['office', 'next']), T(['office', 'home']), T([])]
456
+ const counts = labelCounts(plate)
457
+ expect(counts.get('office')).toBe(2)
458
+ expect(counts.get('home')).toBe(1)
459
+ expect(counts.get('next')).toBe(1)
460
+ expect(counts.get('nope')).toBeUndefined()
461
+ })
462
+
463
+ it('a duplicated slug on one row still counts once for that row', () => {
464
+ expect(labelCounts([T(['office', 'office'])]).get('office')).toBe(1)
465
+ })
466
+
467
+ it('the sum over groups exceeds the plate — which is why the header uses this', () => {
468
+ const plate = [T(['office', 'home']), T(['office', 'home'])]
469
+ const counts = labelCounts(plate)
470
+ expect(counts.get('office')! + counts.get('home')!).toBe(4)
471
+ expect(plate).toHaveLength(2)
472
+ })
473
+ })
474
+
475
+ describe('nextColor — least-used, ties by palette order', () => {
476
+ it('an empty registry takes the first palette token', () => {
477
+ expect(nextColor([])).toBe('blue')
478
+ })
479
+
480
+ it('after the six seed rows the sequence is pink, then plum, then blue', () => {
481
+ // blue/green/teal/indigo/amber/slate are taken; pink and plum are free,
482
+ // and pink comes first in PALETTE.
483
+ const seed = [
484
+ L('office', 'blue'),
485
+ L('home', 'green'),
486
+ L('phone', 'teal'),
487
+ L('next', 'indigo'),
488
+ L('wait-for', 'amber'),
489
+ L('someday', 'slate'),
490
+ ]
491
+ const first = nextColor(seed)
492
+ expect(first).toBe('pink')
493
+ const withPink = [...seed, L('a', first)]
494
+ const second = nextColor(withPink)
495
+ expect(second).toBe('plum')
496
+ const withPlum = [...withPink, L('b', second)]
497
+ // every token used once now, so the tie-break picks PALETTE[0]
498
+ expect(nextColor(withPlum)).toBe('blue')
499
+ })
500
+
501
+ it('delete-then-create lands on a FREE token, not on one in use', () => {
502
+ // The case that kills PALETTE[count % 8]: nine labels, delete one, create
503
+ // one, and the modulo lands back on a token in use while others sit free.
504
+ const nine = LABELS
505
+ const afterDelete = nine.filter((l) => l.slug !== 'errands') // frees `plum`
506
+ expect(nextColor(afterDelete)).toBe('plum')
507
+ })
508
+
509
+ it('a colourless row does not consume a token', () => {
510
+ expect(nextColor([L('x', null)])).toBe('blue')
511
+ })
512
+
513
+ it('cycleColor walks the palette and wraps', () => {
514
+ expect(cycleColor('blue')).toBe('green')
515
+ expect(cycleColor('plum')).toBe('blue')
516
+ expect(cycleColor(null)).toBe('blue')
517
+ })
518
+ })
519
+
520
+ describe('normalizeSlug — the server rule, not the contract prose', () => {
521
+ it.each([
522
+ ['@Office', 'office'],
523
+ ['Wait For', 'wait-for'],
524
+ ['Someday/Maybe', 'someday-maybe'],
525
+ [' Deep Work ', 'deep-work'],
526
+ ['!waiting-on-bank', 'waiting-on-bank'],
527
+ ['@@office', 'office'],
528
+ ])('%s → %s', (raw, slug) => {
529
+ expect(normalizeSlug(raw)).toEqual({ slug, fault: null })
530
+ })
531
+
532
+ it('REPLACES illegal runs with "-" rather than dropping them', () => {
533
+ // Contract §8 step 5 says "drop"; that prose contradicts the contract's own
534
+ // worked example and the shipped server. Dropping would give
535
+ // "somedaymaybe" — this is the check that keeps the right one.
536
+ expect(normalizeSlug('Someday/Maybe').slug).toBe('someday-maybe')
537
+ expect(normalizeSlug('Someday/Maybe').slug).not.toBe('somedaymaybe')
538
+ })
539
+
540
+ it('REFUSES a slug over 32 chars rather than truncating it', () => {
541
+ // The mock's `.slice(0, 32)` would send a value the server 400s.
542
+ const long = 'a'.repeat(40)
543
+ expect(normalizeSlug(long)).toEqual({ slug: long, fault: 'slug_too_long' })
544
+ expect(normalizeSlug('a'.repeat(32)).fault).toBeNull()
545
+ })
546
+
547
+ it.each(['', '@', '@ ', '///', '---'])('%s is invalid_slug', (raw) => {
548
+ expect(normalizeSlug(raw).fault).toBe('invalid_slug')
549
+ })
550
+
551
+ it('a name already in the registry is a DUPLICATE, not a cross-kind clash', () => {
552
+ // THIS ASSERTION TESTED AN AXIS THAT NO LONGER EXISTS. It read "clashWith
553
+ // finds a name already taken by the OTHER kind, both directions", and it
554
+ // guarded the rule that `!office` must not silently attach the context
555
+ // @office — the flat slug namespace was shared by two kinds, so either one
556
+ // could have got there first.
557
+ //
558
+ // `clashWith` is DELETED along with the second kind: with one kind it could
559
+ // only ever return null. WHAT REPLACED IT is the check that was always
560
+ // underneath it — a registry that already has the slug — which the picker
561
+ // and the manage sheet now read directly, and which the server names
562
+ // `labels.duplicate`.
563
+ expect(reg().index.has('next')).toBe(true)
564
+ expect(reg().index.has('office')).toBe(true)
565
+ expect(reg().index.has('brand-new')).toBe(false)
566
+ })
567
+ })
568
+
569
+ describe('unregisteredSlugs', () => {
570
+ it('lists them in first-seen plate order, deduped', () => {
571
+ const plate = [T(['infra']), T(['office', 'offce']), T(['infra'])]
572
+ expect(unregisteredSlugs(plate, reg())).toEqual(['infra', 'offce'])
573
+ })
574
+
575
+ it('off-ready the answer is NONE, not all', () => {
576
+ // Otherwise every "not in your list" surface offers to add back the
577
+ // owner's own labels and each tap 409s.
578
+ const plate = [T(['office', 'infra'])]
579
+ expect(unregisteredSlugs(plate, reg('unavailable'))).toEqual([])
580
+ })
581
+ })
582
+
583
+ describe('diagnoseRegistry — the three content answers must not collapse', () => {
584
+ it('rows present → populated', () => {
585
+ expect(diagnoseRegistry(reg(), [T(['office'])])).toBe('populated')
586
+ })
587
+
588
+ it('zero rows and a bare plate → empty, which is an ANSWER not a failure', () => {
589
+ // `{"labels": []}` is a legitimate 200 from an owner running no vocabulary.
590
+ expect(diagnoseRegistry(reg('ready', []), [T([]), T([])])).toBe('empty')
591
+ })
592
+
593
+ it('zero rows but a LABELLED plate → orphaned, the corrupt-looks-missing trap', () => {
594
+ // An entirely unparseable registry answers with zero rows, never the seed,
595
+ // so it is indistinguishable from a deliberately emptied one EXCEPT by
596
+ // this: emptying detaches on the way out, so labels still on the plate
597
+ // mean the list did not come back.
598
+ expect(diagnoseRegistry(reg('ready', []), [T(['office'])])).toBe('orphaned')
599
+ })
600
+ })
601
+
602
+ describe('reconcileView — runs on a FRESH registry only', () => {
603
+ const base = {
604
+ group: null,
605
+ filter: [],
606
+ matchAll: false,
607
+ states: [],
608
+ collapsed: {},
609
+ } as const
610
+
611
+ it('a merge REPLACES the slug with its target and dedupes', () => {
612
+ const r = reconcileView(
613
+ { ...base, filter: ['office', 'work'] },
614
+ [L('work', 'blue')],
615
+ new Set(),
616
+ [{ from: 'office', into: 'work' }],
617
+ )
618
+ expect(r.next.filter).toEqual(['work'])
619
+ expect(r.notice).toContain('“office” was merged into @work')
620
+ })
621
+
622
+ it('a merge in ANOTHER TAB is followed too — that is what `into` buys', () => {
623
+ // The receiving tab has only {action:'merge', slug, into} from the wire.
624
+ const r = reconcileView(
625
+ { ...base, filter: ['office'] },
626
+ [L('work', 'blue')],
627
+ new Set(),
628
+ [{ from: 'office', into: 'work' }],
629
+ )
630
+ expect(r.next.filter).toEqual(['work'])
631
+ })
632
+
633
+ it('a delete DROPS the slug and names it in a notice', () => {
634
+ const r = reconcileView(
635
+ { ...base, filter: ['office'] },
636
+ [L('home', 'green')],
637
+ new Set(),
638
+ )
639
+ expect(r.next.filter).toEqual([])
640
+ expect(r.notice).toContain('office')
641
+ expect(r.notice).toContain('filter cleared')
642
+ })
643
+
644
+ it('a partial delete says the filter changed rather than that it cleared', () => {
645
+ const r = reconcileView(
646
+ { ...base, filter: ['office', 'home'] },
647
+ [L('home', 'green')],
648
+ new Set(),
649
+ )
650
+ expect(r.next.filter).toEqual(['home'])
651
+ expect(r.notice).toContain('removed from your filter')
652
+ })
653
+
654
+ it('an UNREGISTERED slug still on the plate is NOT treated as deleted', () => {
655
+ // Without the plate argument every agent-written filter chip is cleared on
656
+ // the next refetch — a filter the owner set, silently destroyed.
657
+ const r = reconcileView(
658
+ { ...base, filter: ['infra'] },
659
+ [L('home', 'green')],
660
+ new Set(['infra']),
661
+ )
662
+ expect(r.next.filter).toEqual(['infra'])
663
+ expect(r.notice).toBeNull()
664
+ })
665
+
666
+ it('G3 — the "your last state was deleted" branch is GONE, not adapted', () => {
667
+ // THIS ASSERTION TESTED AN AXIS THAT NO LONGER EXISTS. It read "grouping by
668
+ // state with zero states falls back to ungrouped, with a notice", and it
669
+ // guarded a real behaviour: delete your last state row and grouping by
670
+ // state had nothing left to project onto, so it switched off and SAID SO.
671
+ //
672
+ // After the retirement `!labels.some(l => l.kind === 'state')` is ALWAYS
673
+ // true. Left in place, that branch would fire on the first refetch for
674
+ // every owner who had grouping on, switch it off, and announce a deletion
675
+ // that never happened. So the branch is deleted rather than adapted, and
676
+ // this test is inverted to hold it deleted.
677
+ //
678
+ // WHAT REPLACED IT: nothing, and nothing is required to. Ticket grouping
679
+ // reads no registry row, so there is no row whose deletion could take it
680
+ // away — which is the same fact `effectiveGroup`'s exemption encodes.
681
+ const r = reconcileView({ ...base, group: 'ticket' }, [L('home', 'green')], new Set())
682
+ expect(r.next.group).toBe('ticket')
683
+ expect(r.notice).toBeNull()
684
+ // …and with an entirely empty registry, which is where the deleted branch
685
+ // would have been loudest.
686
+ const empty = reconcileView({ ...base, group: 'ticket' }, [], new Set())
687
+ expect(empty.next.group).toBe('ticket')
688
+ expect(empty.notice).toBeNull()
689
+ })
690
+
691
+ it('`states` rides through untouched — it is not the registry\'s to reconcile', () => {
692
+ // Not decoration: the caller writes this whole object back to storage, so a
693
+ // dropped key here silently unpersists the owner's state filter on the next
694
+ // registry refetch.
695
+ const r = reconcileView(
696
+ { ...base, filter: ['office'], states: ['working', 'completed'] },
697
+ [L('home', 'green')],
698
+ new Set(),
699
+ )
700
+ expect(r.next.states).toEqual(['working', 'completed'])
701
+ })
702
+
703
+ it('a collapsed TICKET group key survives a refetch', () => {
704
+ // A ticket key names no label, so neither the registry nor the plate can
705
+ // vouch for it — and the dead-key sweep would drop it, unfolding the
706
+ // owner's folded Done group on every refetch.
707
+ const r = reconcileView(
708
+ { ...base, collapsed: { '__tk:completed': true, office: true } },
709
+ [L('home', 'green')],
710
+ new Set(),
711
+ )
712
+ expect(r.next.collapsed).toEqual({ '__tk:completed': true })
713
+ })
714
+
715
+ it('grouping by context with nothing to project onto falls back SILENTLY', () => {
716
+ const r = reconcileView({ ...base, group: 'context' }, [], new Set())
717
+ expect(r.next.group).toBeNull()
718
+ expect(r.notice).toBeNull()
719
+ })
720
+
721
+ it('a context group survives on unregistered slugs alone', () => {
722
+ const r = reconcileView({ ...base, group: 'context' }, [], new Set(['infra']))
723
+ expect(r.next.group).toBe('context')
724
+ })
725
+
726
+ it('a dead collapsed key is dropped INERTLY — no notice', () => {
727
+ const r = reconcileView(
728
+ { ...base, collapsed: { office: true, home: false, [RESIDUAL_KEY]: true } },
729
+ [L('home', 'green')],
730
+ new Set(),
731
+ )
732
+ expect(r.next.collapsed).toEqual({ home: false, [RESIDUAL_KEY]: true })
733
+ expect(r.notice).toBeNull()
734
+ })
735
+
736
+ it('a steady-state refetch changes nothing and reports changed:false', () => {
737
+ const r = reconcileView({ ...base, filter: ['home'] }, [L('home', 'green')], new Set())
738
+ expect(r.changed).toBe(false)
739
+ expect(r.notice).toBeNull()
740
+ })
741
+
742
+ it('matchAll is carried through untouched', () => {
743
+ const r = reconcileView(
744
+ { ...base, filter: ['home'], matchAll: true },
745
+ [L('home', 'green')],
746
+ new Set(),
747
+ )
748
+ expect(r.next.matchAll).toBe(true)
749
+ })
750
+ })
751
+
752
+ describe('effectiveGroup — suppressed off-ready, except where it need not be', () => {
753
+ it('renders the stored LABEL axis when the registry is held', () => {
754
+ // Re-pointed from `'state'`, which is no longer a value this can hold.
755
+ expect(effectiveGroup(view({ group: 'context' }), 'ready')).toBe('context')
756
+ expect(effectiveGroup(view({ group: 'context' }), 'provisional')).toBe('context')
757
+ })
758
+
759
+ it('suppresses the LABEL axis while the registry is missing, so it returns by itself', () => {
760
+ expect(effectiveGroup(view({ group: 'context' }), 'unavailable')).toBeNull()
761
+ expect(effectiveGroup(view({ group: 'context' }), 'cold')).toBeNull()
762
+ })
763
+
764
+ it('G2 — `ticket` is EXEMPT from the suppression, in every registry state', () => {
765
+ // The exemption is the whole reason ticket grouping is worth having:
766
+ // suppressed alongside `context`, the one axis that still functions would
767
+ // vanish at exactly the moment it is the only one left. It partitions on a
768
+ // field that arrives with the to-do, so a dead registry costs it nothing.
769
+ for (const st of ['ready', 'provisional', 'cold', 'unavailable'] as RegistryState[]) {
770
+ expect(effectiveGroup(view({ group: 'ticket' }), st)).toBe('ticket')
771
+ }
772
+ })
773
+ })
774
+
775
+ describe('unknownAxisReason — `cold` is "not yet", `unavailable` is "not going to"', () => {
776
+ it('does not report a failure that has not happened', () => {
777
+ expect(unknownAxisReason('cold')).toBe('Still loading your labels…')
778
+ expect(unknownAxisReason('cold')).not.toMatch(/didn't load/)
779
+ })
780
+
781
+ it('does report one that has', () => {
782
+ expect(unknownAxisReason('unavailable')).toMatch(/didn't load/)
783
+ })
784
+ })
785
+
786
+ describe('registryWritable — the guard on every create surface', () => {
787
+ it('allows a create against a registry we can read', () => {
788
+ expect(registryWritable('ready', 'populated')).toBe(true)
789
+ expect(registryWritable('ready', 'empty')).toBe(true)
790
+ // An empty registry is an ANSWER. Refusing to create there would leave a
791
+ // first-time owner with no way to get a first label.
792
+ })
793
+
794
+ it('refuses one against an ORPHANED registry', () => {
795
+ // Zero rows over a labelled plate means the list did not come back — and
796
+ // `create_label` would append to that empty list and save it over the file.
797
+ expect(registryWritable('ready', 'orphaned')).toBe(false)
798
+ expect(registryWritable('provisional', 'orphaned')).toBe(false)
799
+ })
800
+
801
+ it('refuses one when the axis is unknown at all', () => {
802
+ expect(registryWritable('cold', 'empty')).toBe(false)
803
+ expect(registryWritable('unavailable', 'empty')).toBe(false)
804
+ })
805
+ })
806
+
807
+ describe('reconcile notices name a label the way the rail names it', () => {
808
+ const base = { group: null, filter: [], matchAll: false, collapsed: {} } as const
809
+
810
+ it('gives the merge target its sigil and quotes the label that is gone', () => {
811
+ // `@work` is alive and resolvable; `office` has no row left to read a kind
812
+ // from, so a sigil there would assert an axis we just lost.
813
+ const r = reconcileView(
814
+ { ...base, filter: ['office'] },
815
+ [L('work', 'blue')],
816
+ new Set(),
817
+ [{ from: 'office', into: 'work' }],
818
+ )
819
+ expect(r.notice).toBe('“office” was merged into @work — your filter followed it.')
820
+ })
821
+
822
+ it('quotes a deleted label rather than inventing a sigil for it', () => {
823
+ const r = reconcileView({ ...base, filter: ['office'] }, [], new Set(), [])
824
+ expect(r.notice).toBe('Label “office” was deleted — filter cleared.')
825
+ })
826
+
827
+ it('agrees with itself in the plural', () => {
828
+ const r = reconcileView({ ...base, filter: ['office', 'shed'] }, [], new Set(), [])
829
+ expect(r.notice).toBe('Labels “office”, “shed” were deleted — filter cleared.')
830
+ })
831
+
832
+ it('announces a merge AND a delete that land in the same refetch', () => {
833
+ // Two tabs, two hands, one poll. An early return on the merge branch would
834
+ // swallow the delete — and a filter chip that vanishes with no notice is
835
+ // the one thing §1.1's notice exists to prevent.
836
+ const r = reconcileView(
837
+ { ...base, filter: ['office', 'shed'] },
838
+ [L('work', 'blue')],
839
+ new Set(),
840
+ [{ from: 'office', into: 'work' }],
841
+ )
842
+ expect(r.next.filter).toEqual(['work'])
843
+ expect(r.notice).toContain('“office” was merged into @work')
844
+ expect(r.notice).toContain('“shed” was deleted')
845
+ })
846
+ })