openclacky 1.3.4 → 1.3.5

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/clacky/agent/fake_tool_call_detector.rb +52 -0
  4. data/lib/clacky/agent/session_serializer.rb +3 -2
  5. data/lib/clacky/agent/tool_executor.rb +0 -12
  6. data/lib/clacky/agent.rb +74 -9
  7. data/lib/clacky/api_extension.rb +81 -0
  8. data/lib/clacky/api_extension_loader.rb +13 -1
  9. data/lib/clacky/client.rb +14 -17
  10. data/lib/clacky/default_agents/_panels/time_machine/panel.js +22 -0
  11. data/lib/clacky/default_agents/base_prompt.md +1 -0
  12. data/lib/clacky/default_extensions/meeting/handler.rb +331 -0
  13. data/lib/clacky/default_extensions/meeting/meeting.js +790 -0
  14. data/lib/clacky/default_extensions/meeting/meta.yml +3 -0
  15. data/lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md +44 -0
  16. data/lib/clacky/default_skills/media-gen/SKILL.md +63 -0
  17. data/lib/clacky/default_skills/media-gen/scripts/video_seq.sh +114 -0
  18. data/lib/clacky/json_ui_controller.rb +1 -1
  19. data/lib/clacky/media/base.rb +60 -0
  20. data/lib/clacky/media/dashscope.rb +385 -21
  21. data/lib/clacky/media/gemini.rb +9 -0
  22. data/lib/clacky/media/generator.rb +52 -0
  23. data/lib/clacky/media/openai_compat.rb +166 -0
  24. data/lib/clacky/null_ui_controller.rb +13 -0
  25. data/lib/clacky/plain_ui_controller.rb +1 -1
  26. data/lib/clacky/providers.rb +50 -2
  27. data/lib/clacky/rich_ui/rich_ui_controller.rb +1 -1
  28. data/lib/clacky/server/channel/channel_ui_controller.rb +1 -1
  29. data/lib/clacky/server/http_server.rb +144 -9
  30. data/lib/clacky/server/session_registry.rb +4 -2
  31. data/lib/clacky/server/web_ui_controller.rb +3 -2
  32. data/lib/clacky/skill_loader.rb +14 -2
  33. data/lib/clacky/tools/terminal/output_cleaner.rb +1 -3
  34. data/lib/clacky/tools/terminal.rb +0 -43
  35. data/lib/clacky/ui2/components/modal_component.rb +1 -1
  36. data/lib/clacky/ui2/ui_controller.rb +140 -31
  37. data/lib/clacky/ui_interface.rb +10 -1
  38. data/lib/clacky/utils/encoding.rb +25 -0
  39. data/lib/clacky/version.rb +1 -1
  40. data/lib/clacky/web/app.css +145 -22
  41. data/lib/clacky/web/components/onboard.js +1 -14
  42. data/lib/clacky/web/features/brand/view.js +8 -5
  43. data/lib/clacky/web/features/channels/store.js +1 -20
  44. data/lib/clacky/web/features/mcp/store.js +1 -20
  45. data/lib/clacky/web/features/profile/store.js +1 -13
  46. data/lib/clacky/web/features/profile/view.js +16 -4
  47. data/lib/clacky/web/features/skills/store.js +6 -21
  48. data/lib/clacky/web/features/version/store.js +2 -0
  49. data/lib/clacky/web/i18n.js +24 -1
  50. data/lib/clacky/web/index.html +15 -0
  51. data/lib/clacky/web/sessions.js +141 -51
  52. data/lib/clacky/web/settings.js +34 -2
  53. data/lib/clacky/web/ws-dispatcher.js +11 -3
  54. data/lib/clacky.rb +12 -5
  55. metadata +8 -1
