@getenki/ai-darwin-arm64 0.2.6 → 0.2.8
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 +354 -83
- package/enki-ai.darwin-arm64.node +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@getenki/ai`
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
##
|
|
17
|
+
## What It Exports
|
|
18
18
|
|
|
19
|
-
The package
|
|
19
|
+
The current package surface is intentionally small:
|
|
20
20
|
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
21
|
+
- `NativeEnkiAgent`
|
|
22
|
+
- `JsMemoryKind`
|
|
23
|
+
- `JsMemoryModule`
|
|
24
|
+
- `JsMemoryEntry`
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
`NativeEnkiAgent` is the main entrypoint. It can be created in four modes:
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
- `new(...)` for a plain agent
|
|
29
|
+
- `NativeEnkiAgent.withTools(...)`
|
|
30
|
+
- `NativeEnkiAgent.withMemory(...)`
|
|
31
|
+
- `NativeEnkiAgent.withToolsAndMemory(...)`
|
|
27
32
|
|
|
28
|
-
|
|
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 {
|
|
38
|
+
const { NativeEnkiAgent } = require('@getenki/ai')
|
|
32
39
|
|
|
33
40
|
async function main() {
|
|
34
|
-
const agent = new
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
+
Example:
|
|
60
93
|
|
|
61
94
|
```js
|
|
62
|
-
const {
|
|
95
|
+
const { NativeEnkiAgent } = require('@getenki/ai')
|
|
63
96
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
282
|
+
## Tools And Memory Example
|
|
95
283
|
|
|
96
|
-
`
|
|
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 {
|
|
293
|
+
const { JsMemoryKind, NativeEnkiAgent } = require('@getenki/ai')
|
|
100
294
|
|
|
101
|
-
const
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
##
|
|
405
|
+
## Running The Examples
|
|
128
406
|
|
|
129
|
-
|
|
407
|
+
JavaScript example:
|
|
130
408
|
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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.
|
|
Binary file
|