@bitseek/hermes-webui 0.1.0-beta.0 → 0.1.0-beta.1

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 (99) hide show
  1. package/package.json +2 -2
  2. package/vendor/agent-frontend-shell/.bitseek-source.json +2 -2
  3. package/vendor/agent-frontend-shell/CHANGELOG.md +178 -1
  4. package/vendor/agent-frontend-shell/CONTRIBUTORS.md +5 -5
  5. package/vendor/agent-frontend-shell/api/agent_health.py +134 -0
  6. package/vendor/agent-frontend-shell/api/config.py +145 -104
  7. package/vendor/agent-frontend-shell/api/gateway_chat.py +56 -12
  8. package/vendor/agent-frontend-shell/api/helpers.py +4 -2
  9. package/vendor/agent-frontend-shell/api/models.py +202 -20
  10. package/vendor/agent-frontend-shell/api/paths.py +77 -0
  11. package/vendor/agent-frontend-shell/api/plugins.py +185 -0
  12. package/vendor/agent-frontend-shell/api/profiles.py +95 -16
  13. package/vendor/agent-frontend-shell/api/routes.py +831 -30
  14. package/vendor/agent-frontend-shell/api/run_journal.py +1 -0
  15. package/vendor/agent-frontend-shell/api/state_sync.py +5 -4
  16. package/vendor/agent-frontend-shell/api/streaming.py +211 -56
  17. package/vendor/agent-frontend-shell/api/todo_state.py +122 -0
  18. package/vendor/agent-frontend-shell/api/updates.py +30 -3
  19. package/vendor/agent-frontend-shell/api/upload.py +251 -18
  20. package/vendor/agent-frontend-shell/api/workspace.py +323 -65
  21. package/vendor/agent-frontend-shell/bitseek_docs/BitSeek_Claw_Operation_Manual_EN.docx +0 -0
  22. package/vendor/agent-frontend-shell/bitseek_docs/BitSeek_Claw_Operation_Manual_ZH.docx +0 -0
  23. package/vendor/agent-frontend-shell/bitseek_docs/en/00-Installation.md +174 -0
  24. package/vendor/agent-frontend-shell/bitseek_docs/en/01-Overview.md +128 -0
  25. package/vendor/agent-frontend-shell/bitseek_docs/en/02-Page-Operations.md +461 -0
  26. package/vendor/agent-frontend-shell/bitseek_docs/en/README.md +61 -0
  27. package/vendor/agent-frontend-shell/bitseek_docs/en/images/ai-colleagues.png +0 -0
  28. package/vendor/agent-frontend-shell/bitseek_docs/en/images/chat-area.png +0 -0
  29. package/vendor/agent-frontend-shell/bitseek_docs/en/images/kanban.png +0 -0
  30. package/vendor/agent-frontend-shell/bitseek_docs/en/images/main-page.png +0 -0
  31. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-notes.png +0 -0
  32. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-overview.png +0 -0
  33. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-profile.png +0 -0
  34. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-soul.png +0 -0
  35. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory.png +0 -0
  36. package/vendor/agent-frontend-shell/bitseek_docs/en/images/navigation-bar.png +0 -0
  37. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-appearance.png +0 -0
  38. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-conversation.png +0 -0
  39. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-overview.png +0 -0
  40. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-plugins.png +0 -0
  41. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-preferences.png +0 -0
  42. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-providers.png +0 -0
  43. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-system.png +0 -0
  44. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings.png +0 -0
  45. package/vendor/agent-frontend-shell/bitseek_docs/en/images/sidebar.png +0 -0
  46. package/vendor/agent-frontend-shell/bitseek_docs/en/images/skills.png +0 -0
  47. package/vendor/agent-frontend-shell/bitseek_docs/en/images/tasks.png +0 -0
  48. package/vendor/agent-frontend-shell/bitseek_docs/en/images/workspace-panel.png +0 -0
  49. package/vendor/agent-frontend-shell/bitseek_docs/md_to_docx.py +351 -0
  50. package/vendor/agent-frontend-shell/bitseek_docs/zh/00-/345/256/211/350/243/205/345/220/257/345/212/250.md +174 -0
  51. package/vendor/agent-frontend-shell/bitseek_docs/zh/01-/346/225/264/344/275/223/346/246/202/350/247/210.md +128 -0
  52. package/vendor/agent-frontend-shell/bitseek_docs/zh/02-/351/241/265/351/235/242/346/223/215/344/275/234.md +463 -0
  53. package/vendor/agent-frontend-shell/bitseek_docs/zh/README.md +61 -0
  54. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/ai-colleagues.png +0 -0
  55. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/chat-area.png +0 -0
  56. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/kanban.png +0 -0
  57. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/main-page.png +0 -0
  58. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-notes.png +0 -0
  59. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-overview.png +0 -0
  60. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-profile.png +0 -0
  61. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-soul.png +0 -0
  62. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory.png +0 -0
  63. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/navigation-bar.png +0 -0
  64. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-appearance.png +0 -0
  65. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-conversation.png +0 -0
  66. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-overview.png +0 -0
  67. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-plugins.png +0 -0
  68. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-preferences.png +0 -0
  69. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-providers.png +0 -0
  70. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-system.png +0 -0
  71. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings.png +0 -0
  72. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/sidebar.png +0 -0
  73. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/skills.png +0 -0
  74. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/tasks.png +0 -0
  75. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/workspace-panel.png +0 -0
  76. package/vendor/agent-frontend-shell/build-release.sh +62 -0
  77. package/vendor/agent-frontend-shell/ctl.sh +1 -0
  78. package/vendor/agent-frontend-shell/docker-compose.local.yml +33 -0
  79. package/vendor/agent-frontend-shell/docker-compose.yml +8 -0
  80. package/vendor/agent-frontend-shell/docker_init.bash +1 -0
  81. package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +74 -15
  82. package/vendor/agent-frontend-shell/extensions/common/index.css +6 -0
  83. package/vendor/agent-frontend-shell/extensions/manifest.json +6 -0
  84. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +60 -14
  85. package/vendor/agent-frontend-shell/readme-simple.md +103 -0
  86. package/vendor/agent-frontend-shell/requirements.txt +5 -0
  87. package/vendor/agent-frontend-shell/server.py +7 -0
  88. package/vendor/agent-frontend-shell/static/boot.js +53 -1
  89. package/vendor/agent-frontend-shell/static/commands.js +20 -10
  90. package/vendor/agent-frontend-shell/static/i18n.js +1142 -1016
  91. package/vendor/agent-frontend-shell/static/index.html +13 -3
  92. package/vendor/agent-frontend-shell/static/messages.js +48 -3
  93. package/vendor/agent-frontend-shell/static/panels.js +199 -30
  94. package/vendor/agent-frontend-shell/static/sessions.js +249 -39
  95. package/vendor/agent-frontend-shell/static/style.css +46 -2
  96. package/vendor/agent-frontend-shell/static/ui.js +323 -79
  97. package/vendor/agent-frontend-shell/static/workspace.js +185 -7
  98. package/vendor/agent-frontend-shell/README-CUSTOM.md +0 -76
  99. package/vendor/agent-frontend-shell/docker-compose.custom.yml +0 -26
