@arki-moe/agent-ts 1.0.2 → 2.0.0

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/README.md CHANGED
@@ -19,18 +19,16 @@ const getTimeTool: Tool = {
19
19
  const agent = new Agent("openai", {
20
20
  apiKey: "sk-...",
21
21
  model: "gpt-5-nano",
22
- system: "You are a helpful assistant. Reply concisely.", // optional: system role
22
+ system: "You are a helpful assistant. Reply concisely.",
23
+ onToolCall: (msg) => console.log("tool call:", msg),
24
+ onToolResult: (msg) => console.log("tool result:", msg),
23
25
  });
24
26
  agent.registerTool(getTimeTool);
25
27
 
26
28
  // run: Executes tool chain automatically, returns new messages, context is maintained automatically
27
- const msgs = await agent.run({ role: Role.User, content: "What time is it?" });
29
+ const msgs = await agent.run("What time is it?");
28
30
  console.log(msgs);
29
31
 
30
- // step: Single-step inference, returns new messages from the model
31
- const msgs2 = await agent.step({ role: Role.User, content: "Hello" });
32
- console.log(msgs2);
33
-
34
32
  // context is a public property that can be read directly
35
33
  console.log(agent.context);
36
34
  ```
@@ -46,15 +44,24 @@ When `apiKey` is not provided in config, adapters read from the corresponding en
46
44
 
47
45
  ## API
48
46
 
49
- - `Agent(adapterName, config)` - Create Agent, config contains `apiKey`, `model`, `system` (optional), etc.
47
+ - `Agent(adapterName, config)` - Create Agent
50
48
  - `agent.context` - Public property, complete conversation history
51
49
  - `agent.registerTool(tool)` - Register tool
52
- - `agent.step(message?)` - Call model once, returns new `Message[]`
53
- - `agent.run(message, endCondition?)` - Execute tool chain automatically, returns all new `Message[]`
50
+ - `agent.run(message)` - Execute tool chain automatically, returns all new `Message[]`
54
51
  - `agent.fork()` - Create a new agent with a copied context
55
52
 
56
- `agent.step` and `agent.run` always append new messages to `agent.context`.
57
- `endCondition` receives `(context, last)` and stops the run when it returns `true`. Defaults to `last.role === Role.Ai`.
53
+ ### Config
54
+
55
+ | Field | Type | Description |
56
+ |-------|------|-------------|
57
+ | `apiKey` | `string` | API key (or use env var) |
58
+ | `model` | `string` | Model name |
59
+ | `system` | `string` | Optional system prompt |
60
+ | `endCondition` | `(context, last) => boolean` | Stop condition for `run`. Defaults to `last.role === Role.Ai` |
61
+ | `onToolCall` | `(message) => void \| Promise<void>` | Called before each tool execution |
62
+ | `onToolResult` | `(message) => void \| Promise<void>` | Called after each tool execution |
63
+
64
+ `agent.run` always appends new messages to `agent.context`. Multiple tool calls in a single model response are executed in parallel.
58
65
 
59
66
  `agent.fork()` shallow-copies the context array, but message objects are shared. This means:
60
67
  - Shallow copy: `forked.context !== agent.context`, so pushing new messages does not affect the other agent.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { Context, Message, Tool } from "./types";
1
+ import type { AgentConfig, Context, Message, Tool } from "./types";
2
2
  export { openaiAdapter } from "./adapter/openai";
3
3
  export { openrouterAdapter } from "./adapter/openrouter";
4
- export type { Adapter, Context, Message, Tool } from "./types";
4
+ export type { Adapter, AgentConfig, Context, Message, Tool } from "./types";
5
5
  export { Role } from "./types";
6
6
  export declare class Agent {
7
7
  context: Context;
@@ -9,9 +9,11 @@ export declare class Agent {
9
9
  private adapterName;
10
10
  private config;
11
11
  private tools;
12
- constructor(adapterName: string, config: Record<string, unknown>);
12
+ private endCondition;
13
+ private onToolCall?;
14
+ private onToolResult?;
15
+ constructor(adapterName: string, config: AgentConfig);
13
16
  registerTool(tool: Tool): void;
14
- step(message?: Message): Promise<Message[]>;
15
- run(message: Message, endCondition?: (context: Message[], last: Message) => boolean): Promise<Message[]>;
17
+ run(message: string): Promise<Message[]>;
16
18
  fork(): Agent;
17
19
  }
package/dist/index.js CHANGED
@@ -21,32 +21,32 @@ class Agent {
21
21
  this.adapterName = adapterName;
22
22
  this.adapter = adapters[adapterName] ?? (() => { throw new Error(`Adapter "${adapterName}" not found`); })();
23
23
  this.config = config;
24
+ this.endCondition = config.endCondition ?? ((_ctx, last) => last.role === types_1.Role.Ai);
25
+ this.onToolCall = config.onToolCall;
26
+ this.onToolResult = config.onToolResult;
24
27
  }
25
28
  registerTool(tool) {
26
29
  this.tools.push(tool);
27
30
  }
28
- async step(message) {
29
- if (message) {
30
- this.context.push(message);
31
- }
32
- const msgs = await this.adapter(this.config, this.context, this.tools);
33
- this.context.push(...msgs);
34
- return msgs;
35
- }
36
- async run(message, endCondition = (_context, last) => last.role === types_1.Role.Ai) {
31
+ async run(message) {
32
+ this.context.push({ role: types_1.Role.User, content: message });
37
33
  const all = [];
38
- let msgs = await this.step(message);
34
+ let msgs = await this.adapter(this.config, this.context, this.tools);
35
+ this.context.push(...msgs);
39
36
  all.push(...msgs);
40
37
  for (;;) {
41
38
  const last = msgs[msgs.length - 1];
42
- if (endCondition(this.context, last))
39
+ if (this.endCondition(this.context, last))
40
+ return all;
41
+ const toolCalls = msgs.filter((m) => m.role === types_1.Role.ToolCall);
42
+ if (toolCalls.length === 0)
43
43
  return all;
44
- for (const m of msgs) {
45
- if (m.role !== types_1.Role.ToolCall)
46
- continue;
44
+ const results = await Promise.all(toolCalls.map(async (m) => {
47
45
  const tool = this.tools.find((t) => t.name === m.toolName);
48
46
  if (!tool)
49
47
  throw new Error(`Tool "${m.toolName}" is not registered`);
48
+ if (this.onToolCall)
49
+ await Promise.resolve(this.onToolCall(m));
50
50
  let content;
51
51
  let isError = false;
52
52
  try {
@@ -59,10 +59,14 @@ class Agent {
59
59
  content = err instanceof Error ? err.message : String(err);
60
60
  }
61
61
  const result = { role: types_1.Role.ToolResult, callId: m.callId, content, isError };
62
- this.context.push(result);
63
- all.push(result);
64
- }
65
- msgs = await this.step();
62
+ if (this.onToolResult)
63
+ await Promise.resolve(this.onToolResult(result));
64
+ return result;
65
+ }));
66
+ this.context.push(...results);
67
+ all.push(...results);
68
+ msgs = await this.adapter(this.config, this.context, this.tools);
69
+ this.context.push(...msgs);
66
70
  all.push(...msgs);
67
71
  }
68
72
  }
package/dist/types.d.ts CHANGED
@@ -32,4 +32,10 @@ export type Tool = {
32
32
  parameters: unknown;
33
33
  execute: (args: unknown) => Promise<unknown> | unknown;
34
34
  };
35
+ export type AgentConfig = {
36
+ endCondition?: (context: Message[], last: Message) => boolean;
37
+ onToolCall?: (message: Message) => void | Promise<void>;
38
+ onToolResult?: (message: Message) => void | Promise<void>;
39
+ [key: string]: unknown;
40
+ };
35
41
  export type Adapter = (config: Record<string, unknown>, context: Message[], tools: Tool[]) => Promise<Message[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arki-moe/agent-ts",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
4
4
  "description": "Minimal Agent library, zero dependencies",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",