openvoiceui 1.0.0

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 (185) hide show
  1. package/.env.example +104 -0
  2. package/Dockerfile +30 -0
  3. package/LICENSE +21 -0
  4. package/README.md +638 -0
  5. package/SETUP.md +360 -0
  6. package/app.py +232 -0
  7. package/auto-approve-devices.js +111 -0
  8. package/cli/index.js +372 -0
  9. package/config/__init__.py +4 -0
  10. package/config/default.yaml +43 -0
  11. package/config/flags.yaml +67 -0
  12. package/config/loader.py +203 -0
  13. package/config/providers.yaml +71 -0
  14. package/config/speech_normalization.yaml +182 -0
  15. package/config/theme.json +4 -0
  16. package/data/greetings.json +25 -0
  17. package/default-pages/ai-image-creator.html +915 -0
  18. package/default-pages/bulk-image-uploader.html +492 -0
  19. package/default-pages/desktop.html +2865 -0
  20. package/default-pages/file-explorer.html +854 -0
  21. package/default-pages/interactive-map.html +655 -0
  22. package/default-pages/style-guide.html +1005 -0
  23. package/default-pages/website-setup.html +1623 -0
  24. package/deploy/openclaw/Dockerfile +46 -0
  25. package/deploy/openvoiceui.service +30 -0
  26. package/deploy/setup-nginx.sh +50 -0
  27. package/deploy/setup-sudo.sh +306 -0
  28. package/deploy/skill-runner/Dockerfile +19 -0
  29. package/deploy/skill-runner/requirements.txt +14 -0
  30. package/deploy/skill-runner/server.py +269 -0
  31. package/deploy/supertonic/Dockerfile +22 -0
  32. package/deploy/supertonic/server.py +79 -0
  33. package/docker-compose.pinokio.yml +11 -0
  34. package/docker-compose.yml +59 -0
  35. package/greetings.json +25 -0
  36. package/index.html +65 -0
  37. package/inject-device-identity.js +142 -0
  38. package/package.json +82 -0
  39. package/profiles/default.json +114 -0
  40. package/profiles/manager.py +354 -0
  41. package/profiles/schema.json +337 -0
  42. package/prompts/voice-system-prompt.md +149 -0
  43. package/providers/__init__.py +39 -0
  44. package/providers/base.py +63 -0
  45. package/providers/llm/__init__.py +12 -0
  46. package/providers/llm/base.py +71 -0
  47. package/providers/llm/clawdbot_provider.py +112 -0
  48. package/providers/llm/zai_provider.py +115 -0
  49. package/providers/registry.py +320 -0
  50. package/providers/stt/__init__.py +12 -0
  51. package/providers/stt/base.py +58 -0
  52. package/providers/stt/webspeech_provider.py +49 -0
  53. package/providers/stt/whisper_provider.py +100 -0
  54. package/providers/tts/__init__.py +20 -0
  55. package/providers/tts/base.py +91 -0
  56. package/providers/tts/groq_provider.py +74 -0
  57. package/providers/tts/supertonic_provider.py +72 -0
  58. package/requirements.txt +38 -0
  59. package/routes/__init__.py +10 -0
  60. package/routes/admin.py +515 -0
  61. package/routes/canvas.py +1315 -0
  62. package/routes/chat.py +51 -0
  63. package/routes/conversation.py +2158 -0
  64. package/routes/elevenlabs_hybrid.py +306 -0
  65. package/routes/greetings.py +98 -0
  66. package/routes/icons.py +279 -0
  67. package/routes/image_gen.py +364 -0
  68. package/routes/instructions.py +190 -0
  69. package/routes/music.py +838 -0
  70. package/routes/onboarding.py +43 -0
  71. package/routes/pi.py +62 -0
  72. package/routes/profiles.py +215 -0
  73. package/routes/report_issue.py +68 -0
  74. package/routes/static_files.py +533 -0
  75. package/routes/suno.py +664 -0
  76. package/routes/theme.py +81 -0
  77. package/routes/transcripts.py +199 -0
  78. package/routes/vision.py +348 -0
  79. package/routes/workspace.py +288 -0
  80. package/server.py +1510 -0
  81. package/services/__init__.py +1 -0
  82. package/services/auth.py +143 -0
  83. package/services/canvas_versioning.py +239 -0
  84. package/services/db_pool.py +107 -0
  85. package/services/gateway.py +16 -0
  86. package/services/gateway_manager.py +333 -0
  87. package/services/gateways/__init__.py +12 -0
  88. package/services/gateways/base.py +110 -0
  89. package/services/gateways/compat.py +264 -0
  90. package/services/gateways/openclaw.py +1134 -0
  91. package/services/health.py +100 -0
  92. package/services/memory_client.py +455 -0
  93. package/services/paths.py +26 -0
  94. package/services/speech_normalizer.py +285 -0
  95. package/services/tts.py +270 -0
  96. package/setup-config.js +262 -0
  97. package/sounds/air_horn.mp3 +0 -0
  98. package/sounds/bruh.mp3 +0 -0
  99. package/sounds/crowd_cheer.mp3 +0 -0
  100. package/sounds/gunshot.mp3 +0 -0
  101. package/sounds/impact.mp3 +0 -0
  102. package/sounds/lets_go.mp3 +0 -0
  103. package/sounds/record_stop.mp3 +0 -0
  104. package/sounds/rewind.mp3 +0 -0
  105. package/sounds/sad_trombone.mp3 +0 -0
  106. package/sounds/scratch_long.mp3 +0 -0
  107. package/sounds/yeah.mp3 +0 -0
  108. package/src/adapters/ClawdBotAdapter.js +264 -0
  109. package/src/adapters/_template.js +133 -0
  110. package/src/adapters/elevenlabs-classic.js +841 -0
  111. package/src/adapters/elevenlabs-hybrid.js +812 -0
  112. package/src/adapters/hume-evi.js +676 -0
  113. package/src/admin.html +1339 -0
  114. package/src/app.js +8802 -0
  115. package/src/core/Config.js +173 -0
  116. package/src/core/EmotionEngine.js +307 -0
  117. package/src/core/EventBridge.js +180 -0
  118. package/src/core/EventBus.js +117 -0
  119. package/src/core/VoiceSession.js +607 -0
  120. package/src/face/BaseFace.js +259 -0
  121. package/src/face/EyeFace.js +208 -0
  122. package/src/face/HaloSmokeFace.js +509 -0
  123. package/src/face/manifest.json +27 -0
  124. package/src/face/previews/eyes.svg +16 -0
  125. package/src/face/previews/orb.svg +29 -0
  126. package/src/features/MusicPlayer.js +620 -0
  127. package/src/features/Soundboard.js +128 -0
  128. package/src/providers/DeepgramSTT.js +472 -0
  129. package/src/providers/DeepgramStreamingSTT.js +766 -0
  130. package/src/providers/GroqSTT.js +559 -0
  131. package/src/providers/TTSPlayer.js +323 -0
  132. package/src/providers/WebSpeechSTT.js +479 -0
  133. package/src/providers/tts/BaseTTSProvider.js +81 -0
  134. package/src/providers/tts/HumeProvider.js +77 -0
  135. package/src/providers/tts/SupertonicProvider.js +174 -0
  136. package/src/providers/tts/index.js +140 -0
  137. package/src/shell/adapter-registry.js +154 -0
  138. package/src/shell/caller-bridge.js +35 -0
  139. package/src/shell/camera-bridge.js +28 -0
  140. package/src/shell/canvas-bridge.js +32 -0
  141. package/src/shell/commercial-bridge.js +44 -0
  142. package/src/shell/face-bridge.js +44 -0
  143. package/src/shell/music-bridge.js +60 -0
  144. package/src/shell/orchestrator.js +233 -0
  145. package/src/shell/profile-discovery.js +303 -0
  146. package/src/shell/sounds-bridge.js +28 -0
  147. package/src/shell/transcript-bridge.js +61 -0
  148. package/src/shell/waveform-bridge.js +33 -0
  149. package/src/styles/base.css +2862 -0
  150. package/src/styles/face.css +417 -0
  151. package/src/styles/pi-overrides.css +89 -0
  152. package/src/styles/theme-dark.css +67 -0
  153. package/src/test-tts.html +175 -0
  154. package/src/ui/AppShell.js +544 -0
  155. package/src/ui/ProfileSwitcher.js +228 -0
  156. package/src/ui/SessionControl.js +240 -0
  157. package/src/ui/face/FacePicker.js +195 -0
  158. package/src/ui/face/FaceRenderer.js +309 -0
  159. package/src/ui/settings/PlaylistEditor.js +366 -0
  160. package/src/ui/settings/SettingsPanel.css +684 -0
  161. package/src/ui/settings/SettingsPanel.js +419 -0
  162. package/src/ui/settings/TTSVoicePreview.js +210 -0
  163. package/src/ui/themes/ThemeManager.js +213 -0
  164. package/src/ui/visualizers/BaseVisualizer.js +29 -0
  165. package/src/ui/visualizers/PartyFXVisualizer.css +291 -0
  166. package/src/ui/visualizers/PartyFXVisualizer.js +637 -0
  167. package/static/emulators/jsdos/js-dos.css +1 -0
  168. package/static/emulators/jsdos/js-dos.js +22 -0
  169. package/static/favicon.svg +55 -0
  170. package/static/icons/apple-touch-icon.png +0 -0
  171. package/static/icons/favicon-32.png +0 -0
  172. package/static/icons/icon-192.png +0 -0
  173. package/static/icons/icon-512.png +0 -0
  174. package/static/install.html +449 -0
  175. package/static/manifest.json +26 -0
  176. package/static/sw.js +21 -0
  177. package/tts_providers/__init__.py +136 -0
  178. package/tts_providers/base_provider.py +319 -0
  179. package/tts_providers/groq_provider.py +155 -0
  180. package/tts_providers/hume_provider.py +226 -0
  181. package/tts_providers/providers_config.json +119 -0
  182. package/tts_providers/qwen3_provider.py +371 -0
  183. package/tts_providers/resemble_provider.py +315 -0
  184. package/tts_providers/supertonic_provider.py +557 -0
  185. package/tts_providers/supertonic_tts.py +399 -0
