@molroo-io/sdk 0.6.2 → 0.7.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.
Files changed (252) hide show
  1. package/README.md +100 -194
  2. package/dist/cjs/api-client.d.ts +2 -12
  3. package/dist/cjs/api-client.d.ts.map +1 -1
  4. package/dist/cjs/api-client.js +4 -42
  5. package/dist/cjs/errors.d.ts +1 -16
  6. package/dist/cjs/errors.d.ts.map +1 -1
  7. package/dist/cjs/errors.js +2 -18
  8. package/dist/cjs/events/types.d.ts +14 -21
  9. package/dist/cjs/events/types.d.ts.map +1 -1
  10. package/dist/cjs/events/types.js +0 -11
  11. package/dist/cjs/index.d.ts +26 -48
  12. package/dist/cjs/index.d.ts.map +1 -1
  13. package/dist/cjs/index.js +29 -52
  14. package/dist/cjs/llm/resolve.d.ts +4 -22
  15. package/dist/cjs/llm/resolve.d.ts.map +1 -1
  16. package/dist/cjs/llm/resolve.js +19 -7
  17. package/dist/cjs/llm/vercel-ai/adapter.d.ts +4 -10
  18. package/dist/cjs/llm/vercel-ai/adapter.d.ts.map +1 -1
  19. package/dist/cjs/llm/vercel-ai/adapter.js +6 -152
  20. package/dist/cjs/llm/vercel-ai/config.d.ts +8 -5
  21. package/dist/cjs/llm/vercel-ai/config.d.ts.map +1 -1
  22. package/dist/cjs/memory/in-memory.d.ts +14 -37
  23. package/dist/cjs/memory/in-memory.d.ts.map +1 -1
  24. package/dist/cjs/memory/in-memory.js +22 -85
  25. package/dist/cjs/memory/recall.d.ts +10 -21
  26. package/dist/cjs/memory/recall.d.ts.map +1 -1
  27. package/dist/cjs/memory/recall.js +12 -91
  28. package/dist/cjs/memory/types.d.ts +46 -186
  29. package/dist/cjs/memory/types.d.ts.map +1 -1
  30. package/dist/cjs/memory/types.js +0 -10
  31. package/dist/cjs/persona/chat-orchestrator.d.ts +46 -0
  32. package/dist/cjs/persona/chat-orchestrator.d.ts.map +1 -0
  33. package/dist/cjs/persona/chat-orchestrator.js +240 -0
  34. package/dist/cjs/persona/event-emitter.d.ts +7 -0
  35. package/dist/cjs/persona/event-emitter.d.ts.map +1 -0
  36. package/dist/cjs/persona/event-emitter.js +53 -0
  37. package/dist/cjs/persona/memory-pipeline.d.ts +26 -0
  38. package/dist/cjs/persona/memory-pipeline.d.ts.map +1 -0
  39. package/dist/cjs/persona/memory-pipeline.js +69 -0
  40. package/dist/cjs/persona.d.ts +56 -187
  41. package/dist/cjs/persona.d.ts.map +1 -1
  42. package/dist/cjs/persona.js +62 -638
  43. package/dist/cjs/shared/appraisal.d.ts +26 -0
  44. package/dist/cjs/shared/appraisal.d.ts.map +1 -0
  45. package/dist/cjs/shared/appraisal.js +45 -0
  46. package/dist/cjs/shared/client-factory.d.ts +23 -0
  47. package/dist/cjs/shared/client-factory.d.ts.map +1 -0
  48. package/dist/cjs/shared/client-factory.js +48 -0
  49. package/dist/cjs/shared/errors.d.ts +21 -0
  50. package/dist/cjs/shared/errors.d.ts.map +1 -0
  51. package/dist/cjs/shared/errors.js +29 -0
  52. package/dist/cjs/world/client.d.ts +2 -9
  53. package/dist/cjs/world/client.d.ts.map +1 -1
  54. package/dist/cjs/world/client.js +7 -34
  55. package/dist/cjs/world/errors.d.ts +1 -8
  56. package/dist/cjs/world/errors.d.ts.map +1 -1
  57. package/dist/cjs/world/errors.js +2 -12
  58. package/dist/cjs/world/index.d.ts +2 -2
  59. package/dist/cjs/world/index.d.ts.map +1 -1
  60. package/dist/cjs/world/types.d.ts +32 -5
  61. package/dist/cjs/world/types.d.ts.map +1 -1
  62. package/dist/cjs/world/world-domain.d.ts.map +1 -1
  63. package/dist/cjs/world/world-domain.js +4 -32
  64. package/dist/cjs/world/world-persona.d.ts +17 -12
  65. package/dist/cjs/world/world-persona.d.ts.map +1 -1
  66. package/dist/cjs/world/world-persona.js +6 -18
  67. package/dist/cjs/world/world.d.ts +28 -5
  68. package/dist/cjs/world/world.d.ts.map +1 -1
  69. package/dist/cjs/world/world.js +13 -11
  70. package/dist/esm/api-client.d.ts +2 -12
  71. package/dist/esm/api-client.d.ts.map +1 -1
  72. package/dist/esm/api-client.js +3 -38
  73. package/dist/esm/errors.d.ts +1 -16
  74. package/dist/esm/errors.d.ts.map +1 -1
  75. package/dist/esm/errors.js +1 -17
  76. package/dist/esm/events/types.d.ts +14 -21
  77. package/dist/esm/events/types.d.ts.map +1 -1
  78. package/dist/esm/events/types.js +0 -11
  79. package/dist/esm/index.d.ts +26 -48
  80. package/dist/esm/index.d.ts.map +1 -1
  81. package/dist/esm/index.js +25 -38
  82. package/dist/esm/llm/resolve.d.ts +4 -22
  83. package/dist/esm/llm/resolve.d.ts.map +1 -1
  84. package/dist/esm/llm/resolve.js +20 -8
  85. package/dist/esm/llm/vercel-ai/adapter.d.ts +4 -10
  86. package/dist/esm/llm/vercel-ai/adapter.d.ts.map +1 -1
  87. package/dist/esm/llm/vercel-ai/adapter.js +6 -119
  88. package/dist/esm/llm/vercel-ai/config.d.ts +8 -5
  89. package/dist/esm/llm/vercel-ai/config.d.ts.map +1 -1
  90. package/dist/esm/memory/in-memory.d.ts +14 -37
  91. package/dist/esm/memory/in-memory.d.ts.map +1 -1
  92. package/dist/esm/memory/in-memory.js +20 -83
  93. package/dist/esm/memory/recall.d.ts +10 -21
  94. package/dist/esm/memory/recall.d.ts.map +1 -1
  95. package/dist/esm/memory/recall.js +12 -91
  96. package/dist/esm/memory/types.d.ts +46 -186
  97. package/dist/esm/memory/types.d.ts.map +1 -1
  98. package/dist/esm/memory/types.js +1 -9
  99. package/dist/esm/persona/chat-orchestrator.d.ts +46 -0
  100. package/dist/esm/persona/chat-orchestrator.d.ts.map +1 -0
  101. package/dist/esm/persona/chat-orchestrator.js +204 -0
  102. package/dist/esm/persona/event-emitter.d.ts +7 -0
  103. package/dist/esm/persona/event-emitter.d.ts.map +1 -0
  104. package/dist/esm/persona/event-emitter.js +50 -0
  105. package/dist/esm/persona/memory-pipeline.d.ts +26 -0
  106. package/dist/esm/persona/memory-pipeline.d.ts.map +1 -0
  107. package/dist/esm/persona/memory-pipeline.js +65 -0
  108. package/dist/esm/persona.d.ts +56 -187
  109. package/dist/esm/persona.d.ts.map +1 -1
  110. package/dist/esm/persona.js +62 -638
  111. package/dist/esm/shared/appraisal.d.ts +26 -0
  112. package/dist/esm/shared/appraisal.d.ts.map +1 -0
  113. package/dist/esm/shared/appraisal.js +40 -0
  114. package/dist/esm/shared/client-factory.d.ts +23 -0
  115. package/dist/esm/shared/client-factory.d.ts.map +1 -0
  116. package/dist/esm/shared/client-factory.js +41 -0
  117. package/dist/esm/shared/errors.d.ts +21 -0
  118. package/dist/esm/shared/errors.d.ts.map +1 -0
  119. package/dist/esm/shared/errors.js +24 -0
  120. package/dist/esm/world/client.d.ts +2 -9
  121. package/dist/esm/world/client.d.ts.map +1 -1
  122. package/dist/esm/world/client.js +6 -30
  123. package/dist/esm/world/errors.d.ts +1 -8
  124. package/dist/esm/world/errors.d.ts.map +1 -1
  125. package/dist/esm/world/errors.js +1 -11
  126. package/dist/esm/world/index.d.ts +2 -2
  127. package/dist/esm/world/index.d.ts.map +1 -1
  128. package/dist/esm/world/types.d.ts +32 -5
  129. package/dist/esm/world/types.d.ts.map +1 -1
  130. package/dist/esm/world/world-domain.d.ts.map +1 -1
  131. package/dist/esm/world/world-domain.js +4 -32
  132. package/dist/esm/world/world-persona.d.ts +17 -12
  133. package/dist/esm/world/world-persona.d.ts.map +1 -1
  134. package/dist/esm/world/world-persona.js +6 -18
  135. package/dist/esm/world/world.d.ts +28 -5
  136. package/dist/esm/world/world.d.ts.map +1 -1
  137. package/dist/esm/world/world.js +13 -11
  138. package/package.json +4 -101
  139. package/dist/cjs/embedding/cloudflare.d.ts +0 -15
  140. package/dist/cjs/embedding/cloudflare.d.ts.map +0 -1
  141. package/dist/cjs/embedding/cloudflare.js +0 -16
  142. package/dist/cjs/embedding/cohere.d.ts +0 -8
  143. package/dist/cjs/embedding/cohere.d.ts.map +0 -1
  144. package/dist/cjs/embedding/cohere.js +0 -31
  145. package/dist/cjs/embedding/index.d.ts +0 -9
  146. package/dist/cjs/embedding/index.d.ts.map +0 -1
  147. package/dist/cjs/embedding/index.js +0 -11
  148. package/dist/cjs/embedding/local.d.ts +0 -6
  149. package/dist/cjs/embedding/local.d.ts.map +0 -1
  150. package/dist/cjs/embedding/local.js +0 -28
  151. package/dist/cjs/embedding/openai.d.ts +0 -9
  152. package/dist/cjs/embedding/openai.d.ts.map +0 -1
  153. package/dist/cjs/embedding/openai.js +0 -26
  154. package/dist/cjs/events/console.d.ts +0 -25
  155. package/dist/cjs/events/console.d.ts.map +0 -1
  156. package/dist/cjs/events/console.js +0 -41
  157. package/dist/cjs/events/webhook.d.ts +0 -30
  158. package/dist/cjs/events/webhook.d.ts.map +0 -1
  159. package/dist/cjs/events/webhook.js +0 -79
  160. package/dist/cjs/memory/cloudflare/index.d.ts +0 -3
  161. package/dist/cjs/memory/cloudflare/index.d.ts.map +0 -1
  162. package/dist/cjs/memory/cloudflare/index.js +0 -5
  163. package/dist/cjs/memory/cloudflare/vectorize.d.ts +0 -62
  164. package/dist/cjs/memory/cloudflare/vectorize.d.ts.map +0 -1
  165. package/dist/cjs/memory/cloudflare/vectorize.js +0 -55
  166. package/dist/cjs/memory/in-memory-semantic.d.ts +0 -16
  167. package/dist/cjs/memory/in-memory-semantic.d.ts.map +0 -1
  168. package/dist/cjs/memory/in-memory-semantic.js +0 -57
  169. package/dist/cjs/memory/pinecone/index.d.ts +0 -7
  170. package/dist/cjs/memory/pinecone/index.d.ts.map +0 -1
  171. package/dist/cjs/memory/pinecone/index.js +0 -8
  172. package/dist/cjs/memory/pinecone/memory-adapter.d.ts +0 -62
  173. package/dist/cjs/memory/pinecone/memory-adapter.d.ts.map +0 -1
  174. package/dist/cjs/memory/pinecone/memory-adapter.js +0 -220
  175. package/dist/cjs/memory/pinecone/semantic.d.ts +0 -44
  176. package/dist/cjs/memory/pinecone/semantic.d.ts.map +0 -1
  177. package/dist/cjs/memory/pinecone/semantic.js +0 -90
  178. package/dist/cjs/memory/sqlite/index.d.ts +0 -3
  179. package/dist/cjs/memory/sqlite/index.d.ts.map +0 -1
  180. package/dist/cjs/memory/sqlite/index.js +0 -5
  181. package/dist/cjs/memory/sqlite/memory-adapter.d.ts +0 -58
  182. package/dist/cjs/memory/sqlite/memory-adapter.d.ts.map +0 -1
  183. package/dist/cjs/memory/sqlite/memory-adapter.js +0 -336
  184. package/dist/cjs/memory/sqlite/schema.d.ts +0 -4
  185. package/dist/cjs/memory/sqlite/schema.d.ts.map +0 -1
  186. package/dist/cjs/memory/sqlite/schema.js +0 -91
  187. package/dist/cjs/memory/supabase/index.d.ts +0 -7
  188. package/dist/cjs/memory/supabase/index.d.ts.map +0 -1
  189. package/dist/cjs/memory/supabase/index.js +0 -8
  190. package/dist/cjs/memory/supabase/memory-adapter.d.ts +0 -67
  191. package/dist/cjs/memory/supabase/memory-adapter.d.ts.map +0 -1
  192. package/dist/cjs/memory/supabase/memory-adapter.js +0 -335
  193. package/dist/cjs/memory/supabase/semantic.d.ts +0 -44
  194. package/dist/cjs/memory/supabase/semantic.d.ts.map +0 -1
  195. package/dist/cjs/memory/supabase/semantic.js +0 -72
  196. package/dist/esm/embedding/cloudflare.d.ts +0 -15
  197. package/dist/esm/embedding/cloudflare.d.ts.map +0 -1
  198. package/dist/esm/embedding/cloudflare.js +0 -13
  199. package/dist/esm/embedding/cohere.d.ts +0 -8
  200. package/dist/esm/embedding/cohere.d.ts.map +0 -1
  201. package/dist/esm/embedding/cohere.js +0 -28
  202. package/dist/esm/embedding/index.d.ts +0 -9
  203. package/dist/esm/embedding/index.d.ts.map +0 -1
  204. package/dist/esm/embedding/index.js +0 -4
  205. package/dist/esm/embedding/local.d.ts +0 -6
  206. package/dist/esm/embedding/local.d.ts.map +0 -1
  207. package/dist/esm/embedding/local.js +0 -25
  208. package/dist/esm/embedding/openai.d.ts +0 -9
  209. package/dist/esm/embedding/openai.d.ts.map +0 -1
  210. package/dist/esm/embedding/openai.js +0 -23
  211. package/dist/esm/events/console.d.ts +0 -25
  212. package/dist/esm/events/console.d.ts.map +0 -1
  213. package/dist/esm/events/console.js +0 -37
  214. package/dist/esm/events/webhook.d.ts +0 -30
  215. package/dist/esm/events/webhook.d.ts.map +0 -1
  216. package/dist/esm/events/webhook.js +0 -75
  217. package/dist/esm/memory/cloudflare/index.d.ts +0 -3
  218. package/dist/esm/memory/cloudflare/index.d.ts.map +0 -1
  219. package/dist/esm/memory/cloudflare/index.js +0 -1
  220. package/dist/esm/memory/cloudflare/vectorize.d.ts +0 -62
  221. package/dist/esm/memory/cloudflare/vectorize.d.ts.map +0 -1
  222. package/dist/esm/memory/cloudflare/vectorize.js +0 -51
  223. package/dist/esm/memory/in-memory-semantic.d.ts +0 -16
  224. package/dist/esm/memory/in-memory-semantic.d.ts.map +0 -1
  225. package/dist/esm/memory/in-memory-semantic.js +0 -53
  226. package/dist/esm/memory/pinecone/index.d.ts +0 -7
  227. package/dist/esm/memory/pinecone/index.d.ts.map +0 -1
  228. package/dist/esm/memory/pinecone/index.js +0 -3
  229. package/dist/esm/memory/pinecone/memory-adapter.d.ts +0 -62
  230. package/dist/esm/memory/pinecone/memory-adapter.d.ts.map +0 -1
  231. package/dist/esm/memory/pinecone/memory-adapter.js +0 -216
  232. package/dist/esm/memory/pinecone/semantic.d.ts +0 -44
  233. package/dist/esm/memory/pinecone/semantic.d.ts.map +0 -1
  234. package/dist/esm/memory/pinecone/semantic.js +0 -86
  235. package/dist/esm/memory/sqlite/index.d.ts +0 -3
  236. package/dist/esm/memory/sqlite/index.d.ts.map +0 -1
  237. package/dist/esm/memory/sqlite/index.js +0 -1
  238. package/dist/esm/memory/sqlite/memory-adapter.d.ts +0 -58
  239. package/dist/esm/memory/sqlite/memory-adapter.d.ts.map +0 -1
  240. package/dist/esm/memory/sqlite/memory-adapter.js +0 -296
  241. package/dist/esm/memory/sqlite/schema.d.ts +0 -4
  242. package/dist/esm/memory/sqlite/schema.d.ts.map +0 -1
  243. package/dist/esm/memory/sqlite/schema.js +0 -86
  244. package/dist/esm/memory/supabase/index.d.ts +0 -7
  245. package/dist/esm/memory/supabase/index.d.ts.map +0 -1
  246. package/dist/esm/memory/supabase/index.js +0 -3
  247. package/dist/esm/memory/supabase/memory-adapter.d.ts +0 -67
  248. package/dist/esm/memory/supabase/memory-adapter.d.ts.map +0 -1
  249. package/dist/esm/memory/supabase/memory-adapter.js +0 -331
  250. package/dist/esm/memory/supabase/semantic.d.ts +0 -44
  251. package/dist/esm/memory/supabase/semantic.d.ts.map +0 -1
  252. package/dist/esm/memory/supabase/semantic.js +0 -68
