dsh-taskloop
v0.1.0-alpha.10
Published
Taskloop task workspace for DSH: list, board, task detail, context, and execution timeline
Readme
dsh-taskloop
Taskloop is an Agent-facing todo workspace for DSH. People publish Tasks and follow execution progress; Agents move the work forward. It uses a restrained task-list UI inspired by TickTick and provides:
- one all-tasks list, projects, and task creation;
- a separate trash view with task restore;
- one level of full Task children under a parent goal, with creation, association, and persistent order;
- read-only checklist and related-document views parsed from the task body;
- configurable board statuses; a state with task references, including trash, cannot be deleted;
- live unfinished top-level goal counts, a refresh action, and floating task details when list space is limited;
- task details with separate execution status, progress, tags, and related documents;
- a collaboration trace with action headings; title and status changes show their new value;
- a three-column desktop layout with the project sidebar, task list, and task editor; task title and body changes auto-save;
- SQLite persistence for tasks, projects, task events, contexts, and tool calls;
- revision checks for task writes, transactional writes with their audit records, and idempotent Agent progress submissions;
- a right-Sidebar entry plus a full-browser page at
/taskloop.
Opening a task shows its title as text. Click the title to edit it. Enter during IME
composition confirms the input; ordinary Enter saves the title. Add, edit, check, or
remove checklist items and related documents in the task body. New task bodies start empty. Task selection adds ?taskid=<id> to the current
URL; reloading that URL opens the same task. Project and tag order is stored in
SQLite. Drag the handle to reorder; Space and arrow keys provide keyboard controls.
Auto-save submits only changed title/body fields. Clean fields follow newly read
server values; conflicting local input is preserved. Leaving a task waits for pending
writes, including edits or reversions made during a save. If a write fails, retry it
or explicitly discard the local title/body through 重新加载. Deleting a project or
tag refreshes task revisions, including trash, before later edits or restores.
Search covers the complete plain-text body, including content after the short list
description, without matching HTML attributes.
Use ## 检查项 with one - [ ] item or - [x] item per line. Use ## 相关文档
with [type][name][URL, group number, or protocol address][desc] per line. The
parser also accepts Markdown links and standalone URLs. The visible text of each
document, including brackets and address, is limited to 150 Unicode characters.
HTTP and supported app protocol addresses render as links; group numbers remain
text. The sidebar lists are read-only views. Edit every checklist and document in
the task body; SQLite stores derived projections and document history.
Parent goals and subtasks
A top-level Task can have one level of children. Each child is a full Task with
its own body, status, priority, dates, tags, checklist, progress, related documents,
tool calls, and history. parentId identifies its parent; sortOrder preserves
child order. Self-parenting, cycles, and grandchildren are rejected.
In parent details, press Enter to create a child and use pointer or keyboard controls to reorder children. Task menus can add a child, associate an existing Task through a searchable parent picker, change its parent, or detach it. Child details can return to the parent; each child's task URL still opens directly.
Children appear as nested second-level nodes under their parent in the main list, expanded by default, with indentation and connector lines. Manually collapse or expand each parent group. Search and filters retain parent context for matching children and count matching roots and children separately. Project and sidebar unfinished counts count top-level goals. Children follow the parent's project, including trashed children when the parent changes project.
The completion button completes or reopens a task directly. A parent with active
children waits until all are done and a person selects 验收并完成; it does not
complete automatically. Agent and system completion is rejected with
TASK_PARENT_REVIEW_REQUIRED. Reopen a completed parent before creating,
associating, restoring, or reopening an unfinished child.
Deleting a parent moves its active children to trash in the same batch. Restoring it restores only that batch; independently deleted children stay in trash. The trash UI asks the person to restore a deleted parent before restoring its child.
Product direction
Taskloop is designed around a human-controlled workflow: people advance work and inspect the trace, while Agents make changes through explicit interactions. The workspace gathers enterprise information from IM, meeting notes, and documents, then carries that context into task execution. Tool calls are recorded as traceable nodes so a task can link back to the session that produced each action.
When it runs inside DSH, the workspace tabs are rendered in the top bar and the plugin itself remains in the host's right Sidebar. When opened as the full browser page, the workspace keeps its left sidebar and its overview tabs. The Host owns the storage boundary. Tasks are stored in SQLite at:
$DSH_HOME/tita-agent/taskloop/taskloop.sqliteIf DSH_HOME is not set, the default is
~/.dsh/tita-agent/taskloop/taskloop.sqlite. The browser surface uses the
same /taskloop/api routes in the embedded and full-page modes, so task
creation and human/Agent actions survive a reload.
The host must run on Node.js 22.5 or newer because the plugin uses Node's
built-in node:sqlite runtime module. No separate database service is
required.
Existing SQLite databases migrate in place by adding parent_id, sort_order,
delete_batch_id, and the derived body_text search field. Existing bodies are
backfilled transactionally without changing task revisions. Hierarchy writes retain
revision checks, transactional audit records, and Agent idempotency.
Task content editing uses Tiptap, an open-source, headless React editor built on ProseMirror. The editor stores the rendered body as content_html while the short description remains available for list search and summaries. Uploaded images are streamed to the local assets directory and the body stores their relative URL; legacy Base64 images remain readable.
The basic API is:
GET/POST/PUT /taskloop/api/tasksfor reading and creating tasks.POSTacceptsparentIdto create a child.PUTonly seeds an empty database; it cannot replace existing work or history. A task can omit its project;GET /taskloop/api/trashandPOST /taskloop/api/tasks/:id/restorefor soft-deleted tasks;GET/PATCH /taskloop/api/tasks/:idfor task details and rich-text edits.GETreturnstask, an optionalparent, and a boundedsubtaskssummary withtotal,done,items, andhasMore;itemscontains at most 25 children.PATCHacceptsparentId; an empty string ornulldetaches the task;PUT /taskloop/api/tasks/:id/subtasks/orderwith{ "ids": [...], "expectedRevision": n }for child order.idsmust contain the complete active child set exactly once. A successful reorder updates the parent and child revisions;GET/POST/DELETE /taskloop/api/projectsand/taskloop/api/projects/:idfor projects;GET/POST /taskloop/api/tagsandDELETE /taskloop/api/tags/:idfor the managed tag catalog. Deleting a tag removes only that tag from tasks;GET/POST /taskloop/api/statusesandDELETE /taskloop/api/statuses/:idfor the workflow catalog. Statuses have a name andtodo,inProgress, ordonecategory. At least one state must remain;PUT /taskloop/api/projects/orderand/taskloop/api/tags/orderwith{ "ids": [...] }for persistent catalog order;POST /taskloop/api/tasks/:id/actionsfor status changes, Agent updates, and human Agent requests. In a DSH session, a request is sent to that session's Conversation service and then recorded in the task history;GET/POST /taskloop/api/tasks/:id/documentsandPATCH/DELETE /taskloop/api/tasks/:id/documents/:documentIdfor related documents; edits and deletes requireexpectedRevision. related documents use{ "content": "[type][name][address][desc]" };GET/POST /taskloop/api/tasks/:id/progressfor immutable execution progress;GET/POST /taskloop/api/tasks/:id/tool-callsfor traceable tool calls;GET /taskloop/api/tasks/:id/eventsfor the task audit trail.
Agent tools
Taskloop registers eight narrow tools through ctx.tools.register. Every tool
uses the taskloop_ prefix so it does not collide with tools from other plugins:
taskloop_task_list: query task summaries by status id, priority, tag, project, and keyword;parent_id: "<id>"returns that parent's children andparent_id: ""returns top-level tasks. Usecursorfor later pages. Returns ordered status, project, and tag catalogs, plusdefaultStatus;taskloop_task_get: read one task, its complete checklist, workflow semantics, and a bounded plain-textbodyText. The task body is the source for checklist, related document, and progress content. Use## 检查项with- [ ]/- [x]items,## 相关文档with[类型][名称][地址][说明], Markdown links, or standalone URLs, and## 进展with one### <时间> · <角色>heading per entry. The same format is rendered back verbatim, so a body read and written back is unchanged. Children includeparentId,parentTitle, andsortOrder; parents with active children include a boundedsubtaskssummary. Rich HTML, complete description, and history are not returned;taskloop_task_activity_list: read the body's progress entries (kind: progress, newest first), related documents (kind: documents), event headings, or tool-call summaries one bounded page at a time.include_deleted: trueshows removed document history;taskloop_task_create: create work with a title, plain-text body, existing status id, project, start date, end date, priority, and tags; optionalparent_idcreates a full child Task;taskloop_task_update: update the task title, body, project, dates, priority, tags, status, orparent_id; an emptyparent_iddetaches the task. Only supply the fields to change.body_mode: appendinserts the new prose in front of the structured sections, so appended text never becomes a checklist item or a progress report;taskloop_status_create: create a unique board state with a name andtodo,inProgress, ordonecategory;taskloop_status_delete: delete an unused state. References from active or trashed tasks block deletion, and at least one state must remain;taskloop_task_submit_progress: append one progress entry to the body, headed by the local time and the actorAgent, and keep its DSH session, call, and turn references in the task history. The result carries the updated task, whose revision the next write must use.
Hierarchy uses these existing tools. The child summary contains at most 25 items;
read further children through paginated taskloop_task_list calls with
parent_id. Agents can complete children, but a person must accept their parent.
Every Agent mutation requires an idempotency_key. Reuse the same key and the same
arguments for network retries. Task updates require the revision returned by
taskloop_task_get or the latest write result. Stale writes are rejected instead of
replacing newer data. Checklist and document changes are task body updates, so use the
task revision from the mutation result for the next write. The same SQLite
transaction stores the mutation and its replay result.
Agent progress uses this Agent-scoped operation record for deduplication, so
different Agents may use the same key independently. HTTP progress keys remain
task-scoped and reject retries with different summary/body content.
Replay results are retained for 30 days. Older keys stay reserved, so a very late
retry fails safely instead of applying the mutation a second time.
status is an execution-state id from statuses; statusName and statusCategory
explain its meaning. priority is high, normal, or low. tags are only
classification labels: a state id or name is rejected as a tag. Execution reports
belong in taskloop_task_submit_progress; submitting progress does not change the
task state automatically.
task.checklist and the progress and documents activity pages are parsed views of
the task body. Change them with taskloop_task_update by replacing the complete body;
use taskloop_task_submit_progress to append one progress entry without rewriting the
body. Read up to 20,000 characters with taskloop_task_get; if it returns
bodyTruncated: true, do not replace the body with incomplete text. The documents
activity page keeps a read-only history of changes found in the body, and a document
line is at most 150 visible characters.
Start/end dates use YYYY-MM-DD; use an empty string to clear a date or project.
end_at is the single deadline field; the former due tool argument is removed.
The former checklist and document mutation tools are no longer available. The documents activity kind remains read-only. Unknown or obsolete arguments fail explicitly. Error messages explain how to recover from task revision conflicts, invalid states, oversized document content, and reused idempotency keys. After a revision conflict, read the latest task and issue a new operation with a new key.
When body is included in taskloop_task_update, body_mode is required. Use
replace to replace the body, or append to retain existing rich text and append
escaped plain text. This avoids accidental loss of formatting and images.
Agent inputs and persisted fields have explicit size limits. Create/update results
omit body fields and legacy progress placeholders. The bounded bodyText in
taskloop_task_get is the body read path. Progress and tool-call activity pages omit
rich HTML and complete tool input/output payloads. Event pages use the same heading
rules as the UI: only title and state changes include their new value.
Progress is deliberately not a field accepted by taskloop_task_update. One progress
entry is one ## 进展 block in the body, so the panel, the trace, and the Agent read
the same text; taskloop_task_submit_progress writes it and emits a
progress_submitted event that keeps the Agent's DSH reference. An Agent that replaces
the body must therefore read it first, or the entry it just appended is lost. Rows
written by the retired progress API stay readable through the progress activity page
(source: "projection") and are no longer written.
Build
pnpm --filter dsh-taskloop build
pnpm --filter dsh-taskloop test