@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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +17 -51
- package/LICENSE +190 -0
- package/dist/app.js +218 -66
- package/dist/b0-b6.test.js +4 -4
- package/dist/boot.d.ts +11 -2
- package/dist/boot.js +9 -8
- package/dist/context-mapper.js +2 -2
- package/dist/index.js +2 -0
- package/dist/knowledge.test.js +20 -7
- package/dist/runtime.test.js +2 -2
- package/package.json +21 -21
- package/src/app.ts +195 -54
- package/src/b0-b6.test.ts +4 -4
- package/src/boot.ts +10 -9
- package/src/context-mapper.ts +2 -2
- package/src/index.ts +2 -0
- package/src/knowledge.test.ts +18 -7
- package/src/runtime.test.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sidurijs/api",
|
|
3
|
-
"version": "1.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
|
-
//
|
|
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
|
-
|
|
542
|
-
|
|
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
|
-
|
|
555
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1105
|
+
if (!runtime.self) return res.status(400).json({ error: "Self organ not configured" });
|
|
1008
1106
|
try {
|
|
1009
|
-
|
|
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
|
-
|
|
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
|
|
70
|
-
const
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
182
|
+
archive,
|
|
182
183
|
voice,
|
|
183
184
|
knowledge,
|
|
184
185
|
vision,
|
package/src/context-mapper.ts
CHANGED
|
@@ -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 },
|
package/src/knowledge.test.ts
CHANGED
|
@@ -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) =>
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
approveClaim: async (id: string) =>
|
|
48
|
-
|
|
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 (
|
|
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',
|
package/src/runtime.test.ts
CHANGED
|
@@ -20,7 +20,7 @@ describe('Siduri Runtime Orchestration', () => {
|
|
|
20
20
|
return {
|
|
21
21
|
speech: "Hello",
|
|
22
22
|
language: "en",
|
|
23
|
-
|
|
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.
|
|
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 () => {
|