@aigne/core 1.71.0 → 1.72.0-beta.10

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.
Files changed (192) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/lib/cjs/agents/agent.d.ts +42 -29
  3. package/lib/cjs/agents/agent.js +32 -11
  4. package/lib/cjs/agents/ai-agent.d.ts +63 -4
  5. package/lib/cjs/agents/ai-agent.js +148 -20
  6. package/lib/cjs/agents/chat-model.d.ts +162 -0
  7. package/lib/cjs/agents/chat-model.js +56 -5
  8. package/lib/cjs/agents/image-agent.d.ts +17 -1
  9. package/lib/cjs/agents/image-agent.js +16 -0
  10. package/lib/cjs/agents/image-model.d.ts +17 -2
  11. package/lib/cjs/agents/image-model.js +2 -0
  12. package/lib/cjs/agents/mcp-agent.d.ts +17 -0
  13. package/lib/cjs/agents/mcp-agent.js +18 -0
  14. package/lib/cjs/agents/team-agent.d.ts +55 -0
  15. package/lib/cjs/agents/team-agent.js +31 -0
  16. package/lib/cjs/agents/transform-agent.d.ts +12 -0
  17. package/lib/cjs/agents/transform-agent.js +13 -0
  18. package/lib/cjs/agents/video-model.d.ts +15 -0
  19. package/lib/cjs/agents/video-model.js +2 -0
  20. package/lib/cjs/aigne/usage.d.ts +5 -0
  21. package/lib/cjs/aigne/usage.js +6 -0
  22. package/lib/cjs/index.d.ts +1 -0
  23. package/lib/cjs/index.js +1 -0
  24. package/lib/cjs/loader/agent-yaml.d.ts +27 -64
  25. package/lib/cjs/loader/agent-yaml.js +22 -129
  26. package/lib/cjs/loader/agents.d.ts +4 -0
  27. package/lib/cjs/loader/agents.js +17 -0
  28. package/lib/cjs/loader/index.d.ts +16 -12
  29. package/lib/cjs/loader/index.js +46 -82
  30. package/lib/cjs/loader/schema.d.ts +21 -6
  31. package/lib/cjs/loader/schema.js +60 -1
  32. package/lib/cjs/memory/recorder.d.ts +4 -4
  33. package/lib/cjs/memory/retriever.d.ts +4 -4
  34. package/lib/cjs/prompt/agent-session.d.ts +135 -0
  35. package/lib/cjs/prompt/agent-session.js +889 -0
  36. package/lib/cjs/prompt/compact/compactor.d.ts +7 -0
  37. package/lib/cjs/prompt/compact/compactor.js +48 -0
  38. package/lib/cjs/prompt/compact/session-memory-extractor.d.ts +7 -0
  39. package/lib/cjs/prompt/compact/session-memory-extractor.js +139 -0
  40. package/lib/cjs/prompt/compact/types.d.ts +329 -0
  41. package/lib/cjs/prompt/compact/types.js +53 -0
  42. package/lib/cjs/prompt/compact/user-memory-extractor.d.ts +7 -0
  43. package/lib/cjs/prompt/compact/user-memory-extractor.js +120 -0
  44. package/lib/cjs/prompt/context/afs/history.d.ts +9 -0
  45. package/lib/cjs/prompt/context/afs/history.js +33 -0
  46. package/lib/cjs/prompt/context/afs/index.d.ts +20 -0
  47. package/lib/cjs/prompt/context/afs/index.js +54 -0
  48. package/lib/cjs/prompt/context/index.d.ts +31 -0
  49. package/lib/cjs/prompt/context/index.js +18 -0
  50. package/lib/cjs/prompt/prompt-builder.d.ts +11 -9
  51. package/lib/cjs/prompt/prompt-builder.js +81 -151
  52. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  53. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.js +69 -0
  54. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  55. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.js +62 -0
  56. package/lib/cjs/prompt/skills/afs/base.d.ts +4 -0
  57. package/lib/cjs/prompt/skills/afs/base.js +8 -0
  58. package/lib/cjs/prompt/skills/afs/delete.d.ts +3 -2
  59. package/lib/cjs/prompt/skills/afs/delete.js +17 -5
  60. package/lib/cjs/prompt/skills/afs/edit.d.ts +9 -11
  61. package/lib/cjs/prompt/skills/afs/edit.js +89 -63
  62. package/lib/cjs/prompt/skills/afs/exec.d.ts +4 -3
  63. package/lib/cjs/prompt/skills/afs/exec.js +23 -7
  64. package/lib/cjs/prompt/skills/afs/index.js +4 -1
  65. package/lib/cjs/prompt/skills/afs/list.d.ts +4 -4
  66. package/lib/cjs/prompt/skills/afs/list.js +38 -55
  67. package/lib/cjs/prompt/skills/afs/read.d.ts +10 -5
  68. package/lib/cjs/prompt/skills/afs/read.js +66 -19
  69. package/lib/cjs/prompt/skills/afs/rename.d.ts +3 -2
  70. package/lib/cjs/prompt/skills/afs/rename.js +20 -6
  71. package/lib/cjs/prompt/skills/afs/search.d.ts +4 -3
  72. package/lib/cjs/prompt/skills/afs/search.js +24 -8
  73. package/lib/cjs/prompt/skills/afs/write.d.ts +3 -2
  74. package/lib/cjs/prompt/skills/afs/write.js +22 -8
  75. package/lib/cjs/prompt/template.d.ts +84 -9
  76. package/lib/cjs/prompt/template.js +46 -17
  77. package/lib/dts/agents/agent.d.ts +42 -29
  78. package/lib/dts/agents/ai-agent.d.ts +63 -4
  79. package/lib/dts/agents/chat-model.d.ts +162 -0
  80. package/lib/dts/agents/image-agent.d.ts +17 -1
  81. package/lib/dts/agents/image-model.d.ts +17 -2
  82. package/lib/dts/agents/mcp-agent.d.ts +17 -0
  83. package/lib/dts/agents/team-agent.d.ts +55 -0
  84. package/lib/dts/agents/transform-agent.d.ts +12 -0
  85. package/lib/dts/agents/video-model.d.ts +15 -0
  86. package/lib/dts/aigne/context.d.ts +2 -2
  87. package/lib/dts/aigne/usage.d.ts +5 -0
  88. package/lib/dts/index.d.ts +1 -0
  89. package/lib/dts/loader/agent-yaml.d.ts +27 -64
  90. package/lib/dts/loader/agents.d.ts +4 -0
  91. package/lib/dts/loader/index.d.ts +16 -12
  92. package/lib/dts/loader/schema.d.ts +21 -6
  93. package/lib/dts/memory/recorder.d.ts +4 -4
  94. package/lib/dts/memory/retriever.d.ts +4 -4
  95. package/lib/dts/prompt/agent-session.d.ts +135 -0
  96. package/lib/dts/prompt/compact/compactor.d.ts +7 -0
  97. package/lib/dts/prompt/compact/session-memory-extractor.d.ts +7 -0
  98. package/lib/dts/prompt/compact/types.d.ts +329 -0
  99. package/lib/dts/prompt/compact/user-memory-extractor.d.ts +7 -0
  100. package/lib/dts/prompt/context/afs/history.d.ts +9 -0
  101. package/lib/dts/prompt/context/afs/index.d.ts +20 -0
  102. package/lib/dts/prompt/context/index.d.ts +31 -0
  103. package/lib/dts/prompt/prompt-builder.d.ts +11 -9
  104. package/lib/dts/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  105. package/lib/dts/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  106. package/lib/dts/prompt/skills/afs/base.d.ts +4 -0
  107. package/lib/dts/prompt/skills/afs/delete.d.ts +3 -2
  108. package/lib/dts/prompt/skills/afs/edit.d.ts +9 -11
  109. package/lib/dts/prompt/skills/afs/exec.d.ts +4 -3
  110. package/lib/dts/prompt/skills/afs/list.d.ts +4 -4
  111. package/lib/dts/prompt/skills/afs/read.d.ts +10 -5
  112. package/lib/dts/prompt/skills/afs/rename.d.ts +3 -2
  113. package/lib/dts/prompt/skills/afs/search.d.ts +4 -3
  114. package/lib/dts/prompt/skills/afs/write.d.ts +3 -2
  115. package/lib/dts/prompt/template.d.ts +84 -9
  116. package/lib/esm/agents/agent.d.ts +42 -29
  117. package/lib/esm/agents/agent.js +32 -11
  118. package/lib/esm/agents/ai-agent.d.ts +63 -4
  119. package/lib/esm/agents/ai-agent.js +148 -20
  120. package/lib/esm/agents/chat-model.d.ts +162 -0
  121. package/lib/esm/agents/chat-model.js +55 -4
  122. package/lib/esm/agents/image-agent.d.ts +17 -1
  123. package/lib/esm/agents/image-agent.js +16 -0
  124. package/lib/esm/agents/image-model.d.ts +17 -2
  125. package/lib/esm/agents/image-model.js +2 -0
  126. package/lib/esm/agents/mcp-agent.d.ts +17 -0
  127. package/lib/esm/agents/mcp-agent.js +18 -0
  128. package/lib/esm/agents/team-agent.d.ts +55 -0
  129. package/lib/esm/agents/team-agent.js +31 -0
  130. package/lib/esm/agents/transform-agent.d.ts +12 -0
  131. package/lib/esm/agents/transform-agent.js +13 -0
  132. package/lib/esm/agents/video-model.d.ts +15 -0
  133. package/lib/esm/agents/video-model.js +2 -0
  134. package/lib/esm/aigne/context.d.ts +2 -2
  135. package/lib/esm/aigne/usage.d.ts +5 -0
  136. package/lib/esm/aigne/usage.js +6 -0
  137. package/lib/esm/index.d.ts +1 -0
  138. package/lib/esm/index.js +1 -0
  139. package/lib/esm/loader/agent-yaml.d.ts +27 -64
  140. package/lib/esm/loader/agent-yaml.js +22 -128
  141. package/lib/esm/loader/agents.d.ts +4 -0
  142. package/lib/esm/loader/agents.js +14 -0
  143. package/lib/esm/loader/index.d.ts +16 -12
  144. package/lib/esm/loader/index.js +47 -82
  145. package/lib/esm/loader/schema.d.ts +21 -6
  146. package/lib/esm/loader/schema.js +57 -0
  147. package/lib/esm/memory/recorder.d.ts +4 -4
  148. package/lib/esm/memory/retriever.d.ts +4 -4
  149. package/lib/esm/prompt/agent-session.d.ts +135 -0
  150. package/lib/esm/prompt/agent-session.js +849 -0
  151. package/lib/esm/prompt/compact/compactor.d.ts +7 -0
  152. package/lib/esm/prompt/compact/compactor.js +44 -0
  153. package/lib/esm/prompt/compact/session-memory-extractor.d.ts +7 -0
  154. package/lib/esm/prompt/compact/session-memory-extractor.js +135 -0
  155. package/lib/esm/prompt/compact/types.d.ts +329 -0
  156. package/lib/esm/prompt/compact/types.js +50 -0
  157. package/lib/esm/prompt/compact/user-memory-extractor.d.ts +7 -0
  158. package/lib/esm/prompt/compact/user-memory-extractor.js +116 -0
  159. package/lib/esm/prompt/context/afs/history.d.ts +9 -0
  160. package/lib/esm/prompt/context/afs/history.js +30 -0
  161. package/lib/esm/prompt/context/afs/index.d.ts +20 -0
  162. package/lib/esm/prompt/context/afs/index.js +51 -0
  163. package/lib/esm/prompt/context/index.d.ts +31 -0
  164. package/lib/esm/prompt/context/index.js +15 -0
  165. package/lib/esm/prompt/prompt-builder.d.ts +11 -9
  166. package/lib/esm/prompt/prompt-builder.js +80 -150
  167. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  168. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.js +65 -0
  169. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  170. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.js +54 -0
  171. package/lib/esm/prompt/skills/afs/base.d.ts +4 -0
  172. package/lib/esm/prompt/skills/afs/base.js +4 -0
  173. package/lib/esm/prompt/skills/afs/delete.d.ts +3 -2
  174. package/lib/esm/prompt/skills/afs/delete.js +17 -5
  175. package/lib/esm/prompt/skills/afs/edit.d.ts +9 -11
  176. package/lib/esm/prompt/skills/afs/edit.js +89 -63
  177. package/lib/esm/prompt/skills/afs/exec.d.ts +4 -3
  178. package/lib/esm/prompt/skills/afs/exec.js +23 -7
  179. package/lib/esm/prompt/skills/afs/index.js +4 -1
  180. package/lib/esm/prompt/skills/afs/list.d.ts +4 -4
  181. package/lib/esm/prompt/skills/afs/list.js +38 -55
  182. package/lib/esm/prompt/skills/afs/read.d.ts +10 -5
  183. package/lib/esm/prompt/skills/afs/read.js +66 -19
  184. package/lib/esm/prompt/skills/afs/rename.d.ts +3 -2
  185. package/lib/esm/prompt/skills/afs/rename.js +20 -6
  186. package/lib/esm/prompt/skills/afs/search.d.ts +4 -3
  187. package/lib/esm/prompt/skills/afs/search.js +24 -8
  188. package/lib/esm/prompt/skills/afs/write.d.ts +3 -2
  189. package/lib/esm/prompt/skills/afs/write.js +22 -8
  190. package/lib/esm/prompt/template.d.ts +84 -9
  191. package/lib/esm/prompt/template.js +46 -17
  192. package/package.json +6 -5
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ import { Agent } from "../../../../agents/agent.js";
3
+ const skillToolInputSchema = z.object({
4
+ skill: z.string().describe("The name of the skill agent to invoke."),
5
+ args: z.string().optional().describe("The arguments to pass to the skill."),
6
+ });
7
+ export class AgentSkill extends Agent {
8
+ static formatOutput(output) {
9
+ if (!("result" in output) || typeof output.result !== "string") {
10
+ throw new Error("Invalid SkillToolOutput: missing 'result' field");
11
+ }
12
+ return output.result;
13
+ }
14
+ constructor(options) {
15
+ super({
16
+ name: "Skill",
17
+ taskTitle: "Invoke {{skill}}: {{args}}",
18
+ ...options,
19
+ description: `\
20
+ Execute a skill within the main conversation
21
+
22
+ <skills_instructions>
23
+ When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
24
+
25
+ When users ask you to run a "slash command" or reference "/" (e.g., "/commit", "/review-pr"), they are referring to a skill. Use this tool to invoke the corresponding skill.
26
+
27
+ User: "run /commit" Assistant: [Calls Skill tool with skill: "commit"]
28
+ How to invoke:
29
+
30
+ Use this tool with the skill name and optional arguments
31
+
32
+ Important:
33
+
34
+ When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action
35
+ NEVER just announce or mention a skill in your text response without actually calling this tool
36
+ This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task
37
+ Only use skills listed in <available_skills> below
38
+ Do not invoke a skill that is already running
39
+ Do not use this tool for built-in CLI commands (like /help, /clear, etc.)
40
+ </skills_instructions>
41
+
42
+ <available_skills>
43
+ ${options.agentSkills.map((s) => `${s.name}: ${s.description}`).join("\n\n")}
44
+ </available_skills>
45
+ `,
46
+ inputSchema: skillToolInputSchema,
47
+ });
48
+ this.agentSkills = options.agentSkills;
49
+ }
50
+ agentSkills;
51
+ async process(input) {
52
+ const skill = this.agentSkills.find((s) => s.name === input.skill);
53
+ if (!skill)
54
+ throw new Error(`Skill not found: ${input.skill}`);
55
+ return {
56
+ result: `\
57
+ Base directory for this skill: ${skill.path}
58
+
59
+ ${skill.content}
60
+
61
+ ${input.args ? `ARGUMENTS: ${input.args ?? "None"}` : ""}
62
+ `,
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,13 @@
1
+ import type { AFS } from "@aigne/afs";
2
+ import { AgentSkill } from "./agent-skill.js";
3
+ export interface Skill {
4
+ path: string;
5
+ name: string;
6
+ description: string;
7
+ content: string;
8
+ }
9
+ export declare function loadSkill(path: string): Promise<Skill>;
10
+ export declare function loadSkills(paths: string[]): Promise<Skill[]>;
11
+ export declare function loadAgentSkillFromAFS({ afs, }: {
12
+ afs: AFS;
13
+ }): Promise<AgentSkill | undefined>;
@@ -0,0 +1,54 @@
1
+ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
+ import fm from "front-matter";
3
+ import { AgentSkill } from "./agent-skill.js";
4
+ function parseSkill(content, path) {
5
+ const meta = fm(content);
6
+ return {
7
+ path,
8
+ name: meta.attributes.name,
9
+ description: meta.attributes.description,
10
+ content: meta.body,
11
+ };
12
+ }
13
+ export async function loadSkill(path) {
14
+ const entry = nodejs.path.join(path, "SKILL.md");
15
+ const skill = await nodejs.fs.readFile(entry, "utf-8");
16
+ return parseSkill(skill, path);
17
+ }
18
+ export async function loadSkills(paths) {
19
+ const skills = [];
20
+ for (const path of paths) {
21
+ const skill = await loadSkill(path);
22
+ skills.push(skill);
23
+ }
24
+ return skills;
25
+ }
26
+ export async function loadAgentSkillFromAFS({ afs, }) {
27
+ const modules = await afs.listModules();
28
+ const filtered = modules.filter(({ module: m }) => "options" in m &&
29
+ typeof m.options === "object" &&
30
+ m.options &&
31
+ "agentSkills" in m.options &&
32
+ m.options.agentSkills === true);
33
+ if (!filtered.length)
34
+ return;
35
+ const skills = [];
36
+ for (const module of filtered) {
37
+ const data = (await afs.list(module.path, {
38
+ pattern: "**/SKILL.md",
39
+ maxDepth: 10,
40
+ })).data;
41
+ for (const entry of data) {
42
+ const { data: file } = await afs.read(entry.path);
43
+ if (typeof file?.content !== "string")
44
+ continue;
45
+ const skill = parseSkill(file.content, nodejs.path.dirname(entry.path));
46
+ skills.push(skill);
47
+ }
48
+ }
49
+ if (!skills.length)
50
+ return;
51
+ return new AgentSkill({
52
+ agentSkills: skills,
53
+ });
54
+ }
@@ -0,0 +1,4 @@
1
+ import { Agent, type Message } from "../../../agents/agent.js";
2
+ export declare abstract class AFSSkillBase<I extends Message = Message, O extends Message = Message> extends Agent<I, O> {
3
+ tag: string;
4
+ }
@@ -0,0 +1,4 @@
1
+ import { Agent } from "../../../agents/agent.js";
2
+ export class AFSSkillBase extends Agent {
3
+ tag = "AFS";
4
+ }
@@ -1,4 +1,5 @@
1
- import { Agent, type AgentInvokeOptions, type AgentOptions, type Message } from "../../../agents/agent.js";
1
+ import type { AgentInvokeOptions, AgentOptions, Message } from "../../../agents/agent.js";
2
+ import { AFSSkillBase } from "./base.js";
2
3
  export interface AFSDeleteInput extends Message {
3
4
  path: string;
4
5
  recursive?: boolean;
@@ -12,7 +13,7 @@ export interface AFSDeleteOutput extends Message {
12
13
  export interface AFSDeleteAgentOptions extends AgentOptions<AFSDeleteInput, AFSDeleteOutput> {
13
14
  afs: NonNullable<AgentOptions<AFSDeleteInput, AFSDeleteOutput>["afs"]>;
14
15
  }
15
- export declare class AFSDeleteAgent extends Agent<AFSDeleteInput, AFSDeleteOutput> {
16
+ export declare class AFSDeleteAgent extends AFSSkillBase<AFSDeleteInput, AFSDeleteOutput> {
16
17
  constructor(options: AFSDeleteAgentOptions);
17
18
  process(input: AFSDeleteInput, _options: AgentInvokeOptions): Promise<AFSDeleteOutput>;
18
19
  }
@@ -1,18 +1,30 @@
1
1
  import { z } from "zod";
2
- import { Agent, } from "../../../agents/agent.js";
3
- export class AFSDeleteAgent extends Agent {
2
+ import { AFSSkillBase } from "./base.js";
3
+ export class AFSDeleteAgent extends AFSSkillBase {
4
4
  constructor(options) {
5
5
  super({
6
6
  name: "afs_delete",
7
- description: "Permanently delete files or directories. Use when removing unwanted files or cleaning up temporary data.",
7
+ description: `Permanently delete files or directories from the Agentic File System (AFS)
8
+ - Removes files or directories at the specified AFS path
9
+ - Supports recursive deletion for directories with contents
10
+ - Use with caution as deletion is permanent
11
+
12
+ Usage:
13
+ - The path must be an absolute AFS path starting with "/" (e.g., "/docs/old-file.md", "/temp")
14
+ - This is NOT a local system file path - it operates within the AFS virtual file system
15
+ - To delete a directory, you MUST set recursive=true
16
+ - Deleting a non-empty directory without recursive=true will fail
17
+ - This operation cannot be undone`,
8
18
  ...options,
9
19
  inputSchema: z.object({
10
- path: z.string().describe("Absolute file or directory path to delete"),
20
+ path: z
21
+ .string()
22
+ .describe("Absolute AFS path to delete (e.g., '/docs/old-file.md', '/temp'). Must start with '/'"),
11
23
  recursive: z
12
24
  .boolean()
13
25
  .optional()
14
26
  .default(false)
15
- .describe("Allow directory deletion (default: false, required for directories)"),
27
+ .describe("MUST be set to true to delete directories. Default: false (files only)"),
16
28
  }),
17
29
  outputSchema: z.object({
18
30
  status: z.string(),
@@ -1,26 +1,24 @@
1
- import { Agent, type AgentInvokeOptions, type AgentOptions, type Message } from "../../../agents/agent.js";
2
- export interface Patch {
3
- start_line: number;
4
- end_line: number;
5
- replace?: string;
6
- delete: boolean;
7
- }
1
+ import type { AgentInvokeOptions, AgentOptions, Message } from "../../../agents/agent.js";
2
+ import { AFSSkillBase } from "./base.js";
8
3
  export interface AFSEditInput extends Message {
9
4
  path: string;
10
- patches: Patch[];
5
+ oldString: string;
6
+ newString: string;
7
+ replaceAll?: boolean;
11
8
  }
12
9
  export interface AFSEditOutput extends Message {
13
10
  status: string;
14
11
  tool: string;
15
12
  path: string;
16
13
  message: string;
17
- content: string;
14
+ snippet: string;
18
15
  }
19
16
  export interface AFSEditAgentOptions extends AgentOptions<AFSEditInput, AFSEditOutput> {
20
17
  afs: NonNullable<AgentOptions<AFSEditInput, AFSEditOutput>["afs"]>;
21
18
  }
22
- export declare class AFSEditAgent extends Agent<AFSEditInput, AFSEditOutput> {
19
+ export declare class AFSEditAgent extends AFSSkillBase<AFSEditInput, AFSEditOutput> {
23
20
  constructor(options: AFSEditAgentOptions);
24
21
  process(input: AFSEditInput, _options: AgentInvokeOptions): Promise<AFSEditOutput>;
25
- applyCustomPatches(text: string, patches: Patch[]): string;
22
+ private countOccurrences;
23
+ private extractSnippet;
26
24
  }
@@ -1,94 +1,120 @@
1
1
  import { z } from "zod";
2
- import { Agent, } from "../../../agents/agent.js";
3
- export class AFSEditAgent extends Agent {
2
+ import { AFSSkillBase } from "./base.js";
3
+ const CONTEXT_LINES = 4; // Number of lines to show before and after the edit
4
+ export class AFSEditAgent extends AFSSkillBase {
4
5
  constructor(options) {
5
6
  super({
6
7
  name: "afs_edit",
7
- description: "Apply precise line-based patches to modify file content. Use when making targeted changes without rewriting the entire file.",
8
+ description: `Performs exact string replacements in files within the Agentic File System (AFS).
9
+
10
+ Usage:
11
+ - You must use afs_read at least once before editing to understand the file content
12
+ - The path must be an absolute AFS path starting with "/" (e.g., "/docs/readme.md")
13
+ - Preserve exact indentation (tabs/spaces) as it appears in the file
14
+ - The edit will FAIL if oldString is not found in the file
15
+ - The edit will FAIL if oldString appears multiple times (unless replaceAll is true)
16
+ - Use replaceAll to replace/rename strings across the entire file`,
8
17
  ...options,
9
18
  inputSchema: z.object({
10
- path: z.string().describe("Absolute file path to edit"),
11
- patches: z
12
- .array(z.object({
13
- start_line: z.number().int().describe("Start line number (0-based, inclusive)"),
14
- end_line: z.number().int().describe("End line number (0-based, exclusive)"),
15
- replace: z.string().optional().describe("New content to replace the line range"),
16
- delete: z.boolean().describe("Delete mode: true to delete lines, false to replace"),
17
- }))
18
- .min(1)
19
- .describe("List of patches to apply sequentially"),
19
+ path: z
20
+ .string()
21
+ .describe("Absolute AFS path to the file to edit (e.g., '/docs/readme.md'). Must start with '/'"),
22
+ oldString: z
23
+ .string()
24
+ .describe("The exact text to replace. Must match file content exactly including whitespace"),
25
+ newString: z
26
+ .string()
27
+ .describe("The text to replace it with (must be different from oldString)"),
28
+ replaceAll: z
29
+ .boolean()
30
+ .optional()
31
+ .default(false)
32
+ .describe("Replace all occurrences of oldString (default: false)"),
20
33
  }),
21
34
  outputSchema: z.object({
22
35
  status: z.string(),
23
36
  tool: z.string(),
24
37
  path: z.string(),
25
38
  message: z.string(),
26
- content: z.string(),
39
+ snippet: z.string(),
27
40
  }),
28
41
  });
29
42
  }
30
43
  async process(input, _options) {
31
44
  if (!this.afs)
32
45
  throw new Error("AFS is not configured for this agent.");
33
- if (!input.patches?.length) {
34
- throw new Error("No patches provided for afs_edit.");
46
+ const { path, oldString, newString, replaceAll = false } = input;
47
+ if (oldString === newString) {
48
+ throw new Error("oldString and newString must be different");
35
49
  }
36
- const readResult = await this.afs.read(input.path);
37
- if (!readResult.result?.content || typeof readResult.result.content !== "string") {
38
- throw new Error(`Cannot read file content from: ${input.path}`);
50
+ const readResult = await this.afs.read(path);
51
+ if (!readResult.data?.content || typeof readResult.data.content !== "string") {
52
+ throw new Error(`Cannot read file content from: ${path}`);
39
53
  }
40
- const originalContent = readResult.result.content;
41
- const updatedContent = this.applyCustomPatches(originalContent, input.patches);
42
- await this.afs.write(input.path, {
54
+ const originalContent = readResult.data.content;
55
+ // Check if oldString exists in the file
56
+ const occurrences = this.countOccurrences(originalContent, oldString);
57
+ if (occurrences === 0) {
58
+ throw new Error(`oldString not found in file: ${path}`);
59
+ }
60
+ if (occurrences > 1 && !replaceAll) {
61
+ throw new Error(`oldString appears ${occurrences} times in file. Use replaceAll=true to replace all occurrences, or provide more context to make oldString unique.`);
62
+ }
63
+ // Find the position of the first occurrence for snippet extraction
64
+ const firstOccurrenceIndex = originalContent.indexOf(oldString);
65
+ // Perform the replacement
66
+ const updatedContent = replaceAll
67
+ ? originalContent.split(oldString).join(newString)
68
+ : originalContent.replace(oldString, newString);
69
+ await this.afs.write(path, {
43
70
  content: updatedContent,
44
71
  });
72
+ // Generate snippet around the edit location
73
+ const snippet = this.extractSnippet(updatedContent, firstOccurrenceIndex, newString.length);
74
+ const replacementCount = replaceAll ? occurrences : 1;
45
75
  return {
46
76
  status: "success",
47
77
  tool: "afs_edit",
48
- path: input.path,
49
- message: `Applied ${input.patches.length} patches to ${input.path}`,
50
- content: updatedContent,
78
+ path,
79
+ message: `Replaced ${replacementCount} occurrence${replacementCount > 1 ? "s" : ""} in ${path}`,
80
+ snippet,
51
81
  };
52
82
  }
53
- applyCustomPatches(text, patches) {
54
- // Sort by start_line to ensure sequential application
55
- const sorted = [...patches].sort((a, b) => a.start_line - b.start_line);
56
- const lines = text.split("\n");
57
- for (let i = 0; i < sorted.length; i++) {
58
- const patch = sorted[i];
59
- if (!patch)
60
- continue;
61
- const start = patch.start_line;
62
- const end = patch.end_line;
63
- const deleteCount = end - start; // [start, end) range
64
- let delta = 0;
65
- if (patch.delete) {
66
- // Delete mode: remove the specified lines [start, end)
67
- lines.splice(start, deleteCount);
68
- delta = -deleteCount;
69
- }
70
- else {
71
- // Replace mode: replace the specified lines with new content
72
- const replaceLines = patch.replace ? patch.replace.split("\n") : [];
73
- lines.splice(start, deleteCount, ...replaceLines);
74
- delta = replaceLines.length - deleteCount;
75
- }
76
- // Update subsequent patches' line numbers
77
- // For exclusive-end semantics [start, end), we adjust patches that start >= current patch's start_line
78
- // after the current patch has been applied
79
- if (delta !== 0) {
80
- for (let j = i + 1; j < sorted.length; j++) {
81
- const next = sorted[j];
82
- if (!next)
83
- continue;
84
- // Adjust patches that start at or after the current patch's end line
85
- if (next.start_line >= patch.end_line) {
86
- next.start_line += delta;
87
- next.end_line += delta;
88
- }
89
- }
83
+ countOccurrences(text, search) {
84
+ let count = 0;
85
+ let position = text.indexOf(search);
86
+ while (position !== -1) {
87
+ count++;
88
+ position = text.indexOf(search, position + search.length);
89
+ }
90
+ return count;
91
+ }
92
+ extractSnippet(content, editStartIndex, newStringLength) {
93
+ const lines = content.split("\n");
94
+ // Find the line number where the edit starts
95
+ let charCount = 0;
96
+ let editStartLine = 0;
97
+ for (let i = 0; i < lines.length; i++) {
98
+ const lineLength = (lines[i]?.length ?? 0) + 1; // +1 for newline
99
+ if (charCount + lineLength > editStartIndex) {
100
+ editStartLine = i;
101
+ break;
90
102
  }
103
+ charCount += lineLength;
91
104
  }
92
- return lines.join("\n");
105
+ // Calculate how many lines the new content spans
106
+ const newContentLines = content
107
+ .substring(editStartIndex, editStartIndex + newStringLength)
108
+ .split("\n").length;
109
+ const editEndLine = editStartLine + newContentLines - 1;
110
+ // Extract lines with context
111
+ const startLine = Math.max(0, editStartLine - CONTEXT_LINES);
112
+ const endLine = Math.min(lines.length - 1, editEndLine + CONTEXT_LINES);
113
+ // Format with line numbers (1-based)
114
+ const snippetLines = lines.slice(startLine, endLine + 1).map((line, idx) => {
115
+ const lineNum = startLine + idx + 1;
116
+ return `${String(lineNum).padStart(4)}| ${line}`;
117
+ });
118
+ return snippetLines.join("\n");
93
119
  }
94
120
  }
@@ -1,15 +1,16 @@
1
- import { Agent, type AgentInvokeOptions, type AgentOptions, type Message } from "../../../agents/agent.js";
1
+ import type { AgentInvokeOptions, AgentOptions, Message } from "../../../agents/agent.js";
2
+ import { AFSSkillBase } from "./base.js";
2
3
  export interface AFSExecInput extends Message {
3
4
  path: string;
4
5
  args: string;
5
6
  }
6
7
  export interface AFSExecOutput extends Message {
7
- result: Record<string, any>;
8
+ data: Record<string, any>;
8
9
  }
9
10
  export interface AFSExecAgentOptions extends AgentOptions<AFSExecInput, AFSExecOutput> {
10
11
  afs: NonNullable<AgentOptions<AFSExecInput, AFSExecOutput>["afs"]>;
11
12
  }
12
- export declare class AFSExecAgent extends Agent<AFSExecInput, AFSExecOutput> {
13
+ export declare class AFSExecAgent extends AFSSkillBase<AFSExecInput, AFSExecOutput> {
13
14
  constructor(options: AFSExecAgentOptions);
14
15
  process(input: AFSExecInput, options: AgentInvokeOptions): Promise<AFSExecOutput>;
15
16
  }
@@ -1,23 +1,39 @@
1
1
  import { z } from "zod";
2
- import { Agent, } from "../../../agents/agent.js";
3
- export class AFSExecAgent extends Agent {
2
+ import { AFSSkillBase } from "./base.js";
3
+ export class AFSExecAgent extends AFSSkillBase {
4
4
  constructor(options) {
5
5
  super({
6
6
  name: "afs_exec",
7
- description: "Execute functions or commands from AFS modules. Use when running operations provided by mounted modules.",
7
+ description: `Execute files marked as executable in the Agentic File System (AFS)
8
+ - Runs executable entries (functions, agents, skills) registered at a given AFS path
9
+ - Passes arguments to the executable and returns its output
10
+ - Use this to invoke dynamic functionality stored in AFS
11
+
12
+ Usage:
13
+ - The path must be an absolute AFS path to an executable entry (e.g., "/skills/summarize", "/agents/translator")
14
+ - This is NOT a local system file path - it operates within the AFS virtual file system
15
+ - Use afs_list to discover available executables (look for entries with execute metadata)
16
+ - Arguments must be a valid JSON string matching the executable's input schema
17
+ - The executable's input/output schema can be found in its metadata`,
8
18
  ...options,
9
19
  inputSchema: z.object({
10
- path: z.string().describe("Absolute path to the executable function in AFS"),
11
- args: z.string().describe("JSON string of arguments matching the function's input schema"),
20
+ path: z
21
+ .string()
22
+ .describe("Absolute AFS path to the executable (e.g., '/skills/summarize'). Must start with '/'"),
23
+ args: z
24
+ .string()
25
+ .describe('JSON string of arguments matching the executable\'s input schema (e.g., \'{"text": "hello"}\')'),
12
26
  }),
13
27
  outputSchema: z.object({
14
- result: z.record(z.any()),
28
+ data: z.record(z.any()),
15
29
  }),
16
30
  });
17
31
  }
18
32
  async process(input, options) {
19
33
  if (!this.afs)
20
34
  throw new Error("AFS is not configured for this agent.");
21
- return await this.afs.exec(input.path, JSON.parse(input.args), options);
35
+ return {
36
+ ...(await this.afs.exec(input.path, JSON.parse(input.args), options)),
37
+ };
22
38
  }
23
39
  }
@@ -1,3 +1,5 @@
1
+ import { isNonNullable } from "../../../utils/type-utils.js";
2
+ import { loadAgentSkillFromAFS } from "./agent-skill/skill-loader.js";
1
3
  import { AFSDeleteAgent } from "./delete.js";
2
4
  import { AFSEditAgent } from "./edit.js";
3
5
  import { AFSExecAgent } from "./exec.js";
@@ -16,5 +18,6 @@ export async function getAFSSkills(afs) {
16
18
  new AFSDeleteAgent({ afs }),
17
19
  new AFSRenameAgent({ afs }),
18
20
  new AFSExecAgent({ afs }),
19
- ];
21
+ await loadAgentSkillFromAFS({ afs }),
22
+ ].filter(isNonNullable);
20
23
  }
@@ -1,5 +1,6 @@
1
1
  import type { AFSListOptions } from "@aigne/afs";
2
- import { Agent, type AgentInvokeOptions, type AgentOptions, type Message } from "../../../agents/agent.js";
2
+ import type { AgentInvokeOptions, AgentOptions, Message } from "../../../agents/agent.js";
3
+ import { AFSSkillBase } from "./base.js";
3
4
  export interface AFSListInput extends Message {
4
5
  path: string;
5
6
  options?: AFSListOptions;
@@ -10,13 +11,12 @@ export interface AFSListOutput extends Message {
10
11
  path: string;
11
12
  options?: AFSListOptions;
12
13
  message?: string;
13
- result: string;
14
+ data?: unknown;
14
15
  }
15
16
  export interface AFSListAgentOptions extends AgentOptions<AFSListInput, AFSListOutput> {
16
17
  afs: NonNullable<AgentOptions<AFSListInput, AFSListOutput>["afs"]>;
17
18
  }
18
- export declare class AFSListAgent extends Agent<AFSListInput, AFSListOutput> {
19
+ export declare class AFSListAgent extends AFSSkillBase<AFSListInput, AFSListOutput> {
19
20
  constructor(options: AFSListAgentOptions);
20
21
  process(input: AFSListInput, _options: AgentInvokeOptions): Promise<AFSListOutput>;
21
- private buildTreeView;
22
22
  }