@tabbybyte/kimten 0.1.4 → 0.2.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
@@ -14,8 +14,8 @@ It’s meant to feel like a smart helper, not a framework.
14
14
 
15
15
  ## ✅ What it does
16
16
 
17
- - Runs a simple agent loop (bounded by `hops`)
18
- - Lets the model call your tools (`toys`)
17
+ - Runs a simple, single-agent loop (bounded by `hops`)
18
+ - Lets the LLM model (the brain) call your tool functions (the toys)
19
19
  - Keeps short-term conversation memory (in-process, per instance)
20
20
  - Supports optional structured output via Zod
21
21
 
@@ -24,19 +24,19 @@ It’s meant to feel like a smart helper, not a framework.
24
24
  - No planners/graphs/state machines
25
25
  - No streaming API surface
26
26
  - No persistence or long-term memory
27
- - No plugin system or orchestration runtime
27
+ - No plugin system or multi-agent orchestration
28
28
 
29
29
  ---
30
30
 
31
31
  ## ✨ Why Kimten?
32
32
 
33
- Use it when you just want an agent loop with tools and a little memory, without adopting a larger framework.
33
+ Use it when you just want an agent loop with toys and a little memory, without adopting a larger framework.
34
34
 
35
35
  Good fits:
36
36
 
37
37
  - CLI helpers
38
38
  - small automations
39
- - local tools
39
+ - local toys
40
40
  - scripting
41
41
  - quick AI utilities
42
42
  - “just let the model call a function” use cases
@@ -68,9 +68,19 @@ const cat = Kimten({
68
68
  brain: openai('gpt-4o-mini'), // or, any other available model
69
69
 
70
70
  toys: {
71
- add: async ({ a, b }) => a + b,
71
+ randomNumber: {
72
+ description: 'Generate a random integer between min and max (inclusive).',
73
+ inputSchema: z.object({ min: z.number().int(), max: z.number().int() }),
74
+ async execute({ min, max }) {
75
+ const low = Math.min(min, max);
76
+ const high = Math.max(min, max);
77
+ return Math.floor(Math.random() * (high - low + 1)) + low;
78
+ },
79
+ },
72
80
  },
73
81
 
82
+ personality: 'You are a helpful assistant.',
83
+
74
84
  hops: 10,
75
85
  });
76
86
 
@@ -119,19 +129,18 @@ Create a new instance.
119
129
 
120
130
  #### Optional
121
131
 
122
- * `toys` → object map of tool definitions. Each entry can be:
123
- * async function shorthand: `async (args) => result`
132
+ * `toys` → object map of toy (tool) definitions. Each entry is:
124
133
  * object form: `{ inputSchema?, description?, strict?, execute }`
125
134
  default: `{}`
126
- * `personality` → system prompt / behavior description (default: `"You are a helpful assistant."`)
127
- * `hops` → max agent loop steps (default: `10`)
128
- prevents infinite zoomies 🌀
135
+ * `personality` → system instructions / prompt for overall behavior description (default: `'You are a helpful assistant.'`)
136
+ * `hops` → max agent loop steps (default: `10`) - prevents infinite zoomies 🌀
129
137
 
130
- #### Tool semantics
138
+ #### Toy semantics
131
139
 
132
- - Tool inputs are validated only if you provide `inputSchema` (shorthand tools accept anything).
133
- - Tool results should be JSON-serializable; `undefined` becomes `null`.
134
- - If a tool throws, Kimten returns `{ error, toolName }` as the tool result (it does not re-throw).
140
+ - Toy inputs are validated only if you provide `inputSchema`.
141
+ - Toy results should be JSON-serializable; `undefined` becomes `null`.
142
+ - If a toy function throws, Kimten returns `{ error, toolName }` as the toy result (it does not re-throw).
143
+ - Under the hood, each toy is implemented as an AI SDK tool.
135
144
 
136
145
  #### Returns
137
146
 
@@ -153,32 +162,25 @@ Create a new instance.
153
162
 
154
163
  For the `brain` part, feel free to use any compatible provider and their models.
155
164
 
