@tabbybyte/kimten 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The Project Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # 🐈 kimten
2
+
3
+ A micro-agent library: thin wrapper over the **[Agent interface in Vercel AI SDK Core v6+](https://ai-sdk.dev/docs/agents)**
4
+
5
+ Small surface area, sharp claws, zero fluff (well… almost).
6
+
7
+ Think:
8
+
9
+ > minimal agent loop + tools + short-term memory
10
+ > but delivered as a smol terminal cat 🐾
11
+
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.
14
+
15
+ No planners.
16
+ No graphs.
17
+ No magic state machines.
18
+ Just *play → result → nap*. 😼
19
+
20
+ ---
21
+
22
+ ## ✨ Why Kimten?
23
+
24
+ Sometimes you don’t want:
25
+
26
+ - 15 abstractions
27
+ - 6 middlewares
28
+ - 4 “agent runtimes”
29
+ - 200MB of dependencies
30
+
31
+ You just want:
32
+
33
+ ✔ call an LLM
34
+ ✔ give it tools
35
+ ✔ keep a bit of convo memory
36
+ ✔ maybe get structured output
37
+ ✔ done
38
+
39
+ Kimten = **tiny agent loop with paws** 🐾
40
+
41
+ Perfect for:
42
+
43
+ - CLI helpers
44
+ - small automations
45
+ - local tools
46
+ - scripting
47
+ - quick AI utilities
48
+ - “just let the model call a function” use cases
49
+
50
+ ---
51
+
52
+ ## 📦 Install
53
+
54
+ Feed the cat some treats:
55
+
56
+ ```bash
57
+ npm i kimten ai zod @ai-sdk/openai
58
+ ```
59
+
60
+ That’s it. No ceremony. No rituals. 🍗
61
+
62
+ ---
63
+
64
+ ## 🚀 Usage
65
+
66
+ Summon your little helper (with or without `toys`) and let it `play`.
67
+
68
+ ```js
69
+ import { openai } from '@ai-sdk/openai'; // or, any other provider
70
+ import { z } from 'zod';
71
+ import Kimten from 'kimten';
72
+
73
+ const cat = Kimten({
74
+ brain: openai('gpt-4o-mini'), // or, any other available model
75
+
76
+ toys: {
77
+ add: async ({ a, b }) => a + b,
78
+ },
79
+
80
+ personality: 'Helpful terminal cat',
81
+
82
+ hops: 10,
83
+ });
84
+
85
+ // free-form text
86
+ const text = await cat.play('summarize this repo');
87
+
88
+ // structured output
89
+ const structured = await cat.play(
90
+ 'extract the name',
91
+ z.object({ name: z.string() })
92
+ );
93
+
94
+ // wipe short-term memory
95
+ cat.forget();
96
+ ```
97
+
98
+ Done.
99
+ No lifecycle hooks. No config jungle. 🧘
100
+
101
+ ---
102
+
103
+ ## 🧠 Mental Model
104
+
105
+ Kimten is basically:
106
+
107
+ ```
108
+ loop:
109
+ include short-term conversation memory
110
+ prompt LLM
111
+ maybe call a tool
112
+ repeat (max hops)
113
+ return result
114
+ ```
115
+
116
+ That’s the whole thing.
117
+
118
+ Each instance keeps a **small, short-term chat memory** 🧠
119
+ So follow-up prompts naturally reference earlier messages:
120
+
121
+ > “summarize this” → “make it shorter” → “now extract bullets”
122
+
123
+ When you’re done, call `forget()` and the brain goes blank again. 🫧
124
+
125
+ It’s intentionally:
126
+
127
+ * tiny
128
+ * predictable
129
+ * hackable
130
+ * easy to read in one sitting
131
+
132
+ If you can read the source in ~5 minutes, we did it right 😺
133
+
134
+ ---
135
+
136
+ ## ⚙️ API
137
+
138
+ ### `Kimten(config)`
139
+
140
+ Create a new cat.
141
+
142
+ ### Required (must-haves)
143
+
144
+ * `brain` → AI SDK model instance
145
+
146
+ ### Optional (extra whiskers)
147
+
148
+ * `toys` → object map of async functions (tools the model may call), default: `{}`
149
+ * `personality` → system prompt / behavior description (default: `"You are a helpful assistant."`)
150
+ * `hops` → max agent loop steps (default: `10`)
151
+ prevents infinite zoomies 🌀
152
+
153
+ ### Returns
154
+
155
+ * `play(input, schema?)`
156
+
157
+ * runs the agent
158
+ * uses short-term memory automatically
159
+ * optional Zod schema for structured output
160
+
161
+ * `forget()`
162
+
163
+ * clears short-term memory/context
164
+
165
+ ---
166
+
167
+ ## 🧩 Design Philosophy
168
+
169
+ Kimten intentionally avoids “big agent framework energy”.
170
+
171
+ No:
172
+
173
+ * streaming APIs
174
+ * planners or graphs
175
+ * middleware/plugins
176
+ * long-term memory
177
+ * persistence/storage
178
+ * hidden background processes
179
+ * TypeScript runtime/build nonsense
180
+
181
+ If you need those… use something heavier.
182
+
183
+ If you want **simple + fast + composable**, Kimten fits nicely.
184
+
185
+ ---
186
+
187
+ ## 🛠 Tips
188
+
189
+ ### Providers & models
190
+
191
+ For the `brain` part, feel free to use any compatible provider and their models.
192
+
193
+ Refer to https://ai-sdk.dev/docs/foundations/providers-and-models
194
+
195
+ ### Add tools freely
196
+
197
+ Tools are just async functions:
198
+
199
+ ```js
200
+ toys: {
201
+ readFile,
202
+ writeFile,
203
+ fetchJson,
204
+ runCommand,
205
+ }
206
+ ```
207
+
208
+ The model decides when to use them.
209
+
210
+ ### Structured output = sanity
211
+
212
+ Use Zod schemas whenever possible.
213
+ LLMs lie less when types exist 😼
214
+
215
+ ### Keep hops low
216
+
217
+ If you need 50+ steps, you probably want a planner, not Kimten.
218
+
219
+ ### Reset when needed
220
+
221
+ Fresh task? Call `forget()`.
222
+ Cats don’t hold grudges (or context). 🐾
223
+
224
+ ---
225
+
226
+ ## 🐾 Vibes
227
+
228
+ Kimten is:
229
+
230
+ * small
231
+ * opinionated
232
+ * dependency-light
233
+ * short-memory by design
234
+ * easy to embed anywhere
235
+
236
+ It’s not trying to be LangChain or a full orchestration system.
237
+
238
+ It’s just a cat.
239
+
240
+ A helpful one.
241
+
242
+ In your terminal.
243
+
244
+ Typing. 🐈‍⬛
245
+
246
+ ---
247
+
248
+ ## License
249
+
250
+ MIT
251
+ Pet responsibly.
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { Kimten } from './lib/kimten.js';
2
+
3
+ export { Kimten };
4
+ export default Kimten;
package/lib/kimten.js ADDED
@@ -0,0 +1,106 @@
1
+ import { ToolLoopAgent, stepCountIs, Output } from 'ai';
2
+ import { createMemory } from './memory.js';
3
+ import { buildMessages } from './prompt.js';
4
+ import { normalizeToys } from './tools.js';
5
+
6
+ const DEFAULT_PERSONALITY = 'You are a helpful assistant.';
7
+
8
+ function validateConfig(config) {
9
+ if (config === null || typeof config !== 'object' || Array.isArray(config)) {
10
+ throw new TypeError('Kimten requires a config object.');
11
+ }
12
+
13
+ const { brain, toys = {}, personality = null, hops = 10 } = config;
14
+
15
+ if (!brain || typeof brain !== 'object') {
16
+ throw new TypeError('Kimten config "brain" is required and must be an AI SDK model instance.');
17
+ }
18
+
19
+ const resolvedPersonality = personality ?? DEFAULT_PERSONALITY;
20
+ if (typeof resolvedPersonality !== 'string' || resolvedPersonality.trim() === '') {
21
+ throw new TypeError('Kimten config "personality" must be a non-empty string when provided.');
22
+ }
23
+
24
+ if (!Number.isInteger(hops) || hops <= 0) {
25
+ throw new TypeError('Kimten config "hops" must be a positive integer.');
26
+ }
27
+
28
+ return {
29
+ brain,
30
+ toys,
31
+ personality: resolvedPersonality,
32
+ hops,
33
+ };
34
+ }
35
+
36
+ export function Kimten(config) {
37
+ const { brain, toys, personality, hops } = validateConfig(config);
38
+ const memory = createMemory();
39
+ const tools = normalizeToys(toys);
40
+ const structuredAgents = new WeakMap(); // Cache for agents based on output schema
41
+
42
+ const textAgent = new ToolLoopAgent({
43
+ model: brain,
44
+ instructions: personality,
45
+ tools,
46
+ stopWhen: stepCountIs(hops),
47
+ });
48
+
49
+ function createStructuredAgent(schema) {
50
+ return new ToolLoopAgent({
51
+ model: brain,
52
+ instructions: personality,
53
+ tools,
54
+ stopWhen: stepCountIs(hops),
55
+ output: Output.object({ schema }),
56
+ });
57
+ }
58
+
59
+ function getStructuredAgent(schema) {
60
+ if (schema !== null && (typeof schema === 'object' || typeof schema === 'function')) {
61
+ const cached = structuredAgents.get(schema);
62
+ if (cached) {
63
+ return cached;
64
+ }
65
+
66
+ const created = createStructuredAgent(schema);
67
+ structuredAgents.set(schema, created);
68
+ return created;
69
+ }
70
+
71
+ return createStructuredAgent(schema);
72
+ }
73
+
74
+ async function play(input, schema = null) {
75
+ if (typeof input !== 'string') {
76
+ throw new TypeError('Kimten play(input) expects input to be a string.');
77
+ }
78
+
79
+ memory.add({ role: 'user', content: input });
80
+
81
+ const agent = schema ? getStructuredAgent(schema) : textAgent;
82
+ const result = await agent.generate({
83
+ messages: buildMessages(personality, memory.list()),
84
+ });
85
+
86
+ const assistantContent =
87
+ schema
88
+ ? (typeof result.text === 'string' && result.text.trim() !== ''
89
+ ? result.text
90
+ : JSON.stringify(result.output ?? null))
91
+ : (typeof result.text === 'string' ? result.text : '');
92
+
93
+ memory.add({ role: 'assistant', content: assistantContent });
94
+
95
+ return schema ? result.output : assistantContent;
96
+ }
97
+
98
+ function forget() {
99
+ memory.clear();
100
+ }
101
+
102
+ return {
103
+ play,
104
+ forget,
105
+ };
106
+ }
package/lib/memory.js ADDED
@@ -0,0 +1,26 @@
1
+ export const MEMORY_LIMIT = 10;
2
+
3
+ export function createMemory(limit = MEMORY_LIMIT) {
4
+ const history = [];
5
+
6
+ function add(message) {
7
+ history.push(message);
8
+ if (history.length > limit) {
9
+ history.shift();
10
+ }
11
+ }
12
+
13
+ function list() {
14
+ return history.slice();
15
+ }
16
+
17
+ function clear() {
18
+ history.length = 0;
19
+ }
20
+
21
+ return {
22
+ add,
23
+ list,
24
+ clear,
25
+ };
26
+ }
package/lib/prompt.js ADDED
@@ -0,0 +1,6 @@
1
+ export function buildMessages(personality, history) {
2
+ return [
3
+ { role: 'system', content: personality },
4
+ ...history,
5
+ ];
6
+ }
package/lib/tools.js ADDED
@@ -0,0 +1,58 @@
1
+ import { tool } from 'ai';
2
+ import { z } from 'zod';
3
+
4
+ function isPlainObject(value) {
5
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
6
+ return false;
7
+ }
8
+
9
+ const proto = Object.getPrototypeOf(value);
10
+ return proto === Object.prototype || proto === null;
11
+ }
12
+
13
+ export function normalizeToys(toys) {
14
+ if (toys === undefined || toys === null) {
15
+ return {};
16
+ }
17
+
18
+ if (!isPlainObject(toys)) {
19
+ throw new TypeError('Kimten config "toys" must be an object map of functions.');
20
+ }
21
+
22
+ const wrapped = {};
23
+
24
+ for (const [name, fn] of Object.entries(toys)) {
25
+ if (typeof fn !== 'function') {
26
+ throw new TypeError(`Kimten tool "${name}" must be a function.`);
27
+ }
28
+
29
+ wrapped[name] = tool({
30
+ inputSchema: z.any(),
31
+ async execute(args) {
32
+ try {
33
+ const result = await fn(args);
34
+ return toJsonSafe(result);
35
+ } catch (error) {
36
+ return {
37
+ error: error instanceof Error ? error.message : String(error),
38
+ toolName: name,
39
+ };
40
+ }
41
+ },
42
+ });
43
+ }
44
+
45
+ return wrapped;
46
+ }
47
+
48
+ function toJsonSafe(value) {
49
+ if (value === undefined) {
50
+ return null;
51
+ }
52
+
53
+ try {
54
+ return JSON.parse(JSON.stringify(value));
55
+ } catch {
56
+ return { value: String(value) };
57
+ }
58
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@tabbybyte/kimten",
3
+ "version": "0.1.1",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "A micro-agent library: thin wrapper over the Agent interface from Vercel's AI SDK Core v6+",
8
+ "type": "module",
9
+ "main": "index.js",
10
+ "exports": {
11
+ ".": "./index.js"
12
+ },
13
+ "files": [
14
+ "index.js",
15
+ "lib"
16
+ ],
17
+ "scripts": {
18
+ "test": "node --test"
19
+ },
20
+ "keywords": [
21
+ "ai",
22
+ "agent",
23
+ "llm",
24
+ "tools",
25
+ "vercel-ai"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/tabbybyte-technologies/kimten.git"
30
+ },
31
+ "peerDependencies": {
32
+ "ai": ">=6",
33
+ "zod": ">=3"
34
+ },
35
+ "devDependencies": {
36
+ "ai": "^6.0.0-beta.89",
37
+ "zod": "^3.25.76"
38
+ },
39
+ "engines": {
40
+ "node": ">=22"
41
+ },
42
+ "license": "MIT"
43
+ }