@aigne/core 1.63.0-beta → 1.63.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 (44) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/lib/cjs/agents/agent.d.ts +8 -1
  3. package/lib/cjs/agents/agent.js +14 -4
  4. package/lib/cjs/agents/ai-agent.d.ts +18 -1
  5. package/lib/cjs/agents/ai-agent.js +60 -17
  6. package/lib/cjs/agents/chat-model.js +1 -1
  7. package/lib/cjs/loader/agent-yaml.d.ts +14 -1
  8. package/lib/cjs/loader/agent-yaml.js +24 -0
  9. package/lib/cjs/loader/index.d.ts +7 -0
  10. package/lib/cjs/loader/index.js +22 -0
  11. package/lib/cjs/prompt/prompt-builder.d.ts +1 -0
  12. package/lib/cjs/prompt/prompt-builder.js +57 -16
  13. package/lib/cjs/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
  14. package/lib/cjs/prompt/prompts/afs-builtin-prompt.js +23 -0
  15. package/lib/cjs/prompt/skills/afs.d.ts +3 -0
  16. package/lib/cjs/prompt/skills/afs.js +75 -0
  17. package/lib/cjs/utils/type-utils.d.ts +1 -0
  18. package/lib/cjs/utils/type-utils.js +14 -0
  19. package/lib/dts/agents/agent.d.ts +8 -1
  20. package/lib/dts/agents/ai-agent.d.ts +18 -1
  21. package/lib/dts/loader/agent-yaml.d.ts +14 -1
  22. package/lib/dts/loader/index.d.ts +7 -0
  23. package/lib/dts/prompt/prompt-builder.d.ts +1 -0
  24. package/lib/dts/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
  25. package/lib/dts/prompt/skills/afs.d.ts +3 -0
  26. package/lib/dts/utils/type-utils.d.ts +1 -0
  27. package/lib/esm/agents/agent.d.ts +8 -1
  28. package/lib/esm/agents/agent.js +14 -4
  29. package/lib/esm/agents/ai-agent.d.ts +18 -1
  30. package/lib/esm/agents/ai-agent.js +57 -17
  31. package/lib/esm/agents/chat-model.js +1 -1
  32. package/lib/esm/loader/agent-yaml.d.ts +14 -1
  33. package/lib/esm/loader/agent-yaml.js +24 -0
  34. package/lib/esm/loader/index.d.ts +7 -0
  35. package/lib/esm/loader/index.js +22 -0
  36. package/lib/esm/prompt/prompt-builder.d.ts +1 -0
  37. package/lib/esm/prompt/prompt-builder.js +58 -17
  38. package/lib/esm/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
  39. package/lib/esm/prompt/prompts/afs-builtin-prompt.js +20 -0
  40. package/lib/esm/prompt/skills/afs.d.ts +3 -0
  41. package/lib/esm/prompt/skills/afs.js +72 -0
  42. package/lib/esm/utils/type-utils.d.ts +1 -0
  43. package/lib/esm/utils/type-utils.js +13 -0
  44. package/package.json +3 -3
@@ -1,3 +1,4 @@
1
+ import fastq from "fastq";
1
2
  import { z } from "zod";
2
3
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
4
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "../prompt/prompts/structured-stream-instructions.js";
@@ -5,6 +6,7 @@ import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js
5
6
  import { ExtractMetadataTransform } from "../utils/structured-stream-extractor.js";
6
7
  import { checkArguments, isEmpty } from "../utils/type-utils.js";
7
8
  import { Agent, agentOptionsSchema, isAgentResponseDelta, } from "./agent.js";
9
+ import { fileUnionContentsSchema } from "./model.js";
8
10
  import { isTransferAgentOutput } from "./types.js";
9
11
  export const DEFAULT_OUTPUT_KEY = "message";
10
12
  export const DEFAULT_OUTPUT_FILE_KEY = "files";