@@ -1508,6 +1508,7 @@ const Settings = (() => {
1508
1508
  _showBrandResult(true, I18n.t("settings.brand.activated", { name: data.product_name || "configured" }));
1509
1509
  // Apply brand name and logo across the entire UI immediately
1510
1510
  if (data.product_name) Brand.applyBrandName(data.product_name);
1511
+ if (data.theme_color) _applyAccentColor(data.theme_color, { persist: true });
1511
1512
  Brand.clearBrandCache();
1512
1513
  Brand.applyHeaderLogo();
1513
1514
  // Refresh brand status flags (user_licensed may have flipped from false
@@ -1683,7 +1684,7 @@ const Settings = (() => {
1683
1684
  // The state object per kind:
1684
1685
  // { source, configured, model, base_url, api_key_masked, provider, available }
1685
1686
 
1686
- const MEDIA_KINDS = ["image", "video", "audio", "ocr"];
1687
+ const MEDIA_KINDS = ["image", "video", "audio", "stt", "video_understanding", "ocr"];
1687
1688
  let _mediaState = null;
1688
1689
  let _mediaDefaults = null;
1689
1690
  const _mediaCustomDraft = {};
@@ -2173,6 +2174,8 @@ const Settings = (() => {
2173
2174
  Brand.applyBrandName("OpenClacky");
2174
2175
  Brand.clearBrandCache();
2175
2176
  Brand.applyHeaderLogo();
2177
+ const userAccent = (() => { try { return localStorage.getItem("clacky-accent-color"); } catch (_) { return null; } })();
2178
+ _applyAccentColor(userAccent || ACCENT_DEFAULT, { persist: false });
2176
2179
  // Reset Skills panel state (hide Brand Skills tab, switch to My Skills)
2177
2180
  if (typeof Skills !== "undefined" && Skills.resetAfterUnbind) {
2178
2181
  Skills.resetAfterUnbind();
@@ -2203,6 +2206,7 @@ const Settings = (() => {
2203
2206
  _initLangBtns();
2204
2207
  _initFontBtns();
2205
2208
  _initCurrencyBtns();
2209
+ _initAccentColorBtns();
2206
2210
 
2207
2211
  // Re-render model cards when language changes (dynamic HTML, not data-i18n)
2208
2212
  document.addEventListener("langchange", () => {
@@ -2364,6 +2368,34 @@ const Settings = (() => {
2364
2368
  });
2365
2369
  }
2366
2370
 
2371
+ // ── Accent Color ──────────────────────────────────────────────────────
2372
+ const ACCENT_STORAGE_KEY = "clacky-accent-color";
2373
+ const ACCENT_DEFAULT = "#4f46e5";
2374
+
2375
+ function _applyAccentColor(color, { persist = true } = {}) {
2376
+ const root = document.documentElement;
2377
+ root.style.setProperty("--color-accent-primary", color);
2378
+ root.style.setProperty("--color-accent-hover", color);
2379
+ root.style.setProperty("--color-button-primary", color);
2380
+ root.style.setProperty("--color-button-primary-hover", `color-mix(in srgb, ${color} 82%, #000)`);
2381
+ if (persist) {
2382
+ try { localStorage.setItem(ACCENT_STORAGE_KEY, color); } catch (_) {}
2383
+ }
2384
+ document.querySelectorAll("#accent-color-section .settings-accent-swatch").forEach(btn => {
2385
+ btn.classList.toggle("active", btn.dataset.color === color);
2386
+ });
2387
+ }
2388
+
2389
+ function _initAccentColorBtns() {
2390
+ let saved = null;
2391
+ try { saved = localStorage.getItem(ACCENT_STORAGE_KEY); } catch (_) {}
2392
+ _applyAccentColor(saved || ACCENT_DEFAULT, { persist: !!saved });
2393
+
2394
+ document.querySelectorAll("#accent-color-section .settings-accent-swatch").forEach(btn => {
2395
+ btn.addEventListener("click", () => _applyAccentColor(btn.dataset.color));
2396
+ });
2397
+ }
2398
+
2367
2399
  // ── QR Code Lightbox ───────────────────────────────────────────────────
2368
2400
  // Sets up click-to-enlarge behaviour for the support QR code.
2369
2401
  // Safe to call multiple times — idempotent via a data attribute guard.
@@ -2404,5 +2436,5 @@ const Settings = (() => {
2404
2436
  });
2405
2437
  }
2406
2438
 
2407
- return { open, init, loadBrand: _loadBrand };
2439
+ return { open, init, loadBrand: _loadBrand, applyAccentColor: _applyAccentColor };
2408
2440
  })();
@@ -216,7 +216,7 @@ WS.onEvent(ev => {
216
216
  // send it now — after restoreFromHash has settled — so appendMsg won't be wiped.
217
217
  const pendingMsg = Sessions.takePendingMessage();
218
218
  if (pendingMsg && pendingMsg.session_id === ev.session_id) {
219
- Sessions.appendMsg("user", escapeHtml(pendingMsg.content), { time: new Date() });
219
+ Sessions.appendMsg("user", escapeHtml(pendingMsg.display || pendingMsg.content), { time: new Date() });
220
220
  WS.send({ type: "message", session_id: pendingMsg.session_id, content: pendingMsg.content, lang: I18n.lang() });
221
221
  }
222
222
  break;
@@ -310,6 +310,9 @@ WS.onEvent(ev => {
310
310
  if (ev.session_id !== Sessions.activeId) break;
311
311
  Sessions.clearProgress();
312
312
  Sessions.appendMsg("assistant", ev.content);
313
+ if (window.Clacky && Clacky.ext) {
314
+ Clacky.ext.emit("session:assistant-message", { sessionId: ev.session_id, content: ev.content });
315
+ }
313
316
  break;
314
317
 
315
318
  case "tool_call":
@@ -442,10 +445,15 @@ function renderErrorEvent(ev) {
442
445
  const action = ev.top_up_url
443
446
  ? ` <a href="${escapeHtml(ev.top_up_url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(I18n.t("error.insufficient_credit.action"))} →</a>`
444
447
  : "";
445
- Sessions.appendMsg("error", `<span>${body}${action}</span>`);
448
+ Sessions.appendMsg("error", `<span>${body}${action}</span>${_buildRawDetail(ev.raw_message)}`);
446
449
  return;
447
450
  }
448
- Sessions.appendMsg("error", escapeHtml(ev.message));
451
+ Sessions.appendMsg("error", escapeHtml(ev.message) + _buildRawDetail(ev.raw_message));
452
+ }
453
+
454
+ function _buildRawDetail(raw) {
455
+ if (!raw) return "";
456
+ return `<details class="error-raw-detail"><summary>${escapeHtml(I18n.t("error.show_detail"))}</summary><pre>${escapeHtml(raw)}</pre></details>`;
449
457
  }
450
458
 
451
459
  window.renderErrorEvent = renderErrorEvent;
data/lib/clacky.rb CHANGED
@@ -155,20 +155,27 @@ require_relative "clacky/server/api_extension_dispatcher"
155
155
 
156
156
  module Clacky
157
157
  class AgentInterrupted < Exception; end # Inherit from Exception to bypass rescue StandardError
158
- class AgentError < StandardError; end
158
+ class AgentError < StandardError
159
+ attr_reader :raw_message
160
+
161
+ def initialize(message, raw_message: nil)
162
+ super(message)
163
+ @raw_message = raw_message
164
+ end
165
+ end
159
166
  class BadRequestError < AgentError
160
167
  attr_reader :display_message
161
168
 
162
- def initialize(message, display_message: nil)
163
- super(message)
169
+ def initialize(message, display_message: nil, raw_message: nil)
170
+ super(message, raw_message: raw_message)
164
171
  @display_message = display_message
165
172
  end
166
173
  end
167
174
  class InsufficientCreditError < AgentError
168
175
  attr_reader :error_code, :provider_id
169
176
 
170
- def initialize(message, error_code: nil, provider_id: nil)
171
- super(message)
177
+ def initialize(message, error_code: nil, provider_id: nil, raw_message: nil)
178
+ super(message, raw_message: raw_message)
172
179
  @error_code = error_code
173
180
  @provider_id = provider_id
174
181
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openclacky
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.4
4
+ version: 1.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - windy
@@ -338,6 +338,7 @@ files:
338
338
  - lib/clacky/aes_gcm.rb
339
339
  - lib/clacky/agent.rb
340
340
  - lib/clacky/agent/cost_tracker.rb
341
+ - lib/clacky/agent/fake_tool_call_detector.rb
341
342
  - lib/clacky/agent/hook_manager.rb
342
343
  - lib/clacky/agent/llm_caller.rb
343
344
  - lib/clacky/agent/memory_updater.rb
@@ -375,6 +376,10 @@ files:
375
376
  - lib/clacky/default_agents/coding/webui/.gitkeep
376
377
  - lib/clacky/default_agents/general/profile.yml
377
378
  - lib/clacky/default_agents/general/system_prompt.md
379
+ - lib/clacky/default_extensions/meeting/handler.rb
380
+ - lib/clacky/default_extensions/meeting/meeting.js
381
+ - lib/clacky/default_extensions/meeting/meta.yml
382
+ - lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md
378
383
  - lib/clacky/default_parsers/doc_parser.rb
379
384
  - lib/clacky/default_parsers/docx_parser.rb
380
385
  - lib/clacky/default_parsers/pdf_parser.rb
@@ -399,6 +404,7 @@ files:
399
404
  - lib/clacky/default_skills/extend-openclacky/SKILL.md
400
405
  - lib/clacky/default_skills/mcp-manager/SKILL.md
401
406
  - lib/clacky/default_skills/media-gen/SKILL.md
407
+ - lib/clacky/default_skills/media-gen/scripts/video_seq.sh
402
408
  - lib/clacky/default_skills/new/SKILL.md
403
409
  - lib/clacky/default_skills/new/scripts/create_rails_project.sh
404
410
  - lib/clacky/default_skills/onboard/SKILL.md
@@ -449,6 +455,7 @@ files:
449
455
  - lib/clacky/message_format/bedrock.rb
450
456
  - lib/clacky/message_format/open_ai.rb
451
457
  - lib/clacky/message_history.rb
458
+ - lib/clacky/null_ui_controller.rb
452
459
  - lib/clacky/openai_stream_aggregator.rb
453
460
  - lib/clacky/patch_loader.rb
454
461
  - lib/clacky/plain_ui_controller.rb