@molroo-io/sdk 0.8.4 → 0.9.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 (73) hide show
  1. package/dist/cjs/errors.d.ts +1 -1
  2. package/dist/cjs/errors.d.ts.map +1 -1
  3. package/dist/cjs/errors.js +2 -1
  4. package/dist/cjs/events/types.d.ts +1 -1
  5. package/dist/cjs/events/types.d.ts.map +1 -1
  6. package/dist/cjs/index.d.ts +4 -2
  7. package/dist/cjs/index.d.ts.map +1 -1
  8. package/dist/cjs/index.js +5 -1
  9. package/dist/cjs/llm/resolve.d.ts.map +1 -1
  10. package/dist/cjs/llm/resolve.js +2 -1
  11. package/dist/cjs/llm/schema.d.ts +0 -114
  12. package/dist/cjs/llm/schema.d.ts.map +1 -1
  13. package/dist/cjs/llm/schema.js +1 -44
  14. package/dist/cjs/persona/chat-orchestrator.d.ts +18 -3
  15. package/dist/cjs/persona/chat-orchestrator.d.ts.map +1 -1
  16. package/dist/cjs/persona/chat-orchestrator.js +29 -109
  17. package/dist/cjs/persona/conversation.d.ts +84 -0
  18. package/dist/cjs/persona/conversation.d.ts.map +1 -0
  19. package/dist/cjs/persona/conversation.js +72 -0
  20. package/dist/cjs/persona/memory-pipeline.d.ts.map +1 -1
  21. package/dist/cjs/persona/memory-pipeline.js +30 -4
  22. package/dist/cjs/persona.d.ts +441 -22
  23. package/dist/cjs/persona.d.ts.map +1 -1
  24. package/dist/cjs/persona.js +418 -6
  25. package/dist/cjs/shared/errors.d.ts +32 -2
  26. package/dist/cjs/shared/errors.d.ts.map +1 -1
  27. package/dist/cjs/shared/errors.js +33 -2
  28. package/dist/cjs/types.d.ts +70 -5
  29. package/dist/cjs/types.d.ts.map +1 -1
  30. package/dist/cjs/types.js +2 -1
  31. package/dist/cjs/world/world-persona.d.ts +20 -5
  32. package/dist/cjs/world/world-persona.d.ts.map +1 -1
  33. package/dist/cjs/world/world-persona.js +21 -5
  34. package/dist/cjs/world/world.d.ts +28 -5
  35. package/dist/cjs/world/world.d.ts.map +1 -1
  36. package/dist/cjs/world/world.js +29 -3
  37. package/dist/esm/errors.d.ts +1 -1
  38. package/dist/esm/errors.d.ts.map +1 -1
  39. package/dist/esm/errors.js +1 -1
  40. package/dist/esm/events/types.d.ts +1 -1
  41. package/dist/esm/events/types.d.ts.map +1 -1
  42. package/dist/esm/index.d.ts +4 -2
  43. package/dist/esm/index.d.ts.map +1 -1
  44. package/dist/esm/index.js +3 -1
  45. package/dist/esm/llm/resolve.d.ts.map +1 -1
  46. package/dist/esm/llm/resolve.js +2 -1
  47. package/dist/esm/llm/schema.d.ts +0 -114
  48. package/dist/esm/llm/schema.d.ts.map +1 -1
  49. package/dist/esm/llm/schema.js +0 -43
  50. package/dist/esm/persona/chat-orchestrator.d.ts +18 -3
  51. package/dist/esm/persona/chat-orchestrator.d.ts.map +1 -1
  52. package/dist/esm/persona/chat-orchestrator.js +29 -109
  53. package/dist/esm/persona/conversation.d.ts +84 -0
  54. package/dist/esm/persona/conversation.d.ts.map +1 -0
  55. package/dist/esm/persona/conversation.js +68 -0
  56. package/dist/esm/persona/memory-pipeline.d.ts.map +1 -1
  57. package/dist/esm/persona/memory-pipeline.js +30 -4
  58. package/dist/esm/persona.d.ts +441 -22
  59. package/dist/esm/persona.d.ts.map +1 -1
  60. package/dist/esm/persona.js +418 -7
  61. package/dist/esm/shared/errors.d.ts +32 -2
  62. package/dist/esm/shared/errors.d.ts.map +1 -1
  63. package/dist/esm/shared/errors.js +32 -1
  64. package/dist/esm/types.d.ts +70 -5
  65. package/dist/esm/types.d.ts.map +1 -1
  66. package/dist/esm/types.js +1 -1
  67. package/dist/esm/world/world-persona.d.ts +20 -5
  68. package/dist/esm/world/world-persona.d.ts.map +1 -1
  69. package/dist/esm/world/world-persona.js +21 -5
  70. package/dist/esm/world/world.d.ts +28 -5
  71. package/dist/esm/world/world.d.ts.map +1 -1
  72. package/dist/esm/world/world.js +29 -3
  73. package/package.json +1 -1
