@sidurijs/api 1.0.0 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sidurijs/api",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -8,28 +8,22 @@
8
8
  "engines": {
9
9
  "node": ">=22.16.0"
10
10
  },
11
- "scripts": {
12
- "build": "tsc",
13
- "start": "node dist/index.js",
14
- "dev": "tsc -w & DEV_LOCAL_AUTH_ROLE=OWNER node dist/index.js",
15
- "test": "jest --config jest.config.json"
16
- },
17
11
  "dependencies": {
18
- "@sidurijs/body": "workspace:*",
19
- "@sidurijs/brain": "workspace:*",
20
- "@sidurijs/core": "workspace:*",
21
- "@sidurijs/ear": "workspace:*",
22
- "@sidurijs/hands": "workspace:*",
23
- "@sidurijs/knowledge": "workspace:*",
24
- "@sidurijs/memory": "workspace:*",
25
- "@sidurijs/mouth": "workspace:*",
26
- "@sidurijs/observation": "workspace:*",
27
- "@sidurijs/self": "workspace:*",
28
- "@sidurijs/vision": "workspace:*",
29
- "@sidurijs/voice": "workspace:*",
30
12
  "cors": "^2.8.6",
31
13
  "dotenv": "^17.4.2",
32
- "express": "^5.2.1"
14
+ "express": "^5.2.1",
15
+ "@sidurijs/body": "1.0.0",
16
+ "@sidurijs/brain": "1.0.1",
17
+ "@sidurijs/core": "1.0.2",
18
+ "@sidurijs/ear": "1.0.0",
19
+ "@sidurijs/hands": "1.0.0",
20
+ "@sidurijs/knowledge": "1.0.0",
21
+ "@sidurijs/archive": "1.0.1",
22
+ "@sidurijs/mouth": "1.0.0",
23
+ "@sidurijs/observation": "1.0.0",
24
+ "@sidurijs/self": "1.0.0",
25
+ "@sidurijs/vision": "1.0.0",
26
+ "@sidurijs/voice": "1.0.0"
33
27
  },
34
28
  "devDependencies": {
35
29
  "@types/cors": "^2.8.19",
@@ -40,5 +34,11 @@
40
34
  "supertest": "^7.2.2",
41
35
  "ts-jest": "^29.4.12",
42
36
  "typescript": "^5.9.3"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "start": "node dist/index.js",
41
+ "dev": "tsc -w & DEV_LOCAL_AUTH_ROLE=OWNER node dist/index.js",
42
+ "test": "jest --config jest.config.json"
43
43
  }
44
- }
44
+ }
package/src/app.ts CHANGED
@@ -531,15 +531,50 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
531
531
  return res.json({ success: true, interrupted: true, reason });
532
532
  });
533
533
 
