@xgjktech/xg-openclaw-harness-tools 0.3.0 → 0.3.2
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plan-execute-tool.d.ts +2 -1
- package/dist/plan-execute-tool.d.ts.map +1 -1
- package/dist/plan-execute-tool.js +32 -3
- package/dist/plan-scope.d.ts +8 -0
- package/dist/plan-scope.d.ts.map +1 -0
- package/dist/plan-scope.js +16 -0
- package/dist/plan-view.d.ts +1 -1
- package/dist/plan-view.d.ts.map +1 -1
- package/dist/plan-view.js +3 -1
- package/dist/plan-write-tool.d.ts +4 -3
- package/dist/plan-write-tool.d.ts.map +1 -1
- package/dist/plan-write-tool.js +103 -14
- package/package.json +2 -2
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAYA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAYA,wBAoCG"}
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ export default defineToolPlugin({
|
|
|
34
34
|
description: PLAN_EXECUTE_DESCRIPTION,
|
|
35
35
|
parameters: PlanExecuteParams,
|
|
36
36
|
optional: true,
|
|
37
|
-
factory: ({ api }) => buildPlanExecuteTool({ store: getStore(api) }),
|
|
37
|
+
factory: ({ api, toolContext }) => buildPlanExecuteTool({ store: getStore(api), origin: toPlanOrigin(toolContext) }),
|
|
38
38
|
}),
|
|
39
39
|
];
|
|
40
40
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import type { AnyAgentTool } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
-
import type
|
|
3
|
+
import { type PlanOrigin, type PlanStore } from "@xgjktech/xg-openclaw-shared";
|
|
4
4
|
export declare const PlanExecuteParams: Type.TObject<{
|
|
5
5
|
planId: Type.TOptional<Type.TString>;
|
|
6
6
|
}>;
|
|
@@ -8,5 +8,6 @@ export type PlanExecuteParamsT = Static<typeof PlanExecuteParams>;
|
|
|
8
8
|
export declare const PLAN_EXECUTE_DESCRIPTION: string;
|
|
9
9
|
export declare function buildPlanExecuteTool(deps: {
|
|
10
10
|
store: PlanStore;
|
|
11
|
+
origin?: PlanOrigin;
|
|
11
12
|
}): AnyAgentTool;
|
|
12
13
|
//# sourceMappingURL=plan-execute-tool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-execute-tool.d.ts","sourceRoot":"","sources":["../src/plan-execute-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"plan-execute-tool.d.ts","sourceRoot":"","sources":["../src/plan-execute-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,SAAS,EAEf,MAAM,8BAA8B,CAAC;AAKtC,eAAO,MAAM,iBAAiB;;EAK7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,wBAAwB,QAQzB,CAAC;AAuBb,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,YAAY,CA+ClG"}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
+
import { isPlanCancelled, } from "@xgjktech/xg-openclaw-shared";
|
|
2
3
|
import { renderPlanView } from "./plan-view.js";
|
|
4
|
+
import { planInScope, scopeKey } from "./plan-scope.js";
|
|
3
5
|
import { toolJsonResult } from "./tool-json-result.js";
|
|
4
6
|
export const PlanExecuteParams = Type.Object({
|
|
5
7
|
planId: Type.Optional(Type.String({ description: "要查看的计划 id;留空则列出最近计划" })),
|
|
6
8
|
}, { additionalProperties: false });
|
|
7
9
|
export const PLAN_EXECUTE_DESCRIPTION = [
|
|
8
10
|
"用途:只读查看已保存的计划;不执行任务,也不修改计划。",
|
|
11
|
+
"只能看到当前会话来源的计划。",
|
|
9
12
|
"",
|
|
10
13
|
"- 带 planId:返回该计划当前的完整清单。",
|
|
11
14
|
"- 不带 planId:返回最近计划的摘要列表,用于忘记或跨会话找回 planId。",
|
|
12
15
|
"",
|
|
13
16
|
"需要更新进度时,用 xg_plan_write 重新提交整张计划。",
|
|
14
17
|
].join("\n");
|
|
18
|
+
function summarizePlan(plan) {
|
|
19
|
+
const taskCounts = { total: 0, completed: 0, blocked: 0, inProgress: 0 };
|
|
20
|
+
for (const task of plan.tasks) {
|
|
21
|
+
taskCounts.total += 1;
|
|
22
|
+
if (task.status === "completed")
|
|
23
|
+
taskCounts.completed += 1;
|
|
24
|
+
if (task.status === "blocked")
|
|
25
|
+
taskCounts.blocked += 1;
|
|
26
|
+
if (task.status === "in_progress")
|
|
27
|
+
taskCounts.inProgress += 1;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
planId: plan.planId,
|
|
31
|
+
goal: plan.goal,
|
|
32
|
+
updatedAt: plan.updatedAt,
|
|
33
|
+
taskCounts,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
15
36
|
export function buildPlanExecuteTool(deps) {
|
|
16
|
-
const { store } = deps;
|
|
37
|
+
const { store, origin } = deps;
|
|
38
|
+
const scope = scopeKey(origin);
|
|
17
39
|
return {
|
|
18
40
|
name: "xg_plan_execute",
|
|
19
41
|
label: "【xg-harness】查看计划",
|
|
@@ -23,11 +45,18 @@ export function buildPlanExecuteTool(deps) {
|
|
|
23
45
|
const params = rawParams;
|
|
24
46
|
try {
|
|
25
47
|
if (params.planId === undefined) {
|
|
26
|
-
const
|
|
48
|
+
const plans = scope === undefined
|
|
49
|
+
? []
|
|
50
|
+
: store
|
|
51
|
+
.listPlansByOrigin(scope)
|
|
52
|
+
.filter((plan) => !isPlanCancelled(plan))
|
|
53
|
+
.slice(0, 10)
|
|
54
|
+
.map(summarizePlan);
|
|
55
|
+
const result = { ok: true, plans };
|
|
27
56
|
return toolJsonResult(result);
|
|
28
57
|
}
|
|
29
58
|
const plan = store.get(params.planId);
|
|
30
|
-
if (plan === undefined) {
|
|
59
|
+
if (plan === undefined || !planInScope(plan, scope)) {
|
|
31
60
|
const result = { ok: false, error: { code: "PLAN_NOT_FOUND" } };
|
|
32
61
|
return toolJsonResult(result);
|
|
33
62
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Plan, PlanOrigin } from "@xgjktech/xg-openclaw-shared";
|
|
2
|
+
export interface PlanScope {
|
|
3
|
+
channel: string;
|
|
4
|
+
to: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function scopeKey(origin?: PlanOrigin): PlanScope | undefined;
|
|
7
|
+
export declare function planInScope(plan: Plan, scope: PlanScope | undefined): boolean;
|
|
8
|
+
//# sourceMappingURL=plan-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-scope.d.ts","sourceRoot":"","sources":["../src/plan-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAMD,wBAAgB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAKnE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAQ7E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function isNonEmptyString(value) {
|
|
2
|
+
return typeof value === "string" && value.trim() !== "";
|
|
3
|
+
}
|
|
4
|
+
export function scopeKey(origin) {
|
|
5
|
+
if (!isNonEmptyString(origin?.channel) || !isNonEmptyString(origin?.to)) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
return { channel: origin.channel, to: origin.to };
|
|
9
|
+
}
|
|
10
|
+
export function planInScope(plan, scope) {
|
|
11
|
+
const planScope = scopeKey(plan.origin);
|
|
12
|
+
return (scope !== undefined &&
|
|
13
|
+
planScope !== undefined &&
|
|
14
|
+
planScope.channel === scope.channel &&
|
|
15
|
+
planScope.to === scope.to);
|
|
16
|
+
}
|
package/dist/plan-view.d.ts
CHANGED
package/dist/plan-view.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-view.d.ts","sourceRoot":"","sources":["../src/plan-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"plan-view.d.ts","sourceRoot":"","sources":["../src/plan-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,IAAI,EAGV,MAAM,8BAA8B,CAAC;AAoBtC,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAgBjD"}
|
package/dist/plan-view.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPlanCancelled, } from "@xgjktech/xg-openclaw-shared";
|
|
1
2
|
const TASK_STATUS_MARKERS = {
|
|
2
3
|
pending: "[ ]",
|
|
3
4
|
in_progress: "[>]",
|
|
@@ -14,7 +15,8 @@ function singleLine(text) {
|
|
|
14
15
|
return text.replace(/\s*\r?\n\s*/g, " ");
|
|
15
16
|
}
|
|
16
17
|
export function renderPlanView(plan) {
|
|
17
|
-
const
|
|
18
|
+
const cancelledMarker = isPlanCancelled(plan) ? "【已取消】" : "";
|
|
19
|
+
const lines = [`goal: ${cancelledMarker}${singleLine(plan.goal)}`];
|
|
18
20
|
plan.tasks.forEach((task, taskIndex) => {
|
|
19
21
|
const note = task.note !== undefined ? ` | note: ${singleLine(task.note)}` : "";
|
|
20
22
|
lines.push(`T${taskIndex} ${TASK_STATUS_MARKERS[task.status]} ${singleLine(task.title)}${note}`);
|
|
@@ -3,12 +3,13 @@ import type { AnyAgentTool } from "openclaw/plugin-sdk/plugin-entry";
|
|
|
3
3
|
import { type PlanOrigin, type PlanStore } from "@xgjktech/xg-openclaw-shared";
|
|
4
4
|
export declare const PlanWriteParams: Type.TObject<{
|
|
5
5
|
planId: Type.TOptional<Type.TString>;
|
|
6
|
-
goal: Type.TString
|
|
7
|
-
tasks: Type.TArray<Type.TObject<{
|
|
6
|
+
goal: Type.TOptional<Type.TString>;
|
|
7
|
+
tasks: Type.TOptional<Type.TArray<Type.TObject<{
|
|
8
8
|
title: Type.TString;
|
|
9
9
|
status: Type.TOptional<Type.TUnion<[Type.TLiteral<"pending">, Type.TLiteral<"in_progress">, Type.TLiteral<"completed">, Type.TLiteral<"blocked">]>>;
|
|
10
10
|
note: Type.TOptional<Type.TString>;
|
|
11
|
-
}
|
|
11
|
+
}>>>;
|
|
12
|
+
cancel: Type.TOptional<Type.TBoolean>;
|
|
12
13
|
}>;
|
|
13
14
|
export type PlanWriteParamsT = Static<typeof PlanWriteParams>;
|
|
14
15
|
export declare const PLAN_WRITE_DESCRIPTION: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-write-tool.d.ts","sourceRoot":"","sources":["../src/plan-write-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"plan-write-tool.d.ts","sourceRoot":"","sources":["../src/plan-write-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,SAAS,EAGf,MAAM,8BAA8B,CAAC;AA8BtC,eAAO,MAAM,eAAe;;;;;;;;;EAwB3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,sBAAsB,QAmBvB,CAAC;AA0Eb,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAsHtF"}
|
package/dist/plan-write-tool.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { assertPlanInvariants, } from "@xgjktech/xg-openclaw-shared";
|
|
3
|
+
import { assertPlanInvariants, isPlanCancelled, } from "@xgjktech/xg-openclaw-shared";
|
|
4
4
|
import { toolJsonResult } from "./tool-json-result.js";
|
|
5
5
|
import { renderPlanView } from "./plan-view.js";
|
|
6
|
+
import { planInScope, scopeKey } from "./plan-scope.js";
|
|
6
7
|
const TaskInput = Type.Object({
|
|
7
8
|
title: Type.String({
|
|
8
9
|
minLength: 1,
|
|
@@ -19,16 +20,17 @@ const TaskInput = Type.Object({
|
|
|
19
20
|
}, { additionalProperties: false });
|
|
20
21
|
export const PlanWriteParams = Type.Object({
|
|
21
22
|
planId: Type.Optional(Type.String({ maxLength: 128, description: "留空=新建;填写=整张覆盖该计划" })),
|
|
22
|
-
goal: Type.String({
|
|
23
|
+
goal: Type.Optional(Type.String({
|
|
23
24
|
minLength: 1,
|
|
24
25
|
maxLength: 500,
|
|
25
26
|
description: "计划总目标,一句话说明完成态",
|
|
26
|
-
}),
|
|
27
|
-
tasks: Type.Array(TaskInput, {
|
|
27
|
+
})),
|
|
28
|
+
tasks: Type.Optional(Type.Array(TaskInput, {
|
|
28
29
|
minItems: 1,
|
|
29
30
|
maxItems: 20,
|
|
30
31
|
description: "完整任务清单;更新时未提交的旧任务会被删除",
|
|
31
|
-
}),
|
|
32
|
+
})),
|
|
33
|
+
cancel: Type.Optional(Type.Boolean({ description: "取消该计划;true 时只需带 planId,不带 goal/tasks" })),
|
|
32
34
|
}, { additionalProperties: false });
|
|
33
35
|
export const PLAN_WRITE_DESCRIPTION = [
|
|
34
36
|
"用途:维护一份对用户可见、可跨回合保存的任务清单。每次调用都是整张替换,不是增量更新。",
|
|
@@ -45,9 +47,10 @@ export const PLAN_WRITE_DESCRIPTION = [
|
|
|
45
47
|
"",
|
|
46
48
|
"提交规则:",
|
|
47
49
|
"- 新建时不传 planId;更新时传入原 planId。",
|
|
48
|
-
"-
|
|
50
|
+
"- 更新计划内容时必须重新提交 goal 和所有要保留的 tasks,包括未变化任务及其当前 status/note。只提交变化项会删除其他任务;记不清时先用 xg_plan_execute 查看。",
|
|
49
51
|
"- goal 用一句话;tasks 通常 3-7 项,每项是能判断是否完成的具体动作,不写 TODO、待定或内部思考。",
|
|
50
52
|
"- status 为 pending、in_progress、completed 或 blocked。通常只有一项 in_progress,确实并行时可以有多项;blocked 时必须用 note 写明原因和继续所需条件。",
|
|
53
|
+
"- 同一件工作只维护一份计划;任务方向变化时,先取消不再推进的旧计划({planId, cancel:true})再新建。",
|
|
51
54
|
].join("\n");
|
|
52
55
|
function toTask(input) {
|
|
53
56
|
const task = {
|
|
@@ -59,24 +62,52 @@ function toTask(input) {
|
|
|
59
62
|
}
|
|
60
63
|
return task;
|
|
61
64
|
}
|
|
62
|
-
function buildPlan(
|
|
63
|
-
const now = new Date().toISOString();
|
|
65
|
+
function buildPlan(goal, tasks, existing, planId, origin, now) {
|
|
64
66
|
const plan = {
|
|
65
67
|
planId,
|
|
66
|
-
goal
|
|
68
|
+
goal,
|
|
67
69
|
createdAt: existing?.createdAt ?? now,
|
|
68
70
|
updatedAt: now,
|
|
69
|
-
tasks:
|
|
71
|
+
tasks: tasks.map(toTask),
|
|
70
72
|
};
|
|
71
|
-
// 改写已有计划时保留原 origin
|
|
72
|
-
//
|
|
73
|
+
// 改写已有计划时保留原 origin(改写是内容编辑,不是重新发起)。
|
|
74
|
+
// 无 origin 的旧计划已在作用域校验阶段被拒绝,不会在这里被认领。
|
|
73
75
|
const resolvedOrigin = existing !== undefined ? (existing.origin ?? origin) : origin;
|
|
74
76
|
if (resolvedOrigin !== undefined) {
|
|
75
77
|
plan.origin = resolvedOrigin;
|
|
76
78
|
}
|
|
77
79
|
return plan;
|
|
78
80
|
}
|
|
81
|
+
function compactText(text) {
|
|
82
|
+
return text.replace(/\s*\r?\n\s*/g, " ");
|
|
83
|
+
}
|
|
84
|
+
function relativeTime(updatedAt, nowMs) {
|
|
85
|
+
const updatedAtMs = Date.parse(updatedAt);
|
|
86
|
+
if (!Number.isFinite(updatedAtMs))
|
|
87
|
+
return "时间未知";
|
|
88
|
+
const elapsedMs = Math.max(0, nowMs - updatedAtMs);
|
|
89
|
+
const minutes = Math.floor(elapsedMs / 60_000);
|
|
90
|
+
if (minutes < 1)
|
|
91
|
+
return "刚刚";
|
|
92
|
+
if (minutes < 60)
|
|
93
|
+
return `${minutes} 分钟前`;
|
|
94
|
+
const hours = Math.floor(minutes / 60);
|
|
95
|
+
if (hours < 24)
|
|
96
|
+
return `${hours} 小时前`;
|
|
97
|
+
return `${Math.floor(hours / 24)} 天前`;
|
|
98
|
+
}
|
|
99
|
+
function unfinishedPlanWarning(plans, nowMs) {
|
|
100
|
+
const unfinished = plans.filter((plan) => !isPlanCancelled(plan) && plan.tasks.some((task) => task.status !== "completed"));
|
|
101
|
+
if (unfinished.length === 0)
|
|
102
|
+
return undefined;
|
|
103
|
+
const details = unfinished.slice(0, 3).map((plan) => {
|
|
104
|
+
const completed = plan.tasks.filter((task) => task.status === "completed").length;
|
|
105
|
+
return `${plan.planId} «${compactText(plan.goal)}»(${completed}/${plan.tasks.length},最后更新 ${relativeTime(plan.updatedAt, nowMs)})`;
|
|
106
|
+
});
|
|
107
|
+
return `本会话还有 ${unfinished.length} 份未完成计划:${details.join(";")}。若已不再推进,请用 {planId, cancel:true} 取消,避免面板残留。`;
|
|
108
|
+
}
|
|
79
109
|
export function buildPlanWriteTool(store, origin) {
|
|
110
|
+
const scope = scopeKey(origin);
|
|
80
111
|
return {
|
|
81
112
|
name: "xg_plan_write",
|
|
82
113
|
label: "【xg-harness】编写计划",
|
|
@@ -85,10 +116,61 @@ export function buildPlanWriteTool(store, origin) {
|
|
|
85
116
|
async execute(_toolCallId, rawParams) {
|
|
86
117
|
const params = rawParams;
|
|
87
118
|
try {
|
|
119
|
+
if (params.cancel === true) {
|
|
120
|
+
if (params.planId === undefined ||
|
|
121
|
+
params.goal !== undefined ||
|
|
122
|
+
params.tasks !== undefined) {
|
|
123
|
+
const result = {
|
|
124
|
+
ok: false,
|
|
125
|
+
error: { code: "CANCEL_FIELDS_CONFLICT" },
|
|
126
|
+
};
|
|
127
|
+
return toolJsonResult(result);
|
|
128
|
+
}
|
|
129
|
+
const existing = store.get(params.planId);
|
|
130
|
+
if (existing === undefined || !planInScope(existing, scope)) {
|
|
131
|
+
const result = {
|
|
132
|
+
ok: false,
|
|
133
|
+
error: { code: "PLAN_NOT_FOUND" },
|
|
134
|
+
};
|
|
135
|
+
return toolJsonResult(result);
|
|
136
|
+
}
|
|
137
|
+
if (isPlanCancelled(existing)) {
|
|
138
|
+
const result = {
|
|
139
|
+
ok: true,
|
|
140
|
+
planId: existing.planId,
|
|
141
|
+
cancelled: true,
|
|
142
|
+
view: renderPlanView(existing),
|
|
143
|
+
};
|
|
144
|
+
return toolJsonResult(result);
|
|
145
|
+
}
|
|
146
|
+
const now = new Date().toISOString();
|
|
147
|
+
const cancelledPlan = {
|
|
148
|
+
...existing,
|
|
149
|
+
status: "cancelled",
|
|
150
|
+
cancelledAt: now,
|
|
151
|
+
updatedAt: now,
|
|
152
|
+
};
|
|
153
|
+
assertPlanInvariants(cancelledPlan);
|
|
154
|
+
store.set(cancelledPlan);
|
|
155
|
+
const result = {
|
|
156
|
+
ok: true,
|
|
157
|
+
planId: cancelledPlan.planId,
|
|
158
|
+
cancelled: true,
|
|
159
|
+
view: renderPlanView(cancelledPlan),
|
|
160
|
+
};
|
|
161
|
+
return toolJsonResult(result);
|
|
162
|
+
}
|
|
163
|
+
if (params.goal === undefined || params.tasks === undefined) {
|
|
164
|
+
const result = {
|
|
165
|
+
ok: false,
|
|
166
|
+
error: { code: "WRITE_FIELDS_REQUIRED" },
|
|
167
|
+
};
|
|
168
|
+
return toolJsonResult(result);
|
|
169
|
+
}
|
|
88
170
|
let existing;
|
|
89
171
|
if (params.planId !== undefined) {
|
|
90
172
|
existing = store.get(params.planId);
|
|
91
|
-
if (existing === undefined) {
|
|
173
|
+
if (existing === undefined || !planInScope(existing, scope)) {
|
|
92
174
|
const result = {
|
|
93
175
|
ok: false,
|
|
94
176
|
error: { code: "PLAN_NOT_FOUND" },
|
|
@@ -96,14 +178,21 @@ export function buildPlanWriteTool(store, origin) {
|
|
|
96
178
|
return toolJsonResult(result);
|
|
97
179
|
}
|
|
98
180
|
}
|
|
181
|
+
let warnings;
|
|
182
|
+
if (params.planId === undefined && scope !== undefined) {
|
|
183
|
+
const warning = unfinishedPlanWarning(store.listPlansByOrigin(scope), Date.now());
|
|
184
|
+
if (warning !== undefined)
|
|
185
|
+
warnings = [warning];
|
|
186
|
+
}
|
|
99
187
|
const planId = params.planId ?? randomUUID();
|
|
100
|
-
const plan = buildPlan(params, existing, planId, origin);
|
|
188
|
+
const plan = buildPlan(params.goal, params.tasks, existing, planId, origin, new Date().toISOString());
|
|
101
189
|
assertPlanInvariants(plan);
|
|
102
190
|
store.set(plan);
|
|
103
191
|
const result = {
|
|
104
192
|
ok: true,
|
|
105
193
|
planId: plan.planId,
|
|
106
194
|
view: renderPlanView(plan),
|
|
195
|
+
...(warnings !== undefined ? { warnings } : {}),
|
|
107
196
|
};
|
|
108
197
|
return toolJsonResult(result);
|
|
109
198
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xgjktech/xg-openclaw-harness-tools",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "企业工具插件:plan_write / plan_execute 计划工具(OpenClaw 第三方工具插件)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"typebox": "1.1.39",
|
|
30
|
-
"@xgjktech/xg-openclaw-shared": "^0.2.
|
|
30
|
+
"@xgjktech/xg-openclaw-shared": "^0.2.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.19.0",
|