@tabbybyte/kimten 0.1.3 → 0.1.5

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
@@ -1,44 +1,38 @@
1
1
  # 🐈 kimten
2
+ 🐾 _**A tiny agent loop with paws**_ 🐾
2
3
 
3
- A micro-agent library: thin wrapper over the **[Agent interface in Vercel AI SDK Core v6+](https://ai-sdk.dev/docs/agents)**
4
+ [![build](https://github.com/tabbybyte-technologies/kimten/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/tabbybyte-technologies/kimten/actions/workflows/ci.yml)
5
+ [![codecov](https://codecov.io/gh/tabbybyte-technologies/kimten/branch/main/graph/badge.svg)](https://codecov.io/gh/tabbybyte-technologies/kimten)
6
+ [![npm](https://img.shields.io/npm/v/%40tabbybyte%2Fkimten)](https://www.npmjs.com/package/@tabbybyte/kimten)
7
+ [![last commit](https://img.shields.io/github/last-commit/tabbybyte-technologies/kimten)](https://github.com/tabbybyte-technologies/kimten/commits/main)
8
+ [![license](https://img.shields.io/npm/l/%40tabbybyte%2Fkimten)](LICENSE)
4
9
 
5
- Small surface area, sharp claws, zero fluff (well… almost).
6
10
 
7
- Think:
11
+ Kimten is a minimal micro-agent library: a thin wrapper over the **[Agent interface in Vercel AI SDK Core v6+](https://ai-sdk.dev/docs/agents)**.
8
12
 
9
- > minimal agent loop + tools + short-term memory
10
- > but delivered as a smol terminal cat 🐾
13
+ It’s meant to feel like a smart helper, not a framework.
11
14
 
12
- Kimten doesn’t try to be a “framework”.
13
- It’s just a neat little helper that runs prompts, calls tools, remembers a little, and gets out of your way.
15
+ ## What it does
14
16
 
15
- No planners.
16
- No graphs.
17
- No magic state machines.
18
- Just *play result nap*. 😼
17
+ - Runs a simple agent loop (bounded by `hops`)
18
+ - Lets the model call your tools (`toys`)
19
+ - Keeps short-term conversation memory (in-process, per instance)
20
+ - Supports optional structured output via Zod
19
21
 
20
- ---
21
-
22
- ## ✨ Why Kimten?
22
+ ## ❌ What it does *not* do
23
23
 
24
- Sometimes you don’t want:
24
+ - No planners/graphs/state machines
25
+ - No streaming API surface
26
+ - No persistence or long-term memory
27
+ - No plugin system or orchestration runtime
25
28
 
26
- - 15 abstractions
27
- - 6 middlewares
28
- - 4 “agent runtimes”
29
- - 200MB of dependencies
30
-
31
- You just want:
29
+ ---
32
30
 
33
- call an LLM
34
- ✔ give it tools
35
- ✔ keep a bit of convo memory
36
- ✔ maybe get structured output
37
- ✔ done
31
+ ## Why Kimten?
38
32
 
39
- Kimten = **tiny agent loop with paws** 🐾
33
+ Use it when you just want an agent loop with tools and a little memory, without adopting a larger framework.
40
34
 
41
- Perfect for:
35
+ Good fits:
42
36
 
43
37
  - CLI helpers
44
38
  - small automations
@@ -51,14 +45,10 @@ Perfect for:
51
45
 
52
46
  ## 📦 Install
53
47
 
54
- Feed the cat some treats:
55
-
56
48
  ```bash
57
49
  npm i @tabbybyte/kimten ai zod @ai-sdk/openai
58
50
  ```
59
51
 
60
- That’s it. No ceremony. No rituals. 🍗
61
-
62
52
  ### Requirements
63
53
 
64
54
  - Node `>=22`
@@ -69,8 +59,6 @@ That’s it. No ceremony. No rituals. 🍗
69
59
 
70
60
  ## 🚀 Usage
71
61
 
72
- Summon your little helper (with or without `toys`) and let it `play`.
73
-
74
62
  ```js
75
63
  import { openai } from '@ai-sdk/openai'; // or, any other provider
76
64
  import { z } from 'zod';
@@ -83,8 +71,6 @@ const cat = Kimten({
83
71
  add: async ({ a, b }) => a + b,
84
72
  },
85
73
 
86
- personality: 'Helpful terminal cat',
87
-
88
74
  hops: 10,
89
75
  });
90
76
 
@@ -101,9 +87,6 @@ const structured = await cat.play(
101
87
  cat.forget();
102
88
  ```
103
89
 
104
- Done.
105
- No lifecycle hooks. No config jungle. 🧘
106
-
107
90
  ---
108
91
 
109
92
  ## 🧠 Mental Model
@@ -119,37 +102,22 @@ loop:
119
102
  return result
120
103
  ```
121
104
 
122
- That’s the whole thing.
123
-
124
- Each instance keeps a **small, short-term chat memory** 🧠
125
- So follow-up prompts naturally reference earlier messages:
105
+ Each instance keeps short-term chat memory, so follow-up prompts naturally reference earlier messages:
126
106
 
127
107
  > “summarize this” → “make it shorter” → “now extract bullets”
128
108
 
129
- When you’re done, call `forget()` and the brain goes blank again. 🫧
130
-
131
- It’s intentionally:
132
-
133
- * tiny
134
- * predictable
135
- * hackable
136
- * easy to read in one sitting
137
-
138
- If you can read the source in ~5 minutes, we did it right 😺
139
-
140
109
  ---
141
110
 
142
111
  ## ⚙️ API
143
112
 
144
113
  ### `Kimten(config)`
145
114
 
146
- Create a new cat.
147
-
148
- ### Required (must-haves)
115
+ Create a new instance.
149
116
 
150
- * `brain` → AI SDK model instance
117
+ #### Required
118
+ * `brain` → AI SDK model instance
151
119
 
152
- ### Optional (extra whiskers)
120
+ #### Optional
153
121
 
154
122
  * `toys` → object map of tool definitions. Each entry can be:
155
123
  * async function shorthand: `async (args) => result`
@@ -159,13 +127,13 @@ Create a new cat.
159
127
  * `hops` → max agent loop steps (default: `10`)
160
128
  prevents infinite zoomies 🌀
161
129
 
162
- ### Tool semantics (important)
130
+ #### Tool semantics
163
131
 
164
132
  - Tool inputs are validated only if you provide `inputSchema` (shorthand tools accept anything).
165
133
  - Tool results should be JSON-serializable; `undefined` becomes `null`.
166
134
  - If a tool throws, Kimten returns `{ error, toolName }` as the tool result (it does not re-throw).
167
135
 
168
- ### Returns
136
+ #### Returns
169
137
 
170
138
  * `play(input, schema?)`
171
139
 
@@ -179,46 +147,17 @@ Create a new cat.
179
147
 
180
148
  ---
181
149
 
182
- ## 🧩 Design Philosophy & Vibes
183
-
184
- Kimten intentionally avoids “big agent framework energy”.
185
-
186
- It’s meant to be:
187
-
188
- * small
189
- * opinionated
190
- * dependency-light
191
- * short-term memory by design
192
- * easy to embed anywhere
193
-
194
- No:
195
-
196
- * streaming APIs
197
- * planners or graphs
198
- * middleware/plugins
199
- * long-term memory
200
- * persistence/storage
201
- * hidden background processes
202
- * TypeScript runtime/build nonsense
203
- * full fledged orchestration system
204
-
205
- If you need those… use something heavier.
206
-
207
- If you want **simple + fast + composable**, Kimten fits nicely.
208
-
209
- ---
210
-
211
150
  ## 🛠 Tips
212
151
 
213
152
  ### Providers & models
214
153
 
215
154
  For the `brain` part, feel free to use any compatible provider and their models.
216
155
 
217
- Refer to https://ai-sdk.dev/docs/foundations/providers-and-models
156
+ Refer to the AI SDK docs: **[providers and models](https://ai-sdk.dev/docs/foundations/providers-and-models)**.
218
157
 
219
158
  ### Add tools freely
220
159
 
221
- Tools can stay simple:
160
+ Tools can stay simple, just normal async functions:
222
161
 
223
162
  ```js
224
163
  toys: {
@@ -229,8 +168,6 @@ toys: {
229
168
  }
230
169
  ```
231
170
 
232
- The model decides when to use them.
233
-
234
171
  For stronger arg validation and better tool selection, use object form:
235
172
 
236
173
  ```js
@@ -246,27 +183,7 @@ toys: {
246
183
  },
247
184
  }
248
185
  ```
249
-
250
- ### Small “real” example
251
-
252
- ```js
253
- toys: {
254
- fetchJson: {
255
- description: 'Fetch JSON from a URL (GET).',
256
- inputSchema: z.object({ url: z.string().url() }),
257
- async execute({ url }) {
258
- const res = await fetch(url);
259
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
260
- return res.json();
261
- },
262
- },
263
- }
264
- ```
265
-
266
- ### Structured output = sanity
267
-
268
- Use Zod schemas whenever possible.
269
- LLMs lie less when types exist 😼
186
+ 💡 For further details, refer to [AI SDK docs on Tools](https://ai-sdk.dev/docs/foundations/tools)
270
187
 
271
188
  ### Keep hops low
272
189
 
@@ -275,11 +192,11 @@ If you need 50+ steps, you probably want a planner, not Kimten.
275
192
  ### Reset when needed
276
193
 
277
194
  Fresh task? Call `forget()`.
278
- Cats don’t hold grudges (or context). 🐾
195
+ Cats don’t hold grudges (or context).😽
279
196
 
280
197
  ---
281
198
 
282
199
  ## License
283
200
 
284
- MIT
285
- Pet responsibly.
201
+ [MIT](LICENSE)
202
+ Pet responsibly. 🐈‍⬛
package/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { ZodTypeAny, infer as ZodInfer } from 'zod';
2
+
3
+ export type BrainModel = Record<string, unknown>;
4
+
5
+ export type ToyFn = (args: any) => any | Promise<any>;
6
+
7
+ export type ToyDefinition = {
8
+ inputSchema?: ZodTypeAny;
9
+ description?: string;
10
+ strict?: boolean;
11
+ execute: ToyFn;
12
+ };
13
+
14
+ export type Toys = Record<string, ToyFn | ToyDefinition>;
15
+
16
+ export type KimtenConfig = {
17
+ brain: BrainModel;
18
+ toys?: Toys;
19
+ personality?: string;
20
+ hops?: number;
21
+ };
22
+
23
+ export type KimtenAgent = {
24
+ play(input: string): Promise<string>;
25
+ play<S extends ZodTypeAny>(input: string, schema: S): Promise<ZodInfer<S>>;
26
+ forget(): void;
27
+ };
28
+
29
+ export declare function Kimten(config: KimtenConfig): KimtenAgent;
30
+
31
+ declare const _default: typeof Kimten;
32
+ export default _default;
33
+
package/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @module @tabbybyte/kimten
3
+ *
4
+ * Public entrypoint for Kimten.
5
+ */
1
6
  import { Kimten } from './lib/kimten.js';
2
7
 
3
8
  export { Kimten };
package/lib/kimten.js CHANGED
@@ -5,6 +5,65 @@ import { normalizeToys } from './tools.js';
5
5
 
6
6
  const DEFAULT_PERSONALITY = 'You are a helpful assistant.';
7
7
 
8
+ /**
9
+ * @typedef {import('zod').ZodTypeAny} ZodSchema
10
+ */
11
+
12
+ /**
13
+ * A minimal AI SDK model-like object accepted by Kimten.
14
+ *
15
+ * This is intentionally loose to avoid coupling Kimten to AI SDK internal types,
16
+ * while still providing useful IDE hints.
17
+ *
18
+ * @typedef {object} BrainModel
19
+ * @property {string} [specificationVersion]
20
+ * @property {string} [provider]
21
+ * @property {string} [modelId]
22
+ * @property {Record<string, unknown>} [supportedUrls]
23
+ */
24
+
25
+ /**
26
+ * Shorthand tool form: any async/sync function.
27
+ *
28
+ * @callback ToyFn
29
+ * @param {any} args
30
+ * @returns {any | Promise<any>}
31
+ */
32
+
33
+ /**
34
+ * Object-form tool definition.
35
+ *
36
+ * @typedef {object} ToyDefinition
37
+ * @property {import('zod').ZodTypeAny} [inputSchema]
38
+ * @property {string} [description]
39
+ * @property {boolean} [strict]
40
+ * @property {ToyFn} execute
41
+ */
42
+
43
+ /**
44
+ * Tool registry map.
45
+ *
46
+ * @typedef {Record<string, ToyFn | ToyDefinition>} Toys
47
+ */
48
+
49
+ /**
50
+ * Kimten factory config.
51
+ *
52
+ * @typedef {object} KimtenConfig
53
+ * @property {BrainModel} brain AI SDK model instance.
54
+ * @property {Toys} [toys] Tool registry.
55
+ * @property {string} [personality] System prompt / instructions.
56
+ * @property {number} [hops] Max loop steps (prevents infinite loops).
57
+ */
58
+
59
+ /**
60
+ * Returned Kimten instance.
61
+ *
62
+ * @typedef {object} KimtenAgent
63
+ * @property {(input: string, schema?: ZodSchema | null) => Promise<any>} play Run the agent loop.
64
+ * @property {() => void} forget Clear short-term memory.
65
+ */
66
+
8
67
  function validateConfig(config) {
9
68
  if (config === null || typeof config !== 'object' || Array.isArray(config)) {
10
69
  throw new TypeError('Kimten requires a config object.');
@@ -33,6 +92,12 @@ function validateConfig(config) {
33
92
  };
34
93
  }
35
94
 
95
+ /**
96
+ * Create a tiny tool-using agent with short-term memory.
97
+ *
98
+ * @param {KimtenConfig} config
99
+ * @returns {KimtenAgent}
100
+ */
36
101
  export function Kimten(config) {
37
102
  const { brain, toys, personality, hops } = validateConfig(config);
38
103
  const memory = createMemory();
@@ -71,6 +136,16 @@ export function Kimten(config) {
71
136
  return createStructuredAgent(schema);
72
137
  }
73
138
 
139
+ /**
140
+ * Run the agent loop.
141
+ *
142
+ * - Stores the conversation in short-term memory (in-process, per instance).
143
+ * - If `schema` is provided, returns structured output (via AI SDK output mode).
144
+ *
145
+ * @param {string} input
146
+ * @param {ZodSchema | null} [schema]
147
+ * @returns {Promise<any>}
148
+ */
74
149
  async function play(input, schema = null) {
75
150
  if (typeof input !== 'string') {
76
151
  throw new TypeError('Kimten play(input) expects input to be a string.');
@@ -95,6 +170,11 @@ export function Kimten(config) {
95
170
  return schema ? result.output : assistantContent;
96
171
  }
97
172
 
173
+ /**
174
+ * Clear short-term memory for this instance.
175
+ *
176
+ * @returns {void}
177
+ */
98
178
  function forget() {
99
179
  memory.clear();
100
180
  }
package/lib/memory.js CHANGED
@@ -1,8 +1,32 @@
1
1
  export const MEMORY_LIMIT = 10;
2
2
 
3
+ /**
4
+ * A single chat message stored in short-term memory.
5
+ *
6
+ * @typedef {object} MemoryMessage
7
+ * @property {'system' | 'user' | 'assistant' | 'tool'} role
8
+ * @property {any} content
9
+ */
10
+
11
+ /**
12
+ * Short-term FIFO memory store (sliding window).
13
+ *
14
+ * @typedef {object} MemoryStore
15
+ * @property {(message: MemoryMessage) => void} add
16
+ * @property {() => MemoryMessage[]} list
17
+ * @property {() => void} clear
18
+ */
19
+
20
+ /**
21
+ * Create a short-term FIFO memory store.
22
+ *
23
+ * @param {number} [limit=MEMORY_LIMIT] Max number of messages to keep.
24
+ * @returns {MemoryStore}
25
+ */
3
26
  export function createMemory(limit = MEMORY_LIMIT) {
4
27
  const history = [];
5
28
 
29
+ /** @param {MemoryMessage} message */
6
30
  function add(message) {
7
31
  history.push(message);
8
32
  if (history.length > limit) {
package/lib/prompt.js CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Build AI SDK messages for a generation call.
3
+ *
4
+ * @param {string} personality System prompt / instructions.
5
+ * @param {Array<{ role: string, content: any }>} history Prior conversation messages.
6
+ * @returns {Array<{ role: string, content: any }>}
7
+ */
1
8
  export function buildMessages(personality, history) {
2
9
  return [
3
10
  { role: 'system', content: personality },
package/lib/tools.js CHANGED
@@ -1,6 +1,34 @@
1
1
  import { tool } from 'ai';
2
2
  import { z } from 'zod';
3
3
 
4
+ /**
5
+ * @typedef {import('zod').ZodTypeAny} ZodSchema
6
+ */
7
+
8
+ /**
9
+ * Shorthand tool form: any async/sync function.
10
+ *
11
+ * @callback ToyFn
12
+ * @param {any} args
13
+ * @returns {any | Promise<any>}
14
+ */
15
+
16
+ /**
17
+ * Object-form tool definition.
18
+ *
19
+ * @typedef {object} ToyDefinition
20
+ * @property {ZodSchema} [inputSchema]
21
+ * @property {string} [description]
22
+ * @property {boolean} [strict]
23
+ * @property {ToyFn} execute
24
+ */
25
+
26
+ /**
27
+ * Tool registry map.
28
+ *
29
+ * @typedef {Record<string, ToyFn | ToyDefinition>} Toys
30
+ */
31
+
4
32
  function isPlainObject(value) {
5
33
  if (value === null || typeof value !== 'object' || Array.isArray(value)) {
6
34
  return false;
@@ -10,6 +38,18 @@ function isPlainObject(value) {
10
38
  return proto === Object.prototype || proto === null;
11
39
  }
12
40
 
41
+ /**
42
+ * Normalize a toy registry into AI SDK tool objects.
43
+ *
44
+ * - Shorthand entry: `async (args) => result`
45
+ * - Object form: `{ inputSchema?, description?, strict?, execute }`
46
+ *
47
+ * Tool execution is wrapped so thrown errors become JSON-safe results:
48
+ * `{ error, toolName }` (Kimten does not re-throw tool errors).
49
+ *
50
+ * @param {Toys | null | undefined} toys
51
+ * @returns {Record<string, ReturnType<typeof tool>>}
52
+ */
13
53
  export function normalizeToys(toys) {
14
54
  if (toys === undefined || toys === null) {
15
55
  return {};
package/package.json CHANGED
@@ -1,21 +1,28 @@
1
1
  {
2
2
  "name": "@tabbybyte/kimten",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "description": "A micro-agent library: thin wrapper over the Agent interface from Vercel's AI SDK Core v6+",
8
8
  "type": "module",
9
9
  "main": "index.js",
10
+ "types": "index.d.ts",
10
11
  "exports": {
11
- ".": "./index.js"
12
+ ".": {
13
+ "types": "./index.d.ts",
14
+ "default": "./index.js"
15
+ }
12
16
  },
13
17
  "files": [
14
18
  "index.js",
19
+ "index.d.ts",
15
20
  "lib"
16
21
  ],
17
22
  "scripts": {
18
- "test": "node --test"
23
+ "test": "node --test",
24
+ "test:coverage": "node --test --experimental-test-coverage --test-coverage-include=lib/**/*.js --test-coverage-include=index.js --test-coverage-exclude=test/**/*.js --test-coverage-exclude=node_modules/**",
25
+ "test:coverage:ci": "node --test --experimental-test-coverage --test-coverage-include=lib/**/*.js --test-coverage-include=index.js --test-coverage-exclude=test/**/*.js --test-coverage-exclude=node_modules/** --test-coverage-lines=95 --test-coverage-functions=95 --test-coverage-branches=85"
19
26
  },
20
27
  "keywords": [
21
28
  "ai",