openclacky 1.5.3 → 1.5.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +13 -0
  3. data/CHANGELOG.md +57 -0
  4. data/Dockerfile +21 -1
  5. data/README.md +26 -7
  6. data/README_CN.md +26 -7
  7. data/README_JA.md +26 -7
  8. data/lib/clacky/agent/message_compressor.rb +4 -29
  9. data/lib/clacky/agent/message_compressor_helper.rb +17 -16
  10. data/lib/clacky/agent/session_serializer.rb +7 -0
  11. data/lib/clacky/agent.rb +14 -2
  12. data/lib/clacky/agent_config.rb +10 -5
  13. data/lib/clacky/agent_profile.rb +25 -3
  14. data/lib/clacky/brand_config.rb +32 -12
  15. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +15 -0
  16. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +1 -5
  17. data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -2
  18. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +43 -34
  19. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +9 -3
  20. data/lib/clacky/default_extensions/git/ext.yml +3 -0
  21. data/lib/clacky/default_extensions/meeting/ext.yml +3 -0
  22. data/lib/clacky/default_extensions/time_machine/ext.yml +4 -0
  23. data/lib/clacky/default_parsers/xlsx_parser.py +66 -0
  24. data/lib/clacky/default_skills/new/SKILL.md +17 -12
  25. data/lib/clacky/extension/api_extension.rb +5 -2
  26. data/lib/clacky/extension/loader.rb +1 -0
  27. data/lib/clacky/extension/packager.rb +60 -15
  28. data/lib/clacky/extension/verifier.rb +1 -1
  29. data/lib/clacky/message_format/anthropic.rb +1 -1
  30. data/lib/clacky/message_format/bedrock.rb +1 -1
  31. data/lib/clacky/message_format/open_ai.rb +34 -3
  32. data/lib/clacky/providers.rb +77 -8
  33. data/lib/clacky/server/http_server.rb +495 -39
  34. data/lib/clacky/server/project_manager.rb +150 -0
  35. data/lib/clacky/server/server_master.rb +7 -0
  36. data/lib/clacky/server/session_registry.rb +14 -1
  37. data/lib/clacky/server/web_ui_controller.rb +12 -2
  38. data/lib/clacky/session_manager.rb +6 -4
  39. data/lib/clacky/tools/file_reader.rb +2 -1
  40. data/lib/clacky/tools/terminal.rb +30 -3
  41. data/lib/clacky/utils/model_pricing.rb +69 -18
  42. data/lib/clacky/utils/parser_manager.rb +115 -7
  43. data/lib/clacky/version.rb +1 -1
  44. data/lib/clacky/web/app.css +1216 -318
  45. data/lib/clacky/web/app.js +27 -4
  46. data/lib/clacky/web/components/notify.js +139 -22
  47. data/lib/clacky/web/core/aside.js +21 -0
  48. data/lib/clacky/web/features/brand/store.js +3 -1
  49. data/lib/clacky/web/features/brand/view.js +29 -5
  50. data/lib/clacky/web/features/extensions/store.js +214 -9
  51. data/lib/clacky/web/features/extensions/view.js +156 -11
  52. data/lib/clacky/web/features/new-session/store.js +25 -3
  53. data/lib/clacky/web/features/new-session/view.js +339 -142
  54. data/lib/clacky/web/features/skills/view.js +1 -1
  55. data/lib/clacky/web/features/trash/store.js +23 -9
  56. data/lib/clacky/web/features/trash/view.js +51 -0
  57. data/lib/clacky/web/i18n.js +124 -15
  58. data/lib/clacky/web/index.html +105 -79
  59. data/lib/clacky/web/projects.js +1143 -0
  60. data/lib/clacky/web/sessions.js +320 -538
  61. data/lib/clacky/web/settings.js +138 -5
  62. data/lib/clacky/web/utils.js +1 -0
  63. data/lib/clacky/web/ws-dispatcher.js +98 -4
  64. data/scripts/build/src/install_system_deps.sh.cc +11 -3
  65. data/scripts/install_system_deps.sh +11 -3
  66. metadata +5 -2
  67. data/lib/clacky/default_parsers/xlsx_parser.rb +0 -121
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04d2d81c4aaa380d4600066802c78e1137bdad1cb16c3e5e47e32e69ede15da6
4
- data.tar.gz: 9a332e0305e96bae97799ce707ef492b24735e33ef8da70a390c06a10ac11ab4
3
+ metadata.gz: 5803d9775474800f6f61d6771837ebe90597080e0f068a9dff49d5d44fdc90d1
4
+ data.tar.gz: f111f2b083635876cd2e4477e5a64da14245684c5c18a5f5f23860541ac9def0
5
5
  SHA512:
