dsh-taskboard 0.2.2 → 0.4.0
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.
- package/README.md +62 -6
- package/lib/client.js +1839 -74
- package/lib/host/execution.js +199 -54
- package/lib/host/execution.js.map +1 -1
- package/lib/host/git.js +327 -0
- package/lib/host/git.js.map +1 -0
- package/lib/host/protocol-text.js +5 -3
- package/lib/host/protocol-text.js.map +1 -1
- package/lib/host/routes.js +435 -5
- package/lib/host/routes.js.map +1 -1
- package/lib/host/store.js +12 -0
- package/lib/host/store.js.map +1 -1
- package/lib/host/templates.js +166 -0
- package/lib/host/templates.js.map +1 -0
- package/lib/host/tools.js +217 -3
- package/lib/host/tools.js.map +1 -1
- package/lib/index.js +23 -3
- package/lib/index.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/protocol.js +286 -1
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +74 -74
- package/src/client/api.ts +47 -3
- package/src/client/board/ImportModal.tsx +182 -0
- package/src/client/board/TaskBoard.tsx +118 -3
- package/src/client/board/TaskCard.tsx +9 -0
- package/src/client/board/TaskDetail.tsx +360 -4
- package/src/client/board/TaskFormModal.tsx +193 -12
- package/src/client/board/TemplateManager.tsx +121 -0
- package/src/client/controller.ts +238 -11
- package/src/client/index.ts +18 -1
- package/src/client/styles.ts +198 -0
- package/src/host/execution.ts +301 -67
- package/src/host/git.ts +370 -0
- package/src/host/protocol-text.ts +5 -3
- package/src/host/routes.ts +483 -5
- package/src/host/store.ts +13 -0
- package/src/host/templates.ts +143 -0
- package/src/host/tools.ts +215 -2
- package/src/index.ts +30 -1
- package/src/shared/api.ts +89 -3
- package/src/shared/protocol.ts +408 -0
- package/src/shared/version.ts +1 -1
package/lib/host/tools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { asStatus, asUrgency, canTransition, effectivePrompt, isClaim, isClaimedBy, newCommentId, newTaskId, normalizeBody, normalizeExecution, normalizeModel, normalizePrompt, normalizeTitle, summarize, syncClaim } from "../shared/protocol.js";
|
|
1
|
+
import { asIsolation, asStatus, asUrgency, canTransition, checklistFromTexts, effectivePrompt, isClaim, isClaimedBy, newCommentId, newTaskId, normalizeBody, normalizeExecution, normalizeExecutionReport, normalizeModel, normalizePrompt, normalizeTitle, summarize, syncClaim } from "../shared/protocol.js";
|
|
2
2
|
import { defineTool } from "./sdk.js";
|
|
3
3
|
//#region src/host/tools.ts
|
|
4
4
|
/** Render side: one compact task line (id/status/version are load-bearing). */
|
|
@@ -7,6 +7,7 @@ function taskLine(t) {
|
|
|
7
7
|
if (t.blocked) parts.push("·受阻");
|
|
8
8
|
if (t.executionMode === "scheduled") parts.push("·定时");
|
|
9
9
|
if (t.commentCount !== void 0 && t.commentCount > 0) parts.push(`·评论${t.commentCount}`);
|
|
10
|
+
if (t.checklist !== void 0 && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`);
|
|
10
11
|
if (t.lastExecutionOutcome !== void 0) parts.push(`·上次执行${t.lastExecutionOutcome}`);
|
|
11
12
|
if (t.trashed === true) parts.push("·已删");
|
|
12
13
|
return parts.join(" ");
|
|
@@ -16,14 +17,26 @@ function taskDetail(t) {
|
|
|
16
17
|
const lines = [
|
|
17
18
|
`任务 ${t.id} 「${t.title}」`,
|
|
18
19
|
`状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? " · 受阻" : ""}`,
|
|
19
|
-
`执行方式: ${t.execution.mode}${t.execution.cron !== void 0 ? ` cron=${t.execution.cron}` : ""}
|
|
20
|
+
`执行方式: ${t.execution.mode}${t.execution.cron !== void 0 ? ` cron=${t.execution.cron}` : ""}`,
|
|
21
|
+
`隔离: ${t.isolation === "none" ? "关闭(原目录执行)" : "Git Worktree"}${t.branch !== void 0 ? `(分支 ${t.branch})` : ""}`
|
|
20
22
|
];
|
|
21
23
|
const holder = isClaimedBy(t);
|
|
22
24
|
if (holder !== void 0) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`);
|
|
23
25
|
if (t.execution.nextRunAt !== void 0) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`);
|
|
24
26
|
if (t.model !== void 0) lines.push(`固定模型: ${t.model.provider}/${t.model.model}`);
|
|
27
|
+
if (t.presetId !== void 0) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`);
|
|
25
28
|
lines.push(`描述: ${t.description.length > 0 ? t.description : "(无)"}`);
|
|
26
29
|
lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`);
|
|
30
|
+
if (t.checklist !== void 0 && t.checklist.length > 0) {
|
|
31
|
+
const done = t.checklist.filter((i) => i.checked).length;
|
|
32
|
+
lines.push(`验收清单 (${done}/${t.checklist.length}):`);
|
|
33
|
+
for (const item of t.checklist) {
|
|
34
|
+
const mark = item.checked ? "☑" : "☐";
|
|
35
|
+
const who = item.checkedBy === void 0 ? "" : item.checkedBy === "user" ? " ·用户勾选" : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`;
|
|
36
|
+
const note = item.note !== void 0 ? ` ·证据: ${item.note}` : "";
|
|
37
|
+
lines.push(` ${mark} ${item.text}${who}${note}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
if (t.comments.length > 0) {
|
|
28
41
|
lines.push(`评论 (${t.comments.length}):`);
|
|
29
42
|
for (const c of t.comments) {
|
|
@@ -36,7 +49,8 @@ function taskDetail(t) {
|
|
|
36
49
|
for (const e of t.executions) {
|
|
37
50
|
const at = e.startedAt !== void 0 ? new Date(e.startedAt).toISOString() : "?";
|
|
38
51
|
const err = e.error !== void 0 ? ` 错误: ${e.error}` : "";
|
|
39
|
-
|
|
52
|
+
const report = e.report !== void 0 ? " [已交报告]" : "";
|
|
53
|
+
lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`);
|
|
40
54
|
}
|
|
41
55
|
} else lines.push("执行记录: 无");
|
|
42
56
|
const updatedBy = t.updatedBy.kind === "agent" ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : "user";
|
|
@@ -293,6 +307,19 @@ function registerTaskboardTools(ctx, deps) {
|
|
|
293
307
|
description: "Provider-owned model id."
|
|
294
308
|
}
|
|
295
309
|
}
|
|
310
|
+
},
|
|
311
|
+
isolation: {
|
|
312
|
+
type: "string",
|
|
313
|
+
description: "Code isolation for executions: \"worktree\" (default — each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction)."
|
|
314
|
+
},
|
|
315
|
+
presetId: {
|
|
316
|
+
type: "string",
|
|
317
|
+
description: "Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional."
|
|
318
|
+
},
|
|
319
|
+
checklist: {
|
|
320
|
+
type: "array",
|
|
321
|
+
description: `Acceptance checklist (DoD) item texts (≤30 × 200 chars); agents check them off at handoff, the user reviews.`,
|
|
322
|
+
items: { type: "string" }
|
|
296
323
|
}
|
|
297
324
|
},
|
|
298
325
|
output: {
|
|
@@ -315,6 +342,9 @@ function registerTaskboardTools(ctx, deps) {
|
|
|
315
342
|
if (status === "done" || status === "archived") throw new ToolError(ERR.invalidTransition, "a new task cannot start as done/archived");
|
|
316
343
|
const execution = normalizeExecution(args.execution ?? {}, deps.now());
|
|
317
344
|
const model = args.model !== void 0 ? checkModel(deps, args.model) : void 0;
|
|
345
|
+
const isolation = args.isolation === void 0 ? void 0 : asIsolation(args.isolation);
|
|
346
|
+
const presetId = args.presetId?.trim() || void 0;
|
|
347
|
+
const checklist = args.checklist !== void 0 ? checklistFromTexts(args.checklist) : void 0;
|
|
318
348
|
const now = deps.now();
|
|
319
349
|
const task = {
|
|
320
350
|
id: newTaskId(),
|
|
@@ -327,6 +357,9 @@ function registerTaskboardTools(ctx, deps) {
|
|
|
327
357
|
blocked: false,
|
|
328
358
|
execution,
|
|
329
359
|
model,
|
|
360
|
+
...isolation !== void 0 ? { isolation } : {},
|
|
361
|
+
...presetId !== void 0 ? { presetId } : {},
|
|
362
|
+
...checklist !== void 0 ? { checklist } : {},
|
|
330
363
|
version: 1,
|
|
331
364
|
createdAt: now,
|
|
332
365
|
updatedAt: now,
|
|
@@ -622,6 +655,187 @@ function registerTaskboardTools(ctx, deps) {
|
|
|
622
655
|
}
|
|
623
656
|
}
|
|
624
657
|
})));
|
|
658
|
+
disposers.push(register(defineTool({
|
|
659
|
+
name: "taskboard_checklist",
|
|
660
|
+
description: "Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), \"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.",
|
|
661
|
+
parameters: {
|
|
662
|
+
id: {
|
|
663
|
+
type: "string",
|
|
664
|
+
required: true,
|
|
665
|
+
description: "Task id."
|
|
666
|
+
},
|
|
667
|
+
action: {
|
|
668
|
+
type: "string",
|
|
669
|
+
required: true,
|
|
670
|
+
description: "add | check | uncheck."
|
|
671
|
+
},
|
|
672
|
+
ifVersion: {
|
|
673
|
+
type: "number",
|
|
674
|
+
required: true,
|
|
675
|
+
description: "Task version you read; fails on mismatch."
|
|
676
|
+
},
|
|
677
|
+
items: {
|
|
678
|
+
type: "array",
|
|
679
|
+
description: "Item texts to append (action=add only; 1..10 per call, 200 chars each).",
|
|
680
|
+
items: { type: "string" }
|
|
681
|
+
},
|
|
682
|
+
itemId: {
|
|
683
|
+
type: "string",
|
|
684
|
+
description: "The checklist item id (action=check/uncheck)."
|
|
685
|
+
},
|
|
686
|
+
note: {
|
|
687
|
+
type: "string",
|
|
688
|
+
description: "Evidence note recorded with the check (≤400 chars)."
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
output: {
|
|
692
|
+
schema: JSON_OUT,
|
|
693
|
+
render: (_args, value) => {
|
|
694
|
+
const v = value;
|
|
695
|
+
if (v.task === void 0 || v.checklist === void 0) return [{
|
|
696
|
+
type: "text",
|
|
697
|
+
text: "清单操作失败。"
|
|
698
|
+
}];
|
|
699
|
+
const lines = v.checklist.map((i, index) => `${i.checked === true ? "☑" : "☐"} [${index + 1}] ${String(i.text)}${i.note !== void 0 ? `(证据: ${String(i.note)})` : ""} id=${String(i.id)}`);
|
|
700
|
+
return [{
|
|
701
|
+
type: "text",
|
|
702
|
+
text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\n${lines.join("\n")}`
|
|
703
|
+
}];
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
async execute(args, exec) {
|
|
707
|
+
try {
|
|
708
|
+
const { actor } = caller(exec);
|
|
709
|
+
const task = store.get(args.id);
|
|
710
|
+
if (task === void 0 || task.trashedAt !== void 0) throw new ToolError(ERR.notFound, `no task ${args.id}`);
|
|
711
|
+
versionGuard(task, args.ifVersion);
|
|
712
|
+
if (task.status === "archived") throw new ToolError(ERR.invalidTransition, "archived tasks are immutable");
|
|
713
|
+
const next = structuredClone(task);
|
|
714
|
+
const checklist = next.checklist === void 0 ? [] : [...next.checklist];
|
|
715
|
+
if (args.action === "add") {
|
|
716
|
+
const texts = args.items ?? [];
|
|
717
|
+
if (texts.length === 0 || texts.length > 10) throw new ToolError(ERR.invalidInput, "items must carry 1..10 texts per add call");
|
|
718
|
+
if (checklist.length + texts.length > 30) throw new ToolError(ERR.invalidInput, `checklist may hold at most 30 items (currently ${checklist.length})`);
|
|
719
|
+
checklist.push(...checklistFromTexts(texts));
|
|
720
|
+
} else if (args.action === "check") {
|
|
721
|
+
if (args.itemId === void 0) throw new ToolError(ERR.invalidInput, "itemId is required for check");
|
|
722
|
+
const item = checklist.find((i) => i.id === args.itemId);
|
|
723
|
+
if (item === void 0) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`);
|
|
724
|
+
const note = args.note !== void 0 && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : void 0;
|
|
725
|
+
item.checked = true;
|
|
726
|
+
item.checkedBy = actor.sessionId;
|
|
727
|
+
item.checkedAt = deps.now();
|
|
728
|
+
if (note !== void 0) item.note = note;
|
|
729
|
+
} else if (args.action === "uncheck") {
|
|
730
|
+
if (args.itemId === void 0) throw new ToolError(ERR.invalidInput, "itemId is required for uncheck");
|
|
731
|
+
const item = checklist.find((i) => i.id === args.itemId);
|
|
732
|
+
if (item === void 0) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`);
|
|
733
|
+
item.checked = false;
|
|
734
|
+
delete item.checkedBy;
|
|
735
|
+
delete item.checkedAt;
|
|
736
|
+
delete item.note;
|
|
737
|
+
} else throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got "${args.action}")`);
|
|
738
|
+
if (checklist.length > 0) next.checklist = checklist;
|
|
739
|
+
else delete next.checklist;
|
|
740
|
+
next.version = task.version + 1;
|
|
741
|
+
next.updatedAt = deps.now();
|
|
742
|
+
next.updatedBy = actor;
|
|
743
|
+
await store.mutate("task-updated", (ledger) => {
|
|
744
|
+
const i = ledger.tasks.findIndex((t) => t.id === args.id);
|
|
745
|
+
ledger.tasks[i] = next;
|
|
746
|
+
return [next];
|
|
747
|
+
});
|
|
748
|
+
const progress = next.checklist !== void 0 ? {
|
|
749
|
+
done: next.checklist.filter((i) => i.checked).length,
|
|
750
|
+
total: next.checklist.length
|
|
751
|
+
} : {
|
|
752
|
+
done: 0,
|
|
753
|
+
total: 0
|
|
754
|
+
};
|
|
755
|
+
return json({
|
|
756
|
+
task: {
|
|
757
|
+
id: next.id,
|
|
758
|
+
version: next.version
|
|
759
|
+
},
|
|
760
|
+
checklist: next.checklist ?? [],
|
|
761
|
+
...progress
|
|
762
|
+
});
|
|
763
|
+
} catch (error) {
|
|
764
|
+
fail(error);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
})));
|
|
768
|
+
disposers.push(register(defineTool({
|
|
769
|
+
name: "taskboard_execution_report",
|
|
770
|
+
description: "Submit the structured execution report for the task you are currently executing (summary / changed files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; a later submission overwrites the previous report. Commits and diffs are host-collected — do not repeat them.",
|
|
771
|
+
parameters: {
|
|
772
|
+
summary: {
|
|
773
|
+
type: "string",
|
|
774
|
+
required: true,
|
|
775
|
+
description: "What was done (1..2000 chars)."
|
|
776
|
+
},
|
|
777
|
+
changedFiles: {
|
|
778
|
+
type: "array",
|
|
779
|
+
description: "Files you changed (paths, ≤50 × 300 chars).",
|
|
780
|
+
items: { type: "string" }
|
|
781
|
+
},
|
|
782
|
+
checks: {
|
|
783
|
+
type: "array",
|
|
784
|
+
description: "How the work was verified (e.g. test commands + outcomes, ≤50 entries).",
|
|
785
|
+
items: { type: "string" }
|
|
786
|
+
},
|
|
787
|
+
artifacts: {
|
|
788
|
+
type: "array",
|
|
789
|
+
description: "Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).",
|
|
790
|
+
items: { type: "string" }
|
|
791
|
+
},
|
|
792
|
+
risk: {
|
|
793
|
+
type: "string",
|
|
794
|
+
description: "Known remaining risks or follow-ups (≤2000 chars, optional)."
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
output: {
|
|
798
|
+
schema: JSON_OUT,
|
|
799
|
+
render: (_args, value) => {
|
|
800
|
+
const v = value;
|
|
801
|
+
if (v.taskId === void 0 || v.report === void 0) return [{
|
|
802
|
+
type: "text",
|
|
803
|
+
text: "报告提交失败。"
|
|
804
|
+
}];
|
|
805
|
+
return [{
|
|
806
|
+
type: "text",
|
|
807
|
+
text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ""}\n接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。`
|
|
808
|
+
}];
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
async execute(args, exec) {
|
|
812
|
+
try {
|
|
813
|
+
const { sessionId } = caller(exec);
|
|
814
|
+
const report = normalizeExecutionReport(args);
|
|
815
|
+
let taskId;
|
|
816
|
+
let executionId;
|
|
817
|
+
await store.mutate("execution-recorded", (ledger) => {
|
|
818
|
+
for (const task of ledger.tasks) {
|
|
819
|
+
const execution = task.executions.find((e) => e.sessionId === sessionId && e.outcome === "running");
|
|
820
|
+
if (execution !== void 0) {
|
|
821
|
+
execution.report = report;
|
|
822
|
+
taskId = task.id;
|
|
823
|
+
executionId = execution.id;
|
|
824
|
+
return [task];
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
if (taskId === void 0 || executionId === void 0) throw new ToolError(ERR.forbidden, "no running execution belongs to this session — the report can only be submitted while the taskboard execution session is still running");
|
|
829
|
+
return json({
|
|
830
|
+
taskId,
|
|
831
|
+
executionId,
|
|
832
|
+
report
|
|
833
|
+
});
|
|
834
|
+
} catch (error) {
|
|
835
|
+
fail(error);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
})));
|
|
625
839
|
return disposers;
|
|
626
840
|
}
|
|
627
841
|
//#endregion
|
package/lib/host/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["/**\n * The eight `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n asStatus,\n asUrgency,\n canTransition,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` cron=${t.execution.cron}` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. */\nclass ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all eight tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (process.env.ATB_TRACE === '1' && typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim|scheduled + cron), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Prompt sent to a fresh session when executed; default = title+description.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) or { mode: \"scheduled\", cron: \"m h dom mon dow\" }.',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled only).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model }. Omit to use the default model.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: { provider?: string; model?: string }\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status === 'done' || status === 'archived') {\n throw new ToolError(ERR.invalidTransition, 'a new task cannot start as done/archived')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const model = args.model !== undefined ? checkModel(deps, args.model) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n const next: TaskRecord = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n await store.mutate('task-updated', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ task: summarize(next) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to)) {\n const wsId = await callerWorkspace(deps, exec as ToolRunContext)\n if (wsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n }\n const next: TaskRecord = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n await store.mutate('task-moved', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ task: summarize(next) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n const next: TaskRecord = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n await store.mutate('comment-added', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ comment, task: { id: next.id, version: next.version, status: next.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n const next: TaskRecord = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n await store.mutate('task-deleted', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["/**\n * The ten `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n * - checklist items may be added/checked by agents, but checking never\n * completes the task (done stays user-only)\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n MAX_CHECKLIST_ITEMS,\n asIsolation,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newChecklistItemId,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeExecutionReport,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type ChecklistItem,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n checklist?: { done: number; total: number }\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.checklist !== undefined && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` cron=${t.execution.cron}` : ''}`,\n `隔离: ${t.isolation === 'none' ? '关闭(原目录执行)' : 'Git Worktree'}${t.branch !== undefined ? `(分支 ${t.branch})` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}`)\n if (t.presetId !== undefined) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.checklist !== undefined && t.checklist.length > 0) {\n const done = t.checklist.filter(i => i.checked).length\n lines.push(`验收清单 (${done}/${t.checklist.length}):`)\n for (const item of t.checklist) {\n const mark = item.checked ? '☑' : '☐'\n const who = item.checkedBy === undefined ? '' : item.checkedBy === 'user' ? ' ·用户勾选' : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`\n const note = item.note !== undefined ? ` ·证据: ${item.note}` : ''\n lines.push(` ${mark} ${item.text}${who}${note}`)\n }\n }\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n const report = e.report !== undefined ? ' [已交报告]' : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. */\nclass ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all eight tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (process.env.ATB_TRACE === '1' && typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim|scheduled + cron), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Prompt sent to a fresh session when executed; default = title+description.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) or { mode: \"scheduled\", cron: \"m h dom mon dow\" }.',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled only).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model }. Omit to use the default model.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n },\n },\n isolation: {\n type: 'string',\n description: 'Code isolation for executions: \"worktree\" (default — each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction).',\n },\n presetId: {\n type: 'string',\n description: 'Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional.',\n },\n checklist: {\n type: 'array',\n description: `Acceptance checklist (DoD) item texts (≤${MAX_CHECKLIST_ITEMS} × 200 chars); agents check them off at handoff, the user reviews.`,\n items: { type: 'string' },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: { provider?: string; model?: string }\n isolation?: string\n presetId?: string\n checklist?: string[]\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status === 'done' || status === 'archived') {\n throw new ToolError(ERR.invalidTransition, 'a new task cannot start as done/archived')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const model = args.model !== undefined ? checkModel(deps, args.model) : undefined\n const isolation = args.isolation === undefined ? undefined : asIsolation(args.isolation)\n const presetId = args.presetId?.trim() || undefined\n const checklist = args.checklist !== undefined ? checklistFromTexts(args.checklist) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n ...(isolation !== undefined ? { isolation } : {}),\n ...(presetId !== undefined ? { presetId } : {}),\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n const next: TaskRecord = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n await store.mutate('task-updated', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ task: summarize(next) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number } }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to)) {\n const wsId = await callerWorkspace(deps, exec as ToolRunContext)\n if (wsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n }\n const next: TaskRecord = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n await store.mutate('task-moved', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ task: summarize(next) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n const next: TaskRecord = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n await store.mutate('comment-added', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return json({ comment, task: { id: next.id, version: next.version, status: next.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n const next: TaskRecord = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n await store.mutate('task-deleted', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- checklist\n disposers.push(register(defineTool({\n name: 'taskboard_checklist',\n description:\n `Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), `\n + '\"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). '\n + 'Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n action: { type: 'string', required: true, description: 'add | check | uncheck.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n items: {\n type: 'array',\n description: 'Item texts to append (action=add only; 1..10 per call, 200 chars each).',\n items: { type: 'string' },\n },\n itemId: { type: 'string', description: 'The checklist item id (action=check/uncheck).' },\n note: { type: 'string', description: 'Evidence note recorded with the check (≤400 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; version?: number }; checklist?: Array<Record<string, unknown>>; done?: number; total?: number }\n if (v.task === undefined || v.checklist === undefined) return [{ type: 'text', text: '清单操作失败。' }]\n const lines = v.checklist.map((i, index) => `${i.checked === true ? '☑' : '☐'} [${index + 1}] ${String(i.text)}${i.note !== undefined ? `(证据: ${String(i.note)})` : ''} id=${String(i.id)}`)\n return [{\n type: 'text',\n text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\\n${lines.join('\\n')}`,\n }]\n },\n },\n async execute(args: {\n id: string\n action: string\n ifVersion: number\n items?: string[]\n itemId?: string\n note?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n const next: TaskRecord = structuredClone(task)\n const checklist: ChecklistItem[] = next.checklist === undefined ? [] : [...next.checklist]\n\n if (args.action === 'add') {\n const texts = args.items ?? []\n if (texts.length === 0 || texts.length > 10) {\n throw new ToolError(ERR.invalidInput, 'items must carry 1..10 texts per add call')\n }\n if (checklist.length + texts.length > MAX_CHECKLIST_ITEMS) {\n throw new ToolError(ERR.invalidInput, `checklist may hold at most ${MAX_CHECKLIST_ITEMS} items (currently ${checklist.length})`)\n }\n checklist.push(...checklistFromTexts(texts))\n } else if (args.action === 'check') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for check')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n const note = args.note !== undefined && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : undefined\n item.checked = true\n item.checkedBy = actor.sessionId\n item.checkedAt = deps.now()\n if (note !== undefined) item.note = note\n } else if (args.action === 'uncheck') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for uncheck')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n item.checked = false\n delete item.checkedBy\n delete item.checkedAt\n delete item.note\n } else {\n throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got \"${args.action}\")`)\n }\n\n if (checklist.length > 0) next.checklist = checklist\n else delete next.checklist\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n await store.mutate('task-updated', ledger => {\n const i = ledger.tasks.findIndex(t => t.id === args.id)\n ledger.tasks[i] = next\n return [next]\n })\n const progress = next.checklist !== undefined\n ? { done: next.checklist.filter(i => i.checked).length, total: next.checklist.length }\n : { done: 0, total: 0 }\n return json({ task: { id: next.id, version: next.version }, checklist: next.checklist ?? [], ...progress })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------- execution report\n disposers.push(register(defineTool({\n name: 'taskboard_execution_report',\n description:\n 'Submit the structured execution report for the task you are currently executing (summary / changed '\n + 'files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; '\n + 'a later submission overwrites the previous report. Commits and diffs are host-collected — do not repeat them.',\n parameters: {\n summary: { type: 'string', required: true, description: 'What was done (1..2000 chars).' },\n changedFiles: {\n type: 'array',\n description: 'Files you changed (paths, ≤50 × 300 chars).',\n items: { type: 'string' },\n },\n checks: {\n type: 'array',\n description: 'How the work was verified (e.g. test commands + outcomes, ≤50 entries).',\n items: { type: 'string' },\n },\n artifacts: {\n type: 'array',\n description: 'Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).',\n items: { type: 'string' },\n },\n risk: { type: 'string', description: 'Known remaining risks or follow-ups (≤2000 chars, optional).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { taskId?: string; executionId?: string; report?: { summary?: string } }\n if (v.taskId === undefined || v.report === undefined) return [{ type: 'text', text: '报告提交失败。' }]\n return [{\n type: 'text',\n text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ''}\\n`\n + '接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。',\n }]\n },\n },\n async execute(args: {\n summary: string\n changedFiles?: string[]\n checks?: string[]\n artifacts?: string[]\n risk?: string\n }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const report = normalizeExecutionReport(args)\n // Locate the RUNNING execution this session owns — reports attach to\n // the live run, so the agent never needs to know execution ids.\n let taskId: string | undefined\n let executionId: string | undefined\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n const execution = task.executions.find(e => e.sessionId === sessionId && e.outcome === 'running')\n if (execution !== undefined) {\n execution.report = report\n taskId = task.id\n executionId = execution.id\n return [task]\n }\n }\n return undefined\n })\n if (taskId === undefined || executionId === undefined) {\n throw new ToolError(ERR.forbidden, 'no running execution belongs to this session — the report can only be submitted while the taskboard execution session is still running')\n }\n return json({ taskId, executionId, report })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { PROTOCOL_SECTION_NAME, TASKBOARD_PROTOCOL } from "./host/protocol-text.js";
|
|
2
|
+
import { createGitFace } from "./host/git.js";
|
|
2
3
|
import { dshHomePath } from "./host/sdk.js";
|
|
3
4
|
import { ExecutionService } from "./host/execution.js";
|
|
4
5
|
import { registerTaskboardRoutes } from "./host/routes.js";
|
|
5
6
|
import { SchedulerService } from "./host/scheduler.js";
|
|
6
7
|
import { TaskStore } from "./host/store.js";
|
|
8
|
+
import { TemplateStore } from "./host/templates.js";
|
|
7
9
|
import { registerTaskboardTools, workspaceFace } from "./host/tools.js";
|
|
8
10
|
//#region src/index.ts
|
|
9
11
|
/** Ledger file name under the DSH home. */
|
|
10
12
|
const LEDGER_FILE = "dsh-taskboard.json";
|
|
13
|
+
/** Task-template side file name under the DSH home (0.4.0). */
|
|
14
|
+
const TEMPLATES_FILE = "dsh-taskboard-templates.json";
|
|
11
15
|
/** Cordis plugin name. */
|
|
12
16
|
const name = "dsh-taskboard";
|
|
13
17
|
/** Required host services (tool registry + prompt assembly). */
|
|
@@ -18,6 +22,7 @@ const inject = ["tools", "systemPrompt"];
|
|
|
18
22
|
*/
|
|
19
23
|
function apply(ctx) {
|
|
20
24
|
const store = new TaskStore({ file: dshHomePath(LEDGER_FILE) });
|
|
25
|
+
const templates = new TemplateStore(dshHomePath(TEMPLATES_FILE));
|
|
21
26
|
const now = () => Date.now();
|
|
22
27
|
const maxConcurrent = Math.max(1, Number.parseInt(process.env.DSH_TASKBOARD_MAX_CONCURRENT ?? "", 10) || 3);
|
|
23
28
|
const disposeSection = ctx.systemPrompt.section({
|
|
@@ -45,6 +50,7 @@ function apply(ctx) {
|
|
|
45
50
|
const events = { onSessionEvent: (listener) => wsCtx.on("session/event", (session, event) => {
|
|
46
51
|
listener(session.id, event);
|
|
47
52
|
}) };
|
|
53
|
+
const git = createGitFace();
|
|
48
54
|
wsCtx.inject(["agents"], (agentCtx) => {
|
|
49
55
|
const execution = new ExecutionService({
|
|
50
56
|
store,
|
|
@@ -58,6 +64,18 @@ function apply(ctx) {
|
|
|
58
64
|
},
|
|
59
65
|
events,
|
|
60
66
|
now,
|
|
67
|
+
git,
|
|
68
|
+
composeAgent: async (presetId) => {
|
|
69
|
+
const presets = agentCtx.get("agentPresets");
|
|
70
|
+
if (presets === void 0) return void 0;
|
|
71
|
+
const resolved = await presets.resolve(presetId);
|
|
72
|
+
return {
|
|
73
|
+
agentPreset: resolved.id,
|
|
74
|
+
setup: async (ctx) => {
|
|
75
|
+
await presets.mount(ctx, resolved.id);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
},
|
|
61
79
|
renameSession: (sessionId, title) => {
|
|
62
80
|
try {
|
|
63
81
|
const sessions = agentCtx.get("sessions");
|
|
@@ -83,9 +101,11 @@ function apply(ctx) {
|
|
|
83
101
|
store,
|
|
84
102
|
workspaces: workspaceFace(wsCtx.workspaceRegistry),
|
|
85
103
|
now,
|
|
86
|
-
run: (taskId) => execution.run(taskId, "manual"),
|
|
104
|
+
run: (taskId, runOptions) => execution.run(taskId, "manual", runOptions),
|
|
87
105
|
cancel: (taskId) => execution.cancel(taskId),
|
|
88
|
-
modelProviders
|
|
106
|
+
modelProviders,
|
|
107
|
+
git,
|
|
108
|
+
templates
|
|
89
109
|
});
|
|
90
110
|
return () => disposeRoutes?.();
|
|
91
111
|
});
|
|
@@ -109,6 +129,6 @@ function apply(ctx) {
|
|
|
109
129
|
});
|
|
110
130
|
}
|
|
111
131
|
//#endregion
|
|
112
|
-
export { LEDGER_FILE, apply, inject, name };
|
|
132
|
+
export { LEDGER_FILE, TEMPLATES_FILE, apply, inject, name };
|
|
113
133
|
|
|
114
134
|
//# sourceMappingURL=index.js.map
|