JianFeeeee 1b792b91f5 fix(packaging): amd64 GUI 从未走过 electron 缓存,且空壳 node_modules 被当作已安装
干净 worktree 上打包时 GUI 被静默跳过。两个缺陷叠加,都属于「所有外层
检查都通过,只有嵌套的运行时缺失,而没有任何东西喊出来」。

## 一:electron 架构名与 Debian 架构名混用

electron 官方发布物命名用 x64/arm64,Debian 用 amd64/arm64。缓存查找
一直统一用 TAR_ARCH(amd64),于是 electron-v*-linux-x64.zip 永远命中
不到。arm64 两边恰好同名,所以上次修 arm64 GUI 架构污染(743b963)时
这个不一致没暴露。

推论:v1.0.3 的 amd64 GUI 实际是靠「回退到 host node_modules/electron/
dist」这条路组装的,不是走缓存——那条回退只在目标架构 == host 架构时
才允许,恰好成立所以没出错。干净 checkout 里没有完整 node_modules,
回退路径也没有,GUI 就消失了。

修法:单独映射 ELECTRON_ARCH(amd64→x64,arm64→arm64)。

## 二:判 node_modules 目录存在,而非判 electron 包存在

npm install 失败(离线/网络受限)会留下只有一两个条目的空壳
node_modules。原判据 [ ! -d node_modules ] 认为「已安装」,于是跳过
install → ever 读不到版本 → 缓存匹配退化到通配 → host dist 也没有 →
静默跳过 GUI。包名、目录名、变体名全部正确,只是没有 GUI。

修法:判据改为 electron/package.json 是否存在;目录在而包缺失时明确
说明「疑似上次 npm install 未完成」再重试;install 失败给出明确提示
而不是继续往下走。

顺带给 ever 加兜底:读不到已安装版本时从 package.json 的依赖声明取
数字部分(那里是 "^33.0.0" 这类范围,仅用于给缓存匹配一个提示)。

## 验证

干净 worktree(/tmp/rel104,release/v1.0.x)上重跑:
  node_modules 存在但 electron 缺失(疑似上次 npm install 未完成)
  electron 版本取自 package.json 依赖声明: 33.0.0(非精确)
  electron runtime: electron-v33.4.11-linux-x64.zip
  GUI built: build/homeagent-gui-linux-amd64 (263M, x86-64)

file -b 确认 electron 二进制为 x86-64,与目标架构一致(该硬校验由
743b963 引入,此处继续生效)。
2026-09-05 14:59:15 +08:00
2026-09-05 07:26:01 +08:00
2026-07-29 15:45:24 +08:00
2026-09-05 07:26:01 +08:00
2026-09-05 07:26:01 +08:00

⚠️ AI-Assisted Programming Notice: Parts of this project's code, documentation, and commit history were generated or modified with AI assistance. Key changes have been human-reviewed, but please evaluate and verify before use.

HomeAgent

中文: README.md

An Agent framework designed around separation of core domain and application domain. The kernel enforces a zero-IO policy — all external interaction (WebUI, QQ, CLI, file operations, web search, memos, etc.) is handled by the plugin layer; the kernel performs no direct IO operations.

Combined with a three-layer memory architecture (Context → Document → Graph), it maintains contextual coherence across long-running single-conversation sessions through tiered storage and automated archival.

homed (kernel, zero IO) ← PluginSDK → plugins (all IO capabilities)

Since v1.0.0 external plugins are independent subprocesses, communicating with the kernel over stdio JSON-RPC (control plane) + a shared memory segment (data plane) + an event ring (notification plane). A plugin crash cannot take down the kernel and it restarts automatically; swapping plugin.bin gives true hot-reload.

Design Principles

Separation of Core Domain and Application Domain — The kernel's responsibilities are limited to LLM orchestration, memory management, and knowledge retrieval; all IO capabilities (message send/receive, file read/write, network requests, hardware interaction, etc.) are implemented by plugins. This separation defines domain boundaries at the Agent framework level, with distinct responsibility scopes for the kernel and plugins.