@@ -27,6 +27,8 @@ services:
27
27
  - ${HERMES_HOME:-${HOME}/.hermes}:/home/hermeswebui/.hermes
28
28
  # Your workspace directory shown on first launch (adapt if yours is different, the container will use the mounted /workspace)
29
29
  - ${HERMES_WORKSPACE:-${HOME}/workspace}:/workspace
30
+ # Mount extensions directory for custom themes/scripts
31
+ - ./extensions:/app/extensions
30
32
  environment:
31
33
  # Set to your host user ID: run `id -u` and `id -g` to find them.
32
34
  # On macOS, UIDs start at 501 (not 1000), so set UID and GID in a .env file:
@@ -44,6 +46,12 @@ services:
44
46
  # - HERMES_WEBUI_DEFAULT_WORKSPACE=/workspace
45
47
  # Optional: set a password for remote access
46
48
  # - HERMES_WEBUI_PASSWORD=your-secret-password
49
+ # Extension configuration
50
+ - HERMES_WEBUI_EXTENSION_DIR=/app/extensions
51
+ - HERMES_WEBUI_EXTENSION_STYLESHEET_URLS=${HERMES_WEBUI_EXTENSION_STYLESHEET_URLS}
52
+ - HERMES_WEBUI_EXTENSION_SCRIPT_URLS=${HERMES_WEBUI_EXTENSION_SCRIPT_URLS}
53
+ - HERMES_WEBUI_BOT_NAME=${HERMES_WEBUI_BOT_NAME}
54
+ - HERMES_WEBUI_SKIP_ONBOARDING=${HERMES_WEBUI_SKIP_ONBOARDING}
47
55
  #
48
56
  # Bind-mount permission handling (fixes #1389, #1399):
49
57
  # When you mount an EXISTING ~/.hermes directory (the common case),
@@ -196,6 +196,7 @@ chown_home_hermeswebui() {
196
196
  # ownership alignment.
197
197
  find /home/hermeswebui \
198
198
  -path "/home/hermeswebui/.hermes/hermes-agent" -prune \
199
+ -o -name ".git" -prune \
199
200
  -o -exec chown -h "${WANTED_UID}:${WANTED_GID}" {} +
200
201
  }
