mindroot 9.2.0__py3-none-any.whl → 9.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +63 -0
  14. mindroot/coreplugins/admin/plugin_router.py +1 -1
  15. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  16. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  17. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  18. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  19. mindroot/coreplugins/admin/router.py +116 -15
  20. mindroot/coreplugins/admin/service_models.py +1 -1
  21. mindroot/coreplugins/admin/settings_router.py +1 -0
  22. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  23. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  24. mindroot/coreplugins/admin/static/css/default.css +4 -0
  25. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  26. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  27. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  28. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  29. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  30. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  31. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  32. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  33. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager-old.js +385 -0
  36. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  37. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  38. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  39. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  40. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  41. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  42. mindroot/coreplugins/admin/static/js/registry-shared-services.js +857 -0
  43. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  44. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  45. mindroot/coreplugins/admin/static/logo.png +0 -0
  46. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  47. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  48. mindroot/coreplugins/agent/agent.py +2 -2
  49. mindroot/coreplugins/agent/command_parser.py +25 -10
  50. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  51. mindroot/coreplugins/chat/__init__.py +4 -1
  52. mindroot/coreplugins/chat/router.py +132 -20
  53. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  54. mindroot/coreplugins/chat/services.py +31 -1
  55. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  56. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  57. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  58. mindroot/coreplugins/chat/static/css/default.css +24 -3
  59. mindroot/coreplugins/chat/static/css/main.css +1 -0
  60. mindroot/coreplugins/chat/static/js/action.js +137 -60
  61. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  62. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  63. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  64. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  65. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  66. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  67. mindroot/coreplugins/chat/widget_manager.py +139 -0
  68. mindroot/coreplugins/chat/widget_routes.py +287 -0
  69. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  70. mindroot/coreplugins/email/__init__.py +2 -0
  71. mindroot/coreplugins/email/email_provider.py +2 -2
  72. mindroot/coreplugins/email/mod.py +100 -0
  73. mindroot/coreplugins/email/services.py +5 -3
  74. mindroot/coreplugins/email/smtp_handler.py +9 -3
  75. mindroot/coreplugins/email/test_email_service.py +75 -0
  76. mindroot/coreplugins/env_manager/mod.py +61 -25
  77. mindroot/coreplugins/home/router.py +37 -2
  78. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  82. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  83. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  84. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  85. mindroot/coreplugins/index/handlers/plugin_ops.py +1 -1
  86. mindroot/coreplugins/index/indices/default/index.json +6 -6
  87. mindroot/coreplugins/jwt_auth/middleware.py +47 -1
  88. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  89. mindroot/coreplugins/l8n/__init__.py +6 -0
  90. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  91. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  92. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  93. mindroot/coreplugins/l8n/language_detection.py +183 -0
  94. mindroot/coreplugins/l8n/middleware.py +151 -0
  95. mindroot/coreplugins/l8n/mod.py +277 -0
  96. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  97. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  98. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  99. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  100. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  101. mindroot/coreplugins/l8n/utils.py +232 -0
  102. mindroot/coreplugins/mcp_/__init__.py +14 -0
  103. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  104. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  105. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  106. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  107. mindroot/coreplugins/mcp_/mod.py +367 -0
  108. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  109. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  110. mindroot/coreplugins/mcp_/setup.py +26 -0
  111. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  112. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  113. mindroot/coreplugins/persona/mod.py +12 -7
  114. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  115. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  116. mindroot/coreplugins/subscriptions/mod.py +14 -3
  117. mindroot/coreplugins/subscriptions/router.py +3 -0
  118. mindroot/coreplugins/user_service/__init__.py +1 -2
  119. mindroot/coreplugins/user_service/admin_init.py +1 -0
  120. mindroot/coreplugins/user_service/email_service.py +72 -17
  121. mindroot/coreplugins/user_service/mod.py +10 -2
  122. mindroot/coreplugins/user_service/password_reset_service.py +180 -27
  123. mindroot/coreplugins/user_service/router.py +84 -22
  124. mindroot/lib/auth/api_key.py +28 -0
  125. mindroot/lib/cli/plugins.py +94 -0
  126. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  127. mindroot/lib/plugins/installation.py +5 -5
  128. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  129. mindroot/lib/plugins/loader.py +33 -3
  130. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  131. mindroot/lib/plugins/manifest.py +238 -17
  132. mindroot/lib/providers/commands.py +3 -1
  133. mindroot/lib/route_decorators.py +5 -5
  134. mindroot/lib/templates.py +183 -11
  135. mindroot/lib/utils/merge_arrays.py +1 -1
  136. mindroot/migrate.py +49 -0
  137. mindroot/registry/data_access.py +1 -1
  138. mindroot/server.py +47 -13
  139. mindroot/server_missing_normal_args.py +197 -0
  140. mindroot/server_prev.py +173 -0
  141. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
  142. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +147 -114
  143. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  144. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  147. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  154. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  156. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  157. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  158. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  159. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  160. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  161. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  162. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  163. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  167. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  174. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  176. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  177. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  178. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  179. mindroot/coreplugins/index/default.json +0 -76
  180. mindroot/coreplugins/user_service/file_trigger_service.py +0 -72
  181. mindroot/coreplugins/user_service/hooks.py +0 -23
  182. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  183. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/WHEEL +0 -0
  184. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
  185. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
  186. {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,7 @@ import {markedHighlight} from 'https://cdn.jsdelivr.net/npm/marked-highlight@2.1
9
9
 
10
10
  function tryParse(markdown) {
11
11
  //return renderMarkdown(markdown)
12
- return markdownRenderer.parse(markdown);
12
+ return markdownRenderer.parse(markdown);
13
13
  }
14
14
 
15
15
 
@@ -44,40 +44,84 @@ class ActionComponent extends BaseEl {
44
44
  static properties = {
45
45
  funcName: { type: String },
46
46
  params: { type: String },
47
- result: { type: String }
47
+ result: { type: String },
48
+ isExpanded: { type: Boolean },
49
+ isRunning: { type: Boolean }
48
50
  }
49
51
 
50
52
  static styles = [
51
53
  css`
52
54
  :host {
53
55
  display: block;
56
+ width: 100%;
54
57
  }
55
58
 
56
- details {
57
- border: none;
58
- display: contents;
59
+ .action-container {
60
+ width: 100%;
61
+ border: 1px solid #333;
62
+ border-radius: 8px;
63
+ margin: 4px 0;
64
+ background-color: transparent;
65
+ }
66
+
67
+ .action-container.running {
68
+ border-color: #4a5eff;
69
+ animation: pulse-border 2s infinite;
70
+ }
71
+
72
+ @keyframes pulse-border {
73
+ 0%, 100% {
74
+ border-color: #4a5eff;
75
+ box-shadow: 0 0 5px rgba(74, 94, 255, 0.3);
76
+ }
77
+ 50% {
78
+ border-color: #6a7eff;
79
+ box-shadow: 0 0 10px rgba(74, 94, 255, 0.5);
80
+ }
81
+ }
82
+
83
+ .action-summary {
84
+ padding: 8px 12px;
85
+ cursor: pointer;
86
+ background: rgba(200, 200, 255, 0.1);
87
+ border-radius: 8px;
88
+ width: 100%;
89
+ box-sizing: border-box;
90
+ display: block;
91
+ color: #f0f0f0;
92
+ user-select: none;
93
+ }
94
+
95
+ .action-summary:hover {
96
+ background: #444;
97
+ }
98
+
99
+ .action-content {
100
+ padding: 8px 12px;
101
+ border-top: 1px solid #333;
102
+ color: #f0f0f0;
103
+ display: none;
104
+ }
105
+
106
+ .action-content.expanded {
107
+ display: block;
108
+ }
109
+
110
+ .param-preview {
111
+ color: #ddd;
112
+ font-style: italic;
113
+ margin-left: 8px;
114
+ }
115
+
116
+ .fn_name {
117
+ color: #f0f0f0;
118
+ font-weight: normal;
59
119
  }
60
120
 
61
121
  @keyframes flash {
62
122
  0% { opacity: 0; }
63
123
  50% { opacity: 0.5; }
64
124
  100% { opacity: 0; }
65
- }
66
- /*
67
- .animated-element::before {
68
- content: '';
69
- display: inline-block;
70
- top: 0;
71
- left: 0;
72
- right: 0;
73
- bottom: 0;
74
- background: rgba(200,200,200,0.5);
75
- animation: flash 0.2s;
76
- pointer-events: none;
77
- } */
78
-
79
- .animated-element {
80
- /* position: relative; */
81
125
  }
82
126
  `
83
127
  ];
@@ -85,25 +129,34 @@ details {
85
129
  constructor() {
86
130
  super();
87
131
  this.funcName = '';
88
- //this.params = {};
89
132
  this.result = '';
133
+ this.isExpanded = true;
134
+ this.isRunning = false;
135
+ }
136
+
137
+ connectedCallback() {
138
+ super.connectedCallback();
139
+ this.checkRunningState();
140
+ }
141
+
142
+ checkRunningState() {
143
+ const chatMessages = document.querySelectorAll('chat-message');
144
+ const lastMessage = chatMessages[chatMessages.length - 1];
145
+ if (lastMessage && lastMessage.getAttribute('spinning') === 'yes') {
146
+ this.isRunning = true;
147
+ setTimeout(() => {
148
+ this.isRunning = false;
149
+ this.requestUpdate();
150
+ }, 1000);
151
+ }
152
+ }
153
+
154
+ _toggleExpanded() {
155
+ this.isExpanded = !this.isExpanded;
90
156
  }
91
157
 
92
158
  _paramsHTML(params) {
93
159
  let paramshtml = '';
94
- /* const format_ = (x) => {
95
- // if it is a string then split, otherwise return the object
96
- if (typeof(x) == 'string') {
97
- return x.split('\n')[0].slice(0, 160)
98
- } else {
99
- if (x+"" == "[[object Object]]") {
100
- return JSON.stringify(x)
101
- } else {
102
- return x
103
- }
104
- }
105
- }
106
- */
107
160
  if (Array.isArray(params)) {
108
161
  for (let item of params) {
109
162
  paramshtml += `<span class="param_value">(${format_(item)}), </span> `;
@@ -120,6 +173,31 @@ details {
120
173
  return paramshtml;
121
174
  }
122
175
 
176
+ _getFirstParamPreview(params) {
177
+ if (!params) return '';
178
+
179
+ let firstValue = '';
180
+ if (Array.isArray(params) && params.length > 0) {
181
+ firstValue = params[0];
182
+ } else if (typeof(params) == 'object') {
183
+ const keys = Object.keys(params);
184
+ if (keys.length > 0) {
185
+ firstValue = params[keys[0]];
186
+ }
187
+ } else {
188
+ firstValue = params;
189
+ }
190
+
191
+ if (typeof(firstValue) === 'string') {
192
+ return firstValue.length > 100 ? firstValue.substring(0, 100) + '...' : firstValue;
193
+ } else if (typeof(firstValue) === 'object') {
194
+ const str = JSON.stringify(firstValue);
195
+ return str.length > 100 ? str.substring(0, 100) + '...' : str;
196
+ } else {
197
+ const str = String(firstValue);
198
+ return str.length > 100 ? str.substring(0, 100) + '...' : str;
199
+ }
200
+ }
123
201
 
124
202
  _render() {
125
203
  let {funcName, params, result} = this;
@@ -136,18 +214,8 @@ details {
136
214
  if (params.val) {
137
215
  params = params.val
138
216
  }
139
- let dontTruncate = false;
140
- //let format_;
141
- /* if (funcName != 'write') {
142
- format_ = (str) => {
143
- return str
144
- }
145
- } else {
146
- format_ = (str) => {
147
- return str.split('\n')[0].slice(0, 160)
148
- }
149
- }
150
- */
217
+
218
+ const paramPreview = this._getFirstParamPreview(params);
151
219
  paramshtml = this._paramsHTML(params)
152
220
 
153
221
  console.log('paramshtml', paramshtml)
@@ -164,7 +232,7 @@ details {
164
232
  </details>`;
165
233
  }
166
234
  }
167
- if (funcName === 'write' || funcName == 'think') {
235
+ if (funcName === 'overwrite' || funcName === 'write' || funcName == 'think') {
168
236
  let {fname, text} = params;
169
237
  if (params.extensive_chain_of_thoughts) {
170
238
  text = params.extensive_chain_of_thoughts
@@ -173,7 +241,9 @@ details {
173
241
  console.log("Displaying file")
174
242
  if (fname?.endsWith('.md') || funcName == 'think') {
175
243
  console.log("Displaying markdown")
176
- res = html`<div class="markdown-content">${unsafeHTML(tryParse(text, {breaks: true}))}</div>`;
244
+ if (true || Date.now() - window.lastParsed > 300) {
245
+ res = html`<div class="markdown-content">${unsafeHTML(tryParse(text, {breaks: true}))}</div>`;
246
+ }
177
247
  } else {
178
248
  console.log("Displaying code")
179
249
  const hih = hljs.highlightAuto(text).value;
@@ -183,7 +253,9 @@ details {
183
253
  } else {
184
254
  if (typeof(params) === "string") {
185
255
  try {
186
- res = html`<div class="markdown-content">${unsafeHTML(tryParse(params))}</div>`;
256
+ if (true || Date.now() - window.lastParsed > 300) {
257
+ res = html`<div class="markdown-content">${unsafeHTML(tryParse(params))}</div>`;
258
+ }
187
259
  } catch (e) {
188
260
  res = html`<pre><code>${params}</code></pre>`;
189
261
  }
@@ -193,7 +265,9 @@ details {
193
265
  if (typeof(params[key]) === 'string' && params[key].split('\n').length > 2) {
194
266
  console.log('rendering markdown', params[key])
195
267
  try {
196
- res = html`<div class="markdown-content">${unsafeHTML(tryParse(params[key]+" "))}</div>`;
268
+ if (true || Date.now() - window.lastParsed > 300) {
269
+ res = html`<div class="markdown-content">${unsafeHTML(tryParse(params[key]+" "))}</div>`;
270
+ }
197
271
  } catch (e) {
198
272
  res = html`<pre><code>${params[key]}</code></pre>`;
199
273
  }
@@ -209,15 +283,18 @@ details {
209
283
  }
210
284
 
211
285
  return html`
212
- <details class="animated-element-x" style="position: relative; max-width: 800px;" open >
213
- <!-- we need to make sure it's open by default so we will set property to true -->
214
- <summary class="fn_name">⚡ ${funcName}</summary>
215
- <div class="av-x"></div>
216
- <div class="action">
217
- <span class="fn_name"> ${unsafeHTML(paramshtml)}</span>
218
- ${res}
219
- </div>
220
- </details>
286
+ <div class="action-container ${this.isRunning ? 'running' : ''}">
287
+ <div class="action-summary" @click="${this._toggleExpanded}">
288
+ <span class="fn_name">${funcName}</span>
289
+ ${paramPreview ? html`<span class="param-preview">${paramPreview}</span>` : ''}
290
+ </div>
291
+ <div class="action-content ${this.isExpanded ? 'expanded' : ''}">
292
+ <div class="action">
293
+ <span class="fn_name"> ${unsafeHTML(paramshtml)}</span>
294
+ ${res}
295
+ </div>
296
+ </div>
297
+ </div>
221
298
  `;
222
299
  }
223
300
  }
@@ -89,6 +89,9 @@ export class ChatHistory {
89
89
  console.log('user mesage: found command')
90
90
  let parsed = cmd.result
91
91
  try {
92
+ if (typeof parsed != 'string') {
93
+ parsed = JSON.stringify(parsed);
94
+ }
92
95
  parsed = markdownRenderer.parse(parsed);
93
96
  console.log("rendered command result markdown")
94
97
  } catch (e) {
@@ -12,6 +12,11 @@ import { SSE } from './sse.js';
12
12
  import { registerDelegate } from './delegate_task.js'
13
13
  import showNotification from './notification.js';
14
14
 
15
+ if (!window.lastParsed) window.lastParsed = Date.now();
16
+ if (!window.lastScrolled) window.lastScrolled = Date.now();
17
+
18
+ window.lastScrolled = Date.now();
19
+
15
20
  const commandHandlers = {};
16
21
 
17
22
  // Function to register command handlers
@@ -26,6 +31,8 @@ function tryParse(markdown) {
26
31
  return markdownRenderer.parse(markdown);
27
32
  }
28
33
 
34
+ const noAction = [ 'say', 'json_encoded_md', 'wait_for_user_reply', 'markdown_await_user', 'tell_and_continue', 'think' ]
35
+
29
36
  class Chat extends BaseEl {
30
37
  static properties = {
31
38
  sessionid: { type: String },
@@ -56,6 +63,7 @@ class Chat extends BaseEl {
56
63
  this.history = new ChatHistory(this);
57
64
  console.log(this);
58
65
  }
66
+
59
67
 
60
68
  exposeSubcomponents() {
61
69
  // Get all chat-message elements
@@ -136,6 +144,8 @@ class Chat extends BaseEl {
136
144
  showNotification('error', data.error);
137
145
  }
138
146
 
147
+
148
+
139
149
  _addMessage(event) {
140
150
  const { content, sender, persona } = event.detail;
141
151
 
@@ -187,13 +197,24 @@ class Chat extends BaseEl {
187
197
  }, 100)
188
198
  }
189
199
 
190
- _finished(event) {
200
+ async _finished(event) {
191
201
  console.log('Chat finished');
192
202
  this.task_id = null
193
203
  this.userScrolling = false;
194
204
  this._scrollToBottom()
195
205
  }
196
206
 
207
+ textParam (data) {
208
+ if (data.params.text) {
209
+ return data.params.text;
210
+ } else if (data.params.markdown) {
211
+ return data.params.markdown;
212
+ } else if (data.params.extensive_chain_of_thoughts) {
213
+ return data.params.extensive_chain_of_thoughts;
214
+ }
215
+ return JSON.stringify(data.params);
216
+ }
217
+
197
218
  async _partialCmd(event) {
198
219
  console.log('Event received');
199
220
  console.log(event);
@@ -211,28 +232,33 @@ class Chat extends BaseEl {
211
232
  this.startNewMsg = false
212
233
  }
213
234
 
214
- if (data.command == 'say' || data.command == 'json_encoded_md' ||
215
- data.command == 'wait_for_user_reply' || data.command == 'markdown_await_user' ||
216
- data.command == 'tell_and_continue' || data.command=='think') {
235
+ if (noAction.includes(data.command)) {
217
236
  // Check if there's a registered handler for this command
218
237
  if (handler) {
219
238
  console.log('Used registered handler for', data.command);
220
239
  this.msgSoFar = null
221
- } else if (data.params.text) {
222
- this.msgSoFar = data.params.text
223
- } else if (data.params.markdown) {
224
- this.msgSoFar = data.params.markdown
225
- } else if (data.params.extensive_chain_of_thoughts) {
226
- this.msgSoFar = data.params.extensive_chain_of_thoughts
227
240
  } else {
228
- this.msgSoFar = data.params
241
+ this.msgSoFar = this.textParam(data);
229
242
  }
230
243
 
231
244
  try {
245
+ if (!window.lastParsed) window.lastParsed = Date.now();
232
246
  if (content) {
233
247
  this.messages[this.messages.length - 1].content = content
234
248
  } else if (this.msgSoFar) {
235
- this.messages[this.messages.length - 1].content = tryParse(this.msgSoFar);
249
+ let elapsed_ = Date.now() - window.lastParsed;
250
+ if (elapsed_ > 40 || this.msgSoFar + '' == '[object Object]' ) {
251
+ const parsed_ = tryParse(this.msgSoFar);
252
+ if (false && parsed_+'' == '[object Object]') {
253
+ console.log('msgSoFar is an object, not parsing:', this.msgSoFar);
254
+ } else {
255
+ this.messages[this.messages.length - 1].content = parsed_;
256
+ console.log(' parsed ', elapsed_);
257
+ window.lastParsed = Date.now();
258
+ }
259
+ } else {
260
+ console.log('********************************* only ',elapsed_);
261
+ }
236
262
  }
237
263
  } catch (e) {
238
264
  console.error("Could not parse markdown:", e)
@@ -263,12 +289,17 @@ class Chat extends BaseEl {
263
289
  const paramStr = JSON.stringify(data.params)
264
290
  const escaped = escapeJsonForHtml(paramStr)
265
291
  if (content) {
292
+ console.log('found content, not using action component')
266
293
  this.messages[this.messages.length - 1].content = content
267
294
  } else {
268
- this.messages[this.messages.length - 1].content = `
269
- <action-component funcName="${data.command}" params="${escaped}"
270
- result="">
271
- </action-component>`;
295
+ if (this.messages[this.messages.length - 1].content == '' ||
296
+ Date.now()- window.lastParsed > 40) {
297
+ window.lastParsed = Date.now();
298
+ this.messages[this.messages.length - 1].content = `
299
+ <action-component funcName="${data.command}" params="${escaped}"
300
+ result="">
301
+ </action-component>`;
302
+ }
272
303
  }
273
304
  }
274
305
  this.requestUpdate();
@@ -308,6 +339,11 @@ class Chat extends BaseEl {
308
339
  }
309
340
  } else {
310
341
  console.warn('No handler for command:', data.command)
342
+ if (!noAction.includes(data.command)) {
343
+ this.messages[this.messages.length - 1].content = `<action-component funcName="${data.command}" params="${escapeJsonForHtml(JSON.stringify(data.args))}" result=""></action-component>`;
344
+ } else {
345
+ this.messages[this.messages.length - 1].content = tryParse(this.textParam(data));
346
+ }
311
347
  }
312
348
  window.initializeCodeCopyButtons();
313
349
  this.requestUpdate();
@@ -345,6 +381,7 @@ class Chat extends BaseEl {
345
381
  this.messages = [...this.messages, { content: html, sender: 'ai', spinning: 'no' }]
346
382
  }
347
383
 
384
+
348
385
  _scrollToBottom(forceInstant = false) {
349
386
  const chatLog = this.shadowRoot.querySelector('.chat-log');
350
387
  if (!chatLog) return;
@@ -359,6 +396,12 @@ class Chat extends BaseEl {
359
396
  if (window.access_token && window.access_token.length > 20) {
360
397
  console.log('this is an embed probably, not scrolling messages')
361
398
  } else {
399
+ const elapsed = Date.now() - window.lastScrolled;
400
+ if (elapsed < 300) {
401
+ console.log('Not scrolling to bottom, too soon after last scroll');
402
+ return;
403
+ }
404
+ window.lastScrolled = Date.now();
362
405
  lastEl.scrollIntoView({ behavior: forceInstant ? 'instant' : 'instant', block: 'end' });
363
406
  }
364
407
 
@@ -0,0 +1,221 @@
1
+ diff --git a/src/mindroot/coreplugins/chat/static/js/chat.js b/src/mindroot/coreplugins/chat/static/js/chat.js
2
+ index 0f2ce57..53e28b9 100644
3
+ --- a/src/mindroot/coreplugins/chat/static/js/chat.js
4
+ +++ b/src/mindroot/coreplugins/chat/static/js/chat.js
5
+ @@ -34,7 +34,8 @@ function debounce(func, delay) {
6
+ };
7
+ }
8
+
9
+ -// Create a throttled parser that limits parsing frequency but doesn't skip updates
10
+ +// Single place to perform a safe markdown parse and update the message
11
+ +function updateParsedMessage(markdown, messageIndex, chatInstance) {
12
+ const throttledParse = throttle((markdown, messageIndex, chatInstance) => {
13
+ try {
14
+ const result = markdownRenderer.parse(markdown);
15
+ @@ -46,32 +47,44 @@ const throttledParse = throttle((markdown, messageIndex, chatInstance) => {
16
+ }
17
+ } catch (e) {
18
+ console.error("Could not parse markdown:", e);
19
+ + console.error(\"Could not parse markdown:\", e);
20
+ if (chatInstance.messages[messageIndex]) {
21
+ chatInstance.messages[messageIndex].content = `<pre><code>${markdown}</code></pre>`;
22
+ chatInstance.requestUpdate();
23
+ }
24
+ }
25
+ -}, 100); // Throttle to max 10 updates per second
26
+ -
27
+ -function tryParse(markdown, useThrottle = false, messageIndex = null, chatInstance = null) {
28
+ - if (!useThrottle || !chatInstance || messageIndex === null) {
29
+ - // Immediate parsing for cases where we don't need throttling
30
+ - try {
31
+ - return markdownRenderer.parse(markdown);
32
+ - } catch (e) {
33
+ - console.error("Could not parse markdown:", e);
34
+ - return `<pre><code>${markdown}</code></pre>`;
35
+ +}, 350); // Throttle to max 10 updates per second
36
+ +
37
+ +// Debounced parser to ensure we always flush the last update after a quiet period
38
+ +const debouncedParse = debounce((markdown, messageIndex, chatInstance) => {
39
+ + try {
40
+ + const result = markdownRenderer.parse(markdown);
41
+ + if (chatInstance.messages[messageIndex]) {
42
+ + chatInstance.messages[messageIndex].content = result;
43
+ + chatInstance.requestUpdate();
44
+ + chatInstance._scrollToBottom();
45
+ + if (typeof window.initializeCodeCopyButtons === 'function') {
46
+ + try { window.initializeCodeCopyButtons(); } catch (_) {}
47
+ + }
48
+ }
49
+ - } else {
50
+ - // Throttled parsing - will update the message in place
51
+ - throttledParse(markdown, messageIndex, chatInstance);
52
+ - // Return current content immediately (might be partially parsed)
53
+ - try {
54
+ - return markdownRenderer.parse(markdown);
55
+ - } catch (e) {
56
+ - return `<pre><code>${markdown}</code></pre>`;
57
+ + } catch (e) {
58
+ + console.error("Could not parse markdown:", e);
59
+ + if (chatInstance.messages[messageIndex]) {
60
+ + chatInstance.messages[messageIndex].content = `<pre><code>${markdown}</code></pre>`;
61
+ + chatInstance.requestUpdate();
62
+ }
63
+ }
64
+ +}, 150);
65
+ +}
66
+ +
67
+ +function tryParse(markdown) {
68
+ + // Immediate parse for non-streaming contexts
69
+ + try {
70
+ + return markdownRenderer.parse(markdown);
71
+ + } catch (e) {
72
+ + console.error("Could not parse markdown:", e);
73
+ + return `<pre><code>${markdown}</code></pre>`;
74
+ + }
75
+ }
76
+
77
+ class Chat extends BaseEl {
78
+ @@ -103,6 +116,12 @@ class Chat extends BaseEl {
79
+ console.log('Chat component created');
80
+ this.history = new ChatHistory(this);
81
+ console.log(this);
82
+ +
83
+ + // Per-message streaming schedulers (index -> state)
84
+ + this._streamSchedulers = new Map();
85
+ + this._lastCopyInit = 0;
86
+ + // Throttle scroll to reduce layout thrash during streaming
87
+ + this._scrollToBottomThrottled = throttle(() => this._scrollToBottom(), 150);
88
+ }
89
+
90
+ exposeSubcomponents() {
91
+ @@ -178,6 +197,82 @@ class Chat extends BaseEl {
92
+ }, 100);
93
+ }
94
+
95
+ + // Hybrid scheduler: leading throttle + trailing debounce + guaranteed final flush
96
+ + _ensureScheduler(messageIndex) {
97
+ + if (!this._streamSchedulers.has(messageIndex)) {
98
+ + this._streamSchedulers.set(messageIndex, {
99
+ + lastText: '',
100
+ + lastRun: 0,
101
+ + leadingTimer: null,
102
+ + trailingTimer: null,
103
+ + running: false
104
+ + });
105
+ + }
106
+ + return this._streamSchedulers.get(messageIndex);
107
+ + }
108
+ +
109
+ + _scheduleMarkdownUpdate(messageIndex, text, { final = false } = {}) {
110
+ + const MIN_INTERVAL = 150; // ms between heavy parses while streaming
111
+ + const QUIET_DEBOUNCE = 120; // flush after quiet period
112
+ + const state = this._ensureScheduler(messageIndex);
113
+ + state.lastText = text ?? '';
114
+ +
115
+ + const parseNow = (isFinal) => {
116
+ + if (!this.messages[messageIndex]) return;
117
+ + // Avoid overlapping parses
118
+ + if (state.running) return;
119
+ + state.running = true;
120
+ + try {
121
+ + const result = markdownRenderer.parse(state.lastText);
122
+ + this.messages[messageIndex].content = result;
123
+ + } catch (e) {
124
+ + console.error('Could not parse markdown:', e);
125
+ + this.messages[messageIndex].content = `<pre><code>${state.lastText}</code></pre>`;
126
+ + } finally {
127
+ + state.lastRun = Date.now();
128
+ + state.running = false;
129
+ + }
130
+ + // Only initialize copy buttons occasionally or on final
131
+ + const now = Date.now();
132
+ + if (isFinal || now - this._lastCopyInit > 1000) {
133
+ + if (typeof window.initializeCodeCopyButtons === 'function') {
134
+ + try { window.initializeCodeCopyButtons(); } catch (_) {}
135
+ + }
136
+ + this._lastCopyInit = now;
137
+ + }
138
+ + this.requestUpdate();
139
+ + this._scrollToBottomThrottled();
140
+ + };
141
+ +
142
+ + // Final flush: cancel timers and render immediately
143
+ + if (final) {
144
+ + if (state.leadingTimer) clearTimeout(state.leadingTimer);
145
+ + if (state.trailingTimer) clearTimeout(state.trailingTimer);
146
+ + state.leadingTimer = null;
147
+ + state.trailingTimer = null;
148
+ + parseNow(true);
149
+ + return;
150
+ + }
151
+ +
152
+ + const now = Date.now();
153
+ + const elapsed = now - state.lastRun;
154
+ +
155
+ + // Leading throttle: if enough time has passed, schedule a near-immediate parse
156
+ + if (elapsed >= MIN_INTERVAL && !state.leadingTimer) {
157
+ + state.leadingTimer = setTimeout(() => {
158
+ + state.leadingTimer = null;
159
+ + parseNow(false);
160
+ + }, 0);
161
+ + }
162
+ +
163
+ + // Trailing debounce: always ensure a quiet-period flush happens
164
+ + if (state.trailingTimer) clearTimeout(state.trailingTimer);
165
+ + state.trailingTimer = setTimeout(() => {
166
+ + state.trailingTimer = null;
167
+ + parseNow(false);
168
+ + }, QUIET_DEBOUNCE);
169
+ + }
170
+ +
171
+ _showError(event) {
172
+ console.log("NOTIFICATION", event)
173
+ const data = JSON.parse(event.data);
174
+ @@ -276,18 +371,11 @@ class Chat extends BaseEl {
175
+ this.msgSoFar = data.params
176
+ }
177
+
178
+ - if (content) {
179
+ - this.messages[this.messages.length - 1].content = content
180
+ - this.requestUpdate();
181
+ - this._scrollToBottom()
182
+ } else if (this.msgSoFar) {
183
+ - // Use throttled parsing for frequent updates - this ensures all updates are shown
184
+ + } else if (this.msgSoFar) {
185
+ + // Hybrid scheduled parsing for frequent partial updates when no handler content
186
+ const messageIndex = this.messages.length - 1;
187
+ - const parsedContent = tryParse(this.msgSoFar, true, messageIndex, this);
188
+ - this.messages[messageIndex].content = parsedContent;
189
+ - this.requestUpdate();
190
+ - this._scrollToBottom();
191
+ - window.initializeCodeCopyButtons();
192
+ + this._scheduleMarkdownUpdate(messageIndex, this.msgSoFar, { final: false });
193
+ }
194
+ } else {
195
+ console.log('partial. data.params', data.params)
196
+ @@ -320,8 +408,7 @@ class Chat extends BaseEl {
197
+ </action-component>`;
198
+ }
199
+ this.requestUpdate();
200
+ - this._scrollToBottom()
201
+ - window.initializeCodeCopyButtons();
202
+ + this._scrollToBottomThrottled();
203
+ }
204
+ }
205
+
206
+ @@ -374,7 +461,14 @@ class Chat extends BaseEl {
207
+ msg.spinning = 'no'
208
+ console.log('Spinner set to false:', msg);
209
+ }
210
+ - window.initializeCodeCopyButtons();
211
+ + // Ensure final flush of any in-flight partial markdown for the last AI message
212
+ + const lastIndex = this.messages.length - 1;
213
+ + const sched = this._streamSchedulers.get(lastIndex);
214
+ + if (sched && typeof sched.lastText === 'string' && sched.lastText.length > 0) {
215
+ + this._scheduleMarkdownUpdate(lastIndex, sched.lastText, { final: true });
216
+ + } else if (typeof window.initializeCodeCopyButtons === 'function') {
217
+ + try { window.initializeCodeCopyButtons(); } catch (_) {}
218
+ + }
219
+ this.requestUpdate();
220
+ }
221
+
@@ -53,7 +53,7 @@ class ChatForm extends BaseEl {
53
53
  position: absolute;
54
54
  right: 21px; /* Original optimal position */
55
55
  margin-right: 5px;
56
- bottom: 15px; /* Raised 5px to prevent bottom overlap */
56
+ bottom: 1.5em; /* Raised 5px to prevent bottom overlap */
57
57
  /* background: transparent; */
58
58
  background-color: #101020
59
59
 
@@ -64,7 +64,7 @@ class ChatForm extends BaseEl {
64
64
  position: absolute;
65
65
  margin-right: 5px;
66
66
  right: 61px; /* Maintains 40px spacing from send button */
67
- bottom: 15px; /* Matches send button exactly */
67
+ bottom: 1.5em; /* Matches send button exactly */
68
68
  background: #ff4d4d;
69
69
  color: white;
70
70
  border: none;