dsh-tree-task-flow 0.1.1 → 0.2.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 CHANGED
@@ -126,8 +126,10 @@ dsh plugin --profile web add dsh-tree-task-flow
126
126
  - 节点上可以直接完成、丢弃,不用敲命令
127
127
  - 顶部状态条:「停止自动续行」「清空计划」「刷新」
128
128
 
129
- **设置 → 树形任务流**:显示插件当前是启用还是未启用,并逐条列明启用后会影响什么,
130
- 以及怎么开、怎么关。
129
+ **没有计划的会话一个像素都不占**——读不到计划就返回 `null`,不会平白挤掉内置的
130
+ 待办与目标条目。
131
+
132
+ 开关不在界面上,在 profile 的 `cordis.patch.yml` 里(见下面的「启用」一节)。
131
133
 
132
134
  ## 命令
133
135
 
package/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * dsh-tree-task-flow 的客户端:输入框上方的树形任务流条目 + 设置页里的插件开关说明。
2
+ * dsh-tree-task-flow 的客户端:输入框上方的树形任务流条目。
3
3
  *
4
4
  * 这是**手写**的模块包装格式(和 dsh-purge 的 client.js 一样),不经过打包,
5
5
  * 所以用 `h()` 而不是 JSX。
@@ -161,25 +161,6 @@ window.__ModuleLoader__.load({
161
161
  "写清这个节点产出了什么。它整段执行过程会被这条结果顶替,所以要能独立看懂。",
162
162
  "confirm.reset": "确定要清空这个会话的计划树吗?",
163
163
  "confirm.drop": "确定要丢弃「{title}」吗?",
164
- "settings.title": "树形任务流",
165
- "settings.enabled": "已启用",
166
- "settings.disabled": "未启用",
167
- "settings.impact": "启用后它会做什么",
168
- "settings.impact.1": "在每个会话的系统提示词里加一段固定的工具组说明(会话内逐字节不变)。",
169
- "settings.impact.2": "注册 6 个 tree_task_* 工具。",
170
- "settings.impact.3":
171
- "一个节点完成时,把它那一段执行过程从模型可见的上下文里折叠掉,换成一条携带提交结果的汇总消息。",
172
- "settings.impact.4":
173
- "一个节点的子节点全部结束时转入检查点:那一刻只放行 tree_task_plan 与 tree_task_done。",
174
- "settings.impact.5":
175
- "可选:模型停下来时自动续行(默认关闭;你按下停止后不会再被拉起来)。",
176
- "settings.howto": "怎么开 / 怎么关",
177
- "settings.howto.body":
178
- "改 profile 的 cordis.patch.yml 里 task-tree 那一行的 config.enabled,然后重启 dsh web。装上不等于启用——默认是关的。",
179
- "settings.stateDir": "状态目录",
180
- "settings.sessions": "有计划的会话",
181
- "settings.noSessions": "(还没有)",
182
- "settings.checkpoint": "检查点",
183
164
  };
184
165
 
185
166
  const en = {
@@ -208,25 +189,6 @@ window.__ModuleLoader__.load({
208
189
  "State what this node produced. Its whole execution range is replaced by this result, so it must stand on its own.",
209
190
  "confirm.reset": "Clear this session's plan tree?",
210
191
  "confirm.drop": "Drop “{title}”?",
211
- "settings.title": "Tree Task Flow",
212
- "settings.enabled": "Enabled",
213
- "settings.disabled": "Not enabled",
214
- "settings.impact": "What it does once enabled",
215
- "settings.impact.1": "Adds one static tool-group section to every session's system prompt.",
216
- "settings.impact.2": "Registers six tree_task_* tools.",
217
- "settings.impact.3":
218
- "When a node completes, folds its execution range out of the model-visible context and replaces it with one summary message carrying the submitted result.",
219
- "settings.impact.4":
220
- "When every child of a node finishes, the tree enters a checkpoint: only tree_task_plan and tree_task_done are allowed there.",
221
- "settings.impact.5":
222
- "Optional auto-continue when the model goes idle (off by default; never resumes after you stop it).",
223
- "settings.howto": "How to enable / disable",
224
- "settings.howto.body":
225
- "Set config.enabled on the task-tree row in the profile's cordis.patch.yml, then restart dsh web. Installing is not enabling — it is off by default.",
226
- "settings.stateDir": "State directory",
227
- "settings.sessions": "Sessions with a plan",
228
- "settings.noSessions": "(none yet)",
229
- "settings.checkpoint": "checkpoint",
230
192
  };
231
193
 
232
194
  // ---------------------------------------------------------------- 样式
@@ -791,84 +753,6 @@ window.__ModuleLoader__.load({
791
753
  );
792
754
  }
793
755
 
794
- // ---------------------------------------------------------------- 设置页
795
-
796
- function SettingsSection() {
797
- const t = useT();
798
- const [state, setState] = useState({ loading: true });
799
-
800
- useEffect(() => {
801
- api("/dsh-tree-task-flow/sessions")
802
- .then((data) => setState({ loading: false, data }))
803
- .catch((error) => setState({ loading: false, error: String((error && error.message) || error) }));
804
- }, []);
805
-
806
- if (state.loading) return h("div", { style: S.wrap }, "…");
807
- if (state.error) return h("div", { style: S.wrap }, h("div", { style: S.err }, state.error));
808
-
809
- const enabled = state.data && state.data.enabled === true;
810
- const sessions = (state.data && state.data.sessions) || [];
811
-
812
- const row = (label, value) =>
813
- h(
814
- "div",
815
- { style: { display: "flex", gap: "8px", margin: "2px 0" } },
816
- h("span", { style: { minWidth: "9em", opacity: 0.7 } }, label),
817
- h("span", null, value),
818
- );
819
-
820
- return h(
821
- "div",
822
- { style: S.wrap },
823
- h("div", { style: { fontWeight: 600, marginBottom: "6px" } }, t("settings.title")),
824
- row(
825
- "",
826
- h("span", { style: S.badge }, enabled ? t("settings.enabled") : t("settings.disabled")),
827
- ),
828
- h("div", { style: { marginTop: "10px", fontWeight: 600 } }, t("settings.impact")),
829
- h(
830
- "ul",
831
- { style: { margin: "4px 0 10px 1.2em", padding: 0 } },
832
- [
833
- "settings.impact.1",
834
- "settings.impact.2",
835
- "settings.impact.3",
836
- "settings.impact.4",
837
- "settings.impact.5",
838
- ].map((key) => h("li", { key }, t(key))),
839
- ),
840
- h("div", { style: { fontWeight: 600 } }, t("settings.howto")),
841
- h("div", { style: S.hint }, t("settings.howto.body")),
842
- h("div", { style: { marginTop: "10px", fontWeight: 600 } }, t("settings.stateDir")),
843
- h("div", { style: S.meta }, "$DSH_HOME/dsh-task-tree/"),
844
- h(
845
- "div",
846
- { style: { marginTop: "10px", fontWeight: 600 } },
847
- t("settings.sessions") + ":" + sessions.length,
848
- ),
849
- sessions.length === 0
850
- ? h("div", { style: S.hint }, t("settings.noSessions"))
851
- : h(
852
- "div",
853
- { style: S.child },
854
- sessions.map((item) =>
855
- h(
856
- "div",
857
- { key: item.sessionId, style: S.meta },
858
- item.goal +
859
- " · " +
860
- item.done +
861
- "/" +
862
- item.steps +
863
- (item.checkpoint ? " · " + t("settings.checkpoint") : "") +
864
- " · " +
865
- item.sessionId,
866
- ),
867
- ),
868
- ),
869
- );
870
- }
871
-
872
756
  // ---------------------------------------------------------------- 挂载
873
757
 
874
758
  function apply(ctx) {
@@ -892,25 +776,6 @@ window.__ModuleLoader__.load({
892
776
  ),
893
777
  "dsh-tree-task-flow: conversation dock",
894
778
  );
895
-
896
- // 设置页:只显示状态与影响说明(启用与否由 profile 配置决定,运行时不改)。
897
- ctx.effect(
898
- () =>
899
- ctx.slots.inject("settings.section", () =>
900
- ctx.slots.register(
901
- {
902
- name: "settings.section",
903
- id: ENTRY_ID,
904
- order: 45,
905
- label: () => translate("nav"),
906
- locale: NS,
907
- inject: () => ({}),
908
- },
909
- SettingsSection,
910
- ),
911
- ),
912
- "dsh-tree-task-flow: settings section",
913
- );
914
779
  }
915
780
 
916
781
  exports.name = name;
package/lib/http.js CHANGED
@@ -1,26 +1,23 @@
1
1
  /**
2
- * 设置面板用的 HTTP 接口。
2
+ * 任务树条目用的 HTTP 接口。
3
3
  *
4
- * 面板跑在浏览器里,拿不到宿主内存中的 store,所以数据要经由这里出去。
4
+ * 条目跑在浏览器里,拿不到宿主内存中的 store,所以数据要经由这里出去。
5
5
  * 全部挂在 `/dsh-tree-task-flow/` 前缀下;只读写插件**自己的**状态目录,
6
6
  * 不碰会话日志(那是后端内部格式)。
7
7
  *
8
- * 面板要显示两样东西,都在这里算好给它:
8
+ * 条目要显示两样东西,都在这里算好给它:
9
9
  *
10
- * - **每层的结果**:节点完成时提交的文本。面板不自己去挖计划树的深层结构,
10
+ * - **每层的结果**:节点完成时提交的文本。条目不自己去挖计划树的深层结构,
11
11
  * 接口直接把三层各自的结果摆出来。
12
12
  * - **检查点状态**:一个节点自己的子节点全部结束、而它自己还开着的那一刻。
13
13
  * 此刻只有两条路——继续拆子节点,或者提交结果并完成它。
14
14
  *
15
- * 面板上的按钮都写计划树,所以写完要把**新的**活动路径、结果与检查点一并回传,
16
- * 免得面板还要再发一次请求才知道自己按出了什么效果。
15
+ * 条目上的按钮都写计划树,所以写完要把**新的**活动路径、结果与检查点一并回传,
16
+ * 免得条目还要再发一次请求才知道自己按出了什么效果。
17
17
  *
18
18
  * @module dsh-tree-task-flow/http
19
19
  */
20
20
 
21
- import { readdirSync } from "node:fs";
22
- import { join } from "node:path";
23
-
24
21
  import { activePath, checkpointOf, complete, dropNode, levelLabel } from "./plan.js";
25
22
 
26
23
  /** 统一的 JSON 响应。 */
@@ -97,63 +94,15 @@ function afterWrite(plan) {
97
94
  };
98
95
  }
99
96
 
100
- /** 列出状态目录里所有**有计划的会话**。 */
101
- function listSessions(store) {
102
- let names = [];
103
- try {
104
- names = readdirSync(join(store.root, "plans"));
105
- } catch {
106
- return [];
107
- }
108
-
109
- const out = [];
110
- for (const name of names) {
111
- if (!name.endsWith(".json")) continue;
112
- const sessionId = name.slice(0, -".json".length);
113
- const plan = store.readPlan(sessionId);
114
- if (plan === null) continue;
115
- const steps = (plan.goal?.tasks ?? []).flatMap((task) => task.steps ?? []);
116
- out.push({
117
- sessionId,
118
- goal: plan.goal?.title ?? "(未命名目标)",
119
- status: plan.goal?.status ?? "pending",
120
- updatedAt: plan.updatedAt ?? 0,
121
- steps: steps.length,
122
- done: steps.filter((step) => step.status === "done").length,
123
- result: plan.goal?.result?.text ?? null,
124
- checkpoint: checkpointInfo(plan),
125
- });
126
- }
127
- out.sort((a, b) => b.updatedAt - a.updatedAt);
128
- return out;
129
- }
130
-
131
97
  /**
132
98
  * 注册全部面板接口。
133
99
  *
134
100
  * @param ctx - 挂载本行的 Cordis 上下文。
135
- * @param options - `{ store, cfg, autoContinue }`。
101
+ * @param options - `{ store, autoContinue }`。
136
102
  */
137
- export function registerHttp(ctx, { store, cfg, autoContinue }) {
103
+ export function registerHttp(ctx, { store, autoContinue }) {
138
104
  ctx.inject(["webServer"], (host) => {
139
105
  host.effect(() => {
140
- // 会话列表:面板顶部的下拉框。
141
- host.webServer.register(
142
- {
143
- kind: "exact",
144
- path: "/dsh-tree-task-flow/sessions",
145
- handler: async (request, response) => {
146
- if (request.method !== "GET") return sendJson(response, 405, { ok: false, error: "只支持 GET" });
147
- sendJson(response, 200, {
148
- ok: true,
149
- enabled: cfg.enabled === true,
150
- sessions: listSessions(store),
151
- });
152
- },
153
- },
154
- "dsh-tree-task-flow: sessions",
155
- );
156
-
157
106
  // 一棵树的全部信息:结构 + 每层结果 + 检查点 + 当前路径 + 三层折叠游标。
158
107
  host.webServer.register(
159
108
  {
package/lib/index.js CHANGED
@@ -78,9 +78,10 @@ export function apply(ctx, config) {
78
78
  // 不折叠上下文、不续行——只留一条命令,让人能确认"装了但没开"以及怎么开。
79
79
  if (cfg.enabled !== true) {
80
80
  registerCommands(ctx, { store, cfg, autoContinue: null, enabled: false });
81
- // 面板接口照挂:设置页要能显示"装了但没启用"以及启用后的影响。
82
- // 只读自己的状态目录,不碰任何会话。
83
- registerHttp(ctx, { store, cfg, autoContinue: null });
81
+ // 条目接口照挂:客户端条目与 enabled 无关,照样会来读。读不到计划它就返回
82
+ // null,一个像素都不占;而既有的计划文件仍然能看到。
83
+ // 只读插件自己的状态目录,不碰任何会话。
84
+ registerHttp(ctx, { store, autoContinue: null });
84
85
  ctx.logger?.warn?.(
85
86
  "dsh-tree-task-flow: 已装载但**未启用**(enabled=false)。要启用请在 profile 的 cordis.patch.yml 里设为 true。",
86
87
  );
@@ -103,8 +104,8 @@ export function apply(ctx, config) {
103
104
  // /task-tree 命令:查看与管理任务树,也是"停止自动执行"的开关。
104
105
  registerCommands(ctx, { store, cfg, autoContinue, enabled: true });
105
106
 
106
- // 任务树条目与设置页要用的 HTTP 接口。
107
- registerHttp(ctx, { store, cfg, autoContinue });
107
+ // 任务树条目要用的 HTTP 接口。
108
+ registerHttp(ctx, { store, autoContinue });
108
109
 
109
110
  ctx.logger?.info?.(`dsh-tree-task-flow: 已启用(状态目录 ${root})`);
110
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-tree-task-flow",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "树形任务流:把多步任务组织成目标→任务→子任务三级树,节点完成时由 AI 提交结果,插件随即把该节点的执行过程从上下文里折叠掉,只留下结果。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",