@arki-moe/agent-ts 1.0.1 → 1.0.3

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
@@ -35,29 +35,41 @@ console.log(msgs2);
35
35
  console.log(agent.context);
36
36
  ```
37
37
 
38
- ## OpenRouter
38
+ ## Supported Adapters
39
39
 
40
- ```ts
41
- import { Agent, Role } from "@arki-moe/agent-ts";
40
+ | Adapter | Required | Optional |
41
+ |---------|----------|----------|
42
+ | `openai` | `apiKey` (or `OPENAI_API_KEY` env), `model` | `system`, `baseUrl` |
43
+ | `openrouter` | `apiKey` (or `OPENROUTER_API_KEY` env), `model` | `system`, `baseUrl`, `httpReferer`, `title` |
42
44
 
43
- const agent = new Agent("openrouter", {
44
- apiKey: "or-...",
45
- model: "gpt-5-nano",
46
- httpReferer: "https://your-site.example", // optional
47
- title: "Your App", // optional
48
- });
49
-
50
- const msgs = await agent.step({ role: Role.User, content: "Hello" });
51
- console.log(msgs);
52
- ```
45
+ When `apiKey` is not provided in config, adapters read from the corresponding environment variable. An error is thrown only when both are missing.
53
46
 
54
47
  ## API
55
48
 
56
49
  - `Agent(adapterName, config)` - Create Agent, config contains `apiKey`, `model`, `system` (optional), etc.
57
50
  - `agent.context` - Public property, complete conversation history
58
51
  - `agent.registerTool(tool)` - Register tool
59
- - `agent.step(message)` - Call model once, returns new `Message[]`
60
- - `agent.run(message)` - Execute tool chain automatically, returns all new `Message[]`
52
+ - `agent.step(message?)` - Call model once, returns new `Message[]`
53
+ - `agent.run(message, endCondition?)` - Execute tool chain automatically, returns all new `Message[]`
54
+ - `agent.fork()` - Create a new agent with a copied context
55
+
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`.
58
+
59
+ `agent.fork()` shallow-copies the context array, but message objects are shared. This means:
60
+ - Shallow copy: `forked.context !== agent.context`, so pushing new messages does not affect the other agent.
61
+ - Shared messages: modifying a message object in one context will be visible in the other.
62
+
63
+ Example:
64
+
65
+ ```ts
66
+ const forked = agent.fork();
67
+ forked.context.push({ role: Role.User, content: "hi" });
68
+ // agent.context length is unchanged
69
+
70
+ forked.context[0].content = "changed";
71
+ // agent.context[0].content is also "changed" because messages are shared
72
+ ```
61
73
 
62
74
  ## Scripts
63
75
 
@@ -27,10 +27,10 @@ function toOpenAIMessages(context) {
27
27
  }
28
28
  async function openaiAdapter(config, context, tools) {
29
29
  const baseUrl = config.baseUrl ?? "https://api.openai.com";
30
- const apiKey = config.apiKey ?? "";
30
+ const apiKey = config.apiKey || process.env.OPENAI_API_KEY || "";
31
31
  const model = config.model ?? "gpt-5-nano";
32
32
  if (!apiKey)
33
- throw new Error("OpenAI adapter requires apiKey in config");
33
+ throw new Error("OpenAI adapter requires apiKey in config or OPENAI_API_KEY env");
34
34
  const contextMessages = toOpenAIMessages(context);
35
35
  const systemContent = config.system;
36
36
  const messages = systemContent
@@ -27,12 +27,12 @@ function toOpenRouterMessages(context) {
27
27
  }
28
28
  async function openrouterAdapter(config, context, tools) {
29
29
  const baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
30
- const apiKey = config.apiKey ?? "";
30
+ const apiKey = config.apiKey || process.env.OPENROUTER_API_KEY || "";
31
31
  const model = config.model ?? "gpt-5-nano";
32
32
  const httpReferer = config.httpReferer;
33
33
  const title = config.title;
34
34
  if (!apiKey)
35
- throw new Error("OpenRouter adapter requires apiKey in config");
35
+ throw new Error("OpenRouter adapter requires apiKey in config or OPENROUTER_API_KEY env");
36
36
  const contextMessages = toOpenRouterMessages(context);
37
37
  const systemContent = config.system;
38
38
  const messages = systemContent
package/dist/index.d.ts CHANGED
@@ -6,10 +6,12 @@ export { Role } from "./types";
6
6
  export declare class Agent {
7
7
  context: Context;
8
8
  private adapter;
9
+ private adapterName;
9
10
  private config;
10
11
  private tools;
11
12
  constructor(adapterName: string, config: Record<string, unknown>);
12
13
  registerTool(tool: Tool): void;
13
- step(message: Message): Promise<Message[]>;
14
- run(message: Message): Promise<Message[]>;
14
+ step(message?: Message): Promise<Message[]>;
15
+ run(message: Message, endCondition?: (context: Message[], last: Message) => boolean): Promise<Message[]>;
16
+ fork(): Agent;
15
17
  }
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ class Agent {
18
18
  constructor(adapterName, config) {
19
19
  this.context = [];
20
20
  this.tools = [];
21
+ this.adapterName = adapterName;
21
22
  this.adapter = adapters[adapterName] ?? (() => { throw new Error(`Adapter "${adapterName}" not found`); })();
22
23
  this.config = config;
23
24
  }
@@ -25,20 +26,20 @@ class Agent {
25
26
  this.tools.push(tool);
26
27
  }
27
28
  async step(message) {
28
- this.context.push(message);
29
+ if (message) {
30
+ this.context.push(message);
31
+ }
29
32
  const msgs = await this.adapter(this.config, this.context, this.tools);
30
33
  this.context.push(...msgs);
31
34
  return msgs;
32
35
  }
33
- async run(message) {
34
- this.context.push(message);
36
+ async run(message, endCondition = (_context, last) => last.role === types_1.Role.Ai) {
35
37
  const all = [];
38
+ let msgs = await this.step(message);
39
+ all.push(...msgs);
36
40
  for (;;) {
37
- const msgs = await this.adapter(this.config, this.context, this.tools);
38
- this.context.push(...msgs);
39
- all.push(...msgs);
40
41
  const last = msgs[msgs.length - 1];
41
- if (last.role === types_1.Role.Ai)
42
+ if (endCondition(this.context, last))
42
43
  return all;
43
44
  for (const m of msgs) {
44
45
  if (m.role !== types_1.Role.ToolCall)
@@ -61,7 +62,16 @@ class Agent {
61
62
  this.context.push(result);
62
63
  all.push(result);
63
64
  }
65
+ msgs = await this.step();
66
+ all.push(...msgs);
64
67
  }
65
68
  }
69
+ fork() {
70
+ const agent = new Agent(this.adapterName, this.config);
71
+ agent.adapter = this.adapter;
72
+ agent.tools = [...this.tools];
73
+ agent.context = [...this.context];
74
+ return agent;
75
+ }
66
76
  }
67
77
  exports.Agent = Agent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arki-moe/agent-ts",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Minimal Agent library, zero dependencies",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",