chore(minecraft): make linter happy
This commit is contained in:
@@ -3,19 +3,19 @@ import type { PlanStep } from '../../../agents/planning/adapter'
|
||||
export type ActionType = 'physical' | 'chat'
|
||||
|
||||
export interface BaseActionInstruction {
|
||||
type: ActionType
|
||||
description?: string
|
||||
require_feedback?: boolean
|
||||
type: ActionType
|
||||
description?: string
|
||||
require_feedback?: boolean
|
||||
}
|
||||
|
||||
export interface PhysicalActionInstruction extends BaseActionInstruction {
|
||||
type: 'physical'
|
||||
step: PlanStep
|
||||
type: 'physical'
|
||||
step: PlanStep
|
||||
}
|
||||
|
||||
export interface ChatActionInstruction extends BaseActionInstruction {
|
||||
type: 'chat'
|
||||
message: string
|
||||
type: 'chat'
|
||||
message: string
|
||||
}
|
||||
|
||||
export type ActionInstruction = PhysicalActionInstruction | ChatActionInstruction
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import type { ReflexContextState } from '../reflex/context'
|
||||
|
||||
export interface ConsciousContextView {
|
||||
selfSummary: string
|
||||
environmentSummary: string
|
||||
selfSummary: string
|
||||
environmentSummary: string
|
||||
}
|
||||
|
||||
export function buildConsciousContextView(ctx: ReflexContextState): ConsciousContextView {
|
||||
const selfSummary = `Position ${String(ctx.self.location)} Health ${ctx.self.health}/20 Food ${ctx.self.food}/20 Oxygen ${ctx.self.oxygen}/20 Holding ${ctx.self.holding ?? 'nothing'}`
|
||||
const selfSummary = `Position ${String(ctx.self.location)} Health ${ctx.self.health}/20 Food ${ctx.self.food}/20 Oxygen ${ctx.self.oxygen}/20 Holding ${ctx.self.holding ?? 'nothing'}`
|
||||
|
||||
const players = ctx.environment.nearbyPlayers.map(p => p.name).join(',')
|
||||
const entities = ctx.environment.nearbyEntities.map(e => e.name).join(',')
|
||||
const environmentSummary = `${ctx.environment.time} ${ctx.environment.weather} Nearby players [${players}] Nearby entities [${entities}] Light ${ctx.environment.lightLevel}`
|
||||
const players = ctx.environment.nearbyPlayers.map(p => p.name).join(',')
|
||||
const entities = ctx.environment.nearbyEntities.map(e => e.name).join(',')
|
||||
const environmentSummary = `${ctx.environment.time} ${ctx.environment.weather} Nearby players [${players}] Nearby entities [${entities}] Light ${ctx.environment.lightLevel}`
|
||||
|
||||
return {
|
||||
selfSummary,
|
||||
environmentSummary,
|
||||
}
|
||||
return {
|
||||
selfSummary,
|
||||
environmentSummary,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,77 +3,77 @@ import { describe, expect, it } from 'vitest'
|
||||
import { AttentionDetector } from './attention-detector'
|
||||
|
||||
function makeLogger() {
|
||||
const logger: any = {
|
||||
withFields: () => logger,
|
||||
withError: () => logger,
|
||||
log: () => { },
|
||||
warn: () => { },
|
||||
error: () => { },
|
||||
}
|
||||
return logger
|
||||
const logger: any = {
|
||||
withFields: () => logger,
|
||||
withError: () => logger,
|
||||
log: () => { },
|
||||
warn: () => { },
|
||||
error: () => { },
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
describe('AttentionDetector', () => {
|
||||
it('emits punch attention after 3 arm_swing events', () => {
|
||||
const emitted: any[] = []
|
||||
const detector = new AttentionDetector({
|
||||
logger: makeLogger(),
|
||||
onAttention: payload => emitted.push(payload),
|
||||
})
|
||||
|
||||
const base: any = {
|
||||
modality: 'sighted',
|
||||
kind: 'arm_swing',
|
||||
entityType: 'player',
|
||||
entityId: 'p1',
|
||||
displayName: 'alice',
|
||||
distance: 10,
|
||||
hasLineOfSight: true,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
}
|
||||
|
||||
detector.ingest({ ...base })
|
||||
detector.ingest({ ...base })
|
||||
detector.ingest({ ...base })
|
||||
|
||||
expect(emitted.length).toBe(1)
|
||||
expect(emitted[0]).toMatchObject({
|
||||
kind: 'player',
|
||||
playerAction: 'punch',
|
||||
playerName: 'alice',
|
||||
})
|
||||
describe('attentionDetector', () => {
|
||||
it('emits punch attention after 3 arm_swing events', () => {
|
||||
const emitted: any[] = []
|
||||
const detector = new AttentionDetector({
|
||||
logger: makeLogger(),
|
||||
onAttention: payload => emitted.push(payload),
|
||||
})
|
||||
|
||||
it('emits sound attention (gated per soundId)', () => {
|
||||
const emitted: any[] = []
|
||||
const detector = new AttentionDetector({
|
||||
logger: makeLogger(),
|
||||
onAttention: payload => emitted.push(payload),
|
||||
})
|
||||
const base: any = {
|
||||
modality: 'sighted',
|
||||
kind: 'arm_swing',
|
||||
entityType: 'player',
|
||||
entityId: 'p1',
|
||||
displayName: 'alice',
|
||||
distance: 10,
|
||||
hasLineOfSight: true,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
}
|
||||
|
||||
detector.ingest({
|
||||
modality: 'heard',
|
||||
kind: 'sound',
|
||||
soundId: 's1',
|
||||
distance: 5,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
} as any)
|
||||
detector.ingest({ ...base })
|
||||
detector.ingest({ ...base })
|
||||
detector.ingest({ ...base })
|
||||
|
||||
detector.ingest({
|
||||
modality: 'heard',
|
||||
kind: 'sound',
|
||||
soundId: 's1',
|
||||
distance: 5,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
} as any)
|
||||
|
||||
expect(emitted.length).toBe(1)
|
||||
expect(emitted[0]).toMatchObject({
|
||||
kind: 'player',
|
||||
playerAction: 'sound',
|
||||
})
|
||||
expect(emitted.length).toBe(1)
|
||||
expect(emitted[0]).toMatchObject({
|
||||
kind: 'player',
|
||||
playerAction: 'punch',
|
||||
playerName: 'alice',
|
||||
})
|
||||
})
|
||||
|
||||
it('emits sound attention (gated per soundId)', () => {
|
||||
const emitted: any[] = []
|
||||
const detector = new AttentionDetector({
|
||||
logger: makeLogger(),
|
||||
onAttention: payload => emitted.push(payload),
|
||||
})
|
||||
|
||||
detector.ingest({
|
||||
modality: 'heard',
|
||||
kind: 'sound',
|
||||
soundId: 's1',
|
||||
distance: 5,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
} as any)
|
||||
|
||||
detector.ingest({
|
||||
modality: 'heard',
|
||||
kind: 'sound',
|
||||
soundId: 's1',
|
||||
distance: 5,
|
||||
timestamp: Date.now(),
|
||||
source: 'minecraft',
|
||||
} as any)
|
||||
|
||||
expect(emitted.length).toBe(1)
|
||||
expect(emitted[0]).toMatchObject({
|
||||
kind: 'player',
|
||||
playerAction: 'sound',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@ export class MineflayerPerceptionCollector {
|
||||
event: string
|
||||
handler: (...args: any[]) => void
|
||||
}> = []
|
||||
|
||||
private lastSelfHealth: number | null = null
|
||||
|
||||
private lastStatsAt = 0
|
||||
@@ -231,8 +232,9 @@ export class MineflayerPerceptionCollector {
|
||||
|
||||
for (const { event, handler } of this.listeners) {
|
||||
try {
|
||||
(this.bot.bot as any).off?.(event, handler)
|
||||
(this.bot.bot as any).removeListener?.(event, handler)
|
||||
const b = this.bot.bot as any
|
||||
b.off?.(event, handler)
|
||||
b.removeListener?.(event, handler)
|
||||
}
|
||||
catch (err) {
|
||||
this.deps.logger.withError(err as Error).error('MineflayerPerceptionCollector: failed to remove listener')
|
||||
@@ -293,5 +295,4 @@ export class MineflayerPerceptionCollector {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@ import type { Logg } from '@guiiai/logg'
|
||||
|
||||
import type { MineflayerWithAgents } from '../types'
|
||||
import type { EventManager } from './event-manager'
|
||||
|
||||
import type { PerceptionFrame } from './frame'
|
||||
|
||||
import type { PerceptionSignal } from './types/signals'
|
||||
import type { PerceptionStage } from './types/stage'
|
||||
|
||||
import { AttentionDetector } from './attention-detector'
|
||||
import { createPerceptionFrameFromRawEvent } from './frame'
|
||||
import { MineflayerPerceptionCollector } from './mineflayer-perception-collector'
|
||||
import { RawEventBuffer } from './raw-event-buffer'
|
||||
import type { PerceptionStage } from './types/stage'
|
||||
|
||||
export class PerceptionPipeline {
|
||||
private readonly buffer = new RawEventBuffer()
|
||||
|
||||
@@ -3,70 +3,70 @@ import type { Vec3 } from 'vec3'
|
||||
export type PerceptionModality = 'sighted' | 'heard' | 'felt'
|
||||
|
||||
export interface RawPerceptionEventBase {
|
||||
modality: PerceptionModality
|
||||
timestamp: number
|
||||
source: 'minecraft'
|
||||
pos?: Vec3
|
||||
modality: PerceptionModality
|
||||
timestamp: number
|
||||
source: 'minecraft'
|
||||
pos?: Vec3
|
||||
}
|
||||
|
||||
export interface SightedEntityMovedEvent extends RawPerceptionEventBase {
|
||||
modality: 'sighted'
|
||||
kind: 'entity_moved'
|
||||
entityType: 'player' | 'mob'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
modality: 'sighted'
|
||||
kind: 'entity_moved'
|
||||
entityType: 'player' | 'mob'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
}
|
||||
|
||||
export interface SightedArmSwingEvent extends RawPerceptionEventBase {
|
||||
modality: 'sighted'
|
||||
kind: 'arm_swing'
|
||||
entityType: 'player'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
modality: 'sighted'
|
||||
kind: 'arm_swing'
|
||||
entityType: 'player'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
}
|
||||
|
||||
export interface SightedSneakToggleEvent extends RawPerceptionEventBase {
|
||||
modality: 'sighted'
|
||||
kind: 'sneak_toggle'
|
||||
entityType: 'player'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
sneaking: boolean
|
||||
modality: 'sighted'
|
||||
kind: 'sneak_toggle'
|
||||
entityType: 'player'
|
||||
entityId: string
|
||||
displayName?: string
|
||||
distance: number
|
||||
hasLineOfSight: boolean
|
||||
sneaking: boolean
|
||||
}
|
||||
|
||||
export type SightedEvent = SightedEntityMovedEvent | SightedArmSwingEvent | SightedSneakToggleEvent
|
||||
|
||||
export interface HeardSoundEvent extends RawPerceptionEventBase {
|
||||
modality: 'heard'
|
||||
kind: 'sound'
|
||||
soundId: string
|
||||
distance: number
|
||||
inferredEntityType?: 'player' | 'mob'
|
||||
inferredEntityId?: string
|
||||
modality: 'heard'
|
||||
kind: 'sound'
|
||||
soundId: string
|
||||
distance: number
|
||||
inferredEntityType?: 'player' | 'mob'
|
||||
inferredEntityId?: string
|
||||
}
|
||||
|
||||
export type HeardEvent = HeardSoundEvent
|
||||
|
||||
export interface FeltDamageTakenEvent extends RawPerceptionEventBase {
|
||||
modality: 'felt'
|
||||
kind: 'damage_taken'
|
||||
amount?: number
|
||||
attackerEntityType?: 'player' | 'mob'
|
||||
attackerEntityId?: string
|
||||
distance?: number
|
||||
modality: 'felt'
|
||||
kind: 'damage_taken'
|
||||
amount?: number
|
||||
attackerEntityType?: 'player' | 'mob'
|
||||
attackerEntityId?: string
|
||||
distance?: number
|
||||
}
|
||||
|
||||
export interface FeltItemCollectedEvent extends RawPerceptionEventBase {
|
||||
modality: 'felt'
|
||||
kind: 'item_collected'
|
||||
itemName: string
|
||||
count?: number
|
||||
modality: 'felt'
|
||||
kind: 'item_collected'
|
||||
itemName: string
|
||||
count?: number
|
||||
}
|
||||
|
||||
export type FeltEvent = FeltDamageTakenEvent | FeltItemCollectedEvent
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
|
||||
export type PerceptionSignalType =
|
||||
| 'chat_message'
|
||||
| 'entity_attention' // e.g. someone waving, teabagging
|
||||
| 'environmental_anomaly' // e.g. sudden loud sound
|
||||
| 'saliency_high' // generic high saliency event
|
||||
export type PerceptionSignalType
|
||||
= | 'chat_message'
|
||||
| 'entity_attention' // e.g. someone waving, teabagging
|
||||
| 'environmental_anomaly' // e.g. sudden loud sound
|
||||
| 'saliency_high' // generic high saliency event
|
||||
|
||||
export interface PerceptionSignal {
|
||||
type: PerceptionSignalType
|
||||
description: string // Textual summary for LLM
|
||||
type: PerceptionSignalType
|
||||
description: string // Textual summary for LLM
|
||||
|
||||
// Contextual Data
|
||||
sourceId?: string // Who/What caused this
|
||||
confidence?: number // 0-1
|
||||
timestamp: number
|
||||
// Contextual Data
|
||||
sourceId?: string // Who/What caused this
|
||||
confidence?: number // 0-1
|
||||
timestamp: number
|
||||
|
||||
// Structured Data (for logic)
|
||||
metadata: Record<string, any>
|
||||
// Structured Data (for logic)
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PerceptionFrame } from '../frame'
|
||||
|
||||
export interface PerceptionStage {
|
||||
name: string
|
||||
tick?: (deltaMs: number) => void
|
||||
handle: (frame: PerceptionFrame) => PerceptionFrame | null
|
||||
name: string
|
||||
tick?: (deltaMs: number) => void
|
||||
handle: (frame: PerceptionFrame) => PerceptionFrame | null
|
||||
}
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
import type { Vec3 } from 'vec3'
|
||||
|
||||
export interface ReflexSelfState {
|
||||
location: Vec3 | null
|
||||
holding: string | null
|
||||
health: number
|
||||
food: number
|
||||
oxygen: number
|
||||
location: Vec3 | null
|
||||
holding: string | null
|
||||
health: number
|
||||
food: number
|
||||
oxygen: number
|
||||
}
|
||||
|
||||
export interface ReflexEnvironmentState {
|
||||
time: 'day' | 'night' | 'sunset' | 'sunrise'
|
||||
weather: 'clear' | 'rain' | 'thunder'
|
||||
nearbyPlayers: Array<{ name: string, distance?: number }>
|
||||
nearbyEntities: Array<{ name: string, distance?: number, kind?: string }>
|
||||
lightLevel: number
|
||||
time: 'day' | 'night' | 'sunset' | 'sunrise'
|
||||
weather: 'clear' | 'rain' | 'thunder'
|
||||
nearbyPlayers: Array<{ name: string, distance?: number }>
|
||||
nearbyEntities: Array<{ name: string, distance?: number, kind?: string }>
|
||||
lightLevel: number
|
||||
}
|
||||
|
||||
export interface ReflexSocialState {
|
||||
lastSpeaker: string | null
|
||||
lastMessage: string | null
|
||||
lastMessageAt: number | null
|
||||
lastGreetingAtBySpeaker: Record<string, number>
|
||||
lastSpeaker: string | null
|
||||
lastMessage: string | null
|
||||
lastMessageAt: number | null
|
||||
lastGreetingAtBySpeaker: Record<string, number>
|
||||
}
|
||||
|
||||
export interface ReflexThreatState {
|
||||
threatScore: number
|
||||
lastThreatAt: number | null
|
||||
lastThreatSource: string | null
|
||||
threatScore: number
|
||||
lastThreatAt: number | null
|
||||
lastThreatSource: string | null
|
||||
}
|
||||
|
||||
export interface ReflexContextState {
|
||||
now: number
|
||||
self: ReflexSelfState
|
||||
environment: ReflexEnvironmentState
|
||||
social: ReflexSocialState
|
||||
threat: ReflexThreatState
|
||||
now: number
|
||||
self: ReflexSelfState
|
||||
environment: ReflexEnvironmentState
|
||||
social: ReflexSocialState
|
||||
threat: ReflexThreatState
|
||||
}
|
||||
|
||||
export class ReflexContext {
|
||||
private state: ReflexContextState
|
||||
private state: ReflexContextState
|
||||
|
||||
constructor() {
|
||||
this.state = {
|
||||
now: Date.now(),
|
||||
self: {
|
||||
location: null,
|
||||
holding: null,
|
||||
health: 20,
|
||||
food: 20,
|
||||
oxygen: 20,
|
||||
},
|
||||
environment: {
|
||||
time: 'day',
|
||||
weather: 'clear',
|
||||
nearbyPlayers: [],
|
||||
nearbyEntities: [],
|
||||
lightLevel: 15,
|
||||
},
|
||||
social: {
|
||||
lastSpeaker: null,
|
||||
lastMessage: null,
|
||||
lastMessageAt: null,
|
||||
lastGreetingAtBySpeaker: {},
|
||||
},
|
||||
threat: {
|
||||
threatScore: 0,
|
||||
lastThreatAt: null,
|
||||
lastThreatSource: null,
|
||||
},
|
||||
}
|
||||
constructor() {
|
||||
this.state = {
|
||||
now: Date.now(),
|
||||
self: {
|
||||
location: null,
|
||||
holding: null,
|
||||
health: 20,
|
||||
food: 20,
|
||||
oxygen: 20,
|
||||
},
|
||||
environment: {
|
||||
time: 'day',
|
||||
weather: 'clear',
|
||||
nearbyPlayers: [],
|
||||
nearbyEntities: [],
|
||||
lightLevel: 15,
|
||||
},
|
||||
social: {
|
||||
lastSpeaker: null,
|
||||
lastMessage: null,
|
||||
lastMessageAt: null,
|
||||
lastGreetingAtBySpeaker: {},
|
||||
},
|
||||
threat: {
|
||||
threatScore: 0,
|
||||
lastThreatAt: null,
|
||||
lastThreatSource: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
public getSnapshot(): ReflexContextState {
|
||||
return {
|
||||
...this.state,
|
||||
self: { ...this.state.self },
|
||||
environment: {
|
||||
...this.state.environment,
|
||||
nearbyPlayers: this.state.environment.nearbyPlayers.map(p => ({ ...p })),
|
||||
nearbyEntities: this.state.environment.nearbyEntities.map(e => ({ ...e })),
|
||||
},
|
||||
social: {
|
||||
...this.state.social,
|
||||
lastGreetingAtBySpeaker: { ...this.state.social.lastGreetingAtBySpeaker },
|
||||
},
|
||||
threat: { ...this.state.threat },
|
||||
}
|
||||
public getSnapshot(): ReflexContextState {
|
||||
return {
|
||||
...this.state,
|
||||
self: { ...this.state.self },
|
||||
environment: {
|
||||
...this.state.environment,
|
||||
nearbyPlayers: this.state.environment.nearbyPlayers.map(p => ({ ...p })),
|
||||
nearbyEntities: this.state.environment.nearbyEntities.map(e => ({ ...e })),
|
||||
},
|
||||
social: {
|
||||
...this.state.social,
|
||||
lastGreetingAtBySpeaker: { ...this.state.social.lastGreetingAtBySpeaker },
|
||||
},
|
||||
threat: { ...this.state.threat },
|
||||
}
|
||||
}
|
||||
|
||||
public updateNow(now: number): void {
|
||||
this.state.now = now
|
||||
}
|
||||
public updateNow(now: number): void {
|
||||
this.state.now = now
|
||||
}
|
||||
|
||||
public updateSelf(patch: Partial<ReflexSelfState>): void {
|
||||
this.state.self = { ...this.state.self, ...patch }
|
||||
}
|
||||
public updateSelf(patch: Partial<ReflexSelfState>): void {
|
||||
this.state.self = { ...this.state.self, ...patch }
|
||||
}
|
||||
|
||||
public updateEnvironment(patch: Partial<ReflexEnvironmentState>): void {
|
||||
this.state.environment = { ...this.state.environment, ...patch }
|
||||
}
|
||||
public updateEnvironment(patch: Partial<ReflexEnvironmentState>): void {
|
||||
this.state.environment = { ...this.state.environment, ...patch }
|
||||
}
|
||||
|
||||
public updateSocial(patch: Partial<ReflexSocialState>): void {
|
||||
this.state.social = { ...this.state.social, ...patch }
|
||||
}
|
||||
public updateSocial(patch: Partial<ReflexSocialState>): void {
|
||||
this.state.social = { ...this.state.social, ...patch }
|
||||
}
|
||||
|
||||
public updateThreat(patch: Partial<ReflexThreatState>): void {
|
||||
this.state.threat = { ...this.state.threat, ...patch }
|
||||
}
|
||||
public updateThreat(patch: Partial<ReflexThreatState>): void {
|
||||
this.state.threat = { ...this.state.threat, ...patch }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import type { ReflexContextState } from './context'
|
||||
export type ReflexModeId = 'idle' | 'social' | 'alert'
|
||||
|
||||
export function selectMode(ctx: ReflexContextState): ReflexModeId {
|
||||
if (ctx.threat.threatScore > 0)
|
||||
return 'alert'
|
||||
if (ctx.threat.threatScore > 0)
|
||||
return 'alert'
|
||||
|
||||
if (ctx.social.lastMessageAt && ctx.now - ctx.social.lastMessageAt < 15_000)
|
||||
return 'social'
|
||||
if (ctx.social.lastMessageAt && ctx.now - ctx.social.lastMessageAt < 15_000)
|
||||
return 'social'
|
||||
|
||||
return 'idle'
|
||||
return 'idle'
|
||||
}
|
||||
|
||||
@@ -1,61 +1,60 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { EventManager } from '../perception/event-manager'
|
||||
|
||||
import { ReflexManager } from './reflex-manager'
|
||||
|
||||
function makeLogger() {
|
||||
return {
|
||||
log: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
withFields: vi.fn(() => makeLogger()),
|
||||
withError: vi.fn(() => makeLogger()),
|
||||
} as any
|
||||
return {
|
||||
log: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
withFields: vi.fn(() => makeLogger()),
|
||||
withError: vi.fn(() => makeLogger()),
|
||||
} as any
|
||||
}
|
||||
|
||||
function makeBot() {
|
||||
const bot = {
|
||||
bot: {
|
||||
username: 'bot',
|
||||
chat: vi.fn(),
|
||||
entity: {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
},
|
||||
health: 20,
|
||||
food: 20,
|
||||
oxygenLevel: 20,
|
||||
heldItem: null,
|
||||
time: { isDay: true },
|
||||
isRaining: false,
|
||||
players: {},
|
||||
},
|
||||
}
|
||||
const bot = {
|
||||
bot: {
|
||||
username: 'bot',
|
||||
chat: vi.fn(),
|
||||
entity: {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
},
|
||||
health: 20,
|
||||
food: 20,
|
||||
oxygenLevel: 20,
|
||||
heldItem: null,
|
||||
time: { isDay: true },
|
||||
isRaining: false,
|
||||
players: {},
|
||||
},
|
||||
}
|
||||
|
||||
return bot as any
|
||||
return bot as any
|
||||
}
|
||||
|
||||
describe('ReflexManager', () => {
|
||||
it('handles greeting via reflex and marks stimulus event handled', () => {
|
||||
const eventManager = new EventManager()
|
||||
const logger = makeLogger()
|
||||
const reflex = new ReflexManager({ eventManager, logger })
|
||||
describe('reflexManager', () => {
|
||||
it('handles greeting via reflex and marks stimulus event handled', () => {
|
||||
const eventManager = new EventManager()
|
||||
const logger = makeLogger()
|
||||
const reflex = new ReflexManager({ eventManager, logger })
|
||||
|
||||
const bot = makeBot()
|
||||
reflex.init(bot)
|
||||
const bot = makeBot()
|
||||
reflex.init(bot)
|
||||
|
||||
const stimulus: any = {
|
||||
type: 'stimulus',
|
||||
payload: { content: 'hello' },
|
||||
source: { type: 'minecraft', id: 'alice' },
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
const stimulus: any = {
|
||||
type: 'stimulus',
|
||||
payload: { content: 'hello' },
|
||||
source: { type: 'minecraft', id: 'alice' },
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
|
||||
eventManager.emit(stimulus)
|
||||
eventManager.emit(stimulus)
|
||||
|
||||
expect(stimulus.handled).toBe(true)
|
||||
expect(bot.bot.chat).toHaveBeenCalled()
|
||||
expect(stimulus.handled).toBe(true)
|
||||
expect(bot.bot.chat).toHaveBeenCalled()
|
||||
|
||||
reflex.destroy()
|
||||
})
|
||||
reflex.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { Logg } from '@guiiai/logg'
|
||||
import type { EventManager } from '../perception/event-manager'
|
||||
import type { PerceptionSignal } from '../perception/types/signals'
|
||||
import type { BotEvent, MineflayerWithAgents } from '../types'
|
||||
|
||||
import type { ReflexContextState } from './context'
|
||||
|
||||
import { greetingBehavior } from './behaviors/greeting'
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Logg } from '@guiiai/logg'
|
||||
|
||||
import type { MineflayerWithAgents } from '../types'
|
||||
import type { ReflexModeId } from './modes'
|
||||
import type { ReflexBehavior } from './types/behavior'
|
||||
|
||||
import { ReflexContext } from './context'
|
||||
import type { ReflexBehavior } from './types/behavior'
|
||||
import { selectMode, type ReflexModeId } from './modes'
|
||||
import { selectMode } from './modes'
|
||||
|
||||
export class ReflexRuntime {
|
||||
private readonly context = new ReflexContext()
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import type { MineflayerWithAgents } from '../../types'
|
||||
|
||||
import type { ReflexContext } from '../context'
|
||||
import type { ReflexModeId } from '../modes'
|
||||
|
||||
export interface ReflexApi {
|
||||
bot: MineflayerWithAgents
|
||||
context: ReflexContext
|
||||
bot: MineflayerWithAgents
|
||||
context: ReflexContext
|
||||
}
|
||||
|
||||
export interface ReflexBehavior {
|
||||
id: string
|
||||
modes: ReflexModeId[]
|
||||
cooldownMs?: number
|
||||
when: (ctx: ReturnType<ReflexContext['getSnapshot']>) => boolean
|
||||
score: (ctx: ReturnType<ReflexContext['getSnapshot']>) => number
|
||||
run: (api: ReflexApi) => Promise<void> | void
|
||||
id: string
|
||||
modes: ReflexModeId[]
|
||||
cooldownMs?: number
|
||||
when: (ctx: ReturnType<ReflexContext['getSnapshot']>) => boolean
|
||||
score: (ctx: ReturnType<ReflexContext['getSnapshot']>) => number
|
||||
run: (api: ReflexApi) => Promise<void> | void
|
||||
}
|
||||
|
||||
export interface BehaviorRunRecord {
|
||||
lastRunAt: number
|
||||
lastRunAt: number
|
||||
}
|
||||
|
||||
@@ -5,49 +5,49 @@ import type { Mineflayer } from '../../libs/mineflayer'
|
||||
import type { ActionAgent, ChatAgent, PlanningAgent } from '../../libs/mineflayer/base-agent'
|
||||
|
||||
export interface LLMConfig {
|
||||
agent: Neuri
|
||||
model?: string
|
||||
retryLimit?: number
|
||||
delayInterval?: number
|
||||
maxContextLength?: number
|
||||
agent: Neuri
|
||||
model?: string
|
||||
retryLimit?: number
|
||||
delayInterval?: number
|
||||
maxContextLength?: number
|
||||
}
|
||||
|
||||
export interface LLMResponse {
|
||||
content: string
|
||||
usage?: any
|
||||
content: string
|
||||
usage?: any
|
||||
}
|
||||
|
||||
export interface MineflayerWithAgents extends Mineflayer {
|
||||
planning: PlanningAgent
|
||||
action: ActionAgent
|
||||
chat: ChatAgent
|
||||
planning: PlanningAgent
|
||||
action: ActionAgent
|
||||
chat: ChatAgent
|
||||
}
|
||||
|
||||
export interface CognitiveEngineOptions {
|
||||
agent: Neuri
|
||||
airiClient: Client
|
||||
agent: Neuri
|
||||
airiClient: Client
|
||||
}
|
||||
|
||||
// TODO: currently stimulus is just chat events, consider renaming to 'input' or 'user_interaction'
|
||||
export type EventCategory = 'perception' | 'feedback' | 'world_update' | 'system_alert'
|
||||
|
||||
export interface BotEventSource {
|
||||
type: 'minecraft' | 'airi' | 'system'
|
||||
id: string // Agent/Source identifier
|
||||
reply?: (message: string) => void
|
||||
type: 'minecraft' | 'airi' | 'system'
|
||||
id: string // Agent/Source identifier
|
||||
reply?: (message: string) => void
|
||||
}
|
||||
|
||||
export interface BotEvent<T = any> {
|
||||
type: EventCategory
|
||||
payload: T
|
||||
source: BotEventSource
|
||||
timestamp: number
|
||||
// Layered Architecture Metadata
|
||||
priority?: number // Higher is more urgent
|
||||
handled?: boolean // Set by Reflex layer to inhibit Conscious layer
|
||||
type: EventCategory
|
||||
payload: T
|
||||
source: BotEventSource
|
||||
timestamp: number
|
||||
// Layered Architecture Metadata
|
||||
priority?: number // Higher is more urgent
|
||||
handled?: boolean // Set by Reflex layer to inhibit Conscious layer
|
||||
}
|
||||
|
||||
export interface WorldUpdatePayload {
|
||||
event: string
|
||||
data: any
|
||||
event: string
|
||||
data: any
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export type ActionErrorCode =
|
||||
| 'RESOURCE_MISSING'
|
||||
| 'CRAFTING_FAILED'
|
||||
| 'NAVIGATION_FAILED'
|
||||
| 'INTERRUPTED'
|
||||
| 'INVENTORY_FULL'
|
||||
| 'UNKNOWN'
|
||||
export type ActionErrorCode
|
||||
= | 'RESOURCE_MISSING'
|
||||
| 'CRAFTING_FAILED'
|
||||
| 'NAVIGATION_FAILED'
|
||||
| 'INTERRUPTED'
|
||||
| 'INVENTORY_FULL'
|
||||
| 'UNKNOWN'
|
||||
|
||||
export class ActionError extends Error {
|
||||
public readonly code: ActionErrorCode
|
||||
|
||||
Reference in New Issue
Block a user