201
202
 
@@ -956,28 +956,34 @@ Non-goals for Slice 4e:
956
956
 
957
957
  #### Slice 4f: Supervised local runner client backend gate
958
958
 
959
- Status as of 2026-05-28: client transport proposed in #3073 behind
960
- `HERMES_WEBUI_RUNNER_BASE_URL`; it should be described as under review until a
961
- release PR actually ships it.
962
- `runner-local` still remains default-off and returns the bounded not-configured
963
- path unless that endpoint is explicitly configured. When configured, WebUI uses a
964
- JSON HTTP client boundary for start / observe / status / controls and bridges
965
- observed runner events through the existing SSE stream route rather than adding
966
- main-process runner-owned maps.
959
+ Status as of 2026-05-31: shipped in v0.51.188 via #3073 / #3274. The client
960
+ transport is now implemented behind `HERMES_WEBUI_RUNNER_BASE_URL` and remains
961
+ default-off. With no endpoint configured, `runner-local` still returns the
962
+ bounded not-configured path and the live in-process `_run_agent_streaming` path
963
+ is unchanged. When configured, WebUI uses a JSON HTTP client boundary for start /
964
+ observe / status / controls and bridges observed runner events through the
965
+ existing SSE stream route rather than adding main-process runner-owned maps.
966
+
967
+ The release added two security hardening checks while absorbing #3073:
968
+ `HttpRunnerClient` rejects non-`http(s)` base URL schemes and uses an opener that
969
+ does not follow redirects, so a misconfigured or compromised runner cannot leak a
970
+ Bearer token to a redirected host. The release gate reported full pytest passing
971
+ and independent default-off/inert-path review.
972
+
967
973
  This bridge is intentionally a WebUI consumer transport seam: the configured
968
974
  runner must emit events that are already compatible with the browser SSE event
969
975
  names/payloads, or a later runner-owned normalization layer must translate
970
976
  Hermes runtime families such as `token.delta`, `tool.started`, and `done` before
971
977
  they reach this route.
972
978
 
973
- After the route-selection harness ships, the next reviewable step is not to make
974
- `runner-local` the default. It is to define the first concrete supervised/local
975
- runner client backend that can replace the bounded 501 path under the existing
976
- feature flag and prove execution ownership has moved out of the main WebUI
977
- request process.
979
+ After the configured runner-client boundary ships, the next reviewable step is
980
+ not to make `runner-local` the default. It is to define the first supervised
981
+ runner process harness that can actually own `AIAgent` execution behind that
982
+ client boundary and prove restart/reattach with a real local runner, not just a
983
+ configured external endpoint or fake-runner fixture.
978
984
 
979
- This slice is a contract gate before backend code lands. The goal is to pin the
980
- minimum runner client behavior so the implementation cannot become a renamed
985
+ This slice was the client-boundary implementation gate. The goal was to pin the
986
+ minimum runner client behavior so the implementation could not become a renamed
981
987
  `STREAMS` / `CANCEL_FLAGS` / cached `AIAgent` surrogate inside `api/routes.py`.
982
988
 
983
989
  Scope:
@@ -1027,6 +1033,59 @@ Non-goals for Slice 4f:
1027
1033
  - no permanent WebUI-owned active-run discovery cache that duplicates runner or
1028
1034
  future Hermes Runtime API responsibility.
1029
1035
 
