openclacky 1.0.0.beta.1 → 1.0.0.beta.3
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/.clacky/skills/gem-release/SKILL.md +67 -13
- data/CHANGELOG.md +25 -0
- data/lib/clacky/agent/cost_tracker.rb +14 -7
- data/lib/clacky/agent/session_serializer.rb +5 -0
- data/lib/clacky/agent.rb +13 -0
- data/lib/clacky/agent_config.rb +44 -3
- data/lib/clacky/cli.rb +21 -0
- data/lib/clacky/json_ui_controller.rb +1 -1
- data/lib/clacky/message_format/bedrock.rb +18 -6
- data/lib/clacky/message_history.rb +8 -0
- data/lib/clacky/plain_ui_controller.rb +1 -1
- data/lib/clacky/providers.rb +13 -6
- data/lib/clacky/server/channel/channel_ui_controller.rb +6 -2
- data/lib/clacky/server/http_server.rb +223 -93
- data/lib/clacky/server/session_registry.rb +5 -1
- data/lib/clacky/server/web_ui_controller.rb +4 -2
- data/lib/clacky/skill_loader.rb +6 -12
- data/lib/clacky/tools/terminal.rb +46 -3
- data/lib/clacky/ui2/ui_controller.rb +1 -1
- data/lib/clacky/ui_interface.rb +1 -1
- data/lib/clacky/utils/file_processor.rb +188 -15
- data/lib/clacky/utils/model_pricing.rb +18 -31
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.js +88 -15
- data/lib/clacky/web/index.html +2 -2
- data/lib/clacky/web/sessions.js +39 -16
- data/lib/clacky/web/settings.js +128 -49
- data/scripts/install.ps1 +20 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b145934170a510f46e3263c3fbce94acf618cdd416c93e17a7758984361c02b7
|
|
4
|
+
data.tar.gz: 618fdf4917ce68514e0332ad44c522afa061a21884d6e95511f564da985f8433
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50a63fc4087f97c9a3c3242e2e379da95de6e73cdac3bb75ab11ad67bc03eb151afaf47e3a229bd769a4790f57e3b116309c0908807f35618ae64222feb30575
|
|
7
|
+
data.tar.gz: f492569e101a0b6af312c65cffa244b29a5c47d79201129214e66ac62db7181c29678d64c9ddec88e6dc61525cf2442b0f22647e8dcdb430e0bbc87ca9f1a370
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
name: gem-release
|
|
4
4
|
description: >-
|
|
5
5
|
Automates the complete process of releasing a new version of the openclacky Ruby
|
|
6
|
-
gem
|
|
6
|
+
gem. Supports both stable releases (auto-increment) and pre-release versions
|
|
7
|
+
(user-specified, e.g., 1.0.0.beta.1). Handles version bumping, testing, building,
|
|
8
|
+
RubyGems publishing, GitHub Releases, and OSS CDN mirroring.
|
|
7
9
|
disable-model-invocation: false
|
|
8
10
|
user-invocable: true
|
|
9
11
|
---
|
|
@@ -21,6 +23,7 @@ This skill handles the entire gem release workflow from version bumping to publi
|
|
|
21
23
|
To use this skill, simply say:
|
|
22
24
|
- "Release a new version"
|
|
23
25
|
- "Publish a new gem version"
|
|
26
|
+
- "Release version 1.0.0.beta.1" (pre-release with explicit version)
|
|
24
27
|
- Use the command: `/gem-release`
|
|
25
28
|
|
|
26
29
|
## Process Steps
|
|
@@ -31,10 +34,30 @@ To use this skill, simply say:
|
|
|
31
34
|
- Ensure the repository is in a clean state
|
|
32
35
|
|
|
33
36
|
### 2. Version Management
|
|
37
|
+
|
|
38
|
+
**Stable releases (default):**
|
|
34
39
|
- Read current version from `lib/clacky/version.rb`
|
|
35
40
|
- Increment version number (typically patch version: x.y.z → x.y.z+1)
|
|
36
41
|
- Update the VERSION constant in the version file
|
|
37
42
|
|
|
43
|
+
**Pre-release versions (when user specifies a version like `1.0.0.beta.1`):**
|
|
44
|
+
- Accept the user-provided version string directly — do NOT auto-increment
|
|
45
|
+
- The version must follow semver pre-release format: `X.Y.Z-<identifier>` or `X.Y.Z.<identifier>` (e.g., `1.0.0.beta.1`, `2.0.0-alpha`, `1.5.0-rc1`)
|
|
46
|
+
- Before proceeding, warn the user about pre-release caveats (see Pre-Release Caveats below)
|
|
47
|
+
|
|
48
|
+
### 2a. Pre-Release Caveats
|
|
49
|
+
|
|
50
|
+
When releasing a pre-release version, inform the user of these known behaviors in the Clacky ecosystem:
|
|
51
|
+
|
|
52
|
+
| Concern | Behavior | Impact |
|
|
53
|
+
|---------|----------|--------|
|
|
54
|
+
| **Version check notification** | RubyGems API returns the highest version number, including prereleases. `Gem::Version("0.9.38") < Gem::Version("1.0.0.beta.1")` → `true`. | ✅ The upgrade dot WILL appear in the Web UI for most users. |
|
|
55
|
+
| **`gem update` (official source)** | `gem update openclacky --no-document` does NOT install prereleases without `--pre`. | ❌ Users on official RubyGems source who click "Upgrade" will see the notification but the upgrade will silently do nothing. |
|
|
56
|
+
| **OSS CDN upgrade (mirror users)** | `upgrade_via_oss_cdn` downloads the exact `.gem` from `latest.txt` on OSS. | ⚠️ If you update `latest.txt` to point to the prerelease, mirror users WILL get the beta. |
|
|
57
|
+
| **OSS `latest.txt`** | Stable users fetching `latest.txt` for fresh installs would get the beta. | ⚠️ By default, do NOT update `latest.txt` for pre-releases. Only update if this is intentional (e.g., a release candidate for broad testing). |
|
|
58
|
+
|
|
59
|
+
**Action**: Ask the user whether to update `latest.txt` on OSS before proceeding. For internal testing, the answer is usually "no".
|
|
60
|
+
|
|
38
61
|
### 3. Quality Assurance
|
|
39
62
|
- Run the full test suite with `bundle exec rspec`
|
|
40
63
|
- Ensure all 167+ tests pass
|
|
@@ -93,15 +116,26 @@ To use this skill, simply say:
|
|
|
93
116
|
|
|
94
117
|
4. **Create GitHub Release and Upload gem**
|
|
95
118
|
|
|
96
|
-
Extract the release notes for this version from CHANGELOG.md, then create a GitHub Release with the .gem file attached
|
|
119
|
+
Extract the release notes for this version from CHANGELOG.md, then create a GitHub Release with the .gem file attached.
|
|
120
|
+
|
|
121
|
+
**For stable releases:**
|
|
97
122
|
```bash
|
|
98
123
|
gh release create v{version} \
|
|
99
124
|
--title "v{version}" \
|
|
100
|
-
--notes-file /tmp/
|
|
125
|
+
--notes-file /tmp/release_notes_{version}.md \
|
|
101
126
|
--latest \
|
|
102
127
|
openclacky-{version}.gem
|
|
103
128
|
```
|
|
104
129
|
|
|
130
|
+
**For pre-release versions (e.g., `1.0.0.beta.1`):** use `--prerelease` instead of `--latest`:
|
|
131
|
+
```bash
|
|
132
|
+
gh release create v{version} \
|
|
133
|
+
--title "v{version}" \
|
|
134
|
+
--notes-file /tmp/release_notes_{version}.md \
|
|
135
|
+
--prerelease \
|
|
136
|
+
openclacky-{version}.gem
|
|
137
|
+
```
|
|
138
|
+
|
|
105
139
|
Steps:
|
|
106
140
|
- Parse the CHANGELOG.md section for `[{version}]`
|
|
107
141
|
- Write it to a temp file (e.g., `/tmp/release_notes_{version}.md`) to avoid shell escaping issues
|
|
@@ -112,22 +146,28 @@ To use this skill, simply say:
|
|
|
112
146
|
|
|
113
147
|
5. **Sync to Tencent Cloud OSS (CN mirror)**
|
|
114
148
|
|
|
115
|
-
After GitHub Release is created, upload the .gem file
|
|
149
|
+
After GitHub Release is created, upload the .gem file to OSS so Chinese users can install without hitting GitHub directly.
|
|
116
150
|
|
|
117
151
|
```bash
|
|
118
|
-
# Upload .gem file
|
|
152
|
+
# Upload .gem file (always do this for any release)
|
|
119
153
|
coscli cp openclacky-{version}.gem cos://clackyai-1258723534/openclacky/openclacky-{version}.gem
|
|
154
|
+
```
|
|
120
155
|
|
|
121
|
-
|
|
156
|
+
**For stable releases only** — update `latest.txt` so fresh installs and mirror users pick up the new version:
|
|
157
|
+
```bash
|
|
122
158
|
echo "{version}" > /tmp/latest.txt
|
|
123
159
|
coscli cp /tmp/latest.txt cos://clackyai-1258723534/openclacky/latest.txt
|
|
124
160
|
|
|
125
161
|
# Verify
|
|
126
162
|
curl -fsSL https://oss.1024code.com/openclacky/latest.txt
|
|
127
163
|
```
|
|
128
|
-
|
|
129
164
|
Expected output of verify: `{version}`
|
|
130
165
|
|
|
166
|
+
**For pre-release versions** — do NOT update `latest.txt` unless the user explicitly requested it. Updating `latest.txt` to a prerelease would cause:
|
|
167
|
+
- Mirror users clicking "Upgrade" to get the beta via `upgrade_via_oss_cdn`
|
|
168
|
+
- Fresh installs via the install script to get the beta
|
|
169
|
+
- Only skip this if the user explicitly wants broad beta distribution
|
|
170
|
+
|
|
131
171
|
> **Prerequisite**: `coscli` installed at `/usr/local/bin/coscli` and configured at `~/.cos.yaml`
|
|
132
172
|
|
|
133
173
|
6. **Sync scripts/ to OSS**
|
|
@@ -325,22 +365,34 @@ git tag vX.Y.Z
|
|
|
325
365
|
git push origin main
|
|
326
366
|
git push origin --tags
|
|
327
367
|
|
|
328
|
-
#
|
|
329
|
-
|
|
330
|
-
#
|
|
331
|
-
# 3. Create the release and attach .gem file
|
|
368
|
+
# ── GitHub Release ──────────────────────────────────────────────────────
|
|
369
|
+
|
|
370
|
+
# Stable release:
|
|
332
371
|
gh release create vX.Y.Z \
|
|
333
372
|
--title "vX.Y.Z" \
|
|
334
373
|
--notes-file /tmp/release_notes_X.Y.Z.md \
|
|
335
374
|
--latest \
|
|
336
375
|
openclacky-X.Y.Z.gem
|
|
337
376
|
|
|
338
|
-
#
|
|
377
|
+
# Pre-release (use --prerelease instead of --latest):
|
|
378
|
+
gh release create vX.Y.Z-beta.1 \
|
|
379
|
+
--title "vX.Y.Z-beta.1" \
|
|
380
|
+
--notes-file /tmp/release_notes_X.Y.Z-beta.1.md \
|
|
381
|
+
--prerelease \
|
|
382
|
+
openclacky-X.Y.Z.beta.1.gem
|
|
383
|
+
|
|
384
|
+
# ── OSS CDN (CN mirror) ─────────────────────────────────────────────────
|
|
385
|
+
|
|
386
|
+
# Always upload the .gem file:
|
|
339
387
|
coscli cp openclacky-X.Y.Z.gem cos://clackyai-1258723534/openclacky/openclacky-X.Y.Z.gem
|
|
388
|
+
|
|
389
|
+
# Stable releases ONLY — update latest.txt:
|
|
340
390
|
echo "X.Y.Z" > /tmp/latest.txt
|
|
341
391
|
coscli cp /tmp/latest.txt cos://clackyai-1258723534/openclacky/latest.txt
|
|
342
392
|
curl -fsSL https://oss.1024code.com/openclacky/latest.txt # verify
|
|
343
393
|
|
|
394
|
+
# Pre-releases — skip latest.txt update unless user explicitly requests it
|
|
395
|
+
|
|
344
396
|
# Sync scripts/ to OSS (build from templates first)
|
|
345
397
|
bash scripts/build/build.sh
|
|
346
398
|
for script in scripts/*; do
|
|
@@ -365,8 +417,10 @@ curl -fsSL https://oss.1024code.com/clacky-ai/openclacky/main/scripts/install.sh
|
|
|
365
417
|
- Git repository updated with version tag
|
|
366
418
|
- CHANGELOG.md updated with release notes
|
|
367
419
|
- GitHub Release created with .gem file attached at https://github.com/clacky-ai/openclacky/releases
|
|
420
|
+
- Use `--latest` for stable releases, `--prerelease` for pre-releases
|
|
368
421
|
- .gem file uploaded to OSS: https://oss.1024code.com/openclacky/openclacky-{version}.gem
|
|
369
|
-
- latest.txt updated on OSS: https://oss.1024code.com/openclacky/latest.txt returns the new version
|
|
422
|
+
- For stable releases: `latest.txt` updated on OSS: https://oss.1024code.com/openclacky/latest.txt returns the new version
|
|
423
|
+
- For pre-releases: `latest.txt` NOT updated (unless user explicitly opts in)
|
|
370
424
|
- No build or deployment errors
|
|
371
425
|
- User-facing release summary presented at the end
|
|
372
426
|
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.0.beta.3] - 2026-04-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Gemini 2.5 Pro support.** The new `gemini2.5-pro` model is now available as a selectable option, giving you access to Google's latest flagship model.
|
|
14
|
+
- **File attachments now support Markdown, plain text, and `.tar.gz` archives.** When you attach `.md`, `.txt`, or `.tar.gz` files to a session, the agent can read and reason over their contents directly.
|
|
15
|
+
- **Image type auto-detection.** Image files are now correctly identified by their binary content (magic bytes), not just their file extension — preventing misclassified images from causing upload or vision errors.
|
|
16
|
+
|
|
17
|
+
### Improved
|
|
18
|
+
- **Settings page fully revamped.** The Web UI Settings panel now saves configuration correctly and exposes a richer set of options for managing providers, models, and API keys.
|
|
19
|
+
- **Skills no longer have a 50-item cap.** The skill loader previously limited the results list to 50 entries; that cap has been removed so all available skills show up.
|
|
20
|
+
- **Cost tracking no longer requires a hard-coded price list.** Model pricing is now resolved dynamically, so new models show real cost figures instead of falling back to a default.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Terminal tool no longer crashes on non-UTF-8 output.** Commands that emit binary or non-UTF-8 bytes (e.g. compiled output, legacy scripts) no longer raise an encoding error in the terminal tool.
|
|
24
|
+
|
|
25
|
+
## [1.0.0.beta.2] - 2026-04-27
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **New session creation supports model & working-directory options.** The Web UI "new session" dialog now lets you pick the model and starting directory up front, instead of having to adjust them after the session opens.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **System prompt now refreshes when you switch models.** Previously the system prompt captured at session start stuck around even after `/model` or `/provider` switches, which could leave model-specific instructions out of sync. The agent now re-injects the correct system prompt on every model change.
|
|
32
|
+
- **Port 7070 properly released when the terminal tool exits.** A lingering listener on port 7070 could block subsequent runs; the terminal tool now cleans it up on shutdown.
|
|
33
|
+
- **Windows installer uses `[IO.Path]::GetTempPath()` for the temp directory** (#58) — more reliable than `$env:TEMP` on systems where the env var is unset or points to a non-ASCII path.
|
|
34
|
+
|
|
10
35
|
## [1.0.0.beta.1] - 2026-04-26
|
|
11
36
|
|
|
12
37
|
### Added
|
|
@@ -24,15 +24,22 @@ module Clacky
|
|
|
24
24
|
cost = result[:cost]
|
|
25
25
|
pricing_source = result[:source]
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
# Only accumulate cost when the model has known pricing.
|
|
28
|
+
# Unknown models return nil — display N/A, don't add to total.
|
|
29
|
+
if cost
|
|
30
|
+
@total_cost += cost
|
|
31
|
+
iteration_cost = cost
|
|
32
|
+
@cost_source = pricing_source
|
|
33
|
+
@task_cost_source = pricing_source
|
|
34
|
+
end
|
|
32
35
|
|
|
33
36
|
if @config.verbose
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if cost
|
|
38
|
+
source_label = pricing_source == :price ? "model pricing" : "default pricing"
|
|
39
|
+
@ui&.log("Calculated cost for #{@config.model_name} using #{source_label}: $#{cost.round(6)}", level: :debug)
|
|
40
|
+
else
|
|
41
|
+
@ui&.log("No pricing data available for #{@config.model_name} — cost is unknown", level: :debug)
|
|
42
|
+
end
|
|
36
43
|
@ui&.log("Usage breakdown: prompt=#{usage[:prompt_tokens]}, completion=#{usage[:completion_tokens]}, cache_write=#{usage[:cache_creation_input_tokens] || 0}, cache_read=#{usage[:cache_read_input_tokens] || 0}", level: :debug)
|
|
37
44
|
end
|
|
38
45
|
end
|
|
@@ -18,6 +18,10 @@ module Clacky
|
|
|
18
18
|
@working_dir = session_data[:working_dir]
|
|
19
19
|
@created_at = session_data[:created_at]
|
|
20
20
|
@total_tasks = session_data.dig(:stats, :total_tasks) || 0
|
|
21
|
+
# Restore cost_source so frontend knows if cost is reliable
|
|
22
|
+
cost_src = session_data.dig(:stats, :cost_source)
|
|
23
|
+
@cost_source = (cost_src && cost_src.to_sym) || :estimated
|
|
24
|
+
@task_cost_source = :estimated
|
|
21
25
|
# Restore source; fall back to :manual for sessions saved before this field existed
|
|
22
26
|
@source = (session_data[:source] || "manual").to_sym
|
|
23
27
|
|
|
@@ -65,6 +69,7 @@ module Clacky
|
|
|
65
69
|
total_tasks: @total_tasks,
|
|
66
70
|
total_iterations: @iterations,
|
|
67
71
|
total_cost_usd: @total_cost.round(4),
|
|
72
|
+
cost_source: @cost_source.to_s,
|
|
68
73
|
duration_seconds: @start_time ? (Time.now - @start_time).round(2) : 0,
|
|
69
74
|
last_status: status.to_s,
|
|
70
75
|
cache_stats: @cache_stats,
|
data/lib/clacky/agent.rb
CHANGED
|
@@ -477,6 +477,7 @@ module Clacky
|
|
|
477
477
|
@ui&.show_complete(
|
|
478
478
|
iterations: result[:iterations],
|
|
479
479
|
cost: result[:total_cost_usd],
|
|
480
|
+
cost_source: result[:cost_source],
|
|
480
481
|
duration: result[:duration_seconds],
|
|
481
482
|
cache_stats: result[:cache_stats],
|
|
482
483
|
awaiting_user_feedback: awaiting_user_feedback
|
|
@@ -1371,7 +1372,19 @@ module Clacky
|
|
|
1371
1372
|
# Core method to inject session context (date, model, OS, paths).
|
|
1372
1373
|
# Called by inject_session_context_if_needed (with date check)
|
|
1373
1374
|
# and by switch_model (without date check, to force update).
|
|
1375
|
+
#
|
|
1376
|
+
# IMPORTANT: Skip injection when the system prompt hasn't been built yet.
|
|
1377
|
+
# Otherwise, appending a user message to an empty history makes
|
|
1378
|
+
# @history.empty? false, which causes run() to skip building the
|
|
1379
|
+
# system prompt entirely (see run()'s "first run" guard).
|
|
1380
|
+
# The injection will happen naturally in run() via
|
|
1381
|
+
# inject_session_context_if_needed after the system prompt is in place.
|
|
1374
1382
|
private def inject_session_context
|
|
1383
|
+
# Don't inject context before system prompt exists — defer to
|
|
1384
|
+
# inject_session_context_if_needed which runs inside run()
|
|
1385
|
+
# after the system prompt has been built.
|
|
1386
|
+
return unless @history.has_system_prompt?
|
|
1387
|
+
|
|
1375
1388
|
today = Time.now.strftime("%Y-%m-%d")
|
|
1376
1389
|
os = Clacky::Utils::EnvironmentDetector.os_type
|
|
1377
1390
|
desktop = Clacky::Utils::EnvironmentDetector.desktop_path
|
data/lib/clacky/agent_config.rb
CHANGED
|
@@ -190,6 +190,10 @@ module Clacky
|
|
|
190
190
|
auto_create_threshold: 12,
|
|
191
191
|
reflection_mode: "llm_analysis"
|
|
192
192
|
}
|
|
193
|
+
# Deep-symbolize keys — YAML-loaded hashes come with string keys,
|
|
194
|
+
# but the rest of the codebase accesses with symbols.
|
|
195
|
+
@skill_evolution = @skill_evolution.transform_keys(&:to_sym)
|
|
196
|
+
@skill_evolution.transform_values! { |v| v.is_a?(Hash) ? v.transform_keys(&:to_sym) : v }
|
|
193
197
|
|
|
194
198
|
# Per-session virtual model overlay.
|
|
195
199
|
# When set, #current_model returns a *merged* hash (the resolved @models
|
|
@@ -211,6 +215,13 @@ module Clacky
|
|
|
211
215
|
data = nil
|
|
212
216
|
end
|
|
213
217
|
|
|
218
|
+
# Extract settings from hash-format config (new format).
|
|
219
|
+
# Old flat-array configs have no settings section — all defaults.
|
|
220
|
+
loaded_settings = {}
|
|
221
|
+
if data.is_a?(Hash) && data["settings"].is_a?(Hash)
|
|
222
|
+
loaded_settings = data["settings"]
|
|
223
|
+
end
|
|
224
|
+
|
|
214
225
|
# Parse models from config
|
|
215
226
|
models = parse_models(data)
|
|
216
227
|
|
|
@@ -278,7 +289,21 @@ module Clacky
|
|
|
278
289
|
default_index = models.find_index { |m| m["type"] == "default" } || 0
|
|
279
290
|
default_id = models[default_index] && models[default_index]["id"]
|
|
280
291
|
|
|
281
|
-
|
|
292
|
+
# Build constructor args from loaded settings (new hash-format config)
|
|
293
|
+
# plus the parsed models. Only pass settings that have explicit values;
|
|
294
|
+
# omitted keys get their default from AgentConfig#initialize.
|
|
295
|
+
constructor_args = {
|
|
296
|
+
models: models,
|
|
297
|
+
current_model_index: default_index,
|
|
298
|
+
current_model_id: default_id
|
|
299
|
+
}
|
|
300
|
+
CONFIG_SETTINGS_KEYS.each do |key|
|
|
301
|
+
if loaded_settings.key?(key)
|
|
302
|
+
constructor_args[key.to_sym] = loaded_settings[key]
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
new(**constructor_args)
|
|
282
307
|
end
|
|
283
308
|
|
|
284
309
|
# Auto-injection of provider-preset lite models into @models has been
|
|
@@ -339,11 +364,27 @@ module Clacky
|
|
|
339
364
|
# config.yml remains backward compatible with users on older versions.
|
|
340
365
|
RUNTIME_ONLY_FIELDS = %w[id auto_injected].freeze
|
|
341
366
|
|
|
367
|
+
# Settings keys that are persisted to config.yml.
|
|
368
|
+
# These map directly to AgentConfig accessors.
|
|
369
|
+
CONFIG_SETTINGS_KEYS = %w[
|
|
370
|
+
enable_compression enable_prompt_caching memory_update_enabled
|
|
371
|
+
skill_evolution
|
|
372
|
+
].freeze
|
|
373
|
+
|
|
374
|
+
# Serialize the current agent configuration to YAML.
|
|
375
|
+
# Outputs a hash with "settings" and "models" keys (new format).
|
|
376
|
+
# Backward compatibility: old flat-array format is still readable by .load.
|
|
342
377
|
def to_yaml
|
|
343
|
-
|
|
378
|
+
persistable_models = @models.reject { |m| m["auto_injected"] }.map do |m|
|
|
344
379
|
m.reject { |k, _| RUNTIME_ONLY_FIELDS.include?(k) }
|
|
345
380
|
end
|
|
346
|
-
|
|
381
|
+
settings = {
|
|
382
|
+
"enable_compression" => @enable_compression,
|
|
383
|
+
"enable_prompt_caching" => @enable_prompt_caching,
|
|
384
|
+
"memory_update_enabled" => @memory_update_enabled,
|
|
385
|
+
"skill_evolution" => @skill_evolution
|
|
386
|
+
}
|
|
387
|
+
YAML.dump("settings" => settings, "models" => persistable_models)
|
|
347
388
|
end
|
|
348
389
|
|
|
349
390
|
# Check if any model is configured
|
data/lib/clacky/cli.rb
CHANGED
|
@@ -905,6 +905,14 @@ module Clacky
|
|
|
905
905
|
option :port, type: :numeric, default: 7070, desc: "Listen port (default: 7070)"
|
|
906
906
|
option :brand_test, type: :boolean, default: false,
|
|
907
907
|
desc: "Enable brand test mode: mock license activation without calling remote API"
|
|
908
|
+
option :no_compression, type: :boolean, default: false,
|
|
909
|
+
desc: "Disable message compression (saves tokens but may lose context)"
|
|
910
|
+
option :no_memory, type: :boolean, default: false,
|
|
911
|
+
desc: "Disable automatic memory updates"
|
|
912
|
+
option :no_caching, type: :boolean, default: false,
|
|
913
|
+
desc: "Disable prompt caching"
|
|
914
|
+
option :no_skill_evolution, type: :boolean, default: false,
|
|
915
|
+
desc: "Disable automatic skill evolution"
|
|
908
916
|
def server
|
|
909
917
|
# ── Security gate ──────────────────────────────────────────────────────
|
|
910
918
|
# Binding to 0.0.0.0 exposes the server to the public network.
|
|
@@ -948,6 +956,15 @@ module Clacky
|
|
|
948
956
|
agent_config = Clacky::AgentConfig.load
|
|
949
957
|
agent_config.permission_mode = :confirm_all
|
|
950
958
|
|
|
959
|
+
# Apply CLI overrides to agent config (--no-compression etc.)
|
|
960
|
+
# These override whatever is stored in config.yml.
|
|
961
|
+
agent_config.enable_compression = false if options[:no_compression]
|
|
962
|
+
agent_config.memory_update_enabled = false if options[:no_memory]
|
|
963
|
+
agent_config.enable_prompt_caching = false if options[:no_caching]
|
|
964
|
+
if options[:no_skill_evolution]
|
|
965
|
+
agent_config.skill_evolution[:enabled] = false
|
|
966
|
+
end
|
|
967
|
+
|
|
951
968
|
client_factory = lambda do
|
|
952
969
|
Clacky::Client.new(
|
|
953
970
|
agent_config.api_key,
|
|
@@ -987,6 +1004,10 @@ module Clacky
|
|
|
987
1004
|
|
|
988
1005
|
extra_flags = []
|
|
989
1006
|
extra_flags << "--brand-test" if options[:brand_test]
|
|
1007
|
+
extra_flags << "--no-compression" if options[:no_compression]
|
|
1008
|
+
extra_flags << "--no-memory" if options[:no_memory]
|
|
1009
|
+
extra_flags << "--no-caching" if options[:no_caching]
|
|
1010
|
+
extra_flags << "--no-skill-evolution" if options[:no_skill_evolution]
|
|
990
1011
|
|
|
991
1012
|
Clacky::Logger.console = true
|
|
992
1013
|
|
|
@@ -78,7 +78,7 @@ module Clacky
|
|
|
78
78
|
emit("token_usage", **token_data)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false)
|
|
81
|
+
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil)
|
|
82
82
|
data = { iterations: iterations, cost: cost }
|
|
83
83
|
data[:duration] = duration if duration
|
|
84
84
|
data[:cache_stats] = cache_stats if cache_stats
|
|
@@ -19,14 +19,26 @@ module Clacky
|
|
|
19
19
|
# Detect if the request should use the Bedrock Converse API.
|
|
20
20
|
# Matches any of:
|
|
21
21
|
# - API key with "ABSK" prefix (native AWS Bedrock)
|
|
22
|
-
# - API key with "clacky-" prefix (Clacky workspace key, proxied via Bedrock Converse)
|
|
23
22
|
# - Model ID with "abs-" prefix (Clacky AI proxy that speaks Bedrock Converse)
|
|
23
|
+
#
|
|
24
|
+
# A bare "clacky-" key is NOT enough: that same workspace key is also
|
|
25
|
+
# used for dsk-*, or-*, and other OpenAI-compatible aliases served by
|
|
26
|
+
# the same Clacky proxy on a different endpoint. The *model prefix* is
|
|
27
|
+
# the source of truth for which upstream format the proxy expects:
|
|
28
|
+
#
|
|
29
|
+
# abs-* → Bedrock Converse (POST /model/{id}/converse)
|
|
30
|
+
# dsk-* → OpenAI-compatible (POST /chat/completions)
|
|
31
|
+
# or-* → OpenAI-compatible (POST /chat/completions)
|
|
32
|
+
# other → depends on base_url + explicit anthropic_format flag
|
|
33
|
+
#
|
|
34
|
+
# Historically this method also returned true for any "clacky-" key,
|
|
35
|
+
# which forced non-abs aliases into the Bedrock endpoint and produced
|
|
36
|
+
# `unknown model "..."` errors. Keep the explicit-prefix rule: if you
|
|
37
|
+
# add a new OpenAI-compatible alias family on the Clacky proxy, it
|
|
38
|
+
# will route correctly without touching this file.
|
|
24
39
|
def self.bedrock_api_key?(api_key, model)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return false if model.to_s.start_with?("dsk-")
|
|
28
|
-
|
|
29
|
-
api_key.to_s.start_with?("ABSK", "clacky-") || model.to_s.start_with?("abs-")
|
|
40
|
+
return true if api_key.to_s.start_with?("ABSK")
|
|
41
|
+
model.to_s.start_with?("abs-")
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
module_function
|
|
@@ -100,6 +100,14 @@ module Clacky
|
|
|
100
100
|
# Business queries
|
|
101
101
|
# ─────────────────────────────────────────────
|
|
102
102
|
|
|
103
|
+
# True when a system prompt message is present in the history.
|
|
104
|
+
# Used by inject_session_context to avoid injecting context messages
|
|
105
|
+
# before the system prompt has been built (which would cause the
|
|
106
|
+
# guard in run() to skip building it altogether).
|
|
107
|
+
def has_system_prompt?
|
|
108
|
+
@messages.any? { |m| m[:role] == "system" }
|
|
109
|
+
end
|
|
110
|
+
|
|
103
111
|
# True when the last assistant message has tool_calls but no
|
|
104
112
|
# tool_result has been appended yet (would cause a 400 from the API).
|
|
105
113
|
def pending_tool_calls?
|
|
@@ -97,7 +97,7 @@ module Clacky
|
|
|
97
97
|
puts_line("[shell] #{command}")
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false)
|
|
100
|
+
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil)
|
|
101
101
|
parts = ["[done] iterations=#{iterations}", "cost=$#{cost.round(4)}"]
|
|
102
102
|
parts << "duration=#{duration.round(1)}s" if duration
|
|
103
103
|
puts_line(parts.join(" "))
|
data/lib/clacky/providers.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Clacky
|
|
|
22
22
|
"name" => "OpenClacky",
|
|
23
23
|
"base_url" => "https://api.openclacky.com",
|
|
24
24
|
"api" => "bedrock",
|
|
25
|
-
"default_model" => "abs-claude-sonnet-4-
|
|
25
|
+
"default_model" => "abs-claude-sonnet-4-6",
|
|
26
26
|
"models" => [
|
|
27
27
|
"abs-claude-opus-4-7",
|
|
28
28
|
"abs-claude-opus-4-6",
|
|
@@ -30,12 +30,15 @@ module Clacky
|
|
|
30
30
|
"abs-claude-sonnet-4-5",
|
|
31
31
|
"abs-claude-haiku-4-5",
|
|
32
32
|
"dsk-deepseek-v4-pro",
|
|
33
|
-
"dsk-deepseek-v4-flash"
|
|
33
|
+
"dsk-deepseek-v4-flash",
|
|
34
|
+
"or-gemini-3-1-pro"
|
|
34
35
|
],
|
|
35
36
|
# Provider-level default: the Claude family served here is vision-capable.
|
|
36
37
|
"capabilities" => { "vision" => true }.freeze,
|
|
37
38
|
# Model-level overrides: DeepSeek models routed through this provider
|
|
38
39
|
# are text-only; images uploaded for them must be downgraded to disk refs.
|
|
40
|
+
# Gemini 3.1 Pro keeps the provider-default vision=true (it accepts
|
|
41
|
+
# image/audio/video input natively via OpenRouter).
|
|
39
42
|
"model_capabilities" => {
|
|
40
43
|
"dsk-deepseek-v4-pro" => { "vision" => false }.freeze,
|
|
41
44
|
"dsk-deepseek-v4-flash" => { "vision" => false }.freeze
|
|
@@ -46,6 +49,10 @@ module Clacky
|
|
|
46
49
|
# weak models (haiku / v4-flash) ARE the lite tier themselves, so
|
|
47
50
|
# they're intentionally not listed here — no injection happens when
|
|
48
51
|
# the default model is already lite-class.
|
|
52
|
+
#
|
|
53
|
+
# or-gemini-3-1-pro is intentionally absent: Gemini has no lite
|
|
54
|
+
# sibling wired up (yet) on this provider; subagents using the
|
|
55
|
+
# Gemini default will just reuse it for lite work until we add one.
|
|
49
56
|
"lite_models" => {
|
|
50
57
|
"abs-claude-opus-4-7" => "abs-claude-haiku-4-5",
|
|
51
58
|
"abs-claude-opus-4-6" => "abs-claude-haiku-4-5",
|
|
@@ -86,8 +93,6 @@ module Clacky
|
|
|
86
93
|
"models" => [
|
|
87
94
|
"deepseek-v4-flash",
|
|
88
95
|
"deepseek-v4-pro",
|
|
89
|
-
"deepseek-chat",
|
|
90
|
-
"deepseek-reasoner"
|
|
91
96
|
],
|
|
92
97
|
# DeepSeek V4 API does not accept image inputs — text-only across all models.
|
|
93
98
|
"capabilities" => { "vision" => false }.freeze,
|
|
@@ -137,9 +142,11 @@ module Clacky
|
|
|
137
142
|
"abs-claude-sonnet-4-5",
|
|
138
143
|
"abs-claude-haiku-4-5",
|
|
139
144
|
"dsk-deepseek-v4-pro",
|
|
140
|
-
"dsk-deepseek-v4-flash"
|
|
145
|
+
"dsk-deepseek-v4-flash",
|
|
146
|
+
"or-gemini-3-1-pro"
|
|
141
147
|
],
|
|
142
|
-
# Same lineup as openclacky — Claude is vision, DeepSeek is text-only
|
|
148
|
+
# Same lineup as openclacky — Claude is vision, DeepSeek is text-only,
|
|
149
|
+
# Gemini inherits the provider-default vision=true.
|
|
143
150
|
"capabilities" => { "vision" => true }.freeze,
|
|
144
151
|
"model_capabilities" => {
|
|
145
152
|
"dsk-deepseek-v4-pro" => { "vision" => false }.freeze,
|
|
@@ -104,10 +104,14 @@ module Clacky
|
|
|
104
104
|
# Suppress
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false)
|
|
107
|
+
def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil)
|
|
108
108
|
flush_buffer
|
|
109
109
|
parts = ["Done", "#{iterations} step#{"s" if iterations != 1}"]
|
|
110
|
-
|
|
110
|
+
# Only show cost when pricing source is known (model matched pricing table).
|
|
111
|
+
# Unknown models return nil — skip to avoid misleading numbers.
|
|
112
|
+
if cost && cost > 0 && cost_source
|
|
113
|
+
parts << "$#{cost.round(4)}"
|
|
114
|
+
end
|
|
111
115
|
parts << "#{duration.round(1)}s" if duration
|
|
112
116
|
send_text(parts.join(" · "))
|
|
113
117
|
end
|