Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Auto-clear completed task lists: when a new prompt is sent and all tasks are done, the list is automatically cleared.

## 0.142.2

- `/context` now shows where auto-compaction triggers: a `🔲` marker on the threshold cell of the grid plus an `Auto-compaction at N%` line.
Expand Down
1 change: 0 additions & 1 deletion resources/prompts/tools/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ Workflow & Strict Execution Rules:
4. SUBAGENTS & PARALLEL WORK: For sequential work, keep only one task `in_progress`; later tasks must stay pending until you start them. Multiple tasks may be `in_progress` only for concurrent separate workstreams (e.g. separate subagents). Only the main agent updates the task list.
5. COMPLETION TIMING: Once a task is verified, close it as soon as possible. Do not delay completion just to align with other tasks.
6. ADAPTABILITY: If completing tasks reveals follow-up work, use 'add' to append new tasks.
7. CLEANUP: When all tasks are done and no further work remains, use 'clear'.
8 changes: 8 additions & 0 deletions src/eca/features/chat.clj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
[eca.features.skills :as f.skills]
[eca.features.tools :as f.tools]
[eca.features.tools.mcp :as f.mcp]
[eca.features.tools.task :as f.tools.task]
[eca.llm-api :as llm-api]
[eca.llm-providers.errors :as llm-providers.errors]
[eca.llm-util :as llm-util]
Expand Down Expand Up @@ -1653,6 +1654,13 @@
_ (when (and seeded-default-trust? provided-chat-id)
(config/notify-fields-changed-only! {:chat {:select-trust true}} messenger db* chat-id))]
(logger/with-chat-context chat-id (:parent-chat-id base-chat-ctx)
(when-let [cleared-details (f.tools.task/auto-clear-completed! db* chat-id)]
(logger/info logger-tag "Auto-cleared completed task list" {:chat-id chat-id})
(lifecycle/send-content! base-chat-ctx :assistant
{:type :toolCalled
:server "eca"
:name "task"
:details cleared-details}))
Comment on lines +1657 to +1663
(try
(prompt* params base-chat-ctx)
(catch Exception e
Expand Down
19 changes: 18 additions & 1 deletion src/eca/features/tools/task.clj
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,23 @@
[_name _arguments before-details result _ctx]
(or (:details result) before-details))

;; --- Auto-clear ---

(defn ^:private all-tasks-done?
"Returns true if the task list has tasks and all are :done."
[state]
(let [tasks (:tasks state)
{:keys [done pending in-progress]} (status-counts tasks)]
(and (pos? done) (zero? pending) (zero? in-progress))))

(defn auto-clear-completed!
"Clear the task list when all tasks are done.
Returns task-details of the cleared state if clearing occurred, nil otherwise."
[db* chat-id]
(when (all-tasks-done? (get-task @db* chat-id))
(mutate-task! db* chat-id (fn [_] {:state empty-task}))
(task-details empty-task)))
Comment on lines +268 to +274

;; --- Operations ---

(defn ^:private op-read [_arguments {:keys [db chat-id]}]
Expand Down Expand Up @@ -502,7 +519,7 @@
:items {:type "integer"}
:description "Task IDs (required for start/complete/delete)"}
:active_summary {:type "string"
:description "Summary of what will be done in the current active session. Required for start operation."}
:description "Summary of what will be done in the current active session. Required for start operation."}
:task {:type "object"
:description "Single task data (for add/update)"
:properties {:subject {:type "string" :description "Task subject/title (required)"}
Expand Down
45 changes: 45 additions & 0 deletions test/eca/features/tools/task_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,48 @@
result
nil)]
(is (= (:details result) details)))))

(deftest auto-clear-completed-test
(testing "clears task list when all tasks are done"
(let [db* (atom {:chats {"c1" {:task {:next-id 3
:active-summary nil
:tasks [{:id 1 :subject "T1" :description "D1" :status :done :priority :medium :blocked-by #{}}
{:id 2 :subject "T2" :description "D2" :status :done :priority :medium :blocked-by #{}}]}}}})
result (task/auto-clear-completed! db* "c1")]
(is (some? result))
(is (= :task (:type result)))
(is (empty? (:tasks result)))
(is (= {:next-id 1 :active-summary nil :tasks []}
(task/get-task @db* "c1")))))

(testing "does not clear when tasks are still pending"
(let [db* (atom {:chats {"c1" {:task {:next-id 3
:active-summary nil
:tasks [{:id 1 :subject "T1" :description "D1" :status :done :priority :medium :blocked-by #{}}
{:id 2 :subject "T2" :description "D2" :status :pending :priority :medium :blocked-by #{}}]}}}})
result (task/auto-clear-completed! db* "c1")]
(is (nil? result))
(is (= 2 (count (:tasks (task/get-task @db* "c1")))))))

(testing "does not clear when tasks are in progress"
(let [db* (atom {:chats {"c1" {:task {:next-id 3
:active-summary "working"
:tasks [{:id 1 :subject "T1" :description "D1" :status :done :priority :medium :blocked-by #{}}
{:id 2 :subject "T2" :description "D2" :status :in-progress :priority :medium :blocked-by #{}}]}}}})
result (task/auto-clear-completed! db* "c1")]
(is (nil? result))
(is (= 2 (count (:tasks (task/get-task @db* "c1")))))))

(testing "does nothing when task list is empty"
(let [db* (atom {:chats {"c1" {:task {:next-id 1 :active-summary nil :tasks []}}}})
result (task/auto-clear-completed! db* "c1")]
(is (nil? result))
(is (= {:next-id 1 :active-summary nil :tasks []}
(task/get-task @db* "c1")))))

(testing "does nothing when chat has no task list"
(let [db* (atom {})
result (task/auto-clear-completed! db* "c1")]
(is (nil? result))
(is (= {:next-id 1 :active-summary nil :tasks []}
(task/get-task @db* "c1"))))))
Loading