forge-openclaw-plugin 0.2.70 → 0.2.71

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 (32) hide show
  1. package/dist/assets/{board-BfqxFNiQ.js → board-B0TuXl4u.js} +1 -1
  2. package/dist/assets/index-DtT3Y-Bj.css +1 -0
  3. package/dist/assets/index-clNilMKr.js +91 -0
  4. package/dist/assets/{motion-C0ALlgho.js → motion-Dmjq6HPm.js} +1 -1
  5. package/dist/assets/{table-WcMjnJll.js → table-CKKimYN1.js} +1 -1
  6. package/dist/assets/{ui-B5I-3U91.js → ui-JBdCP1Qb.js} +1 -1
  7. package/dist/assets/{vendor-C56o26_3.js → vendor-fiXu5f59.js} +233 -228
  8. package/dist/index.html +7 -7
  9. package/dist/server/server/migrations/063_psyche_flashcards.sql +31 -0
  10. package/dist/server/server/src/app.js +226 -6
  11. package/dist/server/server/src/health.js +56 -27
  12. package/dist/server/server/src/openapi.js +1 -0
  13. package/dist/server/server/src/psyche-types.js +54 -0
  14. package/dist/server/server/src/repositories/psyche.js +146 -1
  15. package/dist/server/server/src/services/entity-crud.js +27 -5
  16. package/dist/server/server/src/services/gamification.js +2 -0
  17. package/dist/server/server/src/services/knowledge-graph.js +87 -1
  18. package/dist/server/server/src/services/psyche-observation-calendar.js +1 -0
  19. package/dist/server/server/src/services/psyche.js +4 -1
  20. package/dist/server/server/src/types.js +3 -0
  21. package/dist/server/src/lib/api.js +43 -0
  22. package/dist/server/src/lib/entity-visuals.js +9 -0
  23. package/dist/server/src/lib/knowledge-graph-types.js +19 -0
  24. package/dist/server/src/lib/psyche-schemas.js +177 -0
  25. package/openclaw.plugin.json +1 -1
  26. package/package.json +1 -1
  27. package/server/migrations/063_psyche_flashcards.sql +31 -0
  28. package/skills/forge-openclaw/SKILL.md +20 -4
  29. package/skills/forge-openclaw/entity_conversation_playbooks.md +6 -3
  30. package/skills/forge-openclaw/psyche_entity_playbooks.md +64 -3
  31. package/dist/assets/index-BfLQnCNZ.js +0 -91
  32. package/dist/assets/index-DIapFz9v.css +0 -1
@@ -6,7 +6,7 @@ import { clearEntityOwner, decorateOwnedEntity, setEntityOwner } from "./entity-
6
6
  import { recordEventLog } from "./event-log.js";
7
7
  import { unlinkNotesForEntity } from "./notes.js";
8
8
  import { recordPsycheClarityReward, recordPsycheReflectionReward } from "./rewards.js";
