Fix conversation failure handling and confirmed delivery memories
This commit is contained in:
+1
-1
@@ -61,7 +61,7 @@ function CreateBot({state,act,busy,close}:any){const [name,setName]=useState('')
|
||||
function RecipeEditor({recipe,act,busy,close}:any){const [r,setR]=useState<Recipe>(recipe??{name:'',description:'',tags:['Curious'],sociability:50,aggression:20,curiosity:70,economy:30,activities:['explore','quest']});const [tags,setTags]=useState(r.tags.join(', '));return <form onSubmit={async e=>{e.preventDefault();try{await act({type:'saveRecipe',recipe:{...r,tags:tags.split(',').map(t=>t.trim()).filter(Boolean)}});toast.success('Recipe saved');close();}catch{}}}><div className="form-grid"><label className="field full">Recipe name<Input required minLength={2} maxLength={80} value={r.name} onChange={e=>setR({...r,name:e.target.value})}/></label><label className="field full">Personality description<textarea maxLength={500} value={r.description} onChange={e=>setR({...r,description:e.target.value})}/></label><label className="field full">Tags, separated by commas<Input value={tags} onChange={e=>setTags(e.target.value)}/></label></div><div className="section-grid" style={{gap:25,marginTop:20}}><div>{(['sociability','aggression','curiosity','economy'] as const).map(key=><div className="range-row" key={key}><div className="range-label"><span style={{textTransform:'capitalize'}}>{key}</span><span>{r[key]}</span></div><Slider aria-label={key} min={0} max={100} step={1} value={[r[key]]} onValueChange={v=>setR({...r,[key]:v[0]})}/></div>)}</div><div><h3 style={{margin:'15px 0'}}>Preferred activities</h3><div className="choices">{ACTIVITIES.map(a=><label className="choice-row" key={a}><Checkbox checked={r.activities.includes(a)} onCheckedChange={v=>setR({...r,activities:v?[...r.activities,a]:r.activities.filter(x=>x!==a)})}/>{a}</label>)}</div></div></div><div className="form-footer">{recipe&&<Button type="button" variant="outline" disabled={busy} onClick={async()=>{try{const {id,version,...copy}=r;void id;void version;await act({type:'saveRecipe',recipe:{...copy,name:`${copy.name} copy`.slice(0,80)}});close();}catch{}}}><Copy size={15}/>Duplicate</Button>}<Button disabled={busy||!r.activities.length}>Save recipe</Button></div></form>}
|
||||
function BotProfile({bot:b,state,act,busy,perform,confirm}:any){const [memory,setMemory]=useState(''),[importance,setImportance]=useState(7),[target,setTarget]=useState(state.bots.find((x:Bot)=>x.id!==b.id)?.id??''),[affinity,setAffinity]=useState(40),[note,setNote]=useState(''),[recipeId,setRecipeId]=useState(b.recipeId);const actions=b.status==='online'?['pause','despawn']:b.status==='paused'?['resume','despawn']:b.status==='offline'?['spawn','retire']:[];return <><div className="detail-header"><Avatar bot={b}/><div><h2>{b.name}</h2><p className="subtitle">Level {b.level} {b.race} {b.role}</p></div><span className={`tag ${b.status}`} style={{marginLeft:'auto'}}>{b.status}</span></div><div className="actions">{actions.map(a=><Button key={a} size="sm" variant={a==='spawn'?'default':'outline'} disabled={busy} onClick={()=>a==='retire'?confirm({title:`Retire ${b.name}?`,description:'The identity and memories remain in your realm, but this bot cannot spawn again in this prototype.',action:{type:'lifecycle',botId:b.id,action:a}}):perform({type:'lifecycle',botId:b.id,action:a})}>{a==='pause'?<Pause size={14}/>:a==='spawn'||a==='resume'?<Play size={14}/>:null}{a.charAt(0).toUpperCase()+a.slice(1)}</Button>)}<Button size="sm" disabled={busy||b.status!=='online'} onClick={()=>perform({type:'think',botId:b.id})}><Brain size={15}/>{busy?'Working…':'Run decision'}</Button></div><Tabs defaultValue="identity" className="mt-6"><TabsList className="w-full"><TabsTrigger value="identity">Identity</TabsTrigger><TabsTrigger value="memory">Memory</TabsTrigger><TabsTrigger value="bonds">Relationships</TabsTrigger></TabsList><TabsContent value="identity"><div className="detail-block"><h3>Current goal</h3><p>{b.goal}</p><p style={{marginTop:12,display:'flex',gap:7,alignItems:'center'}}><MapPin size={15}/>{b.zone}</p></div><div className="detail-block"><h3>{b.personality.name} <small>v{b.recipeVersion}</small></h3><p>{b.personality.description}</p><div className="actions" style={{marginTop:12}}>{b.personality.tags.map((t:string)=><span className="tag" key={t}>{t}</span>)}</div><div className="field" style={{marginTop:20}}><Choice label="Apply recipe" value={recipeId} onChange={setRecipeId} items={state.recipes.map((r:Recipe)=>({value:r.id,label:`${r.name} · v${r.version}`}))}/><Button variant="outline" disabled={busy} onClick={()=>perform({type:'applyRecipe',botId:b.id,recipeId})}>Apply current recipe version</Button></div></div><div className="detail-block"><h3>Latest decision</h3><p>{b.lastDecision?`${b.lastDecision.action}: ${b.lastDecision.reason}`:'No decisions yet. Run a decision while this bot is online.'}</p><small>{b.decisions} decisions acknowledged</small></div></TabsContent><TabsContent value="memory"><form className="detail-block" onSubmit={async e=>{e.preventDefault();try{await act({type:'memory',botId:b.id,text:memory,importance});setMemory('');}catch{}}}><label className="field">Add a memory<textarea required value={memory} maxLength={1000} onChange={e=>setMemory(e.target.value)} placeholder="What should this character remember?"/></label><div className="range-row"><div className="range-label">Importance <span>{importance}/10</span></div><Slider aria-label="Memory importance" min={1} max={10} value={[importance]} onValueChange={v=>setImportance(v[0])}/></div><Button disabled={busy}>Save memory</Button></form>{[...b.memories].reverse().map((m:any)=><div className="memory" key={m.id}><p className="list-muted">{m.text}</p><small>{m.source} · Importance {m.importance}/10 · {new Date(m.at).toLocaleDateString()}</small></div>)}</TabsContent><TabsContent value="bonds"><p className="fineprint">Bonds are directional. One character’s trust does not imply the other’s.</p><form className="detail-block" onSubmit={async e=>{e.preventDefault();try{await act({type:'relationship',botId:b.id,targetId:target,affinity,note});setNote('');}catch{}}}><div className="field"><label>Other character</label><Choice label="Other character" value={target} onChange={setTarget} items={state.bots.filter((x:Bot)=>x.id!==b.id).map((x:Bot)=>({value:x.id,label:x.name}))}/></div><div className="range-row"><div className="range-label">Affinity <span>{affinity>0?'+':''}{affinity}</span></div><Slider aria-label="Affinity" min={-100} max={100} value={[affinity]} onValueChange={v=>setAffinity(v[0])}/></div><label className="field">Why this bond exists<textarea required maxLength={300} value={note} onChange={e=>setNote(e.target.value)}/></label><Button className="mt-4" disabled={busy||!target}>Save relationship</Button></form>{b.relationships.map((r:any)=><div className="memory" key={r.targetId}><h3>{state.bots.find((x:Bot)=>x.id===r.targetId)?.name} <span className="tag">{r.affinity>0?'+':''}{r.affinity}</span></h3><p className="list-muted">{r.note}</p></div>)}</TabsContent></Tabs></>}
|
||||
function WorldMonitor({state,monitor,capabilities,openBot}:any){
|
||||
const living=state.bots.filter((b:Bot)=>b.status!=='dead'&&b.status!=='retired');
|
||||
const living:Bot[]=state.bots.filter((b:Bot)=>b.status!=='dead'&&b.status!=='retired');
|
||||
const active=living.filter((b:Bot)=>b.status==='online');
|
||||
const managed=living.filter((b:Bot)=>b.autonomy?.managed===true);
|
||||
const foreground=living.filter((b:Bot)=>b.simulationBand==='foreground');
|
||||
|
||||
Reference in New Issue
Block a user