Skip to content

Commit f37b655

Browse files
ericdalloeca-agent
andcommitted
Add $/progress server notification for init progress reporting
Sends start/finish lifecycle events for initialization tasks: models sync, remote server, plugins, MCP servers, and cleanup. Clients can track these to show aggregate init progress. 🤖 Generated with [eca](https://eca.dev) Co-Authored-By: eca <git@eca.dev>
1 parent aa77459 commit f37b655

File tree

7 files changed

+83
-35
lines changed

7 files changed

+83
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Fix ECA stop timing out.
6+
- Add `$/progress` server notification for initialization progress reporting (models sync, plugins, MCP servers, remote server, cleanup).
67

78
## 0.118.0
89

docs/protocol.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,6 +2185,28 @@ _Response:_
21852185

21862186
## General features
21872187

2188+
### progress (⬅️)
2189+
2190+
A notification from the server reporting the progress of an initialization task. Each task
2191+
sends a `start` notification when it begins and a `finish` notification when it completes (or fails).
2192+
Clients can track all tasks to derive aggregate initialization progress.
2193+
2194+
_Notification:_
2195+
2196+
* method: `$/progress`
2197+
* params: `ProgressParams` defined as follows:
2198+
2199+
```typescript
2200+
interface ProgressParams {
2201+
/** Whether this task is starting or finishing. */
2202+
type: "start" | "finish";
2203+
/** Stable identifier for the task (e.g. "models", "plugins", "mcp-servers"). */
2204+
taskId: string;
2205+
/** Human-readable label for the task. */
2206+
title: string;
2207+
}
2208+
```
2209+
21882210
### showMessage (⬅️)
21892211

21902212
A notification from server telling client to present a message to user.

src/eca/handlers.clj

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@
6363

6464
{:chat-welcome-message (welcome-message config)})))
6565

66+
(defn ^:private send-progress! [db* messenger params]
67+
(when-not (:stopping @db*)
68+
(try
69+
(messenger/progress messenger params)
70+
(catch Exception _))))
71+
6672
(defn initialized [{:keys [db* messenger config metrics start-remote-server!] :as components}]
6773
(metrics/task metrics :eca/initialized
6874
(let [sync-models-and-notify!
@@ -98,40 +104,50 @@
98104
messenger
99105
db*)))))))]
100106
(swap! db* assoc-in [:config-updated-fns :sync-models] #(sync-models-and-notify! %))
101-
(shared/future* config (sync-models-and-notify! config))))
102-
(when (get-in config [:remote :enabled])
103-
(start-remote-server! components))
104-
(future
105-
(Thread/sleep 1000) ;; wait chat window is open in some editors.
106-
(when-let [error (config/validation-error)]
107-
(messenger/chat-content-received
108-
messenger
109-
{:role "system"
110-
:content {:type :text
111-
:text (format "\nFailed to parse '%s' config, check stderr logs, double check your config and restart\n"
112-
error)}}))
113-
(config/listen-for-changes! db*))
114-
(future
115-
;; Resolve plugins before MCP init so plugin-provided servers are included
116-
(try
117-
(let [plugins-config (:plugins config)]
118-
(when (seq plugins-config)
119-
(reset! config/plugin-components* (f.plugins/resolve-all! plugins-config))))
120-
(catch Exception e
121-
(logger/warn "[PLUGINS]" "Plugin resolution failed:" (.getMessage e))))
122-
(config/deliver-plugins-resolved!)
123-
(let [config (config/all @db*)]
124-
;; Trigger sessionStart with plugin-aware config
125107
(shared/future* config
126-
(f.hooks/trigger-if-matches! :sessionStart
127-
(f.hooks/base-hook-data @db*)
128-
{}
129-
@db*
130-
config))
131-
(f.tools/init-servers! db* messenger config metrics)))
132-
(future
133-
(cache/cleanup-tool-call-outputs!)
134-
(db/cleanup-old-chats! db* metrics)))
108+
(do (send-progress! db* messenger {:type "start" :taskId "models" :title "Syncing models"})
109+
(sync-models-and-notify! config)
110+
(send-progress! db* messenger {:type "finish" :taskId "models" :title "Syncing models"}))))
111+
(when (get-in config [:remote :enabled])
112+
(send-progress! db* messenger {:type "start" :taskId "remote-server" :title "Starting remote server"})
113+
(start-remote-server! components)
114+
(send-progress! db* messenger {:type "finish" :taskId "remote-server" :title "Starting remote server"}))
115+
(future
116+
(Thread/sleep 1000) ;; wait chat window is open in some editors.
117+
(when-let [error (config/validation-error)]
118+
(messenger/chat-content-received
119+
messenger
120+
{:role "system"
121+
:content {:type :text
122+
:text (format "\nFailed to parse '%s' config, check stderr logs, double check your config and restart\n"
123+
error)}}))
124+
(config/listen-for-changes! db*))
125+
(future
126+
(send-progress! db* messenger {:type "start" :taskId "plugins" :title "Resolving plugins"})
127+
(try
128+
(let [plugins-config (:plugins config)]
129+
(when (seq plugins-config)
130+
(reset! config/plugin-components* (f.plugins/resolve-all! plugins-config))))
131+
(catch Exception e
132+
(logger/warn "[PLUGINS]" "Plugin resolution failed:" (.getMessage e))))
133+
(send-progress! db* messenger {:type "finish" :taskId "plugins" :title "Resolving plugins"})
134+
(config/deliver-plugins-resolved!)
135+
(let [config (config/all @db*)]
136+
;; Trigger sessionStart with plugin-aware config
137+
(shared/future* config
138+
(f.hooks/trigger-if-matches! :sessionStart
139+
(f.hooks/base-hook-data @db*)
140+
{}
141+
@db*
142+
config))
143+
(send-progress! db* messenger {:type "start" :taskId "mcp-servers" :title "Initializing MCP servers"})
144+
(f.tools/init-servers! db* messenger config metrics)
145+
(send-progress! db* messenger {:type "finish" :taskId "mcp-servers" :title "Initializing MCP servers"})))
146+
(future
147+
(send-progress! db* messenger {:type "start" :taskId "cleanup" :title "Cleaning up"})
148+
(cache/cleanup-tool-call-outputs!)
149+
(db/cleanup-old-chats! db* metrics)
150+
(send-progress! db* messenger {:type "finish" :taskId "cleanup" :title "Cleaning up"}))))
135151

