@getenki/ai-linux-x64-gnu 0.2.6 → 0.2.7

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.
Files changed (2) hide show
  1. package/README.md +354 -83
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@getenki/ai`
2
2
 
3
- JavaScript bindings for Enki's Rust agent runtime, published as a native Node.js package via `napi-rs`.
3
+ Node.js bindings for Enki's Rust agent runtime, published as a native package via `napi-rs`.
4
4
 
5
5
  ## Install
6
6
 
@@ -14,30 +14,37 @@ The package ships prebuilt native binaries for:
14
14
  - macOS x64 and arm64
15
15
  - Linux x64 and arm64 (GNU libc)
16
16
 
17
- ## API
17
+ ## What It Exports
18
18
 
19
- The package exposes two layers:
19
+ The current package surface is intentionally small:
20
20
 
21
- - `EnkiAgent`: thin wrapper over the native runtime
22
- - `Agent`: higher-level JavaScript wrapper for tools, memories, and custom LLM providers
21
+ - `NativeEnkiAgent`
22
+ - `JsMemoryKind`
23
+ - `JsMemoryModule`
24
+ - `JsMemoryEntry`
23
25
 
24
- It also exports `NativeEnkiAgent`, `Tool`, `MemoryModule`, `MemoryBackend`, `LlmProviderBackend`, `RunContext`, and `AgentRunResult`.
26
+ `NativeEnkiAgent` is the main entrypoint. It can be created in four modes:
25
27
 
26
- ## `EnkiAgent`
28
+ - `new(...)` for a plain agent
29
+ - `NativeEnkiAgent.withTools(...)`
30
+ - `NativeEnkiAgent.withMemory(...)`
31
+ - `NativeEnkiAgent.withToolsAndMemory(...)`
27
32
 
28
- Use `EnkiAgent` when you want a simple session-oriented interface backed directly by the native runtime.
33
+ ## Basic Agent
34
+
35
+ Use the constructor when you only need a session-based agent backed by the native runtime.
29
36
 
30
37
  ```js
31
- const { EnkiAgent } = require('@getenki/ai')
38
+ const { NativeEnkiAgent } = require('@getenki/ai')
32
39
 
