openclacky 1.5.10 → 1.5.12
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/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/cost_tracker.rb +2 -0
- data/lib/clacky/agent/llm_caller.rb +7 -3
- data/lib/clacky/agent/message_compressor_helper.rb +1 -1
- data/lib/clacky/agent/session_serializer.rb +60 -30
- data/lib/clacky/agent/skill_manager.rb +1 -2
- data/lib/clacky/agent/time_machine.rb +1 -0
- data/lib/clacky/agent/tool_executor.rb +1 -1
- data/lib/clacky/agent/tool_registry.rb +6 -4
- data/lib/clacky/agent.rb +197 -47
- data/lib/clacky/billing/billing_record.rb +3 -0
- data/lib/clacky/billing/billing_store.rb +31 -5
- data/lib/clacky/billing/platform_billing.rb +148 -0
- data/lib/clacky/brand_config.rb +15 -17
- data/lib/clacky/cancel_flag.rb +23 -0
- data/lib/clacky/cli.rb +2 -2
- data/lib/clacky/client.rb +5 -2
- data/lib/clacky/default_extensions/advisor/advisors/general.md +54 -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 +390 -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 +479 -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 +29 -0
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +125 -59
- data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +25 -39
- data/lib/clacky/default_extensions/git/panels/git/view.js +10 -0
- 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/new/SKILL.md +1 -1
- data/lib/clacky/default_skills/onboard/SKILL.md +2 -2
- data/lib/clacky/extension/hook_loader.rb +2 -0
- data/lib/clacky/extension/packager.rb +23 -10
- data/lib/clacky/extension/scaffold/templates/full/panels/dashboard/view.js.erb +12 -25
- data/lib/clacky/extension/scaffold/templates/hello/panels/hello/view.js.erb +3 -3
- data/lib/clacky/fanout.rb +50 -3
- data/lib/clacky/idle_compression_timer.rb +3 -5
- data/lib/clacky/json_ui_controller.rb +3 -2
- data/lib/clacky/mcp/http_transport.rb +1 -1
- data/lib/clacky/mcp/registry.rb +2 -3
- data/lib/clacky/mcp/stdio_transport.rb +21 -17
- data/lib/clacky/message_history.rb +38 -0
- data/lib/clacky/plain_ui_controller.rb +30 -17
- data/lib/clacky/platform_http_client.rb +14 -14
- data/lib/clacky/providers.rb +23 -10
- data/lib/clacky/rich_ui/rich_ui_controller.rb +6 -6
- 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/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 +25 -9
- data/lib/clacky/server/channel/channel_manager.rb +109 -27
- data/lib/clacky/server/channel/channel_ui_controller.rb +40 -8
- data/lib/clacky/server/http_server.rb +423 -71
- data/lib/clacky/server/scheduler.rb +4 -5
- data/lib/clacky/server/server_master.rb +15 -3
- data/lib/clacky/server/session_registry.rb +7 -2
- data/lib/clacky/server/web_ui_controller.rb +19 -18
- data/lib/clacky/session_manager.rb +82 -18
- data/lib/clacky/shell_hook_loader.rb +3 -3
- data/lib/clacky/shutdown.rb +71 -0
- data/lib/clacky/telemetry.rb +1 -1
- data/lib/clacky/thread_registry.rb +102 -0
- data/lib/clacky/tools/ask_user.rb +213 -0
- data/lib/clacky/tools/base.rb +5 -0
- data/lib/clacky/tools/terminal.rb +4 -1
- data/lib/clacky/tools/trash_manager.rb +16 -0
- data/lib/clacky/tools/web_search.rb +1 -1
- 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 +23 -19
- data/lib/clacky/ui_interface.rb +4 -4
- data/lib/clacky/utils/arguments_parser.rb +31 -4
- data/lib/clacky/utils/model_pricing.rb +59 -0
- data/lib/clacky/utils/parser_manager.rb +2 -2
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +750 -115
- data/lib/clacky/web/components/code-editor.js +33 -1
- data/lib/clacky/web/components/composer.js +277 -0
- data/lib/clacky/web/components/mentions.js +599 -0
- data/lib/clacky/web/components/notify.js +9 -7
- data/lib/clacky/web/core/aside.js +3 -3
- data/lib/clacky/web/core/ext.js +4 -6
- data/lib/clacky/web/features/billing/store.js +2 -6
- data/lib/clacky/web/features/billing/view.js +12 -10
- data/lib/clacky/web/features/channels/store.js +16 -1
- data/lib/clacky/web/features/channels/view.js +45 -2
- data/lib/clacky/web/features/new-session/view.js +49 -12
- data/lib/clacky/web/features/workspace/view.js +712 -77
- data/lib/clacky/web/i18n.js +80 -10
- data/lib/clacky/web/index.html +38 -15
- data/lib/clacky/web/sessions.js +538 -138
- data/lib/clacky/web/settings.js +32 -15
- data/lib/clacky/web/skills.js +11 -17
- data/lib/clacky/web/ws-dispatcher.js +22 -8
- data/lib/clacky.rb +4 -1
- metadata +15 -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: 858730306a6e6647d79a5ebb35160c52fba7ed26c62ec97043373e7f052332e2
|
|
4
|
+
data.tar.gz: b412bc405daae81f1b6d71dd4dd2dffc197a00ac97540ecfd4916f7daf58843d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 685d67556e68efd81a8bc446edd473a57939f0404b754e4432c7d29e073d2c47adc0c939fc5da3ea32144f3c6026aa17d73ff4766420399515089a3a8a4bc98d
|
|
7
|
+
data.tar.gz: dc85168d4b103185d20087635cfc5972c4e3e23b8dee95cb2ba28017d276dda425d8d92d23159dad463bd6cdcc29b8e02404d50dd4a79e5e6345193e8b4167c8
|
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.12] - 2026-08-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Add VS Code-style file tree with inline file viewer
|
|
11
|
+
- Add quick-access sidebar for directory picker with WSL support
|
|
12
|
+
- Add Web UI @mention references for files, directories and sessions
|
|
13
|
+
- Add Gemini 3.7 Flash model with STT/video support; switch OCR default to DeepSeek vision
|
|
14
|
+
- Add timestamps to assistant messages
|
|
15
|
+
- Merge platform usage API data with local billing records
|
|
16
|
+
- Refresh git panel on session completion
|
|
17
|
+
- Allow disabling local extensions
|
|
18
|
+
- Expose task mappings in core events (#496 - @YeEmrick)
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
- Move file-trash sweep off the session save path and speed up session creation
|
|
22
|
+
- Make recharge link a visible button on model cards
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Fix ask_user free-text input: render without options, guard IME composition, route Enter, render as text in IM channels, keep questions concise (#497 - @afumu)
|
|
26
|
+
- Fix advisor recommendations: send structured options, keep card state per session, dismiss button no longer permanently suppresses
|
|
27
|
+
- Fix home favorite pointing at the Windows profile under WSL
|
|
28
|
+
- Fix session list pagination and pin truncation
|
|
29
|
+
- Preserve task ids in compressed history (#500 - @YeEmrick)
|
|
30
|
+
- Clear stale media model override when reverting to auto
|
|
31
|
+
- Normalize gemini-3.7-flash name for pricing
|
|
32
|
+
- Fix sidebar scrollbar visibility and session list styling
|
|
33
|
+
- Fix messages loading created_at
|
|
34
|
+
- Render parallel fan-out subagent phases as flat, isolated cards
|
|
35
|
+
- Preserve subagent transcripts when an interrupted fan-out is resumed
|
|
36
|
+
- Fix spinner in subagent raise process
|
|
37
|
+
- Truncate messages carrying the system_injected flag
|
|
38
|
+
- Stop reporting awaiting_feedback for question-mark endings
|
|
39
|
+
- Detach channel_ui and keep channel_info unique on /bind and /unbind
|
|
40
|
+
|
|
41
|
+
## [1.5.11] - 2026-08-23
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- Creator Studio: separate Marketplace and Brand sections for published extensions
|
|
45
|
+
- Advisor extension for next-job suggestions
|
|
46
|
+
- ask_user tool with multi-question support (replaces feedback_user)
|
|
47
|
+
- Sidebar states for awaiting-feedback and just-finished sessions
|
|
48
|
+
- Auto-purge file-trash entries older than 8 days
|
|
49
|
+
- DeepSeek V4 flash vision exp model preset
|
|
50
|
+
- IM channel toggle to mute tool-call narration and previews
|
|
51
|
+
|
|
52
|
+
### Improved
|
|
53
|
+
- Unified internal thread management with graceful shutdown
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- Skill decrypt retry now picks up a renewed license key
|
|
57
|
+
- DeepSeek weekends billed entirely at off-peak rates
|
|
58
|
+
- Skill and extension zip downloads fall back to a secondary CDN host
|
|
59
|
+
- Session status dot aligned with name and channel badge
|
|
60
|
+
- Project session tag top margin
|
|
61
|
+
- Unified modal close button and ext-studio modal semantics
|
|
62
|
+
- Unified checkbox accent and distinct verified badge
|
|
63
|
+
- Edit-model modal overflow on small screens
|
|
64
|
+
- Subagent UI fixes
|
|
65
|
+
- Star History charts migrated to star-history.dera.page (#491 - @CrustyMozarella)
|
|
66
|
+
|
|
7
67
|
## [1.5.10] - 2026-08-19
|
|
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
|
|
|
@@ -136,11 +136,13 @@ module Clacky
|
|
|
136
136
|
# This ensures all API calls are recorded for statistics purposes.
|
|
137
137
|
billing_model = model || current_model
|
|
138
138
|
effective_cost = cost || 0.0 # Use 0 if pricing is unknown
|
|
139
|
+
provider = @config.provider_id_for(@config.current_model)
|
|
139
140
|
|
|
140
141
|
record = Billing::BillingRecord.new(
|
|
141
142
|
session_id: @session_id,
|
|
142
143
|
timestamp: Time.now,
|
|
143
144
|
model: billing_model,
|
|
145
|
+
provider: provider,
|
|
144
146
|
prompt_tokens: usage[:prompt_tokens] || 0,
|
|
145
147
|
completion_tokens: usage[:completion_tokens] || 0,
|
|
146
148
|
cache_read_tokens: usage[:cache_read_input_tokens] || 0,
|
|
@@ -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.
|
|
@@ -391,7 +391,11 @@ module Clacky
|
|
|
391
391
|
preview_path: f[:preview_path] || f["preview_path"] }
|
|
392
392
|
}
|
|
393
393
|
all_files = image_files + disk_files
|
|
394
|
-
|
|
394
|
+
references = Array(msg[:display_references])
|
|
395
|
+
ref_options = references.empty? ? {} : { references: references }
|
|
396
|
+
task_options = msg[:task_id] ? { task_id: msg[:task_id] } : {}
|
|
397
|
+
ui.show_user_message(raw_text, **task_options, created_at: msg[:created_at], files: all_files,
|
|
398
|
+
**ref_options,
|
|
395
399
|
editable: round[:editable] != false,
|
|
396
400
|
skill_command: msg[:skill_command],
|
|
397
401
|
skill_command_display: msg[:skill_command_display])
|
|
@@ -490,30 +494,34 @@ module Clacky
|
|
|
490
494
|
current_role = nil
|
|
491
495
|
current_lines = []
|
|
492
496
|
current_nested_chunk = nil # chunk reference from a Compressed Summary heading
|
|
497
|
+
current_task_id = nil
|
|
493
498
|
|
|
494
499
|
raw.each_line do |line|
|
|
495
500
|
stripped = line.chomp
|
|
496
501
|
if (m = stripped.match(/\A## Assistant \[Compressed Summary — original conversation at: (.+)\]/))
|
|
497
502
|
# Nested chunk reference — record it, treat as assistant section
|
|
498
|
-
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk } if current_role
|
|
503
|
+
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk, task_id: current_task_id } if current_role
|
|
499
504
|
current_role = "assistant"
|
|
500
505
|
current_lines = []
|
|
501
506
|
current_nested_chunk = File.join(chunk_dir, m[1])
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
current_role
|
|
507
|
+
current_task_id = nil
|
|
508
|
+
elsif (m = stripped.match(/\A## (User|Assistant)(?: \[Task ([1-9]\d*)\])?\z/))
|
|
509
|
+
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk, task_id: current_task_id } if current_role
|
|
510
|
+
current_role = m[1].downcase
|
|
505
511
|
current_lines = []
|
|
506
512
|
current_nested_chunk = nil
|
|
513
|
+
current_task_id = m[2]&.to_i
|
|
507
514
|
elsif stripped.match?(/\A### Tool Result:/)
|
|
508
|
-
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk } if current_role
|
|
515
|
+
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk, task_id: current_task_id } if current_role
|
|
509
516
|
current_role = "tool"
|
|
510
517
|
current_lines = []
|
|
511
518
|
current_nested_chunk = nil
|
|
519
|
+
current_task_id = nil
|
|
512
520
|
else
|
|
513
521
|
current_lines << line
|
|
514
522
|
end
|
|
515
523
|
end
|
|
516
|
-
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk } if current_role
|
|
524
|
+
sections << { role: current_role, lines: current_lines.dup, nested_chunk: current_nested_chunk, task_id: current_task_id } if current_role
|
|
517
525
|
|
|
518
526
|
# Remove front-matter / header noise sections (nil role or non-user/assistant/tool)
|
|
519
527
|
sections.select! { |s| %w[user assistant tool].include?(s[:role]) }
|
|
@@ -544,14 +552,16 @@ module Clacky
|
|
|
544
552
|
round_index += 1
|
|
545
553
|
# Synthetic timestamp: spread rounds backwards from archived_at
|
|
546
554
|
synthetic_ts = base_time - (sections.size - round_index) * 1.0
|
|
555
|
+
user_msg = {
|
|
556
|
+
role: "user",
|
|
557
|
+
content: text,
|
|
558
|
+
created_at: synthetic_ts,
|
|
559
|
+
ext_events: sec_ext_events,
|
|
560
|
+
_from_chunk: true
|
|
561
|
+
}
|
|
562
|
+
user_msg[:task_id] = sec[:task_id] if sec[:task_id]
|
|
547
563
|
current_round = {
|
|
548
|
-
user_msg:
|
|
549
|
-
role: "user",
|
|
550
|
-
content: text,
|
|
551
|
-
created_at: synthetic_ts,
|
|
552
|
-
ext_events: sec_ext_events,
|
|
553
|
-
_from_chunk: true
|
|
554
|
-
},
|
|
564
|
+
user_msg: user_msg,
|
|
555
565
|
events: [],
|
|
556
566
|
# editable: false — this message was archived into a chunk MD and no
|
|
557
567
|
# longer exists in the active in-memory @history, so it cannot be
|
|
@@ -733,7 +743,7 @@ module Clacky
|
|
|
733
743
|
else
|
|
734
744
|
raw_text
|
|
735
745
|
end
|
|
736
|
-
ui.show_assistant_message(text, files: [])
|
|
746
|
+
ui.show_assistant_message(text, files: [], created_at: msg[:created_at])
|
|
737
747
|
end
|
|
738
748
|
|
|
739
749
|
# Tool calls embedded in assistant message
|
|
@@ -742,16 +752,17 @@ module Clacky
|
|
|
742
752
|
args_raw = tc[:arguments] || tc.dig(:function, :arguments) || {}
|
|
743
753
|
args = args_raw.is_a?(String) ? (JSON.parse(args_raw) rescue args_raw) : args_raw
|
|
744
754
|
|
|
745
|
-
# Special handling:
|
|
746
|
-
#
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
options = args.is_a?(Hash) ? (args[:options] || args["options"]) : nil
|
|
752
|
-
options = Array(options) if options && !options.is_a?(Array)
|
|
755
|
+
# Special handling: the ask_user question is shown as an assistant
|
|
756
|
+
# message (matching real-time behavior), not as a tool call.
|
|
757
|
+
if Clacky::Tools::AskUser.feedback_tool?(name)
|
|
758
|
+
questions = Clacky::Tools::AskUser.normalize_questions(args)
|
|
759
|
+
context = Clacky::Tools::AskUser.fetch_key(args, :context).to_s
|
|
760
|
+
first = questions.first
|
|
753
761
|
|
|
754
|
-
|
|
762
|
+
unless first.nil?
|
|
763
|
+
ui.show_feedback_request(first[:question], context, first[:options],
|
|
764
|
+
questions: questions)
|
|
765
|
+
end
|
|
755
766
|
else
|
|
756
767
|
ui.show_tool_call(name, args)
|
|
757
768
|
end
|
|
@@ -760,6 +771,11 @@ module Clacky
|
|
|
760
771
|
# Emit token usage stored on this message (for history replay display)
|
|
761
772
|
ui.show_token_usage(msg[:token_usage]) if msg[:token_usage]
|
|
762
773
|
|
|
774
|
+
# Interrupted fan-out anchors its captured subagent trails onto the
|
|
775
|
+
# last message, which is usually this assistant turn (its tool result
|
|
776
|
+
# never got written). Replay them here so they survive a reload.
|
|
777
|
+
replay_subagent_transcript(msg, ui)
|
|
778
|
+
|
|
763
779
|
when "user"
|
|
764
780
|
# Anthropic-format tool results (role: user, content: array of tool_result blocks)
|
|
765
781
|
if msg[:content].is_a?(Array)
|
|
@@ -796,19 +812,32 @@ module Clacky
|
|
|
796
812
|
end
|
|
797
813
|
end
|
|
798
814
|
|
|
799
|
-
# Replay
|
|
815
|
+
# Replay subagent transcripts stored on a tool result message. Emits a
|
|
800
816
|
# bracketed sequence of UI events the frontend can render as a collapsible
|
|
801
817
|
# sub-process block: subagent_start → (assistant_message / tool_call /
|
|
802
|
-
# tool_result)* → subagent_end.
|
|
818
|
+
# tool_result)* → subagent_end. A fan-out batch renders one block per job,
|
|
819
|
+
# in the caller's original job order.
|
|
820
|
+
#
|
|
821
|
+
# Sessions saved before fan-out support stored a bare Hash here.
|
|
803
822
|
def replay_subagent_transcript(msg, ui)
|
|
804
|
-
|
|
805
|
-
|
|
823
|
+
stored = msg[:subagent_transcript]
|
|
824
|
+
Array(stored.is_a?(Hash) ? [stored] : stored).each do |transcript|
|
|
825
|
+
next unless transcript.is_a?(Hash)
|
|
806
826
|
|
|
827
|
+
replay_one_subagent_transcript(transcript, ui)
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
def replay_one_subagent_transcript(transcript, ui)
|
|
807
832
|
events = transcript[:events] || transcript["events"] || []
|
|
808
833
|
return if events.empty?
|
|
809
834
|
|
|
835
|
+
skill = transcript[:skill] || transcript["skill"]
|
|
836
|
+
supports_phase = ui.respond_to?(:phase_start) && ui.respond_to?(:phase_end)
|
|
837
|
+
pid = ui.phase_start(kind: "fanout_subagent", label: skill) if supports_phase
|
|
838
|
+
|
|
810
839
|
ui.show_subagent_start(
|
|
811
|
-
skill:
|
|
840
|
+
skill: skill,
|
|
812
841
|
iterations: transcript[:iterations] || transcript["iterations"],
|
|
813
842
|
cost_usd: transcript[:cost_usd] || transcript["cost_usd"]
|
|
814
843
|
)
|
|
@@ -821,7 +850,7 @@ module Clacky
|
|
|
821
850
|
case role
|
|
822
851
|
when "assistant"
|
|
823
852
|
text = extract_text_from_content(content).to_s.strip
|
|
824
|
-
ui.show_assistant_message(text, files: []) unless text.empty?
|
|
853
|
+
ui.show_assistant_message(text, files: [], created_at: ev[:created_at] || ev["created_at"]) unless text.empty?
|
|
825
854
|
Array(tool_calls).each do |tc|
|
|
826
855
|
name = tc[:name] || tc["name"] || ""
|
|
827
856
|
args_raw = tc[:arguments] || tc["arguments"] || {}
|
|
@@ -834,6 +863,7 @@ module Clacky
|
|
|
834
863
|
end
|
|
835
864
|
|
|
836
865
|
ui.show_subagent_end
|
|
866
|
+
ui.phase_end(pid) if supports_phase && pid
|
|
837
867
|
end
|
|
838
868
|
|
|
839
869
|
# Replace the system message in @messages with a freshly built system prompt.
|
|
@@ -651,8 +651,7 @@ module Clacky
|
|
|
651
651
|
# observe() attaches each trail to its own tool result message.
|
|
652
652
|
# Kept out of the LLM payload via INTERNAL_FIELDS.
|
|
653
653
|
if tool_call_id
|
|
654
|
-
|
|
655
|
-
extract_subagent_transcript(subagent, skill.identifier)
|
|
654
|
+
record_subagent_transcript(tool_call_id, subagent, skill.identifier)
|
|
656
655
|
end
|
|
657
656
|
|
|
658
657
|
# Mutate the subagent_instructions message in-place to become the result summary
|
|
@@ -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",
|