feat: HouseholdEntity Actor — Living with AI Demo#129
Conversation
Implement the channel runtime architecture (issue #113) that allows Aevatar to receive bot platform webhooks directly instead of relying on NyxID as a relay middleman. Nyx remains the credentialed outbound provider for sending messages. New project: agents/Aevatar.GAgents.ChannelRuntime with: - IPlatformAdapter abstraction for multi-platform support - LarkPlatformAdapter: URL verification + im.message.receive_v1 parsing - ChannelBotRegistrationStore: persistent protobuf-based config store - ChannelCallbackEndpoints: webhook receiver + registration CRUD - Async acknowledge-then-respond pattern (platforms have short timeouts) - Outbound replies via NyxIdApiClient.ProxyRequestAsync Existing NyxID relay path (/api/webhooks/nyxid-relay) is untouched. Closes #113 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…apter) 15 tests covering: - ChannelBotRegistrationStore: CRUD operations, persistence, edge cases - LarkPlatformAdapter: URL verification, message parsing, bot filtering, empty text handling, missing header handling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensure that session completion response is published immediately to consumers (relay, SSE) before attempting persistence. Persistence is now best-effort with logging for failures, preventing concurrency conflicts from blocking the reply to the user.
Standalone ASP.NET minimal API server at tools/Aevatar.Tools.MockNyxId
that simulates the NyxID external service. Enables end-to-end testing
of channel runtime, LLM calls, and proxy operations without a real
NyxID instance.
Endpoints:
- GET /api/v1/users/me — mock user info
- POST /api/v1/auth/test-token — issue test JWTs
- GET /api/v1/proxy/services — service discovery (api-lark-bot, api-github)
- * /api/v1/proxy/s/{slug}/{**path} — catch-all proxy with Lark/Telegram-aware responses
- POST /api/v1/llm/gateway/v1/chat/completions — OpenAI-compatible (streaming + non-streaming)
Usage:
dotnet run --project tools/Aevatar.Tools.MockNyxId
export Aevatar__NyxId__Authority=http://localhost:5199
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78cc194965
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| typeProp.GetString() == "url_verification") | ||
| { | ||
| var challenge = root.TryGetProperty("challenge", out var ch) ? ch.GetString() : null; | ||
| _logger.LogInformation("Lark URL verification challenge received"); | ||
| return Results.Json(new { challenge }); |
There was a problem hiding this comment.
Verify Lark callback token before accepting challenge
In TryHandleVerificationAsync, the challenge is echoed without checking the incoming token/signature against registration.VerificationToken. This leaves the webhook path effectively unauthenticated: anyone who can hit the callback URL can forge Lark payloads and drive bot traffic/costs. The registration model already stores a verification token, so the adapter should reject verification and message callbacks that fail authenticity checks.
Useful? React with 👍 / 👎.
|
|
||
| try | ||
| { | ||
| var actorId = $"channel-{inbound.Platform}-{inbound.ConversationId}"; |
There was a problem hiding this comment.
Include registration ID in channel actor key
ProcessAndReplyAsync keys chat actors only by platform + conversationId. If two registrations (different scope_id/Nyx token) receive the same platform chat ID, they will reuse one NyxIdChatGAgent, mixing history and tool context across registrations. Actor identity should include a registration-scoped dimension (for example registration.Id) to avoid cross-bot/tenant state bleed.
Useful? React with 👍 / 👎.
| if (payload.Is(TextMessageContentEvent.Descriptor)) | ||
| { | ||
| var evt = payload.Unpack<TextMessageContentEvent>(); | ||
| if (!string.IsNullOrEmpty(evt.Delta)) | ||
| responseBuilder.Append(evt.Delta); |
There was a problem hiding this comment.
Filter subscribed stream events to the active request
The subscription callback appends every TextMessageContentEvent from the actor and completes on any TextMessageEndEvent without checking session_id/request correlation. When two callbacks for the same actor overlap, one request can consume the other request’s stream and return the wrong reply. The handler should gate on the current request’s correlation key before appending or completing.
Useful? React with 👍 / 👎.
1. Verify Lark callback token before accepting verification challenges and event callbacks — rejects forged payloads when a verification token is configured on the registration. 2. Include registration ID in channel actor key to prevent cross-tenant state bleed when two registrations share the same platform chat ID. 3. Filter subscribed stream events by session_id so overlapping callbacks for the same actor cannot consume each other's responses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the autonomous home AI agent as described in issue #97. HouseholdEntity inherits AIGAgentBase with a Perceive-Reason-Act loop driven by stream events (sensor, camera, chat, heartbeat) with safety guardrails (kill switch, rate limiting, debounce) and dynamic context injection into LLM system prompt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cd25bc0 to
13958e2
Compare
Implements the Agent-as-Tool pattern: NyxIdChatGAgent's LLM can call the "household" tool to dispatch home automation requests to a HouseholdEntity actor. - HouseholdEntityTool: IAgentTool that dispatches HouseholdChatEvent to HouseholdEntity via IActorRuntime, returns environment state and last action as JSON - HouseholdEntityToolSource: IAgentToolSource for auto-discovery - ServiceCollectionExtensions.AddHouseholdEntityTools() for DI - 10 new tests (32 total) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Closes #97
HouseholdEntityactor inheritingAIGAgentBase<HouseholdEntityState>with Perceive-Reason-Act loopEnvironmentSnapshot,ActionRecord,MemoryEntry,SafetyState+ domain eventsArchitecture decisions
AIGAgentBase(notRoleGAgent): HouseholdEntity is driven by stream events, notChatRequestEventagents/Aevatar.GAgents.Household/following existing agent naming conventionsIAgentToolSource; system prompt guides LLM to relevant toolsFiles added
agents/Aevatar.GAgents.Household/— Actor implementation (5 files)test/Aevatar.GAgents.Household.Tests/— Unit tests (22 tests, all passing)aevatar.slnx— Solution updated with both projectsTest plan
dotnet buildsucceedsdotnet test— 22/22 tests pass🤖 Generated with Claude Code