136152
(defn workspace-did-change-folders [{:keys [db*]} params]
137153
(let [{:keys [added removed]} (:event params)
@@ -156,8 +172,8 @@
156172
config)
157173

158174
;; 3. Then shutdown
159-
(f.mcp/shutdown! db*)
160175
(swap! db* assoc :stopping true)
176+
(f.mcp/shutdown! db*)
161177
nil))
162178

163179
(defn chat-prompt [{:keys [messenger db* config metrics]} params]
@@ -292,4 +308,4 @@
292308
[{:keys [db* config metrics messenger]} params]
293309
(metrics/task metrics :eca/rewrite-prompt
294310
(handle-expected-errors
295-
(f.rewrite/prompt params db* config messenger metrics))))
311+
(f.rewrite/prompt params db* config messenger metrics))))

src/eca/messenger.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
(tool-server-updated [this params])
1414
(config-updated [this params])
1515
(showMessage [this msg])
16+
(progress [this params])
1617
(editor-diagnostics [this uri]))

src/eca/remote/messenger.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,9 @@
4545
(messenger/showMessage inner msg)
4646
(sse/broadcast! sse-connections* "session:message" (->camel msg)))
4747

48+
(progress [_this params]
49+
(messenger/progress inner params)
50+
(sse/broadcast! sse-connections* "session:progress" (->camel params)))
51+
4852
(editor-diagnostics [_this uri]
4953
(messenger/editor-diagnostics inner uri)))

src/eca/server.clj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@
165165
(showMessage [_this msg]
166166
(jsonrpc.server/discarding-stdout
167167
(jsonrpc.server/send-notification server "$/showMessage" msg)))
168+
(progress [_this params]
169+
(jsonrpc.server/discarding-stdout
170+
(jsonrpc.server/send-notification server "$/progress" params)))
168171
(editor-diagnostics [_this uri]
169172
(jsonrpc.server/discarding-stdout
170173
(jsonrpc.server/send-request server "editor/getDiagnostics" (assoc-some {} :uri uri)))))

test/eca/test_helper.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
(config-updated [_ data] (swap! messages* update :config-updated (fnil conj []) data))
3737
(tool-server-updated [_ data] (swap! messages* update :tool-server-update (fnil conj []) data))
3838
(showMessage [_ data] (swap! messages* update :show-message (fnil conj []) data))
39+
(progress [_ data] (swap! messages* update :progress (fnil conj []) data))
3940
(editor-diagnostics [_ _uri] (future {:diagnostics @diagnostics*})))
4041

4142
(defn ^:private make-components []

0 commit comments

Comments
 (0)