534
- // MEMORY GETTERS
534
+ // SELF DIRECTIVES & PROPOSALS (RFC VX-26-13: Primitive 1)
535
+ app.get('/self/directives', requireAuth, async (req, res) => {
536
+ const id = req.query.id as string || Array.from(runtimes.keys())[0];
537
+ const runtime = runtimes.get(id);
538
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
539
+ try {
540
+ if (runtime.self && typeof (runtime.self as any).getActiveDirectives === 'function') {
541
+ const directives = typeof (runtime.self as any).getAllDirectives === 'function'
542
+ ? await (runtime.self as any).getAllDirectives(id)
543
+ : await (runtime.self as any).getActiveDirectives(id);
544
+ return res.json({ directives });
545
+ }
546
+ res.json({ directives: [] });
547
+ } catch (e: any) {
548
+ res.status(500).json({ error: e.message });
549
+ }
550
+ });
551
+
552
+ app.get('/self/proposals', requireAuth, async (req, res) => {
553
+ const id = req.query.id as string || Array.from(runtimes.keys())[0];
554
+ const runtime = runtimes.get(id);
555
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
556
+ try {
557
+ if (runtime.self && typeof (runtime.self as any).getPendingDirectives === 'function') {
558
+ const proposals = await (runtime.self as any).getPendingDirectives(id);
559
+ return res.json({ proposals });
560
+ }
561
+ res.json({ proposals: [] });
562
+ } catch (e: any) {
563
+ res.status(500).json({ error: e.message });
564
+ }
565
+ });
566
+
567
+ // PROPOSALS GETTERS (RFC VX-26-13: routed through self, legacy compatibility aliases)
535
568
  app.get('/memory/proposals', requireAuth, async (req, res) => {
536
569
  const id = req.query.id as string || Array.from(runtimes.keys())[0];
537
570
  const runtime = runtimes.get(id);
538
571
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
539
- if (!runtime.memory) return res.json({ proposals: [] });
540
572
  try {
541
- const proposals = await runtime.memory.getPendingClaims();
542
- res.json({ proposals });
573
+ if (runtime.self && typeof (runtime.self as any).getPendingDirectives === 'function') {
574
+ const proposals = await (runtime.self as any).getPendingDirectives(id);
575
+ return res.json({ proposals });
576
+ }
577
+ res.json({ proposals: [] });
543
578
  } catch (e: any) {
544
579
  res.status(500).json({ error: e.message });
545
580
  }
@@ -549,10 +584,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
549
584
  const id = req.query.id as string || Array.from(runtimes.keys())[0];
550
585
  const runtime = runtimes.get(id);
551
586
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
552
- if (!runtime.memory) return res.json({ items: [] });
553
587
  try {
554
- const items = await runtime.memory.getClaims();
555
- res.json({ items });
588
+ if (runtime.self && typeof (runtime.self as any).getAllDirectives === 'function') {
589
+ const items = await (runtime.self as any).getAllDirectives(id);
590
+ return res.json({ items });
591
+ }
592
+ res.json({ items: [] });
556
593
  } catch (e: any) {
557
594
  res.status(500).json({ error: e.message });
558
595
  }
@@ -562,12 +599,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
562
599
  const id = req.query.id as string || Array.from(runtimes.keys())[0];
563
600
  const runtime = runtimes.get(id);
564
601
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
565
- if (!runtime.memory) return res.json({ claims: [] });
566
602
  try {
567
- const claims = typeof (runtime.memory as any).getAllClaims === 'function'
568
- ? await (runtime.memory as any).getAllClaims()
569
- : await runtime.memory.getClaims();
570
- res.json({ claims });
603
+ if (runtime.self && typeof (runtime.self as any).getAllDirectives === 'function') {
604
+ const claims = await (runtime.self as any).getAllDirectives(id);
605
+ return res.json({ claims });
606
+ }
607
+ res.json({ claims: [] });
571
608
  } catch (e: any) {
572
609
  res.status(500).json({ error: e.message });
573
610
  }
@@ -577,12 +614,14 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
577
614
  const id = req.query.id as string || Array.from(runtimes.keys())[0];
578
615
  const runtime = runtimes.get(id);
579
616
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
580
- if (!runtime.memory) return res.json({ directives: [] });
581
617
  try {
582
- const directives = typeof (runtime.memory as any).getAllDirectives === 'function'
583
- ? await (runtime.memory as any).getAllDirectives()
584
- : await runtime.memory.getDirectives();
585
- res.json({ directives });
618
+ if (runtime.self && typeof (runtime.self as any).getActiveDirectives === 'function') {
619
+ const directives = typeof (runtime.self as any).getAllDirectives === 'function'
620
+ ? await (runtime.self as any).getAllDirectives(id)
621
+ : await (runtime.self as any).getActiveDirectives(id);
622
+ return res.json({ directives });
623
+ }
624
+ res.json({ directives: [] });
586
625
  } catch (e: any) {
587
626
  res.status(500).json({ error: e.message });
588
627
  }
@@ -878,38 +917,24 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
878
917
  }
879
918
  });