@@ -34,11 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.MolrooPersona = void 0;
37
+ exports.buildPrompt = buildPrompt;
37
38
  const resolve_1 = require("./llm/resolve");
38
39
  const errors_1 = require("./errors");
39
40
  const api_client_1 = require("./api-client");
40
41
  const memory_pipeline_1 = require("./persona/memory-pipeline");
41
42
  const chat_orchestrator_1 = require("./persona/chat-orchestrator");
43
+ const conversation_1 = require("./persona/conversation");
42
44
  // ── SDK Persona Types ──
43
45
  const DEFAULT_BASE_URL = 'https://api.molroo.io';
44
46
  // ── MolrooPersona ──
@@ -57,12 +59,13 @@ class MolrooPersona {
57
59
  this.events = config.events ?? null;
58
60
  this.memoryAdapter = config.memory ?? null;
59
61
  this.memoryRecallConfig = config.recall;
60
- this.appraisalMode = config.appraisalMode ?? 'direct';
61
62
  }
62
63
  // ── Properties ──
64
+ /** Unique persona instance ID. */
63
65
  get id() {
64
66
  return this._personaId;
65
67
  }
68
+ /** @deprecated Use {@link id} instead. */
66
69
  get personaId() {
67
70
  return this._personaId;
68
71
  }
