@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.
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.d.ts.map +1 -1
- package/dist/cjs/errors.js +2 -1
- package/dist/cjs/events/types.d.ts +1 -1
- package/dist/cjs/events/types.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +4 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/llm/resolve.d.ts.map +1 -1
- package/dist/cjs/llm/resolve.js +2 -1
- package/dist/cjs/llm/schema.d.ts +0 -114
- package/dist/cjs/llm/schema.d.ts.map +1 -1
- package/dist/cjs/llm/schema.js +1 -44
- package/dist/cjs/persona/chat-orchestrator.d.ts +18 -3
- package/dist/cjs/persona/chat-orchestrator.d.ts.map +1 -1
- package/dist/cjs/persona/chat-orchestrator.js +29 -109
- package/dist/cjs/persona/conversation.d.ts +84 -0
- package/dist/cjs/persona/conversation.d.ts.map +1 -0
- package/dist/cjs/persona/conversation.js +72 -0
- package/dist/cjs/persona/memory-pipeline.d.ts.map +1 -1
- package/dist/cjs/persona/memory-pipeline.js +30 -4
- package/dist/cjs/persona.d.ts +441 -22
- package/dist/cjs/persona.d.ts.map +1 -1
- package/dist/cjs/persona.js +418 -6
- package/dist/cjs/shared/errors.d.ts +32 -2
- package/dist/cjs/shared/errors.d.ts.map +1 -1
- package/dist/cjs/shared/errors.js +33 -2
- package/dist/cjs/types.d.ts +70 -5
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +2 -1
- package/dist/cjs/world/world-persona.d.ts +20 -5
- package/dist/cjs/world/world-persona.d.ts.map +1 -1
- package/dist/cjs/world/world-persona.js +21 -5
- package/dist/cjs/world/world.d.ts +28 -5
- package/dist/cjs/world/world.d.ts.map +1 -1
- package/dist/cjs/world/world.js +29 -3
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +1 -1
- package/dist/esm/events/types.d.ts +1 -1
- package/dist/esm/events/types.d.ts.map +1 -1
- package/dist/esm/index.d.ts +4 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/llm/resolve.d.ts.map +1 -1
- package/dist/esm/llm/resolve.js +2 -1
- package/dist/esm/llm/schema.d.ts +0 -114
- package/dist/esm/llm/schema.d.ts.map +1 -1
- package/dist/esm/llm/schema.js +0 -43
- package/dist/esm/persona/chat-orchestrator.d.ts +18 -3
- package/dist/esm/persona/chat-orchestrator.d.ts.map +1 -1
- package/dist/esm/persona/chat-orchestrator.js +29 -109
- package/dist/esm/persona/conversation.d.ts +84 -0
- package/dist/esm/persona/conversation.d.ts.map +1 -0
- package/dist/esm/persona/conversation.js +68 -0
- package/dist/esm/persona/memory-pipeline.d.ts.map +1 -1
- package/dist/esm/persona/memory-pipeline.js +30 -4
- package/dist/esm/persona.d.ts +441 -22
- package/dist/esm/persona.d.ts.map +1 -1
- package/dist/esm/persona.js +418 -7
- package/dist/esm/shared/errors.d.ts +32 -2
- package/dist/esm/shared/errors.d.ts.map +1 -1
- package/dist/esm/shared/errors.js +32 -1
- package/dist/esm/types.d.ts +70 -5
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +1 -1
- package/dist/esm/world/world-persona.d.ts +20 -5
- package/dist/esm/world/world-persona.d.ts.map +1 -1
- package/dist/esm/world/world-persona.js +21 -5
- package/dist/esm/world/world.d.ts +28 -5
- package/dist/esm/world/world.d.ts.map +1 -1
- package/dist/esm/world/world.js +29 -3
- package/package.json +1 -1
package/dist/cjs/persona.js
CHANGED
|
@@ -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.',
|
|
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
|
-
|
|
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
|
-
|
|
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.',
|
|
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
|
|
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
|
|
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
|
|
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
|
|
43
|
+
/** Machine-readable error code. Use {@link MolrooErrorCode} for type-safe matching. */
|
|
13
44
|
code,
|
|
14
45
|
/** HTTP status code. */
|
|
15
46
|
status) {
|