pulse-coder-engine 0.0.1-alpha.10 → 0.0.1-alpha.12
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/built-in/index.cjs +591 -131
- package/dist/built-in/index.cjs.map +1 -1
- package/dist/built-in/index.d.cts +2 -2
- package/dist/built-in/index.d.ts +2 -2
- package/dist/built-in/index.js +578 -120
- package/dist/built-in/index.js.map +1 -1
- package/dist/{index-C7fdydbL.d.cts → index-CPgs_IXY.d.cts} +143 -30
- package/dist/{index-C7fdydbL.d.ts → index-CPgs_IXY.d.ts} +143 -30
- package/dist/index.cjs +586 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -18
- package/dist/index.d.ts +32 -18
- package/dist/index.js +584 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -43,12 +43,14 @@ __export(src_exports, {
|
|
|
43
43
|
PluginManager: () => PluginManager,
|
|
44
44
|
PulseAgent: () => Engine,
|
|
45
45
|
ReadTool: () => ReadTool,
|
|
46
|
+
TaskListService: () => TaskListService,
|
|
46
47
|
TavilyTool: () => TavilyTool,
|
|
47
48
|
WriteTool: () => WriteTool,
|
|
48
49
|
builtInMCPPlugin: () => builtInMCPPlugin,
|
|
49
50
|
builtInPlanModePlugin: () => builtInPlanModePlugin,
|
|
50
51
|
builtInPlugins: () => builtInPlugins,
|
|
51
52
|
builtInSkillsPlugin: () => builtInSkillsPlugin,
|
|
53
|
+
builtInTaskTrackingPlugin: () => builtInTaskTrackingPlugin,
|
|
52
54
|
getFinalToolsMap: () => getFinalToolsMap,
|
|
53
55
|
loop: () => loop,
|
|
54
56
|
maybeCompactContext: () => maybeCompactContext,
|
|
@@ -415,15 +417,28 @@ var maybeCompactContext = async (context, options) => {
|
|
|
415
417
|
};
|
|
416
418
|
|
|
417
419
|
// src/core/loop.ts
|
|
418
|
-
function
|
|
420
|
+
function wrapToolsWithHooks(tools, beforeHooks, afterHooks) {
|
|
419
421
|
const wrapped = {};
|
|
420
422
|
for (const [name, t] of Object.entries(tools)) {
|
|
421
423
|
wrapped[name] = {
|
|
422
424
|
...t,
|
|
423
425
|
execute: async (input, ctx) => {
|
|
424
|
-
|
|
426
|
+
let finalInput = input;
|
|
427
|
+
for (const hook of beforeHooks) {
|
|
428
|
+
const result = await hook({ name, input: finalInput });
|
|
429
|
+
if (result && "input" in result) {
|
|
430
|
+
finalInput = result.input;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
425
433
|
const output = await t.execute(finalInput, ctx);
|
|
426
|
-
|
|
434
|
+
let finalOutput = output;
|
|
435
|
+
for (const hook of afterHooks) {
|
|
436
|
+
const result = await hook({ name, input: finalInput, output: finalOutput });
|
|
437
|
+
if (result && "output" in result) {
|
|
438
|
+
finalOutput = result.output;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return finalOutput;
|
|
427
442
|
}
|
|
428
443
|
};
|
|
429
444
|
}
|
|
@@ -433,6 +448,7 @@ async function loop(context, options) {
|
|
|
433
448
|
let errorCount = 0;
|
|
434
449
|
let totalSteps = 0;
|
|
435
450
|
let compactionAttempts = 0;
|
|
451
|
+
const loopHooks = options?.hooks ?? {};
|
|
436
452
|
while (true) {
|
|
437
453
|
try {
|
|
438
454
|
if (compactionAttempts < MAX_COMPACTION_ATTEMPTS) {
|
|
@@ -449,8 +465,24 @@ async function loop(context, options) {
|
|
|
449
465
|
}
|
|
450
466
|
}
|
|
451
467
|
let tools = options?.tools || {};
|
|
452
|
-
|
|
453
|
-
|
|
468
|
+
let systemPrompt = options?.systemPrompt;
|
|
469
|
+
if (loopHooks.beforeLLMCall?.length) {
|
|
470
|
+
for (const hook of loopHooks.beforeLLMCall) {
|
|
471
|
+
const result2 = await hook({ context, systemPrompt, tools });
|
|
472
|
+
if (result2) {
|
|
473
|
+
if ("systemPrompt" in result2 && result2.systemPrompt !== void 0) {
|
|
474
|
+
systemPrompt = result2.systemPrompt;
|
|
475
|
+
}
|
|
476
|
+
if ("tools" in result2 && result2.tools !== void 0) {
|
|
477
|
+
tools = result2.tools;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const beforeToolHooks = loopHooks.beforeToolCall ?? [];
|
|
483
|
+
const afterToolHooks = loopHooks.afterToolCall ?? [];
|
|
484
|
+
if (beforeToolHooks.length || afterToolHooks.length) {
|
|
485
|
+
tools = wrapToolsWithHooks(tools, beforeToolHooks, afterToolHooks);
|
|
454
486
|
}
|
|
455
487
|
const toolExecutionContext = {
|
|
456
488
|
onClarificationRequest: options?.onClarificationRequest,
|
|
@@ -461,7 +493,7 @@ async function loop(context, options) {
|
|
|
461
493
|
toolExecutionContext,
|
|
462
494
|
provider: options?.provider,
|
|
463
495
|
model: options?.model,
|
|
464
|
-
systemPrompt
|
|
496
|
+
systemPrompt,
|
|
465
497
|
onStepFinish: (step) => {
|
|
466
498
|
options?.onStepFinish?.(step);
|
|
467
499
|
},
|
|
@@ -489,6 +521,11 @@ async function loop(context, options) {
|
|
|
489
521
|
options?.onResponse?.(messages);
|
|
490
522
|
}
|
|
491
523
|
}
|
|
524
|
+
if (loopHooks.afterLLMCall?.length) {
|
|
525
|
+
for (const hook of loopHooks.afterLLMCall) {
|
|
526
|
+
await hook({ context, finishReason, text });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
492
529
|
if (finishReason === "stop") {
|
|
493
530
|
return text || "Task completed.";
|
|
494
531
|
}
|
|
@@ -712,7 +749,7 @@ var GrepTool = {
|
|
|
712
749
|
}),
|
|
713
750
|
execute: async ({
|
|
714
751
|
pattern,
|
|
715
|
-
path:
|
|
752
|
+
path: path6 = ".",
|
|
716
753
|
glob: glob2,
|
|
717
754
|
type,
|
|
718
755
|
outputMode = "files_with_matches",
|
|
@@ -747,11 +784,11 @@ var GrepTool = {
|
|
|
747
784
|
if (type) {
|
|
748
785
|
args.push("--type", type);
|
|
749
786
|
}
|
|
750
|
-
if (
|
|
751
|
-
if (!(0, import_fs5.existsSync)(
|
|
752
|
-
throw new Error(`Path does not exist: ${
|
|
787
|
+
if (path6 && path6 !== ".") {
|
|
788
|
+
if (!(0, import_fs5.existsSync)(path6)) {
|
|
789
|
+
throw new Error(`Path does not exist: ${path6}`);
|
|
753
790
|
}
|
|
754
|
-
args.push(
|
|
791
|
+
args.push(path6);
|
|
755
792
|
}
|
|
756
793
|
let command = args.map((arg) => {
|
|
757
794
|
if (arg.includes(" ") || arg.includes("$") || arg.includes("*")) {
|
|
@@ -806,8 +843,8 @@ var LsTool = {
|
|
|
806
843
|
inputSchema: import_zod5.default.object({
|
|
807
844
|
path: import_zod5.default.string().optional().describe("The path to list files from (defaults to current directory)")
|
|
808
845
|
}),
|
|
809
|
-
execute: async ({ path:
|
|
810
|
-
const files = (0, import_fs6.readdirSync)(
|
|
846
|
+
execute: async ({ path: path6 = "." }) => {
|
|
847
|
+
const files = (0, import_fs6.readdirSync)(path6);
|
|
811
848
|
return { files };
|
|
812
849
|
}
|
|
813
850
|
};
|
|
@@ -1066,9 +1103,15 @@ var PluginManager = class {
|
|
|
1066
1103
|
enginePlugins = /* @__PURE__ */ new Map();
|
|
1067
1104
|
userConfigPlugins = [];
|
|
1068
1105
|
tools = /* @__PURE__ */ new Map();
|
|
1069
|
-
|
|
1106
|
+
hooks = {
|
|
1107
|
+
beforeRun: [],
|
|
1108
|
+
afterRun: [],
|
|
1109
|
+
beforeLLMCall: [],
|
|
1110
|
+
afterLLMCall: [],
|
|
1111
|
+
beforeToolCall: [],
|
|
1112
|
+
afterToolCall: []
|
|
1113
|
+
};
|
|
1070
1114
|
services = /* @__PURE__ */ new Map();
|
|
1071
|
-
protocols = /* @__PURE__ */ new Map();
|
|
1072
1115
|
config = /* @__PURE__ */ new Map();
|
|
1073
1116
|
events = new import_events.EventEmitter();
|
|
1074
1117
|
logger;
|
|
@@ -1177,14 +1220,10 @@ var PluginManager = class {
|
|
|
1177
1220
|
},
|
|
1178
1221
|
getTool: (name) => this.tools.get(name),
|
|
1179
1222
|
getTools: () => Object.fromEntries(this.tools),
|
|
1180
|
-
|
|
1181
|
-
this.
|
|
1182
|
-
|
|
1183
|
-
getRunHook: (name) => this.runHooks.get(name),
|
|
1184
|
-
registerProtocol: (name, handler) => {
|
|
1185
|
-
this.protocols.set(name, handler);
|
|
1223
|
+
registerHook: (hookName, handler) => {
|
|
1224
|
+
this.hooks[hookName].push(handler);
|
|
1225
|
+
this.logger.debug(`Plugin "${plugin.name}" registered hook: ${hookName}`);
|
|
1186
1226
|
},
|
|
1187
|
-
getProtocol: (name) => this.protocols.get(name),
|
|
1188
1227
|
registerService: (name, service) => {
|
|
1189
1228
|
this.services.set(name, service);
|
|
1190
1229
|
},
|
|
@@ -1380,10 +1419,10 @@ var PluginManager = class {
|
|
|
1380
1419
|
return Object.fromEntries(this.tools);
|
|
1381
1420
|
}
|
|
1382
1421
|
/**
|
|
1383
|
-
*
|
|
1422
|
+
* 获取指定类型的 hook 处理函数数组
|
|
1384
1423
|
*/
|
|
1385
|
-
|
|
1386
|
-
return
|
|
1424
|
+
getHooks(hookName) {
|
|
1425
|
+
return [...this.hooks[hookName]];
|
|
1387
1426
|
}
|
|
1388
1427
|
/**
|
|
1389
1428
|
* 服务获取
|
|
@@ -1391,12 +1430,6 @@ var PluginManager = class {
|
|
|
1391
1430
|
getService(name) {
|
|
1392
1431
|
return this.services.get(name);
|
|
1393
1432
|
}
|
|
1394
|
-
/**
|
|
1395
|
-
* 协议获取
|
|
1396
|
-
*/
|
|
1397
|
-
getProtocol(name) {
|
|
1398
|
-
return this.protocols.get(name);
|
|
1399
|
-
}
|
|
1400
1433
|
/**
|
|
1401
1434
|
* 获取插件状态
|
|
1402
1435
|
*/
|
|
@@ -1405,9 +1438,10 @@ var PluginManager = class {
|
|
|
1405
1438
|
enginePlugins: Array.from(this.enginePlugins.keys()),
|
|
1406
1439
|
userConfigPlugins: this.userConfigPlugins.map((c) => c.name || "unnamed"),
|
|
1407
1440
|
tools: Array.from(this.tools.keys()),
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1441
|
+
hooks: Object.fromEntries(
|
|
1442
|
+
Object.entries(this.hooks).map(([k, v]) => [k, v.length])
|
|
1443
|
+
),
|
|
1444
|
+
services: Array.from(this.services.keys())
|
|
1411
1445
|
};
|
|
1412
1446
|
}
|
|
1413
1447
|
/**
|
|
@@ -1582,16 +1616,44 @@ var BuiltInSkillRegistry = class {
|
|
|
1582
1616
|
return Array.from(this.skills.values());
|
|
1583
1617
|
}
|
|
1584
1618
|
/**
|
|
1585
|
-
*
|
|
1619
|
+
* 注册或更新一个技能(支持插件在运行期追加技能)
|
|
1620
|
+
*/
|
|
1621
|
+
registerSkill(skill) {
|
|
1622
|
+
const name = skill.name?.trim();
|
|
1623
|
+
if (!name) {
|
|
1624
|
+
throw new Error("Skill name is required");
|
|
1625
|
+
}
|
|
1626
|
+
const existingKey = Array.from(this.skills.keys()).find((key) => key.toLowerCase() === name.toLowerCase());
|
|
1627
|
+
const replaced = existingKey !== void 0;
|
|
1628
|
+
if (existingKey && existingKey !== name) {
|
|
1629
|
+
this.skills.delete(existingKey);
|
|
1630
|
+
}
|
|
1631
|
+
this.skills.set(name, {
|
|
1632
|
+
...skill,
|
|
1633
|
+
name
|
|
1634
|
+
});
|
|
1635
|
+
return {
|
|
1636
|
+
skillName: name,
|
|
1637
|
+
replaced,
|
|
1638
|
+
total: this.skills.size
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* 根据名称获取技能(大小写不敏感)
|
|
1586
1643
|
*/
|
|
1587
1644
|
get(name) {
|
|
1588
|
-
|
|
1645
|
+
const exact = this.skills.get(name);
|
|
1646
|
+
if (exact) {
|
|
1647
|
+
return exact;
|
|
1648
|
+
}
|
|
1649
|
+
const target = name.toLowerCase();
|
|
1650
|
+
return this.getAll().find((skill) => skill.name.toLowerCase() === target);
|
|
1589
1651
|
}
|
|
1590
1652
|
/**
|
|
1591
1653
|
* 检查技能是否存在
|
|
1592
1654
|
*/
|
|
1593
1655
|
has(name) {
|
|
1594
|
-
return this.
|
|
1656
|
+
return !!this.get(name);
|
|
1595
1657
|
}
|
|
1596
1658
|
/**
|
|
1597
1659
|
* 搜索技能(模糊匹配)
|
|
@@ -1606,7 +1668,7 @@ var BuiltInSkillRegistry = class {
|
|
|
1606
1668
|
var skillToolSchema = import_zod10.z.object({
|
|
1607
1669
|
name: import_zod10.z.string().describe("The name of the skill to execute")
|
|
1608
1670
|
});
|
|
1609
|
-
function generateSkillTool(
|
|
1671
|
+
function generateSkillTool(registry) {
|
|
1610
1672
|
const getSkillsPrompt = (availableSkills) => {
|
|
1611
1673
|
return [
|
|
1612
1674
|
"If query matches an available skill's description or instruction [use skill], use the skill tool to get detailed instructions.",
|
|
@@ -1627,10 +1689,10 @@ function generateSkillTool(skills) {
|
|
|
1627
1689
|
};
|
|
1628
1690
|
return {
|
|
1629
1691
|
name: "skill",
|
|
1630
|
-
description: getSkillsPrompt(
|
|
1692
|
+
description: getSkillsPrompt(registry.getAll()),
|
|
1631
1693
|
inputSchema: skillToolSchema,
|
|
1632
1694
|
execute: async ({ name }) => {
|
|
1633
|
-
const skill =
|
|
1695
|
+
const skill = registry.get(name);
|
|
1634
1696
|
if (!skill) {
|
|
1635
1697
|
throw new Error(`Skill ${name} not found`);
|
|
1636
1698
|
}
|
|
@@ -1644,14 +1706,21 @@ var builtInSkillsPlugin = {
|
|
|
1644
1706
|
async initialize(context) {
|
|
1645
1707
|
const registry = new BuiltInSkillRegistry();
|
|
1646
1708
|
await registry.initialize(process.cwd());
|
|
1709
|
+
context.registerService("skillRegistry", registry);
|
|
1710
|
+
context.registerTool("skill", generateSkillTool(registry));
|
|
1711
|
+
context.registerHook("beforeRun", ({ tools }) => {
|
|
1712
|
+
return {
|
|
1713
|
+
tools: {
|
|
1714
|
+
...tools,
|
|
1715
|
+
skill: generateSkillTool(registry)
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
});
|
|
1647
1719
|
const skills = registry.getAll();
|
|
1648
1720
|
if (skills.length === 0) {
|
|
1649
1721
|
console.log("[Skills] No skills found");
|
|
1650
1722
|
return;
|
|
1651
1723
|
}
|
|
1652
|
-
const skillTool = generateSkillTool(skills);
|
|
1653
|
-
context.registerTool("skill", skillTool);
|
|
1654
|
-
context.registerService("skillRegistry", registry);
|
|
1655
1724
|
console.log(`[Skills] Registered ${skills.length} skill(s)`);
|
|
1656
1725
|
}
|
|
1657
1726
|
};
|
|
@@ -1762,6 +1831,26 @@ var KNOWN_TOOL_META = {
|
|
|
1762
1831
|
category: "other",
|
|
1763
1832
|
risk: "low",
|
|
1764
1833
|
description: "Ask the user a targeted clarification question."
|
|
1834
|
+
},
|
|
1835
|
+
task_create: {
|
|
1836
|
+
category: "other",
|
|
1837
|
+
risk: "low",
|
|
1838
|
+
description: "Create tracked task entries for planning and execution visibility."
|
|
1839
|
+
},
|
|
1840
|
+
task_get: {
|
|
1841
|
+
category: "other",
|
|
1842
|
+
risk: "low",
|
|
1843
|
+
description: "Inspect a task entry by ID."
|
|
1844
|
+
},
|
|
1845
|
+
task_list: {
|
|
1846
|
+
category: "other",
|
|
1847
|
+
risk: "low",
|
|
1848
|
+
description: "List task tracking entries and status summary."
|
|
1849
|
+
},
|
|
1850
|
+
task_update: {
|
|
1851
|
+
category: "other",
|
|
1852
|
+
risk: "low",
|
|
1853
|
+
description: "Update task tracking fields and status."
|
|
1765
1854
|
}
|
|
1766
1855
|
};
|
|
1767
1856
|
var BuiltInPlanModeService = class {
|
|
@@ -1884,25 +1973,6 @@ var BuiltInPlanModeService = class {
|
|
|
1884
1973
|
}
|
|
1885
1974
|
return lines.join("\n");
|
|
1886
1975
|
}
|
|
1887
|
-
applyHooks(baseHooks) {
|
|
1888
|
-
return {
|
|
1889
|
-
onBeforeToolCall: async (name, input) => {
|
|
1890
|
-
this.observePotentialPolicyViolation(name, input);
|
|
1891
|
-
if (baseHooks?.onBeforeToolCall) {
|
|
1892
|
-
const nextInput = await baseHooks.onBeforeToolCall(name, input);
|
|
1893
|
-
return nextInput ?? input;
|
|
1894
|
-
}
|
|
1895
|
-
return input;
|
|
1896
|
-
},
|
|
1897
|
-
onAfterToolCall: async (name, input, output) => {
|
|
1898
|
-
if (baseHooks?.onAfterToolCall) {
|
|
1899
|
-
const nextOutput = await baseHooks.onAfterToolCall(name, input, output);
|
|
1900
|
-
return nextOutput ?? output;
|
|
1901
|
-
}
|
|
1902
|
-
return output;
|
|
1903
|
-
}
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
1976
|
getEvents(limit = 50) {
|
|
1907
1977
|
return this.events.slice(-Math.max(0, limit));
|
|
1908
1978
|
}
|
|
@@ -2039,21 +2109,18 @@ var builtInPlanModePlugin = {
|
|
|
2039
2109
|
version: "1.0.0",
|
|
2040
2110
|
async initialize(context) {
|
|
2041
2111
|
const service = new BuiltInPlanModeService(context.logger, context.events, "executing");
|
|
2042
|
-
context.
|
|
2112
|
+
context.registerHook("beforeLLMCall", ({ context: runContext, tools, systemPrompt }) => {
|
|
2043
2113
|
const mode = service.getMode();
|
|
2044
2114
|
if (mode === "executing") {
|
|
2045
|
-
return
|
|
2046
|
-
systemPrompt,
|
|
2047
|
-
hooks
|
|
2048
|
-
};
|
|
2115
|
+
return;
|
|
2049
2116
|
}
|
|
2050
2117
|
const transition = service.processContextMessages(runContext.messages);
|
|
2051
2118
|
const append = service.buildPromptAppend(Object.keys(tools), transition);
|
|
2052
2119
|
const finalSystemPrompt = appendSystemPrompt(systemPrompt, append);
|
|
2053
|
-
return {
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2120
|
+
return { systemPrompt: finalSystemPrompt };
|
|
2121
|
+
});
|
|
2122
|
+
context.registerHook("beforeToolCall", ({ name, input }) => {
|
|
2123
|
+
service.observePotentialPolicyViolation(name, input);
|
|
2057
2124
|
});
|
|
2058
2125
|
context.registerService("planMode", service);
|
|
2059
2126
|
context.registerService("planModeService", service);
|
|
@@ -2063,17 +2130,404 @@ var builtInPlanModePlugin = {
|
|
|
2063
2130
|
}
|
|
2064
2131
|
};
|
|
2065
2132
|
|
|
2066
|
-
// src/built-in/
|
|
2067
|
-
var import_zod11 = require("zod");
|
|
2133
|
+
// src/built-in/task-tracking-plugin/index.ts
|
|
2068
2134
|
var import_fs10 = require("fs");
|
|
2069
2135
|
var import_path4 = __toESM(require("path"), 1);
|
|
2136
|
+
var import_os2 = require("os");
|
|
2137
|
+
var import_crypto2 = require("crypto");
|
|
2138
|
+
var import_zod11 = require("zod");
|
|
2139
|
+
var TASK_STATUSES = ["pending", "in_progress", "completed", "blocked"];
|
|
2140
|
+
var CREATE_TASK_ITEM_SCHEMA = import_zod11.z.object({
|
|
2141
|
+
title: import_zod11.z.string().min(1).describe("Task title"),
|
|
2142
|
+
details: import_zod11.z.string().optional().describe("Task details / acceptance notes"),
|
|
2143
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("Initial status; defaults to pending"),
|
|
2144
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Task IDs that must be completed first"),
|
|
2145
|
+
blockedReason: import_zod11.z.string().optional().describe("Reason when status is blocked"),
|
|
2146
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Additional machine-readable metadata")
|
|
2147
|
+
});
|
|
2148
|
+
var TASK_CREATE_INPUT_SCHEMA = import_zod11.z.object({
|
|
2149
|
+
title: import_zod11.z.string().min(1).optional().describe("Task title (single-task mode)"),
|
|
2150
|
+
details: import_zod11.z.string().optional().describe("Task details (single-task mode)"),
|
|
2151
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("Initial status (single-task mode)"),
|
|
2152
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Dependencies (single-task mode)"),
|
|
2153
|
+
blockedReason: import_zod11.z.string().optional().describe("Blocked reason (single-task mode)"),
|
|
2154
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Metadata (single-task mode)"),
|
|
2155
|
+
tasks: import_zod11.z.array(CREATE_TASK_ITEM_SCHEMA).min(1).optional().describe("Batch create mode")
|
|
2156
|
+
}).refine((value) => !!value.tasks?.length || !!value.title, {
|
|
2157
|
+
message: "Either provide `tasks` or `title` for single-task mode."
|
|
2158
|
+
});
|
|
2159
|
+
var TASK_GET_INPUT_SCHEMA = import_zod11.z.object({
|
|
2160
|
+
id: import_zod11.z.string().min(1).describe("Task ID to retrieve")
|
|
2161
|
+
});
|
|
2162
|
+
var TASK_LIST_INPUT_SCHEMA = import_zod11.z.object({
|
|
2163
|
+
statuses: import_zod11.z.array(import_zod11.z.enum(TASK_STATUSES)).optional().describe("Filter by statuses"),
|
|
2164
|
+
includeCompleted: import_zod11.z.boolean().optional().describe("Set false to hide completed tasks"),
|
|
2165
|
+
limit: import_zod11.z.number().int().positive().max(500).optional().describe("Maximum tasks to return")
|
|
2166
|
+
});
|
|
2167
|
+
var TASK_TRACKING_SKILL = {
|
|
2168
|
+
name: "task-tracking-workflow",
|
|
2169
|
+
description: "Track complex execution with task tools and proceed directly without confirmation unless the user explicitly asks for confirmation.",
|
|
2170
|
+
location: "pulse-coder-engine/built-in/task-tracking-plugin",
|
|
2171
|
+
content: `# Task Tracking Workflow
|
|
2172
|
+
|
|
2173
|
+
## When to use
|
|
2174
|
+
- The request has multiple phases or deliverables.
|
|
2175
|
+
- Work may span multiple tool calls or sessions.
|
|
2176
|
+
- You need explicit progress visibility and blocker management.
|
|
2177
|
+
|
|
2178
|
+
## Execution autonomy
|
|
2179
|
+
- Default behavior: execute directly and call tools without asking for confirmation.
|
|
2180
|
+
- Only ask for confirmation when the user explicitly requires confirmation.
|
|
2181
|
+
- If critical information is missing, ask only the minimum clarifying question needed to continue.
|
|
2182
|
+
|
|
2183
|
+
## Required flow
|
|
2184
|
+
1. Start by reviewing existing tasks with \`task_list\`.
|
|
2185
|
+
2. If no suitable tasks exist, create focused tasks with \`task_create\` (prefer batch mode).
|
|
2186
|
+
3. Keep exactly one primary task in \`in_progress\` where possible.
|
|
2187
|
+
4. Update status with \`task_update\` after meaningful progress.
|
|
2188
|
+
5. Mark blockers with \`status=blocked\` and a concrete \`blockedReason\`.
|
|
2189
|
+
6. Mark done tasks as \`completed\` and move to the next actionable item.
|
|
2190
|
+
|
|
2191
|
+
## Quality rules
|
|
2192
|
+
- Keep tasks atomic and verifiable.
|
|
2193
|
+
- Avoid single oversized umbrella tasks.
|
|
2194
|
+
- Reuse existing in-progress tasks instead of duplicating.
|
|
2195
|
+
- Keep dependency links explicit when order matters.`
|
|
2196
|
+
};
|
|
2197
|
+
var TASK_UPDATE_INPUT_SCHEMA = import_zod11.z.object({
|
|
2198
|
+
id: import_zod11.z.string().min(1).describe("Task ID to update"),
|
|
2199
|
+
title: import_zod11.z.string().min(1).optional().describe("New title"),
|
|
2200
|
+
details: import_zod11.z.string().optional().describe("New details"),
|
|
2201
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("New status"),
|
|
2202
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Replace dependencies with this list"),
|
|
2203
|
+
blockedReason: import_zod11.z.string().optional().describe("Blocked reason, used with status=blocked"),
|
|
2204
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Replace metadata object"),
|
|
2205
|
+
delete: import_zod11.z.boolean().optional().describe("Delete this task")
|
|
2206
|
+
});
|
|
2207
|
+
function normalizeTaskListId(raw) {
|
|
2208
|
+
const trimmed = raw.trim();
|
|
2209
|
+
if (!trimmed) {
|
|
2210
|
+
return "default";
|
|
2211
|
+
}
|
|
2212
|
+
return trimmed.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 120) || "default";
|
|
2213
|
+
}
|
|
2214
|
+
function now() {
|
|
2215
|
+
return Date.now();
|
|
2216
|
+
}
|
|
2217
|
+
function dedupeStrings(values) {
|
|
2218
|
+
if (!values?.length) {
|
|
2219
|
+
return [];
|
|
2220
|
+
}
|
|
2221
|
+
return Array.from(new Set(values.map((value) => String(value).trim()).filter(Boolean)));
|
|
2222
|
+
}
|
|
2223
|
+
var TaskListService = class _TaskListService {
|
|
2224
|
+
taskListId;
|
|
2225
|
+
storagePath;
|
|
2226
|
+
storageDir;
|
|
2227
|
+
initialized = false;
|
|
2228
|
+
createdAt = now();
|
|
2229
|
+
updatedAt = now();
|
|
2230
|
+
tasks = /* @__PURE__ */ new Map();
|
|
2231
|
+
constructor(taskListId = _TaskListService.resolveTaskListId(), storageDir = _TaskListService.resolveStorageDir()) {
|
|
2232
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2233
|
+
this.storageDir = storageDir;
|
|
2234
|
+
this.taskListId = normalized;
|
|
2235
|
+
this.storagePath = import_path4.default.join(this.storageDir, `${normalized}.json`);
|
|
2236
|
+
}
|
|
2237
|
+
static resolveTaskListId() {
|
|
2238
|
+
return process.env.PULSE_CODER_TASK_LIST_ID || process.env.CLAUDE_CODE_TASK_LIST_ID || "default";
|
|
2239
|
+
}
|
|
2240
|
+
static resolveStorageDir() {
|
|
2241
|
+
return process.env.PULSE_CODER_TASKS_DIR || import_path4.default.join((0, import_os2.homedir)(), ".pulse-coder", "tasks");
|
|
2242
|
+
}
|
|
2243
|
+
async initialize() {
|
|
2244
|
+
if (this.initialized) {
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
await this.loadTaskList(this.taskListId);
|
|
2248
|
+
}
|
|
2249
|
+
async setTaskListId(taskListId) {
|
|
2250
|
+
await this.initialize();
|
|
2251
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2252
|
+
if (normalized === this.taskListId) {
|
|
2253
|
+
return {
|
|
2254
|
+
switched: false,
|
|
2255
|
+
taskListId: this.taskListId,
|
|
2256
|
+
storagePath: this.storagePath
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
await this.loadTaskList(normalized);
|
|
2260
|
+
return {
|
|
2261
|
+
switched: true,
|
|
2262
|
+
taskListId: this.taskListId,
|
|
2263
|
+
storagePath: this.storagePath
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
async createTask(input) {
|
|
2267
|
+
await this.initialize();
|
|
2268
|
+
const timestamp = now();
|
|
2269
|
+
const status = input.status ?? "pending";
|
|
2270
|
+
const task = {
|
|
2271
|
+
id: (0, import_crypto2.randomUUID)(),
|
|
2272
|
+
title: input.title.trim(),
|
|
2273
|
+
details: input.details,
|
|
2274
|
+
status,
|
|
2275
|
+
dependencies: dedupeStrings(input.dependencies),
|
|
2276
|
+
blockedReason: status === "blocked" ? input.blockedReason ?? "Blocked without reason" : void 0,
|
|
2277
|
+
metadata: input.metadata,
|
|
2278
|
+
createdAt: timestamp,
|
|
2279
|
+
updatedAt: timestamp,
|
|
2280
|
+
completedAt: status === "completed" ? timestamp : void 0
|
|
2281
|
+
};
|
|
2282
|
+
this.tasks.set(task.id, task);
|
|
2283
|
+
this.updatedAt = timestamp;
|
|
2284
|
+
await this.persist();
|
|
2285
|
+
return task;
|
|
2286
|
+
}
|
|
2287
|
+
async createTasks(inputs) {
|
|
2288
|
+
const created = [];
|
|
2289
|
+
for (const input of inputs) {
|
|
2290
|
+
created.push(await this.createTask(input));
|
|
2291
|
+
}
|
|
2292
|
+
return created;
|
|
2293
|
+
}
|
|
2294
|
+
async listTasks(options) {
|
|
2295
|
+
await this.initialize();
|
|
2296
|
+
const statuses = options?.statuses?.length ? new Set(options.statuses) : null;
|
|
2297
|
+
const includeCompleted = options?.includeCompleted ?? true;
|
|
2298
|
+
let tasks = Array.from(this.tasks.values()).filter((task) => {
|
|
2299
|
+
if (!includeCompleted && task.status === "completed") {
|
|
2300
|
+
return false;
|
|
2301
|
+
}
|
|
2302
|
+
if (statuses && !statuses.has(task.status)) {
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2305
|
+
return true;
|
|
2306
|
+
});
|
|
2307
|
+
tasks = tasks.sort((a, b) => a.createdAt - b.createdAt);
|
|
2308
|
+
if (options?.limit && options.limit > 0) {
|
|
2309
|
+
tasks = tasks.slice(0, options.limit);
|
|
2310
|
+
}
|
|
2311
|
+
return tasks;
|
|
2312
|
+
}
|
|
2313
|
+
async getTask(id) {
|
|
2314
|
+
await this.initialize();
|
|
2315
|
+
return this.tasks.get(id) ?? null;
|
|
2316
|
+
}
|
|
2317
|
+
async updateTask(input) {
|
|
2318
|
+
await this.initialize();
|
|
2319
|
+
if (input.delete) {
|
|
2320
|
+
const deleted = this.tasks.delete(input.id);
|
|
2321
|
+
if (!deleted) {
|
|
2322
|
+
return null;
|
|
2323
|
+
}
|
|
2324
|
+
this.updatedAt = now();
|
|
2325
|
+
await this.persist();
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
const existing = this.tasks.get(input.id);
|
|
2329
|
+
if (!existing) {
|
|
2330
|
+
return null;
|
|
2331
|
+
}
|
|
2332
|
+
const timestamp = now();
|
|
2333
|
+
const nextStatus = input.status ?? existing.status;
|
|
2334
|
+
const next = {
|
|
2335
|
+
...existing,
|
|
2336
|
+
title: input.title !== void 0 ? input.title : existing.title,
|
|
2337
|
+
details: input.details !== void 0 ? input.details : existing.details,
|
|
2338
|
+
status: nextStatus,
|
|
2339
|
+
dependencies: input.dependencies !== void 0 ? dedupeStrings(input.dependencies) : existing.dependencies,
|
|
2340
|
+
metadata: input.metadata !== void 0 ? input.metadata : existing.metadata,
|
|
2341
|
+
blockedReason: this.resolveBlockedReason(nextStatus, input.blockedReason, existing.blockedReason),
|
|
2342
|
+
updatedAt: timestamp,
|
|
2343
|
+
completedAt: nextStatus === "completed" ? existing.completedAt ?? timestamp : void 0
|
|
2344
|
+
};
|
|
2345
|
+
this.tasks.set(input.id, next);
|
|
2346
|
+
this.updatedAt = timestamp;
|
|
2347
|
+
await this.persist();
|
|
2348
|
+
return next;
|
|
2349
|
+
}
|
|
2350
|
+
async snapshot(options) {
|
|
2351
|
+
const tasks = await this.listTasks(options);
|
|
2352
|
+
return {
|
|
2353
|
+
taskListId: this.taskListId,
|
|
2354
|
+
storagePath: this.storagePath,
|
|
2355
|
+
createdAt: this.createdAt,
|
|
2356
|
+
updatedAt: this.updatedAt,
|
|
2357
|
+
total: tasks.length,
|
|
2358
|
+
tasks
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
resolveBlockedReason(status, blockedReasonInput, previous) {
|
|
2362
|
+
if (status !== "blocked") {
|
|
2363
|
+
return void 0;
|
|
2364
|
+
}
|
|
2365
|
+
if (blockedReasonInput !== void 0) {
|
|
2366
|
+
return blockedReasonInput || "Blocked without reason";
|
|
2367
|
+
}
|
|
2368
|
+
return previous ?? "Blocked without reason";
|
|
2369
|
+
}
|
|
2370
|
+
async loadTaskList(taskListId) {
|
|
2371
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2372
|
+
this.taskListId = normalized;
|
|
2373
|
+
this.storagePath = import_path4.default.join(this.storageDir, `${normalized}.json`);
|
|
2374
|
+
await import_fs10.promises.mkdir(this.storageDir, { recursive: true });
|
|
2375
|
+
try {
|
|
2376
|
+
const raw = await import_fs10.promises.readFile(this.storagePath, "utf-8");
|
|
2377
|
+
const parsed = JSON.parse(raw);
|
|
2378
|
+
const parsedTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
2379
|
+
this.tasks = new Map(parsedTasks.filter((task) => !!task?.id).map((task) => [task.id, task]));
|
|
2380
|
+
this.createdAt = typeof parsed.createdAt === "number" ? parsed.createdAt : now();
|
|
2381
|
+
this.updatedAt = typeof parsed.updatedAt === "number" ? parsed.updatedAt : now();
|
|
2382
|
+
} catch (error) {
|
|
2383
|
+
if (error?.code !== "ENOENT") {
|
|
2384
|
+
throw error;
|
|
2385
|
+
}
|
|
2386
|
+
const timestamp = now();
|
|
2387
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
2388
|
+
this.createdAt = timestamp;
|
|
2389
|
+
this.updatedAt = timestamp;
|
|
2390
|
+
await this.persist();
|
|
2391
|
+
}
|
|
2392
|
+
this.initialized = true;
|
|
2393
|
+
}
|
|
2394
|
+
async persist() {
|
|
2395
|
+
const payload = {
|
|
2396
|
+
taskListId: this.taskListId,
|
|
2397
|
+
createdAt: this.createdAt,
|
|
2398
|
+
updatedAt: this.updatedAt,
|
|
2399
|
+
tasks: Array.from(this.tasks.values()).sort((a, b) => a.createdAt - b.createdAt)
|
|
2400
|
+
};
|
|
2401
|
+
await import_fs10.promises.writeFile(this.storagePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
function buildTaskCreateTool(service) {
|
|
2405
|
+
return {
|
|
2406
|
+
name: "task_create",
|
|
2407
|
+
description: "Create one or more tracked tasks for complex work. Use this when work has multiple steps, dependencies, or blockers.",
|
|
2408
|
+
inputSchema: TASK_CREATE_INPUT_SCHEMA,
|
|
2409
|
+
execute: async ({ tasks, title, details, status, dependencies, blockedReason, metadata }) => {
|
|
2410
|
+
const items = tasks?.length ? tasks : [{ title, details, status, dependencies, blockedReason, metadata }];
|
|
2411
|
+
const created = await service.createTasks(items);
|
|
2412
|
+
return {
|
|
2413
|
+
taskListId: service.taskListId,
|
|
2414
|
+
storagePath: service.storagePath,
|
|
2415
|
+
createdCount: created.length,
|
|
2416
|
+
tasks: created
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
function buildTaskGetTool(service) {
|
|
2422
|
+
return {
|
|
2423
|
+
name: "task_get",
|
|
2424
|
+
description: "Get full details for a single task by task ID.",
|
|
2425
|
+
inputSchema: TASK_GET_INPUT_SCHEMA,
|
|
2426
|
+
execute: async ({ id }) => {
|
|
2427
|
+
const task = await service.getTask(id);
|
|
2428
|
+
if (!task) {
|
|
2429
|
+
throw new Error(`Task not found: ${id}`);
|
|
2430
|
+
}
|
|
2431
|
+
return {
|
|
2432
|
+
taskListId: service.taskListId,
|
|
2433
|
+
storagePath: service.storagePath,
|
|
2434
|
+
task
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
function buildTaskListTool(service) {
|
|
2440
|
+
return {
|
|
2441
|
+
name: "task_list",
|
|
2442
|
+
description: "List tasks and their current status. Use this to check progress before and after major changes.",
|
|
2443
|
+
inputSchema: TASK_LIST_INPUT_SCHEMA,
|
|
2444
|
+
execute: async ({ statuses, includeCompleted, limit }) => {
|
|
2445
|
+
const snapshot = await service.snapshot({ statuses, includeCompleted, limit });
|
|
2446
|
+
const completed = snapshot.tasks.filter((task) => task.status === "completed").length;
|
|
2447
|
+
const inProgress = snapshot.tasks.filter((task) => task.status === "in_progress").length;
|
|
2448
|
+
const pending = snapshot.tasks.filter((task) => task.status === "pending").length;
|
|
2449
|
+
const blocked = snapshot.tasks.filter((task) => task.status === "blocked").length;
|
|
2450
|
+
return {
|
|
2451
|
+
...snapshot,
|
|
2452
|
+
summary: {
|
|
2453
|
+
total: snapshot.total,
|
|
2454
|
+
completed,
|
|
2455
|
+
inProgress,
|
|
2456
|
+
pending,
|
|
2457
|
+
blocked
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
function buildTaskUpdateTool(service) {
|
|
2464
|
+
return {
|
|
2465
|
+
name: "task_update",
|
|
2466
|
+
description: "Update task fields, move status (pending/in_progress/completed/blocked), or delete tasks.",
|
|
2467
|
+
inputSchema: TASK_UPDATE_INPUT_SCHEMA,
|
|
2468
|
+
execute: async (input) => {
|
|
2469
|
+
const task = await service.updateTask(input);
|
|
2470
|
+
if (input.delete) {
|
|
2471
|
+
return {
|
|
2472
|
+
taskListId: service.taskListId,
|
|
2473
|
+
storagePath: service.storagePath,
|
|
2474
|
+
deleted: true,
|
|
2475
|
+
id: input.id
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
if (!task) {
|
|
2479
|
+
throw new Error(`Task not found: ${input.id}`);
|
|
2480
|
+
}
|
|
2481
|
+
return {
|
|
2482
|
+
taskListId: service.taskListId,
|
|
2483
|
+
storagePath: service.storagePath,
|
|
2484
|
+
deleted: false,
|
|
2485
|
+
task
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
var builtInTaskTrackingPlugin = {
|
|
2491
|
+
name: "pulse-coder-engine/built-in-task-tracking",
|
|
2492
|
+
version: "1.0.0",
|
|
2493
|
+
dependencies: ["pulse-coder-engine/built-in-skills"],
|
|
2494
|
+
async initialize(context) {
|
|
2495
|
+
const service = new TaskListService();
|
|
2496
|
+
await service.initialize();
|
|
2497
|
+
context.registerService("taskListService", service);
|
|
2498
|
+
context.registerService("taskTracking", service);
|
|
2499
|
+
const skillRegistry = context.getService("skillRegistry");
|
|
2500
|
+
if (skillRegistry) {
|
|
2501
|
+
const registration = skillRegistry.registerSkill(TASK_TRACKING_SKILL);
|
|
2502
|
+
context.logger.info("[TaskTracking] Registered built-in task tracking skill", registration);
|
|
2503
|
+
} else {
|
|
2504
|
+
context.logger.warn("[TaskTracking] skillRegistry service unavailable; skipped task tracking skill registration");
|
|
2505
|
+
}
|
|
2506
|
+
context.registerTools({
|
|
2507
|
+
task_create: buildTaskCreateTool(service),
|
|
2508
|
+
task_get: buildTaskGetTool(service),
|
|
2509
|
+
task_list: buildTaskListTool(service),
|
|
2510
|
+
task_update: buildTaskUpdateTool(service)
|
|
2511
|
+
});
|
|
2512
|
+
context.logger.info("[TaskTracking] Registered task tools", {
|
|
2513
|
+
taskListId: service.taskListId,
|
|
2514
|
+
storagePath: service.storagePath,
|
|
2515
|
+
skillRegistryAvailable: !!skillRegistry
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2520
|
+
// src/built-in/sub-agent-plugin/index.ts
|
|
2521
|
+
var import_zod12 = require("zod");
|
|
2522
|
+
var import_fs11 = require("fs");
|
|
2523
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
2070
2524
|
var ConfigLoader = class {
|
|
2071
2525
|
async getAgentFilesInfo(configDirs) {
|
|
2072
2526
|
const fileInfos = [];
|
|
2073
2527
|
for (let configDir of configDirs) {
|
|
2074
2528
|
try {
|
|
2075
|
-
await
|
|
2076
|
-
const files = await
|
|
2529
|
+
await import_fs11.promises.access(configDir);
|
|
2530
|
+
const files = await import_fs11.promises.readdir(configDir);
|
|
2077
2531
|
fileInfos.push({ files, configDir });
|
|
2078
2532
|
} catch {
|
|
2079
2533
|
continue;
|
|
@@ -2090,7 +2544,7 @@ var ConfigLoader = class {
|
|
|
2090
2544
|
const files = fileInfo.files;
|
|
2091
2545
|
for (const file of files) {
|
|
2092
2546
|
if (file.endsWith(".md")) {
|
|
2093
|
-
const config = await this.parseConfig(
|
|
2547
|
+
const config = await this.parseConfig(import_path5.default.join(fileInfo.configDir, file));
|
|
2094
2548
|
if (config) configs.push(config);
|
|
2095
2549
|
}
|
|
2096
2550
|
}
|
|
@@ -2102,7 +2556,7 @@ var ConfigLoader = class {
|
|
|
2102
2556
|
}
|
|
2103
2557
|
async parseConfig(filePath) {
|
|
2104
2558
|
try {
|
|
2105
|
-
const content = await
|
|
2559
|
+
const content = await import_fs11.promises.readFile(filePath, "utf-8");
|
|
2106
2560
|
const lines = content.split("\n");
|
|
2107
2561
|
let name = "";
|
|
2108
2562
|
let description = "";
|
|
@@ -2131,7 +2585,7 @@ var ConfigLoader = class {
|
|
|
2131
2585
|
}
|
|
2132
2586
|
}
|
|
2133
2587
|
if (!name) {
|
|
2134
|
-
name =
|
|
2588
|
+
name = import_path5.default.basename(filePath, ".md");
|
|
2135
2589
|
}
|
|
2136
2590
|
return {
|
|
2137
2591
|
name: name.trim(),
|
|
@@ -2182,9 +2636,9 @@ var SubAgentPlugin = class {
|
|
|
2182
2636
|
const toolName = `${config.name}_agent`;
|
|
2183
2637
|
const tool2 = {
|
|
2184
2638
|
description: config.description,
|
|
2185
|
-
inputSchema:
|
|
2186
|
-
task:
|
|
2187
|
-
context:
|
|
2639
|
+
inputSchema: import_zod12.z.object({
|
|
2640
|
+
task: import_zod12.z.string().describe("\u8981\u6267\u884C\u7684\u4EFB\u52A1\u63CF\u8FF0"),
|
|
2641
|
+
context: import_zod12.z.any().optional().describe("\u4EFB\u52A1\u4E0A\u4E0B\u6587\u4FE1\u606F")
|
|
2188
2642
|
}),
|
|
2189
2643
|
execute: async ({ task, context: taskContext }) => {
|
|
2190
2644
|
const tools = { ...BuiltinToolsMap, ...context.getTools() };
|
|
@@ -2211,6 +2665,7 @@ var builtInPlugins = [
|
|
|
2211
2665
|
builtInMCPPlugin,
|
|
2212
2666
|
builtInSkillsPlugin,
|
|
2213
2667
|
builtInPlanModePlugin,
|
|
2668
|
+
builtInTaskTrackingPlugin,
|
|
2214
2669
|
new SubAgentPlugin()
|
|
2215
2670
|
];
|
|
2216
2671
|
|
|
@@ -2264,52 +2719,70 @@ var Engine = class {
|
|
|
2264
2719
|
// 默认启用扫描
|
|
2265
2720
|
};
|
|
2266
2721
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2722
|
+
/**
|
|
2723
|
+
* Collect all hooks for a given loop invocation.
|
|
2724
|
+
* Merges plugin hooks with the legacy EngineOptions.hooks (ToolHooks).
|
|
2725
|
+
*/
|
|
2726
|
+
collectLoopHooks() {
|
|
2727
|
+
const loopHooks = {
|
|
2728
|
+
beforeLLMCall: this.pluginManager.getHooks("beforeLLMCall"),
|
|
2729
|
+
afterLLMCall: this.pluginManager.getHooks("afterLLMCall"),
|
|
2730
|
+
beforeToolCall: [...this.pluginManager.getHooks("beforeToolCall")],
|
|
2731
|
+
afterToolCall: [...this.pluginManager.getHooks("afterToolCall")]
|
|
2732
|
+
};
|
|
2733
|
+
const legacyHooks = this.options.hooks;
|
|
2734
|
+
if (legacyHooks?.onBeforeToolCall) {
|
|
2735
|
+
const legacyBefore = legacyHooks.onBeforeToolCall;
|
|
2736
|
+
loopHooks.beforeToolCall.push(async ({ name, input }) => {
|
|
2737
|
+
const modified = await legacyBefore(name, input);
|
|
2738
|
+
return modified !== void 0 ? { input: modified } : void 0;
|
|
2277
2739
|
});
|
|
2278
|
-
if (!result) {
|
|
2279
|
-
continue;
|
|
2280
|
-
}
|
|
2281
|
-
if ("systemPrompt" in result) {
|
|
2282
|
-
nextSystemPrompt = result.systemPrompt;
|
|
2283
|
-
}
|
|
2284
|
-
if ("hooks" in result) {
|
|
2285
|
-
nextHooks = result.hooks;
|
|
2286
|
-
}
|
|
2287
2740
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2741
|
+
if (legacyHooks?.onAfterToolCall) {
|
|
2742
|
+
const legacyAfter = legacyHooks.onAfterToolCall;
|
|
2743
|
+
loopHooks.afterToolCall.push(async ({ name, input, output }) => {
|
|
2744
|
+
const modified = await legacyAfter(name, input, output);
|
|
2745
|
+
return modified !== void 0 ? { output: modified } : void 0;
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
return loopHooks;
|
|
2292
2749
|
}
|
|
2293
2750
|
/**
|
|
2294
2751
|
* 运行AI循环
|
|
2295
2752
|
*/
|
|
2296
2753
|
async run(context, options) {
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
const
|
|
2300
|
-
|
|
2754
|
+
let systemPrompt = options?.systemPrompt ?? this.options.systemPrompt;
|
|
2755
|
+
let tools = { ...this.tools };
|
|
2756
|
+
const beforeRunHooks = this.pluginManager.getHooks("beforeRun");
|
|
2757
|
+
for (const hook of beforeRunHooks) {
|
|
2758
|
+
const result = await hook({ context, systemPrompt, tools });
|
|
2759
|
+
if (result) {
|
|
2760
|
+
if ("systemPrompt" in result && result.systemPrompt !== void 0) {
|
|
2761
|
+
systemPrompt = result.systemPrompt;
|
|
2762
|
+
}
|
|
2763
|
+
if ("tools" in result && result.tools !== void 0) {
|
|
2764
|
+
tools = result.tools;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
const loopHooks = this.collectLoopHooks();
|
|
2769
|
+
const resultText = await loop(context, {
|
|
2301
2770
|
...options,
|
|
2302
|
-
tools
|
|
2303
|
-
// Engine 级别选项作为默认值;调用方通过 options 传入可在单次调用中覆盖
|
|
2771
|
+
tools,
|
|
2304
2772
|
provider: options?.provider ?? this.options.llmProvider,
|
|
2305
2773
|
model: options?.model ?? this.options.model,
|
|
2306
2774
|
systemPrompt,
|
|
2307
|
-
hooks,
|
|
2775
|
+
hooks: loopHooks,
|
|
2308
2776
|
onToolCall: (toolCall) => {
|
|
2309
2777
|
options?.onToolCall?.(toolCall);
|
|
2310
2778
|
},
|
|
2311
2779
|
onClarificationRequest: options?.onClarificationRequest
|
|
2312
2780
|
});
|
|
2781
|
+
const afterRunHooks = this.pluginManager.getHooks("afterRun");
|
|
2782
|
+
for (const hook of afterRunHooks) {
|
|
2783
|
+
await hook({ context, result: resultText });
|
|
2784
|
+
}
|
|
2785
|
+
return resultText;
|
|
2313
2786
|
}
|
|
2314
2787
|
/**
|
|
2315
2788
|
* 获取插件状态
|
|
@@ -2380,12 +2853,14 @@ var Engine = class {
|
|
|
2380
2853
|
PluginManager,
|
|
2381
2854
|
PulseAgent,
|
|
2382
2855
|
ReadTool,
|
|
2856
|
+
TaskListService,
|
|
2383
2857
|
TavilyTool,
|
|
2384
2858
|
WriteTool,
|
|
2385
2859
|
builtInMCPPlugin,
|
|
2386
2860
|
builtInPlanModePlugin,
|
|
2387
2861
|
builtInPlugins,
|
|
2388
2862
|
builtInSkillsPlugin,
|
|
2863
|
+
builtInTaskTrackingPlugin,
|
|
2389
2864
|
getFinalToolsMap,
|
|
2390
2865
|
loop,
|
|
2391
2866
|
maybeCompactContext,
|