@@ -0,0 +1,203 @@
1
+ """
2
+ Config loader: reads config/default.yaml with environment variable overrides.
3
+
4
+ Usage:
5
+ from config.loader import config
6
+
7
+ config.get('server.port') # -> 5001
8
+ config.get('gateway.url') # -> 'ws://127.0.0.1:18791'
9
+ config['tts.provider'] # dict-style access also works
10
+ config.flag('use_blueprints') # -> True/False (from flags.yaml)
11
+
12
+ Environment variable override rules:
13
+ - Direct named overrides (highest priority):
14
+ PORT -> server.port
15
+ CLAWDBOT_GATEWAY_URL -> gateway.url
16
+ CLAWDBOT_AUTH_TOKEN -> gateway.auth_token
17
+ GATEWAY_SESSION_KEY -> gateway.session_key
18
+ GEMINI_MODEL -> models.gemini
19
+ TTS_PROVIDER -> tts.provider
20
+ USE_GROQ_TTS -> tts.use_groq (true/false string)
21
+ MAX_HISTORY_MESSAGES -> conversation.max_history_messages
22
+ LOG_LEVEL -> logging.level
23
+ ENABLE_FTS -> features.enable_fts
24
+ ENABLE_BRIEFING -> features.enable_briefing
25
+ ENABLE_HISTORY_RELOAD-> features.enable_history_reload
26
+ - Generic double-underscore override:
27
+ SERVER__PORT=5002 -> server.port = 5002
28
+
29
+ Feature flag environment variable override:
30
+ - FEATURE_<FLAG_NAME_UPPER>=true/false
31
+ FEATURE_USE_BLUEPRINTS=true -> flags.use_blueprints = True
32
+ FEATURE_PERSISTENT_WEBSOCKET=true -> flags.use_persistent_websocket = True
33
+ """
34
+
35
+ import os
36
+ from pathlib import Path
37
+ from typing import Any, Optional
38
+
39
+ try:
40
+ import yaml
41
+ _YAML_AVAILABLE = True
42
+ except ImportError:
43
+ _YAML_AVAILABLE = False
44
+
45
+ # Path to the default config file (same directory as this module)
46
+ _DEFAULT_YAML = Path(__file__).parent / "default.yaml"
47
+
48
+ # Path to the feature flags file
49
+ _FLAGS_YAML = Path(__file__).parent / "flags.yaml"
50
+
51
+ # Named env var → dotted config key mappings
52
+ _ENV_MAP = {
53
+ "PORT": ("server.port", int),
54
+ "CLAWDBOT_GATEWAY_URL": ("gateway.url", str),
55
+ "CLAWDBOT_AUTH_TOKEN": ("gateway.auth_token", str),
56
+ "GATEWAY_SESSION_KEY": ("gateway.session_key", str),
57
+ "GEMINI_MODEL": ("models.gemini", str),
58
+ "TTS_PROVIDER": ("tts.provider", str),
59
+ "USE_GROQ_TTS": ("tts.use_groq", "bool"),
60
+ "MAX_HISTORY_MESSAGES": ("conversation.max_history_messages", int),
61
+ "LOG_LEVEL": ("logging.level", str),
62
+ "ENABLE_FTS": ("features.enable_fts", "bool"),
63
+ "ENABLE_BRIEFING": ("features.enable_briefing", "bool"),
64
+ "ENABLE_HISTORY_RELOAD": ("features.enable_history_reload", "bool"),
65
+ "OPENCLAW_WORKSPACE": ("paths.openclaw_workspace", str),
66
+ }
67
+
68
+
69
+ def _cast(value: str, cast_type) -> Any:
70
+ """Cast a string env var value to the target type."""
71
+ if cast_type == "bool":
72
+ return value.lower() in ("true", "1", "yes")
73
+ if cast_type == int:
74
+ try:
75
+ return int(value)
76
+ except ValueError:
77
+ raise ValueError(f"Cannot cast {value!r} to int")
78
+ if cast_type == float:
79
+ return float(value)
80
+ return value # str passthrough
81
+
82
+
83
+ def _deep_set(data: dict, dotted_key: str, value: Any) -> None:
84
+ """Set a value in a nested dict using a dotted key path."""
85
+ parts = dotted_key.split(".")
86
+ node = data
87
+ for part in parts[:-1]:
88
+ node = node.setdefault(part, {})
89
+ node[parts[-1]] = value
90
+
91
+
92
+ def _deep_get(data: dict, dotted_key: str, default: Any = None) -> Any:
93
+ """Get a value from a nested dict using a dotted key path."""
94
+ parts = dotted_key.split(".")
95
+ node = data
96
+ for part in parts:
97
+ if not isinstance(node, dict) or part not in node:
98
+ return default
99
+ node = node[part]
100
+ return node
101
+
102
+
103
+ def _load_yaml(path: Path) -> dict:
104
+ """Load a YAML file, returning empty dict if missing or yaml unavailable."""
105
+ if not _YAML_AVAILABLE:
106
+ print("⚠ Warning: PyYAML not installed. Run: pip install pyyaml")
107
+ return {}
108
+ if not path.exists():
109
+ print(f"⚠ Warning: Config file not found: {path}")
110
+ return {}
111
+ with open(path) as f:
112
+ return yaml.safe_load(f) or {}
113
+
114
+
115
+ def _apply_env_overrides(data: dict) -> None:
116
+ """Apply named env var overrides to the config dict (in-place)."""
117
+ # Named mappings (highest precedence)
118
+ for env_key, (config_key, cast_type) in _ENV_MAP.items():
119
+ value = os.environ.get(env_key)
120
+ if value is not None:
121
+ try:
122
+ _deep_set(data, config_key, _cast(value, cast_type))
123
+ except (ValueError, TypeError) as e:
124
+ import logging
125
+ logging.getLogger(__name__).warning(
126
+ f"Ignoring env var {env_key}={value!r}: {e}"
127
+ )
128
+
129
+ # Generic double-underscore overrides: SERVER__PORT=5002 → server.port
130
+ for env_key, value in os.environ.items():
131
+ if "__" in env_key:
132
+ parts = env_key.lower().split("__", 1)
133
+ if len(parts) == 2:
134
+ dotted = f"{parts[0]}.{parts[1]}"
135
+ # Only override if the key already exists in the loaded config
136
+ if _deep_get(data, dotted) is not None:
137
+ _deep_set(data, dotted, value)
138
+
139
+
140
+ def _load_flags(path: Path) -> dict:
141
+ """Load feature flags from flags.yaml with env var overrides.
142
+
143
+ Each flag can be overridden via FEATURE_<FLAG_NAME_UPPER>=true/false.
144
+ Example: FEATURE_USE_BLUEPRINTS=true overrides flags.use_blueprints.
145
+ """
146
+ raw = _load_yaml(path)
147
+ flags: dict = raw.get("flags", {})
148
+
149
+ # Apply env overrides: FEATURE_USE_BLUEPRINTS=true -> use_blueprints=True
150
+ for key in list(flags.keys()):
151
+ env_key = f"FEATURE_{key.upper()}"
152
+ env_val = os.environ.get(env_key)
153
+ if env_val is not None:
154
+ flags[key] = env_val.lower() in ("true", "1", "yes")
155
+
156
+ return flags
157
+
158
+
159
+ class Config:
160
+ """Immutable config accessor loaded from YAML + env overrides."""
161
+
162
+ def __init__(self, yaml_path: Path = _DEFAULT_YAML, flags_path: Path = _FLAGS_YAML):
163
+ self._data = _load_yaml(yaml_path)
164
+ _apply_env_overrides(self._data)
165
+ self._flags = _load_flags(flags_path)
166
+
167
+ def get(self, key: str, default: Any = None) -> Any:
168
+ """Get a config value by dotted key. Returns default if not found."""
169
+ return _deep_get(self._data, key, default)
170
+
171
+ def __getitem__(self, key: str) -> Any:
172
+ value = _deep_get(self._data, key)
173
+ if value is None:
174
+ raise KeyError(f"Config key not found: {key}")
175
+ return value
176
+
177
+ def __contains__(self, key: str) -> bool:
178
+ return _deep_get(self._data, key) is not None
179
+
180
+ def as_dict(self) -> dict:
181
+ """Return a copy of the full config dict."""
182
+ import copy
183
+ return copy.deepcopy(self._data)
184
+
185
+ def flag(self, name: str, default: bool = False) -> bool:
186
+ """Check if a feature flag is enabled.
187
+
188
+ Flags are loaded from config/flags.yaml with env var overrides.
189
+ Example: config.flag('use_blueprints') -> True/False
190
+ Env override: FEATURE_USE_BLUEPRINTS=true
191
+ """
192
+ return self._flags.get(name, default)
193
+
194
+ def reload(self) -> None:
195
+ """Reload config from YAML file and re-apply env overrides."""
196
+ self._data = _load_yaml(_DEFAULT_YAML)
197
+ _apply_env_overrides(self._data)
198
+ self._flags = _load_flags(_FLAGS_YAML)
199
+
200
+
201
+ # Module-level singleton — import this everywhere:
202
+ # from config.loader import config
203
+ config = Config()
@@ -0,0 +1,71 @@
1
+ # Provider configuration — P5-T2: Provider registry + auto-discovery
2
+ # ADR-001: YAML format with env interpolation
3
+ # ADR-003: Abstract base class + registry pattern
4
+ #
5
+ # ${ENV_VAR} placeholders are resolved at runtime from environment variables.
6
+ # 'modules' lists Python module paths imported during autodiscover() so that
7
+ # each module's register() calls fire automatically.
8
+
9
+ llm:
10
+ default_provider: zai
11
+ fallback_provider: clawdbot
12
+ modules:
13
+ - providers.llm.zai_provider
14
+ - providers.llm.clawdbot_provider
15
+
16
+ providers:
17
+ zai:
18
+ name: "Z.AI GLM"
19
+ api_key: ${ZAI_API_KEY}
20
+ default_model: glm-4-7-flash
21
+ priority: 10
22
+ models:
23
+ - id: glm-4-7-flash
24
+ name: "GLM-4.7 Flash"
25
+ - id: glm-4-plus
26
+ name: "GLM-4 Plus"
27
+
28
+ clawdbot:
29
+ name: "Clawdbot Gateway"
30
+ gateway_url: ${CLAWDBOT_GATEWAY_URL}
31
+ auth_token: ${CLAWDBOT_AUTH_TOKEN}
32
+ default_agent: main
33
+ priority: 20
34
+
35
+ tts:
36
+ default_provider: supertonic
37
+ fallback_provider: groq
38
+ modules:
39
+ - providers.tts.supertonic_provider
40
+ - providers.tts.groq_provider
41
+
42
+ providers:
43
+ supertonic:
44
+ name: "Supertonic ONNX"
45
+ onnx_dir: "" # Set SUPERTONIC_ONNX_DIR env var or configure path here
46
+ default_voice: M1
47
+ priority: 10
48
+
49
+ groq:
50
+ name: "Groq Orpheus"
51
+ api_key: ${GROQ_API_KEY}
52
+ model: orpheus-english
53
+ voice: alloy
54
+ priority: 20
55
+
56
+ stt:
57
+ default_provider: webspeech
58
+ modules:
59
+ - providers.stt.webspeech_provider
60
+ - providers.stt.whisper_provider
61
+
62
+ providers:
63
+ webspeech:
64
+ name: "Web Speech API"
65
+ priority: 10
66
+
67
+ whisper:
68
+ name: "Whisper Local"
69
+ model: base
70
+ device: cpu
71
+ priority: 20
@@ -0,0 +1,182 @@
1
+ # Speech Normalization Configuration
2
+ # Controls how text is cleaned and transformed before being sent to TTS providers.
3
+ #
4
+ # Rules are applied in order within each section.
5
+ # Per-profile overrides can extend or replace these global rules.
6
+ #
7
+ # ADR-001: YAML format with comments
8
+ # Referenced by: P5-T5 (Canvas Section 23 — Config-Driven Agent Packages)
9
+
10
+ # ── Global defaults ────────────────────────────────────────────────────────────
11
+ global:
12
+ # Strip markdown formatting that sounds awkward when spoken aloud
13
+ strip_markdown: true
14
+
15
+ # Remove URLs entirely (TTS reads them character-by-character otherwise)
16
+ strip_urls: true
17
+
18
+ # Remove emoji characters
19
+ strip_emoji: true
20
+
21
+ # Collapse multiple whitespace/newlines into a single space
22
+ collapse_whitespace: true
23
+
24
+ # Trim leading/trailing whitespace
25
+ trim: true
26
+
27
+ # Maximum text length sent to TTS (hard cap, prevents ONNX overflow)
28
+ max_length: 800
29
+
30
+ # ── Abbreviation expansion ─────────────────────────────────────────────────────
31
+ # Applied after markdown/URL stripping.
32
+ # Keys are exact strings (case-sensitive); values are their spoken form.
33
+ # Wrap with word boundaries in the normalizer so "API" doesn't match inside words.
34
+ abbreviations:
35
+ # Technical
36
+ "API": "A P I"
37
+ "APIs": "A P Is"
38
+ "CPU": "C P U"
39
+ "GPU": "G P U"
40
+ "RAM": "RAM"
41
+ "SSD": "S S D"
42
+ "VPS": "V P S"
43
+ "URL": "U R L"
44
+ "URLs": "U R Ls"
45
+ "HTML": "H T M L"
46
+ "CSS": "C S S"
47
+ "JS": "J S"
48
+ "JSON": "J SON"
49
+ "YAML": "YAML"
50
+ "CLI": "C L I"
51
+ "SSH": "S S H"
52
+ "TTS": "T T S"
53
+ "STT": "S T T"
54
+ "LLM": "L L M"
55
+ "LLMs": "L L Ms"
56
+ "AI": "A I"
57
+ "ML": "M L"
58
+ "UI": "U I"
59
+ "UX": "U X"
60
+ "DB": "database"
61
+ "SQL": "sequel"
62
+ "NoSQL": "no sequel"
63
+ "HTTP": "H T T P"
64
+ "HTTPS": "H T T P S"
65
+ "OS": "O S"
66
+ "FTS": "full-text search"
67
+ "ORM": "O R M"
68
+ "SDK": "S D K"
69
+ "CI": "C I"
70
+ "CD": "C D"
71
+ "PR": "pull request"
72
+
73
+ # Common text abbreviations
74
+ "etc.": "et cetera"
75
+ "e.g.": "for example"
76
+ "i.e.": "that is"
77
+ "vs.": "versus"
78
+ "vs": "versus"
79
+ "approx.": "approximately"
80
+ "mins": "minutes"
81
+ "hrs": "hours"
82
+ "sec": "second"
83
+ "secs": "seconds"
84
+
85
+ # ── Markdown patterns ─────────────────────────────────────────────────────────
86
+ # Regex patterns to strip or replace when strip_markdown is true.
87
+ # Each entry: {pattern: regex, replacement: string}
88
+ # Patterns are applied in order.
89
+ markdown_patterns:
90
+ # Code blocks (``` ... ```)
91
+ - pattern: "```[\\s\\S]*?```"
92
+ replacement: ""
93
+
94
+ # Inline code (`...`)
95
+ - pattern: "`[^`]+`"
96
+ replacement: ""
97
+
98
+ # Headers (## Header → Header)
99
+ - pattern: "^#{1,6}\\s+"
100
+ replacement: ""
101
+ flags: "multiline"
102
+
103
+ # Bold (**text** or __text__)
104
+ - pattern: "\\*\\*(.+?)\\*\\*"
105
+ replacement: "\\1"
106
+
107
+ - pattern: "__(.+?)__"
108
+ replacement: "\\1"
109
+
110
+ # Italic (*text* or _text_)
111
+ - pattern: "\\*(.+?)\\*"
112
+ replacement: "\\1"
113
+
114
+ - pattern: "_(.+?)_"
115
+ replacement: "\\1"
116
+
117
+ # Strikethrough (~~text~~)
118
+ - pattern: "~~(.+?)~~"
119
+ replacement: "\\1"
120
+
121
+ # Horizontal rules
122
+ - pattern: "^[-*_]{3,}$"
123
+ replacement: ""
124
+ flags: "multiline"
125
+
126
+ # Markdown links [text](url) → text
127
+ - pattern: "\\[([^\\]]+)\\]\\([^)]+\\)"
128
+ replacement: "\\1"
129
+
130
+ # Bare markdown images ![alt](url)
131
+ - pattern: "!\\[[^\\]]*\\]\\([^)]+\\)"
132
+ replacement: ""
133
+
134
+ # Blockquotes (> text)
135
+ - pattern: "^>\\s*"
136
+ replacement: ""
137
+ flags: "multiline"
138
+
139
+ # Bullet list markers (-, *, +)
140
+ - pattern: "^[\\-\\*\\+]\\s+"
141
+ replacement: ""
142
+ flags: "multiline"
143
+
144
+ # Numbered list markers (1. 2. etc.)
145
+ - pattern: "^\\d+\\.\\s+"
146
+ replacement: ""
147
+ flags: "multiline"
148
+
149
+ # ── URL pattern ───────────────────────────────────────────────────────────────
150
+ # Applied when strip_urls is true.
151
+ url_pattern: "https?://[^\\s]+"
152
+
153
+ # ── Emoji pattern ─────────────────────────────────────────────────────────────
154
+ # Unicode ranges for common emoji (applied when strip_emoji is true).
155
+ # Uses a simplified set; the normalizer will use a broader regex.
156
+ emoji_pattern: "[\\U0001F300-\\U0001FFFF\\u2600-\\u27BF]+"
157
+
158
+ # ── Profile overrides ─────────────────────────────────────────────────────────
159
+ # Per-profile settings that override the global defaults.
160
+ # Profile IDs must match profiles/*.json "id" fields.
161
+ profiles:
162
+ default:
163
+ strip_markdown: true
164
+ strip_urls: true
165
+ strip_emoji: true
166
+ max_length: 600
167
+ abbreviations: {}
168
+
169
+ developer:
170
+ strip_markdown: true
171
+ strip_urls: true
172
+ strip_emoji: true
173
+ max_length: 800
174
+ # Developer profile: spell out more technical terms
175
+ abbreviations:
176
+ "fn": "function"
177
+ "var": "variable"
178
+ "const": "constant"
179
+ "req": "request"
180
+ "res": "response"
181
+ "err": "error"
182
+ "cb": "callback"
@@ -0,0 +1,4 @@
1
+ {
2
+ "primary": "#0088ff",
3
+ "accent": "#00ffff"
4
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": 1,
3
+ "updated_at": "",
4
+ "updated_by": "system",
5
+ "notes": "Default greeting library. Edit or extend via /api/greetings/add. Categories: generic (all users) and contextual (face-recognized users).",
6
+ "next_greeting": null,
7
+ "last_context": null,
8
+ "greetings": {
9
+ "generic": {
10
+ "standard": [
11
+ "Hello! How can I help you today?",
12
+ "Hey there! What can I do for you?",
13
+ "Hi! What's on your mind?",
14
+ "Good to see you. What do you need?",
15
+ "Hello! Ready when you are.",
16
+ "Hey! What can I help you with?",
17
+ "Hi there! Ask me anything.",
18
+ "What can I do for you today?",
19
+ "Hey, what's up? How can I help?",
20
+ "Hello! What would you like to talk about?"
21
+ ]
22
+ },
23
+ "contextual": []
24
+ }
25
+ }