156
- Refer to the AI SDK docs: **[providers and models](https://ai-sdk.dev/docs/foundations/providers-and-models)**.
165
+ Note that not all providers (and models) may work out the box with Kimten, particularly for structured output.
157
166
 
158
- ### Add tools freely
167
+ 💡 Refer to the AI SDK docs: **[providers and models](https://ai-sdk.dev/docs/foundations/providers-and-models)**.
159
168
 
160
- Tools can stay simple, just normal async functions:
161
-
162
- ```js
163
- toys: {
164
- readFile,
165
- writeFile,
166
- fetchJson,
167
- runCommand,
168
- }
169
- ```
169
+ ### Add toys freely
170
170
 
171
- For stronger arg validation and better tool selection, use object form:
171
+ Define `toys` in object form for strong arg validation and proper selection by the LLM:
172
172
 
173
173
  ```js
174
174
  import { z } from 'zod';
175
175
 
176
176
  toys: {
177
- add: {
178
- description: 'Add two numbers.',
179
- inputSchema: z.object({ a: z.number(), b: z.number() }),
180
- async execute({ a, b }) {
181
- return a + b;
177
+ randomNumber: {
178
+ description: 'Generate a random integer between min and max (inclusive).',
179
+ inputSchema: z.object({ min: z.number().int(), max: z.number().int() }),
180
+ async execute({ min, max }) {
181
+ const low = Math.min(min, max);
182
+ const high = Math.max(min, max);
183
+ return Math.floor(Math.random() * (high - low + 1)) + low;
182
184
  },
183
185
  },
184
186
  }
package/index.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { ZodTypeAny, infer as ZodInfer } from 'zod';
2
+
3
+ export type BrainModel = Record<string, unknown>;
4
+
5
+ export type ToolExecute = (args: any) => any | Promise<any>;
6
+
7
+ export type ToyDefinition = {
8
+ inputSchema?: ZodTypeAny;
9
+ description?: string;
10
+ strict?: boolean;
11
+ execute: ToolExecute;
12
+ };
13
+
14
+ export type Toys = Record<string, 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;
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
@@ -1,10 +1,68 @@
1
1
  import { ToolLoopAgent, stepCountIs, Output } from 'ai';
2
2
  import { createMemory } from './memory.js';
3
- import { buildMessages } from './prompt.js';
4
3
  import { normalizeToys } from './tools.js';
5
4
 
6
5
  const DEFAULT_PERSONALITY = 'You are a helpful assistant.';
7
6
 
7
+ /**
8
+ * @typedef {import('zod').ZodTypeAny} ZodSchema
9
+ */
10
+
11
+ /**
12
+ * A minimal AI SDK model-like object accepted by Kimten.
13
+ *
14
+ * This is intentionally loose to avoid coupling Kimten to AI SDK internal types,
15
+ * while still providing useful IDE hints.
16
+ *
17
+ * @typedef {object} BrainModel
18
+ * @property {string} [specificationVersion]
19
+ * @property {string} [provider]
20
+ * @property {string} [modelId]
21
+ * @property {Record<string, unknown>} [supportedUrls]
22
+ */
23
+
24
+ /**
25
+ * Tool execute function.
26
+ *
27
+ * @callback ToolExecute
28
+ * @param {any} args
29
+ * @returns {any | Promise<any>}
30
+ */
31
+
32
+ /**
33
+ * Object-form tool definition.
34
+ *
35
+ * @typedef {object} ToyDefinition
36
+ * @property {import('zod').ZodTypeAny} [inputSchema]
37
+ * @property {string} [description]
38
+ * @property {boolean} [strict]
39
+ * @property {ToolExecute} execute
40
+ */
41
+
42
+ /**
43
+ * Tool registry map.
44
+ *
45
+ * @typedef {Record<string, ToyDefinition>} Toys
46
+ */
47
+
48
+ /**
49
+ * Kimten factory config.
50
+ *
51
+ * @typedef {object} KimtenConfig
52
+ * @property {BrainModel} brain AI SDK model instance.
53
+ * @property {Toys} [toys] Tool registry.
54
+ * @property {string} [personality] System prompt / instructions.
55
+ * @property {number} [hops] Max loop steps (prevents infinite loops).
56
+ */
57
+
58
+ /**
59
+ * Returned Kimten instance.
60
+ *
61
+ * @typedef {object} KimtenAgent
62
+ * @property {(input: string, schema?: ZodSchema | null) => Promise<any>} play Run the agent loop.
63
+ * @property {() => void} forget Clear short-term memory.
64
+ */
65
+
8
66
  function validateConfig(config) {
9
67
  if (config === null || typeof config !== 'object' || Array.isArray(config)) {
10
68
  throw new TypeError('Kimten requires a config object.');
@@ -33,6 +91,12 @@ function validateConfig(config) {
33
91
  };
34
92
  }
35
93
 
94
+ /**
95
+ * Create a tiny tool-using agent with short-term memory.
96
+ *
97
+ * @param {KimtenConfig} config
98
+ * @returns {KimtenAgent}
99
+ */
36
100
  export function Kimten(config) {
37
101
  const { brain, toys, personality, hops } = validateConfig(config);
38
102
  const memory = createMemory();
@@ -71,6 +135,16 @@ export function Kimten(config) {
71
135
  return createStructuredAgent(schema);
72
136
  }
73
137
 
138
+ /**
139
+ * Run the agent loop.
140
+ *
141
+ * - Stores the conversation in short-term memory (in-process, per instance).
142
+ * - If `schema` is provided, returns structured output (via AI SDK output mode).
143
+ *
144
+ * @param {string} input
145
+ * @param {ZodSchema | null} [schema]
146
+ * @returns {Promise<any>}
147
+ */
74
148
  async function play(input, schema = null) {
75
149
  if (typeof input !== 'string') {
76
150
  throw new TypeError('Kimten play(input) expects input to be a string.');
@@ -80,7 +154,7 @@ export function Kimten(config) {
80
154
 
81
155
  const agent = schema ? getStructuredAgent(schema) : textAgent;
82
156
  const result = await agent.generate({
83
- messages: buildMessages(personality, memory.list()),
157
+ messages: memory.list(),
84
158
  });
85
159
 
86
160
  const assistantContent =
@@ -95,6 +169,11 @@ export function Kimten(config) {
95
169
  return schema ? result.output : assistantContent;
96
170
  }
97
171
 
172
+ /**
173
+ * Clear short-term memory for this instance.
174
+ *
175
+ * @returns {void}
176
+ */
98
177
  function forget() {
99
178
  memory.clear();
100
179
  }
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/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
+ * Tool execute function.
10
+ *
11
+ * @callback ToolExecute
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 {ToolExecute} execute
24
+ */
25
+
26
+ /**
27
+ * Tool registry map.
28
+ *
29
+ * @typedef {Record<string, 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,13 +38,24 @@ 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
+ * - Object form only: `{ inputSchema?, description?, strict?, execute }`
45
+ *
46
+ * Tool execution is wrapped so thrown errors become JSON-safe results:
47
+ * `{ error, toolName }` (Kimten does not re-throw tool errors).
48
+ *
49
+ * @param {Toys | null | undefined} toys
50
+ * @returns {Record<string, ReturnType<typeof tool>>}
51
+ */
13
52
  export function normalizeToys(toys) {
14
53
  if (toys === undefined || toys === null) {
15
54
  return {};
16
55
  }
17
56
 
18
57
  if (!isPlainObject(toys)) {
19
- throw new TypeError('Kimten config "toys" must be an object map of functions or tool definitions.');
58
+ throw new TypeError('Kimten config "toys" must be an object map of tool definitions.');
20
59
  }
21
60
 
22
61
  const wrapped = {};
@@ -46,16 +85,9 @@ export function normalizeToys(toys) {
46
85
  }
47
86
 
48
87
  function normalizeToyDefinition(name, entry) {
49
- if (typeof entry === 'function') {
50
- return {
51
- inputSchema: z.any(),
52
- execute: entry,
53
- };
54
- }
55
-
56
88
  if (!isPlainObject(entry)) {
57
89
  throw new TypeError(
58
- `Kimten tool "${name}" must be a function or an object with execute(args).`
90
+ `Kimten tool "${name}" must be an object with execute(args).`
59
91
  );
60
92
  }
61
93
 
package/package.json CHANGED
@@ -1,17 +1,22 @@
1
1
  {
2
2
  "name": "@tabbybyte/kimten",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
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": {
package/lib/prompt.js DELETED
@@ -1,6 +0,0 @@
1
- export function buildMessages(personality, history) {
2
- return [
3
- { role: 'system', content: personality },
4
- ...history,
5
- ];
6
- }