1036
+ #### Slice 4g: Supervised local runner process harness gate
1037
+
1038
+ After #3073 / #3274, WebUI has an explicit configured-runner HTTP client and SSE
1039
+ consumer bridge, but it still does not ship the supervised runner process itself.
1040
+ The next gate should define the smallest local runner harness that can own
1041
+ `AIAgent` execution outside the main WebUI request process while being consumed
1042
+ through the already-shipped `runner-local` client boundary.
1043
+
1044
+ Scope:
1045
+
1046
+ - define the local runner process lifecycle: spawn/start, health check, run
1047
+ ownership, graceful shutdown, crash classification, and cleanup;
1048
+ - keep WebUI as a client of `HERMES_WEBUI_RUNNER_BASE_URL`, not the owner of
1049
+ process-local runner execution state;
1050
+ - persist run/session lookup, ordered events, terminal state, and active controls
1051
+ in runner-owned or journal-backed state that a restarted WebUI can discover;
1052
+ - carry explicit profile, workspace, attachments, provider/model, toolset,
1053
+ source, and metadata payloads into the runner without WebUI process-global
1054
+ environment mutation;
1055
+ - prove cancel as the first live control for active runner-owned runs, with
1056
+ approval, clarify, goal, and queue either mapped to explicit runner
1057
+ capabilities or returned as bounded unsupported/conflict `ControlResult`
1058
+ values.
1059
+
1060
+ Acceptance tests for Slice 4g:
1061
+
1062
+ 1. **Process ownership moved.** A local runner process, not `hermes-webui`, owns
1063
+ `AIAgent` construction/reuse and active run execution for `runner-local` runs.
1064
+ 2. **Restart/reattach with a real runner.** Start a non-trivial `runner-local`
1065
+ run, restart only `hermes-webui`, reload the session, rediscover the active or
1066
+ terminal runner-owned run, replay/catch up from cursor without duplicate
1067
+ transcript/tool/reasoning state, and preserve cancel if still active.
1068
+ 3. **No runtime-surrogate globals in WebUI.** The main WebUI server still does not
1069
+ gain new module-level maps for runner-owned streams, cancel flags,
1070
+ approval/clarify callbacks, cached agents, child process run registries, goal
1071
+ state, or queue schedulers.
1072
+ 4. **Default-off and reversible.** Unset `HERMES_WEBUI_RUNNER_BASE_URL` or switch
1073
+ the adapter mode back to legacy and the existing in-process path remains
1074
+ available without session or journal migration.
1075
+ 5. **Runner health and failure are observable.** A missing, unhealthy, or crashed
1076
+ runner returns bounded diagnostics and terminal/interrupted state rather than
1077
+ silently falling back to WebUI-owned execution for a runner-selected run.
1078
+
1079
+ Non-goals for Slice 4g:
1080
+
1081
+ - no default-on runner mode;
1082
+ - no removal of `legacy-direct` or `legacy-journal`;
1083
+ - no server-side queue scheduler just for adapter symmetry;
1084
+ - no broad WebUI product-surface migration;
1085
+ - no claim that this is the canonical Hermes Agent Runtime API; if Hermes Agent
1086
+ later ships `/v1/runs`, this local runner remains a replaceable backend behind
1087
+ the same adapter/client boundary.
1088
+
1030
1089
  ## First Meaningful Success Criteria
1031
1090
 
1032
1091
  The first meaningful milestones are deliberately split.
