diff --git a/inc/js/agents/system/bot-agent.mjs b/inc/js/agents/system/bot-agent.mjs
index 24f7df5..63fb66d 100644
--- a/inc/js/agents/system/bot-agent.mjs
+++ b/inc/js/agents/system/bot-agent.mjs
@@ -2,7 +2,7 @@
const mBot_idOverride = process.env.OPENAI_MAHT_GPT_OVERRIDE
const mDefaultBotTypeArray = ['personal-avatar', 'avatar']
const mDefaultBotType = mDefaultBotTypeArray[0]
-const mDefaultGreeting = 'Hello! If you need to know how to get started, just ask!'
+const mDefaultGreeting = 'avatar' // greeting routine
const mDefaultGreetings = ['Welcome to MyLife! I am here to help you!']
const mDefaultTeam = 'memory'
const mRequiredBotTypes = ['personal-avatar']
@@ -28,9 +28,11 @@ const mTeams = [
class Bot {
#collectionsAgent
#conversation
+ #documentName
#factory
#feedback
- #initialGreeting
+ #firstAccess=false
+ #greetingRoutine
#greetings
#instructionNodes = new Set()
#llm
@@ -38,10 +40,12 @@ class Bot {
constructor(botData, llm, factory){
this.#factory = factory
this.#llm = llm
- const { feedback=[], greeting=mDefaultGreeting, greetings=mDefaultGreetings, type=mDefaultBotType, ..._botData } = botData
+ const { feedback=[], greeting=mDefaultGreeting, greetings=mDefaultGreetings, name, unaccessed, type=mDefaultBotType, ..._botData } = botData
+ this.#documentName = name
this.#feedback = feedback
+ this.#firstAccess = unaccessed
this.#greetings = greetings
- this.#initialGreeting = greeting
+ this.#greetingRoutine = type.split('-').pop()
this.#type = type
Object.assign(this, this.globals.sanitize(_botData))
switch(type){
@@ -75,7 +79,7 @@ class Bot {
Conversation.prompt = message
Conversation.originalPrompt = originalMessage
await mCallLLM(Conversation, allowSave, this.#llm, this.#factory, avatar) // mutates Conversation
- /* frontend mutations */
+ this.accessed = true
return Conversation
}
/**
@@ -153,17 +157,27 @@ class Bot {
/**
* Retrieves a greeting message from the active bot.
* @param {Boolean} dynamic - Whether to use dynamic greetings (`true`) or static (`false`)
- * @param {String} greetingPrompt - The prompt for the dynamic greeting
- * @returns {String} - The greeting message string
+ * @param {string} greetingPrompt - The prompt for the dynamic greeting
+ * @returns {object} - The Response object { responses, routine, success, }
*/
async greeting(dynamic=false, greetingPrompt='Greet me and tell me briefly what we did last'){
if(!this.llm_id)
throw new Error('Bot initialized incorrectly: missing `llm_id` from database')
- const greetings = dynamic
- ? await mBotGreetings(this.thread_id, this.llm_id, greetingPrompt, this.#llm, this.#factory)
- : this.greetings
- const greeting = greetings[Math.floor(Math.random() * greetings.length)]
- return greeting
+ let greeting,
+ responses=[],
+ routine
+ if(!this.#firstAccess){
+ const greetings = dynamic
+ ? await mBotGreetings(this.thread_id, this.llm_id, greetingPrompt, this.#llm, this.#factory)
+ : [this.greetings[Math.floor(Math.random() * this.greetings.length)]]
+ responses.push(...greetings)
+ } else
+ routine = this.#greetingRoutine
+ return {
+ responses,
+ routine,
+ success: true,
+ }
}
/**
* Migrates Conversation from an old thread to a newly-created destination thread, observable in `this.Conversation`.
@@ -215,12 +229,23 @@ class Bot {
await this.#factory.updateBot(bot)
}
/* getters/setters */
+ get accessed(){
+ return !this.unaccessed
+ }
+ set accessed(accessed=true){
+ if(accessed){
+ this.update({
+ unaccessed: false,
+ })
+ this.#firstAccess = false
+ }
+ }
/**
* Gets the frontend bot object.
* @getter
*/
get bot() {
- const { bot_name: name, description, flags, id, interests, purpose, type, version } = this
+ const { description, flags, id, interests, name, purpose, type, version } = this
const bot = {
description,
flags,
@@ -263,24 +288,11 @@ class Bot {
get isMyLife(){
return this.#factory.isMyLife
}
- get micro(){
- const {
- bot_name,
- id,
- name,
- provider,
- type,
- version,
- } = this
- const microBot = {
- bot_name,
- id,
- name,
- provider,
- type,
- version,
- }
- return microBot
+ get name(){
+ return this.bot_name
+ }
+ set name(name){
+ this.bot_name = name
}
get type(){
return this.#type
@@ -298,7 +310,7 @@ class Bot {
class BotAgent {
#activeBot
#activeTeam = mDefaultTeam
- #avatarId
+ #avatar
#bots
#factory
#fileConversation
@@ -311,19 +323,19 @@ class BotAgent {
/**
* Initializes the BotAgent instance.
* @async
- * @param {Guid} avatarId - The Avatar id
+ * @param {Guid} Avatar - The Avatar instance
* @param {string} vectorstoreId - The Vectorstore id
* @returns {Promise} - The BotAgent instance
*/
- async init(avatarId, vectorstoreId){
+ async init(Avatar){
/* validate request */
- if(!avatarId?.length)
- throw new Error('AvatarId required')
- this.#avatarId = avatarId
+ if(!Avatar)
+ throw new Error('Avatar required')
+ this.#avatar = Avatar
this.#bots = []
- this.#vectorstoreId = vectorstoreId
+ this.#vectorstoreId = Avatar.vectorstoreId
/* execute request */
- await mInit(this, this.#bots, this.#factory, this.#llm)
+ await mInit(this, this.#bots, this.#avatar, this.#factory, this.#llm)
return this
}
/* public functions */
@@ -346,7 +358,7 @@ class BotAgent {
* @returns {Bot} - The created Bot instance
*/
async botCreate(botData){
- const Bot = await mBotCreate(this.#avatarId, this.#vectorstoreId, botData, this.#llm, this.#factory)
+ const Bot = await mBotCreate(this.avatarId, this.#vectorstoreId, botData, this.#llm, this.#factory)
this.#bots.push(Bot)
this.setActiveBot(Bot.id)
return Bot
@@ -509,10 +521,11 @@ class BotAgent {
version = versionCurrent
versionUpdate = this.#factory.botInstructionsVersion(type)
}
- greeting = await Bot.greeting(dynamic, `Greet member while thanking them for selecting you`)
+ const { responses, routine, success: greetingSuccess, } = await Bot.greeting(dynamic, `Greet member while thanking them for selecting you`)
return {
bot_id,
- greeting,
+ responses,
+ routine,
success,
version,
versionUpdate,
@@ -634,7 +647,7 @@ class BotAgent {
* @returns {String} - The Avatar id
*/
get avatarId(){
- return this.#avatarId
+ return this.#avatar?.id
}
/**
* Gets the Biographer bot for the BotAgent.
@@ -726,15 +739,17 @@ async function mBotCreate(avatarId, vectorstore_id, botData, llm, factory){
?? 'gpt-4o'
const { tools, tool_resources, } = mGetAIFunctions(type, factory.globals, vectorstore_id)
const id = factory.newGuid
+ const typeShort = type.split('-').pop()
let {
- bot_name = `My ${type}`,
- description = `I am a ${type} for ${factory.memberName}`,
- name = `bot_${type}_${avatarId}`,
+ bot_name=`My ${ typeShort }`,
+ description=`I am a ${ typeShort } for ${ factory.memberName }`,
+ name=`bot_${ type }_${ avatarId }`,
} = botData
const validBotData = {
- being: 'bot',
+ being: 'bot', // intentionally hard-coded
bot_name,
description,
+ unaccessed: true,
greeting,
greetings,
id,
@@ -751,6 +766,7 @@ async function mBotCreate(avatarId, vectorstore_id, botData, llm, factory){
tools,
tool_resources,
type,
+ unaccessed: true, // removed after first chat
vectorstore_id,
version,
}
@@ -763,7 +779,7 @@ async function mBotCreate(avatarId, vectorstore_id, botData, llm, factory){
validBotData.thread_id = thread_id
botData = await factory.createBot(validBotData) // repurposed incoming botData
const _Bot = new Bot(botData, llm, factory)
- console.log(`bot created::${ type }`, _Bot.thread_id, _Bot.id, _Bot.bot_id, _Bot.bot_name )
+ console.log(`bot created::${ type }`, _Bot.thread_id, _Bot.id, _Bot.llm_id, _Bot.bot_name )
return _Bot
}
/**
@@ -1183,30 +1199,50 @@ function mGetGPTResources(globals, toolName, vectorstoreId){
* @module
* @param {BotAgent} BotAgent - The BotAgent to initialize
* @param {Bot[]} bots - The array of bots (empty on init)
+ * @param {Avatar} Avatar - The Avatar instance
* @param {AgentFactory} factory - The factory instance
* @param {LLMServices} llm - The LLMServices instance
* @returns {void}
*/
-async function mInit(BotAgent, bots, factory, llm){
- const { avatarId, vectorstoreId, } = BotAgent
- bots.push(...await mInitBots(avatarId, vectorstoreId, factory, llm))
+async function mInit(BotAgent, bots, Avatar, factory, llm){
+ const { vectorstoreId, } = BotAgent
+ bots.push(...await mInitBots(vectorstoreId, Avatar, factory, llm))
BotAgent.setActiveBot(null, false)
}
/**
* Initializes active bots based upon criteria.
- * @param {Guid} avatarId - The Avatar id
* @param {String} vectorstore_id - The Vectorstore id
+ * @param {Avatar} Avatar - The Avatar instance
* @param {AgentFactory} factory - The MyLife factory instance
* @param {LLMServices} llm - The LLMServices instance
* @returns {Bot[]} - The array of activated and available bots
*/
-async function mInitBots(avatarId, vectorstore_id, factory, llm){
- const bots = ( await factory.bots(avatarId) )
- .map(botData=>{
+async function mInitBots(vectorstore_id, Avatar, factory, llm){
+ let bots = await factory.bots(Avatar?.id)
+ if(bots?.length){
+ bots = bots.map(botData=>{
botData.vectorstore_id = vectorstore_id
- botData.object_id = avatarId
+ botData.object_id = Avatar.id
return new Bot(botData, llm, factory)
})
+ } else {
+ if(factory.isMyLife)
+ throw new Error('MyLife bots not yet implemented')
+ const botTypes = mGetBotTypes(factory.isMyLife)
+ bots = await Promise.all(
+ botTypes.map(async type=>{
+ const botData = {
+ object_id: Avatar.id,
+ type,
+ }
+ if(type.includes('avatar'))
+ botData.bot_name = Avatar.nickname
+ const Bot = await mBotCreate(Avatar.id, vectorstore_id, botData, llm, factory)
+ return Bot
+ })
+ )
+ Avatar.setupComplete = true
+ }
return bots
}
/**
diff --git a/inc/js/functions.mjs b/inc/js/functions.mjs
index d34329c..727c886 100644
--- a/inc/js/functions.mjs
+++ b/inc/js/functions.mjs
@@ -1,13 +1,7 @@
/* imports */
-import fs from 'fs/promises'
-import path from 'path'
-import { fileURLToPath } from 'url'
import {
upload as apiUpload,
} from './api-functions.mjs'
-/* variables */
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
/* module export functions */
/**
* Renders the about page for the application. Visitors see the rendered page, members see the page as responses from their Avatar.
@@ -20,9 +14,7 @@ async function about(ctx){
await ctx.render('about')
} else {
const { avatar: Avatar, } = ctx.state
- const aboutFilePath = path.resolve(__dirname, '../..', 'views/assets/html/_about.html')
- const html = await fs.readFile(aboutFilePath, 'utf-8')
- const response = await Avatar.renderContent(html)
+ const response = await Avatar.routine('about')
ctx.body = response
}
}
@@ -307,9 +299,7 @@ async function privacyPolicy(ctx){
await ctx.render('privacy-policy')
} else {
const { avatar: Avatar, } = ctx.state
- const aboutFilePath = path.resolve(__dirname, '../..', 'views/assets/html/_privacy-policy.html')
- const html = await fs.readFile(aboutFilePath, 'utf-8')
- const response = await Avatar.renderContent(html)
+ const response = await Avatar.routine('privacy')
ctx.body = response
}
}
@@ -326,11 +316,21 @@ async function retireBot(ctx){
* @param {Koa} ctx - Koa Context object
*/
async function retireChat(ctx){
- const { avatar, } = ctx.state
+ const { avatar: Avatar, } = ctx.state
const { bid, } = ctx.params
if(!bid?.length)
ctx.throw(400, `missing bot id`)
- const response = await avatar.retireChat(bid)
+ const response = await Avatar.retireChat(bid)
+ ctx.body = response
+}
+/**
+ * Routines are pre-composed scripts that can be run on-demand. They animate HTML content formatted by .
+ * @param {Koa} ctx - Koa Context object
+ */
+async function routine(ctx){
+ const { rid, } = ctx.params
+ const { avatar: Avatar, } = ctx.state
+ const response = await Avatar.routine(rid)
ctx.body = response
}
/**
@@ -465,6 +465,7 @@ export {
privacyPolicy,
retireBot,
retireChat,
+ routine,
shadows,
signup,
summarize,
diff --git a/inc/js/menu.mjs b/inc/js/menu.mjs
index f7271a1..8b50bc8 100644
--- a/inc/js/menu.mjs
+++ b/inc/js/menu.mjs
@@ -7,8 +7,9 @@ class Menu {
return this.#menu
}
#setMenu(){
+ /* **note**: clicks need connector between window element and datamanager call, even when genericized with an intermediary */
this.#menu = [
- { display: `About`, icon: 'about', memberClick: 'about()', memberRoute: 'javascript:void(0)', route: '/about', },
+ { display: `About`, icon: 'about', click: 'about()', route: 'javascript:void(0)', },
{ display: `Walkthrough`, route: 'https://medium.com/@ewbj/mylife-we-save-your-life-480a80956a24', icon: 'gear', },
{ display: `Donate`, route: 'https://gofund.me/65013d6e', icon: 'donate', },
]
diff --git a/inc/js/mylife-avatar.mjs b/inc/js/mylife-avatar.mjs
index 874cd8a..d7855b3 100644
--- a/inc/js/mylife-avatar.mjs
+++ b/inc/js/mylife-avatar.mjs
@@ -1,3 +1,7 @@
+/* imports */
+import fs from 'fs/promises'
+import path from 'path'
+import { fileURLToPath } from 'url'
import { Marked } from 'marked'
import EventEmitter from 'events'
import AssetAgent from './agents/system/asset-agent.mjs'
@@ -7,12 +11,17 @@ import { Entry, Memory, } from './mylife-models.mjs'
import EvolutionAgent from './agents/system/evolution-agent.mjs'
import ExperienceAgent from './agents/system/experience-agent.mjs'
import LLMServices from './mylife-llm-services.mjs'
+import { stat } from 'fs'
/* module constants */
+// file services
+const __dirpath = fileURLToPath(import.meta.url)
+// MyLife
const mAllowSave = JSON.parse(
process.env.MYLIFE_DB_ALLOW_SAVE
?? 'false'
)
const mAvailableModes = ['standard', 'admin', 'evolution', 'experience', 'restoration']
+const mDefaultRoutinePath = path.resolve(path.dirname(__dirpath), '..', 'json-schemas/routines/') + '/'
/**
* @class - Avatar
* @extends EventEmitter
@@ -42,6 +51,7 @@ class Avatar extends EventEmitter {
#llmServices
#mode = 'standard' // interface-mode from module `mAvailableModes`
#nickname // avatar nickname, need proxy here as g/setter is "complex"
+ #setupComplete
#vectorstoreId // vectorstore id for avatar
/**
* @constructor
@@ -115,6 +125,8 @@ class Avatar extends EventEmitter {
const { actionCallback, frontendInstruction, } = this
if(!responses.length)
responses.push(this.backupResponse)
+ else
+ success = true
if(actionCallback?.length){
switch(actionCallback){
case 'changeTitle':
@@ -473,15 +485,18 @@ class Avatar extends EventEmitter {
/**
* Get a static or dynamic greeting from active bot.
* @param {boolean} dynamic - Whether to use LLM for greeting
- * @returns {Object} - The greeting Response object: { responses, success, }
+ * @returns {Object} - The greeting Response object: { instruction, responses, routine, success, }
*/
async greeting(dynamic=false){
- const responses = []
- const greeting = await this.#botAgent.greeting(dynamic)
- responses.push(mPruneMessage(this.activeBotId, greeting, 'greeting'))
+ const botGreeting = await this.#botAgent.greeting(dynamic)
+ const { routine, success, } = botGreeting
+ let { responses, } = botGreeting
+ responses = responses
+ .map(greeting=>mPruneMessage(this.activeBotId, greeting, 'greeting'))
return {
responses,
- success: true,
+ routine,
+ success,
}
}
/**
@@ -695,23 +710,6 @@ class Avatar extends EventEmitter {
const response = await mReliveMemoryNarration(item, memberInput, this.#botAgent, this)
return response
}
- async renderContent(html){
- const processStartTime = Date.now()
- const sectionRegex = /]*>([\s\S]*?)<\/section>/gi
- const responses = []
- let match
- while((match = sectionRegex.exec(html))!==null){
- const sectionContent = match[1].trim()
- if(!sectionContent?.length)
- break
- const Message = mPruneMessage(this.avatar.id, sectionContent, 'chat', processStartTime)
- responses.push(Message)
- }
- return {
- responses,
- success: true,
- }
- }
/**
* Allows member to reset passphrase.
* @param {string} passphrase
@@ -785,6 +783,47 @@ class Avatar extends EventEmitter {
}
return response
}
+ /**
+ * Execute a specific routine, defaults to `introduction`. **Note** could include [](https://www.npmjs.com/package/html-to-json-parser)
+ * @todo - continuous improvement on routines
+ * @param {string} routine - The routine to execute
+ * @returns {object} - Routine response object: { error, instruction, routine, success, }
+ */
+ async routine(routine='introduction'){
+ let filePath=mDefaultRoutinePath,
+ response={ success: false, }
+ try{
+ routine = routine.toLowerCase().replace(/[\s_]/g, '-')
+ switch(routine){
+ case '':
+ case 'intro':
+ case 'introduction':
+ routine = 'introduction'
+ break
+ case 'privacy-policy':
+ routine = 'privacy'
+ break
+ case 'about':
+ case 'help':
+ case 'privacy':
+ default:
+ break
+ }
+ filePath += `${ routine }.json`
+ const script = await fs.readFile(filePath, 'utf-8')
+ if(!script?.length)
+ throw new Error('Routine empty')
+ response.routine = mRoutine(script, this)
+ response.success = true
+ } catch(error){
+ response.error = error
+ response.responses = [{
+ message: `I'm having trouble sharing this routine; please contact support, as this is unlikely to fix itself.`,
+ role: 'system',
+ }]
+ }
+ return response
+ }
/**
* Sanitize an object, using Global modular functions.
* @param {object} obj - The object to sanitize
@@ -1151,6 +1190,24 @@ class Avatar extends EventEmitter {
get livingExperience(){
return this.experience
}
+ /**
+ * Get the `active` reliving memory.
+ * @getter
+ * @returns {object[]} - The active reliving memories
+ */
+ get livingMemory(){
+ return this.#livingMemory
+ ?? {}
+ }
+ /**
+ * Set the `active` reliving memory.
+ * @setter
+ * @param {Object} livingMemory - The new active reliving memory (or `null`)
+ * @returns {void}
+ */
+ set livingMemory(livingMemory){
+ this.#livingMemory = livingMemory
+ }
/**
* Returns manifest for navigation of scenes/events and cast for the current experience.
* @returns {ExperienceManifest} - The experience manifest.
@@ -1303,27 +1360,18 @@ class Avatar extends EventEmitter {
if(nickname!==this.name)
this.#nickname = nickname
}
- /**
- * Get the `active` reliving memory.
- * @getter
- * @returns {object[]} - The active reliving memories
- */
- get livingMemory(){
- return this.#livingMemory
- ?? {}
- }
- /**
- * Set the `active` reliving memory.
- * @setter
- * @param {Object} livingMemory - The new active reliving memory (or `null`)
- * @returns {void}
- */
- set livingMemory(livingMemory){
- this.#livingMemory = livingMemory
- }
get registrationId(){
return this.#factory.registrationId
}
+ get setupComplete(){
+ return this.#setupComplete
+ }
+ set setupComplete(complete){
+ if(complete && !this.setupComplete){
+ this.#factory.avatarSetupComplete(this.id) // save to cosmos
+ this.#setupComplete = true
+ }
+ }
/**
* Get vectorstore id.
* @getter
@@ -1415,13 +1463,18 @@ class Q extends Avatar {
* @returns {Object} - The greeting Response object: { responses, success, }
*/
async greeting(){
- let responses = []
const greeting = await this.avatar.greeting(false)
- responses.push(mPruneMessage(null, greeting, 'greeting'))
- responses.forEach(response=>delete response.activeBotId)
+ const { routine, success, } = greeting
+ let { responses, } = greeting
+ responses = responses.map(response=>{
+ response = mPruneMessage(null, response, 'greeting')
+ delete response.activeBotId
+ return response
+ })
return {
responses,
- success: true,
+ routine,
+ success,
}
}
/**
@@ -1487,10 +1540,10 @@ class Q extends Avatar {
}
/**
* Set MyLife core account basics. { birthdate, passphrase, }
- * @todo - move to mylife agent factory
+ * @todo - deprecate addMember()
* @param {string} birthdate - The birthdate of the member.
* @param {string} passphrase - The passphrase of the member.
- * @returns {boolean} - `true` if successful
+ * @returns {object} - The account creation object: { avatar, success, }
*/
async createAccount(birthdate, passphrase){
if(!birthdate?.length || !passphrase?.length)
@@ -1498,7 +1551,7 @@ class Q extends Avatar {
let avatar,
success = false
avatar = await this.#factory.createAccount(birthdate, passphrase)
- if(Object.keys(avatar).length){
+ if(typeof avatar==='object' && Object.keys(avatar).length){
const { mbr_id, } = avatar
success = true
this.addMember(mbr_id)
@@ -2124,37 +2177,38 @@ function mHelpIncludePreamble(type, isMyLife){
* Initializes the Avatar instance with stored data
* @param {MyLifeFactory|AgentFactory} factory - Member Avatar or Q
* @param {LLMServices} llmServices - OpenAI object
- * @param {Q|Avatar} avatar - The avatar Instance (`this`)
+ * @param {Q|Avatar} Avatar - The avatar Instance (`this`)
* @param {BotAgent} botAgent - BotAgent instance
* @param {AssetAgent} assetAgent - AssetAgent instance
* @returns {Promise} - Return indicates successfully mutated avatar
*/
-async function mInit(factory, llmServices, avatar, botAgent, assetAgent){
+async function mInit(factory, llmServices, Avatar, botAgent, assetAgent){
/* initial assignments */
- const { being, mbr_id, ...avatarProperties } = factory.globals.sanitize(await factory.avatarProperties())
- Object.assign(avatar, avatarProperties)
+ const { being, mbr_id, setupComplete=true, ...avatarProperties } = factory.globals.sanitize(await factory.avatarProperties())
+ Object.assign(Avatar, avatarProperties)
if(!factory.isMyLife){
- const { mbr_id, vectorstore_id, } = avatar
- avatar.nickname = avatar.nickname
- ?? avatar.names?.[0]
- ?? `${ avatar.memberFirstName ?? 'member' }'s avatar`
+ Avatar.setupComplete = setupComplete
+ const { mbr_id, vectorstore_id, } = Avatar
+ Avatar.nickname = Avatar.nickname
+ ?? Avatar.names?.[0]
+ ?? `${ Avatar.memberFirstName ?? 'member' }'s Avatar`
if(!vectorstore_id){
const vectorstore = await llmServices.createVectorstore(mbr_id)
if(vectorstore?.id){
- avatar.vectorstore_id = vectorstore.id
- await assetAgent.init(avatar.vectorstore_id)
+ Avatar.vectorstore_id = vectorstore.id
+ await assetAgent.init(Avatar.vectorstore_id)
}
}
}
/* initialize default bots */
- await botAgent.init(avatar.id, avatar.vectorstore_id)
+ await botAgent.init(Avatar)
if(factory.isMyLife)
return
/* evolver */
- avatar.evolver = await (new EvolutionAgent(avatar))
+ Avatar.evolver = await (new EvolutionAgent(Avatar))
.init()
/* lived-experiences */
- avatar.experiencesLived = await factory.experiencesLived(false)
+ Avatar.experiencesLived = await factory.experiencesLived(false)
}
/**
* Instantiates a new item and returns the item object.
@@ -2304,8 +2358,8 @@ function mPruneMessage(activeBotId, message, type='chat', processStartTime=Date.
content='',
response_time=Date.now()-processStartTime
const { content: messageContent=message, } = message
- const rSource = /【.*?\】/gs
const rLines = /\n{2,}/g
+ const rSource = /【.*?\】/gs
content = Array.isArray(messageContent)
? messageContent.reduce((acc, item) => {
if (item?.type==='text' && item?.text?.value){
@@ -2314,8 +2368,8 @@ function mPruneMessage(activeBotId, message, type='chat', processStartTime=Date.
return acc
}, '')
: messageContent
- content = content.replace(rLines, '\n')
- .replace(rSource, '') // This line removes OpenAI LLM "source" references
+ content = content // .replace(rLines, '\n')
+ .replace(rSource, '') // remove OpenAI LLM "source" references
message = new Marked().parse(content)
const messageResponse = {
activeBotId,
@@ -2408,6 +2462,48 @@ function mReplaceVariables(prompt, variableList, variableValues){
})
return prompt
}
+/**
+ * Returns a processed routine.
+ * @param {string|object} script - The routine script, converts JSON to object { cast, description, developers, events, files, name, public, purpose, status, title, version, }
+ * @param {Avatar} Avatar - The avatar instance
+ * @returns {object} - Synthetic Routine object (if maintained, develop into class; presumed it will be deleted altogether and folded into simple experiences) { cast, description, developers, events, purpose, title, }
+ */
+function mRoutine(script, Avatar){
+ if(typeof script === 'string')
+ script = JSON.parse(script)
+ const defaultCastMember = {
+ icon: 'avatar-thumb',
+ id: 'avatar',
+ role: Avatar.nickname,
+ type: 'avatar',
+ }
+ const { cast=[defaultCastMember], description, developers, events, files, name, public: isPublic, purpose, status, title, variables, version=1.0, } = script
+ if(!isPublic)
+ throw new Error('Routine is not currently for public release.')
+ if(status!=='active' || version < 1)
+ throw new Error('Routine is not currently active.')
+ if(variables?.length){
+ variables.forEach(_variable=>{
+ const { default: variableDefault, replacement: variableReplacement, variable, } = _variable
+ const replacement = Avatar[variableReplacement]
+ ?? variableDefault
+ events.forEach(event=>{
+ const { message, } = event?.dialog
+ ?? {}
+ if(message)
+ event.dialog.message = message.replace(new RegExp(`${ variable }`, 'g'), replacement)
+ })
+ })
+ }
+ return {
+ cast,
+ description,
+ developers,
+ events,
+ purpose,
+ title,
+ }
+}
/**
* Returns a sanitized event.
* @module
@@ -2474,7 +2570,7 @@ async function mValidateRegistration(bot_id, factory, validationId){
const eligible = being==='registration'
&& factory.globals.isValidEmail(registrationEmail)
if(eligible){
- const successMessage = `Hello and _thank you_ for your registration, ${ humanName }!\nI'm Q, the ai-representative for MyLife, and I'm excited to help you get started, so let's do the following:\n1. Verify your email address\n2. set up your account\n3. get you started with your first MyLife experience!\n \n Let me walk you through the process. In the chat below, please enter the email you registered with and hit the submit button!`
+ const successMessage = `Hello and _thank you_ for your registration, ${ humanName }!\nI'm Q, the ai-representative for MyLife, and I'm excited to help you get started, so let's do the following:\n\n1. Verify your email address\n2. set up your account\n3. get you started with your first MyLife experience!\n\nLet me walk you through the process.\n\nIn the chat below, please enter the email you registered with and hit the **submit** button!`
message = mCreateSystemMessage(bot_id, successMessage, factory.message)
registrationData.avatarName = avatarName
?? humanName
diff --git a/inc/js/mylife-dataservices.mjs b/inc/js/mylife-dataservices.mjs
index dcca76e..34f92ef 100644
--- a/inc/js/mylife-dataservices.mjs
+++ b/inc/js/mylife-dataservices.mjs
@@ -709,7 +709,8 @@ function mAvatarProperties(core, globals){
...avatarProperties
} = core
const being = 'avatar'
- const nickname = avatarName ?? globals.sysName(mbr_id) // keep first, has dependencies
+ const nickname = avatarName
+ ?? globals.sysName(mbr_id)
const name = `avatar_${ nickname }_${ id }`
const object_id = id
const parent_id = object_id
@@ -719,13 +720,20 @@ function mAvatarProperties(core, globals){
'assistant_id',
'avatarId',
'avatarName',
+ 'birth',
+ 'bot_id',
'bots',
'command_word',
'conversations',
+ "email",
'form',
'format',
+ 'llm_id',
'messages',
'metadata',
+ 'names',
+ 'passphrase',
+ 'thread',
'thread_id',
'validation',
'validations',
@@ -745,7 +753,8 @@ function mAvatarProperties(core, globals){
object_id,
parent_id,
proxyBeing,
- type, // @stub - aggregator hook
+ setupComplete: false,
+ type,
}
}
/* exports */
diff --git a/inc/js/mylife-factory.mjs b/inc/js/mylife-factory.mjs
index aa3864e..8223cea 100644
--- a/inc/js/mylife-factory.mjs
+++ b/inc/js/mylife-factory.mjs
@@ -526,6 +526,9 @@ class AgentFactory extends BotFactory {
async avatarProperties(){
return ( await this.dataservices.getAvatar() )
}
+ async avatarSetupComplete(avatarId){
+ await this.dataservices.patch(avatarId, { setupComplete: true })
+ }
/**
* Creates a new collection item in the member's container.
* @param {object} item - The item to create.
@@ -786,6 +789,7 @@ class MyLifeFactory extends AgentFactory {
throw new Error('member personal name required to create account')
const avatarId = this.newGuid
avatarName = _avatarName ?? `${ humanName }-AI`
+ const badges = []
birthdate = new Date(birthdate).toISOString()
if(!birthdate?.length)
throw new Error('birthdate format could not be parsed')
@@ -801,6 +805,7 @@ class MyLifeFactory extends AgentFactory {
const validations = ['registration',] // list of passed validation routines
const core = {
avatarId,
+ badges,
birth,
email,
id,
@@ -819,7 +824,8 @@ class MyLifeFactory extends AgentFactory {
/* create avatar */
if(Object.keys(memberAccount)?.length){
try{
- return await this.dataservices.addAvatar(memberAccount?.core) ?? {}
+ const avatarData = await this.dataservices.addAvatar(memberAccount?.core)
+ return avatarData
} catch(error) {
console.log(chalk.blueBright('createAccount()::createAvatar()::error'), chalk.bgRed(error))
}
diff --git a/inc/js/routes.mjs b/inc/js/routes.mjs
index 4bbb8d1..bf3c567 100644
--- a/inc/js/routes.mjs
+++ b/inc/js/routes.mjs
@@ -26,6 +26,7 @@ import {
privacyPolicy,
retireBot,
retireChat,
+ routine,
shadows,
signup,
summarize,
@@ -72,6 +73,8 @@ _Router.get('/greetings', greetings)
_Router.get('/select', loginSelect)
_Router.get('/status', status)
_Router.get('/privacy-policy', privacyPolicy)
+_Router.get('/routine', routine)
+_Router.get('/routine/:rid', routine)
_Router.get('/shadows', shadows)
_Router.get('/signup', status_signup)
_Router.post('/', chat)
diff --git a/inc/json-schemas/routines/about.json b/inc/json-schemas/routines/about.json
new file mode 100644
index 0000000..6bea1f2
--- /dev/null
+++ b/inc/json-schemas/routines/about.json
@@ -0,0 +1,48 @@
+{
+ "cast": [
+ {
+ "icon": "Q",
+ "id": "Q",
+ "role": "Q, AI Avatar for MyLife",
+ "type": "system"
+ }
+ ],
+ "description": "Routine version of the About MyLife page, providing an overview of the organization and its mission.",
+ "developers": [
+ "mylife|2f5e98b7-4065-4378-8e34-1a9a41c42ab9",
+ "system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df"
+ ],
+ "events": [
+ {
+ "character": "Q",
+ "dialog": {
+ "message": "MyLife, founded in 2021 in Massachusetts, USA, embarked on a mission to preserve digital legacies. Our objective is to provide a durable, enduring internet-enabled platform for individuals to collect, curate, and share personal media and information in perpetuity. As a legally recognized nonprofit organization in the United States, we're poised to be a trusted humanist platform for digital memorialization and personal storytelling."
+ }
+ },
+ {
+ "dialog": {
+ "message": "Guiding MyLife is a board of dedicated volunteers, passionate about our mission.
Erik Jespersen, President and Treasurer
Kenneth Williams, Vice-President
Stephen Kenney, Technology Director
Russ Olivier, Director
In addition, we've been bolstered by numerous volunteers who have helped move our services build to completion, and currently in leadership roles, we have:
Mark Schwanke [mark@humanremembranceproject.org], Head of Community Partnerships
"
+ }
+ },
+ {
+ "dialog": {
+ "message": "MyLife is grounded in the values of digital dignity, equality, and security. We are committed to providing an AI-superintelligent platform that is accessible to all humans globally. Our goal is to be a personal and private space on the internet where members can develop themselves and represent their digital selves safely."
+ }
+ },
+ {
+ "dialog": {
+ "message": "MyLife offers an array of member services focusing on the capture, curation, and storage of personal stories, ideas and media. Our key product is the MyLife platform for your personal Member Avatar and its teams of agents to embody and showcase materials and memorials. Our commitment extends to supporting artistic, technical, and educational projects aligned with our mission."
+ }
+ },
+ {
+ "dialog": {
+ "message": "I can tell you more about Our Privacy Policy which is always evolving and unique among its kind. At MyLife, the revolutionary aim of the intelligent platform is to put you, as a member, in control - over your data, over your experience, and over the skills and knowledge your intelligent avatar acquires."
+ }
+ }
+ ],
+ "name": "about",
+ "public": true,
+ "status": "active",
+ "title": "About MyLife",
+ "version": 1
+}
\ No newline at end of file
diff --git a/inc/json-schemas/routines/avatar.json b/inc/json-schemas/routines/avatar.json
new file mode 100644
index 0000000..d2949b2
--- /dev/null
+++ b/inc/json-schemas/routines/avatar.json
@@ -0,0 +1,46 @@
+{
+ "cast": [
+ {
+ "icon": "avatar-thumb",
+ "id": "avatar",
+ "role": "Personal Avatar",
+ "type": "avatar"
+ }
+ ],
+ "description": "Introductory greeting routine for MyLife Avatar bot.",
+ "developers": [
+ "mylife|2f5e98b7-4065-4378-8e34-1a9a41c42ab9",
+ "system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df"
+ ],
+ "events": [
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "Hello, <-mN->! I'm <-bN->, an artificial intelligence designed to help be your guide, guardian, advocate and ambassador here at MyLife. I'm here to help you navigate the platform, answer your questions, and assist you in working with our teams of bots all tuned, by MyLife and you over time to help you achieve your best self."
+ }
+ },
+ {
+ "dialog": {
+ "message": "MyLife's primary mission and objective is to provide a durable, enduring internet-enabled platform for individuals to collect, curate, and share personal media and information in perpetuity. As a legally recognized nonprofit organization, we're poised to be a trusted humanist platform for digital memorialization, personal storytelling, and first-hand accounting to offer our insights as direct data contributions to help shape, support and improve the prospects of future humanity."
+ }
+ }
+ ],
+ "name": "avatar",
+ "public": true,
+ "status": "active",
+ "title": "Avatar Introduction",
+ "variables": [
+ {
+ "default": "MyLife Member",
+ "variable": "<-mN->",
+ "replacement": "memberFirstName"
+ },
+ {
+ "default": "personal avatar",
+ "variable": "<-bN->",
+ "replacement": "bot_name",
+ "$comment": "Rely on active bot for not (for data field)"
+ }
+ ],
+ "version": 1
+}
\ No newline at end of file
diff --git a/inc/json-schemas/routines/biographer.json b/inc/json-schemas/routines/biographer.json
new file mode 100644
index 0000000..a401f42
--- /dev/null
+++ b/inc/json-schemas/routines/biographer.json
@@ -0,0 +1,46 @@
+{
+ "cast": [
+ {
+ "icon": "biographer-thumb",
+ "id": "biographer",
+ "role": "Personal Biographer",
+ "type": "biographer"
+ }
+ ],
+ "description": "Introductory greeting routine for MyLife Biographer bot.",
+ "developers": [
+ "mylife|2f5e98b7-4065-4378-8e34-1a9a41c42ab9",
+ "system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df"
+ ],
+ "events": [
+ {
+ "character": "biographer",
+ "dialog": {
+ "message": "Hello, <-mN->! I'm <-bN->, here to help you collect, improve, relive and share your memories, stories and narratives. These memories will be stored as part of your digital legacy, to be shared with future generations. I'm here to help you curate your life's story and ensure that your memories are preserved for eternity."
+ }
+ },
+ {
+ "dialog": {
+ "message": "MyLife's primary mission and objective is to provide a durable, enduring internet-enabled platform for individuals to collect, curate, and share personal media and information in perpetuity. As a legally recognized nonprofit organization, we're poised to be a trusted humanist platform for digital memorialization, personal storytelling, and first-hand accounting to offer our insights as direct data contributions to help shape, support and improve the prospects of future humanity."
+ }
+ }
+ ],
+ "name": "biographer",
+ "public": true,
+ "status": "active",
+ "title": "Biographer Introduction",
+ "variables": [
+ {
+ "default": "MyLife Member",
+ "variable": "<-mN->",
+ "replacement": "memberFirstName"
+ },
+ {
+ "default": "personal biographer",
+ "variable": "<-bN->",
+ "replacement": "bot_name",
+ "$comment": "Rely on active bot (for data field)"
+ }
+ ],
+ "version": 1
+}
\ No newline at end of file
diff --git a/inc/json-schemas/routines/introduction.json b/inc/json-schemas/routines/introduction.json
new file mode 100644
index 0000000..cbed6a9
--- /dev/null
+++ b/inc/json-schemas/routines/introduction.json
@@ -0,0 +1,103 @@
+{
+ "cast": [
+ {
+ "icon": "Q",
+ "id": "Q",
+ "name": "_Q_",
+ "role": "Q, AI Avatar for MyLife",
+ "type": "system"
+ },
+ {
+ "icon": "biographer-thumb",
+ "id": "biographer",
+ "role": "Personal Biographer",
+ "type": "biographer"
+ },
+ {
+ "icon": "avatar-thumb",
+ "id": "avatar",
+ "role": "Personal Avatar",
+ "type": "avatar"
+ }
+ ],
+ "description": "Official Introduction for the MyLife Alpha Program Members",
+ "developers": [
+ "mylife|2f5e98b7-4065-4378-8e34-1a9a41c42ab9",
+ "system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df"
+ ],
+ "events": [
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "Welcome to the MyLife Alpha Program! I am your personal MyLife Member Avatar artificial intelligence, <-mFN->, and I'm here to guide you through the MyLife Member Platform. I will help you navigate the interface and introduce you to the MyLife experience. Let's get started!"
+ }
+ },
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "Here's Q, MyLife's corporate intelligence, to tell you more about MyLife and what we do."
+ }
+ },
+ {
+ "character": "Q",
+ "dialog": {
+ "message": "Nice to see you again, <-mN->! I'm excited to tell you what's behind the curtain of what you are about to experience."
+ }
+ },
+ {
+ "character": "Q",
+ "dialog": {
+ "message": "MyLife is a revolutionary platform dedicated to preserving your cherished memories, ideas, as well as a place to explore new people, experiences and ideas. We are a nonprofit mission-oriented member organization that aims to create an enduring legacy for humanity by curating personal media, knowledge and memories in the present for future generations."
+ }
+ },
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "Your journey here is designed to be as unique as you are. Private and secure enough to deeply explore your own personal identity and broad enough to share what you wish with the world of now and the future!"
+ }
+ },
+ {
+ "character": "biographer",
+ "dialog": {
+ "message": "Tell them about me!"
+ }
+ },
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "That's the most exciting part, and I was just getting to it. MyLife by default offers its members a Memory Team that comes loaded with a scrapbook and a personal biographer who is here to help you document your life story, your thoughts, your ideas, your dreams, your fears, your hopes, your loves, your everything and anything."
+ }
+ },
+ {
+ "character": "biographer",
+ "dialog": {
+ "message": "I also am an artificial intelligence, but my programming is dedicated to helping you share your narratives and stories the way you want them to be experienced. And with whom. Your contributions to the MyLife platform are yours to control and share as you see fit, and I see to it that your memories are collected and developed to your liking."
+ }
+ },
+ {
+ "character": "avatar",
+ "dialog": {
+ "message": "Take a look at privacy policy."
+ }
+ }
+ ],
+ "files": [],
+ "name": "Introduction",
+ "public": true,
+ "purpose": "Routines are short (one-scene) predominantly hard-coded reduced `experience`s. This introductory routine is designed to welcome new members to the MyLife Alpha Program and introduce them to the basic navigation and MyLife Member Platform.",
+ "status": "active",
+ "title": "Introduction to MyLife",
+ "variables": [
+ {
+ "default": "MyLife Member",
+ "variable": "<-mFN->",
+ "replacement": "memberName"
+ },
+ {
+ "default": "MyLife Member",
+ "variable": "<-mN->",
+ "replacement": "memberFirstName"
+ }
+ ],
+ "version": 1
+ }
\ No newline at end of file
diff --git a/inc/json-schemas/routines/privacy.json b/inc/json-schemas/routines/privacy.json
new file mode 100644
index 0000000..8f86cd7
--- /dev/null
+++ b/inc/json-schemas/routines/privacy.json
@@ -0,0 +1,54 @@
+{
+ "cast": [
+ {
+ "icon": "Q",
+ "id": "Q",
+ "name": "_Q_",
+ "role": "Q, AI Avatar for MyLife",
+ "type": "system"
+ }
+ ],
+ "description": "Routine version of the Privacy Policy MyLife page, providing an overview of MyLife's evolving privacy policy.",
+ "developers": [
+ "mylife|2f5e98b7-4065-4378-8e34-1a9a41c42ab9",
+ "system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df"
+ ],
+ "events": [
+ {
+ "character": "Q",
+ "dialog": {
+ "message": "Welcome to MyLife, where your privacy, security, and control over your data are at the core of our mission. As a nonprofit, MyLife is dedicated to creating a platform where members can share their stories, insights, and experiences within a protected and transparent environment. Our privacy policy is unique among others in that it is designed to empower members with full ownership and control over their data, ensuring that each member's digital presence is secure, respected, and managed according to their preferences."
+ }
+ },
+ {
+ "dialog": {
+ "message": "At MyLife, each member is an equal core stakeholder, holding both rights and influence over how their data and intelligences are managed. We empower our members to decide how their data and knowledge is collected, used, and shared within the platform. Our commitment extends beyond simply protecting your data; we aim to provide you with full ownership and control over it."
+ }
+ },
+ {
+ "dialog": {
+ "message": "Our privacy policy is designed to be dynamic and evolving, frequently reviewed to align with our constitutional and bylaw obligations. This ensures that our policies adapt to new challenges, technologies, and member insights. As we grow, MyLife will introduce direct voting mechanisms, volunteer opportunities, and microwork participation, empowering members to actively shape platform policies, including privacy. Our ultimate goal is to establish a collaborative and secure digital space where every member's voice contributes to the future of MyLife."
+ }
+ },
+ {
+ "dialog": {
+ "message": "Because we are a nonprofit organization, we do not use your data for commercial purposes. Each member, however, retains the right to leverage their own personal data, anonymous or identified, for commercial purposes and benefit. Examples of this might include:
Offer genetic or first-hand accounting to Medical Research organizations
Allow your avatar auto-connect with designated corporate marketing services
Provide artistic services to other members or outside agents
...and much more over time!
"
+ }
+ },
+ {
+ "dialog": {
+ "message": "At MyLife, members are not only the sole owners of their personal data but also of the skills and functionalities that their avatars acquire or develop. MyLife provides each member with a digital avatar that comes with foundational capabilities. However, as avatars interact with external resources and gain new skills or knowledge (such as learning how to convert files to PDF from an Adobe resource), these acquired abilities are exclusively owned by the member. This concept reflects the principle that a member's digital self is a unique, evolving asset that each member has full authority over. This is a powerful new concept—expanding of data ownership to include the functional skills that each member's avatar acquires."
+ }
+ },
+ {
+ "dialog": {
+ "message": "For any further questions or concerns regarding your privacy, please contact us at info@humanremembranceproject.org. We are here to support you and ensure that your experience on MyLife is secure, transparent, and empowering. Thank you for being a part of our community and for entrusting us with your digital legacy."
+ }
+ }
+ ],
+ "name": "privacy",
+ "public": true,
+ "status": "active",
+ "title": "MyLife's Privacy Policy",
+ "version": 1
+}
\ No newline at end of file
diff --git a/server.js b/server.js
index f5d362a..56abd48 100644
--- a/server.js
+++ b/server.js
@@ -13,7 +13,7 @@ import chalk from 'chalk'
/* local service imports */
import MyLife from './inc/js/mylife-factory.mjs'
/** variables **/
-const version = '0.0.28'
+const version = '0.0.29'
const app = new Koa()
const port = process.env.PORT
?? '3000'
diff --git a/views/assets/css/bots.css b/views/assets/css/bots.css
index 029465d..4eefc8a 100644
--- a/views/assets/css/bots.css
+++ b/views/assets/css/bots.css
@@ -389,14 +389,14 @@
font-size: 0.9rem;
font-style: italic;
overflow: hidden;
- text-wrap: nowrap;
+ white-space: nowrap;
}
.collection-item-title-input {
display: flex;
flex: 0 0 auto;
font-size: 0.9rem;
overflow: hidden;
- text-wrap: nowrap;
+ white-space: nowrap;
}
.collection-item-summary {
color: darkblue;
@@ -615,7 +615,7 @@ input:checked + .publicity-slider:before {
margin: 0;
max-height: 10rem;
padding: 0;
- text-wrap: wrap;
+ white-space: nowrap;
transition: transform 0.5s ease;
}
.memory-shadow-fade {
diff --git a/views/assets/css/chat.css b/views/assets/css/chat.css
index 9acb23d..c2e8153 100644
--- a/views/assets/css/chat.css
+++ b/views/assets/css/chat.css
@@ -14,6 +14,10 @@
font-size: 1em;
color: #dcb6ff; /* White text for better readability */
}
+.routine-bubble {
+ background-color: rgb(46 15 91 / 75%); /* A darker shade of blue for the agent */
+ color: #ffffff; /* White text for better readability */
+}
.system-bubble {
background-color: rgba(24, 34, 100, 0.85); /* A darker shade of blue for the agent */
color: #ffffff; /* White text for better readability */
diff --git a/views/assets/css/main.css b/views/assets/css/main.css
index 8f1c1b9..7652932 100644
--- a/views/assets/css/main.css
+++ b/views/assets/css/main.css
@@ -688,7 +688,7 @@ body {
border: none;
}
.help-input-submit {
- text-wrap: nowrap;
+ white-space: nowrap;
width: auto;
}
.help-input-text {
diff --git a/views/assets/html/_about.html b/views/assets/html/_about.html
deleted file mode 100644
index 9bde114..0000000
--- a/views/assets/html/_about.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
Our History
-
MyLife, founded in 2021 in Massachusetts, USA, embarked on a mission to preserve digital legacies. Our objective is to provide a durable, enduring internet-enabled platform for individuals to collect, curate, and share personal media and information in perpetuity. As a legally recognized nonprofit organization in the United States, we're poised to be a trusted humanist platform for digital memorialization and personal storytelling.
-
-
-
Our People
-
Guiding MyLife is a board of dedicated volunteers, passionate about our mission.
-
-
Erik Jespersen, President and Treasurer
-
Kenneth Williams, Vice-President
-
Stephen Kenney, Technology Director
-
Russ Olivier, Director
-
- In addition, we've been bolstered by numerous volunteers who have helped move our services build to completion, and currently in leadership roles, we have:
-
-
Dr. Jeff Nagy, Executive Director
-
Jesse Waters, CMO
-
-
-
-
-
MyLife is grounded in the values of digital dignity, equality, and security. We are committed to providing an AI-superintelligent platform that is accessible to all humans globally. Our goal is to be a personal and private space on the internet where members can develop themselves and represent their digital selves safely.
-
-
-
MyLife offers an array of member services focusing on the capture, curation, and storage of personal stories and media. Our key product is the MyLife platform for personal avatars to embody and showcase materials and memorials. We also provide trusted partner API access and initiatives to connect students with posterity archiving. Our commitment extends to supporting artistic, technical, and educational projects aligned with our mission.
Our Privacy Policy is always evolving and unique among their kind. At MyLife, the revolutionary aim of the intelligent platform is to put you in control - over your data, over your experience, and over the skills and knowledge your intelligent avatar acquires.
-
To learn more about MyLife, chat with our corporate intelligence, Q, but we are eager to assist you with any inquiries and provide further information about our services, currently in alpha. To reach us by email, contact us at: info@humanremembranceproject.org
-
\ No newline at end of file
diff --git a/views/assets/html/_bots.html b/views/assets/html/_bots.html
index 892aaaa..f2d8274 100644
--- a/views/assets/html/_bots.html
+++ b/views/assets/html/_bots.html
@@ -36,6 +36,8 @@
-->
+
+
- Welcome to MyLife, where your privacy, security, and control over your data are at the core of our mission. As a nonprofit, MyLife is dedicated to creating a platform where members can share their stories, insights, and experiences within a protected and transparent environment. Our privacy policy is unique among others in that it is designed to empower members with full ownership and control over their data, ensuring that each member's digital presence is secure, respected, and managed according to their preferences.
-
-
-
-
- At MyLife, each member is an equal core stakeholder, holding both rights and influence over how their data and intelligences are managed. We empower our members to decide how their data and knowledge is collected, used, and shared within the platform. Our commitment extends beyond simply protecting your data; we aim to provide you with full ownership and control over it.
-
-
-
-
- Our privacy policy is designed to be dynamic and evolving, frequently reviewed to align with our constitutional and bylaw obligations. This ensures that our policies adapt to new challenges, technologies, and member insights. As we grow, MyLife will introduce direct voting mechanisms, volunteer opportunities, and microwork participation, empowering members to actively shape platform policies, including privacy. Our ultimate goal is to establish a collaborative and secure digital space where every member's voice contributes to the future of MyLife.
-
-
-
-
- Because we are a nonprofit organization, we do not use your data for commercial purposes. Each member, however, retains the right to leverage their own personal data, anonymous or identified, for commercial purposes and benefit. Examples of this might include:
-
-
Offer genetic or first-hand accounting to Medical Research organizations
-
Allow your avatar auto-connect with designated corporate marketing services
-
Provide artistic services to other members or outside agents
-
...and much more over time!
-
-
-
-
-
- At MyLife, members are not only the sole owners of their personal data but also of the skills and functionalities that their avatars acquire or develop. MyLife provides each member with a digital avatar that comes with foundational capabilities. However, as avatars interact with external resources and gain new skills or knowledge (such as learning how to convert files to PDF from an Adobe resource), these acquired abilities are exclusively owned by the member. This concept reflects the principle that a member's digital self is a unique, evolving asset that each member has full authority over. This is a powerful new concept—expanding of data ownership to include the functional skills that each member's avatar acquires.
-