6
- metadata.gz: 435b6c1bd6aac4c2916cf172e5d46dece9a5707d1b18119d4744d29293c1f7b8fb112709718d24b8b9b3a871bfa228294a282525d9b6f7ed14550b89eaaa2793
7
- data.tar.gz: 90599042f3d933db9b7681c33d539937585cd85542d635696ea159056aa47dbe34f86971bd1db6541c4dee28ee73c66e8f4648d91d8f3b1897b179ce9a2b00fc
6
+ metadata.gz: a0cc2a8397b5165b941a5aec4f1fc72770d47fdee4b24b72d862b12154d6a9b8114563b2e977d64e43a39c86d278fb5bbb97b045357c0a00c2f818f6466dbbf8
7
+ data.tar.gz: '07913be97ae9966164649f88a8e8abc60346c80038aec65886c51d9552e75eaf1050e3a9cc6fc6e28d1137d00d9fabcf735a8ac195bc3c5554890a6a8c0a073d'
data/.dockerignore ADDED
@@ -0,0 +1,13 @@
1
+ .git
2
+ .github
3
+ .clacky
4
+ vendor
5
+ spec
6
+ docs
7
+ tmp
8
+ coverage
9
+ pkg
10
+ *.gem
11
+ .rspec_status
12
+ AGENTS.md
13
+ CLAUDE.md
data/CHANGELOG.md CHANGED
@@ -4,6 +4,63 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
 