880
919
 
881
- // MEMORY MUTATIONS - PROPOSALS
920
+ // PROPOSAL MUTATIONS (RFC VX-26-13: routed through self)
882
921
  app.post('/memory/proposals/update', requireAuth, async (req, res) => {
883
922
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
884
923
  const runtime = runtimes.get(id);
885
924
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
886
- if (!runtime.memory || typeof runtime.memory.updateClaim !== 'function') {
887
- return res.status(400).json({ error: "Memory organ does not support updating claims" });
888
- }
889
- try {
890
- const claimId = req.body.id || req.body.claimId;
891
- if (!claimId) {
892
- return res.status(400).json({ error: "Missing required claim id" });
893
- }
894
- const updated = await runtime.memory.updateClaim(claimId, req.body.updates || req.body);
895
- res.json({ success: true, claim: updated });
896
- } catch (e: any) {
897
- res.status(500).json({ error: e.message });
898
- }
925
+ return res.status(400).json({ error: "Claim update not supported. Use directive approval/rejection via self." });
899
926
  });
900
927
 
901
928
  app.post('/memory/proposals/approve', requireAuth, async (req, res) => {
902
929
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
903
930
  const runtime = runtimes.get(id);
904
931
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
905
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
932
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
906
933
  try {
907
934
  let name: string | undefined;
908
935
  if (typeof (runtime as any).approveProposal === 'function') {
909
936
  const pRes = await (runtime as any).approveProposal(req.body.id, { companionId: id });
910
937
  name = pRes?.name;
911
- } else {
912
- await runtime.memory.approveClaim(req.body.id);
913
938
  }
914
939
  if (!name && runtime.self && typeof (runtime.self as any).getIdentity === 'function') {
915
940
  try {
@@ -927,12 +952,10 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
927
952
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
928
953
  const runtime = runtimes.get(id);
929
954
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
930
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
955
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
931
956
  try {
932
957
  if (typeof (runtime as any).rejectProposal === 'function') {
933
958
  await (runtime as any).rejectProposal(req.body.id, { companionId: id });
934
- } else {
935
- await runtime.memory.rejectClaim(req.body.id);
936
959
  }
937
960
  res.json({ rejected: true, status: 'rejected' });
938
961
  } catch (e: any) {
@@ -940,19 +963,94 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
940
963
  }
941
964
  });
942
965
 
943
- // MEMORY MUTATIONS - BEHAVIORAL
966
+ // SELF DIRECTIVE MUTATIONS (RFC VX-26-13: Primitive 1)
967
+ app.post('/self/directives/approve', requireAuth, async (req, res) => {
968
+ const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
969
+ const runtime = runtimes.get(id);
970
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
971
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
972
+ try {
973
+ let name: string | undefined;
974
+ if (typeof (runtime as any).approveDirective === 'function') {
975
+ const bRes = await (runtime as any).approveDirective(req.body.id, { companionId: id });
976
+ name = bRes?.name;
977
+ } else if (runtime.self && typeof (runtime.self as any).approveDirective === 'function') {
978
+ await (runtime.self as any).approveDirective(req.body.id, id);
979
+ }
980
+ if (!name && runtime.self && typeof (runtime.self as any).getIdentity === 'function') {
981
+ try {
982
+ const ident = await (runtime.self as any).getIdentity(id);
983
+ name = ident?.name;
984
+ } catch {}
985
+ }
986
+ res.json({ approved: true, status: 'active', name });
987
+ } catch (e: any) {
988
+ res.status(500).json({ error: e.message });
989
+ }
990
+ });
991
+
992
+ app.post('/self/directives/reject', requireAuth, async (req, res) => {
993
+ const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
994
+ const runtime = runtimes.get(id);
995
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
996
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
997
+ try {
998
+ if (typeof (runtime as any).rejectDirective === 'function') {
999
+ await (runtime as any).rejectDirective(req.body.id, { companionId: id });
1000
+ } else if (runtime.self && typeof (runtime.self as any).rejectDirective === 'function') {
1001
+ await (runtime.self as any).rejectDirective(req.body.id, id);
1002
+ }
1003
+ res.json({ rejected: true, status: 'rejected' });
1004
+ } catch (e: any) {
1005
+ res.status(500).json({ error: e.message });
1006
+ }
1007
+ });
1008
+
1009
+ app.post('/self/directives/revoke', requireAuth, async (req, res) => {
1010
+ const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
1011
+ const runtime = runtimes.get(id);
1012
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
1013
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
1014
+ try {
1015
+ if (typeof (runtime as any).revokeDirective === 'function') {
1016
+ await (runtime as any).revokeDirective(req.body.id, { companionId: id });
1017
+ } else if (runtime.self && typeof (runtime.self as any).revokeDirective === 'function') {
1018
+ await (runtime.self as any).revokeDirective(req.body.id, id);
1019
+ }
1020
+ res.json({ revoked: true, status: 'revoked' });
1021
+ } catch (e: any) {
1022
+ res.status(500).json({ error: e.message });
1023
+ }
1024
+ });
1025
+
1026
+ app.post('/self/directives/disable', requireAuth, async (req, res) => {
1027
+ const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
1028
+ const runtime = runtimes.get(id);
1029
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
1030
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
1031
+ try {
1032
+ if (runtime.self && typeof (runtime.self as any).disableDirective === 'function') {
1033
+ await (runtime.self as any).disableDirective(req.body.id, id);
1034
+ }
1035
+ res.json({ disabled: true, status: 'disabled' });
1036
+ } catch (e: any) {
1037
+ res.status(500).json({ error: e.message });
1038
+ }
1039
+ });
1040
+
1041
+ // BEHAVIORAL DIRECTIVE MUTATIONS (RFC VX-26-13: Sovereign Directives via Self, compatibility aliases)
944
1042
  app.post('/memory/behavioral/approve', requireAuth, async (req, res) => {
945
1043
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
946
1044
  const runtime = runtimes.get(id);
947
1045
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
948
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
1046
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
949
1047
  try {
950
1048
  let name: string | undefined;
951
1049
  if (typeof (runtime as any).approveDirective === 'function') {
952
1050
  const bRes = await (runtime as any).approveDirective(req.body.id, { companionId: id });
953
1051
  name = bRes?.name;
954
- } else {
955
- await runtime.memory.approveDirective(req.body.id);
1052
+ } else if (runtime.self && typeof (runtime.self as any).approveDirective === 'function') {
1053
+ await (runtime.self as any).approveDirective(req.body.id, id);
956
1054
  }
957
1055
  if (!name && runtime.self && typeof (runtime.self as any).getIdentity === 'function') {
958
1056
  try {
@@ -970,12 +1068,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
970
1068
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
971
1069
  const runtime = runtimes.get(id);
972
1070
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
973
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
1071
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
974
1072
  try {
975
1073
  if (typeof (runtime as any).rejectDirective === 'function') {
976
1074
  await (runtime as any).rejectDirective(req.body.id, { companionId: id });
977
- } else {
978
- await runtime.memory.rejectDirective(req.body.id);
1075
+ } else if (runtime.self && typeof (runtime.self as any).rejectDirective === 'function') {
1076
+ await (runtime.self as any).rejectDirective(req.body.id, id);
979
1077
  }
980
1078
  res.json({ rejected: true, status: 'rejected' });
981
1079
  } catch (e: any) {
@@ -987,12 +1085,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
987
1085
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
988
1086
  const runtime = runtimes.get(id);
989
1087
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
990
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
1088
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
991
1089
  try {
992
1090
  if (typeof (runtime as any).revokeDirective === 'function') {
993
1091
  await (runtime as any).revokeDirective(req.body.id, { companionId: id });
994
- } else {
995
- await runtime.memory.revokeDirective(req.body.id);
1092
+ } else if (runtime.self && typeof (runtime.self as any).revokeDirective === 'function') {
1093
+ await (runtime.self as any).revokeDirective(req.body.id, id);
996
1094
  }
997
1095
  res.json({ revoked: true, status: 'revoked' });
998
1096
  } catch (e: any) {
@@ -1004,15 +1102,59 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
1004
1102
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
1005
1103
  const runtime = runtimes.get(id);
1006
1104
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
1007
- if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
1105
+ if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
1008
1106
  try {
1009
- await runtime.memory.disableDirective(req.body.id);
1107
+ if (runtime.self && typeof (runtime.self as any).disableDirective === 'function') {
1108
+ await (runtime.self as any).disableDirective(req.body.id, id);
1109
+ }
1010
1110
  res.json({ disabled: true, status: 'disabled' });
1011
1111
  } catch (e: any) {
1012
1112
  res.status(500).json({ error: e.message });
1013
1113
  }
1014
1114
  });
1015
1115
 
1116
+ // ARCHIVE AUDIT ENDPOINTS (RFC VX-26-13: Primitive 5)
1117
+ app.get('/archive/events', requireAuth, async (req, res) => {
1118
+ const id = (req.query.id as string) || Array.from(runtimes.keys())[0];
1119
+ const runtime = runtimes.get(id);
1120
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
1121
+ const limit = parseInt(req.query.limit as string) || 50;
1122
+ try {
1123
+ if (runtime.archive && typeof runtime.archive.getRecentEvents === 'function') {
1124
+ const events = await runtime.archive.getRecentEvents(id, limit);
1125
+ return res.json({ events });
1126
+ }
1127
+ if (runtime.db && typeof (runtime.db as any).getRecentArchiveEvents === 'function') {
1128
+ const events = (runtime.db as any).getRecentArchiveEvents(id, limit);
1129
+ return res.json({ events });
1130
+ }
1131
+ res.json({ events: [] });
1132
+ } catch (e: any) {
1133
+ res.status(500).json({ error: e.message });
1134
+ }
1135
+ });
1136
+
1137
+ app.get('/archive/search', requireAuth, async (req, res) => {
1138
+ const id = (req.query.id as string) || Array.from(runtimes.keys())[0];
1139
+ const runtime = runtimes.get(id);
1140
+ if (!runtime) return res.status(404).json({ error: "Companion not found" });
1141
+ const query = (req.query.q as string) || '';
1142
+ const limit = parseInt(req.query.limit as string) || 50;
1143
+ try {
1144
+ if (runtime.archive && typeof runtime.archive.searchEvents === 'function') {
1145
+ const results = await runtime.archive.searchEvents(id, query, limit);
1146
+ return res.json({ results });
1147
+ }
1148
+ if (runtime.db && typeof (runtime.db as any).searchArchiveEvents === 'function') {
1149
+ const results = (runtime.db as any).searchArchiveEvents(id, query, limit);
1150
+ return res.json({ results });
1151
+ }
1152
+ res.json({ results: [] });
1153
+ } catch (e: any) {
1154
+ res.status(500).json({ error: e.message });
1155
+ }
1156
+ });
1157
+
1016
1158
  // SYSTEM LOGS
1017
1159
  app.get('/system/logs', requireAuth, async (req, res) => {
1018
1160
  const id = (req.query.id as string) || Array.from(runtimes.keys())[0];
@@ -1049,15 +1191,14 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
1049
1191
 
1050
1192
  const isDevMode = process.env.NODE_ENV !== 'production' || process.env.SIDURI_DEV_MODE === 'true';
1051
1193
  if (isDevMode) {
1052
- app.post('/dev/memory/reset', requireAuth, async (req, res) => {
1194
+ app.post(['/dev/memory/reset', '/dev/directives/reset', '/dev/archive/reset'], requireAuth, async (req, res) => {
1053
1195
  const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
1054
1196
  const runtime = runtimes.get(id);
1055
1197
  if (!runtime) return res.status(404).json({ error: "Companion not found" });
1056
- if (!runtime.memory || typeof runtime.memory.resetMemory !== 'function') {
1057
- return res.status(400).json({ error: "Memory organ does not support reset" });
1058
- }
1059
1198
  try {
1060
- await runtime.memory.resetMemory();
1199
+ if (runtime.db && typeof (runtime.db as any).resetArchive === 'function') {
1200
+ (runtime.db as any).resetArchive();
1201
+ }
1061
1202
  res.json({ reset: true });
1062
1203
  } catch (e: any) {
1063
1204
  res.status(500).json({ error: e.message });
package/src/b0-b6.test.ts CHANGED
@@ -66,11 +66,11 @@ describe('T0 B0 & B6 Runtime Proof Suite', () => {
66
66
 
67
67
  // B0: Fresh companion is empty (no prior claims, no user relationship, no knowledge search on greeting)
68
68
  describe('B0 — Fresh companion is empty', () => {
69
- test('initial state has empty memory and empty directives', async () => {
70
- const claims = await runtime.memory?.getClaims();
71
- const directives = await runtime.memory?.getDirectives();
72
- expect(claims).toEqual([]);
69
+ test('initial state has empty directives and empty archive events', async () => {
70
+ const directives = (runtime.self as any)?.getAllDirectives ? await (runtime.self as any).getAllDirectives() : [];
73
71
  expect(directives).toEqual([]);
72
+ const events = runtime.archive ? await runtime.archive.getRecentEvents('companion-a') : [];
73
+ expect(events).toEqual([]);
74
74
  });
75
75
 
76
76
  test('greeting does not query knowledge or inject prior personal knowledge', async () => {
package/src/boot.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { OpenAICompatibleBrain, OpenRouterBrain } from '@sidurijs/brain';
2
- import { SqliteMemoryStore } from '@sidurijs/memory';
2
+ import { SqliteArchiveLedger } from '@sidurijs/archive';
3
3
  import { VoiceAdapter, VoiceConfig } from '@sidurijs/voice';
4
4
  import { UnifiedKnowledgeOrgan, UnifiedKnowledgeConfig } from '@sidurijs/knowledge';
5
5
  import { OpenRouterVisionAdapter, OpenRouterVisionConfig } from '@sidurijs/vision';
@@ -33,6 +33,7 @@ export interface AppBootCompanionConfig {
33
33
  brain?: AppBrainConfig;
34
34
  voice?: VoiceConfig;
35
35
  memory?: { provider?: string; connectionString?: string; maxConnections?: number; dbPath?: string; [key: string]: unknown };
36
+ archive?: { provider?: string; dbPath?: string; [key: string]: unknown };
36
37
  knowledge?: UnifiedKnowledgeConfig;
37
38
  vision?: OpenRouterVisionConfig;
38
39
  behavior?: AppBehaviorConfig;
@@ -130,10 +131,14 @@ export function createMouth(config?: DefaultMouthOrganConfig & { provider?: stri
130
131
  : new DefaultMouthOrgan({ ...config, voice });
131
132
  }
132
133
 
133
- export function createMemory(config?: { provider?: string; connectionString?: string; maxConnections?: number; dbPath?: string }) {
134
+ export function createArchive(config?: { provider?: string; dbPath?: string }) {
134
135
  if (isDisabled(config)) return undefined;
135
136
  const defaultPath = process.env.NODE_ENV === 'test' ? ':memory:' : 'siduri.sqlite';
136
- return new SqliteMemoryStore({ dbPath: config?.dbPath || process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || defaultPath });
137
+ return new SqliteArchiveLedger({ dbPath: config?.dbPath || process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || defaultPath });
138
+ }
139
+
140
+ export function createMemory(config?: { provider?: string; connectionString?: string; maxConnections?: number; dbPath?: string }) {
141
+ return createArchive(config);
137
142
  }
138
143
 
139
144
  export function createSelf(config?: { dbPath?: string; provider?: string }) {
@@ -160,7 +165,7 @@ export async function bootCompanion(
160
165
  const organs = (config as any)?.organs || {};
161
166
 
162
167
  const brain = createBrain(organs.brain || config?.brain);
163
- const memory = createMemory(organs.memory || (config as any)?.memory);
168
+ const archive = createArchive(organs.archive || organs.memory || (config as any)?.archive || (config as any)?.memory);
164
169
  const selfRepo = createSelf(organs.self || (config as any)?.self);
165
170
  const voice = createVoice(organs.voice || config?.voice);
166
171
  const knowledge = createKnowledge(organs.knowledge || config?.knowledge);
@@ -172,13 +177,9 @@ export async function bootCompanion(
172
177
  const mouth = createMouth(organs.mouth || config?.mouth, voice);
173
178
  const observation = options?.observationOrgan;
174
179
 
175
- if (memory && typeof (memory as any).runMigrations === 'function') {
176
- await (memory as any).runMigrations().catch((e: any) => console.warn("Migrations warning:", e.message));
177
- }
178
-
179
180
  const runtime = new SiduriRuntime(id, config as any, {
180
181
  brain,
181
- memory,
182
+ archive,
182
183
  voice,
183
184
  knowledge,
184
185
  vision,
@@ -99,7 +99,7 @@ export function mapRequestContext(
99
99
 
100
100
  const isViewer = !isAuthenticated || isViewerRequested;
101
101
 
102
- const CANONICAL_CAPABILITIES = new Set(['chat', 'memory:approve', 'action:execute', 'system']);
102
+ const CANONICAL_CAPABILITIES = new Set(['chat', 'memory:approve', 'directive:approve', 'self:approve', 'action:execute', 'system']);
103
103
  const DEFAULT_OWNER_CAPABILITIES = ['chat', 'memory:approve', 'action:execute', 'system'];
104
104
 
105
105
  let safeCapabilities: string[];
@@ -214,7 +214,7 @@ export function mapRequestContext(
214
214
  diagnostics.push('anonymous_session_generated');
215
215
  }
216
216
 
217
- const CANONICAL_CAPABILITIES = new Set(['chat', 'memory:approve', 'action:execute', 'system']);
217
+ const CANONICAL_CAPABILITIES = new Set(['chat', 'memory:approve', 'directive:approve', 'self:approve', 'action:execute', 'system']);
218
218
  const DEFAULT_OWNER_CAPABILITIES = ['chat', 'memory:approve', 'action:execute', 'system'];
219
219
 
220
220
  const rawCaps = Array.isArray(input.capabilities)
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ const defaultCompanionConfig = {
24
24
  name: 'Siduri',
25
25
  brain: { provider: 'openrouter', model: 'gpt-4o-mini' },
26
26
  voice: { provider: 'voicevox', speakerId: 1 },
27
+ archive: { provider: 'sqlite' },
27
28
  memory: { provider: 'sqlite' },
28
29
  knowledge: {
29
30
  provider: (process.env.SIDURI_KNOWLEDGE_PROVIDER as any) || 'unified',
@@ -59,6 +60,7 @@ async function loadCompanionConfig() {
59
60
  id: fileConfig.id || defaultCompanionConfig.id,
60
61
  brain: { ...defaultCompanionConfig.brain, ...fileConfig.brain },
61
62
  voice: { ...defaultCompanionConfig.voice, ...fileConfig.voice },
63
+ archive: { ...(defaultCompanionConfig as any).archive, ...fileConfig.archive, ...fileConfig.memory },
62
64
  memory: { ...defaultCompanionConfig.memory, ...fileConfig.memory },
63
65
  knowledge: { ...defaultCompanionConfig.knowledge, ...fileConfig.knowledge },
64
66
  behavior: { ...defaultCompanionConfig.behavior, ...fileConfig.behavior },
@@ -38,16 +38,27 @@ describe('Life Database & UnifiedKnowledgeOrgan API Integration', () => {
38
38
  }),
39
39
  };
40
40
 
41
+ const claimsStore: any[] = [];
41
42
  const memory: any = {
42
43
  initialize: async () => {},
43
- proposeClaim: async (claim: any) => (knowledge.lifeDb as any).db.proposeClaim(claim),
44
- getClaims: async (limit?: number) => (knowledge.lifeDb as any).db.getAllClaims(undefined, limit || 500),
44
+ proposeClaim: async (claim: any) => {
45
+ const c = { id: claim.id || `claim-${Date.now()}`, status: 'pending', ...claim };
46
+ claimsStore.push(c);
47
+ return c;
48
+ },
49
+ getClaims: async (limit?: number) => claimsStore.slice(0, limit || 500),
45
50
  getPendingClaims: async (limit?: number) =>
46
- (knowledge.lifeDb as any).db.getAllClaims(undefined, limit || 500).filter((c: any) => c.status === 'pending'),
47
- approveClaim: async (id: string) => (knowledge.lifeDb as any).db.approveClaim(id),
48
- rejectClaim: async (id: string) => (knowledge.lifeDb as any).db.rejectClaim(id),
51
+ claimsStore.filter((c) => c.status === 'pending').slice(0, limit || 500),
52
+ approveClaim: async (id: string) => {
53
+ const found = claimsStore.find((c) => c.id === id);
54
+ if (found) found.status = 'approved';
55
+ },
56
+ rejectClaim: async (id: string) => {
57
+ const found = claimsStore.find((c) => c.id === id);
58
+ if (found) found.status = 'rejected';
59
+ },
49
60
  searchClaims: async () => [],
50
- getApprovedClaims: async () => [],
61
+ getApprovedClaims: async () => claimsStore.filter((c) => c.status === 'approved'),
51
62
  getDirectives: async () => [],
52
63
  };
53
64
 
@@ -223,7 +234,7 @@ describe('Life Database & UnifiedKnowledgeOrgan API Integration', () => {
223
234
 
224
235
  test('Truth Gate candidate proposal approval commits Life DB mutations', async () => {
225
236
  // Stage a candidate proposal targeting Life DB
226
- const proposal = await (knowledge.lifeDb as any).db.proposeClaim({
237
+ const proposal = await (runtime.container.organs.memory as any).proposeClaim({
227
238
  id: 'prop-task-gate',
228
239
  companionId: 'test-comp',
229
240
  subject: 'task:buy-filter',
@@ -20,7 +20,7 @@ describe('Siduri Runtime Orchestration', () => {
20
20
  return {
21
21
  speech: "Hello",
22
22
  language: "en",
23
- memoryProposals: [
23
+ claimProposals: [
24
24
  { subject: "Test", predicate: "is", value: "working" }
25
25
  ]
26
26
  };
@@ -80,7 +80,7 @@ describe('Siduri Runtime Orchestration', () => {
80
80
  expect(proposedClaims.length).toBe(1);
81
81
  expect(proposedClaims[0].subject).toBe("Test");
82
82
  expect(proposedClaims[0].scope).toBe("user");
83
- expect(response.metadata.memory_proposals[0].proposal_id).toBe("claim-1");
83
+ expect(response.metadata.claim_proposals[0].proposal_id).toBe("claim-1");
84
84
  });
85
85
 
86
86
  test('Primary Invariant: Brain proposes an action, ActionPolicyEngine authorizes, Hands executes', async () => {