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.
- checksums.yaml +4 -4
- data/.dockerignore +13 -0
- data/CHANGELOG.md +57 -0
- data/Dockerfile +21 -1
- data/README.md +26 -7
- data/README_CN.md +26 -7
- data/README_JA.md +26 -7
- data/lib/clacky/agent/message_compressor.rb +4 -29
- data/lib/clacky/agent/message_compressor_helper.rb +17 -16
- data/lib/clacky/agent/session_serializer.rb +7 -0
- data/lib/clacky/agent.rb +14 -2
- data/lib/clacky/agent_config.rb +10 -5
- data/lib/clacky/agent_profile.rb +25 -3
- data/lib/clacky/brand_config.rb +32 -12
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +15 -0
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +1 -5
- data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -2
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +43 -34
- data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +9 -3
- data/lib/clacky/default_extensions/git/ext.yml +3 -0
- data/lib/clacky/default_extensions/meeting/ext.yml +3 -0
- data/lib/clacky/default_extensions/time_machine/ext.yml +4 -0
- data/lib/clacky/default_parsers/xlsx_parser.py +66 -0
- data/lib/clacky/default_skills/new/SKILL.md +17 -12
- data/lib/clacky/extension/api_extension.rb +5 -2
- data/lib/clacky/extension/loader.rb +1 -0
- data/lib/clacky/extension/packager.rb +60 -15
- data/lib/clacky/extension/verifier.rb +1 -1
- data/lib/clacky/message_format/anthropic.rb +1 -1
- data/lib/clacky/message_format/bedrock.rb +1 -1
- data/lib/clacky/message_format/open_ai.rb +34 -3
- data/lib/clacky/providers.rb +77 -8
- data/lib/clacky/server/http_server.rb +495 -39
- data/lib/clacky/server/project_manager.rb +150 -0
- data/lib/clacky/server/server_master.rb +7 -0
- data/lib/clacky/server/session_registry.rb +14 -1
- data/lib/clacky/server/web_ui_controller.rb +12 -2
- data/lib/clacky/session_manager.rb +6 -4
- data/lib/clacky/tools/file_reader.rb +2 -1
- data/lib/clacky/tools/terminal.rb +30 -3
- data/lib/clacky/utils/model_pricing.rb +69 -18
- data/lib/clacky/utils/parser_manager.rb +115 -7
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +1216 -318
- data/lib/clacky/web/app.js +27 -4
- data/lib/clacky/web/components/notify.js +139 -22
- data/lib/clacky/web/core/aside.js +21 -0
- data/lib/clacky/web/features/brand/store.js +3 -1
- data/lib/clacky/web/features/brand/view.js +29 -5
- data/lib/clacky/web/features/extensions/store.js +214 -9
- data/lib/clacky/web/features/extensions/view.js +156 -11
- data/lib/clacky/web/features/new-session/store.js +25 -3
- data/lib/clacky/web/features/new-session/view.js +339 -142
- data/lib/clacky/web/features/skills/view.js +1 -1
- data/lib/clacky/web/features/trash/store.js +23 -9
- data/lib/clacky/web/features/trash/view.js +51 -0
- data/lib/clacky/web/i18n.js +124 -15
- data/lib/clacky/web/index.html +105 -79
- data/lib/clacky/web/projects.js +1143 -0
- data/lib/clacky/web/sessions.js +320 -538
- data/lib/clacky/web/settings.js +138 -5
- data/lib/clacky/web/utils.js +1 -0
- data/lib/clacky/web/ws-dispatcher.js +98 -4
- data/scripts/build/src/install_system_deps.sh.cc +11 -3
- data/scripts/install_system_deps.sh +11 -3
- metadata +5 -2
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5803d9775474800f6f61d6771837ebe90597080e0f068a9dff49d5d44fdc90d1
|
|
4
|
+
data.tar.gz: f111f2b083635876cd2e4477e5a64da14245684c5c18a5f5f23860541ac9def0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a0cc2a8397b5165b941a5aec4f1fc72770d47fdee4b24b72d862b12154d6a9b8114563b2e977d64e43a39c86d278fb5bbb97b045357c0a00c2f818f6466dbbf8
|
|
7
|
+
data.tar.gz: '07913be97ae9966164649f88a8e8abc60346c80038aec65886c51d9552e75eaf1050e3a9cc6fc6e28d1137d00d9fabcf735a8ac195bc3c5554890a6a8c0a073d'
|
data/.dockerignore
ADDED
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
|
-
|
|
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
|
-
|
|
113
|
+
#### Pre-built image (GHCR)
|
|
114
|
+
|
|
115
|
+
Images are published to GitHub Container Registry on version tags (`v*`).
|
|
114
116
|
|
|
115
117
|
```bash
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
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
|
|
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:
|
|
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>
|
|
196
|
-
|
|
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
|
|
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
|
-
#
|
|
269
|
-
#
|
|
270
|
-
#
|
|
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 &&
|
|
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 << "
|
|
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
|
data/lib/clacky/agent_config.rb
CHANGED
|
@@ -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
|