7
+ ## [1.5.5] - 2026-08-04
8
+
9
+ ### Added
10
+ - Ollama (Cloud) provider preset — connect to self-hosted Ollama instances with one click
11
+ - Extension WebSocket bridge — extensions can now subscribe to session events via `Clacky.ext` bus
12
+ - Hidden field support for extension agent profiles
13
+ - Max reasoning effort level beyond `xhigh` (supports `max` tier)
14
+ - Smart terminal output truncation — keeps head and tail of long output instead of cutting at the end
15
+ - Time machine panel now attached to all views
16
+ - Persist sort and installed filter selection across sessions in extensions panel
17
+ - New extension button added to extensions management page header
18
+ - Auto cleanup of trash sessions after 7 days
19
+
20
+ ### Improved
21
+ - Extensions panel redesigned with system/market tabs and toggle switch
22
+ - New-session page: replaced advanced modal with inline context bar chips; agents refresh on open with localized badges, ordered by most-recently-used
23
+ - Overflow tooltip shown on truncated skill descriptions
24
+ - Compression: idle and threshold switches are now separate controls
25
+ - Extension install is now async with progress polling
26
+
27
+ ### Fixed
28
+ - Ollama base URL corrected from `/api` to root domain
29
+ - Extension WebSocket bridge included `hidden` field in verifier AGENT_KEYS
30
+ - User message files render immediately in Web UI; local disk images proxied correctly
31
+ - Sidebar visibility persisted across page reloads
32
+ - Brand logo injected server-side to eliminate flash on load
33
+ - Extension uninstall 404 resolved by normalizing slug field
34
+ - Extension homepage link color variable corrected
35
+ - `request_user_feedback` skill uses structured JSON example to avoid literal `\n` in output
36
+ - `create_session` in extensions now broadcasts session update correctly
37
+
38
+ ## [1.5.4] - 2026-08-01
39
+
40
+ ### Added
41
+ - Project management: create, manage, and switch between projects
42
+ - Claude Sonnet 5 and Opus 5 models with pricing
43
+ - Desktop notifications for background task completion
44
+ - Configurable platform source and branding restoration
45
+ - Hidden flag on create_session API (#403)
46
+ - MiniMax preset refreshed with Anthropic endpoints, M3 thinking, and current lineup (#402)
47
+
48
+ ### Improved
49
+ - File upload size limit increased from 32 MB to 300 MB
50
+ - Dir picker UX: breadcrumb overflow scroll, row spacing, and padding
51
+ - Session-sidebar collapse button appearance matches opener button
52
+
53
+ ### Fixed
54
+ - XLSX parser switched to Python/openpyxl with 60s timeout kill for reliable parsing (C-5656)
55
+ - Panel agents map missing general profile on first load
56
+ - Stale result on first load due to container extension script execution order
57
+ - Page now reloads after deleting local extension in ext-studio
58
+ - Accent color used for extensions filter tab active underline
59
+ - Missing pricing for Claude Sonnet 5 and Opus 5
60
+ - Docker image builds from source and publishes to GHCR on version tags
61
+ - Session list page size reduced from 20 to 15 for better performance
62
+ - MiniMax-M2.5 restored, default base_url reverted to mainland
63
+
7
64
  ## [1.5.3] - 2026-07-28
8
65
 
9
66
  ### Added
data/Dockerfile CHANGED
@@ -1,14 +1,34 @@
1
1
  FROM ruby:3.4.4-slim AS builder
2
2
 
3
+ ARG VERSION
4
+
3
5
  RUN apt-get update && apt-get install -y \
4
6
  build-essential \
5
7
  curl \
8
+ git \
6
9
  && rm -rf /var/lib/apt/lists/*
7
10
 
8
- RUN gem install openclacky --no-document
11
+ # Build from repository source so the image matches this checkout (not a published gem).
12
+ # gemspec uses `git ls-files`; seed a temporary git index because .git is dockerignored.
13
+ COPY . /src
14
+ WORKDIR /src
15
+ RUN git init \
16
+ && git config user.email "docker@local" \
17
+ && git config user.name "docker" \
18
+ && git add -A \
19
+ && git commit -m "docker build" \
20
+ && gem build openclacky.gemspec \
21
+ && gem install ./openclacky-*.gem --no-document \
22
+ && ruby -e 'require "clacky"; abort "bad version" unless Clacky::VERSION'
9
23
 
10
24
  FROM ruby:3.4.4-slim
11
25
 
26
+ ARG VERSION
27
+ LABEL org.opencontainers.image.title="openclacky" \
28
+ org.opencontainers.image.description="OpenClacky AI agent CLI and Web UI" \
29
+ org.opencontainers.image.source="https://github.com/clacky-ai/openclacky" \
30
+ org.opencontainers.image.version="${VERSION}"
31
+
12
32
  RUN apt-get update && apt-get install -y \
13
33
  git \
14
34
  curl \
data/README.md CHANGED
@@ -110,18 +110,24 @@ see more: https://www.openclacky.com/docs/installation
110
110
 
111
111
  ### Docker
112
112
 
113
- Build:
113
+ #### Pre-built image (GHCR)
114
+
115
+ Images are published to GitHub Container Registry on version tags (`v*`).
114
116
 
115
117
  ```bash
116
- git clone https://github.com/clacky-ai/openclacky.git
117
- cd openclacky
118
- docker build -t openclacky .
118
+ # Replace <owner> with the repo owner (e.g. clacky-ai or your fork)
119
+ docker pull ghcr.io/<owner>/openclacky:latest
120
+ # or pin a release:
121
+ # docker pull ghcr.io/<owner>/openclacky:1.5.3
119
122
  ```
120
123
 
121
124
  **Linux:**
122
125
 
123
126
  ```bash
124
- docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
127
+ docker run -d --name openclacky --network=host \
128
+ -e CLACKY_ACCESS_KEY="" \
129
+ -v openclacky-data:/root/.clacky \
130
+ ghcr.io/<owner>/openclacky:latest
125
131
  ```
126
132
 
127
133
  `--network=host` is required so the agent inside the container can reach Chrome's remote debugging port running on the host.
@@ -129,18 +135,31 @@ docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
129
135
  **macOS / Windows:**
130
136
 
131
137
  ```bash
132
- docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
138
+ docker run -d --name openclacky -p 7070:7070 \
139
+ -e CLACKY_ACCESS_KEY="" \
140
+ -v openclacky-data:/root/.clacky \
141
+ ghcr.io/<owner>/openclacky:latest
133
142
  ```
134
143
 
135
144
  > **Note:** On macOS/Windows, `--network=host` is not supported — browser automation may be limited.
136
145
 
137
146
  Open **http://localhost:7070** after starting.
138
147
 
148
+ #### Build from source
149
+
150
+ ```bash
151
+ git clone https://github.com/clacky-ai/openclacky.git
152
+ cd openclacky
153
+ # optional: set the OCI image version label
154
+ docker build --build-arg VERSION=1.5.3 -t openclacky .
155
+ docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
156
+ ```
157
+
139
158
  Environment variables:
140
159
 
141
160
  | Variable | Description |
142
161
  |---|---|
143
- | `CLACKY_ACCESS_KEY` | Protect the Web UI with an access key (empty = public mode) |
162
+ | `CLACKY_ACCESS_KEY` | Protect the Web UI with an access key (empty = public mode; env must be present when binding `0.0.0.0`) |
144
163
 
145
164
 
146
165
  ## Quick Start
data/README_CN.md CHANGED
@@ -106,18 +106,24 @@ gem install openclacky
106
106
 
107
107
  ### Docker
108
108
 
109
- 构建:
109
+ #### 预构建镜像(GHCR)
110
+
111
+ 镜像在版本 tag(`v*`)时自动发布到 GitHub Container Registry。
110
112
 
111
113
  ```bash
112
- git clone https://github.com/clacky-ai/openclacky.git
113
- cd openclacky
114
- docker build -t openclacky .
114
+ # <owner> 替换为仓库所有者(如 clacky-ai 或你的 fork)
115
+ docker pull ghcr.io/<owner>/openclacky:latest
116
+ # 或固定某个版本:
117
+ # docker pull ghcr.io/<owner>/openclacky:1.5.3
115
118
  ```
116
119
 
117
120
  **Linux:**
118
121
 
119
122
  ```bash
120
- docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
123
+ docker run -d --name openclacky --network=host \
124
+ -e CLACKY_ACCESS_KEY="" \
125
+ -v openclacky-data:/root/.clacky \
126
+ ghcr.io/<owner>/openclacky:latest
121
127
  ```
122
128
 
123
129
  `--network=host` 使容器与宿主机共享网络栈,Agent 可直接访问宿主机上运行的 Chrome 远程调试端口。
@@ -125,18 +131,31 @@ docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
125
131
  **macOS / Windows:**
126
132
 
127
133
  ```bash
128
- docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
134
+ docker run -d --name openclacky -p 7070:7070 \
135
+ -e CLACKY_ACCESS_KEY="" \
136
+ -v openclacky-data:/root/.clacky \
137
+ ghcr.io/<owner>/openclacky:latest
129
138
  ```
130
139
 
131
140
  > **注意:** macOS/Windows 不支持 `--network=host`,浏览器自动化功能可能受限。
132
141
 
133
142
  启动后访问 **http://localhost:7070**。
134
143
 
144
+ #### 从源码构建
145
+
146
+ ```bash
147
+ git clone https://github.com/clacky-ai/openclacky.git
148
+ cd openclacky
149
+ # 可选:设置 OCI 镜像版本标签
150
+ docker build --build-arg VERSION=1.5.3 -t openclacky .
151
+ docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
152
+ ```
153
+
135
154
  环境变量:
136
155
 
137
156
  | 变量 | 说明 |
138
157
  |---|---|
139
- | `CLACKY_ACCESS_KEY` | 设置访问密钥保护 Web UI(留空 = 公开模式) |
158
+ | `CLACKY_ACCESS_KEY` | 设置访问密钥保护 Web UI(留空 = 公开模式;绑定 `0.0.0.0` 时必须设置该环境变量) |
140
159
 
141
160
  ## 快速开始
142
161
 
data/README_JA.md CHANGED
@@ -110,18 +110,24 @@ gem install openclacky
110
110
 
111
111
  ### Docker
112
112
 
113
- ビルド:
113
+ #### ビルド済みイメージ(GHCR)
114
+
115
+ バージョンタグ(`v*`)が付くと GitHub Container Registry にイメージが自動公開されます。
114
116
 
115
117
  ```bash
116
- git clone https://github.com/clacky-ai/openclacky.git
117
- cd openclacky
118
- docker build -t openclacky .
118
+ # <owner> をリポジトリオーナーに置き換えてください(例: clacky-ai またはあなたのフォーク)
119
+ docker pull ghcr.io/<owner>/openclacky:latest
120
+ # またはバージョンを固定する場合:
121
+ # docker pull ghcr.io/<owner>/openclacky:1.5.3
119
122
  ```
120
123
 
121
124
  **Linux:**
122
125
 
123
126
  ```bash
124
- docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
127
+ docker run -d --name openclacky --network=host \
128
+ -e CLACKY_ACCESS_KEY="" \
129
+ -v openclacky-data:/root/.clacky \
130
+ ghcr.io/<owner>/openclacky:latest
125
131
  ```
126
132
 
127
133
  `--network=host` は、コンテナ内のエージェントがホスト上で動作する Chrome のリモートデバッグポートに到達するために必要です。
@@ -129,18 +135,31 @@ docker run -d --network=host -e CLACKY_ACCESS_KEY="" openclacky
129
135
  **macOS / Windows:**
130
136
 
131
137
  ```bash
132
- docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
138
+ docker run -d --name openclacky -p 7070:7070 \
139
+ -e CLACKY_ACCESS_KEY="" \
140
+ -v openclacky-data:/root/.clacky \
141
+ ghcr.io/<owner>/openclacky:latest
133
142
  ```
134
143
 
135
144
  > **注意:** macOS/Windows では `--network=host` がサポートされていないため、ブラウザの自動化が制限される場合があります。
136
145
 
137
146
  起動後、**http://localhost:7070** を開いてください。
138
147
 
148
+ #### ソースからビルド
149
+
150
+ ```bash
151
+ git clone https://github.com/clacky-ai/openclacky.git
152
+ cd openclacky
153
+ # オプション: OCI イメージバージョンラベルを設定
154
+ docker build --build-arg VERSION=1.5.3 -t openclacky .
155
+ docker run -d -p 7070:7070 -e CLACKY_ACCESS_KEY="" openclacky
156
+ ```
157
+
139
158
  環境変数:
140
159
 
141
160
  | 変数 | 説明 |
142
161
  |---|---|
143
- | `CLACKY_ACCESS_KEY` | アクセスキーで Web UI を保護します(空の場合はパブリックモード) |
162
+ | `CLACKY_ACCESS_KEY` | アクセスキーで Web UI を保護します(空の場合はパブリックモード;`0.0.0.0` にバインドする場合は必須) |
144
163
 
145
164
 
146
165
  ## クイックスタート
@@ -39,14 +39,10 @@ module Clacky
39
39
 
40
40
  REQUIRED RESPONSE FORMAT:
41
41
  First output a <topics> line listing 3-6 key topic phrases (comma-separated, concise).
42
- Then output a <continues_previous> line: "true" if this conversation is a direct
43
- continuation of the SAME task/topic as the PREVIOUS chunk shown below, "false" if it
44
- has moved on to a different task or topic. If there is no previous chunk, output "false".
45
42
  Then output the full summary wrapped in <summary> tags.
46
43
 
47
44
  Example format:
48
45
  <topics>Rails setup, database config, deploy pipeline, Tailwind CSS</topics>
49
- <continues_previous>false</continues_previous>
50
46
  <summary>
51
47
  ...full summary text...
52
48
  </summary>
@@ -58,8 +54,7 @@ module Clacky
58
54
  - Errors encountered and fixes applied
59
55
  - Current work status and pending tasks
60
56
 
61
- Begin your response NOW. Remember: PURE TEXT only, starting with <topics> then
62
- <continues_previous> then <summary>.
57
+ Begin your response NOW. Remember: PURE TEXT only, starting with <topics> then <summary>.
63
58
  PROMPT
64
59
 
65
60
  def initialize(client, model: nil)
@@ -77,9 +72,6 @@ module Clacky
77
72
  #
78
73
  # @param messages [Array<Hash>] Original conversation messages
79
74
  # @param recent_messages [Array<Hash>] Recent messages to keep uncompressed (optional)
80
- # @param previous_topics [String, nil] Topics of the most recent chunk on disk,
81
- # shown to the LLM so it can decide whether the current conversation is a
82
- # continuation (drives the <continues_previous> output for chunk merging).
83
75
  # @return [Hash] Compression instruction message to insert, or nil if nothing to compress
84
76
  def build_compression_message(messages, recent_messages: [], previous_topics: nil)
85
77
  # Get messages to compress (exclude system message and recent messages)
@@ -88,14 +80,9 @@ module Clacky
88
80
  # If nothing to compress, return nil
89
81
  return nil if messages_to_compress.empty?
90
82
 
91
- content = COMPRESSION_PROMPT
92
- if previous_topics && !previous_topics.strip.empty?
93
- content = "#{COMPRESSION_PROMPT}\n\nPREVIOUS CHUNK TOPICS (for <continues_previous> judgement): #{previous_topics}"
94
- end
95
-
96
83
  {
97
84
  role: "user",
98
- content: content,
85
+ content: COMPRESSION_PROMPT,
99
86
  system_injected: true
100
87
  }
101
88
  end
@@ -154,15 +141,6 @@ module Clacky
154
141
  m ? m[1].strip : nil
155
142
  end
156
143
 
157
- # Parse the <continues_previous> tag. Returns true only when the LLM
158
- # explicitly says "true"; missing tag or any other value → false.
159
- # This conservative default ensures we never merge unless the model is sure.
160
- def parse_continues_previous(content)
161
- return false if content.nil? || content.to_s.empty?
162
- m = content.to_s.match(/<continues_previous>(.*?)<\/continues_previous>/m)
163
- m ? m[1].strip.downcase == "true" : false
164
- end
165
-
166
144
  def parse_compressed_result(result, chunk_path: nil, topics: nil, previous_chunks: [])
167
145
  # Return the compressed result as a single user message (role: "user").
168
146
  #
@@ -192,11 +170,8 @@ module Clacky
192
170
  if content.empty?
193
171
  []
194
172
  else
195
- # Strip out the <topics> and <continues_previous> blocks they're
196
- # metadata for chunk handling, not for AI context.
197
- content_without_topics = content.gsub(/<topics>.*?<\/topics>\n*/m, "")
198
- .gsub(/<continues_previous>.*?<\/continues_previous>\n*/m, "")
199
- .strip
173
+ # Strip out the <topics> block it's metadata for chunk handling, not for AI context.
174
+ content_without_topics = content.gsub(/<topics>.*?<\/topics>\n*/m, "").strip
200
175
 
201
176
  # Build previous chunks index section — links to older chunk files so the AI
202
177
  # can find earlier conversations without keeping all prior compressed_summary
@@ -23,6 +23,7 @@ module Clacky
23
23
  Clacky::Logger.info(
24
24
  "Idle compression skipped",
25
25
  enable_compression: @config.enable_compression,
26
+ enable_idle_compression: @config.enable_idle_compression,
26
27
  previous_total_tokens: @previous_total_tokens,
27
28
  history_size: @history.size,
28
29
  idle_threshold: IDLE_COMPRESSION_THRESHOLD,
@@ -129,8 +130,10 @@ module Clacky
129
130
  # (force: true) is still allowed as a safety net for long-running subagents.
130
131
  return nil if @is_subagent && !force
131
132
 
132
- # Check if compression is enabled
133
+ # Check if compression is enabled (master switch)
133
134
  return nil unless @config.enable_compression
135
+ # Idle compression has its own sub-switch under the master
136
+ return nil if force && !@config.enable_idle_compression
134
137
 
135
138
  # Use the larger of: API-reported tokens from last response, or current
136
139
  # estimated history size. The estimate guards the case where a single
@@ -200,21 +203,19 @@ module Clacky
200
203
  recent_messages = get_recent_messages_with_tool_pairs(all_messages, target_recent_count)
201
204
  recent_messages = [] if recent_messages.nil?
202
205
 
203
- # Surface the most recent chunk's topics so the compression LLM can judge
204
- # whether this conversation continues the same task (drives chunk merging).
205
- previous_topics = nil
206
- if @session_id && @created_at
207
- latest = session_manager.chunks_for_current(@session_id, @created_at).last
208
- previous_topics = latest && latest[:topics]
209
- end
210
-
211
206
  # Build compression instruction message (to be inserted into conversation)
212
207
  compression_message = @message_compressor.build_compression_message(
213
- all_messages, recent_messages: recent_messages, previous_topics: previous_topics
208
+ all_messages, recent_messages: recent_messages
214
209
  )
215
210
 
216
211
  return nil if compression_message.nil?
217
212
 
213
+ # Active (non-forced) compression means we've accumulated enough
214
+ # conversation to hit the normal thresholds — always create a new chunk.
215
+ # Idle / context-overflow compressions (force: true) always merge into
216
+ # the previous chunk to avoid fragmenting small batches.
217
+ force_new_chunk = !force
218
+
218
219
  # Return compression context for agent to handle
219
220
  {
220
221
  compression_message: compression_message,
@@ -222,7 +223,8 @@ module Clacky
222
223
  pulled_back_messages: pulled_back_messages,
223
224
  original_token_count: total_tokens,
224
225
  original_message_count: @history.size,
225
- compression_level: @compression_level
226
+ compression_level: @compression_level,
227
+ force_new_chunk: force_new_chunk
226
228
  }
227
229
  end
228
230
 
@@ -265,12 +267,11 @@ module Clacky
265
267
  topics = @message_compressor.parse_topics(compressed_content)
266
268
 
267
269
  # Decide whether to MERGE into the previous chunk or create a NEW one.
268
- # The LLM judges (via <continues_previous>) whether this conversation is a
269
- # direct continuation of the previous chunk's task. Merging avoids tiny
270
- # fragmented chunks (e.g. a long task compressed mid-flight into 2-message
271
- # chunks) that pollute the topics index and degrade recall.
270
+ # Active compression (force_new_chunk: true) always creates a new chunk.
271
+ # Idle / overflow compression always merges into the previous chunk if
272
+ # one exists, avoiding tiny fragmented chunks that pollute the topics index.
272
273
  latest_chunk = existing_chunks.last
273
- continues = latest_chunk && @message_compressor.parse_continues_previous(compressed_content)
274
+ continues = latest_chunk && !compression_context[:force_new_chunk]
274
275
 
275
276
  if continues
276
277
  chunk_path = merge_into_previous_chunk(
@@ -11,6 +11,7 @@ module Clacky
11
11
  @session_id = session_data[:session_id]
12
12
  @name = session_data[:name] || ""
13
13
  @pinned = session_data[:pinned] || false
14
+ @hidden = session_data[:hidden] || false
14
15
  @history = MessageHistory.new(session_data[:messages] || [])
15
16
  @todos = session_data[:todos] || [] # Restore todos from session
16
17
  @iterations = session_data.dig(:stats, :total_iterations) || 0
@@ -29,6 +30,9 @@ module Clacky
29
30
  # Restore channel info for IM platform sessions
30
31
  @channel_info = session_data[:channel_info]
31
32
 
33
+ # Restore project association (nil = no project)
34
+ @project_id = session_data[:project_id]
35
+
32
36
  # Restore cache statistics if available
33
37
  @cache_stats = session_data.dig(:stats, :cache_stats) || {
34
38
  cache_creation_input_tokens: 0,
@@ -200,6 +204,7 @@ module Clacky
200
204
  session_id: @session_id,
201
205
  name: @name,
202
206
  pinned: @pinned,
207
+ hidden: @hidden,
203
208
  created_at: @created_at,
204
209
  updated_at: (updated_at || (preserve_updated_at && @persisted_updated_at) || Time.now.iso8601).then { |v| v.is_a?(String) ? v : v.iso8601 },
205
210
  working_dir: @working_dir,
@@ -219,6 +224,7 @@ module Clacky
219
224
  # (falling back to the current default if the model no longer exists).
220
225
  permission_mode: @config.permission_mode.to_s,
221
226
  enable_compression: @config.enable_compression,
227
+ enable_idle_compression: @config.enable_idle_compression,
222
228
  enable_prompt_caching: @config.enable_prompt_caching,
223
229
  max_tokens: @config.max_tokens,
224
230
  verbose: @config.verbose,
@@ -234,6 +240,7 @@ module Clacky
234
240
  sub_model: @config.session_model_overlay_name
235
241
  },
236
242
  channel_info: @channel_info,
243
+ project_id: @project_id,
237
244
  stats: stats_data,
238
245
  messages: @history.to_a
239
246
  }
data/lib/clacky/agent.rb CHANGED
@@ -49,9 +49,11 @@ module Clacky
49
49
  :latest_latency, # Hash of latency metrics from the most recent LLM call (see Client#send_messages_with_tools)
50
50
  :reasoning_effort
51
51
  attr_accessor :pinned
52
+ attr_accessor :hidden
52
53
  attr_accessor :channel_info
54
+ attr_accessor :project_id
53
55
 
54
- REASONING_EFFORTS = %w[low medium high xhigh].freeze
56
+ REASONING_EFFORTS = %w[low medium high xhigh max].freeze
55
57
 
56
58
  def permission_mode
57
59
  @config&.permission_mode&.to_s || ""
@@ -82,6 +84,7 @@ module Clacky
82
84
  @session_id = session_id
83
85
  @name = ""
84
86
  @pinned = false
87
+ @hidden = false
85
88
  @history = MessageHistory.new
86
89
  @todos = [] # Store todos in memory
87
90
  @iterations = 0
@@ -435,8 +438,9 @@ module Clacky
435
438
  lines << "Parse failed: #{parse_error}"
436
439
  if parser_path
437
440
  expected_preview = "#{path}.preview.md"
441
+ interp = Utils::ParserManager.interpreter_for(File.basename(parser_path))
438
442
  lines << "Action required: fix the parser at #{parser_path}, then run:"
439
- lines << " ruby #{parser_path} #{path} > #{expected_preview}"
443
+ lines << " #{interp} #{parser_path} #{path} > #{expected_preview}"
440
444
  lines << "Once done, read #{expected_preview} to continue helping the user."
441
445
  end
442
446
  end
@@ -1005,6 +1009,14 @@ module Clacky
1005
1009
  # Inject working_dir so tools don't rely on Dir.chdir global state
1006
1010
  args[:working_dir] = @working_dir if @working_dir
1007
1011
 
1012
+ # Inject CLACKY_SESSION_ID into every terminal invocation so skills/system
1013
+ # prompts can reliably curl our own HTTP API (e.g. POST /api/ui/show_ext_refresh)
1014
+ # without the AI needing to know or guess the session id. Merges with (and never
1015
+ # overrides) any env the AI explicitly passed.
1016
+ if call[:name] == "terminal" && @session_id
1017
+ args[:env] = { "CLACKY_SESSION_ID" => @session_id.to_s }.merge(args[:env] || {})
1018
+ end
1019
+
1008
1020
  # For terminal: stream live stdout chunks to the UI as they arrive,
1009
1021
  # so the user sees real-time output (e.g. build logs) instead of a
1010
1022
  # blank spinner. The UI buffers lines for Ctrl+O fullscreen view
@@ -159,19 +159,21 @@ module Clacky
159
159
  DEFAULT_MESSAGE_COUNT_THRESHOLD = 200
160
160
 
161
161
  attr_accessor :permission_mode, :max_tokens, :verbose,
162
- :enable_compression, :enable_prompt_caching,
162
+ :enable_compression, :enable_idle_compression, :enable_prompt_caching,
163
163
  :compression_threshold, :message_count_threshold,
164
164
  :models, :current_model_index, :current_model_id,
165
165
  :memory_update_enabled, :skill_evolution,
166
166
  :max_running_agents, :max_idle_agents,
167
167
  :default_working_dir,
168
- :proxy_url
168
+ :proxy_url,
169
+ :clacky_license_server
169
170
 
170
171
  def initialize(options = {})
171
172
  @permission_mode = validate_permission_mode(options[:permission_mode])
172
173
  @max_tokens = options[:max_tokens] || 16384
173
174
  @verbose = options[:verbose] || false
174
175
  @enable_compression = options[:enable_compression].nil? ? true : options[:enable_compression]
176
+ @enable_idle_compression = options[:enable_idle_compression].nil? ? true : options[:enable_idle_compression]
175
177
  # Enable prompt caching by default for cost savings
176
178
  @enable_prompt_caching = options[:enable_prompt_caching].nil? ? true : options[:enable_prompt_caching]
177
179
  # Token threshold that triggers proactive history compression. Local models
@@ -222,6 +224,7 @@ module Clacky
222
224
  # ignored — set proxy_url here to route Clacky's outbound HTTP through
223
225
  # a proxy. Leave nil to go direct.
224
226
  @proxy_url = options[:proxy_url]
227
+ @clacky_license_server = options[:clacky_license_server]
225
228
 
226
229
  # Per-session virtual model overlay.
227
230
  # When set, #current_model returns a *merged* hash (the resolved @models
@@ -409,12 +412,12 @@ module Clacky
409
412
  # Settings keys that are persisted to config.yml.
410
413
  # These map directly to AgentConfig accessors.
411
414
  CONFIG_SETTINGS_KEYS = %w[
412
- enable_compression enable_prompt_caching
415
+ enable_compression enable_idle_compression enable_prompt_caching
413
416
  compression_threshold message_count_threshold
414
417
  memory_update_enabled
415
418
  skill_evolution max_running_agents max_idle_agents
416
419
  default_working_dir
417
- proxy_url
420
+ proxy_url clacky_license_server
418
421
  ].freeze
419
422
 
420
423
  # Serialize the current agent configuration to YAML.
@@ -426,6 +429,7 @@ module Clacky
426
429
  end
427
430
  settings = {
428
431
  "enable_compression" => @enable_compression,
432
+ "enable_idle_compression" => @enable_idle_compression,
429
433
  "enable_prompt_caching" => @enable_prompt_caching,
430
434
  "compression_threshold" => @compression_threshold,
431
435
  "message_count_threshold" => @message_count_threshold,
@@ -434,7 +438,8 @@ module Clacky
434
438
  "max_running_agents" => @max_running_agents,
435
439
  "max_idle_agents" => @max_idle_agents,
436
440
  "default_working_dir" => @default_working_dir,
437
- "proxy_url" => @proxy_url
441
+ "proxy_url" => @proxy_url,
442
+ "clacky_license_server" => @clacky_license_server
438
443
  }
439
444
  YAML.dump("settings" => settings, "models" => persistable_models)
440
445
  end