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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4948,7 +4948,7 @@ function goalToolsV2(services) {
{
name: "update_goal_plan",
description: services.locale === "zh-CN" ? "\u4FDD\u5B58\u76EE\u6807\u7684\u6574\u4F53\u8BA1\u5212\u3001\u9636\u6BB5\u3001\u4EFB\u52A1\u548C\u9A8C\u8BC1\u8BC1\u636E\u3002\u4FDD\u6301\u6574\u4F53\u76EE\u6807\u4E0D\u53D8\uFF1B\u4F7F\u7528 get_goal \u8FD4\u56DE\u7684\u76EE\u6807 ID \u548C\u8BA1\u5212\u7248\u672C\u3002" : "Persist the overall plan, phases, tasks, verification evidence and decisions. Preserve the goal scope; use the goal ID and planRevision from get_goal. Completed work cannot be silently reopened or removed.",
input: v2ObjectSchema(planToolArgs),
input: z2.toJSONSchema(PlanToolSchema, { io: "input", target: "draft-7" }),
options: { codemode: false },
execute: async (args, context) => ({ content: await planFromTool(args, context) })
},
Expand Down
4 changes: 3 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3356,7 +3356,9 @@ function goalToolsV2(services: GoalServices): ToolV2Info[] {
{
name: "update_goal_plan",
description: services.locale === "zh-CN" ? "保存目标的整体计划、阶段、任务和验证证据。保持整体目标不变;使用 get_goal 返回的目标 ID 和计划版本。" : "Persist the overall plan, phases, tasks, verification evidence and decisions. Preserve the goal scope; use the goal ID and planRevision from get_goal. Completed work cannot be silently reopened or removed.",
input: v2ObjectSchema(planToolArgs),
// V2 accepts JSON Schema, not the Zod objects used by the V1 tool API.
// Input mode preserves optional evidence and defaulted plan decisions.
input: z.toJSONSchema(PlanToolSchema, { io: "input", target: "draft-7" }) as ToolSchema.ValueSchema,
options: { codemode: false },
execute: async (args, context) => ({ content: await planFromTool(args, context) }),
},
Expand Down
44 changes: 44 additions & 0 deletions test/server-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,50 @@ test("V2 setup registers goal tools with JSON Schema inputs, codemode:false, and
expect(mock.promptCalls).toHaveLength(0)
})

test("V2 plan tool publishes JSON Schema with required fields and optional nested evidence", async () => {
const mock = makeMockContext({ auto_continue: false })
await setupPlugin(mock as never)
// Inspect the serialized wire schema, not only the outer object wrapper.
const input = JSON.parse(JSON.stringify(goalTool(mock, "update_goal_plan").input))
expect(input.required).toEqual(["goal_id", "expected_revision", "plan", "reason"])
expect(input.additionalProperties).toBe(false)
expect(input.properties.goal_id).toMatchObject({ type: "string", minLength: 1 })
expect(input.properties.expected_revision).toMatchObject({ type: "integer", minimum: 0 })
expect(input.properties.reason).toMatchObject({ type: "string", minLength: 1, maxLength: 2000 })
expect(input.properties.revisit_evidence).toMatchObject({ type: "string", minLength: 1, maxLength: 2000 })

const plan = input.properties.plan
expect(plan.required).toEqual(["summary", "completionCriteria", "phases"])
expect(plan.additionalProperties).toBe(false)
expect(plan.properties.decisions).toMatchObject({ type: "array", default: [], maxItems: 32 })
expect(plan.properties.completionCriteria).toMatchObject({ type: "array", minItems: 1, maxItems: 32 })
const phases = plan.properties.phases
expect(phases).toMatchObject({ type: "array", minItems: 1, maxItems: 64 })
const phase = phases.items
expect(phase.required).toEqual(["id", "objective", "status", "tasks"])
expect(phase.additionalProperties).toBe(false)
const tasks = phase.properties.tasks
expect(tasks).toMatchObject({ type: "array", minItems: 1, maxItems: 128 })
const task = tasks.items
expect(task.required).toEqual(["id", "description", "status"])
expect(task.additionalProperties).toBe(false)
expect(task.properties.status).toEqual({
type: "string",
enum: ["pending", "in_progress", "completed", "blocked"],
})
for (const field of [
phase.properties.verification,
phase.properties.blocker,
task.properties.evidence,
task.properties.blocker,
]) {
expect(field.anyOf).toEqual([
{ type: "string", minLength: 1, maxLength: 2000 },
{ type: "null" },
])
}
})

test("V2 stop, replace, clear, and history tools preserve prior goals", async () => {
const mock = makeMockContext({ auto_continue: false })
const cleanup = await setupPlugin(mock as never)
Expand Down
Loading