9
- import { behaviorPatternSchema, behaviorSchema, beliefEntrySchema, createBehaviorPatternSchema, createBehaviorSchema, createBeliefEntrySchema, createEmotionDefinitionSchema, createEventTypeSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, domainSchema, emotionDefinitionSchema, eventTypeSchema, modeFamilySchema, modeGuideResultSchema, modeGuideSessionSchema, modeProfileSchema, modeTimelineEntrySchema, psycheValueSchema, schemaCatalogEntrySchema, triggerReportSchema, updateBehaviorPatternSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "../psyche-types.js";
9
+ import { behaviorPatternSchema, behaviorSchema, beliefEntrySchema, createBehaviorPatternSchema, createBehaviorSchema, createBeliefEntrySchema, createEmotionDefinitionSchema, createEventTypeSchema, createFlashcardSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, domainSchema, emotionDefinitionSchema, eventTypeSchema, flashcardSchema, modeFamilySchema, modeGuideResultSchema, modeGuideSessionSchema, modeProfileSchema, modeTimelineEntrySchema, psycheValueSchema, schemaCatalogEntrySchema, triggerReportSchema, updateBehaviorPatternSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateFlashcardSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "../psyche-types.js";
10
10
  const PSYCHE_DOMAIN_ID = "domain_psyche";
11
11
  function parseJson(value) {
12
12
  return JSON.parse(value);
@@ -180,6 +180,33 @@ function mapModeGuideSession(row) {
180
180
  updatedAt: row.updated_at
181
181
  });
182
182
  }
183
+ function mapFlashcard(row) {
184
+ return flashcardSchema.parse({
185
+ id: row.id,
186
+ domainId: row.domain_id,
187
+ title: row.title,
188
+ message: row.message,
189
+ triggerSentence: row.trigger_sentence,
190
+ triggerSituation: row.trigger_situation,
191
+ tags: parseJson(row.tags_json),
192
+ backgroundColor: row.background_color,
193
+ textColor: row.text_color,
194
+ accentColor: row.accent_color,
195
+ typography: row.typography,
196
+ imageUrl: row.image_url,
197
+ imageAlt: row.image_alt,
198
+ layout: row.layout,
199
+ visualStyle: row.visual_style,
200
+ linkedValueIds: filterDeletedIds("psyche_value", parseJson(row.linked_value_ids_json)),
201
+ linkedBehaviorIds: filterDeletedIds("behavior", parseJson(row.linked_behavior_ids_json)),
202
+ linkedPatternIds: filterDeletedIds("behavior_pattern", parseJson(row.linked_pattern_ids_json)),
203
+ linkedBeliefIds: filterDeletedIds("belief_entry", parseJson(row.linked_belief_ids_json)),
204
+ linkedModeIds: filterDeletedIds("mode_profile", parseJson(row.linked_mode_ids_json)),
205
+ linkedReportIds: filterDeletedIds("trigger_report", parseJson(row.linked_report_ids_json)),
206
+ createdAt: row.created_at,
207
+ updatedAt: row.updated_at
208
+ });
209
+ }
183
210
  function mapTriggerReport(row) {
184
211
  const emotions = parseJson(row.emotions_json).map((emotion) => emotion.emotionDefinitionId && isEntityDeleted("emotion_definition", emotion.emotionDefinitionId)
185
212
  ? { ...emotion, emotionDefinitionId: null }
@@ -679,6 +706,7 @@ export function deletePsycheValue(valueId, context) {
679
706
  removeIdFromStringArrayColumn("belief_entries", "linked_value_ids_json", valueId);
680
707
  removeIdFromStringArrayColumn("mode_profiles", "linked_value_ids_json", valueId);
681
708
  removeIdFromStringArrayColumn("trigger_reports", "linked_value_ids_json", valueId);
709
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_value_ids_json", valueId);
682
710
  unlinkEntityNotes("psyche_value", valueId);
683
711
  clearEntityOwner("psyche_value", valueId);
684
712
  getDatabase()
@@ -804,6 +832,7 @@ export function deleteBehaviorPattern(patternId, context) {
804
832
  removeIdFromStringArrayColumn("psyche_behaviors", "linked_pattern_ids_json", patternId);
805
833
  removeIdFromStringArrayColumn("mode_profiles", "linked_pattern_ids_json", patternId);
806
834
  removeIdFromStringArrayColumn("trigger_reports", "linked_pattern_ids_json", patternId);
835
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_pattern_ids_json", patternId);
807
836
  unlinkEntityNotes("behavior_pattern", patternId);
808
837
  clearEntityOwner("behavior_pattern", patternId);
809
838
  getDatabase()
@@ -916,6 +945,7 @@ export function deleteBehavior(behaviorId, context) {
916
945
  removeIdFromStringArrayColumn("belief_entries", "linked_behavior_ids_json", behaviorId);
917
946
  removeIdFromStringArrayColumn("mode_profiles", "linked_behavior_ids_json", behaviorId);
918
947
  removeIdFromStringArrayColumn("trigger_reports", "linked_behavior_ids_json", behaviorId);
948
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_behavior_ids_json", behaviorId);
919
949
  nullifyTriggerBehaviorReferences(behaviorId);
920
950
  unlinkEntityNotes("behavior", behaviorId);
921
951
  clearEntityOwner("behavior", behaviorId);
@@ -1028,6 +1058,7 @@ export function deleteBeliefEntry(beliefId, context) {
1028
1058
  return runInTransaction(() => {
1029
1059
  removeIdFromStringArrayColumn("behavior_patterns", "linked_belief_ids_json", beliefId);
1030
1060
  removeIdFromStringArrayColumn("trigger_reports", "linked_belief_ids_json", beliefId);
1061
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_belief_ids_json", beliefId);
1031
1062
  nullifyTriggerThoughtBeliefReferences(beliefId);
1032
1063
  unlinkEntityNotes("belief_entry", beliefId);
1033
1064
  clearEntityOwner("belief_entry", beliefId);
@@ -1142,6 +1173,7 @@ export function deleteModeProfile(modeId, context) {
1142
1173
  removeIdFromStringArrayColumn("psyche_behaviors", "linked_mode_ids_json", modeId);
1143
1174
  removeIdFromStringArrayColumn("belief_entries", "linked_mode_ids_json", modeId);
1144
1175
  removeIdFromStringArrayColumn("trigger_reports", "linked_mode_ids_json", modeId);
1176
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_mode_ids_json", modeId);
1145
1177
  nullifyTriggerTimelineModeReferences(modeId);
1146
1178
  unlinkEntityNotes("mode_profile", modeId);
1147
1179
  clearEntityOwner("mode_profile", modeId);
@@ -1260,6 +1292,118 @@ export function deleteModeGuideSession(sessionId, context) {
1260
1292
  return existing;
1261
1293
  });
1262
1294
  }
1295
+ const FLASHCARD_SELECT = `SELECT
1296
+ id, domain_id, title, message, trigger_sentence, trigger_situation, tags_json,
1297
+ background_color, text_color, accent_color, typography, image_url, image_alt,
1298
+ layout, visual_style, linked_value_ids_json, linked_behavior_ids_json,
1299
+ linked_pattern_ids_json, linked_belief_ids_json, linked_mode_ids_json,
1300
+ linked_report_ids_json, created_at, updated_at
1301
+ FROM psyche_flashcards`;
1302
+ export function listFlashcards() {
1303
+ const rows = getDatabase()
1304
+ .prepare(`${FLASHCARD_SELECT}
1305
+ WHERE domain_id = ?
1306
+ ORDER BY updated_at DESC`)
1307
+ .all(PSYCHE_DOMAIN_ID);
1308
+ return filterDeletedEntities("flashcard", rows.map(mapFlashcard));
1309
+ }
1310
+ export function getFlashcardById(flashcardId) {
1311
+ if (isEntityDeleted("flashcard", flashcardId)) {
1312
+ return undefined;
1313
+ }
1314
+ const row = getRow(`${FLASHCARD_SELECT}
1315
+ WHERE id = ?`, flashcardId);
1316
+ return row ? decorateOwnedEntity("flashcard", mapFlashcard(row)) : undefined;
1317
+ }
1318
+ export function createFlashcard(input, context) {
1319
+ const parsed = createFlashcardSchema.parse(input);
1320
+ const now = new Date().toISOString();
1321
+ const flashcard = flashcardSchema.parse({
1322
+ id: buildId("flc"),
1323
+ domainId: PSYCHE_DOMAIN_ID,
1324
+ ...parsed,
1325
+ createdAt: now,
1326
+ updatedAt: now
1327
+ });
1328
+ getDatabase()
1329
+ .prepare(`INSERT INTO psyche_flashcards (
1330
+ id, domain_id, title, message, trigger_sentence, trigger_situation, tags_json,
1331
+ background_color, text_color, accent_color, typography, image_url, image_alt,
1332
+ layout, visual_style, linked_value_ids_json, linked_behavior_ids_json,
1333
+ linked_pattern_ids_json, linked_belief_ids_json, linked_mode_ids_json,
1334
+ linked_report_ids_json, created_at, updated_at
1335
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1336
+ .run(flashcard.id, flashcard.domainId, flashcard.title, flashcard.message, flashcard.triggerSentence, flashcard.triggerSituation, JSON.stringify(flashcard.tags), flashcard.backgroundColor, flashcard.textColor, flashcard.accentColor, flashcard.typography, flashcard.imageUrl, flashcard.imageAlt, flashcard.layout, flashcard.visualStyle, JSON.stringify(flashcard.linkedValueIds), JSON.stringify(flashcard.linkedBehaviorIds), JSON.stringify(flashcard.linkedPatternIds), JSON.stringify(flashcard.linkedBeliefIds), JSON.stringify(flashcard.linkedModeIds), JSON.stringify(flashcard.linkedReportIds), flashcard.createdAt, flashcard.updatedAt);
1337
+ assignOwnedEntity("flashcard", flashcard.id, parsed.userId, context.actor);
1338
+ mapCreateUpdateContext({
1339
+ entityType: "flashcard",
1340
+ entityId: flashcard.id,
1341
+ title: "Flashcard added",
1342
+ eventKind: "flashcard.created",
1343
+ source: context.source,
1344
+ actor: context.actor ?? null,
1345
+ metadata: { domainId: flashcard.domainId }
1346
+ });
1347
+ return decorateOwnedEntity("flashcard", flashcard);
1348
+ }
1349
+ export function updateFlashcard(flashcardId, patch, context) {
1350
+ const existing = getFlashcardById(flashcardId);
1351
+ if (!existing) {
1352
+ return undefined;
1353
+ }
1354
+ const parsed = updateFlashcardSchema.parse(patch);
1355
+ const updated = flashcardSchema.parse({
1356
+ ...existing,
1357
+ ...parsed,
1358
+ updatedAt: new Date().toISOString()
1359
+ });
1360
+ getDatabase()
1361
+ .prepare(`UPDATE psyche_flashcards
1362
+ SET title = ?, message = ?, trigger_sentence = ?, trigger_situation = ?,
1363
+ tags_json = ?, background_color = ?, text_color = ?, accent_color = ?,
1364
+ typography = ?, image_url = ?, image_alt = ?, layout = ?, visual_style = ?,
1365
+ linked_value_ids_json = ?, linked_behavior_ids_json = ?, linked_pattern_ids_json = ?,
1366
+ linked_belief_ids_json = ?, linked_mode_ids_json = ?, linked_report_ids_json = ?,
1367
+ updated_at = ?
1368
+ WHERE id = ?`)
1369
+ .run(updated.title, updated.message, updated.triggerSentence, updated.triggerSituation, JSON.stringify(updated.tags), updated.backgroundColor, updated.textColor, updated.accentColor, updated.typography, updated.imageUrl, updated.imageAlt, updated.layout, updated.visualStyle, JSON.stringify(updated.linkedValueIds), JSON.stringify(updated.linkedBehaviorIds), JSON.stringify(updated.linkedPatternIds), JSON.stringify(updated.linkedBeliefIds), JSON.stringify(updated.linkedModeIds), JSON.stringify(updated.linkedReportIds), updated.updatedAt, flashcardId);
1370
+ if (parsed.userId !== undefined) {
1371
+ assignOwnedEntity("flashcard", flashcardId, parsed.userId, context.actor);
1372
+ }
1373
+ mapCreateUpdateContext({
1374
+ entityType: "flashcard",
1375
+ entityId: flashcardId,
1376
+ title: "Flashcard updated",
1377
+ eventKind: "flashcard.updated",
1378
+ source: context.source,
1379
+ actor: context.actor ?? null,
1380
+ metadata: { domainId: updated.domainId }
1381
+ });
1382
+ return decorateOwnedEntity("flashcard", updated);
1383
+ }
1384
+ export function deleteFlashcard(flashcardId, context) {
1385
+ const existing = getFlashcardById(flashcardId);
1386
+ if (!existing) {
1387
+ return undefined;
1388
+ }
1389
+ return runInTransaction(() => {
1390
+ unlinkEntityNotes("flashcard", flashcardId);
1391
+ clearEntityOwner("flashcard", flashcardId);
1392
+ getDatabase()
1393
+ .prepare(`DELETE FROM psyche_flashcards WHERE id = ?`)
1394
+ .run(flashcardId);
1395
+ mapCreateUpdateContext({
1396
+ entityType: "flashcard",
1397
+ entityId: flashcardId,
1398
+ title: "Flashcard deleted",
1399
+ eventKind: "flashcard.deleted",
1400
+ source: context.source,
1401
+ actor: context.actor ?? null,
1402
+ metadata: { domainId: existing.domainId }
1403
+ });
1404
+ return existing;
1405
+ });
1406
+ }
1263
1407
  export function listTriggerReports(limit) {
1264
1408
  const limitSql = limit ? "LIMIT ?" : "";
1265
1409
  const rows = getDatabase()
@@ -1391,6 +1535,7 @@ export function deleteTriggerReport(reportId, context) {
1391
1535
  }
1392
1536
  return runInTransaction(() => {
1393
1537
  removeIdFromStringArrayColumn("belief_entries", "linked_report_ids_json", reportId);
1538
+ removeIdFromStringArrayColumn("psyche_flashcards", "linked_report_ids_json", reportId);
1394
1539
  unlinkEntityNotes("trigger_report", reportId);
1395
1540
  clearEntityOwner("trigger_report", reportId);
1396
1541
  getDatabase()
@@ -6,12 +6,12 @@ import { createCalendarEvent, createTaskTimebox, createWorkBlockTemplate, delete
6
6
  import { createNote, deleteNote, getNoteById, listNotes, unlinkNotesForEntity, updateNote } from "../repositories/notes.js";
7
7
  import { clearEntityOwner, filterOwnedEntities } from "../repositories/entity-ownership.js";
8
8
  import { createPreferenceCatalog, createPreferenceCatalogItem, createPreferenceContext, createPreferenceItem, deletePreferenceCatalog, deletePreferenceCatalogItem, deletePreferenceContext, deletePreferenceItem, getPreferenceCatalogById, getPreferenceCatalogItemById, getPreferenceContextById, getPreferenceItemById, listPreferenceCatalogItems, listPreferenceCatalogs, listPreferenceContexts, listPreferenceItems, updatePreferenceCatalog, updatePreferenceCatalogItem, updatePreferenceContext, updatePreferenceItem } from "../repositories/preferences.js";
9
- import { createBehaviorPatternSchema, createBehaviorSchema, createBeliefEntrySchema, createEmotionDefinitionSchema, createEventTypeSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, updateBehaviorPatternSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "../psyche-types.js";
9
+ import { createBehaviorPatternSchema, createBehaviorSchema, createBeliefEntrySchema, createEmotionDefinitionSchema, createEventTypeSchema, createFlashcardSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, updateBehaviorPatternSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateFlashcardSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "../psyche-types.js";
10
10
  import { buildSettingsBinPayload, cascadeSoftDeleteAnchoredCollaboration, clearDeletedEntityRecord, getDeletedEntityRecord, listDeletedEntities, restoreAnchoredCollaboration, restoreDeletedEntityRecord, upsertDeletedEntityRecord } from "../repositories/deleted-entities.js";
11
11
  import { createGoal, deleteGoal, getGoalById, listGoals, updateGoal } from "../repositories/goals.js";
12
12
  import { createHabit, deleteHabit, getHabitById, listHabits, updateHabit } from "../repositories/habits.js";
13
13
  import { createQuestionnaireInstrument, deleteQuestionnaireInstrument, getQuestionnaireInstrumentEntityById, listQuestionnaireInstrumentEntities, updateQuestionnaireInstrument, updateQuestionnaireInstrumentSchema } from "../repositories/questionnaires.js";
14
- import { createBehavior, createBehaviorPattern, createBeliefEntry, createEmotionDefinition, createEventType, createModeGuideSession, createModeProfile, createPsycheValue, createTriggerReport, deleteBehavior, deleteBehaviorPattern, deleteBeliefEntry, deleteEmotionDefinition, deleteEventType, deleteModeGuideSession, deleteModeProfile, deletePsycheValue, deleteTriggerReport, getBehaviorById, getBehaviorPatternById, getBeliefEntryById, getEmotionDefinitionById, getEventTypeById, getModeGuideSessionById, getModeProfileById, getPsycheValueById, getTriggerReportById, listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports, updateBehavior, updateBehaviorPattern, updateBeliefEntry, updateEmotionDefinition, updateEventType, updateModeGuideSession, updateModeProfile, updatePsycheValue, updateTriggerReport } from "../repositories/psyche.js";
14
+ import { createBehavior, createBehaviorPattern, createBeliefEntry, createEmotionDefinition, createEventType, createFlashcard, createModeGuideSession, createModeProfile, createPsycheValue, createTriggerReport, deleteBehavior, deleteBehaviorPattern, deleteBeliefEntry, deleteEmotionDefinition, deleteEventType, deleteFlashcard, deleteModeGuideSession, deleteModeProfile, deletePsycheValue, deleteTriggerReport, getBehaviorById, getBehaviorPatternById, getBeliefEntryById, getEmotionDefinitionById, getEventTypeById, getFlashcardById, getModeGuideSessionById, getModeProfileById, getPsycheValueById, getTriggerReportById, listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listFlashcards, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports, updateBehavior, updateBehaviorPattern, updateBeliefEntry, updateEmotionDefinition, updateEventType, updateFlashcard, updateModeGuideSession, updateModeProfile, updatePsycheValue, updateTriggerReport } from "../repositories/psyche.js";
15
15
  import { createProject, deleteProject, getProjectById, listProjects, updateProject } from "../repositories/projects.js";
16
16
  import { createStrategy, deleteStrategy, getStrategyById, listStrategies, updateStrategy } from "../repositories/strategies.js";
17
17
  import { createTag, deleteTag, getTagById, listTags, updateTag } from "../repositories/tags.js";
@@ -245,6 +245,17 @@ const CRUD_ENTITY_CAPABILITIES = {
245
245
  update: (id, patch, context) => updateModeGuideSession(id, patch, context),
246
246
  hardDelete: (id, context) => deleteModeGuideSession(id, context)
247
247
  },
248
+ flashcard: {
249
+ entityType: "flashcard",
250
+ routeBase: "/api/v1/entities",
251
+ deleteMode: "soft_default",
252
+ inBin: true,
253
+ list: () => listFlashcards(),
254
+ get: (id) => getFlashcardById(id),
255
+ create: (data, context) => createFlashcard(data, context),
256
+ update: (id, patch, context) => updateFlashcard(id, patch, context),
257
+ hardDelete: (id, context) => deleteFlashcard(id, context)
258
+ },
248
259
  event_type: {
249
260
  entityType: "event_type",
250
261
  routeBase: "/api/v1/psyche/event-types",
@@ -367,6 +378,7 @@ const CREATE_ENTITY_SCHEMAS = {
367
378
  belief_entry: createBeliefEntrySchema,
368
379
  mode_profile: createModeProfileSchema,
369
380
  mode_guide_session: createModeGuideSessionSchema,
381
+ flashcard: createFlashcardSchema,
370
382
  event_type: createEventTypeSchema,
371
383
  emotion_definition: createEmotionDefinitionSchema,
372
384
  trigger_report: createTriggerReportSchema,
@@ -396,6 +408,7 @@ const UPDATE_ENTITY_SCHEMAS = {
396
408
  belief_entry: updateBeliefEntrySchema,
397
409
  mode_profile: updateModeProfileSchema,
398
410
  mode_guide_session: updateModeGuideSessionSchema,
411
+ flashcard: updateFlashcardSchema,
399
412
  event_type: updateEventTypeSchema,
400
413
  emotion_definition: updateEmotionDefinitionSchema,
401
414
  trigger_report: updateTriggerReportSchema,
@@ -636,9 +649,11 @@ function describeEntity(entityType, entity) {
636
649
  ? entity.label
637
650
  : typeof entity.summary === "string" && entity.summary.trim().length > 0
638
651
  ? entity.summary
639
- : typeof entity.body === "string" && entity.body.trim().length > 0
640
- ? entity.body.slice(0, 72)
641
- : entityType.replaceAll("_", " ");
652
+ : typeof entity.message === "string" && entity.message.trim().length > 0
653
+ ? entity.message.slice(0, 72)
654
+ : typeof entity.body === "string" && entity.body.trim().length > 0
655
+ ? entity.body.slice(0, 72)
656
+ : entityType.replaceAll("_", " ");
642
657
  const subtitle = typeof entity.description === "string" && entity.description.trim().length > 0
643
658
  ? entity.description
644
659
  : typeof entity.summary === "string" && entity.summary.trim().length > 0
@@ -708,6 +723,13 @@ function matchesLinkedTo(entityType, entity, linkedTo) {
708
723
  return ((linkedTo.entityType === "behavior_pattern" && Array.isArray(entity.linkedPatternIds) && entity.linkedPatternIds.includes(linkedTo.id)) ||
709
724
  (linkedTo.entityType === "behavior" && Array.isArray(entity.linkedBehaviorIds) && entity.linkedBehaviorIds.includes(linkedTo.id)) ||
710
725
  (linkedTo.entityType === "psyche_value" && Array.isArray(entity.linkedValueIds) && entity.linkedValueIds.includes(linkedTo.id)));
726
+ case "flashcard":
727
+ return ((linkedTo.entityType === "psyche_value" && Array.isArray(entity.linkedValueIds) && entity.linkedValueIds.includes(linkedTo.id)) ||
728
+ (linkedTo.entityType === "behavior" && Array.isArray(entity.linkedBehaviorIds) && entity.linkedBehaviorIds.includes(linkedTo.id)) ||
729
+ (linkedTo.entityType === "behavior_pattern" && Array.isArray(entity.linkedPatternIds) && entity.linkedPatternIds.includes(linkedTo.id)) ||
730
+ (linkedTo.entityType === "belief_entry" && Array.isArray(entity.linkedBeliefIds) && entity.linkedBeliefIds.includes(linkedTo.id)) ||
731
+ (linkedTo.entityType === "mode_profile" && Array.isArray(entity.linkedModeIds) && entity.linkedModeIds.includes(linkedTo.id)) ||
732
+ (linkedTo.entityType === "trigger_report" && Array.isArray(entity.linkedReportIds) && entity.linkedReportIds.includes(linkedTo.id)));
711
733
  case "trigger_report":
712
734
  return ((linkedTo.entityType === "behavior_pattern" && Array.isArray(entity.linkedPatternIds) && entity.linkedPatternIds.includes(linkedTo.id)) ||
713
735
  (linkedTo.entityType === "psyche_value" && Array.isArray(entity.linkedValueIds) && entity.linkedValueIds.includes(linkedTo.id)) ||
@@ -34,6 +34,7 @@ const ENTITY_CREATION_REWARD_SOURCES = [
34
34
  { entityType: "behavior", tableName: "psyche_behaviors", titleColumn: "title" },
35
35
  { entityType: "belief_entry", tableName: "belief_entries", titleColumn: "statement" },
36
36
  { entityType: "mode_profile", tableName: "mode_profiles", titleColumn: "title" },
37
+ { entityType: "flashcard", tableName: "psyche_flashcards", titleColumn: "title" },
37
38
  { entityType: "trigger_report", tableName: "trigger_reports", titleColumn: "title" }
38
39
  ];
39
40
  function startOfWeek(date) {
@@ -452,6 +453,7 @@ function buildMetricValues(input) {
452
453
  "belief_entry",
453
454
  "mode_profile",
454
455
  "mode_guide_session",
456
+ "flashcard",
455
457
  "trigger_report"
456
458
  ]);
457
459
  const nonManualXp = positiveRewards.reduce((sum, reward) => sum + reward.deltaXp, 0);
@@ -5,7 +5,7 @@ import { filterOwnedEntities } from "../repositories/entity-ownership.js";
5
5
  import { listGoals } from "../repositories/goals.js";
6
6
  import { listHabits } from "../repositories/habits.js";
7
7
  import { listNotes } from "../repositories/notes.js";
8
- import { listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports } from "../repositories/psyche.js";
8
+ import { listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listFlashcards, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports } from "../repositories/psyche.js";
9
9
  import { listStrategies } from "../repositories/strategies.js";
10
10
  import { listTags } from "../repositories/tags.js";
11
11
  import { listTasks } from "../repositories/tasks.js";
@@ -40,6 +40,7 @@ const BASE_NODE_SIZE = {
40
40
  belief: 38,
41
41
  mode: 38,
42
42
  mode_session: 34,
43
+ flashcard: 36,
43
44
  report: 40,
44
45
  event_type: 32,
45
46
  emotion: 30,
@@ -250,6 +251,7 @@ export function buildKnowledgeGraph(userIds, query = {}) {
250
251
  const beliefs = filterOwnedEntities("belief_entry", listBeliefEntries(), userIds);
251
252
  const modes = filterOwnedEntities("mode_profile", listModeProfiles(), userIds);
252
253
  const modeSessions = filterOwnedEntities("mode_guide_session", listModeGuideSessions(), userIds);
254
+ const flashcards = filterOwnedEntities("flashcard", listFlashcards(), userIds);
253
255
  const reports = filterOwnedEntities("trigger_report", listTriggerReports(), userIds);
254
256
  const wikiSpaces = listWikiSpaces();
255
257
  const flows = listAiConnectors();
@@ -1155,6 +1157,90 @@ export function buildKnowledgeGraph(userIds, query = {}) {
1155
1157
  });
1156
1158
  }
1157
1159
  }
1160
+ for (const flashcard of flashcards) {
1161
+ nodes.set(buildKnowledgeGraphNodeId("flashcard", flashcard.id), makeNode({
1162
+ entityType: "flashcard",
1163
+ entityId: flashcard.id,
1164
+ entityKind: "flashcard",
1165
+ title: flashcard.title || truncate(flashcard.message, 72),
1166
+ subtitle: flashcard.triggerSentence || flashcard.triggerSituation,
1167
+ description: flashcard.message,
1168
+ previewStats: [
1169
+ { label: "Tags", value: flashcard.tags.length },
1170
+ { label: "Values", value: flashcard.linkedValueIds.length },
1171
+ { label: "Reports", value: flashcard.linkedReportIds.length }
1172
+ ],
1173
+ owner: flashcard,
1174
+ href: getKnowledgeGraphEntityHref("flashcard", flashcard.id),
1175
+ updatedAt: flashcard.updatedAt
1176
+ }));
1177
+ for (const linkedId of flashcard.linkedValueIds) {
1178
+ addEdge(edges, {
1179
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1180
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
1181
+ relationKind: "flashcard_value",
1182
+ label: "Flashcard to value",
1183
+ strength: 0.78,
1184
+ directional: true,
1185
+ structural: false
1186
+ });
1187
+ }
1188
+ for (const linkedId of flashcard.linkedBehaviorIds) {
1189
+ addEdge(edges, {
1190
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1191
+ target: buildKnowledgeGraphNodeId("behavior", linkedId),
1192
+ relationKind: "flashcard_behavior",
1193
+ label: "Flashcard to behavior",
1194
+ strength: 0.76,
1195
+ directional: true,
1196
+ structural: false
1197
+ });
1198
+ }
1199
+ for (const linkedId of flashcard.linkedPatternIds) {
1200
+ addEdge(edges, {
1201
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1202
+ target: buildKnowledgeGraphNodeId("behavior_pattern", linkedId),
1203
+ relationKind: "flashcard_pattern",
1204
+ label: "Flashcard to pattern",
1205
+ strength: 0.76,
1206
+ directional: true,
1207
+ structural: false
1208
+ });
1209
+ }
1210
+ for (const linkedId of flashcard.linkedBeliefIds) {
1211
+ addEdge(edges, {
1212
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1213
+ target: buildKnowledgeGraphNodeId("belief_entry", linkedId),
1214
+ relationKind: "flashcard_belief",
1215
+ label: "Flashcard to belief",
1216
+ strength: 0.74,
1217
+ directional: true,
1218
+ structural: false
1219
+ });
1220
+ }
1221
+ for (const linkedId of flashcard.linkedModeIds) {
1222
+ addEdge(edges, {
1223
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1224
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
1225
+ relationKind: "flashcard_mode",
1226
+ label: "Flashcard to mode",
1227
+ strength: 0.74,
1228
+ directional: true,
1229
+ structural: false
1230
+ });
1231
+ }
1232
+ for (const linkedId of flashcard.linkedReportIds) {
1233
+ addEdge(edges, {
1234
+ source: buildKnowledgeGraphNodeId("flashcard", flashcard.id),
1235
+ target: buildKnowledgeGraphNodeId("trigger_report", linkedId),
1236
+ relationKind: "flashcard_report",
1237
+ label: "Flashcard to report",
1238
+ strength: 0.74,
1239
+ directional: true,
1240
+ structural: false
1241
+ });
1242
+ }
1243
+ }
1158
1244
  for (const report of reports) {
1159
1245
  nodes.set(buildKnowledgeGraphNodeId("trigger_report", report.id), makeNode({
1160
1246
  entityType: "trigger_report",
@@ -25,6 +25,7 @@ const ACTIVITY_ENTITY_LABELS = {
25
25
  belief_entry: "Belief",
26
26
  mode_profile: "Mode",
27
27
  mode_guide_session: "Mode guide session",
28
+ flashcard: "Flashcard",
28
29
  trigger_report: "Trigger report",
29
30
  questionnaire_run: "Questionnaire run",
30
31
  event_type: "Event type",
@@ -2,7 +2,7 @@ import { getDomainBySlug } from "../repositories/domains.js";
2
2
  import { listInsights } from "../repositories/collaboration.js";
3
3
  import { listNotes } from "../repositories/notes.js";
4
4
  import { filterOwnedEntities } from "../repositories/entity-ownership.js";
5
- import { listBehaviorPatterns, listBehaviors, listBeliefEntries, listModeProfiles, listPsycheValues, listSchemaCatalog, listTriggerReports } from "../repositories/psyche.js";
5
+ import { listBehaviorPatterns, listBehaviors, listBeliefEntries, listFlashcards, listModeProfiles, listPsycheValues, listSchemaCatalog, listTriggerReports } from "../repositories/psyche.js";
6
6
  import { getDevrageMetricPayload } from "./devrage.js";
7
7
  import { psycheOverviewPayloadSchema } from "../psyche-types.js";
8
8
  const PSYCHE_ENTITY_TYPE_SET = new Set([
@@ -11,6 +11,7 @@ const PSYCHE_ENTITY_TYPE_SET = new Set([
11
11
  "behavior",
12
12
  "belief_entry",
13
13
  "mode_profile",
14
+ "flashcard",
14
15
  "trigger_report"
15
16
  ]);
16
17
  export function getPsycheOverview(userIds) {
@@ -23,6 +24,7 @@ export function getPsycheOverview(userIds) {
23
24
  const behaviors = filterOwnedEntities("behavior", listBehaviors(), userIds);
24
25
  const beliefs = filterOwnedEntities("belief_entry", listBeliefEntries(), userIds);
25
26
  const modes = filterOwnedEntities("mode_profile", listModeProfiles(), userIds);
27
+ const flashcards = filterOwnedEntities("flashcard", listFlashcards(), userIds);
26
28
  const reports = filterOwnedEntities("trigger_report", listTriggerReports(5), userIds);
27
29
  const schemaCatalog = listSchemaCatalog();
28
30
  const notes = filterOwnedEntities("note", listNotes({ limit: 200 }), userIds);
@@ -57,6 +59,7 @@ export function getPsycheOverview(userIds) {
57
59
  behaviors,
58
60
  beliefs,
59
61
  modes,
62
+ flashcards,
60
63
  reports,
61
64
  schemaPressure,
62
65
  devrageMetric,
@@ -147,6 +147,7 @@ export const activityEntityTypeSchema = z.enum([
147
147
  "belief_entry",
148
148
  "mode_profile",
149
149
  "mode_guide_session",
150
+ "flashcard",
150
151
  "trigger_report",
151
152
  "preference_catalog",
152
153
  "preference_catalog_item",
@@ -362,6 +363,7 @@ export const crudEntityTypeSchema = z.enum([
362
363
  "belief_entry",
363
364
  "mode_profile",
364
365
  "mode_guide_session",
366
+ "flashcard",
365
367
  "event_type",
366
368
  "emotion_definition",
367
369
  "trigger_report",
@@ -387,6 +389,7 @@ export const rewardableEntityTypeSchema = z.enum([
387
389
  "behavior",
388
390
  "belief_entry",
389
391
  "mode_profile",
392
+ "flashcard",
390
393
  "trigger_report"
391
394
  ]);
392
395
  export const deleteModeSchema = z.enum(["soft", "hard"]);
@@ -725,6 +725,49 @@ export function deleteModeGuideSession(sessionId) {
725
725
  method: "DELETE"
726
726
  });
727
727
  }
728
+ function readBatchEntity(result) {
729
+ if (result.ok !== true || !result.entity) {
730
+ const error = typeof result.error === "object" && result.error !== null
731
+ ? JSON.stringify(result.error)
732
+ : "Batch entity operation failed.";
733
+ throw new Error(error);
734
+ }
735
+ return result.entity;
736
+ }
737
+ export async function listFlashcards(userIds) {
738
+ const response = await searchEntities({
739
+ searches: [
740
+ {
741
+ entityTypes: ["flashcard"],
742
+ userIds: coerceUserIds(userIds),
743
+ limit: 500
744
+ }
745
+ ]
746
+ });
747
+ const matches = (response.results[0]?.matches ?? []);
748
+ return { flashcards: matches };
749
+ }
750
+ export async function createFlashcard(input) {
751
+ const response = await createEntities({
752
+ operations: [{ entityType: "flashcard", data: input }],
753
+ atomic: true
754
+ });
755
+ return { flashcard: readBatchEntity(response.results[0] ?? {}) };
756
+ }
757
+ export async function patchFlashcard(flashcardId, patch) {
758
+ const response = await updateEntities({
759
+ operations: [{ entityType: "flashcard", id: flashcardId, patch }],
760
+ atomic: true
761
+ });
762
+ return { flashcard: readBatchEntity(response.results[0] ?? {}) };
763
+ }
764
+ export async function deleteFlashcard(flashcardId) {
765
+ const response = await deleteEntities({
766
+ operations: [{ entityType: "flashcard", id: flashcardId }],
767
+ atomic: true
768
+ });
769
+ return { flashcard: readBatchEntity(response.results[0] ?? {}) };
770
+ }
728
771
  export function listEventTypes() {
729
772
  return request("/api/v1/psyche/event-types");
730
773
  }
@@ -20,6 +20,7 @@ export const ENTITY_KINDS = [
20
20
  "belief",
21
21
  "mode",
22
22
  "mode_session",
23
+ "flashcard",
23
24
  "report",
24
25
  "event_type",
25
26
  "emotion",
@@ -168,6 +169,13 @@ const ENTITY_VISUAL_SEEDS = [
168
169
  iconName: "Compass",
169
170
  accentRgb: [192, 132, 252]
170
171
  },
172
+ {
173
+ kind: "flashcard",
174
+ label: "Flashcard",
175
+ icon: PanelTop,
176
+ iconName: "PanelTop",
177
+ accentRgb: [20, 184, 166]
178
+ },
171
179
  {
172
180
  kind: "report",
173
181
  label: "Report",
@@ -257,6 +265,7 @@ const CRUD_ENTITY_KIND_MAP = {
257
265
  belief_entry: "belief",
258
266
  mode_profile: "mode",
259
267
  mode_guide_session: "mode_session",
268
+ flashcard: "flashcard",
260
269
  event_type: "event_type",
261
270
  emotion_definition: "emotion",
262
271
  trigger_report: "report"
@@ -64,6 +64,11 @@ export const KNOWLEDGE_GRAPH_HIERARCHY_LANES = [
64
64
  label: "Modes / Mode Sessions",
65
65
  kinds: ["mode", "mode_session"]
66
66
  },
67
+ {
68
+ id: "flashcards",
69
+ label: "Flashcards",
70
+ kinds: ["flashcard"]
71
+ },
67
72
  {
68
73
  id: "reports",
69
74
  label: "Reports / Event Types / Emotions",
@@ -110,6 +115,12 @@ export const KNOWLEDGE_GRAPH_RELATION_LABELS = {
110
115
  mode_pattern: "Mode to pattern",
111
116
  mode_behavior: "Mode to behavior",
112
117
  mode_value: "Mode to value",
118
+ flashcard_value: "Flashcard to value",
119
+ flashcard_behavior: "Flashcard to behavior",
120
+ flashcard_pattern: "Flashcard to pattern",
121
+ flashcard_belief: "Flashcard to belief",
122
+ flashcard_mode: "Flashcard to mode",
123
+ flashcard_report: "Flashcard to report",
113
124
  report_value: "Report to value",
114
125
  report_pattern: "Report to pattern",
115
126
  report_goal: "Report to goal",
@@ -165,6 +176,12 @@ export const KNOWLEDGE_GRAPH_RELATION_FAMILY_MAP = {
165
176
  mode_pattern: "contextual",
166
177
  mode_behavior: "contextual",
167
178
  mode_value: "contextual",
179
+ flashcard_value: "contextual",
180
+ flashcard_behavior: "contextual",
181
+ flashcard_pattern: "contextual",
182
+ flashcard_belief: "contextual",
183
+ flashcard_mode: "contextual",
184
+ flashcard_report: "contextual",
168
185
  report_value: "contextual",
169
186
  report_pattern: "contextual",
170
187
  report_goal: "contextual",
@@ -234,6 +251,8 @@ export function getKnowledgeGraphEntityHref(entityType, entityId, options) {
234
251
  return `/psyche/modes?focus=${encodeURIComponent(entityId)}`;
235
252
  case "mode_guide_session":
236
253
  return `/psyche/modes`;
254
+ case "flashcard":
255
+ return `/psyche/flashcards?focus=${encodeURIComponent(entityId)}`;
237
256
  case "trigger_report":
238
257
  return `/psyche/reports/${encodeURIComponent(entityId)}`;
239
258
  case "note":