@@ -55,6 +57,7 @@ export const aiAgentOptionsSchema = agentOptionsSchema.extend({
55
57
  inputKey: z.string().optional(),
56
58
  outputKey: z.string().optional(),
57
59
  toolChoice: aiAgentToolChoiceSchema.optional(),
60
+ toolCallsConcurrency: z.number().int().min(0).optional(),
58
61
  keepTextInToolUses: z.boolean().optional(),
59
62
  memoryAgentsAsTools: z.boolean().optional(),
60
63
  memoryPromptTemplate: z.string().optional(),
@@ -108,12 +111,15 @@ export class AIAgent extends Agent {
108
111
  typeof options.instructions === "string"
109
112
  ? PromptBuilder.from(options.instructions)
110
113
  : (options.instructions ?? new PromptBuilder());
114
+ this.autoReorderSystemMessages = options.autoReorderSystemMessages ?? false;
115
+ this.autoMergeSystemMessages = options.autoMergeSystemMessages ?? false;
111
116
  this.inputKey = options.inputKey;
112
117
  this.inputFileKey = options.inputFileKey;
113
118
  this.outputKey = options.outputKey || DEFAULT_OUTPUT_KEY;
114
119
  this.outputFileKey = options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;
115
120
  this.outputFileType = options.outputFileType;
116
121
  this.toolChoice = options.toolChoice;
122
+ this.toolCallsConcurrency = options.toolCallsConcurrency || 1;
117
123
  this.keepTextInToolUses = options.keepTextInToolUses;
118
124
  this.memoryAgentsAsTools = options.memoryAgentsAsTools;
119
125
  this.memoryPromptTemplate = options.memoryPromptTemplate;
@@ -143,6 +149,8 @@ export class AIAgent extends Agent {
143
149
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-prompt-builder}
144
150
  */
145
151
  instructions;
152
+ autoReorderSystemMessages;
153
+ autoMergeSystemMessages;
146
154
  /**
147
155
  * Pick a message from input to use as the user's message
148
156
  */
@@ -170,6 +178,12 @@ export class AIAgent extends Agent {
170
178
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
171
179
  */
172
180
  toolChoice;
181
+ /**
182
+ * Maximum number of tool calls to execute concurrently
183
+ *
184
+ * @default 1
185
+ */
186
+ toolCallsConcurrency;
173
187
  /**
174
188
  * Whether to preserve text generated during tool usage in the final output
175
189
  */
@@ -227,6 +241,20 @@ export class AIAgent extends Agent {
227
241
  * which outputs structured data in YAML format within <metadata> tags.
228
242
  */
229
243
  customStructuredStreamInstructions;
244
+ get inputSchema() {
245
+ let schema = super.inputSchema;
246
+ if (this.inputKey) {
247
+ schema = schema.extend({
248
+ [this.inputKey]: z.string().nullish(),
249
+ });
250
+ }
251
+ if (this.inputFileKey) {
252
+ schema = schema.extend({
253
+ [this.inputFileKey]: fileUnionContentsSchema.nullish(),
254
+ });
255
+ }
256
+ return schema;
257
+ }
230
258
  /**
231
259
  * Process an input message and generate a response
232
260
  *
@@ -284,31 +312,43 @@ export class AIAgent extends Agent {
284
312
  yield { delta: { text: { [outputKey]: "\n" } } };
285
313
  }
286
314
  const executedToolCalls = [];
315
+ let error;
316
+ const queue = fastq.promise(async ({ tool, call }) => {
317
+ try {
318
+ // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
319
+ const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
320
+ if (!this.catchToolsError) {
321
+ return Promise.reject(error);
322
+ }
323
+ return {
324
+ isError: true,
325
+ error: {
326
+ message: error.message,
327
+ },
328
+ };
329
+ });
330
+ executedToolCalls.push({ call, output });
331
+ }
332
+ catch (e) {
333
+ error = e;
334
+ queue.killAndDrain();
335
+ }
336
+ }, this.toolCallsConcurrency || 1);
287
337
  // Execute tools
288
338
  for (const call of toolCalls) {
289
339
  const tool = toolsMap.get(call.function.name);
290
340
  if (!tool)
291
341
  throw new Error(`Tool not found: ${call.function.name}`);
292
- // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
293
- const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
294
- if (!this.catchToolsError) {
295
- return Promise.reject(error);
296
- }
297
- return {
298
- isError: true,
299
- error: {
300
- message: error.message,
301
- },
302
- };
303
- });
304
- // NOTE: Return transfer output immediately
305
- if (isTransferAgentOutput(output)) {
306
- return output;
307
- }
308
- executedToolCalls.push({ call, output });
342
+ queue.push({ tool, call });
309
343
  }
344
+ await queue.drained();
345
+ if (error)
346
+ throw error;
310
347
  // Continue LLM function calling loop if any tools were executed
311
348
  if (executedToolCalls.length) {
349
+ const transferOutput = executedToolCalls.find((i) => isTransferAgentOutput(i.output))?.output;
350
+ if (transferOutput)
351
+ return transferOutput;
312
352
  toolCallMessages.push(await AgentMessageTemplate.from(undefined, executedToolCalls.map(({ call }) => call)).format(), ...(await Promise.all(executedToolCalls.map(({ call, output }) => ToolMessageTemplate.from(output, call.id).format()))));
313
353
  continue;
314
354
  }
@@ -6,7 +6,7 @@ import { checkArguments, isNil, omitByDeep } from "../utils/type-utils.js";
6
6
  import { agentOptionsSchema, } from "./agent.js";
7
7
  import { fileContentSchema, fileUnionContentSchema, localContentSchema, Model, urlContentSchema, } from "./model.js";
8
8
  const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
9
- retries: 3,
9
+ retries: 10,
10
10
  shouldRetry: async (error) => error instanceof StructuredOutputError || (await import("is-network-error")).default(error),
11
11
  };
12
12
  export class StructuredOutputError extends Error {
@@ -1,5 +1,6 @@
1
+ import type { AFSOptions } from "@aigne/afs";
1
2
  import { type ZodType, z } from "zod";
2
- import type { AgentHooks, FunctionAgentFn, TaskRenderMode } from "../agents/agent.js";
3
+ import type { AFSConfig, AgentHooks, FunctionAgentFn, TaskRenderMode } from "../agents/agent.js";
3
4
  import { AIAgentToolChoice } from "../agents/ai-agent.js";
4
5
  import { type Role } from "../agents/chat-model.js";
5
6
  import { ProcessMode, type ReflectionMode } from "../agents/team-agent.js";
@@ -19,6 +20,10 @@ export type NestAgentSchema = string | {
19
20
  defaultInput?: Record<string, any>;
20
21
  hooks?: HooksSchema | HooksSchema[];
21
22
  } | AgentSchema;
23
+ export type AFSModuleSchema = string | {
24
+ module: string;
25
+ options?: Record<string, any>;
26
+ };
22
27
  export interface BaseAgentSchema {
23
28
  name?: string;
24
29
  description?: string;
@@ -36,6 +41,10 @@ export interface BaseAgentSchema {
36
41
  provider: string;
37
42
  subscribeTopic?: string[];
38
43
  };
44
+ afs?: boolean | (Omit<AFSOptions, "modules"> & {
45
+ modules?: AFSModuleSchema[];
46
+ });
47
+ afsConfig?: AFSConfig;
39
48
  }
40
49
  export type Instructions = {
41
50
  role: Exclude<Role, "tool">;
@@ -45,9 +54,13 @@ export type Instructions = {
45
54
  export interface AIAgentSchema extends BaseAgentSchema {
46
55
  type: "ai";
47
56
  instructions?: Instructions;
57
+ autoReorderSystemMessages?: boolean;
58
+ autoMergeSystemMessages?: boolean;
48
59
  inputKey?: string;
60
+ inputFileKey?: string;
49
61
  outputKey?: string;
50
62
  toolChoice?: AIAgentToolChoice;
63
+ toolCallsConcurrency?: number;
51
64
  keepTextInToolUses?: boolean;
52
65
  }
53
66
  export interface ImageAgentSchema extends BaseAgentSchema {
@@ -49,6 +49,25 @@ export async function parseAgentFile(path, data) {
49
49
  subscribeTopic: optionalize(z.array(z.string())),
50
50
  })),
51
51
  ])),
52
+ afs: optionalize(z.union([
53
+ z.boolean(),
54
+ camelizeSchema(z.object({
55
+ storage: optionalize(z.object({
56
+ url: optionalize(z.string()),
57
+ })),
58
+ modules: optionalize(z.array(z.union([
59
+ z.string(),
60
+ z.object({
61
+ module: z.string(),
62
+ options: optionalize(z.record(z.any())),
63
+ }),
64
+ ]))),
65
+ })),
66
+ ])),
67
+ afsConfig: optionalize(camelizeSchema(z.object({
68
+ injectHistory: optionalize(z.boolean()),
69
+ historyWindowSize: optionalize(z.number().int().min(1)),
70
+ }))),
52
71
  });
53
72
  const instructionItemSchema = z.union([
54
73
  z.object({
@@ -89,9 +108,14 @@ export async function parseAgentFile(path, data) {
89
108
  .object({
90
109
  type: z.literal("ai"),
91
110
  instructions: optionalize(instructionsSchema),
111
+ autoReorderSystemMessages: optionalize(z.boolean()),
112
+ autoMergeSystemMessages: optionalize(z.boolean()),
92
113
  inputKey: optionalize(z.string()),
93
114
  outputKey: optionalize(z.string()),
115
+ inputFileKey: optionalize(z.string()),
116
+ outputFileKey: optionalize(z.string()),
94
117
  toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
118
+ toolCallsConcurrency: optionalize(z.number().int().min(0)),
95
119
  keepTextInToolUses: optionalize(z.boolean()),
96
120
  structuredStreamMode: optionalize(z.boolean()),
97
121
  })
@@ -1,3 +1,4 @@
1
+ import { type AFSModule } from "@aigne/afs";
1
2
  import { type ZodType, z } from "zod";
2
3
  import { Agent, type AgentOptions } from "../agents/agent.js";
3
4
  import type { ChatModel } from "../agents/chat-model.js";
@@ -11,6 +12,12 @@ export interface LoadOptions {
11
12
  }[];
12
13
  model?: ChatModel | ((model?: z.infer<typeof aigneFileSchema>["model"]) => PromiseOrValue<ChatModel | undefined>);
13
14
  imageModel?: ImageModel | ((model?: z.infer<typeof aigneFileSchema>["imageModel"]) => PromiseOrValue<ImageModel | undefined>);
15
+ afs?: {
16
+ availableModules?: {
17
+ module: string;
18
+ create: (options?: Record<string, any>) => PromiseOrValue<AFSModule>;
19
+ }[];
20
+ };
14
21
  }
15
22
  export declare function load(path: string, options?: LoadOptions): Promise<AIGNEOptions>;
16
23
  export declare function loadAgent(path: string, options?: LoadOptions, agentOptions?: AgentOptions): Promise<Agent>;
@@ -1,3 +1,4 @@
1
+ import { AFS } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import { parse } from "yaml";
3
4
  import { z } from "zod";
@@ -107,6 +108,26 @@ async function parseAgent(path, agent, options, agentOptions) {
107
108
  const memory = "memory" in agent && options?.memories?.length
108
109
  ? await loadMemory(options.memories, typeof agent.memory === "object" ? agent.memory.provider : undefined, typeof agent.memory === "object" ? agent.memory : {})
109
110
  : undefined;
111
+ let afs;
112
+ if (typeof agent.afs === "boolean") {
113
+ if (agent.afs) {
114
+ afs = new AFS();
115
+ }
116
+ }
117
+ else if (agent.afs) {
118
+ afs = new AFS({
119
+ ...agent.afs,
120
+ modules: agent.afs.modules &&
121
+ (await Promise.all(agent.afs.modules.map((m) => {
122
+ const mod = typeof m === "string"
123
+ ? options?.afs?.availableModules?.find((mod) => mod.module === m)
124
+ : options?.afs?.availableModules?.find((mod) => mod.module === m.module);
125
+ if (!mod)
126
+ throw new Error(`AFS module not found: ${typeof m === "string" ? m : m.module}`);
127
+ return mod.create(typeof m === "string" ? {} : m.options);
128
+ }))),
129
+ });
130
+ }
110
131
  const model = agent.model && typeof options?.model === "function"
111
132
  ? await options.model(agent.model)
112
133
  : undefined;
@@ -124,6 +145,7 @@ async function parseAgent(path, agent, options, agentOptions) {
124
145
  ...((await parseHooks(path, agent.hooks, options)) ?? []),
125
146
  ...[agentOptions?.hooks].flat().filter(isNonNullable),
126
147
  ],
148
+ afs,
127
149
  };
128
150
  let instructions;
129
151
  if ("instructions" in agent && agent.instructions) {
@@ -31,6 +31,7 @@ export declare class PromptBuilder {
31
31
  prompt: string;
32
32
  }>;
33
33
  private buildMessages;
34
+ private refineMessages;
34
35
  private convertMemoriesToMessages;
35
36
  private buildResponseFormat;
36
37
  private buildTools;
@@ -8,9 +8,11 @@ import { DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY } from "../agents/ai-agent.
8
8
  import { fileUnionContentsSchema } from "../agents/model.js";
9
9
  import { optionalize } from "../loader/schema.js";
10
10
  import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
11
- import { checkArguments, flat, isNonNullable, isRecord, unique } from "../utils/type-utils.js";
11
+ import { checkArguments, flat, isNonNullable, isRecord, partition, unique, } from "../utils/type-utils.js";
12
+ import { getAFSSystemPrompt } from "./prompts/afs-builtin-prompt.js";
12
13
  import { MEMORY_MESSAGE_TEMPLATE } from "./prompts/memory-message-template.js";
13
14
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "./prompts/structured-stream-instructions.js";
15
+ import { getAFSSkills } from "./skills/afs.js";
14
16
  import { AgentMessageTemplate, ChatMessagesTemplate, PromptTemplate, SystemMessageTemplate, UserMessageTemplate, } from "./template.js";
15
17
  export class PromptBuilder {
16
18
  static from(instructions, { workingDir } = {}) {
@@ -67,7 +69,7 @@ export class PromptBuilder {
67
69
  ? undefined
68
70
  : this.buildResponseFormat(options),
69
71
  outputFileType: options.agent?.outputFileType,
70
- ...this.buildTools(options),
72
+ ...(await this.buildTools(options)),
71
73
  };
72
74
  }
73
75
  async buildImagePrompt(options) {
@@ -82,9 +84,9 @@ export class PromptBuilder {
82
84
  const { input } = options;
83
85
  const inputKey = options.agent?.inputKey;
84
86
  const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
85
- const messages = (await (typeof this.instructions === "string"
87
+ const [messages, otherCustomMessages] = partition((await (typeof this.instructions === "string"
86
88
  ? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
87
- : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [];
89
+ : this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [], (i) => i.role === "system");
88
90
  const inputFileKey = options.agent?.inputFileKey;
89
91
  const files = flat(inputFileKey
90
92
  ? checkArguments("Check input files", optionalize(fileUnionContentsSchema), input?.[inputFileKey])
@@ -97,16 +99,19 @@ export class PromptBuilder {
97
99
  memories.push(...options.context.memories);
98
100
  }
99
101
  if (options.agent?.afs) {
100
- const history = await options.agent.afs.list(AFSHistory.Path, {
101
- limit: options.agent.maxRetrieveMemoryCount || 1,
102
- orderBy: [["createdAt", "desc"]],
103
- });
104
- if (message) {
105
- const result = await options.agent.afs.search("/", message);
106
- const ms = result.list.map((entry) => ({ content: stringify(entry.content) }));
107
- memories.push(...ms);
102
+ messages.push(await SystemMessageTemplate.from(await getAFSSystemPrompt(options.agent.afs)).format({}));
103
+ if (options.agent.afsConfig?.injectHistory) {
104
+ const history = await options.agent.afs.list(AFSHistory.Path, {
105
+ limit: options.agent.afsConfig.historyWindowSize || 10,
106
+ orderBy: [["createdAt", "desc"]],
107
+ });
108
+ if (message) {
109
+ const result = await options.agent.afs.search("/", message);
110
+ const ms = result.list.map((entry) => ({ content: stringify(entry.content) }));
111
+ memories.push(...ms);
112
+ }
113
+ memories.push(...history.list.filter((i) => isNonNullable(i.content)));
108
114
  }
109
- memories.push(...history.list.filter((i) => isNonNullable(i.content)));
110
115
  }
111
116
  if (memories.length)
112
117
  messages.push(...(await this.convertMemoriesToMessages(memories, options)));
@@ -124,13 +129,46 @@ export class PromptBuilder {
124
129
  }
125
130
  if (message || files.length) {
126
131
  const content = [];
127
- if (message)
132
+ if (message &&
133
+ // avoid duplicate user messages: developer may have already included the message in the custom user messages
134
+ !otherCustomMessages.some((i) => i.role === "user" &&
135
+ (typeof i.content === "string"
136
+ ? i.content.includes(message)
137
+ : i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
128
138
  content.push({ type: "text", text: message });
139
+ }
129
140
  if (files.length)
130
141
  content.push(...files);
131
- messages.push({ role: "user", content });
142
+ if (content.length) {
143
+ messages.push({ role: "user", content });
144
+ }
132
145
  }
133
- return messages;
146
+ messages.push(...otherCustomMessages);
147
+ return this.refineMessages(options, messages);
148
+ }
149
+ refineMessages(options, messages) {
150
+ const { autoReorderSystemMessages, autoMergeSystemMessages } = options.agent ?? {};
151
+ if (!autoReorderSystemMessages && !autoMergeSystemMessages)
152
+ return messages;
153
+ const [systemMessages, otherMessages] = partition(messages, (m) => m.role === "system");
154
+ if (!autoMergeSystemMessages) {
155
+ return systemMessages.concat(otherMessages);
156
+ }
157
+ const result = [];
158
+ if (systemMessages.length) {
159
+ result.push({
160
+ role: "system",
161
+ content: systemMessages
162
+ .map((i) => typeof i.content === "string"
163
+ ? i.content
164
+ : i.content
165
+ ?.map((c) => (c.type === "text" ? c.text : null))
166
+ .filter(isNonNullable)
167
+ .join("\n"))
168
+ .join("\n"),
169
+ });
170
+ }
171
+ return result.concat(otherMessages);
134
172
  }
135
173
  async convertMemoriesToMessages(memories, options) {
136
174
  const messages = [];
@@ -213,11 +251,14 @@ export class PromptBuilder {
213
251
  }
214
252
  : undefined;
215
253
  }
216
- buildTools(options) {
254
+ async buildTools(options) {
217
255
  const toolAgents = unique((options.context?.skills ?? [])
218
256
  .concat(options.agent?.skills ?? [])
219
257
  .concat(options.agent?.memoryAgentsAsTools ? options.agent.memories : [])
220
258
  .flatMap((i) => (i.isInvokable ? i : i.skills)), (i) => i.name);
259
+ if (options.agent?.afs) {
260
+ toolAgents.push(...(await getAFSSkills(options.agent.afs)));
261
+ }
221
262
  const tools = toolAgents.map((i) => ({
222
263
  type: "function",
223
264
  function: {
@@ -0,0 +1,2 @@
1
+ import type { AFS } from "@aigne/afs";
2
+ export declare function getAFSSystemPrompt(afs: AFS): Promise<string>;
@@ -0,0 +1,20 @@
1
+ import { stringify } from "yaml";
2
+ export async function getAFSSystemPrompt(afs) {
3
+ return `\
4
+
5
+ <afs_usage>
6
+ AFS (AIGNE File System) provides tools to interact with a virtual file system, allowing you to list, search, read, and write files. Use these tools to manage and retrieve files as needed.
7
+
8
+ Modules:
9
+ ${stringify(await afs.listModules())}
10
+
11
+ Available Tools:
12
+ 1. afs_list: Browse directory contents like filesystem ls/tree command - shows files and folders in a given path
13
+ 2. afs_search: Find files by content keywords - use specific keywords related to what you're looking for
14
+ 3. afs_read: Read file contents - path must be an exact file path from list or search results
15
+ 4. afs_write: Write content to a file in the AFS
16
+
17
+ Workflow: Use afs_list to browse directories, afs_search to find specific content, then afs_read to access file contents.
18
+ </afs_usage>
19
+ `;
20
+ }
@@ -0,0 +1,3 @@
1
+ import type { AFS } from "@aigne/afs";
2
+ import { type Agent } from "../../agents/agent.js";
3
+ export declare function getAFSSkills(afs: AFS): Promise<Agent[]>;
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { FunctionAgent } from "../../agents/agent.js";
3
+ export async function getAFSSkills(afs) {
4
+ return [
5
+ FunctionAgent.from({
6
+ name: "afs_list",
7
+ description: "Browse directory contents in the AFS like filesystem ls/tree command - shows files and folders in the specified path",
8
+ inputSchema: z.object({
9
+ path: z.string().describe("The directory path to browse (e.g., '/', '/docs', '/src')"),
10
+ options: z
11
+ .object({
12
+ recursive: z.boolean().optional().describe("Whether to list files recursively"),
13
+ maxDepth: z.number().optional().describe("Maximum depth to list files"),
14
+ limit: z.number().optional().describe("Maximum number of entries to return"),
15
+ })
16
+ .optional(),
17
+ }),
18
+ process: async (input) => {
19
+ return afs.list(input.path, input.options);
20
+ },
21
+ }),
22
+ FunctionAgent.from({
23
+ name: "afs_search",
24
+ description: "Find files by searching content using keywords - returns matching files with their paths",
25
+ inputSchema: z.object({
26
+ path: z.string().describe("The directory path to search in (e.g., '/', '/docs')"),
27
+ query: z
28
+ .string()
29
+ .describe("Keywords to search for in file contents (e.g., 'function authentication', 'database config')"),
30
+ options: z
31
+ .object({
32
+ limit: z.number().optional().describe("Maximum number of entries to return"),
33
+ })
34
+ .optional(),
35
+ }),
36
+ process: async (input) => {
37
+ return afs.search(input.path, input.query, input.options);
38
+ },
39
+ }),
40
+ FunctionAgent.from({
41
+ name: "afs_read",
42
+ description: "Read file contents from the AFS - path must be an exact file path from list or search results",
43
+ inputSchema: z.object({
44
+ path: z
45
+ .string()
46
+ .describe("Exact file path from list or search results (e.g., '/docs/api.md', '/src/utils/helper.js')"),
47
+ }),
48
+ process: async (input) => {
49
+ const file = await afs.read(input.path);
50
+ return {
51
+ file,
52
+ };
53
+ },
54
+ }),
55
+ FunctionAgent.from({
56
+ name: "afs_write",
57
+ description: "Create or update a file in the AFS with new content - overwrites existing files",
58
+ inputSchema: z.object({
59
+ path: z
60
+ .string()
61
+ .describe("Full file path where to write content (e.g., '/docs/new-file.md', '/src/component.js')"),
62
+ content: z.string().describe("The text content to write to the file"),
63
+ }),
64
+ process: async (input) => {
65
+ const file = await afs.write(input.path, {
66
+ content: input.content,
67
+ });
68
+ return { file };
69
+ },
70
+ }),
71
+ ];
72
+ }
@@ -27,3 +27,4 @@ export declare function createAccessorArray<T>(array: T[], accessor: (array: T[]
27
27
  export declare function checkArguments<T extends ZodType>(prefix: string, schema: T, args: unknown): z.infer<T>;
28
28
  export declare function tryOrThrow<P extends PromiseOrValue<unknown>>(fn: () => P, error: string | Error | ((error: Error) => Error)): P;
29
29
  export declare function tryOrThrow<P extends PromiseOrValue<unknown>>(fn: () => P, error?: Nullish<string | Error | ((error: Error) => Nullish<Error>)>): P | undefined;
30
+ export declare function partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]];
@@ -177,3 +177,16 @@ export function tryOrThrow(fn, error) {
177
177
  throw error;
178
178
  }
179
179
  }
180
+ export function partition(array, predicate) {
181
+ const pass = [];
182
+ const fail = [];
183
+ for (const item of array) {
184
+ if (predicate(item)) {
185
+ pass.push(item);
186
+ }
187
+ else {
188
+ fail.push(item);
189
+ }
190
+ }
191
+ return [pass, fail];
192
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.63.0-beta",
3
+ "version": "1.63.0-beta.10",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -92,9 +92,9 @@
92
92
  "zod": "^3.25.67",
93
93
  "zod-from-json-schema": "^0.0.5",
94
94
  "zod-to-json-schema": "^3.24.6",
95
- "@aigne/observability-api": "^0.11.2-beta",
95
+ "@aigne/observability-api": "^0.11.2-beta.5",
96
96
  "@aigne/platform-helpers": "^0.6.3",
97
- "@aigne/afs": "^1.0.0"
97
+ "@aigne/afs": "^1.1.0-beta"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@types/bun": "^1.2.22",