openclacky 1.5.19 → 1.5.21

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +95 -0
  3. data/README.md +19 -1
  4. data/README_CN.md +1 -1
  5. data/README_JA.md +1 -1
  6. data/lib/clacky/agent/chunk_index.rb +8 -1
  7. data/lib/clacky/agent/feedback.rb +30 -0
  8. data/lib/clacky/agent/history_navigation.rb +17 -3
  9. data/lib/clacky/agent/hook_manager.rb +6 -1
  10. data/lib/clacky/agent/llm_caller.rb +17 -3
  11. data/lib/clacky/agent/message_compressor_helper.rb +36 -2
  12. data/lib/clacky/agent/session_serializer.rb +45 -4
  13. data/lib/clacky/agent.rb +135 -55
  14. data/lib/clacky/agent_config.rb +82 -4
  15. data/lib/clacky/billing/platform_billing.rb +10 -5
  16. data/lib/clacky/brand_config.rb +1 -1
  17. data/lib/clacky/cli.rb +5 -4
  18. data/lib/clacky/client.rb +12 -5
  19. data/lib/clacky/deepseek_web_tools.rb +156 -0
  20. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +8 -0
  21. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +97 -20
  22. data/lib/clacky/default_extensions/time_machine/panels/time_machine/view.js +1 -0
  23. data/lib/clacky/default_skills/onboard/SKILL.md +1 -1
  24. data/lib/clacky/extension/loader.rb +5 -3
  25. data/lib/clacky/locales/en.rb +5 -5
  26. data/lib/clacky/locales/zh.rb +5 -5
  27. data/lib/clacky/message_format/anthropic.rb +20 -0
  28. data/lib/clacky/message_format/open_ai.rb +31 -14
  29. data/lib/clacky/message_history.rb +45 -4
  30. data/lib/clacky/platform_http_client.rb +6 -2
  31. data/lib/clacky/providers.rb +38 -138
  32. data/lib/clacky/search_config.rb +47 -13
  33. data/lib/clacky/server/browser_manager.rb +35 -5
  34. data/lib/clacky/server/channel/channel_manager.rb +6 -6
  35. data/lib/clacky/server/discover.rb +5 -4
  36. data/lib/clacky/server/http_server.rb +285 -53
  37. data/lib/clacky/server/model_prices.rb +10 -5
  38. data/lib/clacky/server/pid_files.rb +25 -0
  39. data/lib/clacky/server/server_master.rb +9 -3
  40. data/lib/clacky/server/session_registry.rb +37 -7
  41. data/lib/clacky/server/web_ui_controller.rb +72 -0
  42. data/lib/clacky/session_manager.rb +28 -6
  43. data/lib/clacky/skill.rb +1 -1
  44. data/lib/clacky/tools/ask_user.rb +4 -1
  45. data/lib/clacky/tools/base.rb +3 -2
  46. data/lib/clacky/tools/browser.rb +18 -2
  47. data/lib/clacky/tools/file_reader.rb +1 -1
  48. data/lib/clacky/tools/terminal/session_manager.rb +19 -1
  49. data/lib/clacky/tools/terminal.rb +65 -23
  50. data/lib/clacky/tools/trash_manager.rb +5 -3
  51. data/lib/clacky/tools/web_fetch.rb +81 -22
  52. data/lib/clacky/tools/web_search.rb +45 -4
  53. data/lib/clacky/ui2/ui_controller.rb +1 -1
  54. data/lib/clacky/ui_interface.rb +4 -3
  55. data/lib/clacky/utils/file_processor.rb +51 -0
  56. data/lib/clacky/utils/model_pricing.rb +242 -39
  57. data/lib/clacky/version.rb +1 -1
  58. data/lib/clacky/web/app.css +1158 -249
  59. data/lib/clacky/web/app.js +82 -13
  60. data/lib/clacky/web/components/chat-navigator.js +8 -1
  61. data/lib/clacky/web/components/code-editor.js +6 -2
  62. data/lib/clacky/web/components/mentions.js +2 -13
  63. data/lib/clacky/web/components/model-picker.js +92 -97
  64. data/lib/clacky/web/components/onboard.js +13 -10
  65. data/lib/clacky/web/components/sidebar.js +6 -2
  66. data/lib/clacky/web/features/archive/store.js +196 -0
  67. data/lib/clacky/web/features/archive/view.js +409 -0
  68. data/lib/clacky/web/features/backup/view.js +17 -7
  69. data/lib/clacky/web/features/billing/view.js +84 -10
  70. data/lib/clacky/web/features/channels/view.js +75 -29
  71. data/lib/clacky/web/features/new-session/store.js +3 -3
  72. data/lib/clacky/web/features/new-session/view.js +1 -0
  73. data/lib/clacky/web/features/trash/store.js +14 -11
  74. data/lib/clacky/web/features/trash/view.js +66 -13
  75. data/lib/clacky/web/features/workspace/view.js +421 -16
  76. data/lib/clacky/web/i18n.js +242 -70
  77. data/lib/clacky/web/index.html +270 -117
  78. data/lib/clacky/web/projects.js +56 -1
  79. data/lib/clacky/web/sessions.js +609 -267
  80. data/lib/clacky/web/settings.js +186 -32
  81. data/lib/clacky/web/ws-dispatcher.js +33 -21
  82. data/lib/clacky.rb +4 -2
  83. metadata +6 -1