@@ -0,0 +1,6 @@
1
+
2
+ .app-titlebar-inner {width:100%;}
3
+ .app-titlebar-icon {flex: 1;}
4
+ .app-titlebar-inner::after {content: ""; flex: 1;}
5
+
6
+ #btnCheckUpdatesNow {display: none !important; }
@@ -1,5 +1,11 @@
1
1
  {
2
2
  "extensions": [
3
+ {
4
+ "id": "common",
5
+ "order": 1,
6
+ "css": ["/extensions/common/index.css"],
7
+ "js": []
8
+ },
3
9
  {
4
10
  "id": "branding",
5
11
  "order": 1,
@@ -26,6 +26,11 @@
26
26
  ai_teammates_create_title: 'Create AI Teammate',
27
27
  ai_teammates_confirm_delete: 'Are you sure you want to delete this AI teammate?',
28
28
  ai_teammates_profile_prefix: 'bitseek_',
29
+ ai_teammates_cat_hr_ops: 'HR & Operations',
30
+ ai_teammates_cat_finance_tax: 'Finance & Tax',
31
+ ai_teammates_cat_biz_growth: 'Business Growth',
32
+ ai_teammates_cat_compliance: 'Policy & Compliance',
33
+ ai_teammates_cat_im: 'IM & Collaboration',
29
34
  });
30
35
 
31
36
  addTranslations('zh', {
@@ -43,6 +48,11 @@
43
48
  ai_teammates_create_title: '创建 AI 同事',
44
49
  ai_teammates_confirm_delete: '确定要删除这个 AI 同事吗?',
45
50
  ai_teammates_profile_prefix: 'bitseek_',
51
+ ai_teammates_cat_hr_ops: '人力资源与运营',
52
+ ai_teammates_cat_finance_tax: '财务与税务',
53
+ ai_teammates_cat_biz_growth: '业务增长',
54
+ ai_teammates_cat_compliance: '合规与政策',
55
+ ai_teammates_cat_im: '即时通讯',
46
56
  });
47
57
 
48
58
  addTranslations('ja', {
@@ -60,6 +70,11 @@
60
70
  ai_teammates_create_title: 'AI同僚を作成',
61
71
  ai_teammates_confirm_delete: 'このAI同僚を削除してもよろしいですか?',
62
72
  ai_teammates_profile_prefix: 'bitseek_',
73
+ ai_teammates_cat_hr_ops: '人事・オペレーション',
74
+ ai_teammates_cat_finance_tax: '財務・税務',
75
+ ai_teammates_cat_biz_growth: '事業成長',
76
+ ai_teammates_cat_compliance: 'コンプライアンス',
77
+ ai_teammates_cat_im: 'インスタントメッセージ',
63
78
  });
64
79
 
65
80
  addTranslations('ru', {
@@ -77,6 +92,11 @@
77
92
  ai_teammates_create_title: 'Создать AI коллегу',
78
93
  ai_teammates_confirm_delete: 'Вы уверены, что хотите удалить этого AI коллегу?',
79
94
  ai_teammates_profile_prefix: 'bitseek_',
95
+ ai_teammates_cat_hr_ops: 'HR и операции',
96
+ ai_teammates_cat_finance_tax: 'Финансы и налоги',
97
+ ai_teammates_cat_biz_growth: 'Рост бизнеса',
98
+ ai_teammates_cat_compliance: 'Политика и комплаенс',
99
+ ai_teammates_cat_im: 'Мгновенные сообщения',
80
100
  });
81
101
 
82
102
  addTranslations('es', {
@@ -94,6 +114,11 @@
94
114
  ai_teammates_create_title: 'Crear colega AI',
95
115
  ai_teammates_confirm_delete: '¿Estás seguro de que quieres eliminar este colega AI?',
96
116
  ai_teammates_profile_prefix: 'bitseek_',
117
+ ai_teammates_cat_hr_ops: 'RRHH y Operaciones',
118
+ ai_teammates_cat_finance_tax: 'Finanzas y Fiscalidad',
119
+ ai_teammates_cat_biz_growth: 'Crecimiento del Negocio',
120
+ ai_teammates_cat_compliance: 'Política y Cumplimiento',
121
+ ai_teammates_cat_im: 'Mensajería Instantánea',
97
122
  });
98
123
 
99
124
  addTranslations('de', {
@@ -111,6 +136,11 @@
111
136
  ai_teammates_create_title: 'AI-Kollegen erstellen',
112
137
  ai_teammates_confirm_delete: 'Sind Sie sicher, dass Sie diesen AI-Kollegen löschen möchten?',
113
138
  ai_teammates_profile_prefix: 'bitseek_',
139
+ ai_teammates_cat_hr_ops: 'Personal & Betrieb',
140
+ ai_teammates_cat_finance_tax: 'Finanzen & Steuern',
141
+ ai_teammates_cat_biz_growth: 'Unternehmenswachstum',
142
+ ai_teammates_cat_compliance: 'Richtlinien & Compliance',
143
+ ai_teammates_cat_im: 'Instant Messaging',
114
144
  });
115
145
 
116
146
  addTranslations('it', {
@@ -128,6 +158,11 @@
128
158
  ai_teammates_create_title: 'Crea collega AI',
129
159
  ai_teammates_confirm_delete: 'Sei sicuro di voler eliminare questo collega AI?',
130
160
  ai_teammates_profile_prefix: 'bitseek_',
161
+ ai_teammates_cat_hr_ops: 'Risorse Umane & Operazioni',
162
+ ai_teammates_cat_finance_tax: 'Finanza & Fiscalità',
163
+ ai_teammates_cat_biz_growth: 'Crescita Aziendale',
164
+ ai_teammates_cat_compliance: 'Politica & Conformità',
165
+ ai_teammates_cat_im: 'Messaggistica',
131
166
  });
132
167
  }
133
168
 
@@ -155,9 +190,14 @@
155
190
  }
156
191
 
157
192
  /**
158
- * Parse a bitseek profile name into { category, displayName, initials }
193
+ * Parse a bitseek profile name into { category, categoryDisplay, displayName, initials }
159
194
  * Pattern: bitseek_[category]_[displayName]
160
- * e.g. bitseek_collaboration_dingtalk → { category: '协作办公', displayName: '钉钉助手' }
195
+ * e.g. bitseek_collaboration_dingtalk → { category: 'ai_teammates_cat_im', displayName: 'Dingtalk' }
196
+ * Categories: hr/operations/support → HR & Operations
197
+ * collaboration → IM & Collaboration
198
+ * finance → Finance & Tax
199
+ * marketing/sales/product → Business Growth
200
+ * tech/data → Policy & Compliance
161
201
  */