33
40
  async function main() {
34
- const agent = new EnkiAgent({
35
- name: 'Assistant',
36
- systemPromptPreamble: 'Answer clearly and keep responses short.',
37
- model: 'ollama::llama3.2:latest',
38
- maxIterations: 20,
39
- workspaceHome: process.cwd(),
40
- })
41
+ const agent = new NativeEnkiAgent(
42
+ 'Assistant',
43
+ 'Answer clearly and keep responses short.',
44
+ 'ollama::qwen3.5:latest',
45
+ 20,
46
+ process.cwd(),
47
+ )
41
48
 
42
49
  const output = await agent.run('session-1', 'Explain what this project does.')
43
50
  console.log(output)
@@ -46,7 +53,24 @@ async function main() {
46
53
  main().catch(console.error)
47
54
  ```
48
55
 
49
- Constructor options:
56
+ TypeScript version:
57
+
58
+ ```ts
59
+ import { NativeEnkiAgent } from '@getenki/ai'
60
+
61
+ const agent = new NativeEnkiAgent(
62
+ 'Assistant',
63
+ 'Answer clearly and keep responses short.',
64
+ 'ollama::qwen3.5:latest',
65
+ 20,
66
+ process.cwd(),
67
+ )
68
+
69
+ const output = await agent.run('session-1', 'Explain what this project does.')
70
+ console.log(output)
71
+ ```
72
+
73
+ Constructor arguments:
50
74
 
51
75
  - `name?: string`
52
76
  - `systemPromptPreamble?: string`
@@ -54,97 +78,350 @@ Constructor options:
54
78
  - `maxIterations?: number`
55
79
  - `workspaceHome?: string`
56
80
 
57
- ## `Agent`
81
+ If omitted, the runtime falls back to built-in defaults for name, prompt, and max iterations.
82
+
83
+ ## Tools
84
+
85
+ Tools can be attached with `NativeEnkiAgent.withTools(...)`. Each tool object must provide:
86
+
87
+ - `id` or `name`
88
+ - `description`
89
+ - one of `inputSchema`, `inputSchemaJson`, `parameters`, or `parametersJson`
90
+ - either `execute(inputJson, contextJson)` or a shared `toolHandler`
58
91
 
59
- Use `Agent` when you want to register JavaScript tools or plug in your own LLM provider.
92
+ Example:
60
93
 
61
94
  ```js
62
- const { Agent } = require('@getenki/ai')
95
+ const { NativeEnkiAgent } = require('@getenki/ai')
63
96
 
64
- async function main() {
65
- const agent = new Agent('demo-model', {
66
- instructions: 'You are a dice game.',
67
- workspaceHome: process.cwd(),
68
- })
97
+ const tools = [
98
+ {
99
+ id: 'calculate_sum',
100
+ description: 'Add two numbers and return a short text result.',
101
+ inputSchema: {
102
+ type: 'object',
103
+ properties: {
104
+ a: { type: 'number' },
105
+ b: { type: 'number' },
106
+ },
107
+ required: ['a', 'b'],
108
+ },
109
+ execute: (inputJson, contextJson) => {
110
+ const args = inputJson ? JSON.parse(inputJson) : {}
111
+ const ctx = contextJson ? JSON.parse(contextJson) : {}
112
+ const result = Number(args.a) + Number(args.b)
113
+
114
+ return JSON.stringify({
115
+ result,
116
+ workspaceDir: ctx.workspaceDir,
117
+ text: `${args.a} + ${args.b} = ${result}`,
118
+ })
119
+ },
120
+ },
121
+ ]
122
+
123
+ const agent = NativeEnkiAgent.withTools(
124
+ 'Tool Agent',
125
+ 'Use tools when they help.',
126
+ 'ollama::qwen3.5:latest',
127
+ 20,
128
+ process.cwd(),
129
+ tools,
130
+ null,
131
+ )
132
+ ```
133
+
134
+ Per-tool `execute` receives:
135
+
136
+ - `inputJson`: serialized tool arguments
137
+ - `contextJson`: serialized runtime context with `agentDir`, `workspaceDir`, and `sessionsDir`
69
138
 
70
- agent.toolPlain(
71
- function rollDice() {
72
- return '4'
139
+ TypeScript tool example:
140
+
141
+ ```ts
142
+ import { NativeEnkiAgent } from '@getenki/ai'
143
+
144
+ type SumArgs = {
145
+ a?: number
146
+ b?: number
147
+ }
148
+
149
+ type ExampleTool = {
150
+ id: string
151
+ description: string
152
+ inputSchema: Record<string, unknown>
153
+ execute: (inputJson: string, contextJson: string) => string
154
+ }
155
+
156
+ const tools: ExampleTool[] = [
157
+ {
158
+ id: 'calculate_sum',
159
+ description: 'Add two numbers and return a short text result.',
160
+ inputSchema: {
161
+ type: 'object',
162
+ properties: {
163
+ a: { type: 'number' },
164
+ b: { type: 'number' },
165
+ },
166
+ required: ['a', 'b'],
73
167
  },
74
- {
75
- description: 'Roll a six-sided die and return the result.',
76
- parametersJson: JSON.stringify({
77
- type: 'object',
78
- properties: {},
79
- additionalProperties: false,
80
- }),
168
+ execute: (inputJson: string, contextJson: string): string => {
169
+ const args = inputJson ? (JSON.parse(inputJson) as SumArgs) : {}
170
+ const ctx = contextJson
171
+ ? (JSON.parse(contextJson) as { workspaceDir?: string })
172
+ : {}
173
+ const result = Number(args.a) + Number(args.b)
174
+
175
+ return JSON.stringify({
176
+ result,
177
+ workspaceDir: ctx.workspaceDir,
178
+ text: `${args.a} + ${args.b} = ${result}`,
179
+ })
81
180
  },
82
- )
181
+ },
182
+ ]
183
+
184
+ const agent = NativeEnkiAgent.withTools(
185
+ 'Tool Agent',
186
+ 'Use tools when they help.',
187
+ 'ollama::qwen3.5:latest',
188
+ 20,
189
+ process.cwd(),
190
+ tools,
191
+ null,
192
+ )
193
+ ```
83
194
 
84
- const result = await agent.run('My guess is 4', {
85
- sessionId: 'session-tools-1',
86
- })
195
+ Instead of putting `execute` on every tool, you can pass a shared `toolHandler` as the final argument to `withTools(...)` or `withToolsAndMemory(...)`. The shared handler receives:
196
+
197
+ - `toolName`
198
+ - `inputJson`
199
+ - `agentDir`
200
+ - `workspaceDir`
201
+ - `sessionsDir`
202
+
203
+ ## Memory
204
+
205
+ Memory modules are plain objects:
206
+
207
+ ```js
208
+ const memories = [{ name: 'example-memory' }]
209
+ ```
210
+
211
+ When using `withMemory(...)` or `withToolsAndMemory(...)`, you supply four callbacks:
212
+
213
+ - `recordHandler(memoryName, sessionId, userMsg, assistantMsg)`
214
+ - `recallHandler(memoryName, sessionId, query, maxEntries)`
215
+ - `flushHandler(memoryName, sessionId)`
216
+ - `consolidateHandler(memoryName, sessionId)`
217
+
218
+ `recallHandler` must return an array of `JsMemoryEntry` objects:
87
219
 
88
- console.log(result.output)
220
+ ```ts
221
+ type JsMemoryEntry = {
222
+ key: string
223
+ content: string
224
+ kind: JsMemoryKind
225
+ relevance: number
226
+ timestampNs: string
89
227
  }
228
+ ```
90
229
 
91
- main().catch(console.error)
230
+ Supported memory kinds:
231
+
232
+ - `JsMemoryKind.RecentMessage`
233
+ - `JsMemoryKind.Summary`
234
+ - `JsMemoryKind.Entity`
235
+ - `JsMemoryKind.Preference`
236
+
237
+ TypeScript memory typing example:
238
+
239
+ ```ts
240
+ import {
241
+ JsMemoryKind,
242
+ type JsMemoryEntry,
243
+ type JsMemoryModule,
244
+ } from '@getenki/ai'
245
+
246
+ const memories: JsMemoryModule[] = [{ name: 'example-memory' }]
247
+ const memoryStore = new Map<string, JsMemoryEntry[]>()
248
+
249
+ function memoryKey(memoryName: string, sessionId: string): string {
250
+ return `${memoryName}:${sessionId}`
251
+ }
252
+
253
+ function getMemoryEntries(memoryName: string, sessionId: string): JsMemoryEntry[] {
254
+ const key = memoryKey(memoryName, sessionId)
255
+ const existing = memoryStore.get(key)
256
+ if (existing) {
257
+ return existing
258
+ }
259
+
260
+ const empty: JsMemoryEntry[] = []
261
+ memoryStore.set(key, empty)
262
+ return empty
263
+ }
264
+
265
+ const recordHandler = (
266
+ memoryName: string,
267
+ sessionId: string,
268
+ userMsg: string,
269
+ assistantMsg: string,
270
+ ): void => {
271
+ const entries = getMemoryEntries(memoryName, sessionId)
272
+ entries.push({
273
+ key: `entry-${entries.length + 1}`,
274
+ content: `User: ${userMsg}\nAssistant: ${assistantMsg}`,
275
+ kind: JsMemoryKind.RecentMessage,
276
+ relevance: 1,
277
+ timestampNs: `${Date.now() * 1000000}`,
278
+ })
279
+ }
92
280
  ```
93
281
 
94
- ### Context-aware tools
282
+ ## Tools And Memory Example
95
283
 
96
- `agent.tool()` injects a `RunContext` as the first argument so your tool can access runtime dependencies.
284
+ The repository examples in [`example/basic-js/index.js`](/I:/projects/enki/core-next/example/basic-js/index.js) and [`example/basic-ts/index.ts`](/I:/projects/enki/core-next/example/basic-ts/index.ts) use `NativeEnkiAgent.withToolsAndMemory(...)` with:
285
+
286
+ - a `calculate_sum` tool
287
+ - a `get_today` tool
288
+ - an in-memory `Map` for session memory storage
289
+
290
+ Minimal JavaScript version:
97
291
 
98
292
  ```js
99
- const { Agent } = require('@getenki/ai')
293
+ const { JsMemoryKind, NativeEnkiAgent } = require('@getenki/ai')
100
294
 
101
- const agent = new Agent('demo-model')
295
+ const tools = [
296
+ {
297
+ id: 'get_today',
298
+ description: 'Return the current local date in ISO format.',
299
+ inputSchema: { type: 'object', properties: {} },
300
+ execute: () => JSON.stringify({ today: new Date().toISOString().slice(0, 10) }),
301
+ },
302
+ ]
303
+
304
+ const memories = [{ name: 'example-memory' }]
305
+ const memoryStore = new Map()
102
306
 
103
- agent.tool(
104
- function getPlayerName(ctx) {
105
- return ctx.deps.playerName
307
+ function memoryKey(memoryName, sessionId) {
308
+ return `${memoryName}:${sessionId}`
309
+ }
310
+
311
+ const agent = NativeEnkiAgent.withToolsAndMemory(
312
+ 'Basic JS Agent',
313
+ 'Answer clearly and keep responses short.',
314
+ 'ollama::qwen3.5:latest',
315
+ 20,
316
+ process.cwd(),
317
+ tools,
318
+ null,
319
+ memories,
320
+ (memoryName, sessionId, userMsg, assistantMsg) => {
321
+ const key = memoryKey(memoryName, sessionId)
322
+ const entries = memoryStore.get(key) ?? []
323
+ entries.push({
324
+ key: `entry-${entries.length + 1}`,
325
+ content: `User: ${userMsg}\nAssistant: ${assistantMsg}`,
326
+ kind: JsMemoryKind.RecentMessage,
327
+ relevance: 1,
328
+ timestampNs: `${Date.now() * 1000000}`,
329
+ })
330
+ memoryStore.set(key, entries)
106
331
  },
107
- {
108
- description: "Get the player's name.",
109
- parametersJson: JSON.stringify({
110
- type: 'object',
111
- properties: {},
112
- additionalProperties: false,
113
- }),
332
+ (memoryName, sessionId, query, maxEntries) => {
333
+ const entries = memoryStore.get(memoryKey(memoryName, sessionId)) ?? []
334
+ return entries.filter((entry) => entry.content.includes(query)).slice(-maxEntries)
335
+ },
336
+ (memoryName, sessionId) => {
337
+ memoryStore.delete(memoryKey(memoryName, sessionId))
114
338
  },
339
+ () => {},
115
340
  )
116
341
  ```
117
342
 
118
- Then pass dependencies at run time:
343
+ Minimal TypeScript version:
344
+
345
+ ```ts
346
+ import {
347
+ JsMemoryKind,
348
+ type JsMemoryEntry,
349
+ type JsMemoryModule,
350
+ NativeEnkiAgent,
351
+ } from '@getenki/ai'
352
+
353
+ type ExampleTool = {
354
+ id: string
355
+ description: string
356
+ inputSchema: Record<string, unknown>
357
+ execute: (inputJson: string, contextJson: string) => string
358
+ }
119
359
 
120
- ```js
121
- const result = await agent.run('Say hello.', {
122
- sessionId: 'session-ctx-1',
123
- deps: { playerName: 'Anne' },
124
- })
360
+ const tools: ExampleTool[] = [
361
+ {
362
+ id: 'get_today',
363
+ description: 'Return the current local date in ISO format.',
364
+ inputSchema: { type: 'object', properties: {} },
365
+ execute: (): string =>
366
+ JSON.stringify({ today: new Date().toISOString().slice(0, 10) }),
367
+ },
368
+ ]
369
+
370
+ const memories: JsMemoryModule[] = [{ name: 'example-memory' }]
371
+ const memoryStore = new Map<string, JsMemoryEntry[]>()
372
+
373
+ const agent = NativeEnkiAgent.withToolsAndMemory(
374
+ 'Basic TS Agent',
375
+ 'Answer clearly and keep responses short.',
376
+ 'ollama::qwen3.5:latest',
377
+ 20,
378
+ process.cwd(),
379
+ tools,
380
+ null,
381
+ memories,
382
+ (memoryName: string, sessionId: string, userMsg: string, assistantMsg: string): void => {
383
+ const key = `${memoryName}:${sessionId}`
384
+ const entries = memoryStore.get(key) ?? []
385
+ entries.push({
386
+ key: `entry-${entries.length + 1}`,
387
+ content: `User: ${userMsg}\nAssistant: ${assistantMsg}`,
388
+ kind: JsMemoryKind.RecentMessage,
389
+ relevance: 1,
390
+ timestampNs: `${Date.now() * 1000000}`,
391
+ })
392
+ memoryStore.set(key, entries)
393
+ },
394
+ (memoryName: string, sessionId: string, query: string, maxEntries: number): JsMemoryEntry[] => {
395
+ const entries = memoryStore.get(`${memoryName}:${sessionId}`) ?? []
396
+ return entries.filter((entry) => entry.content.includes(query)).slice(-maxEntries)
397
+ },
398
+ (memoryName: string, sessionId: string): void => {
399
+ memoryStore.delete(`${memoryName}:${sessionId}`)
400
+ },
401
+ (): void => {},
402
+ )
125
403
  ```
126
404
 
127
- ## Custom LLM providers
405
+ ## Running The Examples
128
406
 
129
- Pass either a subclass of `LlmProviderBackend` or a function through the `llm` option.
407
+ JavaScript example:
130
408
 
131
- ```js
132
- const { Agent, LlmProviderBackend } = require('@getenki/ai')
133
-
134
- class DemoProvider extends LlmProviderBackend {
135
- complete(model, messages, tools) {
136
- return {
137
- model,
138
- content: `Received ${messages.length} message(s) and ${tools.length} tool(s).`,
139
- }
140
- }
141
- }
409
+ ```bash
410
+ cd example/basic-js
411
+ npm install
412
+ npm start
413
+ ```
414
+
415
+ TypeScript example:
142
416
 
143
- const agent = new Agent('demo-model', {
144
- llm: new DemoProvider(),
145
- })
417
+ ```bash
418
+ cd example/basic-ts
419
+ npm install
420
+ npm start
146
421
  ```
147
422
 
423
+ The checked-in examples currently hardcode `ollama::qwen3.5:latest` as the model, so make sure that model is available in your local provider before running them.
424
+
148
425
  ## Development
149
426
 
150
427
  From [`crates/bindings/enki-js`](/I:/projects/enki/core-next/crates/bindings/enki-js):
@@ -162,9 +439,3 @@ Useful scripts:
162
439
  - `npm test`: run the AVA test suite
163
440
  - `npm run lint`: run `oxlint`
164
441
  - `npm run format`: run Prettier, `cargo fmt`, and `taplo format`
165
-
166
- ## Notes
167
-
168
- - `Agent` can register tools, memories, and custom LLM providers in JavaScript.
169
- - The default native constructor uses `20` max iterations when none is provided.
170
- - `workspaceHome` lets you control where the runtime creates and resolves workspace state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getenki/ai-linux-x64-gnu",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "cpu": [
5
5
  "x64"
6
6
  ],