diff --git a/.changeset/system-notifications-sounds.md b/.changeset/system-notifications-sounds.md new file mode 100644 index 00000000000..f4cad340f79 --- /dev/null +++ b/.changeset/system-notifications-sounds.md @@ -0,0 +1,5 @@ +--- +"kilo-code": minor +--- + +Add OS-level system notifications with sound playback for agent events. When VS Code is not focused, users receive native desktop notifications (macOS Notification Center, Windows Toast, Linux notify-send) for agent task completion, permission requests, questions, and session errors. Includes 45+ configurable notification sounds and per-event enable/disable settings. diff --git a/packages/kilo-vscode/.vscodeignore b/packages/kilo-vscode/.vscodeignore index a1779eec27d..58052292719 100644 --- a/packages/kilo-vscode/.vscodeignore +++ b/packages/kilo-vscode/.vscodeignore @@ -25,3 +25,7 @@ AGENTS.md # Include bin/ directory for CLI binary (production) !bin/** + +# Include audio files for sound notifications +!audio-wav/** +audio/** diff --git a/packages/kilo-vscode/audio-wav/alert-01.wav b/packages/kilo-vscode/audio-wav/alert-01.wav new file mode 100644 index 00000000000..4130d76078a Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-01.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-02.wav b/packages/kilo-vscode/audio-wav/alert-02.wav new file mode 100644 index 00000000000..f80a75db4f3 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-02.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-03.wav b/packages/kilo-vscode/audio-wav/alert-03.wav new file mode 100644 index 00000000000..faaf150584f Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-03.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-04.wav b/packages/kilo-vscode/audio-wav/alert-04.wav new file mode 100644 index 00000000000..6bfe5acf9b9 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-04.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-05.wav b/packages/kilo-vscode/audio-wav/alert-05.wav new file mode 100644 index 00000000000..a6ad6d7d640 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-05.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-06.wav b/packages/kilo-vscode/audio-wav/alert-06.wav new file mode 100644 index 00000000000..eba3b6e6cc7 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-06.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-07.wav b/packages/kilo-vscode/audio-wav/alert-07.wav new file mode 100644 index 00000000000..7b17815a09d Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-07.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-08.wav b/packages/kilo-vscode/audio-wav/alert-08.wav new file mode 100644 index 00000000000..d50b3d8721c Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-08.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-09.wav b/packages/kilo-vscode/audio-wav/alert-09.wav new file mode 100644 index 00000000000..5a1930f27ce Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-09.wav differ diff --git a/packages/kilo-vscode/audio-wav/alert-10.wav b/packages/kilo-vscode/audio-wav/alert-10.wav new file mode 100644 index 00000000000..a7e093f8875 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/alert-10.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-01.wav b/packages/kilo-vscode/audio-wav/bip-bop-01.wav new file mode 100644 index 00000000000..e7701f9859b Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-01.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-02.wav b/packages/kilo-vscode/audio-wav/bip-bop-02.wav new file mode 100644 index 00000000000..5a98327de6c Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-02.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-03.wav b/packages/kilo-vscode/audio-wav/bip-bop-03.wav new file mode 100644 index 00000000000..6c96b8cc1cd Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-03.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-04.wav b/packages/kilo-vscode/audio-wav/bip-bop-04.wav new file mode 100644 index 00000000000..aee1b6fdde6 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-04.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-05.wav b/packages/kilo-vscode/audio-wav/bip-bop-05.wav new file mode 100644 index 00000000000..22de50bc15c Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-05.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-06.wav b/packages/kilo-vscode/audio-wav/bip-bop-06.wav new file mode 100644 index 00000000000..45ea58cfdd3 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-06.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-07.wav b/packages/kilo-vscode/audio-wav/bip-bop-07.wav new file mode 100644 index 00000000000..da75eea5310 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-07.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-08.wav b/packages/kilo-vscode/audio-wav/bip-bop-08.wav new file mode 100644 index 00000000000..b56a7655011 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-08.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-09.wav b/packages/kilo-vscode/audio-wav/bip-bop-09.wav new file mode 100644 index 00000000000..d8de0c30388 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-09.wav differ diff --git a/packages/kilo-vscode/audio-wav/bip-bop-10.wav b/packages/kilo-vscode/audio-wav/bip-bop-10.wav new file mode 100644 index 00000000000..4115a148d45 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/bip-bop-10.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-01.wav b/packages/kilo-vscode/audio-wav/nope-01.wav new file mode 100644 index 00000000000..9b8ef91ba58 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-01.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-02.wav b/packages/kilo-vscode/audio-wav/nope-02.wav new file mode 100644 index 00000000000..b7448531fd9 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-02.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-03.wav b/packages/kilo-vscode/audio-wav/nope-03.wav new file mode 100644 index 00000000000..0d2ab691532 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-03.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-04.wav b/packages/kilo-vscode/audio-wav/nope-04.wav new file mode 100644 index 00000000000..833eab0474f Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-04.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-05.wav b/packages/kilo-vscode/audio-wav/nope-05.wav new file mode 100644 index 00000000000..084a1e67af2 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-05.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-06.wav b/packages/kilo-vscode/audio-wav/nope-06.wav new file mode 100644 index 00000000000..20725a9d5c1 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-06.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-07.wav b/packages/kilo-vscode/audio-wav/nope-07.wav new file mode 100644 index 00000000000..7805d677ece Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-07.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-08.wav b/packages/kilo-vscode/audio-wav/nope-08.wav new file mode 100644 index 00000000000..2c2553cbb77 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-08.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-09.wav b/packages/kilo-vscode/audio-wav/nope-09.wav new file mode 100644 index 00000000000..9e19ce85149 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-09.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-10.wav b/packages/kilo-vscode/audio-wav/nope-10.wav new file mode 100644 index 00000000000..87356b56bc1 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-10.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-11.wav b/packages/kilo-vscode/audio-wav/nope-11.wav new file mode 100644 index 00000000000..53717778019 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-11.wav differ diff --git a/packages/kilo-vscode/audio-wav/nope-12.wav b/packages/kilo-vscode/audio-wav/nope-12.wav new file mode 100644 index 00000000000..2a27f478841 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/nope-12.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-01.wav b/packages/kilo-vscode/audio-wav/staplebops-01.wav new file mode 100644 index 00000000000..adff7946fce Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-01.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-02.wav b/packages/kilo-vscode/audio-wav/staplebops-02.wav new file mode 100644 index 00000000000..aecad5750da Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-02.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-03.wav b/packages/kilo-vscode/audio-wav/staplebops-03.wav new file mode 100644 index 00000000000..a0bdcd17fc3 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-03.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-04.wav b/packages/kilo-vscode/audio-wav/staplebops-04.wav new file mode 100644 index 00000000000..003f95d50a9 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-04.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-05.wav b/packages/kilo-vscode/audio-wav/staplebops-05.wav new file mode 100644 index 00000000000..dd44a211cab Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-05.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-06.wav b/packages/kilo-vscode/audio-wav/staplebops-06.wav new file mode 100644 index 00000000000..17f81f2bd7c Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-06.wav differ diff --git a/packages/kilo-vscode/audio-wav/staplebops-07.wav b/packages/kilo-vscode/audio-wav/staplebops-07.wav new file mode 100644 index 00000000000..2d6feb9c47f Binary files /dev/null and b/packages/kilo-vscode/audio-wav/staplebops-07.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-01.wav b/packages/kilo-vscode/audio-wav/yup-01.wav new file mode 100644 index 00000000000..dd208609aff Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-01.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-02.wav b/packages/kilo-vscode/audio-wav/yup-02.wav new file mode 100644 index 00000000000..893aefdbeb2 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-02.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-03.wav b/packages/kilo-vscode/audio-wav/yup-03.wav new file mode 100644 index 00000000000..6657abf692f Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-03.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-04.wav b/packages/kilo-vscode/audio-wav/yup-04.wav new file mode 100644 index 00000000000..151e359481f Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-04.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-05.wav b/packages/kilo-vscode/audio-wav/yup-05.wav new file mode 100644 index 00000000000..5a034344427 Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-05.wav differ diff --git a/packages/kilo-vscode/audio-wav/yup-06.wav b/packages/kilo-vscode/audio-wav/yup-06.wav new file mode 100644 index 00000000000..f281100818e Binary files /dev/null and b/packages/kilo-vscode/audio-wav/yup-06.wav differ diff --git a/packages/kilo-vscode/audio/alert-01.aac b/packages/kilo-vscode/audio/alert-01.aac new file mode 100644 index 00000000000..dc608d8b1cf Binary files /dev/null and b/packages/kilo-vscode/audio/alert-01.aac differ diff --git a/packages/kilo-vscode/audio/alert-02.aac b/packages/kilo-vscode/audio/alert-02.aac new file mode 100644 index 00000000000..008bc4f7009 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-02.aac differ diff --git a/packages/kilo-vscode/audio/alert-03.aac b/packages/kilo-vscode/audio/alert-03.aac new file mode 100644 index 00000000000..bd977d4afb3 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-03.aac differ diff --git a/packages/kilo-vscode/audio/alert-04.aac b/packages/kilo-vscode/audio/alert-04.aac new file mode 100644 index 00000000000..a1d6cdcc71c Binary files /dev/null and b/packages/kilo-vscode/audio/alert-04.aac differ diff --git a/packages/kilo-vscode/audio/alert-05.aac b/packages/kilo-vscode/audio/alert-05.aac new file mode 100644 index 00000000000..185d7372d8f Binary files /dev/null and b/packages/kilo-vscode/audio/alert-05.aac differ diff --git a/packages/kilo-vscode/audio/alert-06.aac b/packages/kilo-vscode/audio/alert-06.aac new file mode 100644 index 00000000000..8da19b7d8d6 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-06.aac differ diff --git a/packages/kilo-vscode/audio/alert-07.aac b/packages/kilo-vscode/audio/alert-07.aac new file mode 100644 index 00000000000..6cfd9533e86 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-07.aac differ diff --git a/packages/kilo-vscode/audio/alert-08.aac b/packages/kilo-vscode/audio/alert-08.aac new file mode 100644 index 00000000000..88038715010 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-08.aac differ diff --git a/packages/kilo-vscode/audio/alert-09.aac b/packages/kilo-vscode/audio/alert-09.aac new file mode 100644 index 00000000000..13662cdd507 Binary files /dev/null and b/packages/kilo-vscode/audio/alert-09.aac differ diff --git a/packages/kilo-vscode/audio/alert-10.aac b/packages/kilo-vscode/audio/alert-10.aac new file mode 100644 index 00000000000..9720f0b79ae Binary files /dev/null and b/packages/kilo-vscode/audio/alert-10.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-01.aac b/packages/kilo-vscode/audio/bip-bop-01.aac new file mode 100644 index 00000000000..d5ece46f1d1 Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-01.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-02.aac b/packages/kilo-vscode/audio/bip-bop-02.aac new file mode 100644 index 00000000000..2bf272c1ac7 Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-02.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-03.aac b/packages/kilo-vscode/audio/bip-bop-03.aac new file mode 100644 index 00000000000..5011f12d103 Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-03.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-04.aac b/packages/kilo-vscode/audio/bip-bop-04.aac new file mode 100644 index 00000000000..af6fd3c0a3f Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-04.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-05.aac b/packages/kilo-vscode/audio/bip-bop-05.aac new file mode 100644 index 00000000000..75ce28a8c41 Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-05.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-06.aac b/packages/kilo-vscode/audio/bip-bop-06.aac new file mode 100644 index 00000000000..1aa68df769f Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-06.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-07.aac b/packages/kilo-vscode/audio/bip-bop-07.aac new file mode 100644 index 00000000000..47d0e602a1a Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-07.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-08.aac b/packages/kilo-vscode/audio/bip-bop-08.aac new file mode 100644 index 00000000000..ce6c3cffd3c Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-08.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-09.aac b/packages/kilo-vscode/audio/bip-bop-09.aac new file mode 100644 index 00000000000..450bc5e747c Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-09.aac differ diff --git a/packages/kilo-vscode/audio/bip-bop-10.aac b/packages/kilo-vscode/audio/bip-bop-10.aac new file mode 100644 index 00000000000..50076b3c91d Binary files /dev/null and b/packages/kilo-vscode/audio/bip-bop-10.aac differ diff --git a/packages/kilo-vscode/audio/nope-01.aac b/packages/kilo-vscode/audio/nope-01.aac new file mode 100644 index 00000000000..b55a4dc4e3d Binary files /dev/null and b/packages/kilo-vscode/audio/nope-01.aac differ diff --git a/packages/kilo-vscode/audio/nope-02.aac b/packages/kilo-vscode/audio/nope-02.aac new file mode 100644 index 00000000000..efa349ec1c6 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-02.aac differ diff --git a/packages/kilo-vscode/audio/nope-03.aac b/packages/kilo-vscode/audio/nope-03.aac new file mode 100644 index 00000000000..f9650c8e495 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-03.aac differ diff --git a/packages/kilo-vscode/audio/nope-04.aac b/packages/kilo-vscode/audio/nope-04.aac new file mode 100644 index 00000000000..0abd12f2979 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-04.aac differ diff --git a/packages/kilo-vscode/audio/nope-05.aac b/packages/kilo-vscode/audio/nope-05.aac new file mode 100644 index 00000000000..2352663343b Binary files /dev/null and b/packages/kilo-vscode/audio/nope-05.aac differ diff --git a/packages/kilo-vscode/audio/nope-06.aac b/packages/kilo-vscode/audio/nope-06.aac new file mode 100644 index 00000000000..7e5b66ecd90 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-06.aac differ diff --git a/packages/kilo-vscode/audio/nope-07.aac b/packages/kilo-vscode/audio/nope-07.aac new file mode 100644 index 00000000000..80b5b02c01c Binary files /dev/null and b/packages/kilo-vscode/audio/nope-07.aac differ diff --git a/packages/kilo-vscode/audio/nope-08.aac b/packages/kilo-vscode/audio/nope-08.aac new file mode 100644 index 00000000000..96e92f99f99 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-08.aac differ diff --git a/packages/kilo-vscode/audio/nope-09.aac b/packages/kilo-vscode/audio/nope-09.aac new file mode 100644 index 00000000000..d3e545be376 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-09.aac differ diff --git a/packages/kilo-vscode/audio/nope-10.aac b/packages/kilo-vscode/audio/nope-10.aac new file mode 100644 index 00000000000..798291aadbe Binary files /dev/null and b/packages/kilo-vscode/audio/nope-10.aac differ diff --git a/packages/kilo-vscode/audio/nope-11.aac b/packages/kilo-vscode/audio/nope-11.aac new file mode 100644 index 00000000000..64caccf36a6 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-11.aac differ diff --git a/packages/kilo-vscode/audio/nope-12.aac b/packages/kilo-vscode/audio/nope-12.aac new file mode 100644 index 00000000000..39ec243b551 Binary files /dev/null and b/packages/kilo-vscode/audio/nope-12.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-01.aac b/packages/kilo-vscode/audio/staplebops-01.aac new file mode 100644 index 00000000000..8bb6abf28c2 Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-01.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-02.aac b/packages/kilo-vscode/audio/staplebops-02.aac new file mode 100644 index 00000000000..577a9e7f5a4 Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-02.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-03.aac b/packages/kilo-vscode/audio/staplebops-03.aac new file mode 100644 index 00000000000..934f19ce706 Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-03.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-04.aac b/packages/kilo-vscode/audio/staplebops-04.aac new file mode 100644 index 00000000000..3447c273410 Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-04.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-05.aac b/packages/kilo-vscode/audio/staplebops-05.aac new file mode 100644 index 00000000000..6d20a1a0c6a Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-05.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-06.aac b/packages/kilo-vscode/audio/staplebops-06.aac new file mode 100644 index 00000000000..5f68ade43fa Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-06.aac differ diff --git a/packages/kilo-vscode/audio/staplebops-07.aac b/packages/kilo-vscode/audio/staplebops-07.aac new file mode 100644 index 00000000000..895c06f9bb7 Binary files /dev/null and b/packages/kilo-vscode/audio/staplebops-07.aac differ diff --git a/packages/kilo-vscode/audio/yup-01.aac b/packages/kilo-vscode/audio/yup-01.aac new file mode 100644 index 00000000000..fd300404f7f Binary files /dev/null and b/packages/kilo-vscode/audio/yup-01.aac differ diff --git a/packages/kilo-vscode/audio/yup-02.aac b/packages/kilo-vscode/audio/yup-02.aac new file mode 100644 index 00000000000..68ae153a6e9 Binary files /dev/null and b/packages/kilo-vscode/audio/yup-02.aac differ diff --git a/packages/kilo-vscode/audio/yup-03.aac b/packages/kilo-vscode/audio/yup-03.aac new file mode 100644 index 00000000000..6d5d968f80b Binary files /dev/null and b/packages/kilo-vscode/audio/yup-03.aac differ diff --git a/packages/kilo-vscode/audio/yup-04.aac b/packages/kilo-vscode/audio/yup-04.aac new file mode 100644 index 00000000000..d3e88fe958d Binary files /dev/null and b/packages/kilo-vscode/audio/yup-04.aac differ diff --git a/packages/kilo-vscode/audio/yup-05.aac b/packages/kilo-vscode/audio/yup-05.aac new file mode 100644 index 00000000000..3613c5d782e Binary files /dev/null and b/packages/kilo-vscode/audio/yup-05.aac differ diff --git a/packages/kilo-vscode/audio/yup-06.aac b/packages/kilo-vscode/audio/yup-06.aac new file mode 100644 index 00000000000..15786225db1 Binary files /dev/null and b/packages/kilo-vscode/audio/yup-06.aac differ diff --git a/packages/kilo-vscode/eslint.config.mjs b/packages/kilo-vscode/eslint.config.mjs index 0914b0366f7..92a2c385ce5 100644 --- a/packages/kilo-vscode/eslint.config.mjs +++ b/packages/kilo-vscode/eslint.config.mjs @@ -38,7 +38,7 @@ export default [ // New code must stay ≤ 20. Do not raise these caps; refactor instead. { files: ["src/KiloProvider.ts"], - rules: { complexity: ["error", 150], "max-lines": ["error", 3600] }, + rules: { complexity: ["error", 154], "max-lines": ["error", 3689] }, }, { files: ["webview-ui/agent-manager/AgentManagerApp.tsx"], diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index 2a297381b6c..b14bc7ebc6a 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -839,28 +839,166 @@ }, "kilo-code.new.sounds.agent": { "type": "string", - "default": "default", + "default": "system", "enum": [ "default", - "none" + "none", + "system", + "alert-01", + "alert-02", + "alert-03", + "alert-04", + "alert-05", + "alert-06", + "alert-07", + "alert-08", + "alert-09", + "alert-10", + "bip-bop-01", + "bip-bop-02", + "bip-bop-03", + "bip-bop-04", + "bip-bop-05", + "bip-bop-06", + "bip-bop-07", + "bip-bop-08", + "bip-bop-09", + "bip-bop-10", + "staplebops-01", + "staplebops-02", + "staplebops-03", + "staplebops-04", + "staplebops-05", + "staplebops-06", + "staplebops-07", + "nope-01", + "nope-02", + "nope-03", + "nope-04", + "nope-05", + "nope-06", + "nope-07", + "nope-08", + "nope-09", + "nope-10", + "nope-11", + "nope-12", + "yup-01", + "yup-02", + "yup-03", + "yup-04", + "yup-05", + "yup-06" ], "description": "Sound to play when agent completes" }, "kilo-code.new.sounds.permissions": { "type": "string", - "default": "default", + "default": "system", "enum": [ "default", - "none" + "none", + "system", + "alert-01", + "alert-02", + "alert-03", + "alert-04", + "alert-05", + "alert-06", + "alert-07", + "alert-08", + "alert-09", + "alert-10", + "bip-bop-01", + "bip-bop-02", + "bip-bop-03", + "bip-bop-04", + "bip-bop-05", + "bip-bop-06", + "bip-bop-07", + "bip-bop-08", + "bip-bop-09", + "bip-bop-10", + "staplebops-01", + "staplebops-02", + "staplebops-03", + "staplebops-04", + "staplebops-05", + "staplebops-06", + "staplebops-07", + "nope-01", + "nope-02", + "nope-03", + "nope-04", + "nope-05", + "nope-06", + "nope-07", + "nope-08", + "nope-09", + "nope-10", + "nope-11", + "nope-12", + "yup-01", + "yup-02", + "yup-03", + "yup-04", + "yup-05", + "yup-06" ], "description": "Sound to play on permission requests" }, "kilo-code.new.sounds.errors": { "type": "string", - "default": "default", + "default": "system", "enum": [ "default", - "none" + "none", + "system", + "alert-01", + "alert-02", + "alert-03", + "alert-04", + "alert-05", + "alert-06", + "alert-07", + "alert-08", + "alert-09", + "alert-10", + "bip-bop-01", + "bip-bop-02", + "bip-bop-03", + "bip-bop-04", + "bip-bop-05", + "bip-bop-06", + "bip-bop-07", + "bip-bop-08", + "bip-bop-09", + "bip-bop-10", + "staplebops-01", + "staplebops-02", + "staplebops-03", + "staplebops-04", + "staplebops-05", + "staplebops-06", + "staplebops-07", + "nope-01", + "nope-02", + "nope-03", + "nope-04", + "nope-05", + "nope-06", + "nope-07", + "nope-08", + "nope-09", + "nope-10", + "nope-11", + "nope-12", + "yup-01", + "yup-02", + "yup-03", + "yup-04", + "yup-05", + "yup-06" ], "description": "Sound to play on errors" }, diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 4556b4fcbc6..0ba437b75d8 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -61,6 +61,8 @@ import { watchFontSizeConfig } from "./kilo-provider/font-size" import { getTerminalContents } from "./services/terminal/context" import { disposeGitChangesTarget } from "./kilo-provider/git-changes-target" import { interceptMessage } from "./kilo-provider/git-changes-request" +import { sendOsNotification } from "./util/notify" +import { playSound, resolveSoundId } from "./util/sound" import { matchFollowup, recordFollowup, type Followup } from "./kilo-provider/followup-session" import { clearCommandsCache, loadCommands } from "./kilo-provider/commands" import { fetchMessagePage, MESSAGE_PAGE_LIMIT } from "./kilo-provider/message-page" @@ -158,6 +160,9 @@ const SESSION_SCOPED_PART_EVENTS = new Set(["message.part.updated", "message.par const isSessionScopedPartEvent = (type: string) => SESSION_SCOPED_PART_EVENTS.has(type) export class KiloProvider implements vscode.WebviewViewProvider, TelemetryPropertiesProvider { + private readonly notificationCooldowns = new Map() + private readonly COOLDOWN_MS = 700 + public static readonly viewType = "kilo-code.SidebarProvider" private readonly instanceId = crypto.randomUUID() @@ -202,6 +207,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private syncedChildSessions: Set = new Set() /** Tracks the latest status for each session, used to warn before destructive config operations. */ private sessionStatusMap = new Map() + /** Sessions that were in "busy" state — used to detect busy→idle transitions for notifications. */ + private busySessions = new Set() + /** Last known VS Code window focus state — used to decide whether to show OS notifications. */ + private lastFocusState: boolean | undefined = undefined /** Per-session directory overrides (e.g., worktree paths registered by AgentManagerProvider). */ private sessionDirectories = new Map() private permissionDirectories = new Map() @@ -218,6 +227,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private readonly confirmations = new MessageConfirmation() private unsubscribeEvent: (() => void) | null = null private unsubscribeState: (() => void) | null = null + private unsubscribeFocus: vscode.Disposable | null = null /** Cached legacy migration data so migrate() doesn't re-read from disk/SecretStorage. */ // legacy-migration private cachedLegacyData: import("./legacy-migration/legacy-types").LegacyMigrationData | null = null // legacy-migration /** Guard to prevent checkAndShowMigrationWizard running concurrently. */ // legacy-migration @@ -962,6 +972,23 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "requestNotificationSettings": this.sendNotificationSettings() break + case "testNotification": { + const setting = message.settingType as "agent" | "permissions" | "errors" + const sounds = vscode.workspace.getConfiguration("kilo-code.new.sounds") + const soundSetting = sounds.get(setting, "system") + const soundId = resolveSoundId(soundSetting, setting) + if (soundId) { + void playSound(soundId) + } + const title = + setting === "agent" + ? "Test agent notification" + : setting === "permissions" + ? "Test permissions notification" + : "Test errors notification" + void sendOsNotification(title, "This is a sample notification to test the sound.") + break + } case "requestTimelineSetting": this.sendTimelineSetting() break @@ -1172,6 +1199,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // Clean up any existing subscriptions (e.g., sidebar re-shown) this.unsubscribeEvent?.() this.unsubscribeState?.() + this.unsubscribeFocus?.dispose() + this.unsubscribeFocus = null this.unsubscribeNotificationDismiss?.() this.unsubscribeLanguageChange?.() this.unsubscribeProfileChange?.() @@ -1241,6 +1270,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } }) + // Track VS Code window focus state for OS notification decisions + if (vscode.window.onDidChangeWindowState) { + this.unsubscribeFocus = vscode.window.onDidChangeWindowState((state) => { + this.lastFocusState = state.focused + }) + this.lastFocusState = vscode.window.state.focused + } + // Subscribe to notification dismiss broadcast from other KiloProvider instances this.unsubscribeNotificationDismiss = this.connectionService.onNotificationDismissed(() => { this.fetchAndSendNotifications() @@ -2376,9 +2413,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper notifyAgent: notifications.get("agent", true), notifyPermissions: notifications.get("permissions", true), notifyErrors: notifications.get("errors", true), - soundAgent: sounds.get("agent", "default"), - soundPermissions: sounds.get("permissions", "default"), - soundErrors: sounds.get("errors", "default"), + soundAgent: sounds.get("agent", "system"), + soundPermissions: sounds.get("permissions", "system"), + soundErrors: sounds.get("errors", "system"), }, }) } @@ -2400,6 +2437,43 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper return getBusySessionCount(this.sessionStatusMap) } + /** + * Send a native OS notification when VS Code is not focused and the relevant setting is enabled. + * Uses platform-specific commands (osascript/notify-send/PowerShell) instead of VS Code's + * built-in notification system, which only shows in-app toasts. + */ + private notifyIfNotFocused( + setting: "agent" | "permissions" | "errors", + title: string, + message?: string, + sessionID?: string, + ): void { + const now = Date.now() + const cooldownKey = sessionID ? `${setting}:${sessionID}` : setting + const last = this.notificationCooldowns.get(cooldownKey) ?? 0 + if (now - last < this.COOLDOWN_MS) return + this.notificationCooldowns.set(cooldownKey, now) + + const config = vscode.workspace.getConfiguration("kilo-code.new.notifications") + if (!config.get(setting, true)) return + // Only skip notification if we're explicitly focused (not undefined) + if (this.lastFocusState === true) return + + const soundConfig = vscode.workspace.getConfiguration("kilo-code.new.sounds") + const soundSetting = soundConfig.get(setting, "system") + const soundId = resolveSoundId(soundSetting, setting) + if (soundId) { + void playSound(soundId) + } + + void sendOsNotification(title, message ?? title) + } + + /** + * Handle config update request from the webview. + * Applies a partial config update via the global config endpoint, then pushes + * the full merged config back to the webview. + */ private async handleUpdateConfig(partial: Partial, project: Partial = {}): Promise { if (!this.client || this.connectionState !== "connected") { this.postMessage({ type: "configUpdateFailed", message: "Not connected to CLI backend" }) @@ -3151,7 +3225,15 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper // busy-session warning on Save. if (event.type === "session.status") { const sid = event.properties.sessionID - this.sessionStatusMap.set(sid, event.properties.status.type) + const status = event.properties.status.type + this.sessionStatusMap.set(sid, status) + if (status === "busy") { + this.busySessions.add(sid) + } else if (status === "idle" && this.busySessions.has(sid) && this.trackedSessionIds.has(sid)) { + this.busySessions.delete(sid) + if (this.connectionService.shouldNotify(sid, "session.status:idle")) + this.notifyIfNotFocused("agent", "Agent task completed", undefined, sid) + } const msg = mapSSEEventToWebviewMessage(event, sid) if (msg) { this.streams.flush(sid) @@ -3235,6 +3317,32 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper if (path.resolve(directory) !== current) return } + // OS notifications for events that need user attention + if ( + event.type === "permission.asked" && + sessionID && + this.connectionService.shouldNotify(sessionID, "permission.asked", event.properties.id) + ) { + const tool = event.properties.permission ?? event.properties.tool ?? "tool" + this.notifyIfNotFocused("permissions", `Permission required: ${tool}`, undefined, sessionID) + } + if ( + event.type === "question.asked" && + sessionID && + this.connectionService.shouldNotify(sessionID, "question.asked", event.properties.id) + ) { + const questions = event.properties.questions as Array<{ question: string }> | undefined + this.notifyIfNotFocused("permissions", "Agent Question", questions?.[0]?.question, sessionID) + } + if ( + event.type === "session.error" && + sessionID && + this.connectionService.shouldNotify(sessionID, "session.error") + ) { + const err = event.properties.error as { message?: string } | undefined + this.notifyIfNotFocused("errors", `Session error: ${err?.message ?? "Unknown error"}`, undefined, sessionID) + } + const msg = mapSSEEventToWebviewMessage(event, sessionID) if (!msg) return if (msg.type === "partUpdated") { @@ -3549,6 +3657,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.statsGitOps?.dispose() this.unsubscribeEvent?.() this.unsubscribeState?.() + this.unsubscribeFocus?.dispose() this.unsubscribeNotificationDismiss?.() this.unsubscribeLanguageChange?.() this.unsubscribeProfileChange?.() @@ -3572,6 +3681,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.sessionDirectories.clear() this.permissionDirectories.clear() this.sessionStatusMap.clear() + this.busySessions.clear() this.ignoreController?.dispose() this.chatAutocomplete?.dispose() ;(this.marketplace?.dispose(), disposeGitChangesTarget()) diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts index 3b5a9e0d950..84e0ac8605c 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts @@ -84,6 +84,7 @@ export class KiloConnectionService { * Used primarily for message.part.updated where only messageID may be present. */ private readonly messageSessionIdsByMessageId: Map = new Map() + private readonly notifiedEvents: Set = new Set() /** Provider key → single focused session ID. */ private readonly focused: Map = new Map() @@ -245,6 +246,22 @@ export class KiloConnectionService { ) } + /** + * Check-and-mark deduplication for OS notifications. + * Returns true the first time a given sessionID + eventType (+ optional requestID) combination is seen, + * and false for all subsequent calls until the entry expires (TTL). + * This prevents multiple KiloProvider instances from emitting duplicate + * OS notifications for the same backend event. + * When requestID is provided, distinct requests in the same session are not suppressed. + */ + shouldNotify(sessionID: string, eventType: string, requestID?: string): boolean { + const key = requestID ? `${sessionID}:${eventType}:${requestID}` : `${sessionID}:${eventType}` + if (this.notifiedEvents.has(key)) return false + this.notifiedEvents.add(key) + setTimeout(() => this.notifiedEvents.delete(key), 5_000) + return true + } + /** * Subscribe to notification dismiss events broadcast from any KiloProvider. Returns unsubscribe function. */ diff --git a/packages/kilo-vscode/src/util/notify.ts b/packages/kilo-vscode/src/util/notify.ts new file mode 100644 index 00000000000..aceb75337ca --- /dev/null +++ b/packages/kilo-vscode/src/util/notify.ts @@ -0,0 +1,62 @@ +import * as os from "os" +import * as vscode from "vscode" +import { exec } from "./process" + +/** + * Send a native OS notification. + * Uses platform-specific commands so notifications appear even when VS Code is focused. + */ +export async function sendOsNotification(title: string, body: string): Promise { + switch (os.platform()) { + case "darwin": { + const esc = (s: string) => + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t") + + // Try terminal-notifier first for richer notifications + try { + const extensionUri = vscode.extensions.getExtension(`kilocode.kilo-code`)!.extensionUri + const iconPath = vscode.Uri.joinPath(extensionUri, "assets", "icons", "kilo.png").fsPath + await exec("terminal-notifier", ["-message", body, "-title", title, "-appIcon", iconPath]) + break + } catch { + // Fall back to osascript if terminal-notifier fails + } + + // Fallback to osascript + await exec("osascript", ["-e", `display notification "${esc(body)}" with title "${esc(title)}"`]).catch((err) => { + console.log("[Kilo New] macOS notification failed:", err) + }) + break + } + case "linux": + // notify-send receives raw arguments directly, no shell escaping needed + await exec("notify-send", [title, body]).catch((err) => { + console.log("[Kilo New] Linux notification failed:", err) + }) + break + case "win32": { + // base64 encoding prevents injection; use raw text (no shell escaping) + const encodedTitle = Buffer.from(title, "utf8").toString("base64") + const encodedBody = Buffer.from(body, "utf8").toString("base64") + await exec("powershell", [ + "-NonInteractive", + "-Command", + `$t = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("${encodedTitle}")); ` + + `$b = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("${encodedBody}")); ` + + `[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null; ` + + `$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02); ` + + `$xml = [Windows.Data.Xml.Dom.XmlDocument]::new(); $xml.LoadXml($template.GetXml()); ` + + `$textNodes = $xml.GetElementsByTagName("text"); ` + + `$textNodes.Item(0).AppendChild($xml.CreateTextNode($t)) > $null; ` + + `$textNodes.Item(1).AppendChild($xml.CreateTextNode($b)) > $null; ` + + `$audio = $xml.CreateElement("audio"); $audio.SetAttribute("silent", "true"); ` + + `$xml.DocumentElement.AppendChild($audio) > $null; ` + + `$toast = [Windows.UI.Notifications.ToastNotification]::new($xml); ` + + `[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Kilo Code").Show($toast)`, + ]).catch((err) => { + console.log("[Kilo New] Windows notification failed:", err) + }) + break + } + } +} diff --git a/packages/kilo-vscode/src/util/sound.ts b/packages/kilo-vscode/src/util/sound.ts new file mode 100644 index 00000000000..a3d529e5ed8 --- /dev/null +++ b/packages/kilo-vscode/src/util/sound.ts @@ -0,0 +1,221 @@ +import { exec } from "../util/process" +import * as path from "path" +import * as fs from "fs" + +const SOUND_DIR = path.join(__dirname, "../audio-wav") + +export type SoundID = + | "system" + | "alert-01" + | "alert-02" + | "alert-03" + | "alert-04" + | "alert-05" + | "alert-06" + | "alert-07" + | "alert-08" + | "alert-09" + | "alert-10" + | "bip-bop-01" + | "bip-bop-02" + | "bip-bop-03" + | "bip-bop-04" + | "bip-bop-05" + | "bip-bop-06" + | "bip-bop-07" + | "bip-bop-08" + | "bip-bop-09" + | "bip-bop-10" + | "staplebops-01" + | "staplebops-02" + | "staplebops-03" + | "staplebops-04" + | "staplebops-05" + | "staplebops-06" + | "staplebops-07" + | "nope-01" + | "nope-02" + | "nope-03" + | "nope-04" + | "nope-05" + | "nope-06" + | "nope-07" + | "nope-08" + | "nope-09" + | "nope-10" + | "nope-11" + | "nope-12" + | "yup-01" + | "yup-02" + | "yup-03" + | "yup-04" + | "yup-05" + | "yup-06" + +const SOUND_MAP: Record = { + system: "", + "alert-01": "alert-01.wav", + "alert-02": "alert-02.wav", + "alert-03": "alert-03.wav", + "alert-04": "alert-04.wav", + "alert-05": "alert-05.wav", + "alert-06": "alert-06.wav", + "alert-07": "alert-07.wav", + "alert-08": "alert-08.wav", + "alert-09": "alert-09.wav", + "alert-10": "alert-10.wav", + "bip-bop-01": "bip-bop-01.wav", + "bip-bop-02": "bip-bop-02.wav", + "bip-bop-03": "bip-bop-03.wav", + "bip-bop-04": "bip-bop-04.wav", + "bip-bop-05": "bip-bop-05.wav", + "bip-bop-06": "bip-bop-06.wav", + "bip-bop-07": "bip-bop-07.wav", + "bip-bop-08": "bip-bop-08.wav", + "bip-bop-09": "bip-bop-09.wav", + "bip-bop-10": "bip-bop-10.wav", + "staplebops-01": "staplebops-01.wav", + "staplebops-02": "staplebops-02.wav", + "staplebops-03": "staplebops-03.wav", + "staplebops-04": "staplebops-04.wav", + "staplebops-05": "staplebops-05.wav", + "staplebops-06": "staplebops-06.wav", + "staplebops-07": "staplebops-07.wav", + "nope-01": "nope-01.wav", + "nope-02": "nope-02.wav", + "nope-03": "nope-03.wav", + "nope-04": "nope-04.wav", + "nope-05": "nope-05.wav", + "nope-06": "nope-06.wav", + "nope-07": "nope-07.wav", + "nope-08": "nope-08.wav", + "nope-09": "nope-09.wav", + "nope-10": "nope-10.wav", + "nope-11": "nope-11.wav", + "nope-12": "nope-12.wav", + "yup-01": "yup-01.wav", + "yup-02": "yup-02.wav", + "yup-03": "yup-03.wav", + "yup-04": "yup-04.wav", + "yup-05": "yup-05.wav", + "yup-06": "yup-06.wav", +} + +const DEFAULT_SOUNDS: Record = { + agent: "alert-01", + permissions: "bip-bop-01", + errors: "nope-01", +} + +export function resolveSoundId( + settingValue: string | undefined, + settingType: "agent" | "permissions" | "errors", +): SoundID | undefined { + if (!settingValue || settingValue === "none") return undefined + if (settingValue === "system") return "system" + + if (settingValue === "default") { + return DEFAULT_SOUNDS[settingType] + } + + if (settingValue in SOUND_MAP) { + return settingValue as SoundID + } + + return undefined +} + +let chain = Promise.resolve() +let queued = 0 + +const MAX_QUEUED = 3 + +async function playSystem(): Promise { + switch (process.platform) { + case "darwin": + await exec("osascript", ["-e", "beep"]).catch((err) => { + console.log("[Kilo New] macOS system sound failed:", err) + }) + return + case "linux": + await exec("canberra-gtk-play", ["-i", "message-new-instant"]).catch(async (err) => { + console.log("[Kilo New] canberra-gtk-play failed, trying paplay:", err) + await exec("paplay", ["/usr/share/sounds/freedesktop/stereo/message.oga"]).catch((err2) => { + console.log("[Kilo New] Linux system sound failed:", err2) + }) + }) + return + case "win32": + await exec("powershell", [ + "-NonInteractive", + "-Command", + "[System.Media.SystemSounds]::Exclamation.Play()", + ]).catch((err) => { + console.log("[Kilo New] Windows system sound failed:", err) + }) + return + } +} + +async function playFile(soundId: Exclude): Promise { + const file = SOUND_MAP[soundId] + if (!file) return + + const filePath = path.join(SOUND_DIR, file) + if (!fs.existsSync(filePath)) return + if (!filePath.match(/^[^\x00-\x1F\x7F]+$/)) return + + switch (process.platform) { + case "darwin": + await exec("afplay", [filePath]).catch(async (err) => { + console.log("[Kilo New] afplay failed, trying play:", err) + await exec("play", [filePath]).catch((err2) => { + console.log("[Kilo New] Sound playback failed:", err2) + }) + }) + return + case "linux": + await exec("/usr/bin/aplay", ["-f", "CD", filePath]).catch(async (err) => { + console.log("[Kilo New] aplay failed, trying paplay:", err) + await exec("paplay", [filePath]).catch(async (err2) => { + console.log("[Kilo New] paplay failed, trying play:", err2) + await exec("play", [filePath]).catch((err3) => { + console.log("[Kilo New] Sound playback failed:", err3) + }) + }) + }) + return + case "win32": { + const safePath = filePath.replace(/'/g, "''") + await exec("powershell", [ + "-NonInteractive", + "-Command", + `$path = '${safePath}'; $s = New-Object System.Media.SoundPlayer($path); $s.PlaySync(); $s.Dispose()`, + ]).catch((err) => { + console.log("[Kilo New] Windows sound playback failed:", err) + }) + return + } + } +} + +export async function playSound(soundId: SoundID): Promise { + if (queued >= MAX_QUEUED) return + + const currentChain = chain + queued++ + chain = currentChain + .catch(() => {}) + .then(async () => { + if (soundId === "system") { + await playSystem() + return + } + await playFile(soundId) + }) + .finally(() => { + queued-- + }) + + await chain +} diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/NotificationsTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/NotificationsTab.tsx index e77f10e42a4..57e222bec57 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/NotificationsTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/NotificationsTab.tsx @@ -1,4 +1,5 @@ import { Component, createSignal, onCleanup } from "solid-js" +import { Button } from "@kilocode/kilo-ui/button" import { Switch } from "@kilocode/kilo-ui/switch" import { Select } from "@kilocode/kilo-ui/select" import { Card } from "@kilocode/kilo-ui/card" @@ -15,6 +16,52 @@ interface SoundOption { const SOUND_OPTIONS: SoundOption[] = [ { value: "default", labelKey: "settings.notifications.sound.default" }, { value: "none", labelKey: "settings.notifications.sound.none" }, + { value: "system", labelKey: "settings.notifications.sound.system" }, + { value: "alert-01", labelKey: "sound.option.alert01" }, + { value: "alert-02", labelKey: "sound.option.alert02" }, + { value: "alert-03", labelKey: "sound.option.alert03" }, + { value: "alert-04", labelKey: "sound.option.alert04" }, + { value: "alert-05", labelKey: "sound.option.alert05" }, + { value: "alert-06", labelKey: "sound.option.alert06" }, + { value: "alert-07", labelKey: "sound.option.alert07" }, + { value: "alert-08", labelKey: "sound.option.alert08" }, + { value: "alert-09", labelKey: "sound.option.alert09" }, + { value: "alert-10", labelKey: "sound.option.alert10" }, + { value: "bip-bop-01", labelKey: "sound.option.bipbop01" }, + { value: "bip-bop-02", labelKey: "sound.option.bipbop02" }, + { value: "bip-bop-03", labelKey: "sound.option.bipbop03" }, + { value: "bip-bop-04", labelKey: "sound.option.bipbop04" }, + { value: "bip-bop-05", labelKey: "sound.option.bipbop05" }, + { value: "bip-bop-06", labelKey: "sound.option.bipbop06" }, + { value: "bip-bop-07", labelKey: "sound.option.bipbop07" }, + { value: "bip-bop-08", labelKey: "sound.option.bipbop08" }, + { value: "bip-bop-09", labelKey: "sound.option.bipbop09" }, + { value: "bip-bop-10", labelKey: "sound.option.bipbop10" }, + { value: "staplebops-01", labelKey: "sound.option.staplebops01" }, + { value: "staplebops-02", labelKey: "sound.option.staplebops02" }, + { value: "staplebops-03", labelKey: "sound.option.staplebops03" }, + { value: "staplebops-04", labelKey: "sound.option.staplebops04" }, + { value: "staplebops-05", labelKey: "sound.option.staplebops05" }, + { value: "staplebops-06", labelKey: "sound.option.staplebops06" }, + { value: "staplebops-07", labelKey: "sound.option.staplebops07" }, + { value: "nope-01", labelKey: "sound.option.nope01" }, + { value: "nope-02", labelKey: "sound.option.nope02" }, + { value: "nope-03", labelKey: "sound.option.nope03" }, + { value: "nope-04", labelKey: "sound.option.nope04" }, + { value: "nope-05", labelKey: "sound.option.nope05" }, + { value: "nope-06", labelKey: "sound.option.nope06" }, + { value: "nope-07", labelKey: "sound.option.nope07" }, + { value: "nope-08", labelKey: "sound.option.nope08" }, + { value: "nope-09", labelKey: "sound.option.nope09" }, + { value: "nope-10", labelKey: "sound.option.nope10" }, + { value: "nope-11", labelKey: "sound.option.nope11" }, + { value: "nope-12", labelKey: "sound.option.nope12" }, + { value: "yup-01", labelKey: "sound.option.yup01" }, + { value: "yup-02", labelKey: "sound.option.yup02" }, + { value: "yup-03", labelKey: "sound.option.yup03" }, + { value: "yup-04", labelKey: "sound.option.yup04" }, + { value: "yup-05", labelKey: "sound.option.yup05" }, + { value: "yup-06", labelKey: "sound.option.yup06" }, ] const NotificationsTab: Component = () => { @@ -24,9 +71,9 @@ const NotificationsTab: Component = () => { const [agentNotify, setAgentNotify] = createSignal(true) const [permNotify, setPermNotify] = createSignal(true) const [errorNotify, setErrorNotify] = createSignal(true) - const [agentSound, setAgentSound] = createSignal("default") - const [permSound, setPermSound] = createSignal("default") - const [errorSound, setErrorSound] = createSignal("default") + const [agentSound, setAgentSound] = createSignal("system") + const [permSound, setPermSound] = createSignal("system") + const [errorSound, setErrorSound] = createSignal("system") const unsubscribe = vscode.onMessage((message: ExtensionMessage) => { if (message.type !== "notificationSettingsLoaded") { @@ -48,6 +95,10 @@ const NotificationsTab: Component = () => { vscode.postMessage({ type: "updateSetting", key, value }) } + const test = (settingType: "agent" | "permissions" | "errors") => { + vscode.postMessage({ type: "testNotification", settingType }) + } + return (
@@ -105,62 +156,77 @@ const NotificationsTab: Component = () => { title={language.t("settings.notifications.agentSound.title")} description={language.t("settings.notifications.agentSound.description")} > - o.value === agentSound())} + value={(o) => o.value} + label={(o) => language.t(o.labelKey)} + onSelect={(o) => { + if (o) { + setAgentSound(o.value) + save("sounds.agent", o.value) + } + }} + variant="secondary" + size="small" + triggerVariant="settings" + /> + +
- o.value === permSound())} + value={(o) => o.value} + label={(o) => language.t(o.labelKey)} + onSelect={(o) => { + if (o) { + setPermSound(o.value) + save("sounds.permissions", o.value) + } + }} + variant="secondary" + size="small" + triggerVariant="settings" + /> + + - o.value === errorSound())} + value={(o) => o.value} + label={(o) => language.t(o.labelKey)} + onSelect={(o) => { + if (o) { + setErrorSound(o.value) + save("sounds.errors", o.value) + } + }} + variant="secondary" + size="small" + triggerVariant="settings" + /> + + diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 8702427303b..77890a0da26 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -1207,6 +1207,8 @@ export const dict = { "settings.notifications.errorSound.description": "الصوت عند الأخطاء", "settings.notifications.sound.default": "افتراضي", "settings.notifications.sound.none": "بدون", + "settings.notifications.sound.system": "افتراضي النظام", + "settings.notifications.testSound": "اختبار الإشعار", "settings.experimental.share.title": "وضع المشاركة", "settings.experimental.share.description": "سلوك مشاركة الجلسة", "settings.experimental.share.manual": "يدوي", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 2bca3fe4609..045c384863c 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -1235,6 +1235,8 @@ export const dict = { "settings.notifications.errorSound.description": "Som em erros", "settings.notifications.sound.default": "Padrão", "settings.notifications.sound.none": "Nenhum", + "settings.notifications.sound.system": "Padrão do sistema", + "settings.notifications.testSound": "Testar notificação", "settings.experimental.share.title": "Modo de compartilhamento", "settings.experimental.share.description": "Comportamento do compartilhamento de sessão", "settings.experimental.share.manual": "Manual", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index 71749e4a187..0a1bce40b45 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -1236,6 +1236,8 @@ export const dict = { "settings.notifications.errorSound.description": "Zvuk pri greškama", "settings.notifications.sound.default": "Zadano", "settings.notifications.sound.none": "Ništa", + "settings.notifications.sound.system": "Sistemski zadano", + "settings.notifications.testSound": "Testiraj obavijest", "settings.experimental.share.title": "Način dijeljenja", "settings.experimental.share.description": "Ponašanje dijeljenja sesije", "settings.experimental.share.manual": "Ručno", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index 8270b442cc0..6eae3bde1e8 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -1229,6 +1229,8 @@ export const dict = { "settings.notifications.errorSound.description": "Lyd ved fejl", "settings.notifications.sound.default": "Standard", "settings.notifications.sound.none": "Ingen", + "settings.notifications.sound.system": "Systemstandard", + "settings.notifications.testSound": "Test notifikation", "settings.experimental.share.title": "Delingstilstand", "settings.experimental.share.description": "Adfærd for sessionsdeling", "settings.experimental.share.manual": "Manuel", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index d30614336e2..903ad3a8100 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -1250,6 +1250,8 @@ export const dict = { "settings.notifications.errorSound.description": "Ton bei Fehlern abspielen", "settings.notifications.sound.default": "Standard", "settings.notifications.sound.none": "Kein", + "settings.notifications.sound.system": "Systemstandard", + "settings.notifications.testSound": "Benachrichtigung testen", "settings.experimental.share.title": "Freigabemodus", "settings.experimental.share.description": "Verhalten der Sitzungsfreigabe", "settings.experimental.share.manual": "Manuell", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 784b17fe3ca..d0b533b74cc 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -1218,6 +1218,8 @@ export const dict = { "settings.notifications.errorSound.description": "Sound to play on errors", "settings.notifications.sound.default": "Default", "settings.notifications.sound.none": "None", + "settings.notifications.sound.system": "System default", + "settings.notifications.testSound": "Test notification", "settings.experimental.share.title": "Share Mode", "settings.experimental.share.description": "How session sharing behaves", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 91313af992a..40bc41b927e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -1242,6 +1242,8 @@ export const dict = { "settings.notifications.errorSound.description": "Sonido a reproducir en errores", "settings.notifications.sound.default": "Predeterminado", "settings.notifications.sound.none": "Ninguno", + "settings.notifications.sound.system": "Predeterminado del sistema", + "settings.notifications.testSound": "Probar notificación", "settings.experimental.share.title": "Modo de compartir", "settings.experimental.share.description": "Comportamiento de compartir sesión", "settings.experimental.share.manual": "Manual", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 6de0bac6c66..4e4a95f071b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -1254,6 +1254,8 @@ export const dict = { "settings.notifications.errorSound.description": "Son à jouer en cas d'erreur", "settings.notifications.sound.default": "Par défaut", "settings.notifications.sound.none": "Aucun", + "settings.notifications.sound.system": "Par défaut du système", + "settings.notifications.testSound": "Tester la notification", "settings.experimental.share.title": "Mode de partage", "settings.experimental.share.description": "Comportement du partage de session", "settings.experimental.share.manual": "Manuel", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 94cfe25a70e..951e16d99a5 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -1224,6 +1224,8 @@ export const dict = { "settings.notifications.errorSound.description": "エラー発生時に再生するサウンド", "settings.notifications.sound.default": "デフォルト", "settings.notifications.sound.none": "なし", + "settings.notifications.sound.system": "システムデフォルト", + "settings.notifications.testSound": "通知をテスト", "settings.experimental.share.title": "共有モード", "settings.experimental.share.description": "セッション共有の動作", "settings.experimental.share.manual": "手動", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 565416de95e..9fd8140e5c4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -1218,6 +1218,8 @@ export const dict = { "settings.notifications.errorSound.description": "오류 발생 시 재생할 소리", "settings.notifications.sound.default": "기본값", "settings.notifications.sound.none": "없음", + "settings.notifications.sound.system": "시스템 기본값", + "settings.notifications.testSound": "알림 테스트", "settings.experimental.share.title": "공유 모드", "settings.experimental.share.description": "세션 공유 동작 방식", "settings.experimental.share.manual": "수동", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index ae02bc59c97..bd005a27ab1 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -1233,6 +1233,8 @@ export const dict = { "settings.notifications.errorSound.description": "Geluid om af te spelen bij fouten", "settings.notifications.sound.default": "Standaard", "settings.notifications.sound.none": "Geen", + "settings.notifications.sound.system": "Systeemstandaard", + "settings.notifications.testSound": "Melding testen", "settings.experimental.share.title": "Deelmodus", "settings.experimental.share.description": "Hoe sessiedelen zich gedraagt", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index d2382cb5169..ec72b51dfb2 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -1195,6 +1195,8 @@ export const dict = { "settings.notifications.errorSound.description": "Lyd ved feil", "settings.notifications.sound.default": "Standard", "settings.notifications.sound.none": "Ingen", + "settings.notifications.sound.system": "Systemstandard", + "settings.notifications.testSound": "Test varsling", "settings.experimental.share.title": "Delingsmodus", "settings.experimental.share.description": "Oppførsel for sesjonsdeling", "settings.experimental.share.manual": "Manuell", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index bfa4457e4c0..59995628f4f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -1194,6 +1194,8 @@ export const dict = { "settings.notifications.errorSound.description": "Dźwięk odtwarzany przy błędach", "settings.notifications.sound.default": "Domyślny", "settings.notifications.sound.none": "Brak", + "settings.notifications.sound.system": "Domyślny systemu", + "settings.notifications.testSound": "Testuj powiadomienie", "settings.experimental.share.title": "Tryb udostępniania", "settings.experimental.share.description": "Zachowanie udostępniania sesji", "settings.experimental.share.manual": "Ręczny", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 54c6c54c231..63510203546 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -1232,6 +1232,8 @@ export const dict = { "settings.notifications.errorSound.description": "Звук при ошибках", "settings.notifications.sound.default": "По умолчанию", "settings.notifications.sound.none": "Нет", + "settings.notifications.sound.system": "Системный по умолчанию", + "settings.notifications.testSound": "Тестовое уведомление", "settings.experimental.share.title": "Режим обмена", "settings.experimental.share.description": "Поведение обмена сессиями", "settings.experimental.share.manual": "Вручную", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 5e02ca3f5af..7308ac3efff 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -1215,6 +1215,8 @@ export const dict = { "settings.notifications.errorSound.description": "เสียงเมื่อเกิดข้อผิดพลาด", "settings.notifications.sound.default": "ค่าเริ่มต้น", "settings.notifications.sound.none": "ไม่มี", + "settings.notifications.sound.system": "ค่าเริ่มต้นของระบบ", + "settings.notifications.testSound": "ทดสอบการแจ้งเตือน", "settings.experimental.share.title": "โหมดแชร์", "settings.experimental.share.description": "พฤติกรรมการแชร์เซสชัน", "settings.experimental.share.manual": "ด้วยตนเอง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index 33dac0a65a1..d6a2fe3bad6 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -1226,6 +1226,8 @@ export const dict = { "settings.notifications.errorSound.description": "Hata oluştuğunda çalınacak ses", "settings.notifications.sound.default": "Varsayılan", "settings.notifications.sound.none": "Yok", + "settings.notifications.sound.system": "Sistem varsayılanı", + "settings.notifications.testSound": "Bildirim test et", "settings.experimental.share.title": "Paylaşım Modu", "settings.experimental.share.description": "Oturum paylaşımının nasıl davranacağı", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index bbc17472597..6523f39c1bb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -1226,6 +1226,8 @@ export const dict = { "settings.notifications.errorSound.description": "Звук, що відтворюється при виникненні помилок", "settings.notifications.sound.default": "За замовчуванням", "settings.notifications.sound.none": "Немає", + "settings.notifications.sound.system": "Системний", + "settings.notifications.testSound": "Тестове сповіщення", "settings.experimental.share.title": "Режим публікації", "settings.experimental.share.description": "Як поводиться публікація сесій", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index a68d0f2143f..d266a45754f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -1198,6 +1198,8 @@ export const dict = { "settings.notifications.errorSound.description": "发生错误时播放的声音", "settings.notifications.sound.default": "默认", "settings.notifications.sound.none": "无", + "settings.notifications.sound.system": "系统默认", + "settings.notifications.testSound": "测试通知", "settings.experimental.share.title": "分享模式", "settings.experimental.share.description": "会话分享行为", "settings.experimental.share.manual": "手动", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index d37e75f54a1..260c568fd97 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -1166,6 +1166,8 @@ export const dict = { "settings.notifications.errorSound.description": "發生錯誤時播放的聲音", "settings.notifications.sound.default": "預設", "settings.notifications.sound.none": "無", + "settings.notifications.sound.system": "系統預設", + "settings.notifications.testSound": "測試通知", "settings.experimental.share.title": "分享模式", "settings.experimental.share.description": "工作階段分享行為", "settings.experimental.share.manual": "手動", diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts index 9c70feeb759..fa1214eac9c 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts @@ -431,6 +431,11 @@ export interface RequestNotificationSettingsMessage { type: "requestNotificationSettings" } +export interface TestNotificationMessage { + type: "testNotification" + settingType: "agent" | "permissions" | "errors" +} + export interface ResetAllSettingsRequest { type: "resetAllSettings" } @@ -1127,6 +1132,7 @@ export type WebviewMessage = | UpdateConfigMessage | OpenSettingsTabRequest | RequestNotificationSettingsMessage + | TestNotificationMessage | ResetAllSettingsRequest | SettingsTabChangedMessage | SyncSessionRequest