@@ -71,7 +74,7 @@ class MolrooPersona {
71
74
  if (typeof input === 'string') {
72
75
  if (!config.llm) {
73
76
  throw new errors_1.MolrooApiError('LLM adapter is required when using description string. ' +
74
- 'Provide llm option or use explicit PersonaConfigData.', 'LLM_REQUIRED', 400);
77
+ 'Provide llm option or use explicit PersonaConfigData.', errors_1.MolrooErrorCode.LLM_REQUIRED, 400);
75
78
  }
76
79
  const { generatePersona } = await Promise.resolve().then(() => __importStar(require('./generate/persona')));
77
80
  const llm = await (0, resolve_1.resolveLLM)(config.llm);
@@ -106,6 +109,23 @@ class MolrooPersona {
106
109
  static async generate(config, description) {
107
110
  return MolrooPersona.create(config, description);
108
111
  }
112
+ /**
113
+ * Connect to an existing persona by ID.
114
+ *
115
+ * @param config - Connection configuration (apiKey required).
116
+ * @param personaId - The persona instance ID to connect to.
117
+ * @returns A connected MolrooPersona instance.
118
+ *
119
+ * @deprecated Use {@link Molroo.connectPersona} instead.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const sera = await MolrooPersona.connect(
124
+ * { apiKey: 'mk_...', llm: openaiAdapter },
125
+ * 'persona_abc123'
126
+ * );
127
+ * ```
128
+ */
109
129
  static async connect(config, personaId) {
110
130
  const [llm, engineLlm] = await Promise.all([
111
131
  config.llm ? (0, resolve_1.resolveLLM)(config.llm) : undefined,
@@ -118,6 +138,12 @@ class MolrooPersona {
118
138
  });
119
139
  return persona;
120
140
  }
141
+ /**
142
+ * List all personas for the authenticated tenant.
143
+ *
144
+ * @param config - API connection config.
145
+ * @returns Paginated list of persona summaries.
146
+ */
121
147
  static async listPersonas(config) {
122
148
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
123
149
  const client = (0, api_client_1.createApiClient)(baseUrl, config.apiKey);
@@ -125,6 +151,14 @@ class MolrooPersona {
125
151
  return (0, api_client_1.unwrap)(data);
126
152
  }
127
153
  // ── Runtime ──
154
+ /**
155
+ * Send a stimulus to the persona's emotion engine and get the updated emotional response.
156
+ * This is the low-level API — for common cases, prefer {@link hear}, {@link experience}, or {@link chat}.
157
+ *
158
+ * @param message - The stimulus text (user message, event description, etc.).
159
+ * @param options - Appraisal, source, relationship context, and other options.
160
+ * @returns Emotion engine response with VAD, discrete emotion, and side effects.
161
+ */
128
162
  async perceive(message, options) {
129
163
  const eventType = options?.type ?? 'chat_message';
130
164
  const sourceName = typeof options?.from === 'string'
@@ -161,9 +195,118 @@ class MolrooPersona {
161
195
  }
162
196
  return response;
163
197
  }
198
+ /**
199
+ * Send a custom event with a required appraisal vector.
200
+ *
201
+ * @param type - Custom event type (e.g., 'gift_received', 'argument').
202
+ * @param description - Human-readable event description.
203
+ * @param options - Must include a pre-computed appraisal vector.
204
+ * @returns Emotion engine response.
205
+ */
164
206
  async event(type, description, options) {
165
207
  return this.perceive(description, { ...options, type });
166
208
  }
209
+ /**
210
+ * Register a received message with the emotion engine and update the persona's emotional state.
211
+ *
212
+ * This is the **core API** for the `hear() + getState()` pattern. Apps that build their own
213
+ * LLM prompts should call `hear()` after each user message to keep the emotion engine in sync,
214
+ * then call `getState()` to read updated state for prompt assembly.
215
+ *
216
+ * Use {@link chat} when you want the SDK to handle prompt assembly and LLM orchestration.
217
+ * Use `hear()` + `getState()` when your app owns the LLM call.
218
+ *
219
+ * @param message - The incoming message text (user utterance, narration, etc.).
220
+ * @param from - Who sent the message. A plain string is used as the source entity name.
221
+ * Pass an {@link InterlocutorContext} object for richer context (description, extensions).
222
+ * @returns Emotion engine response with updated VAD, discrete emotion, and any side effects
223
+ * (memory episodes, social updates, stage transitions, etc.).
224
+ *
225
+ * @example Basic hear + getState pattern
226
+ * ```typescript
227
+ * // Step 1: register message with emotion engine
228
+ * await persona.hear('You did a great job today!', 'Alice');
229
+ *
230
+ * // Step 2: read updated psychological state
231
+ * const state = await persona.getState();
232
+ *
233
+ * // Step 3: assemble your own system prompt
234
+ * const systemPrompt = buildPrompt(myPersonaConfig, state);
235
+ * const { text } = await myLLM.generate({ system: systemPrompt, messages });
236
+ * ```
237
+ *
238
+ * @example With structured interlocutor context
239
+ * ```typescript
240
+ * await persona.hear('I need your help', {
241
+ * name: 'Alice',
242
+ * description: 'A regular customer, slightly anxious',
243
+ * extensions: { mood: 'stressed' },
244
+ * });
245
+ * ```
246
+ */
247
+ async hear(message, from) {
248
+ return this.perceive(message, { from, type: 'chat_message' });
249
+ }
250
+ /**
251
+ * Perceive an environmental or narrative event with a pre-computed appraisal.
252
+ *
253
+ * @param description - What happened (e.g., 'The sun set over the ocean').
254
+ * @param appraisal - 9-dimensional Scherer CPM appraisal vector.
255
+ * @returns Emotion engine response.
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const response = await persona.experience('Received a surprise gift', {
260
+ * goal_relevance: 0.8, goal_congruence: 0.9, expectedness: 0.2,
261
+ * controllability: 0.5, agency: 0.3, norm_compatibility: 0.8,
262
+ * internal_standards: 0.7, adjustment_potential: 0.6, urgency: 0.3,
263
+ * });
264
+ * ```
265
+ */
266
+ async experience(description, appraisal) {
267
+ return this.perceive(description, { appraisal, type: 'environment' });
268
+ }
269
+ /**
270
+ * Perceive a social interaction with relationship context for trust/intimacy computation.
271
+ *
272
+ * @param message - The interaction message.
273
+ * @param from - Who the interaction is from.
274
+ * @param relationship - Current relationship state (closeness, trust, familiarity).
275
+ * @returns Emotion engine response including socialUpdates.
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * const response = await persona.socialize('I missed you!', 'Bob', {
280
+ * closeness: 0.7, trust: 0.8, familiarity: 0.9, type: 'friend',
281
+ * });
282
+ * console.log(response.socialUpdates); // trust/intimacy deltas
283
+ * ```
284
+ */
285
+ async socialize(message, from, relationship) {
286
+ return this.perceive(message, { from, relationshipContext: relationship, type: 'chat_message' });
287
+ }
288
+ /**
289
+ * Send a message and get an LLM-generated response with emotion processing.
290
+ * This is the primary method for conversational interactions.
291
+ *
292
+ * The system prompt is assembled server-side via {@link getPromptContext} and includes:
293
+ * identity, behavioral instructions, current psychological state, expression style,
294
+ * and consumerSuffix. Do NOT duplicate these in consumerSuffix.
295
+ *
296
+ * @param message - User message to respond to.
297
+ * @param options - Conversation history, source entity, and app-specific suffix.
298
+ * @returns LLM response text, emotion data, and updated conversation history.
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * const result = await persona.chat('Tell me about yourself', {
303
+ * from: 'Alice',
304
+ * history: previousMessages,
305
+ * });
306
+ * console.log(result.text); // LLM response
307
+ * console.log(result.response.emotion); // updated VAD
308
+ * ```
309
+ */
167
310
  async chat(message, options) {
168
311
  const llm = this.requireLLM();
169
312
  const deps = {
@@ -173,12 +316,18 @@ class MolrooPersona {
173
316
  memoryAdapter: this.memoryAdapter,
174
317
  memoryRecallConfig: this.memoryRecallConfig,
175
318
  events: this.events,
176
- appraisalMode: this.appraisalMode,
177
- getPromptContext: (suffix, source) => this.getPromptContext(suffix, source),
319
+ getPromptContext: (opts) => this.getPromptContext(opts),
178
320
  perceive: (msg, opts) => this.perceive(msg, opts),
179
321
  };
180
322
  return (0, chat_orchestrator_1.chat)(deps, message, options);
181
323
  }
324
+ /**
325
+ * Advance persona simulation time. Triggers idle decay, body budget recovery,
326
+ * need regression, and timeline advancement.
327
+ *
328
+ * @param seconds - Number of seconds to advance (must be >= 1).
329
+ * @returns Any pending internal events generated by threshold checks.
330
+ */
182
331
  async tick(seconds) {
183
332
  const { data } = await this.client.POST('/personas/{id}/tick', {
184
333
  params: { path: { id: this._personaId } },
@@ -186,6 +335,12 @@ class MolrooPersona {
186
335
  });
187
336
  return (0, api_client_1.unwrap)(data);
188
337
  }
338
+ /**
339
+ * Override the persona's current emotion to a specific VAD point.
340
+ * Useful for testing or narrative-driven emotion control.
341
+ *
342
+ * @param vad - Partial VAD values to set (V: valence, A: arousal, D: dominance).
343
+ */
189
344
  async setEmotion(vad) {
190
345
  await this.client.POST('/personas/{id}/emotion', {
191
346
  params: { path: { id: this._personaId } },
@@ -217,6 +372,13 @@ class MolrooPersona {
217
372
  * Once set, expression constraints (promptText) are automatically
218
373
  * included in getPromptContext() and modulated by current emotion.
219
374
  */
375
+ /**
376
+ * Set a StyleProfile for expression-aware prompt generation.
377
+ * Once set, expression constraints are automatically included in
378
+ * {@link getPromptContext} and modulated by current emotion.
379
+ *
380
+ * @param profile - StyleProfile object (lexical, structural, register features, etc.).
381
+ */
220
382
  async setStyleProfile(profile) {
221
383
  await this.client.POST('/personas/{id}/style-profile', {
222
384
  params: { path: { id: this._personaId } },
@@ -240,18 +402,87 @@ class MolrooPersona {
240
402
  return result.styleProfile;
241
403
  }
242
404
  // ── State ──
405
+ /**
406
+ * Fetch the full psychological state of this persona from the emotion engine.
407
+ *
408
+ * Returns all active subsystems: emotion, soul stage, mask, mood, somatic sensations,
409
+ * narrative self-perception, emotion regulation, self-esteem, TMT state, and relationship.
410
+ *
411
+ * This is the primary data source for apps that build their own LLM prompts. Combine with
412
+ * `hear()` to implement the recommended `hear() + getState()` pattern:
413
+ *
414
+ * ```
415
+ * user message → hear() → emotion engine updates state → getState() → build prompt → LLM
416
+ * ```
417
+ *
418
+ * @returns All psychological subsystems as a {@link PersonaState} object.
419
+ *
420
+ * @example Building a custom system prompt
421
+ * ```typescript
422
+ * const state = await persona.getState();
423
+ *
424
+ * const lines: string[] = [
425
+ * `You are ${name}.`,
426
+ * `Emotion: ${state.emotion.discrete.primary} (${state.emotion.discrete.intensity.toFixed(2)})`,
427
+ * ];
428
+ *
429
+ * if (state.mood) {
430
+ * lines.push(`Mood: valence=${state.mood.V.toFixed(2)} arousal=${state.mood.A.toFixed(2)}`);
431
+ * }
432
+ * if (state.narrative) {
433
+ * lines.push(`Narrative: agency=${state.narrative.agency.toFixed(2)} tone=${state.narrative.tone.toFixed(2)}`);
434
+ * }
435
+ * if (state.selfEsteem) {
436
+ * lines.push(`Self-esteem: ${state.selfEsteem.global.toFixed(2)}`);
437
+ * }
438
+ * if (state.tmt && state.tmt.mortalitySalience > 0.5) {
439
+ * lines.push(`Existential tension is high. Defense mode: ${state.tmt.defenseMode}`);
440
+ * }
441
+ *
442
+ * const systemPrompt = lines.join('\n');
443
+ * ```
444
+ */
243
445
  async getState() {
244
446
  const { data } = await this.client.GET('/personas/{id}/state', {
245
447
  params: { path: { id: this._personaId } },
246
448
  });
247
449
  return (0, api_client_1.unwrap)(data);
248
450
  }
451
+ /**
452
+ * Fetch this persona's identity config (identity, personality, goals).
453
+ *
454
+ * Returns a {@link PersonaIdentity} suitable for passing to {@link buildPrompt}
455
+ * when assembling system prompts entirely client-side.
456
+ */
457
+ async getIdentity() {
458
+ const { data } = await this.client.GET('/personas/{id}', {
459
+ params: { path: { id: this._personaId } },
460
+ });
461
+ const result = (0, api_client_1.unwrap)(data);
462
+ const config = result?.persona?.config ?? {};
463
+ return {
464
+ identity: config.identity ?? { name: 'Unknown' },
465
+ personality: config.personality ?? { O: 0.5, C: 0.5, E: 0.5, A: 0.5, N: 0.5, H: 0.5 },
466
+ goals: config.goals,
467
+ };
468
+ }
469
+ /**
470
+ * Get a full snapshot of the persona (config + engine state).
471
+ * Useful for backup, migration, or debugging.
472
+ *
473
+ * @returns Full persona snapshot including config and internal state.
474
+ */
249
475
  async getSnapshot() {
250
476
  const { data } = await this.client.GET('/personas/{id}/snapshot', {
251
477
  params: { path: { id: this._personaId } },
252
478
  });
253
479
  return (0, api_client_1.unwrap)(data);
254
480
  }
481
+ /**
482
+ * Restore a persona from a snapshot. Overwrites current state entirely.
483
+ *
484
+ * @param snapshot - Full persona snapshot to restore.
485
+ */
255
486
  async putSnapshot(snapshot) {
256
487
  await this.client.PUT('/personas/{id}/snapshot', {
257
488
  params: { path: { id: this._personaId } },
@@ -259,6 +490,11 @@ class MolrooPersona {
259
490
  });
260
491
  }
261
492
  // ── Config ──
493
+ /**
494
+ * Partially update persona configuration (identity, personality, goals, etc.).
495
+ *
496
+ * @param updates - Fields to update. Only provided fields are changed.
497
+ */
262
498
  async patch(updates) {
263
499
  await this.client.PATCH('/personas/{id}', {
264
500
  params: { path: { id: this._personaId } },
@@ -266,6 +502,9 @@ class MolrooPersona {
266
502
  });
267
503
  }
268
504
  // ── Lifecycle ──
505
+ /**
506
+ * Soft-delete this persona. Can be restored within retention period via {@link restore}.
507
+ */
269
508
  async destroy() {
270
509
  if (this._personaId) {
271
510
  await this.client.DELETE('/personas/{id}', {
@@ -273,6 +512,7 @@ class MolrooPersona {
273
512
  });
274
513
  }
275
514
  }
515
+ /** Restore a soft-deleted persona. */
276
516
  async restore() {
277
517
  if (this._personaId) {
278
518
  await this.client.POST('/personas/{id}/restore', {
@@ -281,16 +521,84 @@ class MolrooPersona {
281
521
  }
282
522
  }
283
523
  // ── Prompt / Memory ──
284
- async getPromptContext(consumerSuffix, sourceEntity) {
524
+ /**
525
+ * Fetch the server-assembled system prompt for this persona.
526
+ *
527
+ * The server builds a complete system prompt from the persona's live state. It includes:
528
+ * 1. **Identity** — name, role, core values, speaking style, description
529
+ * 2. **Behavioral instructions** — stay in character, embody state, match language
530
+ * 3. **Psychological state** — emotion, mood, somatic, narrative, goals (from live engine)
531
+ * 4. **Expression constraints** — message length, formality, patterns (only if StyleProfile set)
532
+ * 5. **consumerRules** — behavioral constraints and absolute rules (e.g., "Never break character")
533
+ * 6. **consumerExamples** — few-shot example messages for style/behavior reference
534
+ * 7. **consumerSuffix** — your app-specific context, appended last
535
+ *
536
+ * This is a **middle-tier API** — between the low-level `getState()` (raw data) and the
537
+ * high-level `chat()` (full orchestration). Use it when you want to drive the LLM yourself
538
+ * but still benefit from server-side prompt assembly.
539
+ *
540
+ * Called automatically by {@link chat}. Call directly when:
541
+ * - You use a custom LLM framework not supported by the SDK
542
+ * - You need to inspect or transform the system prompt before sending
543
+ * - You need prompt + state in a single round-trip (avoids a separate `getState()` call)
544
+ *
545
+ * **Do NOT** include identity, personality, or emotion in `consumerSuffix` — those are
546
+ * already present. Duplicating them wastes tokens and may confuse the LLM.
547
+ *
548
+ * @param options - Prompt context options.
549
+ * @param options.consumerSuffix - Free-form app-specific context appended to the system prompt as-is.
550
+ * Examples: scene description, available actions, current quest objective.
551
+ * @param options.consumerRules - Behavioral constraints and absolute rules.
552
+ * Examples: "Never break character", "Always respond in Korean", "Do not discuss competitors".
553
+ * @param options.consumerExamples - Few-shot example messages for style/behavior reference.
554
+ * Helps the LLM match a specific tone or response format.
555
+ * @param options.sourceEntity - Name of the conversation partner for interlocutor-aware rendering.
556
+ * @returns `systemPrompt` (ready to pass to LLM), `personaPrompt` (structured breakdown),
557
+ * and optional `tools` (if the persona has tools configured).
558
+ *
559
+ * @example Custom LLM call with server-assembled prompt
560
+ * ```typescript
561
+ * const { systemPrompt } = await persona.getPromptContext({
562
+ * consumerSuffix: 'Available actions: [flee, fight, negotiate]',
563
+ * consumerRules: 'Always respond in English. Never break character.',
564
+ * sourceEntity: 'Alice',
565
+ * });
566
+ * const response = await myLLM.complete({ system: systemPrompt, messages });
567
+ * await persona.hear(userMessage, 'Alice');
568
+ * ```
569
+ */
570
+ async getPromptContext(options) {
571
+ const { consumerSuffix, consumerRules, consumerExamples, sourceEntity } = options ?? {};
285
572
  const { data } = await this.client.POST('/personas/{id}/prompt-context', {
286
573
  params: { path: { id: this._personaId } },
287
574
  body: {
288
575
  ...(consumerSuffix ? { consumerSuffix } : {}),
576
+ ...(consumerRules ? { consumerRules } : {}),
577
+ ...(consumerExamples ? { consumerExamples } : {}),
289
578
  ...(sourceEntity ? { sourceEntity } : {}),
290
579
  },
291
580
  });
292
581
  return (0, api_client_1.unwrap)(data);
293
582
  }
583
+ // ── Conversation ──
584
+ /**
585
+ * Create a conversation session that auto-manages message history.
586
+ * Each `.send()` call automatically appends to the history.
587
+ *
588
+ * @param options - Max messages or custom chat options.
589
+ * @returns A new Conversation instance bound to this persona.
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * const conv = persona.conversation({ maxMessages: 20 });
594
+ * const r1 = await conv.send('Hello!');
595
+ * const r2 = await conv.send('Tell me more'); // history auto-managed
596
+ * console.log(conv.messages); // full conversation
597
+ * ```
598
+ */
599
+ conversation(options) {
600
+ return new conversation_1.Conversation(this, options);
601
+ }
294
602
  // ── Private ──
295
603
  get memoryPipelineDeps() {
296
604
  return {
@@ -303,9 +611,113 @@ class MolrooPersona {
303
611
  }
304
612
  requireLLM() {
305
613
  if (!this.llm) {
306
- throw new errors_1.MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', 'LLM_NOT_CONFIGURED', 400);
614
+ throw new errors_1.MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', errors_1.MolrooErrorCode.LLM_NOT_CONFIGURED, 400);
307
615
  }
308
616
  return this.llm;
309
617
  }
310
618
  }
311
619
  exports.MolrooPersona = MolrooPersona;
620
+ // ── Client-side Prompt Assembly ──
621
+ /**
622
+ * Assemble a system prompt entirely client-side from a {@link PersonaIdentity} and
623
+ * {@link PersonaState}.
624
+ *
625
+ * Use this with the `hear() + getState()` pattern when you own the LLM call:
626
+ * ```
627
+ * await persona.hear(userMessage);
628
+ * const [identity, state] = await Promise.all([persona.getIdentity(), persona.getState()]);
629
+ * const systemPrompt = buildPrompt(identity, state, { consumerSuffix: 'Scene: coffee shop.' });
630
+ * ```
631
+ *
632
+ * @param identity - Persona config from {@link MolrooPersona.getIdentity}.
633
+ * @param state - Live emotional state from {@link MolrooPersona.getState}.
634
+ * @param options - Optional consumer-provided context appended to the prompt.
635
+ */
636
+ function buildPrompt(identity, state, options) {
637
+ const { identity: id } = identity;
638
+ const lines = [];
639
+ // ── Identity ──
640
+ lines.push(`Name: ${id.name}`);
641
+ if (id.role)
642
+ lines.push(`Role: ${id.role}`);
643
+ if (id.coreValues?.length)
644
+ lines.push(`Core values: ${id.coreValues.join(', ')}`);
645
+ if (id.speakingStyle)
646
+ lines.push(`Speaking style: ${id.speakingStyle}`);
647
+ if (id.description)
648
+ lines.push(id.description);
649
+ if (id.backstory) {
650
+ lines.push('', '## Backstory', id.backstory);
651
+ }
652
+ if (id.relationships && Object.keys(id.relationships).length) {
653
+ lines.push('', '## Key Relationships');
654
+ for (const [k, v] of Object.entries(id.relationships))
655
+ lines.push(`- ${k}: ${v}`);
656
+ }
657
+ if (id.interests?.length) {
658
+ lines.push('', `Interests: ${id.interests.join(', ')}`);
659
+ }
660
+ if (id.speechPatterns?.length) {
661
+ lines.push('', '## Speech Patterns');
662
+ for (const p of id.speechPatterns)
663
+ lines.push(`- ${p}`);
664
+ }
665
+ if (id.emotionalPatterns && Object.keys(id.emotionalPatterns).length) {
666
+ lines.push('', '## Emotional Patterns');
667
+ for (const [k, v] of Object.entries(id.emotionalPatterns))
668
+ lines.push(`- ${k}: ${v}`);
669
+ }
670
+ if (id.quirks?.length) {
671
+ lines.push('', '## Quirks');
672
+ for (const q of id.quirks)
673
+ lines.push(`- ${q}`);
674
+ }
675
+ if (id.extensions && Object.keys(id.extensions).length) {
676
+ for (const [k, v] of Object.entries(id.extensions)) {
677
+ lines.push('', `## ${k}`, v);
678
+ }
679
+ }
680
+ // ── Behavioral Instructions ──
681
+ lines.push('', 'Stay in character.', 'Embody your psychological state naturally in your responses.', 'Respond in the same language as the user.');
682
+ // ── Psychological State ──
683
+ lines.push('', '## Current Psychological State');
684
+ const { primary, secondary, intensity } = state.emotion.discrete;
685
+ const emotionLabel = secondary ? `${primary} / ${secondary}` : primary;
686
+ lines.push(`Emotion: ${emotionLabel} (intensity: ${intensity.toFixed(2)})`);
687
+ const { V, A, D } = state.emotion.vad;
688
+ lines.push(`VAD: V=${V.toFixed(2)} A=${A.toFixed(2)} D=${D.toFixed(2)}`);
689
+ if (state.mood) {
690
+ lines.push(`Mood: V=${state.mood.V.toFixed(2)} A=${state.mood.A.toFixed(2)} D=${state.mood.D.toFixed(2)}`);
691
+ }
692
+ if (state.somatic) {
693
+ const active = Object.entries(state.somatic).filter(([, v]) => v >= 0.1);
694
+ if (active.length) {
695
+ const somaticStr = active.map(([k, v]) => `${k} (${v.toFixed(2)})`).join(', ');
696
+ lines.push(`Somatic: ${somaticStr}. These bodily sensations unconsciously color your responses.`);
697
+ }
698
+ }
699
+ if (state.narrative) {
700
+ const n = state.narrative;
701
+ lines.push(`Narrative self-perception: tone=${n.tone.toFixed(2)} agency=${n.agency.toFixed(2)} coherence=${n.coherence.toFixed(2)}`);
702
+ }
703
+ lines.push(`Soul stage: ${state.soulStage.name}`);
704
+ if (state.relationship) {
705
+ const r = state.relationship;
706
+ const attachment = r.attachmentStyle ? ` attachment=${r.attachmentStyle}` : '';
707
+ lines.push(`Relationship: trust=${r.trust.toFixed(2)} intimacy=${r.intimacy.toFixed(2)}${attachment}`);
708
+ }
709
+ if (state.mask.state !== 'stable') {
710
+ lines.push(`Mask: ${state.mask.state} (integrity=${state.mask.integrity.toFixed(2)})`);
711
+ }
712
+ // ── Consumer Context ──
713
+ if (options?.consumerRules) {
714
+ lines.push('', '## Rules', options.consumerRules);
715
+ }
716
+ if (options?.consumerExamples) {
717
+ lines.push('', '## Examples', options.consumerExamples);
718
+ }
719
+ if (options?.consumerSuffix) {
720
+ lines.push('', options.consumerSuffix);
721
+ }
722
+ return lines.join('\n');
723
+ }
@@ -3,14 +3,44 @@
3
3
  *
4
4
  * `instanceof MolrooApiError` catches ALL API errors (persona + world).
5
5
  * `instanceof WorldApiError` catches world-only errors.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { MolrooApiError, MolrooErrorCode } from '@molroo-io/sdk';
10
+ *
11
+ * try {
12
+ * await persona.chat('hello');
13
+ * } catch (e) {
14
+ * if (e instanceof MolrooApiError && e.code === MolrooErrorCode.LLM_NOT_CONFIGURED) {
15
+ * console.log('Please provide an LLM adapter');
16
+ * }
17
+ * }
18
+ * ```
6
19
  */
20
+ /** Machine-readable error codes for programmatic error handling. */
21
+ export declare enum MolrooErrorCode {
22
+ /** LLM adapter is required for this operation (e.g., description-based create). */
23
+ LLM_REQUIRED = "LLM_REQUIRED",
24
+ /** LLM adapter was not configured at initialization (e.g., calling chat() without llm). */
25
+ LLM_NOT_CONFIGURED = "LLM_NOT_CONFIGURED",
26
+ /** Requested entity (persona, world, etc.) was not found. */
27
+ ENTITY_NOT_FOUND = "ENTITY_NOT_FOUND",
28
+ /** Authentication failed — invalid or missing API key. */
29
+ UNAUTHORIZED = "UNAUTHORIZED",
30
+ /** API returned an unexpected error not covered by other codes. */
31
+ API_ERROR = "API_ERROR",
32
+ /** Feature is not yet implemented. */
33
+ NOT_IMPLEMENTED = "NOT_IMPLEMENTED",
34
+ /** Memory pipeline operation failed (fire-and-forget, non-blocking). */
35
+ PIPELINE_ERROR = "PIPELINE_ERROR"
36
+ }
7
37
  export declare class MolrooApiError extends Error {
8
- /** Machine-readable error code (e.g., 'ENTITY_NOT_FOUND', 'UNAUTHORIZED'). */
38
+ /** Machine-readable error code. Use {@link MolrooErrorCode} for type-safe matching. */
9
39
  readonly code: string;
10
40
  /** HTTP status code. */
11
41
  readonly status: number;
12
42
  constructor(message: string,
13
- /** Machine-readable error code (e.g., 'ENTITY_NOT_FOUND', 'UNAUTHORIZED'). */
43
+ /** Machine-readable error code. Use {@link MolrooErrorCode} for type-safe matching. */
14
44
  code: string,
15
45
  /** HTTP status code. */
16
46
  status: number);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/shared/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qBAAa,cAAe,SAAQ,KAAK;IAGrC,8EAA8E;aAC9D,IAAI,EAAE,MAAM;IAC5B,wBAAwB;aACR,MAAM,EAAE,MAAM;gBAJ9B,OAAO,EAAE,MAAM;IACf,8EAA8E;IAC9D,IAAI,EAAE,MAAM;IAC5B,wBAAwB;IACR,MAAM,EAAE,MAAM;CAKjC;AAED,qBAAa,aAAc,SAAQ,cAAc;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI1D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/shared/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,oEAAoE;AACpE,oBAAY,eAAe;IACzB,mFAAmF;IACnF,YAAY,iBAAiB;IAC7B,2FAA2F;IAC3F,kBAAkB,uBAAuB;IACzC,6DAA6D;IAC7D,gBAAgB,qBAAqB;IACrC,0DAA0D;IAC1D,YAAY,iBAAiB;IAC7B,mEAAmE;IACnE,SAAS,cAAc;IACvB,sCAAsC;IACtC,eAAe,oBAAoB;IACnC,wEAAwE;IACxE,cAAc,mBAAmB;CAClC;AAED,qBAAa,cAAe,SAAQ,KAAK;IAGrC,uFAAuF;aACvE,IAAI,EAAE,MAAM;IAC5B,wBAAwB;aACR,MAAM,EAAE,MAAM;gBAJ9B,OAAO,EAAE,MAAM;IACf,uFAAuF;IACvE,IAAI,EAAE,MAAM;IAC5B,wBAAwB;IACR,MAAM,EAAE,MAAM;CAKjC;AAED,qBAAa,aAAc,SAAQ,cAAc;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI1D"}
@@ -4,12 +4,43 @@
4
4
  *
5
5
  * `instanceof MolrooApiError` catches ALL API errors (persona + world).
6
6
  * `instanceof WorldApiError` catches world-only errors.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { MolrooApiError, MolrooErrorCode } from '@molroo-io/sdk';
11
+ *
12
+ * try {
13
+ * await persona.chat('hello');
14
+ * } catch (e) {
15
+ * if (e instanceof MolrooApiError && e.code === MolrooErrorCode.LLM_NOT_CONFIGURED) {
16
+ * console.log('Please provide an LLM adapter');
17
+ * }
18
+ * }
19
+ * ```
7
20
  */
8
21
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.WorldApiError = exports.MolrooApiError = void 0;
22
+ exports.WorldApiError = exports.MolrooApiError = exports.MolrooErrorCode = void 0;
23
+ /** Machine-readable error codes for programmatic error handling. */
24
+ var MolrooErrorCode;
25
+ (function (MolrooErrorCode) {
26
+ /** LLM adapter is required for this operation (e.g., description-based create). */
27
+ MolrooErrorCode["LLM_REQUIRED"] = "LLM_REQUIRED";
28
+ /** LLM adapter was not configured at initialization (e.g., calling chat() without llm). */
29
+ MolrooErrorCode["LLM_NOT_CONFIGURED"] = "LLM_NOT_CONFIGURED";
30
+ /** Requested entity (persona, world, etc.) was not found. */
31
+ MolrooErrorCode["ENTITY_NOT_FOUND"] = "ENTITY_NOT_FOUND";
32
+ /** Authentication failed — invalid or missing API key. */
33
+ MolrooErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
34
+ /** API returned an unexpected error not covered by other codes. */
35
+ MolrooErrorCode["API_ERROR"] = "API_ERROR";
36
+ /** Feature is not yet implemented. */
37
+ MolrooErrorCode["NOT_IMPLEMENTED"] = "NOT_IMPLEMENTED";
38
+ /** Memory pipeline operation failed (fire-and-forget, non-blocking). */
39
+ MolrooErrorCode["PIPELINE_ERROR"] = "PIPELINE_ERROR";
40
+ })(MolrooErrorCode || (exports.MolrooErrorCode = MolrooErrorCode = {}));
10
41
  class MolrooApiError extends Error {
11
42
  constructor(message,
12
- /** Machine-readable error code (e.g., 'ENTITY_NOT_FOUND', 'UNAUTHORIZED'). */
43
+ /** Machine-readable error code. Use {@link MolrooErrorCode} for type-safe matching. */
13
44
  code,
14
45
  /** HTTP status code. */
15
46
  status) {