@@ -1,114 +1,56 @@
1
1
  import { resolveLLM } from './llm/resolve';
2
- import { isMemoryConfig } from './memory/types';
3
- import { recallMemories, buildMemoryBlock, stripMetaTags } from './memory/recall';
4
2
  import { MolrooApiError } from './errors';
5
3
  import { createApiClient, unwrap } from './api-client';
6
- /** Clamp appraisal values to engine-valid ranges after LLM generation. */
7
- function clampAppraisal(a) {
8
- const c = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
9
- return {
10
- goal_relevance: c(a.goal_relevance, -1, 1),
11
- goal_congruence: c(a.goal_congruence, -1, 1),
12
- expectedness: c(a.expectedness, 0, 1),
13
- controllability: c(a.controllability, 0, 1),
14
- agency: c(a.agency, -1, 1),
15
- norm_compatibility: c(a.norm_compatibility, -1, 1),
16
- internal_standards: c(a.internal_standards ?? 0, -1, 1),
17
- adjustment_potential: c(a.adjustment_potential ?? 0.5, 0, 1),
18
- urgency: c(a.urgency ?? 0.5, 0, 1),
19
- };
20
- }
21
- /** Build a system prompt section describing the interlocutor. */
22
- function buildInterlocutorBlock(ctx) {
23
- const parts = [`## About ${ctx.name}`];
24
- if (ctx.description)
25
- parts.push(ctx.description);
26
- if (ctx.extensions) {
27
- for (const [title, content] of Object.entries(ctx.extensions)) {
28
- parts.push(`### ${title}`);
29
- parts.push(content);
30
- }
31
- }
32
- return parts.join('\n');
33
- }
4
+ import { postPerceive } from './persona/memory-pipeline';
5
+ import { chat as chatOrchestrator } from './persona/chat-orchestrator';
6
+ // ── SDK Persona Types ──
7
+ const DEFAULT_BASE_URL = 'https://api.molroo.io';
34
8
  // ── MolrooPersona ──
