openclacky 1.5.9 → 1.5.11
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/.clackyrules +1 -0
- data/CHANGELOG.md +60 -0
- data/README.md +4 -4
- data/README_CN.md +4 -4
- data/README_JA.md +4 -4
- data/lib/clacky/agent/llm_caller.rb +7 -3
- data/lib/clacky/agent/memory_updater.rb +1 -43
- data/lib/clacky/agent/session_serializer.rb +26 -14
- data/lib/clacky/agent/skill_auto_creator.rb +12 -9
- data/lib/clacky/agent/skill_evolution.rb +4 -3
- data/lib/clacky/agent/skill_manager.rb +11 -14
- data/lib/clacky/agent/skill_reflector.rb +2 -2
- data/lib/clacky/agent/tool_executor.rb +1 -1
- data/lib/clacky/agent/tool_registry.rb +6 -4
- data/lib/clacky/agent.rb +240 -76
- data/lib/clacky/agent_config.rb +66 -39
- data/lib/clacky/agent_profile.rb +36 -2
- data/lib/clacky/brand_config.rb +15 -17
- data/lib/clacky/cli.rb +26 -7
- data/lib/clacky/client.rb +52 -14
- data/lib/clacky/default_extensions/advisor/advisors/general.md +48 -0
- data/lib/clacky/default_extensions/advisor/api/handler.rb +27 -0
- data/lib/clacky/default_extensions/advisor/ext.yml +35 -0
- data/lib/clacky/default_extensions/advisor/hooks/advisor.rb +291 -0
- data/lib/clacky/default_extensions/advisor/hooks/advisor_complete.rb +11 -0
- data/lib/clacky/default_extensions/advisor/hooks/advisor_tool_use.rb +12 -0
- data/lib/clacky/default_extensions/advisor/panels/advisor/view.js +462 -0
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +6 -19
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +1 -0
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +92 -57
- data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +64 -11
- data/lib/clacky/default_extensions/meeting/api/handler.rb +1 -1
- data/lib/clacky/default_skills/channel-manager/SKILL.md +4 -4
- data/lib/clacky/default_skills/media-gen/SKILL.md +18 -17
- data/lib/clacky/default_skills/new/SKILL.md +1 -1
- data/lib/clacky/default_skills/onboard/SKILL.md +2 -2
- data/lib/clacky/default_skills/skill-creator/SKILL.md +8 -7
- data/lib/clacky/extension/cli_commands.rb +1 -1
- data/lib/clacky/extension/hook_loader.rb +2 -0
- data/lib/clacky/extension/loader.rb +36 -3
- data/lib/clacky/extension/packager.rb +45 -10
- data/lib/clacky/extension/scaffold/templates/full/README.md.erb +3 -1
- data/lib/clacky/extension/scaffold/templates/full/ext.yml.erb +8 -1
- data/lib/clacky/extension/scaffold/templates/full/panels/dashboard/view.js.erb +10 -22
- data/lib/clacky/extension/scaffold/templates/full/tools/hello.rb +28 -0
- data/lib/clacky/extension/scaffold/templates/hello/panels/hello/view.js.erb +3 -3
- data/lib/clacky/extension/scaffold.rb +3 -2
- data/lib/clacky/extension/verifier.rb +4 -2
- data/lib/clacky/fanout.rb +1 -1
- data/lib/clacky/idle_compression_timer.rb +3 -5
- data/lib/clacky/json_ui_controller.rb +1 -1
- data/lib/clacky/locales/en.rb +1 -1
- data/lib/clacky/locales/zh.rb +1 -1
- data/lib/clacky/mcp/http_transport.rb +29 -13
- data/lib/clacky/mcp/registry.rb +2 -3
- data/lib/clacky/mcp/stdio_transport.rb +21 -17
- data/lib/clacky/media/base.rb +22 -13
- data/lib/clacky/message_format/open_ai.rb +17 -1
- data/lib/clacky/message_history.rb +1 -1
- data/lib/clacky/plain_ui_controller.rb +19 -16
- data/lib/clacky/platform_http_client.rb +14 -14
- data/lib/clacky/providers.rb +55 -15
- data/lib/clacky/rich_ui/rich_ui_controller.rb +6 -7
- data/lib/clacky/server/browser_manager.rb +4 -7
- data/lib/clacky/server/channel/adapters/dingtalk/stream_client.rb +2 -1
- data/lib/clacky/server/channel/adapters/discord/gateway_client.rb +7 -5
- data/lib/clacky/server/channel/adapters/feishu/bot.rb +42 -20
- data/lib/clacky/server/channel/adapters/feishu/ws_client.rb +4 -3
- data/lib/clacky/server/channel/adapters/telegram/adapter.rb +3 -2
- data/lib/clacky/server/channel/adapters/telegram/api_client.rb +5 -3
- data/lib/clacky/server/channel/adapters/wecom/ws_client.rb +6 -5
- data/lib/clacky/server/channel/adapters/weixin/adapter.rb +13 -11
- data/lib/clacky/server/channel/adapters/weixin/api_client.rb +7 -2
- data/lib/clacky/server/channel/channel_config.rb +36 -6
- data/lib/clacky/server/channel/channel_manager.rb +24 -11
- data/lib/clacky/server/channel/channel_ui_controller.rb +40 -8
- data/lib/clacky/server/http_server.rb +323 -81
- data/lib/clacky/server/model_prices.rb +39 -0
- data/lib/clacky/server/project_manager.rb +4 -1
- data/lib/clacky/server/scheduler.rb +4 -5
- data/lib/clacky/server/server_master.rb +27 -4
- data/lib/clacky/server/session_registry.rb +7 -2
- data/lib/clacky/server/web_ui_controller.rb +16 -15
- data/lib/clacky/session_manager.rb +11 -4
- data/lib/clacky/shell_hook_loader.rb +3 -3
- data/lib/clacky/shutdown.rb +71 -0
- data/lib/clacky/skill.rb +9 -0
- data/lib/clacky/skill_loader.rb +8 -3
- data/lib/clacky/telemetry.rb +1 -1
- data/lib/clacky/thread_registry.rb +102 -0
- data/lib/clacky/tools/ask_user.rb +212 -0
- data/lib/clacky/tools/base.rb +9 -0
- data/lib/clacky/tools/terminal.rb +1 -1
- data/lib/clacky/tools/trash_manager.rb +16 -0
- data/lib/clacky/tools/web_search.rb +25 -12
- data/lib/clacky/ui2/components/command_suggestions.rb +1 -2
- data/lib/clacky/ui2/components/input_area.rb +2 -2
- data/lib/clacky/ui2/progress_handle.rb +1 -2
- data/lib/clacky/ui2/progress_indicator.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +118 -18
- data/lib/clacky/ui_interface.rb +3 -3
- data/lib/clacky/utils/arguments_parser.rb +31 -4
- data/lib/clacky/utils/logger.rb +3 -1
- data/lib/clacky/utils/model_pricing.rb +114 -8
- data/lib/clacky/utils/parser_manager.rb +2 -2
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +413 -66
- data/lib/clacky/web/components/notify.js +9 -7
- data/lib/clacky/web/core/ext.js +4 -6
- data/lib/clacky/web/features/channels/store.js +32 -2
- data/lib/clacky/web/features/channels/view.js +76 -0
- data/lib/clacky/web/features/extensions/store.js +25 -0
- data/lib/clacky/web/features/extensions/view.js +24 -0
- data/lib/clacky/web/features/model-tester/store.js +2 -1
- data/lib/clacky/web/i18n.js +96 -2
- data/lib/clacky/web/index.html +47 -8
- data/lib/clacky/web/projects.js +29 -1
- data/lib/clacky/web/sessions.js +463 -101
- data/lib/clacky/web/settings.js +206 -22
- data/lib/clacky/web/skills.js +86 -10
- data/lib/clacky/web/ws-dispatcher.js +43 -7
- data/lib/clacky.rb +3 -1
- metadata +13 -2
- data/lib/clacky/tools/request_user_feedback.rb +0 -84
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1cef597eb5d6c066192ba6c2c587e0dcd62fdf2dd9ed422161f71718acbb7f25
|
|
4
|
+
data.tar.gz: 03bcd29f23eacd7b49a37db0494f7964757391ac52e8f3ef9787c49a6b2f8efb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7543c4f518bf5a2e20778472e60909b5eda8f727015b66aad37c69b2d256b53ba3cd5126d2b385a06456a45a6531fe4fdaad7c6d7f8b247aeb2a76bf1446a9f1
|
|
7
|
+
data.tar.gz: d6ceb974a12d29096ae65bcbed3d6c6467880005101664919357572201846792faf4f89956ff5ca568519d74960674e214a39c61333e527741a9eb38083fcbf1
|
data/.clackyrules
CHANGED
|
@@ -70,6 +70,7 @@ the CI matrix - a green local `bundle exec rspec` on Ruby 3.x proves nothing abo
|
|
|
70
70
|
- **IMPORTANT**: When developing new features, write RSpec tests as needed and ensure they pass
|
|
71
71
|
- **DO NOT** write custom test scripts unless explicitly requested by the user
|
|
72
72
|
- **DO NOT** create markdown documentation unless explicitly requested by the user
|
|
73
|
+
- **DO NOT** run RuboCop for verification. `bundle exec rspec` is the only required check; the codebase has many pre-existing offenses, so RuboCop output is noise. Only run it when the user explicitly asks
|
|
73
74
|
- **IMPORTANT**: When testing clacky commands or debugging, always use `bundle exec ruby bin/clacky` instead of the global `clacky` command. The global command loads the system-installed gem version (e.g., `openclacky-0.7.0`), not your local development code
|
|
74
75
|
|
|
75
76
|
### Tool Development
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,66 @@ 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.11] - 2026-08-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Creator Studio: separate Marketplace and Brand sections for published extensions
|
|
11
|
+
- Advisor extension for next-job suggestions
|
|
12
|
+
- ask_user tool with multi-question support (replaces feedback_user)
|
|
13
|
+
- Sidebar states for awaiting-feedback and just-finished sessions
|
|
14
|
+
- Auto-purge file-trash entries older than 8 days
|
|
15
|
+
- DeepSeek V4 flash vision exp model preset
|
|
16
|
+
- IM channel toggle to mute tool-call narration and previews
|
|
17
|
+
|
|
18
|
+
### Improved
|
|
19
|
+
- Unified internal thread management with graceful shutdown
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Skill decrypt retry now picks up a renewed license key
|
|
23
|
+
- DeepSeek weekends billed entirely at off-peak rates
|
|
24
|
+
- Skill and extension zip downloads fall back to a secondary CDN host
|
|
25
|
+
- Session status dot aligned with name and channel badge
|
|
26
|
+
- Project session tag top margin
|
|
27
|
+
- Unified modal close button and ext-studio modal semantics
|
|
28
|
+
- Unified checkbox accent and distinct verified badge
|
|
29
|
+
- Edit-model modal overflow on small screens
|
|
30
|
+
- Subagent UI fixes
|
|
31
|
+
- Star History charts migrated to star-history.dera.page (#491 - @CrustyMozarella)
|
|
32
|
+
|
|
33
|
+
## [1.5.10] - 2026-08-19
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Import extensions from a local zip file
|
|
37
|
+
- Per-model API format override (#484 - @kylezhang)
|
|
38
|
+
- Project pinning (pin to top)
|
|
39
|
+
- Copy Session ID action in session menu
|
|
40
|
+
- Price ratio on model switcher panel
|
|
41
|
+
- Model search filter and provider filter dropdown in settings
|
|
42
|
+
- Remark field for model configs
|
|
43
|
+
- Custom vision model config
|
|
44
|
+
- GPT-5.6 Sol/Terra/Luna presets and pricing
|
|
45
|
+
- GLM-5.3 model support
|
|
46
|
+
- Global IM status-messages toggle (default off)
|
|
47
|
+
- Extension-defined tools support; extensions can disable skills
|
|
48
|
+
- Server restart state and HTTP restart flag
|
|
49
|
+
- Actionable web search provider errors
|
|
50
|
+
- Localized display names for broadcast skill commands
|
|
51
|
+
|
|
52
|
+
### Improved
|
|
53
|
+
- Skill autocomplete dropdown UI with localized source labels
|
|
54
|
+
- Skill auto-creation and evolution reporting refinements
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- MCP: preserve CRLF pairs split across SSE chunk boundaries (#489 - @MohammedAlkindi)
|
|
58
|
+
- Feishu: auto-refresh token and retry when revoked (99991663) (#483 - @chengoak)
|
|
59
|
+
- Preserve head+tail for oversized terminal output (#455 - @shipinliang)
|
|
60
|
+
- openclacky start failed when no TTY
|
|
61
|
+
- Capture master/worker crash logs under LaunchAgent
|
|
62
|
+
- ReferenceError in model modal _syncRemarkField
|
|
63
|
+
- Remark field always visible in model modal
|
|
64
|
+
- api_format field only shown for custom providers and validated before mutating model config
|
|
65
|
+
- Media gen output dir handling
|
|
66
|
+
|
|
7
67
|
## [1.5.9] - 2026-08-14
|
|
8
68
|
|
|
9
69
|
### Added
|
data/README.md
CHANGED
|
@@ -209,11 +209,11 @@ $ openclacky
|
|
|
209
209
|
|
|
210
210
|
## Star History
|
|
211
211
|
|
|
212
|
-
<a href="https://
|
|
212
|
+
<a href="https://star-history.dera.page/#clacky-ai/openclacky&type=date&legend=top-left">
|
|
213
213
|
<picture>
|
|
214
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://
|
|
215
|
-
<source media="(prefers-color-scheme: light)" srcset="https://
|
|
216
|
-
<img alt="Star History Chart" src="https://
|
|
214
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&theme=dark&legend=top-left" />
|
|
215
|
+
<source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
216
|
+
<img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
217
217
|
</picture>
|
|
218
218
|
</a>
|
|
219
219
|
|
data/README_CN.md
CHANGED
|
@@ -204,11 +204,11 @@ $ openclacky
|
|
|
204
204
|
|
|
205
205
|
## Star 历史
|
|
206
206
|
|
|
207
|
-
<a href="https://
|
|
207
|
+
<a href="https://star-history.dera.page/#clacky-ai/openclacky&type=date&legend=top-left">
|
|
208
208
|
<picture>
|
|
209
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://
|
|
210
|
-
<source media="(prefers-color-scheme: light)" srcset="https://
|
|
211
|
-
<img alt="Star History Chart" src="https://
|
|
209
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&theme=dark&legend=top-left" />
|
|
210
|
+
<source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
211
|
+
<img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
212
212
|
</picture>
|
|
213
213
|
</a>
|
|
214
214
|
|
data/README_JA.md
CHANGED
|
@@ -209,11 +209,11 @@ $ openclacky
|
|
|
209
209
|
|
|
210
210
|
## Star History
|
|
211
211
|
|
|
212
|
-
<a href="https://
|
|
212
|
+
<a href="https://star-history.dera.page/#clacky-ai/openclacky&type=date&legend=top-left">
|
|
213
213
|
<picture>
|
|
214
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://
|
|
215
|
-
<source media="(prefers-color-scheme: light)" srcset="https://
|
|
216
|
-
<img alt="Star History Chart" src="https://
|
|
214
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&theme=dark&legend=top-left" />
|
|
215
|
+
<source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
216
|
+
<img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=clacky-ai/openclacky&type=date&legend=top-left" />
|
|
217
217
|
</picture>
|
|
218
218
|
</a>
|
|
219
219
|
|
|
@@ -95,6 +95,7 @@ module Clacky
|
|
|
95
95
|
|
|
96
96
|
begin
|
|
97
97
|
begin
|
|
98
|
+
Clacky::Shutdown.checkpoint!
|
|
98
99
|
# Use active_messages (Time Machine) when undone, otherwise send full history.
|
|
99
100
|
# to_api strips internal fields and handles orphaned tool_calls.
|
|
100
101
|
messages_to_send = if respond_to?(:active_messages)
|
|
@@ -167,6 +168,7 @@ module Clacky
|
|
|
167
168
|
end
|
|
168
169
|
|
|
169
170
|
rescue Faraday::TimeoutError => e
|
|
171
|
+
Clacky::Shutdown.checkpoint!
|
|
170
172
|
# Faraday::TimeoutError on our non-streaming POST almost always means
|
|
171
173
|
# the *response* took longer than the 300s read-timeout to come back —
|
|
172
174
|
# i.e. the model is trying to produce a huge output in one shot
|
|
@@ -198,7 +200,7 @@ module Clacky
|
|
|
198
200
|
metadata: { attempt: retries, total: max_retries }
|
|
199
201
|
)
|
|
200
202
|
retrying_progress_opened = true
|
|
201
|
-
sleep
|
|
203
|
+
Clacky::Shutdown.sleep(retry_delay)
|
|
202
204
|
retry
|
|
203
205
|
else
|
|
204
206
|
# All retries exhausted for a timeout. Try the secondary gateway URL if available.
|
|
@@ -219,6 +221,7 @@ module Clacky
|
|
|
219
221
|
end
|
|
220
222
|
|
|
221
223
|
rescue Faraday::ConnectionFailed, Faraday::SSLError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::EPIPE => e
|
|
224
|
+
Clacky::Shutdown.checkpoint!
|
|
222
225
|
retries += 1
|
|
223
226
|
|
|
224
227
|
# Errno::EPIPE means the underlying TCP socket is dead (server closed the
|
|
@@ -245,7 +248,7 @@ module Clacky
|
|
|
245
248
|
metadata: { attempt: retries, total: max_retries }
|
|
246
249
|
)
|
|
247
250
|
retrying_progress_opened = true
|
|
248
|
-
sleep
|
|
251
|
+
Clacky::Shutdown.sleep(retry_delay)
|
|
249
252
|
retry
|
|
250
253
|
else
|
|
251
254
|
# All retries on the primary endpoint exhausted for a network-level
|
|
@@ -269,6 +272,7 @@ module Clacky
|
|
|
269
272
|
end
|
|
270
273
|
|
|
271
274
|
rescue RetryableError => e
|
|
275
|
+
Clacky::Shutdown.checkpoint!
|
|
272
276
|
retries += 1
|
|
273
277
|
|
|
274
278
|
# Probing failure: primary still down — renew cooling-off and retry with fallback.
|
|
@@ -298,7 +302,7 @@ module Clacky
|
|
|
298
302
|
metadata: { attempt: retries, total: current_max }
|
|
299
303
|
)
|
|
300
304
|
retrying_progress_opened = true
|
|
301
|
-
sleep
|
|
305
|
+
Clacky::Shutdown.sleep(retry_delay)
|
|
302
306
|
retry
|
|
303
307
|
else
|
|
304
308
|
# All retries on the current endpoint exhausted.
|
|
@@ -74,7 +74,7 @@ module Clacky
|
|
|
74
74
|
private def with_memory_update_phase
|
|
75
75
|
return yield unless @ui.respond_to?(:with_phase)
|
|
76
76
|
|
|
77
|
-
pid = @ui.phase_start(kind: "memory_update", label: "
|
|
77
|
+
pid = @ui.phase_start(kind: "memory_update", label: Clacky::I18n.t("phase.memory_update"))
|
|
78
78
|
summary = nil
|
|
79
79
|
begin
|
|
80
80
|
summary = yield
|
|
@@ -127,11 +127,6 @@ module Clacky
|
|
|
127
127
|
# still reflects ONLY the user's task, not the memory update.
|
|
128
128
|
subagent_cost = absorb_subagent_cost(result)
|
|
129
129
|
|
|
130
|
-
# A summary is only worth showing if the subagent actually wrote
|
|
131
|
-
# something. The common "No memory updates needed." path returns nil
|
|
132
|
-
# so the phase leaves no trace at all.
|
|
133
|
-
return unless subagent_wrote_memory?(subagent)
|
|
134
|
-
|
|
135
130
|
"#{result[:iterations]} steps · $#{subagent_cost.round(4)}"
|
|
136
131
|
end
|
|
137
132
|
|
|
@@ -142,43 +137,6 @@ module Clacky
|
|
|
142
137
|
@config.memory_update_enabled != false
|
|
143
138
|
end
|
|
144
139
|
|
|
145
|
-
# Inspect the subagent's history for a successful write/edit tool
|
|
146
|
-
# call targeting a memory file. Used to decide whether to surface a
|
|
147
|
-
# "Memory updated" info line (option C — silent when nothing changed).
|
|
148
|
-
# @param subagent [Clacky::Agent]
|
|
149
|
-
# @return [Boolean]
|
|
150
|
-
private def subagent_wrote_memory?(subagent)
|
|
151
|
-
return false unless subagent.respond_to?(:history) && subagent.history
|
|
152
|
-
|
|
153
|
-
subagent.history.to_a.any? do |msg|
|
|
154
|
-
next false unless msg.is_a?(Hash)
|
|
155
|
-
|
|
156
|
-
# Match OpenAI-style tool_calls on assistant messages …
|
|
157
|
-
tool_calls = msg[:tool_calls] || msg["tool_calls"]
|
|
158
|
-
if tool_calls.is_a?(Array) && tool_calls.any?
|
|
159
|
-
next true if tool_calls.any? do |tc|
|
|
160
|
-
name = tc.dig(:function, :name) || tc.dig("function", "name") || tc[:name] || tc["name"]
|
|
161
|
-
%w[write edit].include?(name.to_s)
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# … and Anthropic-style content blocks with type=tool_use.
|
|
166
|
-
content = msg[:content] || msg["content"]
|
|
167
|
-
if content.is_a?(Array)
|
|
168
|
-
next true if content.any? do |block|
|
|
169
|
-
block.is_a?(Hash) &&
|
|
170
|
-
(block[:type] == "tool_use" || block["type"] == "tool_use") &&
|
|
171
|
-
%w[write edit].include?((block[:name] || block["name"]).to_s)
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
false
|
|
176
|
-
end
|
|
177
|
-
rescue StandardError
|
|
178
|
-
# Defensive: never let introspection errors break memory update.
|
|
179
|
-
false
|
|
180
|
-
end
|
|
181
|
-
|
|
182
140
|
# Build the memory update prompt for the forked subagent.
|
|
183
141
|
#
|
|
184
142
|
# Architecture:
|
|
@@ -392,7 +392,9 @@ module Clacky
|
|
|
392
392
|
}
|
|
393
393
|
all_files = image_files + disk_files
|
|
394
394
|
ui.show_user_message(raw_text, created_at: msg[:created_at], files: all_files,
|
|
395
|
-
editable: round[:editable] != false
|
|
395
|
+
editable: round[:editable] != false,
|
|
396
|
+
skill_command: msg[:skill_command],
|
|
397
|
+
skill_command_display: msg[:skill_command_display])
|
|
396
398
|
replay_ext_events(msg, ui)
|
|
397
399
|
|
|
398
400
|
round[:events].each do |ev|
|
|
@@ -740,16 +742,17 @@ module Clacky
|
|
|
740
742
|
args_raw = tc[:arguments] || tc.dig(:function, :arguments) || {}
|
|
741
743
|
args = args_raw.is_a?(String) ? (JSON.parse(args_raw) rescue args_raw) : args_raw
|
|
742
744
|
|
|
743
|
-
# Special handling:
|
|
744
|
-
#
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
options = args.is_a?(Hash) ? (args[:options] || args["options"]) : nil
|
|
750
|
-
options = Array(options) if options && !options.is_a?(Array)
|
|
745
|
+
# Special handling: the ask_user question is shown as an assistant
|
|
746
|
+
# message (matching real-time behavior), not as a tool call.
|
|
747
|
+
if Clacky::Tools::AskUser.feedback_tool?(name)
|
|
748
|
+
questions = Clacky::Tools::AskUser.normalize_questions(args)
|
|
749
|
+
context = Clacky::Tools::AskUser.fetch_key(args, :context).to_s
|
|
750
|
+
first = questions.first
|
|
751
751
|
|
|
752
|
-
|
|
752
|
+
unless first.nil?
|
|
753
|
+
ui.show_feedback_request(first[:question], context, first[:options],
|
|
754
|
+
questions: questions)
|
|
755
|
+
end
|
|
753
756
|
else
|
|
754
757
|
ui.show_tool_call(name, args)
|
|
755
758
|
end
|
|
@@ -794,14 +797,23 @@ module Clacky
|
|
|
794
797
|
end
|
|
795
798
|
end
|
|
796
799
|
|
|
797
|
-
# Replay
|
|
800
|
+
# Replay subagent transcripts stored on a tool result message. Emits a
|
|
798
801
|
# bracketed sequence of UI events the frontend can render as a collapsible
|
|
799
802
|
# sub-process block: subagent_start → (assistant_message / tool_call /
|
|
800
|
-
# tool_result)* → subagent_end.
|
|
803
|
+
# tool_result)* → subagent_end. A fan-out batch renders one block per job,
|
|
804
|
+
# in the caller's original job order.
|
|
805
|
+
#
|
|
806
|
+
# Sessions saved before fan-out support stored a bare Hash here.
|
|
801
807
|
def replay_subagent_transcript(msg, ui)
|
|
802
|
-
|
|
803
|
-
|
|
808
|
+
stored = msg[:subagent_transcript]
|
|
809
|
+
Array(stored.is_a?(Hash) ? [stored] : stored).each do |transcript|
|
|
810
|
+
next unless transcript.is_a?(Hash)
|
|
811
|
+
|
|
812
|
+
replay_one_subagent_transcript(transcript, ui)
|
|
813
|
+
end
|
|
814
|
+
end
|
|
804
815
|
|
|
816
|
+
def replay_one_subagent_transcript(transcript, ui)
|
|
805
817
|
events = transcript[:events] || transcript["events"] || []
|
|
806
818
|
return if events.empty?
|
|
807
819
|
|
|
@@ -22,8 +22,10 @@ module Clacky
|
|
|
22
22
|
|
|
23
23
|
# Fork an isolated subagent to evaluate + create — does NOT touch main history
|
|
24
24
|
subagent = fork_subagent
|
|
25
|
-
subagent.run(build_skill_creation_prompt)
|
|
26
|
-
|
|
25
|
+
result = subagent.run(build_skill_creation_prompt)
|
|
26
|
+
cost = absorb_subagent_cost(result)
|
|
27
|
+
|
|
28
|
+
"$#{cost.round(4)}"
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
# Determine if this task is a candidate for skill auto-creation
|
|
@@ -84,19 +86,20 @@ module Clacky
|
|
|
84
86
|
## Action
|
|
85
87
|
|
|
86
88
|
If **ALL** criteria are met:
|
|
87
|
-
→ Call invoke_skill with:
|
|
89
|
+
→ Call invoke_skill with EXACTLY these two parameters:
|
|
88
90
|
- skill_name: "skill-creator"
|
|
89
|
-
- task: A clear description of what to automate and how (be specific)
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
- task: A clear description of what to automate and how (be specific).
|
|
92
|
+
Start it with "Quick mode:" and state the suggested skill name
|
|
93
|
+
(lowercase, hyphens OK) inside this text.
|
|
94
|
+
|
|
95
|
+
invoke_skill accepts ONLY skill_name and task. Passing any other
|
|
96
|
+
parameter raises an ArgumentError and the creation fails.
|
|
92
97
|
|
|
93
98
|
Example invocation:
|
|
94
99
|
```
|
|
95
100
|
invoke_skill(
|
|
96
101
|
skill_name: "skill-creator",
|
|
97
|
-
task: "
|
|
98
|
-
mode: "quick",
|
|
99
|
-
suggested_name: "url-summarizer"
|
|
102
|
+
task: "Quick mode: create a skill named 'url-summarizer' (no user interviews). It should extract and summarize content from URLs: 1) fetch the URL content, 2) parse the main text, 3) generate a concise summary. Expected input: URL. Expected output: markdown summary."
|
|
100
103
|
)
|
|
101
104
|
```
|
|
102
105
|
|
|
@@ -26,11 +26,12 @@ module Clacky
|
|
|
26
26
|
def run_skill_evolution_hooks
|
|
27
27
|
return unless skill_evolution_enabled?
|
|
28
28
|
return if @is_subagent
|
|
29
|
+
# Preconditions unmet (short task, default skill, ...): the evolution
|
|
30
|
+
# never ran, so there is nothing to report — stay completely silent.
|
|
31
|
+
return unless skill_evolution_has_work?
|
|
29
32
|
|
|
30
33
|
with_skill_evolution_phase do
|
|
31
|
-
if
|
|
32
|
-
Clacky::I18n.t("phase.skipped")
|
|
33
|
-
elsif @skill_execution_context
|
|
34
|
+
if @skill_execution_context
|
|
34
35
|
maybe_reflect_on_skill
|
|
35
36
|
else
|
|
36
37
|
maybe_create_skill_from_task
|
|
@@ -82,7 +82,7 @@ module Clacky
|
|
|
82
82
|
return { matched: true, found: false, skill_name: skill_name, reason: :not_user_invocable, skill: skill }
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
if @agent_profile &&
|
|
85
|
+
if @agent_profile && !@agent_profile.skill_allowed?(skill)
|
|
86
86
|
return { matched: true, found: false, skill_name: skill_name, reason: :agent_not_allowed, skill: skill }
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -266,22 +266,20 @@ module Clacky
|
|
|
266
266
|
# is injected instructing the LLM to inform the user in their own language and
|
|
267
267
|
# suggest similar skills — no error is raised, the LLM handles the reply.
|
|
268
268
|
#
|
|
269
|
-
# @param
|
|
269
|
+
# @param skill_command [Hash] Result of parse_skill_command for the user input
|
|
270
270
|
# @param task_id [Integer] Current task ID (for message tagging)
|
|
271
271
|
# @return [void]
|
|
272
|
-
def inject_skill_command_as_assistant_message(
|
|
273
|
-
result = parse_skill_command(user_input)
|
|
274
|
-
|
|
272
|
+
def inject_skill_command_as_assistant_message(skill_command, task_id)
|
|
275
273
|
# Not a slash command at all — nothing to do
|
|
276
|
-
return unless
|
|
274
|
+
return unless skill_command[:matched]
|
|
277
275
|
|
|
278
|
-
skill_name =
|
|
276
|
+
skill_name = skill_command[:skill_name]
|
|
279
277
|
|
|
280
278
|
# Slash command recognised but skill could not be dispatched — inject an
|
|
281
279
|
# LLM-facing notice so the model explains the situation to the user in
|
|
282
280
|
# their own language instead of silently ignoring the command.
|
|
283
|
-
unless
|
|
284
|
-
notice = case
|
|
281
|
+
unless skill_command[:found]
|
|
282
|
+
notice = case skill_command[:reason]
|
|
285
283
|
when :not_found
|
|
286
284
|
suggestions = suggest_similar_skills(skill_name)
|
|
287
285
|
msg = "[SYSTEM] The user entered the slash command /#{skill_name} but no matching skill was found. " \
|
|
@@ -302,8 +300,8 @@ module Clacky
|
|
|
302
300
|
return
|
|
303
301
|
end
|
|
304
302
|
|
|
305
|
-
skill =
|
|
306
|
-
arguments =
|
|
303
|
+
skill = skill_command[:skill]
|
|
304
|
+
arguments = skill_command[:arguments]
|
|
307
305
|
|
|
308
306
|
# fork_agent skills run in an isolated subagent
|
|
309
307
|
if skill.fork_agent?
|
|
@@ -443,7 +441,7 @@ module Clacky
|
|
|
443
441
|
def filter_skills_by_profile(skills)
|
|
444
442
|
return skills unless @agent_profile
|
|
445
443
|
|
|
446
|
-
skills.select { |skill|
|
|
444
|
+
skills.select { |skill| @agent_profile.skill_allowed?(skill) }
|
|
447
445
|
end
|
|
448
446
|
|
|
449
447
|
# Build template context for skill content expansion.
|
|
@@ -653,8 +651,7 @@ module Clacky
|
|
|
653
651
|
# observe() attaches each trail to its own tool result message.
|
|
654
652
|
# Kept out of the LLM payload via INTERNAL_FIELDS.
|
|
655
653
|
if tool_call_id
|
|
656
|
-
|
|
657
|
-
extract_subagent_transcript(subagent, skill.identifier)
|
|
654
|
+
record_subagent_transcript(tool_call_id, subagent, skill.identifier)
|
|
658
655
|
end
|
|
659
656
|
|
|
660
657
|
# Mutate the subagent_instructions message in-place to become the result summary
|
|
@@ -26,11 +26,11 @@ module Clacky
|
|
|
26
26
|
subagent = fork_subagent
|
|
27
27
|
result = subagent.run(build_skill_reflection_prompt(skill_name))
|
|
28
28
|
|
|
29
|
-
cost = absorb_subagent_cost(result)
|
|
29
|
+
cost = absorb_subagent_cost(result)
|
|
30
30
|
|
|
31
31
|
@skill_execution_context = nil
|
|
32
32
|
|
|
33
|
-
"#{skill_name} · $#{cost.round(4)}"
|
|
33
|
+
"#{skill_name} · $#{cost.round(4)}"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
private def should_reflect_on_skill?
|
|
@@ -13,7 +13,7 @@ module Clacky
|
|
|
13
13
|
case @config.permission_mode
|
|
14
14
|
when :auto_approve, :confirm_all
|
|
15
15
|
# Both modes auto-execute all file/shell tools without confirmation.
|
|
16
|
-
# The difference is only in
|
|
16
|
+
# The difference is only in ask_user handling:
|
|
17
17
|
# auto_approve → no human present, inject auto_reply
|
|
18
18
|
# confirm_all → human present, truly wait for user input
|
|
19
19
|
true
|
|
@@ -55,10 +55,12 @@ module Clacky
|
|
|
55
55
|
# todo_manager aliases
|
|
56
56
|
"todo" => "todo_manager",
|
|
57
57
|
"task_manager" => "todo_manager",
|
|
58
|
-
#
|
|
59
|
-
"
|
|
60
|
-
"user_feedback" => "
|
|
61
|
-
"ask" => "
|
|
58
|
+
# ask_user aliases
|
|
59
|
+
"request_user_feedback" => "ask_user",
|
|
60
|
+
"user_feedback" => "ask_user",
|
|
61
|
+
"ask" => "ask_user",
|
|
62
|
+
"ask_question" => "ask_user",
|
|
63
|
+
"clarify" => "ask_user",
|
|
62
64
|
# trash_manager aliases
|
|
63
65
|
"trash" => "trash_manager",
|
|
64
66
|
"delete" => "trash_manager",
|