Skip to content

Commit e92ac98

Browse files
feat(synapse): add soul version increment and encoding state propagation
Wire monotonic soul version increment into CognitiveSoulService: - saveAgentSoul: read current version from INSULA, increment, set on rebuilt soul, push to SpectorMemory.setSoulVersion() - saveUserSoul: same pattern with full 8-arg UserSoul constructor - loadAgentSoul: propagate loaded version to SpectorMemory Update all AgentSoul construction sites in synapse module: - AgentController, ChatService, DynamicGraphBuilder, UpdateAgentSoulTool Wire soul version restoration at startup in UserMemoryRegistry alongside salience profile loading from INSULA. Refs #502 Co-authored-by: Bharat Joshi <bharatjoshi@spectrayan.com>
1 parent 1f127b5 commit e92ac98

6 files changed

Lines changed: 93 additions & 7 deletions

File tree

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/agent/AgentController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ public ResponseEntity<AgentSoul> updateSoul(@RequestBody AgentSoul soul) {
8484
.communicationStyle(soul.communicationStyle())
8585
.model(soul.model())
8686
.tools(soul.tools())
87+
.expertiseEmbedding(soul.expertiseEmbedding())
88+
.purposeEmbedding(soul.purposeEmbedding())
89+
.soulVersion(soul.soulVersion())
90+
.createdAt(soul.createdAt())
91+
.updatedAt(soul.updatedAt())
8792
.build();
8893
soulService.saveAgentSoul(updated);
8994
return ResponseEntity.ok(updated);
@@ -141,6 +146,11 @@ public ResponseEntity<AgentSoul> updateAgent(@PathVariable String id, @RequestBo
141146
.communicationStyle(soul.communicationStyle())
142147
.model(soul.model())
143148
.tools(soul.tools())
149+
.expertiseEmbedding(soul.expertiseEmbedding())
150+
.purposeEmbedding(soul.purposeEmbedding())
151+
.soulVersion(soul.soulVersion())
152+
.createdAt(soul.createdAt())
153+
.updatedAt(soul.updatedAt())
144154
.build();
145155
soulService.saveAgentSoul(updated);
146156
return ResponseEntity.ok(updated);

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/agent/chat/service/ChatService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ private AgentChatResponse processChat(
213213
.personality(soul != null ? soul.personality() : null)
214214
.model(model != null ? model : DEFAULT_MODEL)
215215
.tools(soul != null ? soul.tools() : List.of())
216+
.expertiseEmbedding(soul != null ? soul.expertiseEmbedding() : null)
217+
.purposeEmbedding(soul != null ? soul.purposeEmbedding() : null)
218+
.soulVersion(soul != null ? soul.soulVersion() : (short) 1)
216219
.createdAt(soul != null ? soul.createdAt() : java.time.Instant.now())
217220
.updatedAt(soul != null ? soul.updatedAt() : java.time.Instant.now());
218221

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/agent/graph/DynamicGraphBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ private NodeAction<CognitiveState> createAgentNode(String nodeName,
224224
.name(agentSpec.name() != null ? agentSpec.name() : agentId)
225225
.systemPrompt(agentSpec.systemPrompt())
226226
.model(agentSpec.llm() != null ? agentSpec.llm().model() : "qwen3.5:latest")
227+
.soulVersion((short) 1)
227228
.build();
228229
}
229230
// Fall back to active default soul

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/agent/service/CognitiveSoulService.java

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public class CognitiveSoulService {
5656
.communicationStyle("professional")
5757
.model("qwen3.5:latest")
5858
.tools(List.of())
59+
.soulVersion((short) 1)
60+
.createdAt(java.time.Instant.now())
61+
.updatedAt(java.time.Instant.now())
5962
.build();
6063

6164
private final UserMemoryRegistry userMemoryRegistry;
@@ -85,6 +88,7 @@ public Optional<AgentSoul> loadAgentSoul(String id) {
8588
.flatMap(bytes -> fromJsonBytes(bytes, InsulaSelfModel.class))
8689
.map(model -> {
8790
if (model.soul() instanceof AgentSoul agentSoul) {
91+
memory.setSoulVersion(agentSoul.soulVersion());
8892
return agentSoul;
8993
}
9094
return null;
@@ -112,13 +116,48 @@ public void saveAgentSoul(AgentSoul soul) {
112116
return;
113117
}
114118

115-
InsulaSelfModel selfModel = new InsulaSelfModel("AGENT", soul, null, Map.of());
116-
byte[] bytes = toJsonBytes(selfModel);
119+
short nextVersion = 1;
117120
var insula = memory.admin().insularCortex();
121+
if (insula != null) {
122+
nextVersion = insula.get()
123+
.flatMap(bytes -> fromJsonBytes(bytes, InsulaSelfModel.class))
124+
.map(model -> {
125+
if (model.soul() instanceof AgentSoul as) {
126+
return (short)(as.soulVersion() + 1);
127+
}
128+
return (short)1;
129+
})
130+
.orElse((short)1);
131+
}
132+
133+
AgentSoul savedSoul = AgentSoul.builder()
134+
.id(soul.id())
135+
.name(soul.name())
136+
.description(soul.description())
137+
.systemPrompt(soul.systemPrompt())
138+
.purpose(soul.purpose())
139+
.personality(soul.personality())
140+
.expertiseDomains(soul.expertiseDomains())
141+
.coreValues(soul.coreValues())
142+
.ethicalGuardrails(soul.ethicalGuardrails())
143+
.emotionalBaseline(soul.emotionalBaseline())
144+
.communicationStyle(soul.communicationStyle())
145+
.model(soul.model())
146+
.tools(soul.tools())
147+
.expertiseEmbedding(soul.expertiseEmbedding())
148+
.purposeEmbedding(soul.purposeEmbedding())
149+
.soulVersion(nextVersion)
150+
.createdAt(soul.createdAt() != null ? soul.createdAt() : java.time.Instant.now())
151+
.updatedAt(java.time.Instant.now())
152+
.build();
153+
154+
InsulaSelfModel selfModel = new InsulaSelfModel("AGENT", savedSoul, null, Map.of());
155+
byte[] bytes = toJsonBytes(selfModel);
118156
if (bytes != null && insula != null) {
119157
insula.put(bytes);
120158
}
121-
log.info("[CognitiveSoul] Saved agent soul '{}' in INSULA", soul.name());
159+
memory.setSoulVersion(nextVersion);
160+
log.info("[CognitiveSoul] Saved agent soul '{}' v{} in INSULA", soul.name(), nextVersion);
122161
}
123162

124163
/**
@@ -171,18 +210,39 @@ public void saveUserSoul(PersonaContext persona) {
171210
return;
172211
}
173212

174-
UserSoul userSoul = new UserSoul(nsId, "User", "User Persona", persona, persona.aboutEmbedding());
213+
short nextVersion = 1;
214+
java.time.Instant createdAt = java.time.Instant.now();
215+
var insula = memory.admin().insularCortex();
216+
if (insula != null) {
217+
var existingOpt = insula.get()
218+
.flatMap(bytes -> fromJsonBytes(bytes, InsulaSelfModel.class))
219+
.map(model -> {
220+
if (model.soul() instanceof UserSoul us) {
221+
return us;
222+
}
223+
return null;
224+
});
225+
if (existingOpt.isPresent()) {
226+
UserSoul us = existingOpt.get();
227+
nextVersion = (short)(us.soulVersion() + 1);
228+
if (us.createdAt() != null) {
229+
createdAt = us.createdAt();
230+
}
231+
}
232+
}
233+
234+
UserSoul userSoul = new UserSoul(nsId, "User", "User Persona", persona, persona.aboutEmbedding(), nextVersion, createdAt, java.time.Instant.now());
175235
InsulaSelfModel selfModel = new InsulaSelfModel("USER", userSoul, salienceProvider.effectiveProfile(), Map.of());
176236

177237
byte[] bytes = toJsonBytes(selfModel);
178-
var insula = memory.admin().insularCortex();
179238
if (bytes != null && insula != null) {
180239
insula.put(bytes);
181240
}
241+
memory.setSoulVersion(nextVersion);
182242

183243
// Propagate to salience provider
184244
salienceProvider.updateUserPersona(persona);
185-
log.info("[CognitiveSoul] Saved user persona context to INSULA — salience profile updated");
245+
log.info("[CognitiveSoul] Saved user persona v{} to INSULA — salience profile updated", nextVersion);
186246
}
187247

188248
/** Get the current active agent soul, or a default fallback. */
@@ -214,7 +274,11 @@ public AgentSoul patchAgentSoul(Map<String, Object> updates) {
214274
.personality(updates.containsKey("personality") ? (String) updates.get("personality") : current.personality())
215275
.emotionalBaseline(updates.containsKey("emotionalBaseline") ? parseEmotionalBaseline(updates.get("emotionalBaseline")) : current.emotionalBaseline())
216276
.communicationStyle(updates.containsKey("communicationStyle") ? (String) updates.get("communicationStyle") : current.communicationStyle())
217-
.model(updates.containsKey("model") ? (String) updates.get("model") : current.model());
277+
.model(updates.containsKey("model") ? (String) updates.get("model") : current.model())
278+
.expertiseEmbedding(current.expertiseEmbedding())
279+
.purposeEmbedding(current.purposeEmbedding())
280+
.soulVersion(current.soulVersion())
281+
.createdAt(current.createdAt());
218282

219283
if (updates.containsKey("expertiseDomains")) {
220284
builder.expertiseDomains((List<String>) updates.get("expertiseDomains"));

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/agent/tools/UpdateAgentSoulTool.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ private String executeInternal(Map<String, Object> arguments) throws Exception {
131131
.communicationStyle(current.communicationStyle())
132132
.model(current.model())
133133
.tools(current.tools())
134+
.expertiseEmbedding(current.expertiseEmbedding())
135+
.purposeEmbedding(current.purposeEmbedding())
136+
.soulVersion(current.soulVersion())
134137
.createdAt(current.createdAt())
135138
.updatedAt(Instant.now());
136139

synapse/spector-synapse/src/main/java/com/spectrayan/spector/synapse/memory/UserMemoryRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ private SpectorMemory buildInstance(String userId) {
322322
built.setSalienceProfile(model.salience());
323323
log.info("[UserMemoryRegistry] Restored salience profile from INSULA for user/agent {}", userId);
324324
}
325+
if (model != null && model.soul() != null) {
326+
built.setSoulVersion(model.soul().soulVersion());
327+
log.info("[UserMemoryRegistry] Restored soul version {} from INSULA for user/agent {}",
328+
model.soul().soulVersion(), userId);
329+
}
325330
}
326331
}
327332
} catch (Exception e) {

0 commit comments

Comments
 (0)