35
9
  /**
36
10
  * SDK client for interacting with a standalone molroo persona instance.
37
11
  *
38
- * @example
39
- * ```typescript
40
- * // Single adapter (recommended)
41
- * const persona = await MolrooPersona.create(
42
- * { baseUrl: 'https://api.molroo.io', apiKey: 'key', llm,
43
- * memory: new SqliteMemoryAdapter({ dbPath: './memory.db' }) },
44
- * { identity: { name: 'Sera' }, personality: { O: 0.8, C: 0.6, E: 0.7 } },
45
- * );
46
- *
47
- * // Split adapters (advanced)
48
- * const persona = await MolrooPersona.create(
49
- * { baseUrl: 'https://api.molroo.io', apiKey: 'key', llm,
50
- * memory: { episodes: episodeStore, semantic: vectorStore, embedding: embedProvider } },
51
- * { identity: { name: 'Sera' }, personality: { O: 0.8, C: 0.6, E: 0.7 } },
52
- * );
53
- * ```
12
+ * @deprecated Use {@link Molroo} from './world/world' instead.
54
13
  */
55
14
  export class MolrooPersona {
56
15
  constructor(config) {
57
- this.client = createApiClient(config.baseUrl, config.apiKey);
16
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
17
+ this.client = createApiClient(baseUrl, config.apiKey);
58
18
  this._personaId = config.personaId || '';
59
19
  this.llm = config.llm ?? null;
60
20
  this.engineLlm = config.engineLlm ?? null;
61
21
  this.events = config.events ?? null;
62
- // Memory infrastructure
63
- if (config.memory) {
64
- if (isMemoryConfig(config.memory)) {
65
- // Split adapter path (MemoryConfig)
66
- this.memoryAdapter = null;
67
- this.episodeStore = config.memory.episodes;
68
- this.semanticStore = config.memory.semantic ?? null;
69
- this.embeddingProvider = config.memory.embedding ?? null;
70
- this.memoryRecallConfig = config.memory.recall;
71
- }
72
- else {
73
- // Single adapter path (MemoryAdapter)
74
- this.memoryAdapter = config.memory;
75
- this.episodeStore = null;
76
- this.semanticStore = null;
77
- this.embeddingProvider = null;
78
- this.memoryRecallConfig = config.recall;
79
- }
80
- }
81
- else {
82
- this.memoryAdapter = null;
83
- this.episodeStore = null;
84
- this.semanticStore = null;
85
- this.embeddingProvider = null;
86
- this.memoryRecallConfig = undefined;
87
- }
22
+ this.memoryAdapter = config.memory ?? null;
23
+ this.memoryRecallConfig = config.recall;
88
24
  }
89
25
  // ── Properties ──
90
- /** Unique identifier for this persona instance. */
91
26
  get id() {
92
27
  return this._personaId;
93
28
  }
94
- /** Unique identifier for this persona instance (alias for {@link id}). */
95
29
  get personaId() {
96
30
  return this._personaId;
97
31
  }
98
- // ── Static Factory Methods ──
32
+ static async create(config, input) {
33
+ // Handle description string (auto-generate via LLM)
34
+ if (typeof input === 'string') {
35
+ if (!config.llm) {
36
+ throw new MolrooApiError('LLM adapter is required when using description string. ' +
37
+ 'Provide llm option or use explicit PersonaConfigData.', 'LLM_REQUIRED', 400);
38
+ }
39
+ const { generatePersona } = await import('./generate/persona');
40
+ const llm = await resolveLLM(config.llm);
41
+ const personaConfig = await generatePersona(llm, input);
42
+ return MolrooPersona.createWithConfig(config, personaConfig);
43
+ }
44
+ // Handle explicit PersonaConfigData
45
+ return MolrooPersona.createWithConfig(config, input);
46
+ }
99
47
  /**
100
- * Create a new persona on the molroo API and return a connected instance.
101
- *
102
- * @param config - API connection config and optional LLM adapter.
103
- * Accepts a full {@link LLMAdapter} or a shorthand config object:
104
- * ```typescript
105
- * llm: { provider: 'openai', apiKey: '...' }
106
- * ```
107
- * @param personaConfig - Persona identity, personality, and goals.
108
- * @returns A connected MolrooPersona instance.
48
+ * Internal implementation for creating persona with resolved config.
49
+ * @internal
109
50
  */
110
- static async create(config, personaConfig) {
111
- const client = createApiClient(config.baseUrl, config.apiKey);
51
+ static async createWithConfig(config, personaConfig) {
52
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
53
+ const client = createApiClient(baseUrl, config.apiKey);
112
54
  const { data } = await client.POST('/personas', {
113
55
  body: { config: personaConfig },
114
56
  headers: { 'Idempotency-Key': crypto.randomUUID() },
@@ -118,75 +60,34 @@ export class MolrooPersona {
118
60
  config.engineLlm ? resolveLLM(config.engineLlm) : undefined,
119
61
  ]);
120
62
  const result = unwrap(data);
121
- const persona = new MolrooPersona({ ...config, llm, engineLlm, personaId: result.personaId });
122
- return persona;
63
+ return new MolrooPersona({ ...config, llm, engineLlm, personaId: result.personaId });
123
64
  }
124
65
  /**
125
- * Generate a persona from a natural-language description, then create it on the API.
126
- *
127
- * Combines {@link generatePersona} + {@link MolrooPersona.create} in one step.
128
- *
129
- * @param config - API connection config and optional LLM adapter.
130
- * @param description - Natural-language description of the persona to generate.
131
- * @returns A connected MolrooPersona instance with a generated config.
132
- *
133
- * @example
134
- * ```typescript
135
- * const sera = await MolrooPersona.generate(
136
- * { baseUrl: 'https://api.molroo.io', apiKey: 'mk_live_...', llm: { provider: 'openai', apiKey: '...' } },
137
- * 'A warm and friendly barista named Sera who loves coffee and people.',
138
- * );
139
- * ```
66
+ * Generate a persona from description and create it.
67
+ * @deprecated Use `create(config, description)` instead.
140
68
  */
141
69
  static async generate(config, description) {
142
- const { generatePersona } = await import('./generate/persona');
143
- const llm = await resolveLLM(config.llm);
144
- const personaConfig = await generatePersona(llm, description);
145
- return MolrooPersona.create(config, personaConfig);
70
+ return MolrooPersona.create(config, description);
146
71
  }
147
- /**
148
- * Connect to an existing persona by ID.
149
- *
150
- * Verifies the persona exists and fetches its configuration.
151
- *
152
- * @param config - API connection config and optional LLM adapter.
153
- * Accepts a full {@link LLMAdapter} or a shorthand config object:
154
- * ```typescript
155
- * llm: { provider: 'openai', apiKey: '...' }
156
- * ```
157
- * @param personaId - The ID of the persona to connect to.
158
- * @returns A connected MolrooPersona instance.
159
- */
160
72
  static async connect(config, personaId) {
161
73
  const [llm, engineLlm] = await Promise.all([
162
74
  config.llm ? resolveLLM(config.llm) : undefined,
163
75
  config.engineLlm ? resolveLLM(config.engineLlm) : undefined,
164
76
  ]);
77
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
165
78
  const persona = new MolrooPersona({ ...config, llm, engineLlm, personaId });
166
- // Verify persona exists
167
79
  await persona.client.GET('/personas/{id}', {
168
80
  params: { path: { id: personaId } },
169
81
  });
170
82
  return persona;
171
83
  }
172
- /**
173
- * List all personas for the authenticated tenant.
174
- *
175
- * @param config - API connection config (baseUrl + apiKey).
176
- * @returns List of persona summaries with optional pagination cursor.
177
- */
178
84
  static async listPersonas(config) {
179
- const client = createApiClient(config.baseUrl, config.apiKey);
85
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
86
+ const client = createApiClient(baseUrl, config.apiKey);
180
87
  const { data } = await client.GET('/personas');
181
88
  return unwrap(data);
182
89
  }
183
90
  // ── Runtime ──
184
- /**
185
- * Send a message to the persona and get an emotion-processed response.
186
- *
187
- * Internally converts the SDK-friendly options into the API wire format
188
- * (`{ event: PerceiveEvent, context?: PerceiveContext }`).
189
- */
190
91
  async perceive(message, options) {
191
92
  const eventType = options?.type ?? 'chat_message';
192
93
  const sourceName = typeof options?.from === 'string'
@@ -211,196 +112,32 @@ export class MolrooPersona {
211
112
  body: { event, context },
212
113
  });
213
114
  const response = unwrap(data);
214
- // Save episode to memory (unless caller opts out)
215
- if (!options?.skipMemory && response.memoryEpisode) {
216
- // Tag episode with event type so recall can distinguish chat vs events
115
+ if (!options?.skipMemory && response.memoryEpisode && this.memoryAdapter) {
217
116
  if (!response.memoryEpisode.type) {
218
117
  response.memoryEpisode.type = eventType;
219
118
  }
220
- this.postPerceivePipeline(response);
119
+ postPerceive(this.memoryPipelineDeps, response);
221
120
  }
222
121
  return response;
223
122
  }
224
- /**
225
- * Fire a non-chat event and process through the emotion engine.
226
- *
227
- * Unlike chat(), this does not involve LLM generation — it directly
228
- * sends an event to the perceive endpoint with a required appraisal vector.
229
- * The resulting episode is saved to memory and can be recalled during chat().
230
- *
231
- * @param type - Event type identifier (e.g. 'attack', 'gift', 'rest').
232
- * @param description - Human-readable event description (used as message context).
233
- * @param options - Must include `appraisal`. Optionally `from`, `stimulus`, `payload`.
234
- * @returns Emotion engine response.
235
- *
236
- * @example
237
- * ```typescript
238
- * await persona.event('attack', 'goblin attacks with sword', {
239
- * from: 'goblin',
240
- * appraisal: {
241
- * goal_relevance: 0.8,
242
- * goal_congruence: -0.9,
243
- * expectedness: 0.3,
244
- * controllability: 0.4,
245
- * agency: -0.6,
246
- * norm_compatibility: -0.5,
247
- * },
248
- * });
249
- * ```
250
- */
251
123
  async event(type, description, options) {
252
124
  return this.perceive(description, { ...options, type });
253
125
  }
254
- /**
255
- * High-level chat: getState → LLM generate → perceive with appraisal.
256
- *
257
- * 1. Calls `getPromptContext()` for the server-assembled system prompt
258
- * 2. Recalls episodic + semantic memories from client-side stores
259
- * 3. Sends to LLM adapter for text generation + appraisal
260
- * 4. Sends the appraisal to the API via `perceive()` for emotion computation
261
- * 5. Runs post-chat pipeline (episode save, reflection, events)
262
- *
263
- * Requires {@link LLMAdapter}. Without one, use {@link perceive} directly
264
- * for emotion-only interaction.
265
- */
266
126
  async chat(message, options) {
267
- const fromOption = options?.from ?? 'user';
268
- const from = typeof fromOption === 'string' ? fromOption : fromOption.name;
269
- const interlocutor = typeof fromOption === 'object' ? fromOption : null;
270
127
  const llm = this.requireLLM();
271
- // 1. Fetch server-assembled system prompt
272
- const ctx = await this.getPromptContext(options?.consumerSuffix, from);
273
- let systemPrompt = ctx.systemPrompt;
274
- const hasTools = (ctx.tools?.length ?? 0) > 0;
275
- // 2. Recall memories (episodic + semantic + reflections)
276
- const recalled = await recallMemories(message, {
128
+ const deps = {
129
+ personaId: this._personaId,
130
+ llm,
131
+ engineLlm: this.engineLlm,
277
132
  memoryAdapter: this.memoryAdapter,
278
- episodeStore: this.episodeStore,
279
- semanticStore: this.semanticStore,
280
- embeddingProvider: this.embeddingProvider,
281
- config: this.memoryRecallConfig,
282
- sourceEntity: from,
283
- reflectionEntity: this._personaId,
284
- });
285
- if (interlocutor) {
286
- systemPrompt += '\n\n' + buildInterlocutorBlock(interlocutor);
287
- }
288
- const memoryBlock = buildMemoryBlock(recalled.episodic, recalled.semantic, recalled.reflections);
289
- if (memoryBlock) {
290
- systemPrompt += '\n\n' + memoryBlock;
291
- }
292
- // 3. LLM Generate (SDK side, user's API key)
293
- const messages = [];
294
- if (options?.history) {
295
- messages.push(...options.history.map(h => ({ role: h.role, content: h.content })));
296
- }
297
- messages.push({ role: 'user', content: message });
298
- let responseText;
299
- let appraisal;
300
- let earlyPerceiveResponse;
301
- // Split mode: engineLlm handles appraisal, primary llm handles response text
302
- if (this.engineLlm && this.engineLlm !== llm) {
303
- const { AppraisalVectorSchema } = await import('./llm/schema');
304
- const appraisalInstruction = [
305
- '',
306
- '## Appraisal Task',
307
- "Evaluate how RECEIVING the user's message affects this persona emotionally.",
308
- 'The event you are appraising is the message itself arriving — not the described situation.',
309
- "When the user shares their own experience (e.g. loss, success), appraise through the persona's relational goals: caring about the speaker makes their suffering relevant and incongruent.",
310
- "Rate each dimension from the persona's subjective perspective.",
311
- 'An insult should produce negative goal_congruence and norm_compatibility.',
312
- 'A compliment should produce positive values. Neutral small talk should be near zero.',
313
- ].join('\n');
314
- // 3a. engineLlm → appraisal (based on prior emotional state)
315
- // Only send last few messages for appraisal context (full history is wasteful)
316
- const appraisalMessages = messages.length <= 5 ? messages : messages.slice(-5);
317
- const { object: appraisalResult } = await this.engineLlm.generateObject({
318
- system: systemPrompt + appraisalInstruction,
319
- messages: appraisalMessages,
320
- schema: AppraisalVectorSchema,
321
- });
322
- appraisal = clampAppraisal(appraisalResult);
323
- // 3b. perceive with user message → engine updates emotion before response generation
324
- earlyPerceiveResponse = await this.perceive(message, {
325
- from,
326
- appraisal,
327
- priorEpisodes: recalled.episodic.length > 0 ? recalled.episodic : undefined,
328
- skipMemory: true, // chat() runs its own postChatPipeline
329
- });
330
- // 3c. Fetch updated prompt reflecting new emotional state
331
- let updatedPrompt = systemPrompt;
332
- try {
333
- const updatedCtx = await this.getPromptContext(options?.consumerSuffix, from);
334
- updatedPrompt = updatedCtx.systemPrompt;
335
- if (interlocutor) {
336
- updatedPrompt += '\n\n' + buildInterlocutorBlock(interlocutor);
337
- }
338
- if (memoryBlock) {
339
- updatedPrompt += '\n\n' + memoryBlock;
340
- }
341
- }
342
- catch {
343
- // Use original prompt if updated fetch fails
344
- }
345
- // 3d. primary llm → response (with updated emotional state in prompt)
346
- const { text } = await llm.generateText({
347
- system: updatedPrompt,
348
- messages,
349
- });
350
- responseText = text;
351
- }
352
- else if (hasTools) {
353
- // Combined mode with tool-use: LLM can request memory searches
354
- const result = await this.generateWithToolLoop(llm, systemPrompt, messages, options?.onToolCall);
355
- responseText = result.text;
356
- appraisal = clampAppraisal(result.appraisal);
357
- }
358
- else {
359
- // Combined mode (default): single LLM call for response + appraisal
360
- const { LLMResponseSchema } = await import('./llm/schema');
361
- const { object: llmResult } = await llm.generateObject({
362
- system: systemPrompt,
363
- messages,
364
- schema: LLMResponseSchema,
365
- });
366
- responseText = llmResult.response;
367
- appraisal = clampAppraisal(llmResult.appraisal ?? {
368
- goal_relevance: 0,
369
- goal_congruence: 0,
370
- expectedness: 0.5,
371
- controllability: 0.5,
372
- agency: 0,
373
- norm_compatibility: 0,
374
- internal_standards: 0,
375
- adjustment_potential: 0.5,
376
- urgency: 0.5,
377
- });
378
- }
379
- // 4. Send to API for emotion processing (skip if already done in split mode)
380
- let response;
381
- if (earlyPerceiveResponse) {
382
- response = earlyPerceiveResponse;
383
- }
384
- else {
385
- response = await this.perceive(responseText, {
386
- from,
387
- appraisal,
388
- priorEpisodes: recalled.episodic.length > 0 ? recalled.episodic : undefined,
389
- skipMemory: true, // chat() runs its own postChatPipeline
390
- });
391
- }
392
- // Inject LLM-generated text
393
- response.text = responseText;
394
- // 5. Post-chat pipeline (fire-and-forget: episode save, reflection, events)
395
- this.postChatPipeline(response);
396
- return { text: responseText, response };
133
+ memoryRecallConfig: this.memoryRecallConfig,
134
+ events: this.events,
135
+ getPromptContext: (suffix, source) => this.getPromptContext(suffix, source),
136
+ perceive: (msg, opts) => this.perceive(msg, opts),
137
+ searchMemory: (query) => this.searchMemory(query),
138
+ };
139
+ return chatOrchestrator(deps, message, options);
397
140
  }
398
- /**
399
- * Advance persona time by the specified number of seconds.
400
- *
401
- * @param seconds - Number of seconds to advance.
402
- * @returns Any pending events that were processed.
403
- */
404
141
  async tick(seconds) {
405
142
  const { data } = await this.client.POST('/personas/{id}/tick', {
406
143
  params: { path: { id: this._personaId } },
@@ -408,11 +145,6 @@ export class MolrooPersona {
408
145
  });
409
146
  return unwrap(data);
410
147
  }
411
- /**
412
- * Directly set the persona's emotion state in VAD space.
413
- *
414
- * @param vad - Partial VAD values to set (V: -1..1, A: 0..1, D: -1..1).
415
- */
416
148
  async setEmotion(vad) {
417
149
  await this.client.POST('/personas/{id}/emotion', {
418
150
  params: { path: { id: this._personaId } },
@@ -420,33 +152,18 @@ export class MolrooPersona {
420
152
  });
421
153
  }
422
154
  // ── State ──
423
- /**
424
- * Get the current emotional and psychological state of the persona.
425
- *
426
- * @returns Current emotion, mood, somatic, and narrative state.
427
- */
428
155
  async getState() {
429
156
  const { data } = await this.client.GET('/personas/{id}/state', {
430
157
  params: { path: { id: this._personaId } },
431
158
  });
432
159
  return unwrap(data);
433
160
  }
434
- /**
435
- * Get a full snapshot of the persona's internal state.
436
- *
437
- * @returns Complete persona snapshot for backup/restore.
438
- */
439
161
  async getSnapshot() {
440
162
  const { data } = await this.client.GET('/personas/{id}/snapshot', {
441
163
  params: { path: { id: this._personaId } },
442
164
  });
443
165
  return unwrap(data);
444
166
  }
445
- /**
446
- * Restore the persona's internal state from a snapshot.
447
- *
448
- * @param snapshot - The snapshot to restore.
449
- */
450
167
  async putSnapshot(snapshot) {
451
168
  await this.client.PUT('/personas/{id}/snapshot', {
452
169
  params: { path: { id: this._personaId } },
@@ -454,11 +171,6 @@ export class MolrooPersona {
454
171
  });
455
172
  }
456
173
  // ── Config ──
457
- /**
458
- * Patch the persona's configuration (identity, personality, goals).
459
- *
460
- * @param updates - Configuration updates to apply.
461
- */
462
174
  async patch(updates) {
463
175
  await this.client.PATCH('/personas/{id}', {
464
176
  params: { path: { id: this._personaId } },
@@ -466,7 +178,6 @@ export class MolrooPersona {
466
178
  });
467
179
  }
468
180
  // ── Lifecycle ──
469
- /** Soft-delete this persona. Can be restored with {@link restore}. */
470
181
  async destroy() {
471
182
  if (this._personaId) {
472
183
  await this.client.DELETE('/personas/{id}', {
@@ -474,7 +185,6 @@ export class MolrooPersona {
474
185
  });
475
186
  }
476
187
  }
477
- /** Restore a previously soft-deleted persona. */
478
188
  async restore() {
479
189
  if (this._personaId) {
480
190
  await this.client.POST('/personas/{id}/restore', {
@@ -482,216 +192,7 @@ export class MolrooPersona {
482
192
  });
483
193
  }
484
194
  }
485
- // ── Private Helpers ──
486
- /** Whether memory is available (either single adapter or split stores). */
487
- get hasMemory() {
488
- return this.memoryAdapter !== null || this.episodeStore !== null;
489
- }
490
- // ── Post-Chat Pipeline (fire-and-forget) ──
491
- /**
492
- * Lightweight pipeline for perceive()-only interactions (non-chat events).
493
- * Saves the episode and emits events, but skips LLM reflection.
494
- */
495
- postPerceivePipeline(response) {
496
- if (!response.memoryEpisode)
497
- return;
498
- if (this.memoryAdapter) {
499
- // Single adapter path — adapter handles semantic indexing internally
500
- this.memoryAdapter.saveEpisode(response.memoryEpisode).catch(() => { });
501
- }
502
- else if (this.episodeStore) {
503
- // Split adapter path
504
- this.episodeStore.saveEpisode(response.memoryEpisode).catch(() => { });
505
- if (this.semanticStore && this.embeddingProvider && response.memoryEpisode.context) {
506
- const ep = response.memoryEpisode;
507
- this.embeddingProvider
508
- .embed(stripMetaTags(ep.context))
509
- .then((embedding) => {
510
- this.semanticStore.index({
511
- id: ep.id,
512
- embedding,
513
- metadata: {
514
- type: 'episode',
515
- sourceEntity: ep.sourceEntity,
516
- timestamp: ep.timestamp,
517
- importance: ep.importance,
518
- episodeType: ep.type,
519
- },
520
- });
521
- })
522
- .catch(() => { });
523
- }
524
- }
525
- // Event emission
526
- if (this.events) {
527
- this.emitResponseEvents(response, Date.now()).catch(() => { });
528
- }
529
- }
530
- postChatPipeline(response) {
531
- const now = Date.now();
532
- if (this.memoryAdapter) {
533
- // Single adapter path — adapter handles semantic indexing internally
534
- if (response.memoryEpisode) {
535
- this.memoryAdapter.saveEpisode(response.memoryEpisode).catch(() => { });
536
- }
537
- // Reflection generation
538
- if ((this.engineLlm || this.llm) && response.reflectionPrompt) {
539
- this.handleReflection(response.reflectionPrompt, response.emotion.vad).catch(() => { });
540
- }
541
- }
542
- else {
543
- // Split adapter path
544
- // 1. Episode storage + semantic indexing
545
- if (this.episodeStore && response.memoryEpisode) {
546
- this.episodeStore.saveEpisode(response.memoryEpisode).catch(() => { });
547
- if (this.semanticStore && this.embeddingProvider && response.memoryEpisode.context) {
548
- const ep = response.memoryEpisode;
549
- this.embeddingProvider
550
- .embed(stripMetaTags(ep.context))
551
- .then((embedding) => {
552
- this.semanticStore.index({
553
- id: ep.id,
554
- embedding,
555
- metadata: {
556
- type: 'episode',
557
- sourceEntity: ep.sourceEntity,
558
- timestamp: ep.timestamp,
559
- importance: ep.importance,
560
- episodeType: ep.type,
561
- },
562
- });
563
- })
564
- .catch(() => { });
565
- }
566
- }
567
- // 2. Reflection generation (requires LLM + episodeStore)
568
- if (this.episodeStore && (this.engineLlm || this.llm) && response.reflectionPrompt) {
569
- this.handleReflection(response.reflectionPrompt, response.emotion.vad).catch(() => { });
570
- }
571
- }
572
- // 3. Event emission
573
- if (this.events) {
574
- this.emitResponseEvents(response, now).catch(() => { });
575
- }
576
- }
577
- async handleReflection(prompt, emotionVad) {
578
- const reflectionLlm = this.engineLlm ?? this.llm;
579
- if (!reflectionLlm)
580
- return;
581
- if (!this.memoryAdapter && !this.episodeStore)
582
- return;
583
- const { text } = await reflectionLlm.generateText({
584
- system: prompt.system,
585
- messages: [{ role: 'user', content: prompt.user }],
586
- });
587
- const reflection = {
588
- id: `ref-${Date.now()}`,
589
- timestamp: Date.now(),
590
- sourceEntity: this._personaId,
591
- content: text,
592
- trigger: 'interaction',
593
- emotionSnapshot: emotionVad ?? { V: 0, A: 0, D: 0 },
594
- };
595
- if (this.memoryAdapter) {
596
- // Single adapter path — save reflection if supported
597
- if (this.memoryAdapter.saveReflection) {
598
- await this.memoryAdapter.saveReflection(reflection);
599
- }
600
- }
601
- else if (this.episodeStore) {
602
- // Split adapter path
603
- await this.episodeStore.saveReflection(reflection);
604
- if (this.semanticStore && this.embeddingProvider) {
605
- try {
606
- const embedding = await this.embeddingProvider.embed(text);
607
- await this.semanticStore.index({
608
- id: reflection.id,
609
- embedding,
610
- metadata: {
611
- type: 'reflection',
612
- sourceEntity: this._personaId,
613
- timestamp: reflection.timestamp,
614
- },
615
- });
616
- }
617
- catch {
618
- // semantic indexing is optional
619
- }
620
- }
621
- }
622
- await this.events?.emit({
623
- type: 'reflection_generated',
624
- personaId: this._personaId,
625
- timestamp: Date.now(),
626
- payload: { trigger: 'interaction' },
627
- });
628
- }
629
- async emitResponseEvents(response, now) {
630
- if (!this.events)
631
- return;
632
- const batch = [];
633
- if (response.emotion) {
634
- batch.push({
635
- type: 'emotion_changed',
636
- personaId: this._personaId,
637
- timestamp: now,
638
- payload: {
639
- vad: response.emotion.vad,
640
- primary: response.emotion.discrete.primary,
641
- intensity: response.emotion.discrete.intensity,
642
- },
643
- });
644
- }
645
- if (response.socialUpdates?.length) {
646
- batch.push({
647
- type: 'relationship_changed',
648
- personaId: this._personaId,
649
- timestamp: now,
650
- payload: { updates: response.socialUpdates },
651
- });
652
- }
653
- if (response.memoryEpisode) {
654
- batch.push({
655
- type: 'memory_consolidated',
656
- personaId: this._personaId,
657
- timestamp: now,
658
- payload: { episodeId: response.memoryEpisode.id },
659
- });
660
- }
661
- if (response.stageTransition) {
662
- batch.push({ type: 'stage_transition', personaId: this._personaId, timestamp: now, payload: {} });
663
- }
664
- if (response.maskExposure) {
665
- batch.push({ type: 'mask_exposure', personaId: this._personaId, timestamp: now, payload: response.maskExposure });
666
- }
667
- if (response.goalChanges) {
668
- batch.push({ type: 'goal_changed', personaId: this._personaId, timestamp: now, payload: response.goalChanges });
669
- }
670
- if (batch.length === 0)
671
- return;
672
- if (this.events.emitBatch) {
673
- await this.events.emitBatch(batch);
674
- }
675
- else {
676
- for (const event of batch) {
677
- await this.events.emit(event);
678
- }
679
- }
680
- }
681
- buildAppraisalHint(appraisal) {
682
- return [
683
- '## Emotional Evaluation (already computed — embody this, do not describe it)',
684
- `Goal relevance: ${appraisal.goal_relevance.toFixed(2)}, congruence: ${appraisal.goal_congruence.toFixed(2)}`,
685
- `Expectedness: ${appraisal.expectedness.toFixed(2)}, controllability: ${appraisal.controllability.toFixed(2)}`,
686
- `Agency: ${appraisal.agency.toFixed(2)}, norm compatibility: ${appraisal.norm_compatibility.toFixed(2)}`,
687
- ].join('\n');
688
- }
689
- requireLLM() {
690
- if (!this.llm) {
691
- throw new MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', 'LLM_NOT_CONFIGURED', 400);
692
- }
693
- return this.llm;
694
- }
195
+ // ── Prompt / Memory ──
695
196
  async getPromptContext(consumerSuffix, sourceEntity) {
696
197
  const { data } = await this.client.POST('/personas/{id}/prompt-context', {
697
198
  params: { path: { id: this._personaId } },
@@ -702,10 +203,6 @@ export class MolrooPersona {
702
203
  });
703
204
  return unwrap(data);
704
205
  }
705
- /**
706
- * Search persona's episodic memory via the API.
707
- * Used internally by the tool-use loop.
708
- */
709
206
  async searchMemory(query, options) {
710
207
  const { data } = await this.client.POST('/personas/{id}/memory/search', {
711
208
  params: { path: { id: this._personaId } },
@@ -718,93 +215,20 @@ export class MolrooPersona {
718
215
  const result = unwrap(data);
719
216
  return result.episodes;
720
217
  }
721
- /**
722
- * LLM generation loop with tool-use support for standalone persona.
723
- * Uses LLMResponseWithToolsSchema which allows the LLM to request
724
- * memory searches via the search_memory field. Capped at 3 iterations.
725
- */
726
- async generateWithToolLoop(llm, system, messages, onToolCall) {
727
- const MAX_TOOL_ITERATIONS = 3;
728
- const { LLMResponseWithToolsSchema, LLMResponseSchema } = await import('./llm/schema');
729
- let currentSystem = system;
730
- for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
731
- const { object: llmResult } = await llm.generateObject({
732
- system: currentSystem,
733
- messages,
734
- schema: LLMResponseWithToolsSchema,
735
- });
736
- // If no tool call requested, return the response
737
- if (!llmResult.search_memory) {
738
- const text = llmResult.response;
739
- const appraisal = llmResult.appraisal ?? {
740
- goal_relevance: 0,
741
- goal_congruence: 0,
742
- expectedness: 0.5,
743
- controllability: 0.5,
744
- agency: 0,
745
- norm_compatibility: 0,
746
- internal_standards: 0,
747
- adjustment_potential: 0.5,
748
- urgency: 0.5,
749
- };
750
- return { text, appraisal };
751
- }
752
- // Execute memory search tool call
753
- const query = llmResult.search_memory;
754
- let episodes = [];
755
- try {
756
- episodes = await this.searchMemory(query);
757
- }
758
- catch {
759
- // Memory search failed — continue without results
760
- }
761
- // Notify callback if provided
762
- if (onToolCall) {
763
- onToolCall({
764
- name: 'search_memory',
765
- args: { query },
766
- result: episodes,
767
- });
768
- }
769
- // Inject memory search results into system prompt for next iteration
770
- const resultBlock = episodes.length > 0
771
- ? `\n\n## Memory Search Results (query: "${query}")\n${episodes.map(ep => {
772
- const ts = ep.timestamp ? new Date(ep.timestamp).toISOString() : 'unknown';
773
- const source = ep.sourceEntity ?? 'unknown';
774
- const context = ep.context ?? 'no context';
775
- return `- [${ts}] ${source}: ${context}`;
776
- }).join('\n')}`
777
- : `\n\n## Memory Search Results (query: "${query}")\nNo matching memories found.`;
778
- currentSystem = currentSystem + resultBlock;
779
- }
780
- // Exhausted tool iterations — make a final call without tool schema
781
- const { object: finalResult } = await llm.generateObject({
782
- system: currentSystem,
783
- messages,
784
- schema: LLMResponseSchema,
785
- });
786
- const text = finalResult.response;
787
- const appraisal = finalResult.appraisal ?? {
788
- goal_relevance: 0,
789
- goal_congruence: 0,
790
- expectedness: 0.5,
791
- controllability: 0.5,
792
- agency: 0,
793
- norm_compatibility: 0,
794
- internal_standards: 0,
795
- adjustment_potential: 0.5,
796
- urgency: 0.5,
218
+ // ── Private ──
219
+ get memoryPipelineDeps() {
220
+ return {
221
+ personaId: this._personaId,
222
+ memoryAdapter: this.memoryAdapter,
223
+ events: this.events,
224
+ llm: this.llm,
225
+ engineLlm: this.engineLlm,
797
226
  };
798
- return { text, appraisal };
799
227
  }
800
- buildContextBlock(state, from) {
801
- const parts = [];
802
- if (from) {
803
- parts.push(`Speaking with: ${from}.`);
804
- }
805
- if (state.narrative) {
806
- parts.push(`Narrative tone: ${state.narrative.tone.toFixed(2)}, agency: ${state.narrative.agency.toFixed(2)}`);
228
+ requireLLM() {
229
+ if (!this.llm) {
230
+ throw new MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', 'LLM_NOT_CONFIGURED', 400);
807
231
  }
808
- return parts.join('\n');
232
+ return this.llm;
809
233
  }
810
234
  }