soulbridge 0.0.8 → 0.0.10

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.
@@ -67,7 +67,10 @@ export declare class Soul {
67
67
  visibility?: Visibility;
68
68
  }): string;
69
69
  /** Verb 4: session end. */
70
- reflect(turns: Turn[], llm?: LlmFn): Promise<ReflectResult>;
70
+ reflect(turns: Turn[], options?: {
71
+ llm?: LlmFn;
72
+ updateEmotion?: boolean;
73
+ }): Promise<ReflectResult>;
71
74
  stats(): {
72
75
  memories: number;
73
76
  emotion: Emotion;
@@ -83,7 +83,12 @@ export class Soul {
83
83
  const now = turn.at ?? Date.now();
84
84
  const affect = turn.role === 'user' ? await this.analyzeTurn(turn.content) : emptyAffect();
85
85
  const drafts = ruleExtract([turn]);
86
+ // observe runs every turn; repeating a sentence must not create a second row.
87
+ const seen = new Set(this.store.query({ soulId: this.soulId, userId: this.userId, limit: 10_000 }).map((m) => m.content.trim()));
86
88
  for (const draft of drafts) {
89
+ if (seen.has(draft.content.trim()))
90
+ continue;
91
+ seen.add(draft.content.trim());
87
92
  this.store.put({
88
93
  id: `mem_${now.toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
89
94
  soulId: this.soulId,
@@ -139,8 +144,14 @@ export class Soul {
139
144
  return id;
140
145
  }
141
146
  /** Verb 4: session end. */
142
- async reflect(turns, llm) {
143
- return reflect(this.store, { soulId: this.soulId, userId: this.userId, turns, llm });
147
+ async reflect(turns, options = {}) {
148
+ return reflect(this.store, {
149
+ soulId: this.soulId,
150
+ userId: this.userId,
151
+ turns,
152
+ llm: options.llm,
153
+ updateEmotion: options.updateEmotion,
154
+ });
144
155
  }
145
156
  stats() {
146
157
  return {
@@ -34,6 +34,12 @@ export interface ReflectOptions {
34
34
  userId: string;
35
35
  turns: Turn[];
36
36
  now?: number;
37
+ /**
38
+ * Whether this call also advances emotion (default true). A caller that runs
39
+ * reflect per turn, alongside observe(), must pass false or the turn is
40
+ * counted twice.
41
+ */
42
+ updateEmotion?: boolean;
37
43
  /** Reserved for richer extraction; unused in v0. */
38
44
  llm?: LlmFn;
39
45
  }
@@ -144,7 +144,10 @@ export async function reflect(store, options) {
144
144
  store.put(memory);
145
145
  written += 1;
146
146
  }
147
- const emotion = nextEmotion(store.emotion(options.soulId, options.userId), options.turns);
148
- store.saveEmotion(options.soulId, options.userId, emotion);
147
+ let emotion = store.emotion(options.soulId, options.userId);
148
+ if (options.updateEmotion !== false) {
149
+ emotion = nextEmotion(emotion, options.turns);
150
+ store.saveEmotion(options.soulId, options.userId, emotion);
151
+ }
149
152
  return { memories: written, superseded: 0, emotion };
150
153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulbridge",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "A personality layer for any agent: continuity, forgetting, and growth across tools.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",