Three-Layer Memory Architecture — Manages information retention in long-running agents through a tiered storage strategy:

  • Context Layer: Pretrained word embedding / TF-IDF fallback relevance-scored event window, protects last 10 entries, maintains topK context entries
  • Document Layer: Temporary memory with automatic cold data sinking, also supports user-initiated submissions
  • Graph Layer: SQLite graph database, persists entity relationships and semantic memory, supports distillation pipelines to extract triples from conversations

Architecture Diagrams

1. Message Processing Sequence

sequenceDiagram
    participant U as User/Plugin
    participant IO as IOManager
    participant EV as eventLoop
    participant CTX as RelevanceContext
    participant LLM as LLM+Tool Loop
    participant ST as StageHost
    participant MEM as Three-Layer Memory

    U->>IO: InjectInput(type, payload)
    IO->>EV: inputCh
    rect lavender
        Note over EV: processTextInput
        EV->>ST: StageOnInput  Plugin can rewrite/short-circuit
        EV->>CTX: Prune(input,topK)  StaticEmbedder/TF-IDF cosine pruning
        CTX->>MEM: Low-score events archived to Document (original timestamp)
        EV->>CTX: Append(input)  CleanTemplateText→three-branch vector→5s write
    end
    rect lightgreen
        Note over EV,LLM: process()
        EV->>MEM: buildMemoryContext  Indexer recalls from Graph (vector+jieba→BFS depth=2)
        EV->>MEM: buildSystemPrompt  DocQuery summary+Graph memory index+Persona+Skills
        EV->>ST: StagePreAction  Plugin can pre-intercept
        loop Tool loop
            LLM->>LLM: drainInterrupts
            LLM->>LLM: LLM Chat
            LLM->>ST: StagePostAction  Plugin can modify/short-circuit
            alt No tool call
                LLM-->>EV: Returns response
            else
                loop Each tool
                    ST->>ST: StageBeforeToolcall  Plugin can reject
                    LLM->>LLM: executeToolCall
                    ST->>ST: StageAfterToolcall
                end
            end
        end
    end
    rect lightpink
        Note over EV: emitResponse
        CTX->>CTX: Append(response)
        ST->>ST: StageBeforeOutput  Plugin can rewrite
        EV-->>U: ResponseCh CLI sync
        EV-->>EV: Event bus WebUI SSE
        ST->>ST: StageAfterOutput  Read-only
        EV->>MEM: emitMemoryCandidate
    end

2. Stage Pipeline

flowchart LR
    S1[① on_input] --> S2[② pre_action]
    S2 --> S3[③ post_action]
    S3 --> Q{Has tool?}
    Q -->|Yes| S4[④ before_toolcall]
    S4 --> T[executeToolCall]
    T --> S5[⑤ after_toolcall]
    S5 --> S3
    Q -->|No| S6[⑥ before_output]
    S6 --> S7[⑦ after_output]
    style S1 fill:#e1f5fe
    style S3 fill:#fff3e0
    style S6 fill:#e8f5e9

3. Three-Layer Memory

flowchart TB
    subgraph C[① Context Working Window]
        RC[RelevanceContext]
        A[Append] -->|CleanTemplateText→three-branch vector| RC
        P[Prune StaticEmbedder/TF-IDF Cosine] -->|Low score original timestamp| D
        P -->|Keep| TL[timeline→chronological→system prompt]
    end
    subgraph D[② Document File Memory]
        DS[DocStore JSON+TF-IDF]
        Q1[Query summary auto-inject] -->|[Related Memory Docs]| SP
        Q2[doc_query LLM active recall] -->|Consume+delete source| DS
        Q2 -->|Original timestamp write to context| RC
        CD[FindColdDocs 72h] -->|docToTriples| G
    end
    subgraph G[③ Graph Database]
        DB[(SQLite)]
        IDX[Indexer vector+jieba→BFS depth=2] -->|[Memory Index]| SP
        MEM[memory_recall/commit/merge/purge/edit]
        SOC[person_query/set_trait]
    end
    subgraph H[④ Heartbeat Distillation]
        REORG -->|Step3 Cold docs| CD
        REORG -->|Step4 Bigram Jaccard| CONS[consolidation]
        PIPE[Pipeline regex] -->|Name/Address/Likes/Age/Job| DB
    end
    SP[System Prompt] -->|Sequential assembly| LLM
    LLM[LLM] -->|doc_query| Q2
    LLM -->|memory_recall| MEM

