@ai-agent-forge/plugin-goal 0.86.3 → 0.86.5
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/agent-forge.json +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/plugin.json +1 -1
package/agent-forge.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -25,5 +25,12 @@ export interface GoalRecord {
|
|
|
25
25
|
export interface GoalState {
|
|
26
26
|
goals: GoalRecord[];
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* `/goal` command text rendering: one line per goal (`id [status] title`)
|
|
30
|
+
* plus the evidence lines that justified completion. Plain text only —
|
|
31
|
+
* every host shape can render it, no TUI required (the same L1 contract as
|
|
32
|
+
* the workflow plugin's `/workflow`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderGoalState(state: GoalState): string;
|
|
28
35
|
export declare function createCapability(api: CapabilityAPI): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[];
|
|
29
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGrF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,iQAC6N,CAAC;AAE3P,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;AAuDD,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,aAAa,GAChB,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,GAAG,SAAS,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGrF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,iQAC6N,CAAC;AAE3P,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;AAuDD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAYxD;AAED,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,aAAa,GAChB,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,GAAG,SAAS,sBAAsB,EAAE,CA4DhF","sourcesContent":["/**\n * Goal capability plugin: a self-contained goal state machine, extracted from\n * the host's `src/capabilities/goal.ts` (D-075 S4, first B-class batch).\n *\n * Goals persist as append-only `goal.state` session entries and every change\n * is announced as a `goal.changed` event. The plugin only consumes the public\n * `session`, `events`, and `tools` capabilities, keeps soft string references\n * to other domains, and never validates state owned by another plugin.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\n\n/**\n * Task tools guide(任务工具引导 #4,原 system-prompt.ts TASK_TOOL_GUIDE,D-071\n * 迁移):tells the model WHEN to use the goal/plan/todo face. It rides\n * goal_set's promptGuidelines so the text lives with the plugin that owns the\n * tools (宪法 §3) and renders whenever the tool face is active.\n */\nexport const TASK_TOOLS_GUIDE =\n\t\"Task Management: For multi-step tasks that span multiple turns, use goal_set to define the objective, plan_set to outline the approach, and todo_update to track progress. Skip these tools for simple one-step operations — just do the work directly.\";\n\nexport interface GoalRecord {\n\tid: string;\n\ttitle: string;\n\tstatus: \"open\" | \"completed\" | \"cancelled\";\n\trevision: number;\n\tevidence: string[];\n}\n\nexport interface GoalState {\n\tgoals: GoalRecord[];\n}\n\nconst stateEntry = \"goal.state\";\n\nconst goalSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the goal to create or update\" }),\n\ttitle: Type.String({ description: \"Human-readable goal title\" }),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing goal\",\n\t\tminimum: 0,\n\t}),\n\tstatus: Type.Optional(\n\t\tType.Union([Type.Literal(\"open\"), Type.Literal(\"completed\"), Type.Literal(\"cancelled\")], {\n\t\t\tdescription: \"Next status; defaults to the stored status for existing goals, otherwise open\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Evidence references; at least one is required when status is completed\",\n\t\t}),\n\t),\n});\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Goal input must be an object\");\n\treturn value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, name: string): string {\n\tif (typeof value !== \"string\" || value.trim() === \"\") throw new Error(`${name} must be a non-empty string`);\n\treturn value;\n}\n\nfunction revision(value: unknown): number {\n\tif (!Number.isInteger(value) || (value as number) < 0) throw new Error(\"revision must be a non-negative integer\");\n\treturn value as number;\n}\n\nfunction evidence(value: unknown): string[] {\n\tif (value === undefined) return [];\n\tif (!Array.isArray(value) || value.some((item) => typeof item !== \"string\" || item.trim() === \"\")) {\n\t\tthrow new Error(\"evidence must be a string array\");\n\t}\n\treturn [...(value as string[])];\n}\n\nfunction loadState(api: CapabilityAPI): GoalState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === stateEntry);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { goals: [] };\n\tconst data = entry.data as Partial<GoalState>;\n\treturn { goals: Array.isArray(data.goals) ? [...data.goals] : [] };\n}\n\n/**\n * `/goal` command text rendering: one line per goal (`id [status] title`)\n * plus the evidence lines that justified completion. Plain text only —\n * every host shape can render it, no TUI required (the same L1 contract as\n * the workflow plugin's `/workflow`).\n */\nexport function renderGoalState(state: GoalState): string {\n\tif (state.goals.length === 0) return \"no goals in this session; create one with goal_set\";\n\tconst open = state.goals.filter((goal) => goal.status === \"open\").length;\n\tconst lines = [`goals: ${state.goals.length} (${open} open)`];\n\t// Record fields only validate non-emptiness, so fold newlines to keep the\n\t// one-line-per-record shape whatever the model wrote.\n\tconst inline = (value: string): string => value.replace(/\\n/g, \" \");\n\tfor (const goal of state.goals) {\n\t\tlines.push(`- ${inline(goal.id)} [${goal.status}] ${inline(goal.title)}`);\n\t\tfor (const item of goal.evidence) lines.push(` evidence: ${inline(item)}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\n\tconst persist = async (): Promise<GoalState> => {\n\t\tapi.session?.appendEntry(stateEntry, state);\n\t\tawait api.publish({ type: \"goal.changed\", version: 1, correlationId: stateEntry, data: state });\n\t\treturn state;\n\t};\n\n\tconst tool = api.registerTool({\n\t\tname: \"goal_set\",\n\t\tlabel: \"Set goal\",\n\t\tpromptGuidelines: [TASK_TOOLS_GUIDE],\n\t\tdescription: \"Create or update a goal with explicit revision and evidence\",\n\t\tparameters: goalSchema,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst input = objectInput(first, second);\n\t\t\tconst id = text(input.id, \"id\");\n\t\t\tconst title = text(input.title, \"title\");\n\t\t\tconst nextRevision = revision(input.revision);\n\t\t\tconst current = state.goals.find((item) => item.id === id);\n\t\t\tif (current && nextRevision <= current.revision) throw new Error(`stale goal revision: ${id}`);\n\t\t\tif (current?.status === \"completed\" || current?.status === \"cancelled\") {\n\t\t\t\tthrow new Error(`goal is terminal and cannot be updated: ${id}`);\n\t\t\t}\n\t\t\tconst values = evidence(input.evidence);\n\t\t\tconst record: GoalRecord = {\n\t\t\t\tid,\n\t\t\t\ttitle,\n\t\t\t\tstatus:\n\t\t\t\t\tinput.status === undefined\n\t\t\t\t\t\t? (current?.status ?? \"open\")\n\t\t\t\t\t\t: (text(input.status, \"status\") as GoalRecord[\"status\"]),\n\t\t\t\trevision: nextRevision,\n\t\t\t\tevidence: values,\n\t\t\t};\n\t\t\tif (![\"open\", \"completed\", \"cancelled\"].includes(record.status)) throw new Error(\"invalid goal status\");\n\t\t\tif (record.status === \"completed\" && values.length === 0) {\n\t\t\t\tthrow new Error(\"goal completion requires at least one evidence reference\");\n\t\t\t}\n\t\t\tstate = { ...state, goals: [...state.goals.filter((item) => item.id !== id), record] };\n\t\t\treturn persist();\n\t\t},\n\t});\n\n\tconst command = api.registerCommand({\n\t\tname: \"goal\",\n\t\tdescription: \"Show this session's goals (status and evidence)\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tif (args.trim() !== \"\") return { status: \"error\" as const, message: \"Usage: /goal\" };\n\t\t\t\treturn { status: \"success\" as const, message: renderGoalState(state) };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\treturn [tool, command];\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -62,6 +62,27 @@ function loadState(api) {
|
|
|
62
62
|
const data = entry.data;
|
|
63
63
|
return { goals: Array.isArray(data.goals) ? [...data.goals] : [] };
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* `/goal` command text rendering: one line per goal (`id [status] title`)
|
|
67
|
+
* plus the evidence lines that justified completion. Plain text only —
|
|
68
|
+
* every host shape can render it, no TUI required (the same L1 contract as
|
|
69
|
+
* the workflow plugin's `/workflow`).
|
|
70
|
+
*/
|
|
71
|
+
export function renderGoalState(state) {
|
|
72
|
+
if (state.goals.length === 0)
|
|
73
|
+
return "no goals in this session; create one with goal_set";
|
|
74
|
+
const open = state.goals.filter((goal) => goal.status === "open").length;
|
|
75
|
+
const lines = [`goals: ${state.goals.length} (${open} open)`];
|
|
76
|
+
// Record fields only validate non-emptiness, so fold newlines to keep the
|
|
77
|
+
// one-line-per-record shape whatever the model wrote.
|
|
78
|
+
const inline = (value) => value.replace(/\n/g, " ");
|
|
79
|
+
for (const goal of state.goals) {
|
|
80
|
+
lines.push(`- ${inline(goal.id)} [${goal.status}] ${inline(goal.title)}`);
|
|
81
|
+
for (const item of goal.evidence)
|
|
82
|
+
lines.push(` evidence: ${inline(item)}`);
|
|
83
|
+
}
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
65
86
|
export function createCapability(api) {
|
|
66
87
|
let state = loadState(api);
|
|
67
88
|
const persist = async () => {
|
|
@@ -105,6 +126,21 @@ export function createCapability(api) {
|
|
|
105
126
|
return persist();
|
|
106
127
|
},
|
|
107
128
|
});
|
|
108
|
-
|
|
129
|
+
const command = api.registerCommand({
|
|
130
|
+
name: "goal",
|
|
131
|
+
description: "Show this session's goals (status and evidence)",
|
|
132
|
+
execute: async (args) => {
|
|
133
|
+
try {
|
|
134
|
+
if (args.trim() !== "")
|
|
135
|
+
return { status: "error", message: "Usage: /goal" };
|
|
136
|
+
return { status: "success", message: renderGoalState(state) };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
// A slash command must not crash the host (memory /promote precedent).
|
|
140
|
+
return { status: "error", message: error instanceof Error ? error.message : String(error) };
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
return [tool, command];
|
|
109
145
|
}
|
|
110
146
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal capability plugin: a self-contained goal state machine, extracted from\n * the host's `src/capabilities/goal.ts` (D-075 S4, first B-class batch).\n *\n * Goals persist as append-only `goal.state` session entries and every change\n * is announced as a `goal.changed` event. The plugin only consumes the public\n * `session`, `events`, and `tools` capabilities, keeps soft string references\n * to other domains, and never validates state owned by another plugin.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\n\n/**\n * Task tools guide(任务工具引导 #4,原 system-prompt.ts TASK_TOOL_GUIDE,D-071\n * 迁移):tells the model WHEN to use the goal/plan/todo face. It rides\n * goal_set's promptGuidelines so the text lives with the plugin that owns the\n * tools (宪法 §3) and renders whenever the tool face is active.\n */\nexport const TASK_TOOLS_GUIDE =\n\t\"Task Management: For multi-step tasks that span multiple turns, use goal_set to define the objective, plan_set to outline the approach, and todo_update to track progress. Skip these tools for simple one-step operations — just do the work directly.\";\n\nexport interface GoalRecord {\n\tid: string;\n\ttitle: string;\n\tstatus: \"open\" | \"completed\" | \"cancelled\";\n\trevision: number;\n\tevidence: string[];\n}\n\nexport interface GoalState {\n\tgoals: GoalRecord[];\n}\n\nconst stateEntry = \"goal.state\";\n\nconst goalSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the goal to create or update\" }),\n\ttitle: Type.String({ description: \"Human-readable goal title\" }),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing goal\",\n\t\tminimum: 0,\n\t}),\n\tstatus: Type.Optional(\n\t\tType.Union([Type.Literal(\"open\"), Type.Literal(\"completed\"), Type.Literal(\"cancelled\")], {\n\t\t\tdescription: \"Next status; defaults to the stored status for existing goals, otherwise open\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Evidence references; at least one is required when status is completed\",\n\t\t}),\n\t),\n});\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Goal input must be an object\");\n\treturn value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, name: string): string {\n\tif (typeof value !== \"string\" || value.trim() === \"\") throw new Error(`${name} must be a non-empty string`);\n\treturn value;\n}\n\nfunction revision(value: unknown): number {\n\tif (!Number.isInteger(value) || (value as number) < 0) throw new Error(\"revision must be a non-negative integer\");\n\treturn value as number;\n}\n\nfunction evidence(value: unknown): string[] {\n\tif (value === undefined) return [];\n\tif (!Array.isArray(value) || value.some((item) => typeof item !== \"string\" || item.trim() === \"\")) {\n\t\tthrow new Error(\"evidence must be a string array\");\n\t}\n\treturn [...(value as string[])];\n}\n\nfunction loadState(api: CapabilityAPI): GoalState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === stateEntry);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { goals: [] };\n\tconst data = entry.data as Partial<GoalState>;\n\treturn { goals: Array.isArray(data.goals) ? [...data.goals] : [] };\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\n\tconst persist = async (): Promise<GoalState> => {\n\t\tapi.session?.appendEntry(stateEntry, state);\n\t\tawait api.publish({ type: \"goal.changed\", version: 1, correlationId: stateEntry, data: state });\n\t\treturn state;\n\t};\n\n\tconst tool = api.registerTool({\n\t\tname: \"goal_set\",\n\t\tlabel: \"Set goal\",\n\t\tpromptGuidelines: [TASK_TOOLS_GUIDE],\n\t\tdescription: \"Create or update a goal with explicit revision and evidence\",\n\t\tparameters: goalSchema,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst input = objectInput(first, second);\n\t\t\tconst id = text(input.id, \"id\");\n\t\t\tconst title = text(input.title, \"title\");\n\t\t\tconst nextRevision = revision(input.revision);\n\t\t\tconst current = state.goals.find((item) => item.id === id);\n\t\t\tif (current && nextRevision <= current.revision) throw new Error(`stale goal revision: ${id}`);\n\t\t\tif (current?.status === \"completed\" || current?.status === \"cancelled\") {\n\t\t\t\tthrow new Error(`goal is terminal and cannot be updated: ${id}`);\n\t\t\t}\n\t\t\tconst values = evidence(input.evidence);\n\t\t\tconst record: GoalRecord = {\n\t\t\t\tid,\n\t\t\t\ttitle,\n\t\t\t\tstatus:\n\t\t\t\t\tinput.status === undefined\n\t\t\t\t\t\t? (current?.status ?? \"open\")\n\t\t\t\t\t\t: (text(input.status, \"status\") as GoalRecord[\"status\"]),\n\t\t\t\trevision: nextRevision,\n\t\t\t\tevidence: values,\n\t\t\t};\n\t\t\tif (![\"open\", \"completed\", \"cancelled\"].includes(record.status)) throw new Error(\"invalid goal status\");\n\t\t\tif (record.status === \"completed\" && values.length === 0) {\n\t\t\t\tthrow new Error(\"goal completion requires at least one evidence reference\");\n\t\t\t}\n\t\t\tstate = { ...state, goals: [...state.goals.filter((item) => item.id !== id), record] };\n\t\t\treturn persist();\n\t\t},\n\t});\n\n\treturn [tool];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal capability plugin: a self-contained goal state machine, extracted from\n * the host's `src/capabilities/goal.ts` (D-075 S4, first B-class batch).\n *\n * Goals persist as append-only `goal.state` session entries and every change\n * is announced as a `goal.changed` event. The plugin only consumes the public\n * `session`, `events`, and `tools` capabilities, keeps soft string references\n * to other domains, and never validates state owned by another plugin.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\n\n/**\n * Task tools guide(任务工具引导 #4,原 system-prompt.ts TASK_TOOL_GUIDE,D-071\n * 迁移):tells the model WHEN to use the goal/plan/todo face. It rides\n * goal_set's promptGuidelines so the text lives with the plugin that owns the\n * tools (宪法 §3) and renders whenever the tool face is active.\n */\nexport const TASK_TOOLS_GUIDE =\n\t\"Task Management: For multi-step tasks that span multiple turns, use goal_set to define the objective, plan_set to outline the approach, and todo_update to track progress. Skip these tools for simple one-step operations — just do the work directly.\";\n\nexport interface GoalRecord {\n\tid: string;\n\ttitle: string;\n\tstatus: \"open\" | \"completed\" | \"cancelled\";\n\trevision: number;\n\tevidence: string[];\n}\n\nexport interface GoalState {\n\tgoals: GoalRecord[];\n}\n\nconst stateEntry = \"goal.state\";\n\nconst goalSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the goal to create or update\" }),\n\ttitle: Type.String({ description: \"Human-readable goal title\" }),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing goal\",\n\t\tminimum: 0,\n\t}),\n\tstatus: Type.Optional(\n\t\tType.Union([Type.Literal(\"open\"), Type.Literal(\"completed\"), Type.Literal(\"cancelled\")], {\n\t\t\tdescription: \"Next status; defaults to the stored status for existing goals, otherwise open\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Evidence references; at least one is required when status is completed\",\n\t\t}),\n\t),\n});\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Goal input must be an object\");\n\treturn value as Record<string, unknown>;\n}\n\nfunction text(value: unknown, name: string): string {\n\tif (typeof value !== \"string\" || value.trim() === \"\") throw new Error(`${name} must be a non-empty string`);\n\treturn value;\n}\n\nfunction revision(value: unknown): number {\n\tif (!Number.isInteger(value) || (value as number) < 0) throw new Error(\"revision must be a non-negative integer\");\n\treturn value as number;\n}\n\nfunction evidence(value: unknown): string[] {\n\tif (value === undefined) return [];\n\tif (!Array.isArray(value) || value.some((item) => typeof item !== \"string\" || item.trim() === \"\")) {\n\t\tthrow new Error(\"evidence must be a string array\");\n\t}\n\treturn [...(value as string[])];\n}\n\nfunction loadState(api: CapabilityAPI): GoalState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === stateEntry);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { goals: [] };\n\tconst data = entry.data as Partial<GoalState>;\n\treturn { goals: Array.isArray(data.goals) ? [...data.goals] : [] };\n}\n\n/**\n * `/goal` command text rendering: one line per goal (`id [status] title`)\n * plus the evidence lines that justified completion. Plain text only —\n * every host shape can render it, no TUI required (the same L1 contract as\n * the workflow plugin's `/workflow`).\n */\nexport function renderGoalState(state: GoalState): string {\n\tif (state.goals.length === 0) return \"no goals in this session; create one with goal_set\";\n\tconst open = state.goals.filter((goal) => goal.status === \"open\").length;\n\tconst lines = [`goals: ${state.goals.length} (${open} open)`];\n\t// Record fields only validate non-emptiness, so fold newlines to keep the\n\t// one-line-per-record shape whatever the model wrote.\n\tconst inline = (value: string): string => value.replace(/\\n/g, \" \");\n\tfor (const goal of state.goals) {\n\t\tlines.push(`- ${inline(goal.id)} [${goal.status}] ${inline(goal.title)}`);\n\t\tfor (const item of goal.evidence) lines.push(` evidence: ${inline(item)}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\n\tconst persist = async (): Promise<GoalState> => {\n\t\tapi.session?.appendEntry(stateEntry, state);\n\t\tawait api.publish({ type: \"goal.changed\", version: 1, correlationId: stateEntry, data: state });\n\t\treturn state;\n\t};\n\n\tconst tool = api.registerTool({\n\t\tname: \"goal_set\",\n\t\tlabel: \"Set goal\",\n\t\tpromptGuidelines: [TASK_TOOLS_GUIDE],\n\t\tdescription: \"Create or update a goal with explicit revision and evidence\",\n\t\tparameters: goalSchema,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst input = objectInput(first, second);\n\t\t\tconst id = text(input.id, \"id\");\n\t\t\tconst title = text(input.title, \"title\");\n\t\t\tconst nextRevision = revision(input.revision);\n\t\t\tconst current = state.goals.find((item) => item.id === id);\n\t\t\tif (current && nextRevision <= current.revision) throw new Error(`stale goal revision: ${id}`);\n\t\t\tif (current?.status === \"completed\" || current?.status === \"cancelled\") {\n\t\t\t\tthrow new Error(`goal is terminal and cannot be updated: ${id}`);\n\t\t\t}\n\t\t\tconst values = evidence(input.evidence);\n\t\t\tconst record: GoalRecord = {\n\t\t\t\tid,\n\t\t\t\ttitle,\n\t\t\t\tstatus:\n\t\t\t\t\tinput.status === undefined\n\t\t\t\t\t\t? (current?.status ?? \"open\")\n\t\t\t\t\t\t: (text(input.status, \"status\") as GoalRecord[\"status\"]),\n\t\t\t\trevision: nextRevision,\n\t\t\t\tevidence: values,\n\t\t\t};\n\t\t\tif (![\"open\", \"completed\", \"cancelled\"].includes(record.status)) throw new Error(\"invalid goal status\");\n\t\t\tif (record.status === \"completed\" && values.length === 0) {\n\t\t\t\tthrow new Error(\"goal completion requires at least one evidence reference\");\n\t\t\t}\n\t\t\tstate = { ...state, goals: [...state.goals.filter((item) => item.id !== id), record] };\n\t\t\treturn persist();\n\t\t},\n\t});\n\n\tconst command = api.registerCommand({\n\t\tname: \"goal\",\n\t\tdescription: \"Show this session's goals (status and evidence)\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tif (args.trim() !== \"\") return { status: \"error\" as const, message: \"Usage: /goal\" };\n\t\t\t\treturn { status: \"success\" as const, message: renderGoalState(state) };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\treturn [tool, command];\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-agent-forge/plugin-goal",
|
|
3
|
-
"version": "0.86.
|
|
3
|
+
"version": "0.86.5",
|
|
4
4
|
"description": "Goal capability plugin for Agent Forge (goal_set state machine over session entries)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"typebox": "1.3.27",
|
|
47
|
-
"@ai-agent-forge/plugin-sdk": "^0.86.
|
|
47
|
+
"@ai-agent-forge/plugin-sdk": "^0.86.4"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"shx": "0.4.0",
|
|
51
|
-
"vitest": "4.1.
|
|
52
|
-
"@ai-agent-forge/agent-forge": "^0.86.
|
|
51
|
+
"vitest": "4.1.11",
|
|
52
|
+
"@ai-agent-forge/agent-forge": "^0.86.4"
|
|
53
53
|
}
|
|
54
54
|
}
|