@getenki/ai 0.1.91

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/client.js ADDED
@@ -0,0 +1,586 @@
1
+ 'use strict'
2
+
3
+ const { NativeEnkiAgent, ...nativeBinding } = require('./index.js')
4
+
5
+ const DEFAULT_AGENT_NAME = 'Agent'
6
+ const DEFAULT_MAX_ITERATIONS = 20
7
+
8
+ class EnkiAgent {
9
+ constructor(options = {}) {
10
+ if (options == null || typeof options !== 'object' || Array.isArray(options)) {
11
+ throw new TypeError('EnkiAgent options must be an object')
12
+ }
13
+
14
+ const {
15
+ name,
16
+ systemPromptPreamble,
17
+ model,
18
+ maxIterations,
19
+ workspaceHome,
20
+ } = options
21
+
22
+ this._native = new NativeEnkiAgent(
23
+ optionalString(name, 'name'),
24
+ optionalString(systemPromptPreamble, 'systemPromptPreamble'),
25
+ optionalString(model, 'model'),
26
+ optionalPositiveInteger(maxIterations, 'maxIterations'),
27
+ optionalString(workspaceHome, 'workspaceHome'),
28
+ )
29
+ }
30
+
31
+ run(sessionId, userMessage) {
32
+ return this._native.run(
33
+ requiredString(sessionId, 'sessionId'),
34
+ requiredString(userMessage, 'userMessage'),
35
+ )
36
+ }
37
+ }
38
+
39
+ class RunContext {
40
+ constructor(deps) {
41
+ this.deps = deps
42
+ }
43
+ }
44
+
45
+ class AgentRunResult {
46
+ constructor(output) {
47
+ this.output = output
48
+ }
49
+ }
50
+
51
+ class Tool {
52
+ constructor({ name, description = '', parametersJson, func, usesContext }) {
53
+ this.name = requiredString(name, 'name')
54
+ this.description = typeof description === 'string' ? description : ''
55
+ this.parametersJson = validateJsonSchema(parametersJson)
56
+ if (typeof func !== 'function') {
57
+ throw new TypeError('func must be a function')
58
+ }
59
+ this.func = func
60
+ this.usesContext = Boolean(usesContext)
61
+ }
62
+
63
+ static fromFunction(func, options = {}) {
64
+ if (typeof func !== 'function') {
65
+ throw new TypeError('func must be a function')
66
+ }
67
+
68
+ const {
69
+ usesContext,
70
+ name,
71
+ description,
72
+ parametersJson,
73
+ } = options
74
+
75
+ return new Tool({
76
+ name: name ?? func.name ?? 'tool',
77
+ description: description ?? getFunctionDescription(func),
78
+ parametersJson: parametersJson ?? JSON.stringify({
79
+ type: 'object',
80
+ properties: {},
81
+ additionalProperties: false,
82
+ }),
83
+ func,
84
+ usesContext: Boolean(usesContext),
85
+ })
86
+ }
87
+
88
+ asLowLevelTool() {
89
+ return {
90
+ name: this.name,
91
+ description: this.description,
92
+ parametersJson: this.parametersJson,
93
+ }
94
+ }
95
+ }
96
+
97
+ class MemoryModule {
98
+ constructor({ name, record, recall, flush, consolidate }) {
99
+ this.name = requiredString(name, 'name')
100
+ if (typeof record !== 'function') {
101
+ throw new TypeError('record must be a function')
102
+ }
103
+ if (typeof recall !== 'function') {
104
+ throw new TypeError('recall must be a function')
105
+ }
106
+ if (flush != null && typeof flush !== 'function') {
107
+ throw new TypeError('flush must be a function')
108
+ }
109
+ if (consolidate != null && typeof consolidate !== 'function') {
110
+ throw new TypeError('consolidate must be a function')
111
+ }
112
+
113
+ this.record = record
114
+ this.recall = recall
115
+ this.flush = flush
116
+ this.consolidate = consolidate
117
+ }
118
+
119
+ asLowLevelMemory() {
120
+ return { name: this.name }
121
+ }
122
+ }
123
+
124
+ class MemoryBackend {
125
+ asMemoryModule() {
126
+ return new MemoryModule({
127
+ name: this.name ?? 'memory',
128
+ record: this.record.bind(this),
129
+ recall: this.recall.bind(this),
130
+ flush: typeof this.flush === 'function' ? this.flush.bind(this) : undefined,
131
+ consolidate:
132
+ typeof this.consolidate === 'function' ? this.consolidate.bind(this) : undefined,
133
+ })
134
+ }
135
+ }
136
+
137
+ class LlmProviderBackend {}
138
+
139
+ class ToolHandler {
140
+ constructor(tools) {
141
+ this._tools = tools
142
+ this._deps = undefined
143
+ }
144
+
145
+ setDeps(deps) {
146
+ this._deps = deps
147
+ }
148
+
149
+ clearDeps() {
150
+ this._deps = undefined
151
+ }
152
+
153
+ async execute(toolName, argsJson, agentDir, workspaceDir, sessionsDir) {
154
+ const tool = this._tools.get(toolName)
155
+ if (!tool) {
156
+ throw new Error(`Unknown tool '${toolName}'`)
157
+ }
158
+
159
+ const parsedArgs = parseObjectJson(argsJson, `Tool '${toolName}' expected JSON object args`)
160
+ const args = []
161
+ if (tool.usesContext) {
162
+ args.push(new RunContext(this._deps))
163
+ }
164
+
165
+ for (const parameter of getFunctionParameters(tool.func, tool.usesContext)) {
166
+ if (Object.prototype.hasOwnProperty.call(parsedArgs, parameter.name)) {
167
+ args.push(parsedArgs[parameter.name])
168
+ } else if (parameter.hasDefault) {
169
+ args.push(parameter.defaultValue)
170
+ } else {
171
+ throw new TypeError(`Missing required argument '${parameter.name}' for tool '${toolName}'`)
172
+ }
173
+ }
174
+
175
+ void agentDir
176
+ void workspaceDir
177
+ void sessionsDir
178
+
179
+ const result = await Promise.resolve(tool.func(...args))
180
+ return stringifyToolResult(result)
181
+ }
182
+ }
183
+
184
+ class MemoryHandler {
185
+ constructor(memories) {
186
+ this._memories = memories
187
+ }
188
+
189
+ async record(memoryName, sessionId, userMsg, assistantMsg) {
190
+ const memory = this._get(memoryName)
191
+ await Promise.resolve(memory.record(sessionId, userMsg, assistantMsg))
192
+ }
193
+
194
+ async recall(memoryName, sessionId, query, maxEntries) {
195
+ const memory = this._get(memoryName)
196
+ return (await Promise.resolve(memory.recall(sessionId, query, maxEntries))) ?? []
197
+ }
198
+
199
+ async flush(memoryName, sessionId) {
200
+ const memory = this._get(memoryName)
201
+ if (memory.flush) {
202
+ await Promise.resolve(memory.flush(sessionId))
203
+ }
204
+ }
205
+
206
+ async consolidate(memoryName, sessionId) {
207
+ const memory = this._get(memoryName)
208
+ if (memory.consolidate) {
209
+ await Promise.resolve(memory.consolidate(sessionId))
210
+ }
211
+ }
212
+
213
+ _get(memoryName) {
214
+ const memory = this._memories.get(memoryName)
215
+ if (!memory) {
216
+ throw new Error(`Unknown memory '${memoryName}'`)
217
+ }
218
+ return memory
219
+ }
220
+ }
221
+
222
+ class LlmHandler {
223
+ constructor(provider) {
224
+ this._provider = provider
225
+ }
226
+
227
+ async complete(model, messagesJson, toolsJson) {
228
+ const messages = parseArrayJson(messagesJson)
229
+ const tools = parseArrayJson(toolsJson)
230
+ const result =
231
+ this._provider instanceof LlmProviderBackend
232
+ ? await Promise.resolve(this._provider.complete(model, messages, tools))
233
+ : await Promise.resolve(this._provider(model, messages, tools))
234
+
235
+ return typeof result === 'string' ? result : JSON.stringify(result)
236
+ }
237
+ }
238
+
239
+ class Agent {
240
+ constructor(model, options = {}) {
241
+ this.model = requiredString(model, 'model')
242
+
243
+ if (options == null || typeof options !== 'object' || Array.isArray(options)) {
244
+ throw new TypeError('Agent options must be an object')
245
+ }
246
+
247
+ const {
248
+ instructions = '',
249
+ name = DEFAULT_AGENT_NAME,
250
+ maxIterations = DEFAULT_MAX_ITERATIONS,
251
+ workspaceHome,
252
+ tools = [],
253
+ memories = [],
254
+ llm,
255
+ lowLevelAgent,
256
+ } = options
257
+
258
+ this.instructions = optionalString(instructions, 'instructions') ?? ''
259
+ this.name = optionalString(name, 'name') ?? DEFAULT_AGENT_NAME
260
+ this.maxIterations = optionalPositiveInteger(maxIterations, 'maxIterations') ?? DEFAULT_MAX_ITERATIONS
261
+ this.workspaceHome = optionalString(workspaceHome, 'workspaceHome')
262
+ this._lowLevelAgent = lowLevelAgent ?? Agent._LowLevelEnkiAgent
263
+ this._tools = new Map()
264
+ this._memories = new Map()
265
+ this._toolHandler = new ToolHandler(this._tools)
266
+ this._memoryHandler = new MemoryHandler(this._memories)
267
+ this._llmHandler = llm == null ? null : new LlmHandler(llm)
268
+ this._backend = null
269
+ this._dirty = true
270
+
271
+ for (const tool of tools) {
272
+ this.registerTool(tool)
273
+ }
274
+ for (const memory of memories) {
275
+ this.registerMemory(memory)
276
+ }
277
+ }
278
+
279
+ toolPlain(func, options = {}) {
280
+ this.registerTool(Tool.fromFunction(func, { ...options, usesContext: false }))
281
+ return func
282
+ }
283
+
284
+ tool(func, options = {}) {
285
+ if (getFunctionParameters(func, false).length === 0) {
286
+ throw new TypeError(`Tool '${func.name || 'anonymous'}' must accept a RunContext argument`)
287
+ }
288
+ this.registerTool(Tool.fromFunction(func, { ...options, usesContext: true }))
289
+ return func
290
+ }
291
+
292
+ registerTool(tool) {
293
+ const normalized = tool instanceof Tool ? tool : new Tool(tool)
294
+ this._tools.set(normalized.name, normalized)
295
+ this._dirty = true
296
+ return normalized
297
+ }
298
+
299
+ registerMemory(memory) {
300
+ const normalized = memory instanceof MemoryModule ? memory : new MemoryModule(memory)
301
+ this._memories.set(normalized.name, normalized)
302
+ this._dirty = true
303
+ return normalized
304
+ }
305
+
306
+ async run(userMessage, options = {}) {
307
+ const backend = this._ensureBackend()
308
+ const message = requiredString(userMessage, 'userMessage')
309
+ const sessionId = requiredString(options.sessionId ?? createSessionId(), 'sessionId')
310
+
311
+ this._toolHandler.setDeps(options.deps)
312
+ try {
313
+ const output = await backend.run(sessionId, message)
314
+ return new AgentRunResult(output)
315
+ } finally {
316
+ this._toolHandler.clearDeps()
317
+ }
318
+ }
319
+
320
+ _ensureBackend() {
321
+ if (this._backend != null && !this._dirty) {
322
+ return this._backend
323
+ }
324
+
325
+ const toolSpecs = Array.from(this._tools.values(), (tool) => tool.asLowLevelTool())
326
+ const memorySpecs = Array.from(this._memories.values(), (memory) => memory.asLowLevelMemory())
327
+ const LowLevelAgent = this._lowLevelAgent
328
+
329
+ if (this._llmHandler && toolSpecs.length > 0 && memorySpecs.length > 0) {
330
+ this._backend = callFactory(
331
+ LowLevelAgent,
332
+ ['withToolsMemoryAndLlm', 'with_tools_memory_and_llm'],
333
+ {
334
+ name: this.name,
335
+ systemPromptPreamble: this.instructions,
336
+ model: this.model,
337
+ maxIterations: this.maxIterations,
338
+ workspaceHome: this.workspaceHome,
339
+ tools: toolSpecs,
340
+ toolHandler: this._toolHandler,
341
+ memories: memorySpecs,
342
+ memoryHandler: this._memoryHandler,
343
+ llmHandler: this._llmHandler,
344
+ },
345
+ 'custom tools, memory, and llm',
346
+ )
347
+ } else if (this._llmHandler && toolSpecs.length > 0) {
348
+ this._backend = callFactory(
349
+ LowLevelAgent,
350
+ ['withToolsAndLlm', 'with_tools_and_llm'],
351
+ {
352
+ name: this.name,
353
+ systemPromptPreamble: this.instructions,
354
+ model: this.model,
355
+ maxIterations: this.maxIterations,
356
+ workspaceHome: this.workspaceHome,
357
+ tools: toolSpecs,
358
+ handler: this._toolHandler,
359
+ llmHandler: this._llmHandler,
360
+ },
361
+ 'custom tools and llm',
362
+ )
363
+ } else if (this._llmHandler && memorySpecs.length > 0) {
364
+ this._backend = callFactory(
365
+ LowLevelAgent,
366
+ ['withMemoryAndLlm', 'with_memory_and_llm'],
367
+ {
368
+ name: this.name,
369
+ systemPromptPreamble: this.instructions,
370
+ model: this.model,
371
+ maxIterations: this.maxIterations,
372
+ workspaceHome: this.workspaceHome,
373
+ memories: memorySpecs,
374
+ handler: this._memoryHandler,
375
+ llmHandler: this._llmHandler,
376
+ },
377
+ 'custom memory and llm',
378
+ )
379
+ } else if (this._llmHandler) {
380
+ this._backend = callFactory(
381
+ LowLevelAgent,
382
+ ['withLlm', 'with_llm'],
383
+ {
384
+ name: this.name,
385
+ systemPromptPreamble: this.instructions,
386
+ model: this.model,
387
+ maxIterations: this.maxIterations,
388
+ workspaceHome: this.workspaceHome,
389
+ llmHandler: this._llmHandler,
390
+ },
391
+ 'custom llm',
392
+ )
393
+ } else if (toolSpecs.length > 0 && memorySpecs.length > 0) {
394
+ this._backend = callFactory(
395
+ LowLevelAgent,
396
+ ['withToolsAndMemory', 'with_tools_and_memory'],
397
+ {
398
+ name: this.name,
399
+ systemPromptPreamble: this.instructions,
400
+ model: this.model,
401
+ maxIterations: this.maxIterations,
402
+ workspaceHome: this.workspaceHome,
403
+ tools: toolSpecs,
404
+ toolHandler: this._toolHandler,
405
+ memories: memorySpecs,
406
+ memoryHandler: this._memoryHandler,
407
+ },
408
+ 'custom tools and memory',
409
+ )
410
+ } else if (toolSpecs.length > 0) {
411
+ this._backend = callFactory(
412
+ LowLevelAgent,
413
+ ['withTools', 'with_tools'],
414
+ {
415
+ name: this.name,
416
+ systemPromptPreamble: this.instructions,
417
+ model: this.model,
418
+ maxIterations: this.maxIterations,
419
+ workspaceHome: this.workspaceHome,
420
+ tools: toolSpecs,
421
+ handler: this._toolHandler,
422
+ },
423
+ 'custom tools',
424
+ )
425
+ } else if (memorySpecs.length > 0) {
426
+ this._backend = callFactory(
427
+ LowLevelAgent,
428
+ ['withMemory', 'with_memory'],
429
+ {
430
+ name: this.name,
431
+ systemPromptPreamble: this.instructions,
432
+ model: this.model,
433
+ maxIterations: this.maxIterations,
434
+ workspaceHome: this.workspaceHome,
435
+ memories: memorySpecs,
436
+ handler: this._memoryHandler,
437
+ },
438
+ 'custom memory',
439
+ )
440
+ } else {
441
+ this._backend = new LowLevelAgent(
442
+ this.name,
443
+ this.instructions,
444
+ this.model,
445
+ this.maxIterations,
446
+ this.workspaceHome,
447
+ )
448
+ }
449
+
450
+ this._dirty = false
451
+ return this._backend
452
+ }
453
+ }
454
+
455
+ Agent._LowLevelEnkiAgent = NativeEnkiAgent
456
+
457
+ function requiredString(value, field) {
458
+ if (typeof value !== 'string' || value.length === 0) {
459
+ throw new TypeError(`${field} must be a non-empty string`)
460
+ }
461
+ return value
462
+ }
463
+
464
+ function optionalString(value, field) {
465
+ if (value == null) {
466
+ return undefined
467
+ }
468
+ if (typeof value !== 'string') {
469
+ throw new TypeError(`${field} must be a string`)
470
+ }
471
+ return value
472
+ }
473
+
474
+ function optionalPositiveInteger(value, field) {
475
+ if (value == null) {
476
+ return undefined
477
+ }
478
+ if (!Number.isInteger(value) || value < 1) {
479
+ throw new TypeError(`${field} must be a positive integer`)
480
+ }
481
+ return value
482
+ }
483
+
484
+ function validateJsonSchema(value) {
485
+ const json = requiredString(value, 'parametersJson')
486
+ JSON.parse(json)
487
+ return json
488
+ }
489
+
490
+ function getFunctionDescription(func) {
491
+ return typeof func.description === 'string' ? func.description : ''
492
+ }
493
+
494
+ function parseObjectJson(value, errorMessage) {
495
+ const parsed = value ? JSON.parse(value) : {}
496
+ if (parsed == null) {
497
+ return {}
498
+ }
499
+ if (typeof parsed !== 'object' || Array.isArray(parsed)) {
500
+ throw new TypeError(errorMessage)
501
+ }
502
+ return parsed
503
+ }
504
+
505
+ function parseArrayJson(value) {
506
+ const parsed = value ? JSON.parse(value) : []
507
+ return Array.isArray(parsed) ? parsed : []
508
+ }
509
+
510
+ function callFactory(LowLevelAgent, methodNames, args, featureName) {
511
+ for (const methodName of methodNames) {
512
+ if (typeof LowLevelAgent[methodName] === 'function') {
513
+ return LowLevelAgent[methodName](args)
514
+ }
515
+ }
516
+
517
+ throw new Error(`NativeEnkiAgent does not support ${featureName}`)
518
+ }
519
+
520
+ function createSessionId() {
521
+ return `session-${Date.now()}-${Math.random().toString(16).slice(2)}`
522
+ }
523
+
524
+ function getFunctionParameters(func, usesContext) {
525
+ const source = func.toString().replace(/\s+/g, ' ')
526
+ const match =
527
+ source.match(/^[^(]*\(([^)]*)\)/) ??
528
+ source.match(/^([^=()]+?)\s*=>/)
529
+
530
+ const rawParameters = match?.[1]
531
+ ? match[1].split(',').map((part) => part.trim()).filter(Boolean)
532
+ : []
533
+
534
+ const parameters = rawParameters.map((parameter) => {
535
+ const [namePart, defaultPart] = parameter.split('=').map((part) => part.trim())
536
+ return {
537
+ name: namePart,
538
+ hasDefault: defaultPart != null,
539
+ defaultValue: defaultPart == null ? undefined : parseDefaultValue(defaultPart),
540
+ }
541
+ })
542
+
543
+ return usesContext ? parameters.slice(1) : parameters
544
+ }
545
+
546
+ function parseDefaultValue(value) {
547
+ if (value === 'true') {
548
+ return true
549
+ }
550
+ if (value === 'false') {
551
+ return false
552
+ }
553
+ if (value === "''" || value === '""') {
554
+ return ''
555
+ }
556
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
557
+ return Number(value)
558
+ }
559
+ return undefined
560
+ }
561
+
562
+ function stringifyToolResult(value) {
563
+ if (typeof value === 'string') {
564
+ return value
565
+ }
566
+ if (value == null) {
567
+ return ''
568
+ }
569
+ if (typeof value === 'number' || typeof value === 'boolean') {
570
+ return String(value)
571
+ }
572
+ return JSON.stringify(value)
573
+ }
574
+
575
+ module.exports = {
576
+ ...nativeBinding,
577
+ Agent,
578
+ AgentRunResult,
579
+ EnkiAgent,
580
+ LlmProviderBackend,
581
+ MemoryBackend,
582
+ MemoryModule,
583
+ NativeEnkiAgent,
584
+ RunContext,
585
+ Tool,
586
+ }
package/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ export declare class NativeEnkiAgent {
4
+ constructor(name?: string | undefined | null, systemPromptPreamble?: string | undefined | null, model?: string | undefined | null, maxIterations?: number | undefined | null, workspaceHome?: string | undefined | null)
5
+ run(sessionId: string, userMessage: string): Promise<unknown>
6
+ }