diff --git a/packages/client-eliza-home/package.json b/packages/client-eliza-home/package.json new file mode 100644 index 00000000000..929d098c88f --- /dev/null +++ b/packages/client-eliza-home/package.json @@ -0,0 +1,18 @@ +{ + "name": "@elizaos/client-eliza-home", + "version": "0.1.0", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*" + }, + "devDependencies": { + "tsup": "8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint . --fix" + } +} diff --git a/packages/client-eliza-home/src/actions/control_device.ts b/packages/client-eliza-home/src/actions/control_device.ts new file mode 100644 index 00000000000..9ca7b1ece3b --- /dev/null +++ b/packages/client-eliza-home/src/actions/control_device.ts @@ -0,0 +1,109 @@ +import { + Action, + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + State, +} from "@elizaos/core"; +import { SmartHomeManager } from "../smart_home.ts"; +import { smartThingsApi } from "../services/smart_things_api.ts"; +import { deviceStateProvider } from "../providers/device_state.ts"; + +export const controlDeviceAction = { + name: "CONTROL_DEVICE", + similes: ["DEVICE_CONTROL", "SMART_HOME_CONTROL", "HOME_CONTROL"], + description: "Controls smart home devices with specific commands", + validate: async (runtime: IAgentRuntime, message: Memory) => { + const keywords = [ + "turn on", + "turn off", + "switch", + "toggle", + "set", + "change", + "adjust", + "dim", + "brighten", + "lock", + "unlock", + ]; + return keywords.some(keyword => + message.content.text.toLowerCase().includes(keyword) + ); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + const smartHomeManager = new SmartHomeManager(runtime); + const result = await smartHomeManager.handleCommand(message.content.text, message.userId); + + const response: Content = { + text: `Command executed: ${result.message || "Success"}`, + action: "DEVICE_CONTROL_RESPONSE", + source: "home-assistant" + }; + + await callback(response); + return response; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Turn on the living room lights", + }, + }, + { + user: "{{user2}}", + content: { + text: "I'll turn on the living room lights for you", + action: "CONTROL_DEVICE", + }, + }, + ], + ] as ActionExample[][], +} as Action; + +export async function controlDevice(deviceId: string, command: string, args: any = {}) { + try { + // Map internal commands to SmartThings commands + const smartThingsCommand = mapCommand(command, args); + + await smartThingsApi.devices.executeCommand(deviceId, smartThingsCommand); + + // Update local device state + await deviceStateProvider.updateDeviceState(deviceId, command, args); + + } catch (error) { + console.error(`Failed to control device ${deviceId}:`, error); + throw error; + } +} + +function mapCommand(command: string, args: any) { + // Map our internal commands to SmartThings command format + switch (command) { + case 'turnOn': + return { capability: 'switch', command: 'on' }; + case 'turnOff': + return { capability: 'switch', command: 'off' }; + case 'setLevel': + return { + capability: 'switchLevel', + command: 'setLevel', + arguments: [args.level] + }; + // ... map other commands + default: + throw new Error(`Unknown command: ${command}`); + } +} + +export default controlDeviceAction; \ No newline at end of file diff --git a/packages/client-eliza-home/src/actions/discover_devices.ts b/packages/client-eliza-home/src/actions/discover_devices.ts new file mode 100644 index 00000000000..9a6b2f26674 --- /dev/null +++ b/packages/client-eliza-home/src/actions/discover_devices.ts @@ -0,0 +1,113 @@ +import { + Action, + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + State, +} from "@elizaos/core"; +import { EntityManager } from "../entities.ts"; +import { CAPABILITIES } from '../capabilities'; +import { smartThingsApi } from "../services/smart_things_api.ts"; + +export const discoverDevicesAction = { + name: "DISCOVER_DEVICES", + similes: ["SCAN_DEVICES", "FIND_DEVICES", "LIST_DEVICES"], + description: "Discovers and lists all available smart home devices", + validate: async (runtime: IAgentRuntime, message: Memory) => { + const keywords = [ + "discover", + "find", + "scan", + "list", + "show", + "what", + "devices", + "lights", + "switches", + ]; + return keywords.some(keyword => + message.content.text.toLowerCase().includes(keyword) + ); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + const entityManager = new EntityManager(runtime); + await entityManager.discoverEntities(); + + const entities = entityManager.getAllEntities(); + const deviceList = entities + .map(entity => `- ${entity.name} (${entity.entityId}): ${entity.state}`) + .join('\n'); + + const response: Content = { + text: `Here are all the available devices:\n\n${deviceList}`, + action: "DEVICE_LIST_RESPONSE", + source: "home-assistant" + }; + + await callback(response); + return response; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "What devices do you see?", + }, + }, + { + user: "{{user2}}", + content: { + text: "Let me check what devices are available...", + action: "DISCOVER_DEVICES", + }, + }, + ], + ] as ActionExample[][], +} as Action; + +export default discoverDevicesAction; + +export async function discoverDevices() { + // Implementation to discover SmartThings devices + try { + const devices = await smartThingsApi.devices.list(); + + return devices.map(device => ({ + id: device.deviceId, + name: device.label || device.name, + capabilities: device.capabilities.map(cap => ({ + id: cap.id, + version: cap.version + })), + room: device.roomId, + status: parseDeviceStatus(device.status) + })); + } catch (error) { + console.error('Failed to discover devices:', error); + throw error; + } +} + +function parseDeviceStatus(status: any) { + // Convert SmartThings status format to our internal format + const deviceStatus: any = {}; + + if (status.switch) { + deviceStatus.switch = status.switch.value; + } + if (status.level) { + deviceStatus.level = status.level.value; + } + // ... parse other status values + + return deviceStatus; +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/capabilities.ts b/packages/client-eliza-home/src/capabilities.ts new file mode 100644 index 00000000000..ffce19ca3d4 --- /dev/null +++ b/packages/client-eliza-home/src/capabilities.ts @@ -0,0 +1,109 @@ +import { IAgentRuntime } from "@elizaos/core"; + +// Define SmartThings capability interfaces +export interface SmartThingsCapability { + id: string; + version: number; +} + +export const CAPABILITIES = { + // Basic capabilities + SWITCH: { id: 'switch', version: 1 }, + POWER_METER: { id: 'powerMeter', version: 1 }, + ENERGY_METER: { id: 'energyMeter', version: 1 }, + + // Climate capabilities + THERMOSTAT: { id: 'thermostat', version: 1 }, + TEMPERATURE_MEASUREMENT: { id: 'temperatureMeasurement', version: 1 }, + HUMIDITY_MEASUREMENT: { id: 'humidityMeasurement', version: 1 }, + + // Lighting capabilities + SWITCH_LEVEL: { id: 'switchLevel', version: 1 }, + COLOR_CONTROL: { id: 'colorControl', version: 1 }, + COLOR_TEMPERATURE: { id: 'colorTemperature', version: 1 }, + + // Security capabilities + LOCK: { id: 'lock', version: 1 }, + MOTION_SENSOR: { id: 'motionSensor', version: 1 }, + CONTACT_SENSOR: { id: 'contactSensor', version: 1 }, + PRESENCE_SENSOR: { id: 'presenceSensor', version: 1 }, + + // Media capabilities + MEDIA_PLAYBACK: { id: 'mediaPlayback', version: 1 }, + VOLUME: { id: 'volume', version: 1 }, + + // Window/Door capabilities + WINDOW_SHADE: { id: 'windowShade', version: 1 }, + GARAGE_DOOR: { id: 'garageDoor', version: 1 }, + + // Fan capabilities + FAN_SPEED: { id: 'fanSpeed', version: 1 }, + + // Battery capabilities + BATTERY: { id: 'battery', version: 1 }, +} as const; + +export type CapabilityType = keyof typeof CAPABILITIES; + +export interface Capability { + interface: string; + version: string; + type: string; + properties: { + supported: Array<{ + name: string; + }>; + proactivelyReported: boolean; + retrievable: boolean; + }; +} + +export class CapabilityManager { + private runtime: IAgentRuntime; + private capabilities: Map; + + constructor(runtime: IAgentRuntime) { + this.runtime = runtime; + this.capabilities = new Map(); + this.initializeCapabilities(); + } + + private initializeCapabilities() { + // Add standard capabilities + this.addCapability({ + interface: "Alexa.PowerController", + version: "3", + type: "AlexaInterface", + properties: { + supported: [{ name: "powerState" }], + proactivelyReported: true, + retrievable: true, + }, + }); + + this.addCapability({ + interface: "Alexa.BrightnessController", + version: "3", + type: "AlexaInterface", + properties: { + supported: [{ name: "brightness" }], + proactivelyReported: true, + retrievable: true, + }, + }); + + // Add more capabilities as needed + } + + addCapability(capability: Capability) { + this.capabilities.set(capability.interface, capability); + } + + getCapability(interfaceName: string): Capability | undefined { + return this.capabilities.get(interfaceName); + } + + getAllCapabilities(): Capability[] { + return Array.from(this.capabilities.values()); + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/config.ts b/packages/client-eliza-home/src/config.ts new file mode 100644 index 00000000000..2d2ad9953cb --- /dev/null +++ b/packages/client-eliza-home/src/config.ts @@ -0,0 +1,50 @@ +export const DEFAULT_CONFIG = { + DISCOVERY_INTERVAL: 300000, // 5 minutes + STATE_UPDATE_INTERVAL: 60000, // 1 minute + MAX_RETRY_ATTEMPTS: 3, + RETRY_DELAY: 1000, // 1 second +}; + +export const SUPPORTED_CAPABILITIES = [ + 'switch', + 'light', + 'thermostat', + 'lock', + 'colorControl', + 'colorTemperature', + 'motionSensor', + 'contactSensor', + 'mediaPlayback', +]; + +export const CAPABILITY_MAPPINGS = { + switch: ['switch'], + light: ['switch', 'switchLevel', 'colorControl', 'colorTemperature'], + thermostat: ['thermostat', 'temperatureMeasurement', 'humidityMeasurement'], + lock: ['lock'], + motionSensor: ['motionSensor'], + contactSensor: ['contactSensor'], + presenceSensor: ['presenceSensor'], + mediaPlayer: ['mediaPlayback', 'volume'], + windowShade: ['windowShade'], + garageDoor: ['garageDoor'], + fan: ['fanSpeed', 'switch'], + powerMeter: ['powerMeter', 'energyMeter'], + battery: ['battery'] +}; + +export const DEVICE_CLASSES = { + switch: 'switch', + light: 'light', + thermostat: 'climate', + lock: 'lock', + motionSensor: 'binary_sensor', + contactSensor: 'binary_sensor', + presenceSensor: 'binary_sensor', + mediaPlayer: 'media_player', + windowShade: 'cover', + garageDoor: 'cover', + fan: 'fan', + powerMeter: 'sensor', + battery: 'sensor' +}; \ No newline at end of file diff --git a/packages/client-eliza-home/src/entities.ts b/packages/client-eliza-home/src/entities.ts new file mode 100644 index 00000000000..78622f47736 --- /dev/null +++ b/packages/client-eliza-home/src/entities.ts @@ -0,0 +1,71 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { SmartThingsApi } from "./services/smart_things_api"; +import { CAPABILITY_MAPPINGS } from "./config"; + +export interface Entity { + entityId: string; + name: string; + type: string; + capabilities: string[]; + state: any; +} + +export class EntityManager { + private runtime: IAgentRuntime; + private api: SmartThingsApi; + private entities: Map; + + constructor(runtime: IAgentRuntime) { + this.runtime = runtime; + this.api = new SmartThingsApi(runtime); + this.entities = new Map(); + } + + async discoverEntities(): Promise { + try { + const devices = await this.api.devices.list(); + + for (const device of devices) { + const entity: Entity = { + entityId: device.deviceId, + name: device.label || device.name, + type: this.determineDeviceType(device.capabilities), + capabilities: device.capabilities.map(cap => cap.id), + state: device.status, + }; + + this.entities.set(entity.entityId, entity); + } + } catch (error) { + throw new Error(`Entity discovery failed: ${error.message}`); + } + } + + private determineDeviceType(capabilities: any[]): string { + // Map capabilities to device type + for (const [type, requiredCaps] of Object.entries(CAPABILITY_MAPPINGS)) { + if (requiredCaps.every(cap => + capabilities.some(c => c.id === cap) + )) { + return type; + } + } + return 'unknown'; + } + + getEntity(entityId: string): Entity | undefined { + return this.entities.get(entityId); + } + + getAllEntities(): Entity[] { + return Array.from(this.entities.values()); + } + + async updateEntityState(entityId: string, state: any): Promise { + const entity = this.entities.get(entityId); + if (entity) { + entity.state = state; + this.entities.set(entityId, entity); + } + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/environment.ts b/packages/client-eliza-home/src/environment.ts new file mode 100644 index 00000000000..dc6047915e3 --- /dev/null +++ b/packages/client-eliza-home/src/environment.ts @@ -0,0 +1,26 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const homeConfigSchema = z.object({ + SMARTTHINGS_TOKEN: z.string().min(1, "SmartThings token is required"), +}); + +export type HomeConfig = z.infer; + +export async function validateHomeConfig(runtime: IAgentRuntime): Promise { + try { + const config = { + SMARTTHINGS_TOKEN: runtime.getSetting("SMARTTHINGS_TOKEN") || process.env.SMARTTHINGS_TOKEN, + }; + + return homeConfigSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error(`SmartThings configuration validation failed:\n${errorMessages}`); + } + throw error; + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/handlers/device_handlers.ts b/packages/client-eliza-home/src/handlers/device_handlers.ts new file mode 100644 index 00000000000..a9e213e71bc --- /dev/null +++ b/packages/client-eliza-home/src/handlers/device_handlers.ts @@ -0,0 +1,178 @@ +import { SmartThingsApi } from "../services/smart_things_api"; +import { DeviceState } from "../providers/device_state"; + +export class DeviceHandlers { + constructor(private api: SmartThingsApi) {} + + async handleSwitch(deviceId: string, command: string): Promise { + await this.api.devices.executeCommand(deviceId, { + capability: 'switch', + command: command === 'on' ? 'on' : 'off' + }); + } + + async handleLight(deviceId: string, command: string, args: any = {}): Promise { + const commands = []; + + if (command === 'on' || command === 'off') { + commands.push({ + capability: 'switch', + command: command + }); + } + + if (args.brightness) { + commands.push({ + capability: 'switchLevel', + command: 'setLevel', + arguments: [args.brightness] + }); + } + + if (args.color) { + commands.push({ + capability: 'colorControl', + command: 'setColor', + arguments: [args.color] + }); + } + + await this.api.devices.executeCommands(deviceId, commands); + } + + async handleThermostat(deviceId: string, command: string, args: any = {}): Promise { + const commands = []; + + if (args.temperature) { + commands.push({ + capability: 'thermostat', + command: 'setTemperature', + arguments: [args.temperature] + }); + } + + if (args.mode) { + commands.push({ + capability: 'thermostat', + command: 'setMode', + arguments: [args.mode] + }); + } + + await this.api.devices.executeCommands(deviceId, commands); + } + + async handleLock(deviceId: string, command: string): Promise { + await this.api.devices.executeCommand(deviceId, { + capability: 'lock', + command: command === 'lock' ? 'lock' : 'unlock' + }); + } + + async handleWindowShade(deviceId: string, command: string, args: any = {}): Promise { + const commands = []; + + switch (command) { + case 'open': + commands.push({ + capability: 'windowShade', + command: 'open' + }); + break; + case 'close': + commands.push({ + capability: 'windowShade', + command: 'close' + }); + break; + case 'setLevel': + if (args.level !== undefined) { + commands.push({ + capability: 'windowShade', + command: 'setLevel', + arguments: [args.level] + }); + } + break; + } + + await this.api.devices.executeCommands(deviceId, commands); + } + + async handleFan(deviceId: string, command: string, args: any = {}): Promise { + const commands = []; + + if (command === 'on' || command === 'off') { + commands.push({ + capability: 'switch', + command: command + }); + } + + if (args.speed !== undefined) { + commands.push({ + capability: 'fanSpeed', + command: 'setSpeed', + arguments: [args.speed] + }); + } + + await this.api.devices.executeCommands(deviceId, commands); + } + + async handleGarageDoor(deviceId: string, command: string): Promise { + await this.api.devices.executeCommand(deviceId, { + capability: 'garageDoor', + command: command === 'open' ? 'open' : 'close' + }); + } + + async handleMediaPlayer(deviceId: string, command: string, args: any = {}): Promise { + const commands = []; + + switch (command) { + case 'play': + case 'pause': + case 'stop': + commands.push({ + capability: 'mediaPlayback', + command: command + }); + break; + case 'setVolume': + if (args.volume !== undefined) { + commands.push({ + capability: 'volume', + command: 'setVolume', + arguments: [args.volume] + }); + } + break; + } + + await this.api.devices.executeCommands(deviceId, commands); + } + + async handleSensor(deviceId: string, sensorType: string): Promise { + const status = await this.api.devices.getStatus(deviceId); + + switch (sensorType) { + case 'motion': + return status.motionSensor?.motion; + case 'contact': + return status.contactSensor?.contact; + case 'presence': + return status.presenceSensor?.presence; + case 'temperature': + return status.temperatureMeasurement?.temperature; + case 'humidity': + return status.humidityMeasurement?.humidity; + case 'battery': + return status.battery?.battery; + default: + throw new Error(`Unknown sensor type: ${sensorType}`); + } + } + + // Add other handlers for locks, sensors, covers, etc. +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/handlers/room_handlers.ts b/packages/client-eliza-home/src/handlers/room_handlers.ts new file mode 100644 index 00000000000..eabe89a72d7 --- /dev/null +++ b/packages/client-eliza-home/src/handlers/room_handlers.ts @@ -0,0 +1,25 @@ +import { SmartThingsApi } from "../services/smart_things_api"; + +export class RoomHandlers { + constructor(private api: SmartThingsApi) {} + + async listRooms(): Promise { + return await this.api.rooms.list(); + } + + async getRoom(roomId: string): Promise { + return await this.api.rooms.get(roomId); + } + + async getRoomByName(name: string): Promise { + const rooms = await this.listRooms(); + return rooms.find(room => + room.name.toLowerCase() === name.toLowerCase() + ); + } + + async getDevicesInRoom(roomId: string): Promise { + const devices = await this.api.devices.list(); + return devices.filter(device => device.roomId === roomId); + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/handlers/scene_handlers.ts b/packages/client-eliza-home/src/handlers/scene_handlers.ts new file mode 100644 index 00000000000..328d5df0e58 --- /dev/null +++ b/packages/client-eliza-home/src/handlers/scene_handlers.ts @@ -0,0 +1,20 @@ +import { SmartThingsApi } from "../services/smart_things_api"; + +export class SceneHandlers { + constructor(private api: SmartThingsApi) {} + + async listScenes(): Promise { + return await this.api.scenes.list(); + } + + async executeScene(sceneId: string): Promise { + await this.api.scenes.execute(sceneId); + } + + async getSceneByName(name: string): Promise { + const scenes = await this.listScenes(); + return scenes.find(scene => + scene.name.toLowerCase() === name.toLowerCase() + ); + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/index.ts b/packages/client-eliza-home/src/index.ts new file mode 100644 index 00000000000..85aded60774 --- /dev/null +++ b/packages/client-eliza-home/src/index.ts @@ -0,0 +1,114 @@ +import { EventEmitter } from "events"; +import { + IAgentRuntime, + Client as ElizaClient, + elizaLogger, + stringToUuid, + getEmbeddingZeroVector, + Memory, + Content, +} from "@elizaos/core"; +import { validateHomeConfig } from "./environment.ts"; +import { CapabilityManager } from "./capabilities.ts"; +import { EntityManager } from "./entities.ts"; +import { StateManager } from "./state.ts"; +import { SmartHomeManager } from "./smart_home.ts"; +import controlDeviceAction from "./actions/control_device.ts"; +import discoverDevicesAction from "./actions/discover_devices.ts"; +import deviceStateProvider from "./providers/device_state.ts"; +import automationStateProvider from "./providers/automation_state.ts"; + + +export class HomeClient extends EventEmitter { + private runtime: IAgentRuntime; + private capabilityManager: CapabilityManager; + private entityManager: EntityManager; + private stateManager: StateManager; + private smartHomeManager: SmartHomeManager; + + constructor(runtime: IAgentRuntime) { + super(); + this.runtime = runtime; + this.initialize(); + } + + private async initialize() { + try { + const config = await validateHomeConfig(this.runtime); + + this.capabilityManager = new CapabilityManager(this.runtime); + this.entityManager = new EntityManager(this.runtime); + this.stateManager = new StateManager(this.runtime); + this.smartHomeManager = new SmartHomeManager(this.runtime); + + // Register providers + this.runtime.providers.push(this.stateManager.getProvider()); + this.runtime.providers.push(deviceStateProvider); + this.runtime.providers.push(automationStateProvider); + + // Register actions + this.registerActions(); + + // Start state monitoring + this.startStateMonitoring(); + + elizaLogger.success("Home Assistant client initialized successfully"); + } catch (error) { + elizaLogger.error("Failed to initialize Home Assistant client:", error); + throw error; + } + } + + private registerActions() { + this.runtime.registerAction(controlDeviceAction); + this.runtime.registerAction(discoverDevicesAction); + } + + private startStateMonitoring() { + setInterval(async () => { + try { + await this.entityManager.discoverEntities(); + elizaLogger.debug("Updated device states"); + } catch (error) { + elizaLogger.error("Failed to update device states:", error); + } + }, 60000); // Update every minute + } + + async handleCommand(command: string, userId: string) { + const roomId = stringToUuid(`home-${userId}`); + const userIdUUID = stringToUuid(userId); + + const memory: Memory = { + id: stringToUuid(`command-${Date.now()}`), + userId: userIdUUID, + agentId: this.runtime.agentId, + roomId, + content: { + text: command, + source: "home-assistant" + }, + embedding: getEmbeddingZeroVector(), + createdAt: Date.now() + }; + + await this.runtime.messageManager.createMemory(memory); + return this.smartHomeManager.handleCommand(command, userId); + } +} + +export const HomeClientInterface: ElizaClient = { + start: async (runtime: IAgentRuntime) => new HomeClient(runtime), + stop: async (runtime: IAgentRuntime) => { + elizaLogger.warn("Home Assistant client does not support stopping yet"); + } +}; + +export function startHome(runtime: IAgentRuntime) { + return new HomeClient(runtime); +} + +export { + homeShouldRespondTemplate, + homeMessageHandlerTemplate +} from "./templates"; \ No newline at end of file diff --git a/packages/client-eliza-home/src/providers/automation_state.ts b/packages/client-eliza-home/src/providers/automation_state.ts new file mode 100644 index 00000000000..524aabb8d54 --- /dev/null +++ b/packages/client-eliza-home/src/providers/automation_state.ts @@ -0,0 +1,35 @@ +import { IAgentRuntime, Provider } from "@elizaos/core"; + +export const automationStateProvider: Provider = { + name: "automation-state", + get: async (runtime: IAgentRuntime) => { + try { + const response = await fetch( + `${runtime.getSetting("HOME_ASSISTANT_URL")}/api/states`, + { + headers: { + Authorization: `Bearer ${runtime.getSetting("HOME_ASSISTANT_TOKEN")}`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + throw new Error("Failed to fetch automation states"); + } + + const states = await response.json(); + const automations = states.filter(state => state.entity_id.startsWith('automation.')); + + const automationStates = automations + .map(automation => `${automation.attributes.friendly_name}: ${automation.state}`) + .join('\n'); + + return `Current Automation States:\n${automationStates}`; + } catch (error) { + return "Unable to fetch automation states"; + } + } +}; + +export default automationStateProvider; \ No newline at end of file diff --git a/packages/client-eliza-home/src/providers/device_state.ts b/packages/client-eliza-home/src/providers/device_state.ts new file mode 100644 index 00000000000..09a008ca549 --- /dev/null +++ b/packages/client-eliza-home/src/providers/device_state.ts @@ -0,0 +1,60 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { Provider } from "@elizaos/core"; +import { EntityManager } from "../entities.ts"; +import { SmartThingsCapability } from '../capabilities'; + +export interface DeviceState { + id: string; + name: string; + capabilities: SmartThingsCapability[]; + room?: string; + status: { + switch?: 'on' | 'off'; + level?: number; + temperature?: number; + motion?: 'active' | 'inactive'; + contact?: 'open' | 'closed'; + // ... other status fields + }; +} + +export const deviceStateProvider: Provider = { + get: async (runtime: IAgentRuntime) => { + const entityManager = new EntityManager(runtime); + await entityManager.discoverEntities(); + const entities = entityManager.getAllEntities(); + + const deviceStates = entities + .map(entity => `${entity.name}: ${entity.state}`) + .join('\n'); + + return `Current Device States:\n${deviceStates}`; + } +}; + +export default deviceStateProvider; + +export class DeviceStateProvider { + private devices: Map = new Map(); + + async updateDeviceState(deviceId: string, capability: string, value: any) { + const device = this.devices.get(deviceId); + if (!device) { + throw new Error(`Device ${deviceId} not found`); + } + + // Update device status based on capability + switch (capability) { + case 'switch': + device.status.switch = value; + break; + case 'level': + device.status.level = value; + break; + // ... handle other capabilities + } + + this.devices.set(deviceId, device); + + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/services/device_discovery.ts b/packages/client-eliza-home/src/services/device_discovery.ts new file mode 100644 index 00000000000..d2e4b3316de --- /dev/null +++ b/packages/client-eliza-home/src/services/device_discovery.ts @@ -0,0 +1,50 @@ +import { SmartThingsApi } from './smart_things_api'; +import { SmartThingsDevice } from '../types/smart_things'; +import { DEVICE_CLASSES } from '../config'; + +export class DeviceDiscoveryService { + constructor(private api: SmartThingsApi) {} + + async discoverDevices(): Promise { + const devices = await this.api.devices.list(); + return devices.map(device => this.normalizeDevice(device)); + } + + async discoverDevicesByRoom(roomId: string): Promise { + const devices = await this.discoverDevices(); + return devices.filter(device => device.roomId === roomId); + } + + async discoverDevicesByType(type: string): Promise { + const devices = await this.discoverDevices(); + return devices.filter(device => this.getDeviceType(device) === type); + } + + private normalizeDevice(device: any): SmartThingsDevice { + return { + deviceId: device.deviceId, + name: device.label || device.name, + roomId: device.roomId, + capabilities: device.capabilities, + status: device.status || {} + }; + } + + private getDeviceType(device: SmartThingsDevice): string { + for (const [type, deviceClass] of Object.entries(DEVICE_CLASSES)) { + if (this.hasRequiredCapabilities(device, type)) { + return deviceClass; + } + } + return 'unknown'; + } + + private hasRequiredCapabilities(device: SmartThingsDevice, type: string): boolean { + const requiredCaps = DEVICE_CLASSES[type]; + if (!requiredCaps) return false; + + return device.capabilities.some(cap => + requiredCaps.includes(cap.id) + ); + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/services/smart_things_api.ts b/packages/client-eliza-home/src/services/smart_things_api.ts new file mode 100644 index 00000000000..3033480fd90 --- /dev/null +++ b/packages/client-eliza-home/src/services/smart_things_api.ts @@ -0,0 +1,69 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { retryWithBackoff } from "../utils"; + +export class SmartThingsApi { + private baseUrl = 'https://api.smartthings.com/v1'; + private token: string; + + constructor(runtime: IAgentRuntime) { + this.token = runtime.getSetting("SMARTTHINGS_TOKEN"); + if (!this.token) { + throw new Error("SmartThings token is required"); + } + } + + private async request(endpoint: string, options: RequestInit = {}) { + const url = `${this.baseUrl}${endpoint}`; + const response = await fetch(url, { + ...options, + headers: { + 'Authorization': `Bearer ${this.token}`, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + + if (!response.ok) { + throw new Error(`SmartThings API error: ${response.statusText}`); + } + + return response.json(); + } + + devices = { + list: () => this.request('/devices'), + get: (deviceId: string) => this.request(`/devices/${deviceId}`), + getStatus: (deviceId: string) => this.request(`/devices/${deviceId}/status`), + executeCommand: (deviceId: string, command: any) => + this.request(`/devices/${deviceId}/commands`, { + method: 'POST', + body: JSON.stringify({ + commands: [command] + }) + }), + executeCommands: (deviceId: string, commands: any[]) => + this.request(`/devices/${deviceId}/commands`, { + method: 'POST', + body: JSON.stringify({ commands }) + }), + getComponents: (deviceId: string) => + this.request(`/devices/${deviceId}/components`), + getCapabilities: (deviceId: string) => + this.request(`/devices/${deviceId}/capabilities`) + }; + + scenes = { + list: () => this.request('/scenes'), + execute: (sceneId: string) => + this.request(`/scenes/${sceneId}/execute`, { + method: 'POST' + }) + }; + + rooms = { + list: () => this.request('/rooms'), + get: (roomId: string) => this.request(`/rooms/${roomId}`) + }; +} + +export const smartThingsApi = new SmartThingsApi(null as any); // Will be initialized later \ No newline at end of file diff --git a/packages/client-eliza-home/src/smart_home.ts b/packages/client-eliza-home/src/smart_home.ts new file mode 100644 index 00000000000..967b633377b --- /dev/null +++ b/packages/client-eliza-home/src/smart_home.ts @@ -0,0 +1,86 @@ +import { IAgentRuntime, elizaLogger } from "@elizaos/core"; +import { SmartThingsApi } from "./services/smart_things_api"; +import { CommandParser } from "./utils/command_parser"; +import { homeShouldRespondTemplate, homeMessageHandlerTemplate } from "./templates"; + +export class SmartHomeManager { + private runtime: IAgentRuntime; + private api: SmartThingsApi; + + constructor(runtime: IAgentRuntime) { + this.runtime = runtime; + this.api = new SmartThingsApi(runtime); + } + + async handleCommand(command: string, userId: string): Promise { + try { + // First check if we should respond using the template + const shouldRespond = await this.runtime.llm.shouldRespond( + homeShouldRespondTemplate, + command + ); + + if (shouldRespond !== 'RESPOND') { + return null; + } + + // Parse the command using CommandParser + const parsedCommand = CommandParser.parseCommand(command); + const deviceCommand = CommandParser.mapToDeviceCommand( + parsedCommand.command, + parsedCommand.args + ); + + // Execute the command + const result = await this.executeCommand(deviceCommand); + + // Generate response using template + const response = await this.runtime.llm.complete( + homeMessageHandlerTemplate, + { + command, + result, + homeState: await this.getCurrentState() + } + ); + + return { + success: true, + message: response, + data: result + }; + + } catch (error) { + elizaLogger.error("Error handling smart home command:", error); + throw error; + } + } + + private async getCurrentState(): Promise { + try { + const devices = await this.api.devices.list(); + return devices + .map(device => `${device.name}: ${JSON.stringify(device.status)}`) + .join('\n'); + } catch (error) { + elizaLogger.error("Error getting current state:", error); + return "Unable to fetch current state"; + } + } + + private async executeCommand(deviceCommand: any): Promise { + try { + return await this.api.devices.executeCommand( + deviceCommand.deviceId, + { + capability: deviceCommand.capability, + command: deviceCommand.command, + arguments: deviceCommand.arguments + } + ); + } catch (error) { + elizaLogger.error("Error executing smart home command:", error); + throw error; + } + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/state.ts b/packages/client-eliza-home/src/state.ts new file mode 100644 index 00000000000..c120cd08564 --- /dev/null +++ b/packages/client-eliza-home/src/state.ts @@ -0,0 +1,36 @@ +import { IAgentRuntime, Provider } from "@elizaos/core"; +import { Entity } from "./entities.ts"; + +export class StateManager { + private runtime: IAgentRuntime; + private states: Map; + + constructor(runtime: IAgentRuntime) { + this.runtime = runtime; + this.states = new Map(); + } + + async updateState(entityId: string, state: any): Promise { + this.states.set(entityId, state); + } + + getState(entityId: string): any { + return this.states.get(entityId); + } + + getAllStates(): Map { + return this.states; + } + + getProvider(): Provider { + return { + name: "home-assistant-state", + get: async () => { + const states = Array.from(this.states.entries()) + .map(([entityId, state]) => `${entityId}: ${JSON.stringify(state)}`) + .join('\n'); + return `Current Home Assistant States:\n${states}`; + } + }; + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/templates.ts b/packages/client-eliza-home/src/templates.ts new file mode 100644 index 00000000000..84c34ddf9db --- /dev/null +++ b/packages/client-eliza-home/src/templates.ts @@ -0,0 +1,38 @@ +import { messageCompletionFooter, shouldRespondFooter } from "@elizaos/core"; + +export const homeShouldRespondTemplate = ` +# Task: Decide if the assistant should respond to home automation requests. + +# Current home state: +{{homeState}} + +# Recent message: +{{message}} + +# Instructions: Determine if the assistant should respond to the message and control home devices. +Response options are [RESPOND], [IGNORE] and [STOP]. + +The assistant should: +- Respond with [RESPOND] to direct home automation requests (e.g., "turn on the lights") +- Respond with [RESPOND] to questions about device states (e.g., "are the lights on?") +- Respond with [IGNORE] to unrelated messages +- Respond with [STOP] if asked to stop controlling devices + +Choose the option that best describes how the assistant should respond to the message:`; + +export const homeMessageHandlerTemplate = ` +# Task: Generate a response for a home automation request. + +# Current home state: +{{homeState}} + +# User command: +{{command}} + +# Command result: +{{result}} + +# Instructions: Write a natural response that confirms the action taken and its result. +The response should be friendly and conversational while clearly indicating what was done. + +Response:`; \ No newline at end of file diff --git a/packages/client-eliza-home/src/types/smart_things.ts b/packages/client-eliza-home/src/types/smart_things.ts new file mode 100644 index 00000000000..0a6f3c2790b --- /dev/null +++ b/packages/client-eliza-home/src/types/smart_things.ts @@ -0,0 +1,55 @@ +export interface SmartThingsDevice { + deviceId: string; + name: string; + label?: string; + roomId?: string; + capabilities: Array<{ + id: string; + version: number; + }>; + status: DeviceStatus; +} + +export interface DeviceStatus { + switch?: { + value: 'on' | 'off'; + }; + level?: { + value: number; + }; + temperature?: { + value: number; + unit: string; + }; + motion?: { + value: 'active' | 'inactive'; + }; + contact?: { + value: 'open' | 'closed'; + }; + presence?: { + value: 'present' | 'not present'; + }; + battery?: { + value: number; + }; +} + +export interface SmartThingsRoom { + roomId: string; + name: string; + locationId: string; +} + +export interface SmartThingsScene { + sceneId: string; + sceneName: string; + locationId: string; + lastExecutedDate: string; +} + +export interface DeviceCommand { + capability: string; + command: string; + arguments?: any[]; +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/utils.ts b/packages/client-eliza-home/src/utils.ts new file mode 100644 index 00000000000..4e7f9030128 --- /dev/null +++ b/packages/client-eliza-home/src/utils.ts @@ -0,0 +1,39 @@ +import { elizaLogger } from "@elizaos/core"; + +export async function retryWithBackoff( + operation: () => Promise, + maxRetries: number = 3, + baseDelay: number = 1000 +): Promise { + let lastError: Error; + + for (let i = 0; i < maxRetries; i++) { + try { + return await operation(); + } catch (error) { + lastError = error; + const delay = baseDelay * Math.pow(2, i); + elizaLogger.warn(`Operation failed, retrying in ${delay}ms...`, error); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + + throw lastError!; +} + +export function parseEntityId(entityId: string): { domain: string; name: string } { + const [domain, ...rest] = entityId.split('.'); + return { + domain, + name: rest.join('.'), + }; +} + +export function formatResponse(success: boolean, message: string, data?: any) { + return { + success, + message, + data, + timestamp: new Date().toISOString(), + }; +} \ No newline at end of file diff --git a/packages/client-eliza-home/src/utils/command_parser.ts b/packages/client-eliza-home/src/utils/command_parser.ts new file mode 100644 index 00000000000..c0e97a55726 --- /dev/null +++ b/packages/client-eliza-home/src/utils/command_parser.ts @@ -0,0 +1,58 @@ +import { DeviceCommand } from '../types/smart_things'; + +export class CommandParser { + private static readonly COMMAND_PATTERNS = { + turnOn: /turn on|switch on|enable/i, + turnOff: /turn off|switch off|disable/i, + setBrightness: /set brightness to (\d+)|dim to (\d+)|brighten to (\d+)/i, + setTemperature: /set temperature to (\d+)|change temp to (\d+)/i, + setColor: /change color to (\w+)|set color to (\w+)/i, + lock: /lock|secure/i, + unlock: /unlock|unsecure/i, + open: /open|raise/i, + close: /close|lower/i, + }; + + static parseCommand(text: string): { command: string; args?: any } { + for (const [action, pattern] of Object.entries(this.COMMAND_PATTERNS)) { + const match = text.match(pattern); + if (match) { + const args = match.slice(1).find(arg => arg !== undefined); + return { + command: action, + args: args ? { value: args } : undefined + }; + } + } + throw new Error('Unable to parse command'); + } + + static mapToDeviceCommand(command: string, args?: any): DeviceCommand { + switch (command) { + case 'turnOn': + return { capability: 'switch', command: 'on' }; + case 'turnOff': + return { capability: 'switch', command: 'off' }; + case 'setBrightness': + return { + capability: 'switchLevel', + command: 'setLevel', + arguments: [parseInt(args.value)] + }; + case 'setTemperature': + return { + capability: 'thermostat', + command: 'setTemperature', + arguments: [parseInt(args.value)] + }; + case 'setColor': + return { + capability: 'colorControl', + command: 'setColor', + arguments: [{ hex: args.value }] + }; + default: + throw new Error(`Unknown command: ${command}`); + } + } +} \ No newline at end of file diff --git a/packages/client-eliza-home/tsconfig.json b/packages/client-eliza-home/tsconfig.json new file mode 100644 index 00000000000..d946efe36dc --- /dev/null +++ b/packages/client-eliza-home/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/client-eliza-home/tsup.config.ts b/packages/client-eliza-home/tsup.config.ts new file mode 100644 index 00000000000..028c8aa2417 --- /dev/null +++ b/packages/client-eliza-home/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], + external: [ + "dotenv", + "fs", + "path", + ], +}); \ No newline at end of file diff --git a/packages/plugin-depin/src/actions/sentientai.ts b/packages/plugin-depin/src/actions/sentientai.ts index 2a3133cf4db..aa42577919f 100644 --- a/packages/plugin-depin/src/actions/sentientai.ts +++ b/packages/plugin-depin/src/actions/sentientai.ts @@ -13,7 +13,7 @@ export const sentientAI: Action = { "NEWS", "WEATHER" ], - description: "Provde realtime information for Weather, News.", + description: "Provide realtime information for Weather, News.", examples: [ [ { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aebc3fdc168..eeda3e20c77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -931,6 +931,16 @@ importers: specifier: 1.2.1 version: 1.2.1(@types/node@22.10.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5))(terser@5.37.0) + packages/client-eliza-home: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + devDependencies: + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.9(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + packages/client-farcaster: dependencies: '@elizaos/core': @@ -2275,7 +2285,7 @@ importers: version: link:../core '@goat-sdk/adapter-vercel-ai': specifier: 0.2.0 - version: 0.2.0(@goat-sdk/core@0.4.0)(ai@4.1.1(react@19.0.0)(zod@3.23.8)) + version: 0.2.0(@goat-sdk/core@0.4.0)(ai@4.1.2(react@19.0.0)(zod@3.23.8)) '@goat-sdk/core': specifier: 0.4.0 version: 0.4.0 @@ -3052,7 +3062,7 @@ importers: version: 8.3.5(@swc/core@1.10.9(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.7)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -3977,8 +3987,8 @@ packages: peerDependencies: zod: ^3.0.0 - '@ai-sdk/openai@1.1.1': - resolution: {integrity: sha512-0tUlrjSMWYYQxiC/6d6n5C6nxUYSHzlt/FipJgzKQleMts3Br5+u2cM4nwOVtuS14J2MsBM/SK2DGL0lFctirA==} + '@ai-sdk/openai@1.1.2': + resolution: {integrity: sha512-9rfcwjl4g1/Bdr2SmgFQr+aw81r62MvIKE7QDHMC4ulFd/Hej2oClROSMpDFZHXzs7RGeb32VkRyCHUWWgN3RQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -4019,8 +4029,8 @@ packages: zod: optional: true - '@ai-sdk/provider-utils@2.1.1': - resolution: {integrity: sha512-+FRXSAdzPJFJN6TpyvyGWLo7WJuoBKI1g66UL+sli1HrxlldXSwxRPeb8tMMmNcyi3VKQogg2VsoJjlt4ort5w==} + '@ai-sdk/provider-utils@2.1.2': + resolution: {integrity: sha512-ezpQT6kzy/2O4yyn/2YigMqynBYjZIOam3/EMNVzju+Ogj+Z+pf27c/Th78ce0A2ltgrXx6xN14sal/HHZNOOw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -4044,8 +4054,8 @@ packages: resolution: {integrity: sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==} engines: {node: '>=18'} - '@ai-sdk/provider@1.0.5': - resolution: {integrity: sha512-KATFp9CNXtMEzs8KBwLYK2+rGkkeED6p1+4koQveszyscIavObXIRW7vjr0MoZ9HFIHOUlrcak+3s/Xt3UXmAg==} + '@ai-sdk/provider@1.0.6': + resolution: {integrity: sha512-hwj/gFNxpDgEfTaYzCYoslmw01IY9kWLKl/wf8xuPvHtQIzlfXWmmUwc8PnCwxyt8cKzIuV0dfUghCf68HQ0SA==} engines: {node: '>=18'} '@ai-sdk/react@0.0.70': @@ -4060,8 +4070,8 @@ packages: zod: optional: true - '@ai-sdk/react@1.1.1': - resolution: {integrity: sha512-7LX/YF8sis8UM7p8ftUcu0xySG86/TBddcB42w/+mWOXL6hjYzcuGD8G121TobHsnxVxbsBlF/ykps/GYVvLNg==} + '@ai-sdk/react@1.1.2': + resolution: {integrity: sha512-bBcRsDaNHzCKSIBbPngMeqbnwZ1RFadXQo9XzHoGrvLANYRwuphGNB8XTXYVLC/eXjoaGVGw2wWf/TYigEnCuA==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -4099,8 +4109,8 @@ packages: zod: optional: true - '@ai-sdk/ui-utils@1.1.1': - resolution: {integrity: sha512-lkTxGoebnEgs8HtKeWut0AglXN7zpWQwYmun4yuhpiup7DxPWTmt3vGiYvqQTBOFAmyoea3uzIKjHwRHuayr2w==} + '@ai-sdk/ui-utils@1.1.2': + resolution: {integrity: sha512-+0kfBF4Y9jmlg1KlbNKIxchmXx9PzuReSpgRNWhpU10vfl1eeer4xK/XL2qHnzAWhsMFe/SVZXJIQObk44zNEQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -12994,8 +13004,8 @@ packages: zod: optional: true - ai@4.1.1: - resolution: {integrity: sha512-oZTzQfrvrXuXnAJhoCsGcLUxSMWWYKkqrk2LfCzcukvB8us7ZUnFBgs9drhXuFP3JkWVbeIZHXjDexZIZcbi8g==} + ai@4.1.2: + resolution: {integrity: sha512-11efhPorWFphIpeCgjW6r/jk4wB5RWUGjxayHblBXCq6YEc7o5ki7vlmSnESprsDkMEfmONBWb/xM8pWjR5O2g==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -26645,10 +26655,10 @@ snapshots: '@ai-sdk/provider-utils': 2.0.2(zod@3.23.8) zod: 3.23.8 - '@ai-sdk/openai@1.1.1(zod@3.24.1)': + '@ai-sdk/openai@1.1.2(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.5 - '@ai-sdk/provider-utils': 2.1.1(zod@3.24.1) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) zod: 3.24.1 '@ai-sdk/provider-utils@1.0.20(zod@3.23.8)': @@ -26696,18 +26706,18 @@ snapshots: optionalDependencies: zod: 3.23.8 - '@ai-sdk/provider-utils@2.1.1(zod@3.23.8)': + '@ai-sdk/provider-utils@2.1.2(zod@3.23.8)': dependencies: - '@ai-sdk/provider': 1.0.5 + '@ai-sdk/provider': 1.0.6 eventsource-parser: 3.0.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 optionalDependencies: zod: 3.23.8 - '@ai-sdk/provider-utils@2.1.1(zod@3.24.1)': + '@ai-sdk/provider-utils@2.1.2(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.5 + '@ai-sdk/provider': 1.0.6 eventsource-parser: 3.0.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 @@ -26730,7 +26740,7 @@ snapshots: dependencies: json-schema: 0.4.0 - '@ai-sdk/provider@1.0.5': + '@ai-sdk/provider@1.0.6': dependencies: json-schema: 0.4.0 @@ -26744,20 +26754,20 @@ snapshots: react: 19.0.0 zod: 3.23.8 - '@ai-sdk/react@1.1.1(react@19.0.0)(zod@3.23.8)': + '@ai-sdk/react@1.1.2(react@19.0.0)(zod@3.23.8)': dependencies: - '@ai-sdk/provider-utils': 2.1.1(zod@3.23.8) - '@ai-sdk/ui-utils': 1.1.1(zod@3.23.8) + '@ai-sdk/provider-utils': 2.1.2(zod@3.23.8) + '@ai-sdk/ui-utils': 1.1.2(zod@3.23.8) swr: 2.3.0(react@19.0.0) throttleit: 2.1.0 optionalDependencies: react: 19.0.0 zod: 3.23.8 - '@ai-sdk/react@1.1.1(react@19.0.0)(zod@3.24.1)': + '@ai-sdk/react@1.1.2(react@19.0.0)(zod@3.24.1)': dependencies: - '@ai-sdk/provider-utils': 2.1.1(zod@3.24.1) - '@ai-sdk/ui-utils': 1.1.1(zod@3.24.1) + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.2(zod@3.24.1) swr: 2.3.0(react@19.0.0) throttleit: 2.1.0 optionalDependencies: @@ -26791,18 +26801,18 @@ snapshots: optionalDependencies: zod: 3.23.8 - '@ai-sdk/ui-utils@1.1.1(zod@3.23.8)': + '@ai-sdk/ui-utils@1.1.2(zod@3.23.8)': dependencies: - '@ai-sdk/provider': 1.0.5 - '@ai-sdk/provider-utils': 2.1.1(zod@3.23.8) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.23.8) zod-to-json-schema: 3.24.1(zod@3.23.8) optionalDependencies: zod: 3.23.8 - '@ai-sdk/ui-utils@1.1.1(zod@3.24.1)': + '@ai-sdk/ui-utils@1.1.2(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.5 - '@ai-sdk/provider-utils': 2.1.1(zod@3.24.1) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) zod-to-json-schema: 3.24.1(zod@3.24.1) optionalDependencies: zod: 3.24.1 @@ -32536,10 +32546,10 @@ snapshots: '@shikijs/types': 1.29.1 '@shikijs/vscode-textmate': 10.0.1 - '@goat-sdk/adapter-vercel-ai@0.2.0(@goat-sdk/core@0.4.0)(ai@4.1.1(react@19.0.0)(zod@3.23.8))': + '@goat-sdk/adapter-vercel-ai@0.2.0(@goat-sdk/core@0.4.0)(ai@4.1.2(react@19.0.0)(zod@3.23.8))': dependencies: '@goat-sdk/core': 0.4.0 - ai: 4.1.1(react@19.0.0)(zod@3.23.8) + ai: 4.1.2(react@19.0.0)(zod@3.23.8) zod: 3.23.8 '@goat-sdk/core@0.3.8(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)': @@ -43487,24 +43497,24 @@ snapshots: - solid-js - vue - ai@4.1.1(react@19.0.0)(zod@3.23.8): + ai@4.1.2(react@19.0.0)(zod@3.23.8): dependencies: - '@ai-sdk/provider': 1.0.5 - '@ai-sdk/provider-utils': 2.1.1(zod@3.23.8) - '@ai-sdk/react': 1.1.1(react@19.0.0)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.1.1(zod@3.23.8) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.23.8) + '@ai-sdk/react': 1.1.2(react@19.0.0)(zod@3.23.8) + '@ai-sdk/ui-utils': 1.1.2(zod@3.23.8) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 optionalDependencies: react: 19.0.0 zod: 3.23.8 - ai@4.1.1(react@19.0.0)(zod@3.24.1): + ai@4.1.2(react@19.0.0)(zod@3.24.1): dependencies: - '@ai-sdk/provider': 1.0.5 - '@ai-sdk/provider-utils': 2.1.1(zod@3.24.1) - '@ai-sdk/react': 1.1.1(react@19.0.0)(zod@3.24.1) - '@ai-sdk/ui-utils': 1.1.1(zod@3.24.1) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + '@ai-sdk/react': 1.1.2(react@19.0.0)(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.2(zod@3.24.1) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 optionalDependencies: @@ -57771,7 +57781,7 @@ snapshots: solana-agent-kit@1.4.3(@noble/hashes@1.7.1)(@solana/buffer-layout@4.0.1)(@swc/core@1.10.9(@swc/helpers@0.5.15))(@types/node@20.17.9)(arweave@1.15.5)(axios@1.7.9)(borsh@2.0.0)(buffer@6.0.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(handlebars@4.7.8)(react@19.0.0)(sodium-native@3.4.1)(typescript@5.6.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@3land/listings-sdk': 0.0.6(@swc/core@1.10.9(@swc/helpers@0.5.15))(@types/node@20.17.9)(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) - '@ai-sdk/openai': 1.1.1(zod@3.24.1) + '@ai-sdk/openai': 1.1.2(zod@3.24.1) '@bonfida/spl-name-service': 3.0.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) '@cks-systems/manifest-sdk': 0.1.59(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -57804,7 +57814,7 @@ snapshots: '@tensor-oss/tensorswap-sdk': 4.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) '@tiplink/api': 0.3.1(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(sodium-native@3.4.1)(utf-8-validate@5.0.10) '@voltr/vault-sdk': 0.1.1(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) - ai: 4.1.1(react@19.0.0)(zod@3.24.1) + ai: 4.1.2(react@19.0.0)(zod@3.24.1) bn.js: 5.2.1 bs58: 6.0.0 chai: 5.1.2 @@ -57853,7 +57863,7 @@ snapshots: solana-agent-kit@1.4.3(@noble/hashes@1.7.1)(@solana/buffer-layout@4.0.1)(@swc/core@1.10.9(@swc/helpers@0.5.15))(@types/node@22.10.9)(arweave@1.15.5)(axios@1.7.9)(borsh@2.0.0)(buffer@6.0.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(handlebars@4.7.8)(react@19.0.0)(sodium-native@3.4.1)(typescript@5.7.3)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@3land/listings-sdk': 0.0.6(@swc/core@1.10.9(@swc/helpers@0.5.15))(@types/node@22.10.9)(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) - '@ai-sdk/openai': 1.1.1(zod@3.24.1) + '@ai-sdk/openai': 1.1.2(zod@3.24.1) '@bonfida/spl-name-service': 3.0.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) '@cks-systems/manifest-sdk': 0.1.59(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -57886,7 +57896,7 @@ snapshots: '@tensor-oss/tensorswap-sdk': 4.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) '@tiplink/api': 0.3.1(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(sodium-native@3.4.1)(utf-8-validate@5.0.10) '@voltr/vault-sdk': 0.1.1(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) - ai: 4.1.1(react@19.0.0)(zod@3.24.1) + ai: 4.1.2(react@19.0.0)(zod@3.24.1) bn.js: 5.2.1 bs58: 6.0.0 chai: 5.1.2