@@ -157,6 +157,8 @@ const Projects = (() => {
157
157
  Sessions.removeByProjectId(id);
158
158
  Sessions.renderList();
159
159
  renderSection();
160
+ Archive.refreshIfShown();
161
+ Trash.refreshIfShown();
160
162
  }
161
163
 
162
164
  /**
@@ -514,7 +516,8 @@ const Projects = (() => {
514
516
  const iconPin = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="transform:rotate(45deg);display:block"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>`;
515
517
  const iconEdit = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4z"/></svg>`;
516
518
  const iconClearSessions = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 22-1-4"/><path d="M19 14a1 1 0 0 0 1-1v-1a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v1a1 1 0 0 0 1 1"/><path d="M19 14H5l-1.973 6.767A1 1 0 0 0 4 22h16a1 1 0 0 0 .973-1.233z"/><path d="m8 22 1-4"/></svg>`;
517
- const iconTrash = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>`;
519
+ const iconTrash = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>`;
520
+ const iconArchive = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="5" rx="1"/><path d="M4 9v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9"/><line x1="10" y1="14" x2="14" y2="14"/></svg>`;
518
521
 
519
522
  const addItem = (labelKey, defaultLabel, iconSvg, danger, onClick) => {
520
523
  const item = document.createElement("button");
@@ -551,6 +554,14 @@ const Projects = (() => {
551
554
  isPinned ? "Unpin" : "Pin to top", iconPin, false, pinToggle);
552
555
  addItem("projects.menu.edit", "Edit Project", iconEdit, false, () => promptEdit(projectId));
553
556
  addItem("projects.menu.deleteSessions", "Delete Sessions", iconClearSessions, false, () => promptDeleteSessions(projectId));
557
+
558
+ // The last group (archive + delete) mirrors the session menu: an explicit
559
+ // divider above Archive, then Archive right before Delete.
560
+ const divider = document.createElement("div");
561
+ divider.className = "project-actions-menu-divider";
562
+ menu.appendChild(divider);
563
+
564
+ addItem("projects.menu.archiveSessions", "Archive Sessions", iconArchive, false, () => promptArchiveSessions(projectId));
554
565
  addItem("projects.menu.delete", "Delete", iconTrash, true, () => promptDelete(projectId));
555
566
 
556
567
  // Position near anchor
@@ -1050,11 +1061,53 @@ const Projects = (() => {
1050
1061
  Sessions.removeByProjectId(id);
1051
1062
  Sessions.renderList();
1052
1063
  renderSection();
1064
+ Archive.refreshIfShown();
1065
+ Trash.refreshIfShown();
1053
1066
  } catch (err) {
1054
1067
  console.error("[Projects] delete sessions error:", err);
1055
1068
  }
1056
1069
  }
1057
1070
 
1071
+ /**
1072
+ * Archive every session in a project. Unlike deleting, the chats stay
1073
+ * recoverable from the Archived sessions page.
1074
+ */
1075
+ async function promptArchiveSessions(id) {
1076
+ const project = find(id);
1077
+ if (!project) return;
1078
+
1079
+ // Always ask first: the sidebar list may not hold every session of the
1080
+ // project (cron/ext sessions live in folded groups), so it can't be used
1081
+ // to decide whether there is anything worth confirming.
1082
+ const confirmed = await Modal.confirm(
1083
+ I18n.t("projects.archiveSessions.confirm", { name: project.name })
1084
+ );
1085
+ if (!confirmed) return;
1086
+
1087
+ const ids = Sessions.all.filter(s => s.project_id === id).map(s => s.id);
1088
+ try {
1089
+ const res = await fetch(`/api/projects/${id}/sessions/archive`, { method: "POST" });
1090
+ const data = await res.json();
1091
+ if (!res.ok) throw new Error(data.error || "Failed to archive sessions");
1092
+
1093
+ if (!data.archived) {
1094
+ Modal.toast(I18n.t("projects.archiveSessions.empty"), "info");
1095
+ return;
1096
+ }
1097
+
1098
+ if (ids.includes(Sessions.activeId)) Router.navigate("welcome");
1099
+ ids.forEach(sessionId => Sessions.remove(sessionId));
1100
+ Sessions.renderList();
1101
+ renderSection();
1102
+ Archive.refreshIfShown();
1103
+
1104
+ Modal.toast(I18n.t("projects.archiveSessions.done", { count: data.archived }), "success");
1105
+ } catch (err) {
1106
+ console.error("[Projects] archive sessions error:", err);
1107
+ Modal.toast(I18n.t("projects.archiveSessions.fail", { msg: err.message }), "error");
1108
+ }
1109
+ }
1110
+
1058
1111
  /** Confirm and delete a project. */
1059
1112
  async function promptDelete(id) {
1060
1113
  const project = find(id);
@@ -1132,6 +1185,8 @@ const Projects = (() => {
1132
1185
  promptCreate,
1133
1186
  promptEdit,
1134
1187
  promptDelete,
1188
+ promptDeleteSessions,
1189
+ promptArchiveSessions,
1135
1190
  renderSection,
1136
1191
  expand,
1137
1192
  getIconSvg: _getIconSvg,