162
202
  function parseBitseekName(name) {
163
203
  var prefix = 'bitseek_';
@@ -171,22 +211,25 @@
171
211
  var rawCategory = parts[0];
172
212
  var rawDisplayName = parts.slice(1).join('_');
173
213
 
174
- // Map English category keys to Chinese display names
214
+ // Map English category keys to i18n category keys
175
215
  var categoryMap = {
176
- 'collaboration': '协作办公',
177
- 'marketing': '营销增长',
178
- 'sales': '销售营收',
179
- 'support': '客户服务',
180
- 'hr': '人力资源',
181
- 'finance': '财务法务',
182
- 'product': '产品设计',
183
- 'tech': '技术研发',
184
- 'operations': '运营管理',
185
- 'data': '数据分析',
216
+ 'hr': 'ai_teammates_cat_hr_ops',
217
+ 'operations': 'ai_teammates_cat_hr_ops',
218
+ 'support': 'ai_teammates_cat_hr_ops',
219
+ 'im': 'ai_teammates_cat_im',
220
+ 'finance': 'ai_teammates_cat_finance_tax',
221
+ 'marketing': 'ai_teammates_cat_biz_growth',
222
+ 'sales': 'ai_teammates_cat_biz_growth',
223
+ 'product': 'ai_teammates_cat_biz_growth',
224
+ 'tech': 'ai_teammates_cat_compliance',
225
+ 'data': 'ai_teammates_cat_compliance',
186
226
  };
187
227
 
188
228
  var category = categoryMap[rawCategory] || rawCategory;
189
229
 
230
+ // Resolve i18n key to display name
231
+ var categoryDisplay = (category.indexOf('ai_teammates_cat_') === 0) ? t(category) : category;
232
+
190
233
  // Convert raw display name to readable (snake_case → title case as fallback)
191
234
  var displayName = rawDisplayName
192
235
  .replace(/_/g, ' ')
@@ -197,6 +240,7 @@
197
240
 
198
241
  return {
199
242
  category: category,
243
+ categoryDisplay: categoryDisplay,
200
244
  displayName: displayName,
201
245
  initials: initials,
202
246
  rawName: name,
@@ -265,6 +309,7 @@
265
309
  return {
266
310
  name: p.name,
267
311
  category: parsed.category,
312
+ categoryDisplay: parsed.categoryDisplay,
268
313
  displayName: parsed.displayName,
269
314
  initials: getInitials(parsed.displayName),
270
315
  description: p.description || '',
@@ -343,11 +388,12 @@
343
388
  } else {
344
389
  _categoryOrder.forEach(function(category) {
345
390
  var roles = _groupedRoles[category];
391
+ var categoryLabel = (category.indexOf('ai_teammates_cat_') === 0) ? t(category) : category;
346
392
 
347
393
  html += '<div class="at-category">';
348
394
  html += ' <div class="at-category-header">';
349
395
  html += ' <span class="at-category-dot"></span>';
350
- html += ' <span class="at-category-name">' + esc(category) + '</span>';
396
+ html += ' <span class="at-category-name">' + esc(categoryLabel) + '</span>';
351
397
  html += ' <span class="at-category-count">' + esc(t('ai_teammates_role_count', roles.length)) + '</span>';
352
398
  html += ' </div>';
353
399
  html += ' <div class="at-role-grid">';
@@ -0,0 +1,103 @@
1
+ # Hermes Web UI (Simplified) / 简化版说明
2
+
3
+ > **Repository / 仓库**: [https://github.com/adv-org/agent-frontend-shell](https://github.com/adv-org/agent-frontend-shell)
4
+
5
+ A lightweight web interface for Hermes Agent. No build step, no framework — just Python and vanilla JS.
6
+
7
+ Hermes Agent 的轻量级 Web 界面。无需构建步骤,无框架依赖 —— 仅使用 Python 和原生 JavaScript。
8
+
9
+ ---
10
+
11
+ ## Quick Start / 快速开始
12
+
13
+ ### 1. Clone / 克隆
14
+
15
+ ```bash
16
+ git clone https://github.com/adv-org/agent-frontend-shell.git
17
+ cd agent-frontend-shell
18
+ ```
19
+
20
+ ### 2. Local Run / 本地运行
21
+
22
+ **Requirements / 环境要求:** Python 3.12+
23
+
24
+ ```bash
25
+ # Option 1: Direct Python / 方式一:直接运行 Python
26
+ python3 bootstrap.py
27
+
28
+ # Option 2: Shell wrapper (same as above) / 方式二:包装脚本(效果相同)
29
+ ./start.sh
30
+ ```
31
+
32
+ Access at / 访问地址: **http://127.0.0.1:8787**
33
+
34
+ ---
35
+
36
+ ## Docker Run / Docker 运行
37
+
38
+ ### Single container / 单容器模式
39
+
40
+ ```bash
41
+ # Edit .env first / 先编辑 .env
42
+ cp .env.example .env
43
+ # Modify HERMES_HOME, HERMES_WORKSPACE, UID, GID as needed
44
+ # 按需修改 HERMES_HOME、HERMES_WORKSPACE、UID、GID
45
+
46
+ docker compose -f docker-compose.local.yml up -d --build
47
+ ```
48
+
49
+ ### Two-container (WebUI + Agent) / 双容器模式
50
+
51
+ ```bash
52
+ docker compose -f docker-compose.two-container.yml up -d
53
+ ```
54
+
55
+ ### Three-container (WebUI + Agent + DB) / 三容器模式
56
+
57
+ ```bash
58
+ docker compose -f docker-compose.three-container.yml up -d
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Configuration / 配置
64
+
65
+ Key environment variables / 关键环境变量:
66
+
67
+ | Variable | Description / 说明 | Default / 默认值 |
68
+ |---|---|---|
69
+ | `HERMES_WEBUI_PORT` | Server port / 服务端口 | `8787` |
70
+ | `HERMES_WEBUI_PASSWORD` | Access password / 访问密码 | (none) |
71
+ | `HERMES_HOME` | Hermes data dir / 数据目录 | `~/.hermes` |
72
+ | `HERMES_WORKSPACE` | Workspace path / 工作区路径 | `~/workspace` |
73
+ | `UID` / `GID` | User/Group ID for permissions / 权限映射 | `1000` |
74
+
75
+ Full config reference / 完整配置参考: [README.md](README.md) → Configuration section
76
+
77
+ ---
78
+
79
+ ## Common Commands / 常用命令
80
+
81
+ ```bash
82
+ # Check health / 健康检查
83
+ curl http://127.0.0.1:8787/health
84
+
85
+ # View logs / 查看日志
86
+ docker logs -f hermes-webui-custom
87
+
88
+ # Stop / 停止
89
+ docker compose -f docker-compose.local.yml down
90
+
91
+ # Rebuild / 重新构建
92
+ docker compose -f docker-compose.local.yml up -d --build
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Architecture / 架构
98
+
99
+ - **Backend / 后端**: Python (Flask-like), file-based state
100
+ - **Frontend / 前端**: Vanilla JS, no bundler
101
+ - **State / 状态**: Local filesystem (`~/.hermes`)
102
+
103
+ For full details / 完整详情: [ARCHITECTURE.md](ARCHITECTURE.md) | [TESTING.md](TESTING.md)
@@ -1,5 +1,10 @@
1
1
  # Hermes Web UI -- minimal Python dependencies
2
2
  # The server uses PyYAML plus cryptography for optional local passkey/WebAuthn support.
3
3
  # All heavy ML/agent deps live in the Hermes agent venv.
4
+ #
5
+ # OPTIONAL: the Edge TTS speech engine (Settings -> Voice -> TTS Engine -> "Edge TTS")
6
+ # needs `edge-tts`. It is intentionally NOT a hard dependency — the /api/tts
7
+ # endpoint returns 503 with an install hint when it's absent. Install it only if
8
+ # you want server-side Microsoft neural voices: pip install edge-tts
4
9
  pyyaml>=6.0
5
10
  cryptography>=42.0
@@ -581,6 +581,13 @@ def main() -> None:
581
581
  except Exception as e:
582
582
  print(f'[!!] WARNING: Gateway watcher failed to start: {e}', flush=True)
583
583
 
584
+ # Load WebUI dashboard plugins
585
+ try:
586
+ from api.plugins import load_plugins
587
+ load_plugins()
588
+ except Exception as e:
589
+ print(f'[!!] WARNING: Plugin loading failed: {e}', flush=True)
590
+
584
591
  httpd = QuietHTTPServer((HOST, PORT), Handler)
585
592
 
586
593
  # ── TLS/HTTPS setup (optional) ─────────────────────────────────────────
@@ -1,3 +1,6 @@
1
+ // Early boot initialization that must run before any other code.
2
+ // These run during script evaluation to handle server-stopped state
3
+ // and cross-tab shutdown broadcasts as early as possible.
1
4
  (function(){
2
5
  // Clear stale stop-server flag on successful page load (server is reachable)
3
6
  try{localStorage.removeItem('hermes-webui-server-stopped');}catch(_){}
@@ -889,7 +892,56 @@ window._micPendingSend=window._micPendingSend||false;
889
892
  .trim();
890
893
  }
891
894
  if(!clean){ _startListening(); return; }
892
-
895
+ const engine=localStorage.getItem("hermes-tts-engine")||"browser";
896
+ if(engine==="edge"){
897
+ const voice=localStorage.getItem("hermes-tts-voice")||"zh-CN-XiaoxiaoNeural";
898
+ const savedRate=parseFloat(localStorage.getItem("hermes-tts-rate"));
899
+ const savedPitch=parseFloat(localStorage.getItem("hermes-tts-pitch"));
900
+ let rate='', pitch='';
901
+ if(!isNaN(savedRate)){const pct=Math.round((savedRate-1)*100);const sign=pct>=0?'+':'';rate=sign+pct+'%';}
902
+ if(!isNaN(savedPitch)){const hz=Math.round((savedPitch-1)*50);const sign=hz>=0?'+':'';pitch=sign+hz+'Hz';}
903
+ _ttsSpeaking=true;
904
+ fetch(new URL('api/tts', document.baseURI || location.href).href, {
905
+ method: 'POST',
906
+ headers: {'Content-Type': 'application/json'},
907
+ body: JSON.stringify({text: clean, voice, rate, pitch})
908
+ })
909
+ .then(r => {
910
+ if(!r.ok) throw new Error('TTS request failed: ' + r.status);
911
+ return r.blob();
912
+ })
913
+ .then(blob => {
914
+ const url = URL.createObjectURL(blob);
915
+ const audio = new Audio(url);
916
+ // Register with the shared handle (declared in ui.js, same global scope;
917
+ // both scripts are fully evaluated before any voice interaction) so
918
+ // stopTTS() — called from _deactivate() — can actually pause hands-free
919
+ // Edge playback. Without this the audio is local here and unstoppable.
920
+ _playingEdgeAudio=audio;
921
+ audio.onended = () => {
922
+ _ttsSpeaking=false;
923
+ if(_playingEdgeAudio===audio) _playingEdgeAudio=null;
924
+ URL.revokeObjectURL(url);
925
+ if(_voiceModeActive) setTimeout(()=>_startListening(),500);
926
+ };
927
+ audio.onerror = () => {
928
+ _ttsSpeaking=false;
929
+ if(_playingEdgeAudio===audio) _playingEdgeAudio=null;
930
+ URL.revokeObjectURL(url);
931
+ if(_voiceModeActive) setTimeout(()=>_startListening(),1000);
932
+ };
933
+ audio.play().catch(e => {
934
+ _ttsSpeaking=false;
935
+ if(_playingEdgeAudio===audio) _playingEdgeAudio=null;
936
+ if(_voiceModeActive) setTimeout(()=>_startListening(),1000);
937
+ });
938
+ })
939
+ .catch(() => {
940
+ _ttsSpeaking=false;
941
+ if(_voiceModeActive) setTimeout(()=>_startListening(),1000);
942
+ });
943
+ return;
944
+ }
893
945
  const utter=new SpeechSynthesisUtterance(clean);
894
946
 
895
947
  // Apply saved voice preferences
@@ -322,6 +322,24 @@ function cmdClear(){
322
322
  showToast(t('conversation_cleared'));
323
323
  }
324
324
 
325
+ // Find the best matching model <option> for a slash-command query.
326
+ // Returns an exact id/label match if present, otherwise the shortest option
327
+ // whose value or label contains the query. Preferring the shortest match keeps
328
+ // a specific query like "mimo-v2.5" from being shadowed by a longer variant
329
+ // such as "mimo-v2.5-pro". See issue #3368.
330
+ function _bestModelMatch(options,query){
331
+ let best=null;
332
+ for(const opt of options){
333
+ const value=opt.value.toLowerCase();
334
+ const text=opt.textContent.toLowerCase();
335
+ if(value===query||text===query) return opt.value;
336
+ if(value.includes(query)||text.includes(query)){
337
+ if(best===null||opt.value.length<best.length) best=opt.value;
338
+ }
339
+ }
340
+ return best;
341
+ }
342
+
325
343
  async function cmdModel(args){
326
344
  if(!args){showToast(t('model_usage'));return;}
327
345
  const sel=$('modelSelect');
@@ -348,21 +366,13 @@ async function cmdModel(args){
348
366
  let match=(typeof _findModelInDropdown==='function')?_findModelInDropdown(q,sel,preferred):null;
349
367
  // Fallback: fuzzy match across all options
350
368
  if(!match){
351
- for(const opt of sel.options){
352
- if(opt.value.toLowerCase().includes(q)||opt.textContent.toLowerCase().includes(q)){
353
- match=opt.value;break;
354
- }
355
- }
369
+ match=_bestModelMatch(sel.options,q);
356
370
  }
357
371
  // Fallback: if q has provider/ prefix (e.g. "deepseek/deepseek-v4-flash"),
358
372
  // try the bare model name (which is how options appear for the active provider)
359
373
  if(!match && q.includes('/')){
360
374
  const bare=q.slice(q.lastIndexOf('/')+1);
361
- for(const opt of sel.options){
362
- if(opt.value.toLowerCase().includes(bare)||opt.textContent.toLowerCase().includes(bare)){
363
- match=opt.value;break;
364
- }
365
- }
375
+ match=_bestModelMatch(sel.options,bare);
366
376
  // Cross-provider fallback: if still no match, the model is from a
367
377
  // different provider not in the dropdown. Call /api/session/update directly.
368
378
  if(!match && S&&S.session&&S.session.session_id){