Import verified AzerothAI snapshot from GitHub
Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s

Source branch: feature/ai-brain-router
Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
This commit is contained in:
Richard Clark
2026-09-22 13:12:54 -04:00
commit 5f0fc86cf9
175 changed files with 25807 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
import { env } from 'cloudflare:workers';
import { D1Store } from '@/core/store';
import { execute } from '@/core/service';
import { runLiveAutonomyTick } from '@/core/autonomy';
import type { RuntimeConfig } from '@/core/types';
function requestOriginAllowed(request:Request){
const origin=request.headers.get('origin');
if(!origin)return true;
if(origin===new URL(request.url).origin)return true;
const proto=(request.headers.get('x-forwarded-proto')??'').split(',')[0]?.trim().toLowerCase();
const host=(request.headers.get('x-forwarded-host')??request.headers.get('host')??'').split(',')[0]?.trim();
if((proto==='http'||proto==='https')&&host&&origin===`${proto}://${host}`)return true;
return false;
}
export async function POST(request:Request){
if(!requestOriginAllowed(request))return Response.json({error:'Cross-origin action rejected'},{status:403});
try{
const text=await request.text();
if(text.length>16000)return Response.json({error:'Request too large'},{status:413});
const body=JSON.parse(text);
const store=new D1Store();
if(body?.type==='think'&&typeof body.botId==='string'){
const state=await store.load();
const bot=state.bots.find(b=>b.id===body.botId);
if(bot?.autonomy?.managed){
const result=await runLiveAutonomyTick(store,env.DB!,env as unknown as RuntimeConfig,{forceBotId:bot.id});
if(!result.ran)throw new Error(result.reason);
return Response.json(result);
}
}
return Response.json(await execute(store,body,env as unknown as RuntimeConfig));
}
catch(e){const message=e instanceof Error?e.message:'Action failed';return Response.json({error:message},{status:400});}
}
+30
View File
@@ -0,0 +1,30 @@
import { env } from 'cloudflare:workers';
import { z } from 'zod';
import { requireAleOperator,rawAleEnabled } from '@/core/ale-auth';
import { compileAleOperatorRequest } from '@/core/ale';
import { queueAleCommand } from '@/core/ale-store';
import type { RuntimeConfig } from '@/core/types';
const scalar=z.union([z.string(),z.number(),z.boolean(),z.null()]);
const schema=z.discriminatedUnion('kind',[
z.object({kind:z.literal('command'),command:z.string().min(1).max(500)}),
z.object({kind:z.literal('global'),method:z.string().min(1).max(100),args:z.array(scalar).max(32).optional()}),
z.object({kind:z.literal('player'),player:z.string().min(1).max(24),method:z.string().min(1).max(100),args:z.array(scalar).max(32).optional()}),
z.object({kind:z.literal('playerbotAI'),player:z.string().min(1).max(24),method:z.string().min(1).max(100),args:z.array(scalar).max(32).optional()}),
z.object({kind:z.literal('raw'),source:z.string().min(1).max(12000)})
]);
export async function POST(request:Request){
const config=env as unknown as RuntimeConfig;
try{
requireAleOperator(request,config);
const text=await request.text();if(text.length>16000)return Response.json({error:'Request too large'},{status:413});
const body=schema.parse(JSON.parse(text));
const source=compileAleOperatorRequest(body,rawAleEnabled(config));
const queued=await queueAleCommand(env.DB!,source);
return Response.json({ok:true,command:queued},{status:202});
}catch(e){
const message=e instanceof Error?e.message:'ALE operator failure';
return Response.json({error:message},{status:message.startsWith('Unauthorized')?401:400});
}
}
+17
View File
@@ -0,0 +1,17 @@
import { env } from 'cloudflare:workers';
import { requireAleOperator,rawAleEnabled } from '@/core/ale-auth';
import { recentAleCommands,recentAleEvents } from '@/core/ale-store';
import { ALE_SURFACE } from '@/core/ale';
import type { RuntimeConfig } from '@/core/types';
export async function GET(request:Request){
const config=env as unknown as RuntimeConfig;
try{
requireAleOperator(request,config);
const [commands,events]=await Promise.all([recentAleCommands(env.DB!,50),recentAleEvents(env.DB!,100)]);
return Response.json({ok:true,rawEnabled:rawAleEnabled(config),surface:ALE_SURFACE,commands,events},{headers:{'Cache-Control':'no-store'}});
}catch(e){
const message=e instanceof Error?e.message:'ALE status failure';
return Response.json({error:message},{status:message.startsWith('Unauthorized')?401:500});
}
}
+20
View File
@@ -0,0 +1,20 @@
import {env} from 'cloudflare:workers';
import {runLiveAutonomyTick} from '@/core/autonomy';
import {D1Store} from '@/core/store';
import type {RuntimeConfig} from '@/core/types';
function authorized(request:Request,config:RuntimeConfig){
const token=config.AUTONOMY_TOKEN;
if(!token)return false;
return request.headers.get('authorization')===`Bearer ${token}`;
}
export async function POST(request:Request){
const config=env as unknown as RuntimeConfig;
if(!authorized(request,config))return Response.json({error:'Unauthorized autonomy heartbeat'},{status:401});
try{
return Response.json(await runLiveAutonomyTick(new D1Store(),env.DB!,config));
}catch(e){
return Response.json({error:e instanceof Error?e.message:'Autonomy heartbeat failed'},{status:500});
}
}
+59
View File
@@ -0,0 +1,59 @@
import { env } from 'cloudflare:workers';
import { z } from 'zod';
import { requireAleBridge } from '@/core/ale-auth';
import { recordAleEvent } from '@/core/ale-store';
import { queueConversationEvent } from '@/core/conversation-store';
import { D1Store } from '@/core/store';
import { applyAuthoritativeAleEvent,execute } from '@/core/service';
import type { RuntimeConfig } from '@/core/types';
const schema=z.object({
kind:z.string().min(1).max(100),
payload:z.record(z.unknown()).default({})
}).strict();
const STATEFUL_EVENTS=new Set([
'bridge.ready',
'player.login',
'player.logout',
'player.snapshot',
'player.first-login',
'player.zone',
'player.map',
'player.level',
'player.quest.accept',
'player.achievement',
'player.close-call',
'player.killed-by-creature',
'player.repop',
'player.resurrect'
]);
export async function POST(request:Request){
const config=env as unknown as RuntimeConfig;
try{
requireAleBridge(request,config);
const text=await request.text();if(text.length>40000)return Response.json({error:'Event too large'},{status:413});
const body=schema.parse(JSON.parse(text));
await recordAleEvent(env.DB!,body.kind,body.payload);
if((body.kind==='player.chat'||body.kind==='player.whisper')&&body.payload.isBot!==true){
await queueConversationEvent(env.DB!,body.kind,body.payload);
}
const store=new D1Store();
if(STATEFUL_EVENTS.has(body.kind)){
await applyAuthoritativeAleEvent(store,body.kind,body.payload);
}
if(body.kind==='player.zone'&&body.payload.isBot!==true){
const zone=String(body.payload.zoneName??body.payload.zone??'').slice(0,120);
if(zone)await execute(store,{type:'worldTrigger',kind:'zone-change',zone,playerName:String(body.payload.name??'').slice(0,40),detail:'Authoritative ALE human-player zone event'},config);
}
return Response.json({ok:true});
}catch(e){
const message=e instanceof Error?e.message:'ALE event failure';
return Response.json({error:message},{status:message.startsWith('Unauthorized')?401:400});
}
}
+21
View File
@@ -0,0 +1,21 @@
import { env } from 'cloudflare:workers';
import { requireAleBridge } from '@/core/ale-auth';
import { pollAleCommand } from '@/core/ale-store';
import { wrapAleCommand } from '@/core/ale';
import type { RuntimeConfig } from '@/core/types';
async function handlePoll(request:Request){
const config=env as unknown as RuntimeConfig;
try{
requireAleBridge(request,config);
const command=await pollAleCommand(env.DB!);
if(!command)return new Response('return',{status:200,headers:{'Content-Type':'text/plain; charset=utf-8','Cache-Control':'no-store'}});
return new Response(wrapAleCommand(command.id,command.source),{status:200,headers:{'Content-Type':'text/plain; charset=utf-8','Cache-Control':'no-store','X-AzerothAI-Command':command.id}});
}catch(e){
const message=e instanceof Error?e.message:'ALE bridge failure';
return Response.json({error:message},{status:message.startsWith('Unauthorized')?401:500});
}
}
export async function GET(request:Request){ return handlePoll(request); }
export async function POST(request:Request){ return handlePoll(request); }
+21
View File
@@ -0,0 +1,21 @@
import { env } from 'cloudflare:workers';
import { z } from 'zod';
import { requireAleBridge } from '@/core/ale-auth';
import { completeAleCommand } from '@/core/ale-store';
import type { RuntimeConfig } from '@/core/types';
const schema=z.object({id:z.string().uuid(),ok:z.boolean(),result:z.string().max(16000)}).strict();
export async function POST(request:Request){
const config=env as unknown as RuntimeConfig;
try{
requireAleBridge(request,config);
const text=await request.text();if(text.length>20000)return Response.json({error:'Result too large'},{status:413});
const body=schema.parse(JSON.parse(text));
await completeAleCommand(env.DB!,body.id,body.ok,body.result);
return Response.json({ok:true});
}catch(e){
const message=e instanceof Error?e.message:'ALE result failure';
return Response.json({error:message},{status:message.startsWith('Unauthorized')?401:400});
}
}
+20
View File
@@ -0,0 +1,20 @@
import {env} from 'cloudflare:workers';
import {runConversationTick} from '@/core/conversation';
import {D1Store} from '@/core/store';
import type {RuntimeConfig} from '@/core/types';
function authorized(request:Request,config:RuntimeConfig){
const token=config.AUTONOMY_TOKEN;
if(!token)return false;
return request.headers.get('authorization')===`Bearer ${token}`;
}
export async function POST(request:Request){
const config=env as unknown as RuntimeConfig;
if(!authorized(request,config))return Response.json({error:'Unauthorized conversation heartbeat'},{status:401});
try{
return Response.json(await runConversationTick(new D1Store(),env.DB!,config));
}catch(e){
return Response.json({error:e instanceof Error?e.message:'Conversation heartbeat failed'},{status:500});
}
}
+6
View File
@@ -0,0 +1,6 @@
import { D1Store } from '@/core/store';
// Reconnectable SSE batches with Last-Event-ID replay. Closing each batch avoids
// tying a Worker invocation to an unbounded loop; EventSource reconnects in 3s.
export async function GET(request:Request){
try{const s=await new D1Store().load();const raw=request.headers.get('last-event-id')??new URL(request.url).searchParams.get('after')??'0';const after=Math.max(0,Number(raw)||0);let data='retry: 3000\n\n';if(after&&s.events[0]?.seq>after+1)data+='event: gap\ndata: {"reload":true}\n\n';for(const e of s.events.filter(e=>e.seq>after))data+=`id: ${e.seq}\nevent: realm\ndata: ${JSON.stringify(e)}\n\n`;return new Response(data+': heartbeat\n\n',{headers:{'Content-Type':'text/event-stream','Cache-Control':'no-cache'}});}catch{return Response.json({error:'Event stream unavailable'},{status:503});}
}
+28
View File
@@ -0,0 +1,28 @@
import { env } from 'cloudflare:workers';
import { D1Store } from '@/core/store';
import { rawAleEnabled } from '@/core/ale-auth';
import { recentAleEvents } from '@/core/ale-store';
import type { RuntimeConfig } from '@/core/types';
export async function GET(){
const c=env as unknown as RuntimeConfig;
try{
await new D1Store().load();
const aleEvents=await recentAleEvents(env.DB!,20);
const lastAleAt=aleEvents[0]?.at??null;
const aleActiveRecently=!!lastAleAt&&Date.now()-Date.parse(lastAleAt)<90_000;
return Response.json({
ok:true,
aleActiveRecently,
lastAleAt,
bridgeConfigured:!!(c.BRIDGE_URL&&c.BRIDGE_TOKEN),
aleConfigured:!!c.ALE_BRIDGE_TOKEN,
aleOperatorConfigured:!!c.ALE_OPERATOR_TOKEN,
aleRawEnabled:rawAleEnabled(c),
llmConfigured:!!c.LLM_URL,
soapConfigured:!!(c.SOAP_URL&&c.SOAP_USER&&c.SOAP_PASSWORD)
});
}catch{
return Response.json({ok:false},{status:503});
}
}
+2
View File
@@ -0,0 +1,2 @@
import { D1Store } from '@/core/store';
export async function GET(){try{return Response.json(await new D1Store().load(),{headers:{'Cache-Control':'no-store'}});}catch{return Response.json({error:'Realm database unavailable. Please retry.'},{status:503});}}
+70
View File
@@ -0,0 +1,70 @@
import { env } from 'cloudflare:workers';
import { recentAleCommands,recentAleEvents } from '@/core/ale-store';
type SafeWorldEvent={
id:number;
at:string;
kind:string;
name?:string;
zone?:string;
level?:number;
isBot?:boolean;
};
function safeEvent(event:{id:number;at:string;kind:string;payload:unknown}):SafeWorldEvent{
const payload=(event.payload&&typeof event.payload==='object'?event.payload:{}) as Record<string,unknown>;
const out:SafeWorldEvent={id:event.id,at:event.at,kind:event.kind};
if(typeof payload.name==='string')out.name=payload.name.slice(0,40);
const zone=typeof payload.zoneName==='string'?payload.zoneName:typeof payload.zone==='string'?payload.zone:undefined;
if(zone)out.zone=zone.slice(0,120);
if(typeof payload.level==='number'&&Number.isFinite(payload.level))out.level=payload.level;
if(typeof payload.isBot==='boolean')out.isBot=payload.isBot;
return out;
}
export async function GET(){
try{
const [events,commands]=await Promise.all([
recentAleEvents(env.DB!,80),
recentAleCommands(env.DB!,30)
]);
const newest=events[0];
const now=Date.now();
const lastEventAt=newest?.at??null;
const ageMs=lastEventAt?Math.max(0,now-Date.parse(lastEventAt)):null;
const recentWindow=events.filter(e=>now-Date.parse(e.at)<5*60_000);
return Response.json({
ok:true,
ale:{
lastEventAt,
ageMs,
activeRecently:recentWindow.length>0,
bridgeReady:events.some(e=>e.kind==='bridge.ready')
},
playerbots:{
observedBots:new Set(recentWindow.flatMap(e=>{
const p=(e.payload&&typeof e.payload==='object'?e.payload:{}) as Record<string,unknown>;
if(p.isBot!==true)return [];
if(typeof p.characterGuid==='number')return [`guid:${p.characterGuid}`];
if(typeof p.name==='string'&&p.name.trim())return [`name:${p.name.toLowerCase()}`];
return [];
})).size
},
commands:{
pending:commands.filter(c=>c.status==='pending').length,
dispatched:commands.filter(c=>c.status==='dispatched').length,
completed:commands.filter(c=>c.status==='completed').length,
failed:commands.filter(c=>c.status==='failed').length
},
events:events.slice(0,30).map(safeEvent)
},{headers:{'Cache-Control':'no-store'}});
}catch{
return Response.json({
ok:false,
ale:{lastEventAt:null,ageMs:null,activeRecently:false,bridgeReady:false},
playerbots:{observedBots:0},
commands:{pending:0,dispatched:0,completed:0,failed:0},
events:[]
},{status:200,headers:{'Cache-Control':'no-store'}});
}
}