See assets/docs/en/ARCHITECTURE.md for details.

Web Mascot

HomeAgent Web Mascot Xiaozhai

Xiaozhai — HomeAgent Web Mascot

Quick Start

make build build-cli
./build/homed -data /tmp/ha
# Interactive mode
./build/waiter

# Or single message
echo "Hello, remember that I like coffee" | ./build/waiter

API keys are configured via WebUI http://localhost:8080 settings page, persisted in SQLite.

Code Structure

cmd/homed/          Daemon entry, assembles all subsystems
cmd/waiter/         CLI client (Unix socket)
internal/
├── agent/core/     Agent core: event loop, LLM tool loop, 7-stage pipeline
├── agent/api/      LLM Provider + 8 Lua adapters
├── memory/         Three-layer memory: Graph(SQLite) / Document(JSON+TF-IDF) / Text(JSONL) + StaticEmbedder(pretrained word embedding/TF-IDF fallback) + CleanTemplateText(de-template)
├── knowledge/      Knowledge base (filesystem + TF-IDF)
├── plugin/         Plugin registry + subprocess loader (stdio RPC + shared memory segment + event ring)
├── plugins/        11 built-in plugins (webui/cli/timer/cmd/mcp/clawhubadapter/agentcli/healthcheck/pluginmgr/files/cfgmgr)
├── sdk/            PluginSDK (Tool/Stage/Event three channels)
├── config/         SQLite config center
├── events/         Event bus
└── internal/lua/adapters/   8 LLM protocol adapter scripts
External plugin development: see [homeagent-sdk](https://gitcode.com/JianFeeeee/homeagent-sdk) repo, use `plugindev` toolchain, refer to Go and Lua examples in `example/`

Project Status

v1.0.4 — Two data-race fixes (the live /api/v1/device/ws gateway and terminal streaming). A full -race pass exposed both: remotedevice wrote one connection's bufio.Writer from two concurrent paths (handleWS loop replies hello_ack/bind_ack/pong, plus PushJSON/PushData agent→device pushes) — bufio.Writer is not thread-safe, and TestWSPushDataAudio async push hit it reliably; agentcli handed the shared read buffer to the reader goroutine (which the OS keeps overwriting) while readLoop did copy(data, buf[:r.n]) — concurrent read/write of the same buffer. Fix: connection-level write lock (wconn.wmu, shared by Push* and handleWS; PushData holds it across the whole start/chunks/end sequence to preserve protocol order) plus carrying read results in per-result slices instead of a shared buffer. Repo-wide go test ./... -race went from 7 races / 5 failing tests to all-clean.

v1.0.3 — Kernel stage-coordinator double-unlock fix. The production homed main process once died outright with fatal error: sync: unlock of unlocked mutex, taking all 27 subprocess plugins with it: Host.endStage performed "decrement inflight, decide whether I'm the last leaver" outside the coordMu critical section while detaching the coordinator inside it, so a late-arriving plugin could attach to a coordinator that was already finishing, be misjudged as the last leaver, and unlock the same stageMu twice. A sync.Mutex double unlock is a runtime fatal, not a panic, so the two layers of recover structurally cannot catch it—which is exactly why the "a crashing plugin must not take down the kernel" isolation design failed wholesale here. The fix folds counting, decision, and detach into one critical section, and also moves the first arriver's enter() (which writes the shared segment) inside the lock—previously a late arriver could read a half-written segment. Ships with 5 regression cases, including a reverse check that stashes the old implementation back to confirm the tests really do reproduce the fatal.

v1.0.1 — Multimodal bugfix. The plugin ABI/protocol is unchanged, so plugin.bin artifacts built for 1.0.0 need no rebuild. Three defects fixed: (1) vision silently failing—media blocks attached to a tool message are not treated as viewable content by the model (measured on one image: 0/3 read from a tool message, 3/3 from a standalone user message); media now rides its own user message placed immediately after, which is what the plugin's own wording ("injected into the following conversation") always claimed; (2) new multimodal capability declaration + fallback chain—core.llm.sources.<name>.vision/.audio declares whether a source can genuinely process media (a gateway may strip image_url and still return 200, with identical prompt_tokens with and without the image); when it cannot, media is transcribed to text via a vision-capable source, finally wiring up the long-registered but never-read core.input_processing.image.fallback_provider settings; (3) see_video frame-count semantics were inverted—fps=1/N is a rate, not a count, so a 20s video yielded 2 frames when 10 were requested and 20 frames when 1 was requested; now ffprobe measures duration and the filter becomes fps=N/duration with -frames:v as a hard cap.

v1.0.0 — External plugins moved from C ABI shared libraries to subprocess + shared memory. The first release that no longer loads .so/.dll, and it is incompatible with 0.9.x (existing plugins must be rebuilt into plugin.bin with the new plugindev, though business code needs zero changes). Eliminates 6 classes of defects that had caused production incidents: hot-reload silently failing (DF_1_NODELETE making dlclose a no-op), no crash isolation (a plugin panic took down homed), stage lost updates (35.8~36.8% loss under the copy model), uncancellable cgo timeouts (linear OS-thread leaks), output_send reporting false success (the model was told "sent" while the message never went out), and Windows capability degradation (only 3 stage fields visible, no write-back). Three communication planes: stdio JSON-RPC (control) + shared memory segment (data) + event ring (notification); the privilege gradient is now enforced by three explicit gates. RPC round-trip p50 24.1µs; crash-to-recovery under 1s.

v0.9.0 — C ABI v2: external plugin Stage callbacks can now write back (invoke_stage gained a result out-param; plugins may mutate RawMessage/LLMText/ToolResults etc. in OnInput/AfterToolcall/PostAction and have them synced to the core). ABI version now tracks core minor releases (v0.9.x → ABIVersion=2, version_min=1 keeps old plugins loadable). Also fixes the tool-loop zen-compat placeholder that wrongly fired on first-turn system context tail. The SDK ships an enhanced sanitizer example (bad-UTF-8 / U+FFFD / ANSI-escape scrub across the whole pipeline). This ABI retired with v1.0.0.

v0.8.0 — Core is functional, plugin system enhanced. 20+ built-in plugins. External plugin development via homeagent-sdk repo. Added input channel NoMemory/Cleaner, ChannelDef, plugin disable/enable system (CLI + WebUI), plugindev toolchain C ABI ChannelDef support.

Documentation

Downloads

Releases ship three variants:

Variant Contents For
full homed + waiter + desktop GUI + systemd unit Single-machine, everything
server homed + waiter + systemd unit Servers (no desktop environment)
client waiter + desktop GUI Connecting to a remote HomeAgent
  • Linux: .deb (amd64/arm64), .rpm (x86_64), .tar.gz
  • Windows: HomeAgent_v1.0.4_{Full,Server,Client}_win64.exe (NSIS installer)
  • Portable: homeagent-bin-<os>_<arch>.tar.gz (homed/waiter/initconfig)
  • Verification: SHA256SUMS

The macOS homed requires a native macOS build (CGO + sqlite3), so release packages ship only waiter/initconfig.

Build

make build build-cli    # Build daemon + CLI
make test               # go test ./...
make install            # Install to system

Dependencies: Go 1.25+, CGo (go-sqlite3), Linux/Windows.

Description
No description provided
Readme AGPL-3.0 83 MiB
Languages
Go 74.6%
JavaScript 12.8%
HTML 3.9%
CSS 3.1%
Python 2.4%
Other 3.1%