Merge commit 'refs/tmp/gmain' into ci/boot-graph-tests

This commit is contained in:
JianFeeeee
2026-10-24 13:34:57 +08:00
300 changed files with 5310 additions and 939 deletions

View File

@ -1,4 +1,4 @@
# HomeAgent 主仓 CI。 # HologramAgent 主仓 CI。
# #
# 设计原则:**CI 里跑的每一条命令,都是本地已实测通过的命令**。 # 设计原则:**CI 里跑的每一条命令,都是本地已实测通过的命令**。
# 不写「应该有用来试试」的步骤 —— 未验证的 CI 步骤会把假红灯变成常态, # 不写「应该有用来试试」的步骤 —— 未验证的 CI 步骤会把假红灯变成常态,
@ -521,19 +521,124 @@ jobs:
csrc: csrc:
name: C infrastructure gates name: C infrastructure gates
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 20 # 门禁本身实测 0~1 秒(csrc-lint/abi/headers/sanitize/cross 各自 <1s)。
# 这 15 分钟全部是给 apt-get 的余量 —— 卡死历来只发生在装包阶段。
timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
# clang 供双编译器告警对照;gcc-aarch64 供跨架构编译门禁。 # ── 安装 C 工具链 ─────────────────────────────────────────────
# 门禁在缺工具时是显式 SKIP 而不是假通过,这里装齐以免静默降级。 #
- name: 安装 C 工具链 # ★ 加固点(2026-10-22):实测两次该 job 卡死 20 分钟并超时,
# 而**本地同一门禁 0~1 秒全通过**(make check-csrc)。卡死点全在
# apt-get:ubuntu-latest runner 上 `apt-get update` 偶发挂住不返回,
# 既没有失败也没有进展,只能等 job 级 timeout。
#
# 三重加固:
# 1. 逐包检查是否已安装 —— runner 镜像通常自带 cmake/clang,
# 已装就跳过 apt-get(多数情况直接走这条,完全不碰网络)
# 2. 需要装时给 apt-get 加自身的超时与重试,让它**自己失败**,
# 而不是把整个 job 拖到 job 级 timeout(后者报错信息毫无指向性)
# 3. 装完立即 `command -v` 复核,缺工具就红 —— 见下方"缺工具即失败"
- name: 安装 C 工具链(已装则跳过)
# 120s(update) + 180s(install) = 300s,给 5 分钟正好;
# 超出说明 timeout 命令本身没生效,此时应当红而不是继续等。
timeout-minutes: 5
run: | run: |
sudo apt-get update -qq set -uo pipefail
sudo apt-get install -y -qq cmake clang gcc-aarch64-linux-gnu # ★ 不再需要 gcc-aarch64-linux-gnu(2026-10-22)。
#
# 实测:装它要从源拉 10MB+ 的 cpp-13 等依赖,在 runner 上卡住超时
# (15:47:39 开始下 cpp-13-aarch64,180s 后仍未完成)。
# 而 csrc-cross 只需要 `-fsyntax-only`,clang 自带交叉前端即可:
# `clang --target=aarch64-linux-gnu` 会真的切到 aarch64 目标
# (__aarch64__ 宏生效、指针宽度按该架构算),足以暴露本门禁要抓的
# 架构问题。Makefile 的 csrc-cross 已按
# CC_ARM64 > clang --target > aarch64-gcc > SKIP 的优先级挑选。
need=""
command -v cmake >/dev/null || need="$need cmake"
command -v clang >/dev/null || need="$need clang"
# aarch64 的 libc 头(1.7MB,仅头文件,**不含编译器**)。
# 用它 + 已预装的 clang 交叉前端,替代 10MB+ 的完整交叉 GCC
# (实测那个会卡在下载 cpp-13-aarch64,180s 超时)。
[ -d /usr/aarch64-linux-gnu/include ] || need="$need libc6-dev-arm64-cross"
if [ -z "$need" ]; then
echo "✅ 依赖已存在,跳过 apt-get:$(command -v cmake clang | tr '\n' ' ')"
exit 0
fi
echo "需要安装:$need"
# ★ 2026-10-22 实测:加 Acquire::*Timeout=60 + Retries=3 之后仍然卡满
# 6 分钟 step 超时,且**日志里一个 apt 输出都没有** —— 因为 `-qq`
# 把一切静默了。卡死不可观测正是它拖满超时的原因:既看不到卡在哪个
# 源,也看不到到底在下载什么。
#
# 所以:
# · 去掉 `-qq`,保留可读进度(否则下次卡死仍然无从诊断)
# · 用 `timeout` 命令做外层硬超时 —— Acquire::Timeout 只管单个连接,
# 不管 apt 整体(源重试、DPkg 锁等待都不受它约束)
# · 失败后**不重试整段**:宁可红一次让人看见,也不要为偶发网络
# 抖动掩盖真实问题
APT_OPTS="-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 -o Acquire::Retries=2"
# shellcheck disable=SC2086
if ! timeout 120 sudo apt-get $APT_OPTS update; then
echo "::error::apt-get update 超时/失败(120s)—— runner 网络或源不可用"
exit 1
fi
# shellcheck disable=SC2086
if ! timeout 180 sudo apt-get $APT_OPTS install -y --no-install-recommends $need; then
echo "::error::apt-get install 超时/失败(180s)需要安装:$need"
exit 1
fi
# ── 缺工具即失败(★ 这条是本次加固的核心)────────────────────
#
# 此前门禁在缺工具时打 `[SKIP] ... exit 0` —— **CI 照样变绿**。
# 那意味着:apt-get 悄悄失败(或将来有人删掉安装步骤)⇒ 五道门禁
# 全部跳过 ⇒ job 绿 ⇒ 让人以为 C 侧有门禁,实际一个都没跑。
#
# 静默降级比红更危险:红会有人修,绿没人看。
# 所以 CI 里显式要求工具必须在场,SKIP 视为失败。
- name: 确认 C 工具链齐备(缺则失败,不接受 SKIP)
run: |
set -euo pipefail
miss=""
for t in cmake clang; do
if command -v "$t" >/dev/null 2>&1; then
echo " ✅ $t: $(command -v "$t")"
else
echo " ❌ $t 缺失" >&2
miss="$miss $t"
fi
done
[ -z "$miss" ] || {
echo "::error::C 工具链缺失:$miss —— 缺工具时门禁会 SKIP 并 exit 0,等于假装通过"
exit 1
}
# ── 跑门禁 ──────────────────────────────────────────────────
# 门禁自身 0~1 秒,给它 5 分钟绰绰有余;超出说明是异常(不是慢)。
- name: make check-csrc - name: make check-csrc
run: make check-csrc timeout-minutes: 5
run: |
set -o pipefail
make check-csrc 2>&1 | tee /tmp/csrc.log
# ★ 二次确认:任何 [SKIP] 都视为失败。
#
# Makefile 里每道门禁在缺依赖时 `echo "[SKIP] ..."; exit 0`,
# 这在本机是合理的(开发者未必装了交叉工具链),但在 CI 里
# 就等于把"没验证"伪装成"通过"。上面那步已保证工具齐备,
# 这里再拦一次是为了覆盖将来新增门禁时漏配依赖的情况。
if grep -q '\[SKIP\]' /tmp/csrc.log; then
echo "::error::门禁出现 [SKIP],说明有检查没真正执行:"
grep '\[SKIP\]' /tmp/csrc.log >&2
echo "CI 里不接受 SKIP —— 要么装上依赖,要么明确从 check-csrc 摘掉这条。" >&2
exit 1
fi
echo "✅ 全部门禁实际执行,无 SKIP"
# ── 文档站构建(mkdocs,纯 Python,无外部依赖)── # ── 文档站构建(mkdocs,纯 Python,无外部依赖)──
docs: docs:
@ -589,11 +694,11 @@ jobs:
# #
# 取值依据(实测 2026-10-21,直连不带代理,非推测): # 取值依据(实测 2026-10-21,直连不带代理,非推测):
# https://gitcode.com/JianFeeeee/HologramAgent → 200 ← 当前用这个 # https://gitcode.com/JianFeeeee/HologramAgent → 200 ← 当前用这个
# https://gitcode.com/JianFeeeee/HomeAgent → 301 ← 旧名,跳转到上面 # https://gitcode.com/JianFeeeee/HologramAgent → 301 ← 旧名,跳转到上面
# https://gitcode.com/JianFeeeee/hologramagentsdk → 200(SDK 仓,另一对象) # https://gitcode.com/JianFeeeee/hologramagentsdk → 200(SDK 仓,另一对象)
# https://gitcode.com/JianFeeeee/homeagentsdk → 404 # https://gitcode.com/JianFeeeee/homeagentsdk → 404
# #
# 背景:gitcode 侧镜像仓已由 HomeAgent 改名为 HologramAgent(与 GitHub # 背景:gitcode 侧镜像仓已由 HologramAgent 改名为 HologramAgent(与 GitHub
# 主仓同名)。旧地址仍 301 跳转,跟随后也能到达,但走重定向 ⇒ 别再用它。 # 主仓同名)。旧地址仍 301 跳转,跟随后也能到达,但走重定向 ⇒ 别再用它。
# 判定两仓同一对象:两地址 git ls-remote 的 refs 列表 md5 相同。 # 判定两仓同一对象:两地址 git ls-remote 的 refs 列表 md5 相同。
# #
@ -617,7 +722,7 @@ jobs:
with: with:
# 必须取完整历史:镜像可能在任意旧提交上,快进需要共同祖先。 # 必须取完整历史:镜像可能在任意旧提交上,快进需要共同祖先。
fetch-depth: 0 fetch-depth: 0
# ★ 改名(HomeAgent → HologramAgent)后的必然过渡态:gitcode 上的镜像仓 # ★ 改名(HologramAgent → HologramAgent)后的必然过渡态:gitcode 上的镜像仓
# 是**另一个账号下的独立仓库**,改 GitHub 仓名不会自动建出它。 # 是**另一个账号下的独立仓库**,改 GitHub 仓名不会自动建出它。
# 若不在这里显式探测,缺少目标仓时只会得到 git 的原始报错 # 若不在这里显式探测,缺少目标仓时只会得到 git 的原始报错
# ("repository not found" 混在 push 输出里),看不出是「仓没建」。 # ("repository not found" 混在 push 输出里),看不出是「仓没建」。

View File

@ -308,7 +308,7 @@ jobs:
[ "$PRE" = "true" ] && FLAGS+=(--prerelease) [ "$PRE" = "true" ] && FLAGS+=(--prerelease)
gh release create "$TAG" \ gh release create "$TAG" \
--title "$TAG" \ --title "$TAG" \
--notes "HomeAgent $VERSION --notes "HologramAgent $VERSION
产物清单与校验见 SHA256SUMS。 产物清单与校验见 SHA256SUMS。
- \`hologramagent_${VERSION}_linux_amd64.tar.gz\` — 内核 + CLI + GUI 打包 - \`hologramagent_${VERSION}_linux_amd64.tar.gz\` — 内核 + CLI + GUI 打包
@ -361,11 +361,11 @@ jobs:
# #
# 取值依据(实测 2026-10-21,直连不带代理,非推测): # 取值依据(实测 2026-10-21,直连不带代理,非推测):
# https://gitcode.com/JianFeeeee/HologramAgent → 200 ← 当前用这个 # https://gitcode.com/JianFeeeee/HologramAgent → 200 ← 当前用这个
# https://gitcode.com/JianFeeeee/HomeAgent → 301 ← 旧名,跳转到上面 # https://gitcode.com/JianFeeeee/HologramAgent → 301 ← 旧名,跳转到上面
# https://gitcode.com/JianFeeeee/hologramagentsdk → 200(SDK 仓,另一对象) # https://gitcode.com/JianFeeeee/hologramagentsdk → 200(SDK 仓,另一对象)
# https://gitcode.com/JianFeeeee/homeagentsdk → 404 # https://gitcode.com/JianFeeeee/homeagentsdk → 404
# #
# 背景:gitcode 侧镜像仓已由 HomeAgent 改名为 HologramAgent(与 GitHub # 背景:gitcode 侧镜像仓已由 HologramAgent 改名为 HologramAgent(与 GitHub
# 主仓同名)。旧地址仍 301 跳转,跟随后也能到达,但走重定向 ⇒ 别再用它。 # 主仓同名)。旧地址仍 301 跳转,跟随后也能到达,但走重定向 ⇒ 别再用它。
# 判定两仓同一对象:两地址 git ls-remote 的 refs 列表 md5 相同。 # 判定两仓同一对象:两地址 git ls-remote 的 refs 列表 md5 相同。
# #
@ -390,7 +390,7 @@ jobs:
name: linux-amd64 name: linux-amd64
path: dist path: dist
# ★ 前置检查:gitcode 镜像仓是否存在。 # ★ 前置检查:gitcode 镜像仓是否存在。
# 改名(HomeAgent → HologramAgent)后 gitcode 目标仓需要**人工新建**, # 改名(HologramAgent → HologramAgent)后 gitcode 目标仓需要**人工新建**,
# 不探的话失败会埋在 push/curl 的原始输出里,看不出是「仓没建」。 # 不探的话失败会埋在 push/curl 的原始输出里,看不出是「仓没建」。
# 语义:token 未配 = 可选项跳过(上一步);token 有但仓不存在 = 硬失败。 # 语义:token 未配 = 可选项跳过(上一步);token 有但仓不存在 = 硬失败。
# #

View File

@ -1,3 +1,7 @@
# 门禁脚本需要 bash 的 PIPESTATUS(/bin/sh=dash 不支持)。
# 没有它就无法区分"编译器失败"与"head 成功" —— 实测会让门禁假通过。
SHELL := /bin/bash
.PHONY: test-gui all build build-plain build-cli build-gui clean install test run build-static build-linux-arm64 lint fmt sync-client-versions check-client-versions check-starmap-shared check-client-protocol csrc csrc-test csrc-lint csrc-abi csrc-headers csrc-sanitize csrc-cross csrc-fuzz check-csrc check-csrc-full .PHONY: test-gui all build build-plain build-cli build-gui clean install test run build-static build-linux-arm64 lint fmt sync-client-versions check-client-versions check-starmap-shared check-client-protocol csrc csrc-test csrc-lint csrc-abi csrc-headers csrc-sanitize csrc-cross csrc-fuzz check-csrc check-csrc-full
# HOMED_TAGS 默认带 onnxruntime:发行版**默认启用**本地向量空间(与 # HOMED_TAGS 默认带 onnxruntime:发行版**默认启用**本地向量空间(与
# deploy/packaging/build.sh 保持一致)。 # deploy/packaging/build.sh 保持一致)。
@ -255,21 +259,71 @@ csrc-fuzz:
# C 侧的架构相关问题(endianness 假设、指针宽度、size_t vs int 宽度、 # C 侧的架构相关问题(endianness 假设、指针宽度、size_t vs int 宽度、
# -fsanitize 不可用)只有真的用目标编译器编一遍才会暴露。 # -fsanitize 不可用)只有真的用目标编译器编一遍才会暴露。
# 与 deploy/packaging/build.sh 的 arm64 目标共用同一套 CC 变量。 # 与 deploy/packaging/build.sh 的 arm64 目标共用同一套 CC 变量。
#
# ## 为什么首选 clang --target,而不是 aarch64-linux-gnu-gcc(2026-10-22)
#
# 这道门禁只做 `-fsyntax-only`(不链接、不运行),而 clang 自带交叉前端:
# `clang --target=aarch64-linux-gnu` 会真的切到 aarch64 目标(`__aarch64__`
# 宏生效、指针宽度按该架构计算),足以暴露本门禁要抓的那类问题
# (endianness 假设、指针/size_t 宽度、不支持的 C 扩展)。
#
# 而装 `gcc-aarch64-linux-gnu` 需要从源拉 10MB+ 的 cpp-13 等依赖 ——
# 实测在 CI runner 上会卡住超时(见 .github/workflows/ci.yml 的加固说明)。
# 既然只需要语法层验证,就不该为它引入一个会卡死的下载依赖。
#
# 优先级:CC_ARM64(显式指定) > clang --target > aarch64-linux-gnu-gcc > SKIP
#
# ★ 宿主头泄漏陷阱(CI 实测):只加 -isystem 指向 aarch64 头还不够 ——
# 宿主 /usr/include/stdint.h 会先被找到,它再 include <bits/libc-header-start.h>
# (amd64 布局),aarch64 目标下必然找不到:
# /usr/include/stdint.h:26: fatal error: 'bits/libc-header-start.h' file not found
# 所以 clang-target 模式必须 -nostdinc 剃掉全部默认路径,再按
# 「clang 内建头 → aarch64 目标头」顺序加回(见下方 ARM64_INC)。
# explicit / cross-gcc 模式用编译器自带搜索路径,不传这些。
#
# ★ 两个假通过陷阱(实测踩到,勿删):
# 1. 显式 CC_ARM64 指向不存在的命令时,`cmd | head` 的退出码是 head 的(恒 0),
# command-not-found(127) 被吞 ⇒ 门禁显示"通过"。所以循环里改用 PIPESTATUS[0],
# 且对显式指定的编译器先做 command -v 校验。
# 2. 上述 PIPESTATUS 需要 bash;Make 默认 /bin/sh=dash 不支持(见文件顶 SHELL)。
.PHONY: csrc-cross .PHONY: csrc-cross
csrc-cross: csrc-cross:
@echo "== C 侧交叉编译(linux/arm64)==" @echo "== C 侧交叉编译(linux/arm64)=="
@CC_ARM64=$${CC_ARM64:-aarch64-linux-gnu-gcc}; \ @ok=1; \
if ! command -v $$CC_ARM64 >/dev/null 2>&1; then \ if [ -n "$${CC_ARM64:-}" ]; then \
echo " [SKIP] $$CC_ARM64 不存在(未装交叉工具链)"; exit 0; \ if ! command -v "$$CC_ARM64" >/dev/null 2>&1; then \
fi; \ echo " [FAIL] CC_ARM64=$$CC_ARM64 不存在"; exit 1; \
ok=1; \
for src in $(CSRC_SRCS); do \
if ! $$CC_ARM64 $(CSRC_CFLAGS) -Werror -fsyntax-only $$src 2>&1 | head -20; then \
ok=0; \
fi; \ fi; \
CC="$$CC_ARM64"; MODE="explicit"; \
elif command -v clang >/dev/null 2>&1; then \
CC="clang --target=aarch64-linux-gnu"; MODE="clang-target"; \
elif command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \
CC="aarch64-linux-gnu-gcc"; MODE="cross-gcc"; \
else \
echo " [SKIP] 无 clang、无 aarch64-linux-gnu-gcc(未装任何交叉前端)"; exit 0; \
fi; \
\
ARM64_INC="-nostdinc -isystem $$(clang -print-resource-dir)/include"; \
for d in /usr/aarch64-linux-gnu/include /usr/include/aarch64-linux-gnu; do \
[ -d "$$d" ] && ARM64_INC="$$ARM64_INC -isystem $$d"; \
done; \
\
if [ "$$MODE" = "clang-target" ]; then \
printf '#include <string.h>\nint main(void){return 0;}\n' > /tmp/_archchk.c; \
if ! clang --target=aarch64-linux-gnu $$ARM64_INC -fsyntax-only /tmp/_archchk.c >/dev/null 2>&1; then \
echo " [SKIP] clang 交叉前端不可用(缺 aarch64 libc 头,装 libc6-dev-arm64-cross 即可)"; exit 0; \
fi; \
fi; \
\
EXTRA_INC=""; \
[ "$$MODE" = "clang-target" ] && EXTRA_INC="$$ARM64_INC"; \
for src in $(CSRC_SRCS); do \
$$CC $$EXTRA_INC $(CSRC_CFLAGS) -Werror -fsyntax-only $$src 2>&1 | head -20; \
rc=$${PIPESTATUS[0]}; \
if [ "$$rc" != "0" ]; then ok=0; fi; \
done; \ done; \
if [ "$$ok" = "1" ]; then \ if [ "$$ok" = "1" ]; then \
echo " $$CC_ARM64: 0 告警、编译通过 ✓($(words $(CSRC_SRCS)) 个源文件)"; \ echo " $$CC [$$MODE]: 0 告警、编译通过 ✓($(words $(CSRC_SRCS)) 个源文件)"; \
else \ else \
echo " [FAIL] arm64 交叉编译失败"; exit 1; \ echo " [FAIL] arm64 交叉编译失败"; exit 1; \
fi fi

View File

@ -240,7 +240,7 @@ internal/
├── config/ SQLite 配置中心 ├── config/ SQLite 配置中心
├── events/ 事件总线 ├── events/ 事件总线
└── internal/lua/adapters/ 10 个 LLM 协议适配器脚本 └── internal/lua/adapters/ 10 个 LLM 协议适配器脚本
外部插件开发见 [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) 仓库,使用 `hmapdev` 工具链开发,参考 `example/` 目录下的 Go 和 Lua 示例 外部插件开发见 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) 仓库,使用 `hmapdev` 工具链开发,参考 `example/` 目录下的 Go 和 Lua 示例
``` ```
## 项目状态 ## 项目状态
@ -318,7 +318,7 @@ internal/
> inline/small **30.4µs**、frame/small 51.5µs、inline/large 767µs、frame/large 398µs。 > inline/small **30.4µs**、frame/small 51.5µs、inline/large 767µs、frame/large 398µs。
> 保留原文不修改,以免伪造历史。 > 保留原文不修改,以免伪造历史。
**v1.1.1** — 多模态贯通**插件边界**。v1.1.0 让记忆系统支持了二进制多媒体节点,但那条链路只对内核自己开放;本版打通到插件与模型。公开 SDK 新增媒体字段与三个媒体注入接口(配套 [SDK v1.1.0](https://github.com/JianFeeeee/homeagentsdk/tree/v1.1.0),整条 1.1.x 线共用),内核实现对应四个 RPC。桥接层此前在**静默裁字段**:插件交进来的 `Confidence`/类型/`SentenceText` 全被丢弃、`Doc` 只留三个字段、`Remove` 不解引用(媒体永久算「被引用」,GC 收不掉)。`processTextInput`/`processMediaInput` 归一成一条 `processInput`,媒体路径由此获得它一直缺的去重、`no_memory`、通道 `Cleaner`、中断语义、`EventRawInput`。修掉三处真实缺陷:**用户发的图从来没出现在 WebUI 聊天记录里**(媒体路径发布 map 而订阅方断言 string)、**`memory_commit` 的 `sentence_text` 从未暴露给模型**(而它是媒体绑定链的必经环节)、**`PluginSDK` 两处并发竞态**(`-race` 实测 11 处,插件重载瞬间偶发 nil 解引用崩溃)。 **v1.1.1** — 多模态贯通**插件边界**。v1.1.0 让记忆系统支持了二进制多媒体节点,但那条链路只对内核自己开放;本版打通到插件与模型。公开 SDK 新增媒体字段与三个媒体注入接口(配套 [SDK v1.1.0](https://github.com/JianFeeeee/hologramagentsdk/tree/v1.1.0),整条 1.1.x 线共用),内核实现对应四个 RPC。桥接层此前在**静默裁字段**:插件交进来的 `Confidence`/类型/`SentenceText` 全被丢弃、`Doc` 只留三个字段、`Remove` 不解引用(媒体永久算「被引用」,GC 收不掉)。`processTextInput`/`processMediaInput` 归一成一条 `processInput`,媒体路径由此获得它一直缺的去重、`no_memory`、通道 `Cleaner`、中断语义、`EventRawInput`。修掉三处真实缺陷:**用户发的图从来没出现在 WebUI 聊天记录里**(媒体路径发布 map 而订阅方断言 string)、**`memory_commit` 的 `sentence_text` 从未暴露给模型**(而它是媒体绑定链的必经环节)、**`PluginSDK` 两处并发竞态**(`-race` 实测 11 处,插件重载瞬间偶发 nil 解引用崩溃)。
**v1.1.0** — 记忆系统支持**二进制多媒体节点**。内容寻址媒体存储(CAS + SQLite 元数据 + 磁盘 blob,`Get` always 重校 digest),贯通 L0(上下文事件)/L2(文档)/L3(图谱句子)三层,引用计数式 GC(有引用者绝不删)。视觉模型生成的描述文本是持久语义记忆,blob 只是可被容量 GC 淘汰的缓存。 **v1.1.0** — 记忆系统支持**二进制多媒体节点**。内容寻址媒体存储(CAS + SQLite 元数据 + 磁盘 blob,`Get` always 重校 digest),贯通 L0(上下文事件)/L2(文档)/L3(图谱句子)三层,引用计数式 GC(有引用者绝不删)。视觉模型生成的描述文本是持久语义记忆,blob 只是可被容量 GC 淘汰的缓存。
@ -326,7 +326,7 @@ internal/
**v0.9.0** — C ABI v2:外部插件 Stage 回调支持写回(`invoke_stage` 增加 result 输出,插件可在 OnInput/AfterToolcall/PostAction 修改 RawMessage/LLMText/ToolResults 等并同步回内核),ABI 版本随内核 minor 对齐(v0.9.x → ABIVersion=2,`version_min=1` 向后兼容旧插件)。同步修复工具循环 zen 兼容补位误伤首轮 system 上下文的问题。配套 SDK 提供增强版 sanitizer 示例(坏 UTF-8/U+FFFD/ANSI 转义全链路清洗)。**该 ABI 已随 v1.0.0 退场。** **v0.9.0** — C ABI v2:外部插件 Stage 回调支持写回(`invoke_stage` 增加 result 输出,插件可在 OnInput/AfterToolcall/PostAction 修改 RawMessage/LLMText/ToolResults 等并同步回内核),ABI 版本随内核 minor 对齐(v0.9.x → ABIVersion=2,`version_min=1` 向后兼容旧插件)。同步修复工具循环 zen 兼容补位误伤首轮 system 上下文的问题。配套 SDK 提供增强版 sanitizer 示例(坏 UTF-8/U+FFFD/ANSI 转义全链路清洗)。**该 ABI 已随 v1.0.0 退场。**
**v0.8.0** — 核心可用,插件系统增强。内置 20+ 插件,外部插件开发见 [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) 仓库。新增输入通道 `NoMemory`/`Cleaner`、`ChannelDef`、插件禁用/启用系统(CLI + WebUI),`plugindev` 工具链完成 C ABI `ChannelDef` 传递。 **v0.8.0** — 核心可用,插件系统增强。内置 20+ 插件,外部插件开发见 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) 仓库。新增输入通道 `NoMemory`/`Cleaner`、`ChannelDef`、插件禁用/启用系统(CLI + WebUI),`plugindev` 工具链完成 C ABI `ChannelDef` 传递。
## 文档 ## 文档
@ -385,7 +385,7 @@ make install # 安装到系统
**通过网络提供服务时也要向使用者提供源码**(§13 Remote Network Interaction)。 **通过网络提供服务时也要向使用者提供源码**(§13 Remote Network Interaction)。
即:任何人把改过的 HologramAgent 对外提供网络服务,都必须让该服务的使用者拿到改动后的源码。 即:任何人把改过的 HologramAgent 对外提供网络服务,都必须让该服务的使用者拿到改动后的源码。
插件与本项目通过公开 [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) 静态链接 插件与本项目通过公开 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) 静态链接
(SDK 源码会进入插件二进制),但那个仓**以 MIT 发布**——MIT 是宽松许可,拿到授权的代码不继承 (SDK 源码会进入插件二进制),但那个仓**以 MIT 发布**——MIT 是宽松许可,拿到授权的代码不继承
本项目的 AGPL。因此**外部插件不是本项目的衍生作品**,作者可自行选择许可(含闭源、商业、私有), 本项目的 AGPL。因此**外部插件不是本项目的衍生作品**,作者可自行选择许可(含闭源、商业、私有),
既不必同许可、也不受 §13 网络条款约束。第三方插件生态的安全与活跃正建立在这条之上。 既不必同许可、也不受 §13 网络条款约束。第三方插件生态的安全与活跃正建立在这条之上。

View File

@ -221,7 +221,7 @@ internal/
├── config/ SQLite config center ├── config/ SQLite config center
├── events/ Event bus ├── events/ Event bus
└── internal/lua/adapters/ 10 LLM protocol adapter scripts └── internal/lua/adapters/ 10 LLM protocol adapter scripts
External plugin development: see [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) repo, use `hmapdev` toolchain, refer to Go and Lua examples in `example/` External plugin development: see [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) repo, use `hmapdev` toolchain, refer to Go and Lua examples in `example/`
``` ```
## Project Status ## Project Status
@ -323,7 +323,7 @@ External plugin development: see [homeagentsdk](https://github.com/JianFeeeee/ho
**v1.1.1** — Multimodal reaches the **plugin boundary**. v1.1.0 gave the memory system binary **v1.1.1** — Multimodal reaches the **plugin boundary**. v1.1.0 gave the memory system binary
multimedia nodes, but that path was open only to the kernel itself; this release opens it to multimedia nodes, but that path was open only to the kernel itself; this release opens it to
plugins and the model. The public SDK gains media fields and three media injection methods plugins and the model. The public SDK gains media fields and three media injection methods
(paired with [SDK v1.1.0](https://github.com/JianFeeeee/homeagentsdk/tree/v1.1.0), (paired with [SDK v1.1.0](https://github.com/JianFeeeee/hologramagentsdk/tree/v1.1.0),
shared by the whole 1.1.x line), and the kernel implements the four matching RPCs. The bridge shared by the whole 1.1.x line), and the kernel implements the four matching RPCs. The bridge
layer had been **silently dropping fields**: `Confidence`/types/`SentenceText` handed in by a layer had been **silently dropping fields**: `Confidence`/types/`SentenceText` handed in by a
plugin were discarded, `Doc` kept only three fields, and `Remove` never released references plugin were discarded, `Doc` kept only three fields, and `Remove` never released references
@ -346,7 +346,7 @@ semantic memory; the blob is only a cache that capacity GC may evict.
**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.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 [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) repo. Added input channel `NoMemory`/`Cleaner`, `ChannelDef`, plugin disable/enable system (CLI + WebUI), `plugindev` toolchain C ABI `ChannelDef` support. **v0.8.0** — Core is functional, plugin system enhanced. 20+ built-in plugins. External plugin development via [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) repo. Added input channel `NoMemory`/`Cleaner`, `ChannelDef`, plugin disable/enable system (CLI + WebUI), `plugindev` toolchain C ABI `ChannelDef` support.
## Documentation ## Documentation
@ -408,7 +408,7 @@ with it over a network** (§13, Remote Network Interaction). Anyone running a mo
as a network service therefore has to make the modified source available to that service's users. as a network service therefore has to make the modified source available to that service's users.
Plugins are **statically linked** against this project through the public Plugins are **statically linked** against this project through the public
[homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) (the SDK source ends up inside the [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) (the SDK source ends up inside the
plugin binary), but that repository is released under **MIT** — a permissive license, so code plugin binary), but that repository is released under **MIT** — a permissive license, so code
received under it does **not** inherit this project's AGPL. External plugins are therefore **not received under it does **not** inherit this project's AGPL. External plugins are therefore **not
derivative works of this project**: authors pick their own license (closed-source, commercial or derivative works of this project**: authors pick their own license (closed-source, commercial or

View File

@ -111,5 +111,5 @@ Core functionality is operational. Plugin system and SDK are ready for independe
- Built-in plugins (13): webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq - Built-in plugins (13): webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq
- Criterion: **a plugin stays built-in only if it requires internal-SDK-only capabilities**; everything else ships as an .hmap external plugin (see ARCHITECTURE) - Criterion: **a plugin stays built-in only if it requires internal-SDK-only capabilities**; everything else ships as an .hmap external plugin (see ARCHITECTURE)
- External plugin examples ([homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) repo `example/`, both Go and Lua types): qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo - External plugin examples ([hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) repo `example/`, both Go and Lua types): qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo
- Distribution: `.hmap` plugin package format, installable via WebUI - Distribution: `.hmap` plugin package format, installable via WebUI

View File

@ -8,13 +8,20 @@
All external interaction capabilities of HologramAgent comes from plugins. Plugins interact with the kernel through `PluginSDK` (Go API). All external interaction capabilities of HologramAgent comes from plugins. Plugins interact with the kernel through `PluginSDK` (Go API).
**SDK Repository**: Plugin development tools, template code, and example plugins are hosted in the [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) repository. **SDK Repository**: Plugin development tools, template code, and example plugins are hosted in the [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) repository.
```bash ```bash
git clone https://github.com/JianFeeeee/homeagentsdk.git git clone https://github.com/JianFeeeee/hologramagentsdk.git
cd homeagentsdk cd hologramagentsdk
``` ```
> **The repo name and the module path are two different things.** The repository
> was renamed to `hologramagentsdk` in Oct 2026, but the `go.mod` of every
> **published tag** (v1.3.0 and earlier) still declares
> `module github.com/JianFeeeee/homeagentsdk`, so `import` / `go.mod` keep using
> the old module path (as in the examples below). Once the SDK ships a tag with
> the new module name, the core repo and new plugins can migrate together.
Each plugin implements a three-method interface: Each plugin implements a three-method interface:
```go ```go
@ -48,7 +55,7 @@ plugin types, and producing `.hmap` plugin bundles (the tool is named after that
### Installation ### Installation
```bash ```bash
cd homeagentsdk/tools/hmapdev cd hologramagentsdk/tools/hmapdev
go build -o hmapdev go build -o hmapdev
# Add hmapdev to PATH or use directly # Add hmapdev to PATH or use directly
# Prebuilt binaries also ship as release assets (hmapdev_linux_amd64, ...) # Prebuilt binaries also ship as release assets (hmapdev_linux_amd64, ...)
@ -844,31 +851,31 @@ Internal: records are stored in SQLite `disabled_plugins` table (`name`, `disabl
## 7. Example Plugin Reference ## 7. Example Plugin Reference
### SDK Repository Examples (`homeagentsdk/example/`) ### SDK Repository Examples (`hologramagentsdk/example/`)
| Example | Type | Features | | Example | Type | Features |
|---------|------|----------| |---------|------|----------|
| [weather](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/weather) | Go | Weather queries (wttr.in); demonstrates NoMemory/Cleaner/stage hooks/channels/text memory | | [weather](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/weather) | Go | Weather queries (wttr.in); demonstrates NoMemory/Cleaner/stage hooks/channels/text memory |
| [luademo](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/luademo) | Lua | Full-featured Lua example covering the whole v0.8.0 Lua SDK surface | | [luademo](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/luademo) | Lua | Full-featured Lua example covering the whole v0.8.0 Lua SDK surface |
| [qq](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/qq) | Go | NapCat OneBot integration, 24 tools, full input/output channel wiring | | [qq](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/qq) | Go | NapCat OneBot integration, 24 tools, full input/output channel wiring |
| [memo](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/memo) | Go | Memo management, PreAction injection + timed interrupt dual reminder | | [memo](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/memo) | Go | Memo management, PreAction injection + timed interrupt dual reminder |
| [files](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/files) | Go | File system operations, 4 write modes, sandbox isolation | | [files](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/files) | Go | File system operations, 4 write modes, sandbox isolation |
| [browser](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/browser) | Go | Web search + HTTP fetch (SSRF) + Chromium render (merged from web/webfetch) | | [browser](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/browser) | Go | Web search + HTTP fetch (SSRF) + Chromium render (merged from web/webfetch) |
| [bili](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/bili) | Go | Bilibili video download (yt-dlp) | | [bili](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/bili) | Go | Bilibili video download (yt-dlp) |
| [editdoc](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/editdoc) | Go | Office document editing and format conversion | | [editdoc](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/editdoc) | Go | Office document editing and format conversion |
| [a2a](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/a2a) | Go | Agent-to-Agent protocol | | [a2a](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/a2a) | Go | Agent-to-Agent protocol |
| [ocr](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/ocr) | Go | Offline text recognition (Tesseract) | | [ocr](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/ocr) | Go | Offline text recognition (Tesseract) |
| [sanitizer](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/sanitizer) | Go | Output sanitizer filter | | [sanitizer](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/sanitizer) | Go | Output sanitizer filter |
| [calendar](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/calendar) | Go | Calendar management | | [calendar](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/calendar) | Go | Calendar management |
| [rss](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/rss) | Go | RSS subscriptions | | [rss](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/rss) | Go | RSS subscriptions |
| [ai_image](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/ai_image) | Go | AI image generation | | [ai_image](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/ai_image) | Go | AI image generation |
| [music](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/music) | Go | Music playback | | [music](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/music) | Go | Music playback |
| [vikunja](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/vikunja) | Go | Vikunja task management (projects/tasks/labels CRUD) | | [vikunja](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/vikunja) | Go | Vikunja task management (projects/tasks/labels CRUD) |
| [vanblog](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/vanblog) | Go | VanBlog publishing and management | | [vanblog](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/vanblog) | Go | VanBlog publishing and management |
| [deepsearch](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/deepsearch) | Go | Multi-round deep search (progressive focus + cited summary) | | [deepsearch](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/deepsearch) | Go | Multi-round deep search (progressive focus + cited summary) |
| [acp](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/acp) | Go | Agent Client Protocol (external editors/IDEs drive this agent) | | [acp](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/acp) | Go | Agent Client Protocol (external editors/IDEs drive this agent) |
| [recoverydiag](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/recoverydiag) | Go | Five-part fault diagnosis (triage / sqlite check / log signatures / diff / ranked conclusions); core plugin of failback mode | | [recoverydiag](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/recoverydiag) | Go | Five-part fault diagnosis (triage / sqlite check / log signatures / diff / ranked conclusions); core plugin of failback mode |
| [plugindev](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/plugindev) | Go | Plugin scaffolding: generate, build, install — lets the agent develop plugins itself | | [plugindev](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/plugindev) | Go | Plugin scaffolding: generate, build, install — lets the agent develop plugins itself |
### Built-in Plugins ### Built-in Plugins
@ -903,4 +910,4 @@ Internal: records are stored in SQLite `disabled_plugins` table (`name`, `disabl
*Want to understand the project goals? See [OVERVIEW.md](OVERVIEW.md).* *Want to understand the project goals? See [OVERVIEW.md](OVERVIEW.md).*
*Want to understand the architecture? See [ARCHITECTURE.md](ARCHITECTURE.md).* *Want to understand the architecture? See [ARCHITECTURE.md](ARCHITECTURE.md).*
*SDK repository and development tools? See [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk).* *SDK repository and development tools? See [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk).*

View File

@ -10,7 +10,17 @@ HologramAgent 的认知架构由三个核心子系统构成:事件循环(eve
**阶段管道(StageHost)** 管理两类注册:工具定义(ToolDef)与阶段处理器(StageHandler)。ToolDef 包含 `NoMemory bool` 和 `Cleaner func(string) string` 两个可选的记忆控制字段:`NoMemory=true` 时工具输出不参与向量化/jieba/蒸馏计算(原文保留);`Cleaner` 在输出进入计算层前执行过滤(如提取 JSON 的 `content` 字段)。两者均不修改原文,只影响计算层输入。`RegisterTool` 拒绝同名注册,推断工具所属插件名,并维护工具到插件的映射表 `toolPlugins`。`RegisterStage` 将处理器追加至对应阶段的处理器列表。触发阶段执行时(`RunStage`),**所有已注册处理器通过 goroutine 并行执行**,共享同一 `*StageContext` 实例(通过 `sync.RWMutex` 保护并发访问)。单个处理器的 panic 被独立恢复,不影响其他处理器。短路语义通过检查 `ctx.Response != nil` 实现——任一阶段处理器可设置此值提前终止当前链路。工具执行 `ExecuteTool` 内置 panic 恢复与栈追踪记录。`UnregisterPluginTools` 在插件热重载时移除对应工具集。 **阶段管道(StageHost)** 管理两类注册:工具定义(ToolDef)与阶段处理器(StageHandler)。ToolDef 包含 `NoMemory bool` 和 `Cleaner func(string) string` 两个可选的记忆控制字段:`NoMemory=true` 时工具输出不参与向量化/jieba/蒸馏计算(原文保留);`Cleaner` 在输出进入计算层前执行过滤(如提取 JSON 的 `content` 字段)。两者均不修改原文,只影响计算层输入。`RegisterTool` 拒绝同名注册,推断工具所属插件名,并维护工具到插件的映射表 `toolPlugins`。`RegisterStage` 将处理器追加至对应阶段的处理器列表。触发阶段执行时(`RunStage`),**所有已注册处理器通过 goroutine 并行执行**,共享同一 `*StageContext` 实例(通过 `sync.RWMutex` 保护并发访问)。单个处理器的 panic 被独立恢复,不影响其他处理器。短路语义通过检查 `ctx.Response != nil` 实现——任一阶段处理器可设置此值提前终止当前链路。工具执行 `ExecuteTool` 内置 panic 恢复与栈追踪记录。`UnregisterPluginTools` 在插件热重载时移除对应工具集。
**上下文窗口(RelevanceContext)** 维护一个按时间排序的事件列表。`Append` 在录入前经 `textForVector` 聚合工具输出:按 `NoMemory` 跳过、`Cleaner` 过滤(插件为各自工具注册的清洗函数,如 QQ 外置插件剥离工具调用模板),最后经 `CleanText` 做基本空白规范化,再通过三分支向量策略(agent 事件用 Response,用户事件用 Input,cold_storage 用 Input+Response)计算嵌入向量。`Prune` 在事件数超过 `topK` 时触发,**无条件保护最近 10 条事件不被裁剪**(recency bias),对剩余候选事件计算与当前输入的 CosineSimilarity,按评分降序保留 `topK - 10` 条(下限为 0),之后按时间戳重排序。裁剪出的事件中,过滤掉 `agentcli` 和 `terminal` 来源后,其余通过 `docStore.ContextToDoc` 归档至 Document 层,保留原始时间戳。持久化采用 5 秒防抖写入磁盘 JSON 文件。 **上下文窗口(RelevanceContext)** 维护一个按时间排序的事件列表。`Append` 在录入前经 `textForVector` 聚合工具输出:按 `NoMemory` 跳过、`Cleaner` 过滤(插件为各自工具注册的清洗函数),最后经 `CleanText` 做基本空白规范化,再通过三分支向量策略(agent 事件用 Response,用户事件用 Input,cold_storage 用 Input+Response)计算嵌入向量。`Prune` 在事件数超过 `topK` 时触发,**无条件保护最近 10 条事件不被裁剪**(recency bias),对剩余候选事件计算与当前输入的 CosineSimilarity,按评分降序保留 `topK - 10` 条(下限为 0),之后按时间戳重排序。裁剪出的事件通过 `docStore.ContextToDoc` **迁移**至 Document 层:事件连同它持有的块一起下沉,保留原始时间戳与完整正文。持久化采用 5 秒防抖写入磁盘 JSON 文件。
> ★ **哪些事件不该进 L2,由插件自己声明,内核不认插件名**。
>
> 旧版这里写的是「过滤掉 `agentcli` 和 `terminal` 来源后归档」——那是在内核里
> 硬编码插件行为,已废除。正确姿势是插件在注册时就声明:
> - `ToolDef.NoMemory` / `ToolDef.Cleaner`:工具输出不进计算层
> - `ChannelDef.NoMemory` / `ChannelDef.Cleaner`:该通道的输入不进计算层
> - `InjectOptions.NoMemory`:本次注入不参与记忆
>
> 内核只消费这些**声明**(已由 `textForVector` 实现),不认识任何具体插件。
**工具定义聚合自五个来源**:IOManager 注册的插件工具;StageHost 注册的 SDK 插件工具;Indexer 提供的记忆索引工具;内置条件工具(依据 memory / knowledge / docStore / social / pluginReg / providerManager 等模块的非空状态选择性添加,包括记忆操作、知识检索、文档查询、社交网络、插件重载、子代理生成、输出通道工具、LLM 源切换等);以及按 `pendingMedia` 状态添加的媒体处理工具。`buildToolDefs()` 在每次 process 周期中重新聚合所有这些来源。 **工具定义聚合自五个来源**:IOManager 注册的插件工具;StageHost 注册的 SDK 插件工具;Indexer 提供的记忆索引工具;内置条件工具(依据 memory / knowledge / docStore / social / pluginReg / providerManager 等模块的非空状态选择性添加,包括记忆操作、知识检索、文档查询、社交网络、插件重载、子代理生成、输出通道工具、LLM 源切换等);以及按 `pendingMedia` 状态添加的媒体处理工具。`buildToolDefs()` 在每次 process 周期中重新聚合所有这些来源。
@ -83,35 +93,64 @@ eventLoop() → processTextInput()
## 三层记忆 ## 三层记忆
### 记忆块是唯一载体,层是持有关系
三层不是三种数据结构,而是**同一批记忆块的三层持有关系**:
```
记忆块 MemoryBlock(union)
├─ 文本块:负载在 Text
└─ 媒体块:负载在 Data(配 MIME/宽高/尺寸)
↑ 同一个块 ID 在 context → doc → graph 全程不变
↑ 块自带 vector + fingerprint:向量只算一次,跨层复用
```
| 层 | 承载单元 | 与模型的关系 | 容量 | 流动性 |
|---|---|---|---|---|
| **L1 Context** | 事件(内联正文 + 块) | **直接投喂**(进 system prompt 时序) | 最近 n 条 / 上限 m 条 | 超限时按相似度迁出 |
| **L2 Document** | 事件(内联正文 + 块) | **不直接投喂**(需召回才进 L1) | 无上限 | 冷到阈值后蒸馏进 L3 |
| **L3 Graph** | 三元组(节点 + 关系边),节点即记忆块 | 不投喂(经召回进入 L1) | 持久 | 不流出 |
★ **L1 与 L2 结构完全同构**,唯一区别是「是否被直接投喂给模型」。
L2 不是 L1 的摘要层,也不是另一种数据形态——它就是同样的事件 + 同样的块,只是不直接进模型视野。
★ **正文(含多媒体正文)内联在块里**:文本块的 `Text` 就是正文,媒体块的 `Data` 就是字节。
不存在「块 + 外部正文」的分离形态,也不存在靠渲染字符串承载原文的路径。
事件是块的容器,不是块的替代品。
★ **任何一层都不压缩**。视图(检索摘要、给模型看的渲染)可以是压缩的,但视图不是记忆。
### 记忆流转 ### 记忆流转
``` ```
① Context (工作窗口) ① Context (工作窗口 — 直接投喂)
RelevanceContext — 内存 events[] + JSON持久化 RelevanceContext — 内存 events[] + JSON持久化
每条事件:Timestamp / Source / Input / Response(完整) / ToolsUsed / ToolResults / Blocks
Append: 每次输入, CleanText → 三分支向量(textForVector) Append: 每次输入, CleanText → 三分支向量(textForVector)
agent事件→Response, 用户事件→Input, cold_storage→Input+Response agent事件→Response, 用户事件→Input, cold_storage→Input+Response
向量层级:统一多模态空间(主,带 fingerprint)→ StaticEmbedder 词嵌入 → TF-IDF(回退) 向量层级:统一多模态空间(主,带 fingerprint)→ StaticEmbedder 词嵌入 → TF-IDF(回退)
Prune: DenseCosine(仅同指纹才比较)→ 退化 StaticEmbedder CosineSimilarity;保留 topK + 最近10条 Prune: DenseCosine(仅同指纹才比较)→ 退化 StaticEmbedder CosineSimilarity;保留 topK + 最近10条
├── 保留 → timeline → 按时间排序 → system prompt ├── 保留 → timeline → 按时间排序 → system prompt
└── 低分 → Document 层归档 (原始时间戳) └── 低分 → Document 层归档(迁移:事件连同块一起下沉,时间戳与正文不变)
Save: 5s debounce 写盘 Save: 5s debounce 写盘
↓ Prune 归档 ↑ LLM 主动召回 ↓ Prune 归档(迁移) ↑ 召回(迁回)
② Document (文件记忆) ② Document (文件记忆 — 不直接投喂,结构与 L1 同构)
DocStore — JSON文件 + 稠密向量(统一多模态空间;dense_fp 须与当前空间同指纹,不符即重算;兜底: StaticEmbedder / TF-IDF InvertedIndex) DocStore — 每篇文档 = 检索元数据 + 事件列表(内联正文 + 块)
写入: Prune归档 / doc_commit / Graph快照(syncGraphToDocs) 写入: Prune归档 / doc_commit / Graph快照(syncGraphToDocs)
读取: 读取:
├── 自动注入: Query(input, top3) → 同一向量空间下相似度摘要 → 【相关记忆文档】→ system prompt (只读) ├── 自动预填充: Query(input, top3) → 同一向量空间下相似度摘要 → 【相关记忆文档】→ system prompt (只读,不动层)
└── LLM主动: doc_query → Consume(读取并删除) └── LLM主动: doc_query → Consume(取走)
→ 逐条 context.Append{Timestamp: d.CreatedAt, Source: "cold_storage"} → 逐条按各自原始时间戳把事件插回 L1 时序
→ 文档以原始时间戳写入 context 时间线, 从 docStore 删除 → 块随事件迁回(同一块 ID,向量不重算),L2 不再持有
冷化: FindColdDocs(72h, ≤2次访问) → docToTriples → Graph 冷化: FindColdDocs(72h, ≤2次访问) → docToTriples → Graph
↓ 冷文档蒸馏 ↑ 自动召回 ↓ 冷文档蒸馏 ↑ 自动召回
③ Graph (图数据库) ③ Graph (图数据库 — 顶层仍是三元组,节点即记忆块)
SQLite — entities + relations 表 SQLite — memory_blocks(节点) + memory_block_edges(边)
三元组 = 【节点】【关系边】【节点】;putTripleBlocksTx 落为 src --edge_type--> dst
写入: memory_commit / 冷文档蒸馏 / Pipeline 规则蒸馏 / memory_merge 写入: memory_commit / 冷文档蒸馏 / Pipeline 规则蒸馏 / memory_merge
读取: 读取:
├── 自动召回: Indexer.BuildContext(input) ├── 自动召回: Indexer.BuildContext(input)
@ -174,21 +213,33 @@ eventLoop() → processTextInput()
- 向量化前统一经 `CleanText` 去模版噪声 - 向量化前统一经 `CleanText` 去模版噪声
- 三分支 `textForVector`:agent 事件用 Response、用户事件用 Input、cold_storage 用 Input+Response - 三分支 `textForVector`:agent 事件用 Response、用户事件用 Input、cold_storage 用 Input+Response
- 预训练词嵌入 `StaticEmbedder` → CosineSimilarity,模型不可用时自动回退 TF-IDF - 预训练词嵌入 `StaticEmbedder` → CosineSimilarity,模型不可用时自动回退 TF-IDF
- 保护最近 10 条记录免于淘汰,超出部分按相关性排序归档到文档记忆 - 保护最近 10 条记录免于淘汰,超出部分按相关性排序**迁移**到文档记忆
- 归档事件以原始时间戳写入文档记忆,后续 `doc_query` 召回时按原始时间戳插回时序 - 迁移是事件**连同它持有的块**一起下沉,时间戳与正文不变;源事件清空块(同一块不同时留两层)
### Document 层 ### Document 层
`internal/memory/document/document.go` — `Store` `internal/memory/document/document.go` — `Store`
- 消费即删模式:`doc_query` 检索到后删除
- 双路召回:与 Context 共享的 StaticEmbedder 语义向量搜索 + jieba 关键词提取(模型未加载时回退 char-bigram TF-IDF) **与 Context 同构,只是不直接投喂给模型**:
- 每篇文档 = 检索元数据(`ID`/`Summary`/`Vector`/`DenseVec`/`DenseFP`/`AccessCount`)
+ **事件列表**(内联正文 + 块)
- 正文内联在块里:文本块的 `Text` 是正文,媒体块的 `Data` 是字节
- `Doc.Content` 只是**渲染视图**(供摘要/人工阅读/SDK 兼容),不是原文载体
- 自动预填充:与 Context 共享的向量空间做相似度检索 → 摘要进 system prompt(**只读**,不动层)
- 主动召回:`doc_query` 取走后,**逐条按各自原始时间戳**把事件插回 L1 时序,块随事件迁回
- 冷化:`FindColdDocs(72h, ≤2次访问)` → `docToTriples` → Graph
### Graph 层 ### Graph 层
`internal/memory/graph.go` — `GraphDB` `internal/memory/graph.go` — `GraphDB`
- SQLite WAL 模式,两张表(驱动:mattn/go-sqlite3,CGo)
- `Commit(triples)` — UPSERT entities + INSERT relations **顶层结构仍是三元组,变的是节点的物理表示**:
- `Recall(keywords, depth)` — 关键词 LIKE 搜索 + BFS 遍历 - SQLite WAL 模式(驱动:mattn/go-sqlite3,CGo)
- `memory_blocks`(节点,带 vector + fingerprint + modality)+ `memory_block_edges`(边)
- 三元组 = 【节点】【关系边】【节点】:`putTripleBlocksTx` 落为 `src --edge_type--> dst`
- `entities` / `relations` 旧表是**存量过渡路径**,逐步退场(退场的是节点存储,不是三元组概念)
- `Commit(triples)` — 写入时直接块化为节点 + 边
- `Recall(keywords, depth)` — 关键词检索 + BFS 遍历
### 记忆工具(LLM 可直接调用) ### 记忆工具(LLM 可直接调用)

View File

@ -24,12 +24,20 @@ CLI 需要终端面板时,**订阅事件总线**(`EventTerminalOutput` + `Ev
自己攒视图,而不是去读内核里的某个状态组件。内核只提供事件总线与插件注册面—— 自己攒视图,而不是去读内核里的某个状态组件。内核只提供事件总线与插件注册面——
新增一类能力不需要改内核,也不会在核心里堆出第二套业务真相。 新增一类能力不需要改内核,也不会在核心里堆出第二套业务真相。
**三层记忆架构** — 通过分级存储策略管理 Agent 长周期运行中的信息留存: **三层记忆架构** — 三层不是三种数据结构,而是**同一批记忆块的三层持有关系**:
- **Context 层**:内存中预训练词嵌入评分的事件窗口(StaticEmbedder 词向量 → CosineSimilarity,TF-IDF 回退),实时维护最近上下文,低相关性事件自动下沉到下一层 - **Context 层(L1,直接投喂)**:内存中预训练词嵌入评分的事件窗口(StaticEmbedder 词向量 → CosineSimilarity,TF-IDF 回退),实时维护最近上下文,低相关性事件自动下沉到下一层
- **Document 层**:JSON 文件 + TF-IDF 向量索引的临时记忆,支持显式提交和隐式归档,冷数据蒸馏到 Graph - **Document 层(L2,不直接投喂)**:与 L1 **结构同构**——同样的事件 + 同样的块,只是不直接进模型视野;支持显式提交和隐式归档,冷数据蒸馏到 Graph
- **Graph 层**:SQLite 图数据库,持久化实体(entities)和关系(relations),BFS 遍历召回,蒸馏管道从对话中提取三元组 - **Graph 层(L3,持久)**:SQLite 图数据库,**顶层仍是三元组**(节点 + 关系边),但节点从 `entities` 表升级为 `memory_blocks`;BFS 遍历召回,蒸馏管道从对话中提取三元组
三层递进:上下文 → 冷归档 → 长期图记忆,构成从短期到持久的信息衰减与整合管道。 ★ **L1 与 L2 唯一区别是「是否被直接投喂给模型」**:L2 不是 L1 的摘要层,也不是
另一种数据形态。召回 = 把事件(连同它持有的块)从“不投喂”的持有层**迁回**“投喂”的持有层。
★ **正文(含多媒体正文)内联在块里**:文本块的 `Text` 是正文,媒体块的 `Data` 是字节。
不存在「块 + 外部正文」的分离形态。**任何一层都不压缩** —— 视图(检索摘要、渲染结果)
可以是压缩的,但视图不是记忆。
三层流动:上下文 → 冷归档 → 长期图记忆,构成从短期到持久的信息衰减与整合管道;
每一次都是**迁移**(块 ID 与向量不变,只换持有层),不是复制。
**媒体记忆(v1.2.0 起:一等记忆块)** — 图片/音频不是附属物,而是记忆的一等节点: **媒体记忆(v1.2.0 起:一等记忆块)** — 图片/音频不是附属物,而是记忆的一等节点:
- **内容寻址存储(CAS)**:digest 寻址,元数据在 SQLite、blob 在磁盘(两级分桶),相同字节只存一份, - **内容寻址存储(CAS)**:digest 寻址,元数据在 SQLite、blob 在磁盘(两级分桶),相同字节只存一份,
@ -101,5 +109,5 @@ CLI 需要终端面板时,**订阅事件总线**(`EventTerminalOutput` + `Ev
- 内置插件(13 个):webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq - 内置插件(13 个):webui / cli / agentcli / cfgmgr / healthcheck / kbtree / multimodal / pluginmgr / clawhubadapter / remotedevice / skillmgr / seemedia / seq
- 判定标准:**只有必须使用内部 SDK 能力(公开 SDK 没有的接口面)的插件才内置**,其余一律外置为 .hmap 插件(详见 ARCHITECTURE「内置插件的判定标准」) - 判定标准:**只有必须使用内部 SDK 能力(公开 SDK 没有的接口面)的插件才内置**,其余一律外置为 .hmap 插件(详见 ARCHITECTURE「内置插件的判定标准」)
- 外部插件示例([homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) 仓库 `example/`,含 Go 和 Lua 两种类型):qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo - 外部插件示例([hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) 仓库 `example/`,含 Go 和 Lua 两种类型):qq / files / a2a / ai_image / bili / browser / calendar / editdoc / memo / music / ocr / rss / sanitizer / weather / luademo
- 打包分发:`.hmap` 插件包格式,通过 WebUI 安装 - 打包分发:`.hmap` 插件包格式,通过 WebUI 安装

View File

@ -9,13 +9,19 @@
HologramAgent 的所有外部交互能力都来自插件。插件通过 `PluginSDK`(Go API)与内核交互。 HologramAgent 的所有外部交互能力都来自插件。插件通过 `PluginSDK`(Go API)与内核交互。
**SDK 仓库**:插件开发工具、模板代码和示例插件统一托管在 **SDK 仓库**:插件开发工具、模板代码和示例插件统一托管在
[homeagentsdk](https://github.com/JianFeeeee/homeagentsdk) 仓库。 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk) 仓库。
```bash ```bash
git clone https://github.com/JianFeeeee/homeagentsdk.git git clone https://github.com/JianFeeeee/hologramagentsdk.git
cd homeagentsdk cd hologramagentsdk
``` ```
> **仓名与 module 路径不是一回事**:仓 2026-10 已改名为 `hologramagentsdk`,
> 但**已发布 tag(v1.3.0 及更早)的 go.mod 里 module 仍是**
> `github.com/JianFeeeee/homeagentsdk`,所以 `import` 与 `go.mod` 里继续写旧
> module 路径(下面示例即如此)。等 SDK 发新 tag 把 module 改成新名后,
> 主仓与新插件再一起改 import。
每个插件实现一个三方法接口: 每个插件实现一个三方法接口:
```go ```go
@ -49,7 +55,7 @@ type Plugin interface {
### 安装 ### 安装
```bash ```bash
cd homeagentsdk/tools/hmapdev cd hologramagentsdk/tools/hmapdev
go build -o hmapdev go build -o hmapdev
# 将 hmapdev 加入 PATH 或直接使用 # 将 hmapdev 加入 PATH 或直接使用
# 也可从 SDK 的 release 附件下载预编译二进制(hmapdev_linux_amd64 等) # 也可从 SDK 的 release 附件下载预编译二进制(hmapdev_linux_amd64 等)
@ -830,31 +836,31 @@ pmgr.ReloadPlugins() // 重载所有插件
## 七、示例插件参考 ## 七、示例插件参考
### SDK 仓库示例(`homeagentsdk/example/`) ### SDK 仓库示例(`hologramagentsdk/example/`)
| 示例 | 类型 | 特点 | | 示例 | 类型 | 特点 |
|------|------|------| |------|------|------|
| [weather](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/weather) | Go | 天气查询(wttr.in),演示 NoMemory/Cleaner/阶段钩子/通道/文本记忆 | | [weather](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/weather) | Go | 天气查询(wttr.in),演示 NoMemory/Cleaner/阶段钩子/通道/文本记忆 |
| [luademo](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/luademo) | Lua | Lua 全功能示例,覆盖 v0.8.0 Lua SDK 全部 API 面 | | [luademo](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/luademo) | Lua | Lua 全功能示例,覆盖 v0.8.0 Lua SDK 全部 API 面 |
| [qq](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/qq) | Go | NapCat OneBot 对接,24 个工具,输入/输出通道完整对接 | | [qq](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/qq) | Go | NapCat OneBot 对接,24 个工具,输入/输出通道完整对接 |
| [memo](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/memo) | Go | 备忘管理,PreAction 注入 + 定时打断双提醒 | | [memo](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/memo) | Go | 备忘管理,PreAction 注入 + 定时打断双提醒 |
| [files](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/files) | Go | 文件系统操作,4 种写入模式,沙箱隔离 | | [files](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/files) | Go | 文件系统操作,4 种写入模式,沙箱隔离 |
| [browser](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/browser) | Go | 网络搜索、网页抓取(SSRF)、浏览器渲染(合并自 web/webfetch) | | [browser](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/browser) | Go | 网络搜索、网页抓取(SSRF)、浏览器渲染(合并自 web/webfetch) |
| [bili](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/bili) | Go | B 站视频下载(yt-dlp) | | [bili](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/bili) | Go | B 站视频下载(yt-dlp) |
| [editdoc](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/editdoc) | Go | Office 文档编辑与格式转换 | | [editdoc](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/editdoc) | Go | Office 文档编辑与格式转换 |
| [a2a](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/a2a) | Go | Agent-to-Agent 协议 | | [a2a](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/a2a) | Go | Agent-to-Agent 协议 |
| [ocr](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/ocr) | Go | 离线文字识别(Tesseract) | | [ocr](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/ocr) | Go | 离线文字识别(Tesseract) |
| [sanitizer](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/sanitizer) | Go | 输出清洗过滤器 | | [sanitizer](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/sanitizer) | Go | 输出清洗过滤器 |
| [calendar](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/calendar) | Go | 日历管理 | | [calendar](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/calendar) | Go | 日历管理 |
| [rss](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/rss) | Go | RSS 订阅 | | [rss](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/rss) | Go | RSS 订阅 |
| [ai_image](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/ai_image) | Go | AI 图片生成 | | [ai_image](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/ai_image) | Go | AI 图片生成 |
| [music](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/music) | Go | 音乐播放 | | [music](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/music) | Go | 音乐播放 |
| [vikunja](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/vikunja) | Go | Vikunja 任务管理对接(项目/任务/标签 CRUD) | | [vikunja](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/vikunja) | Go | Vikunja 任务管理对接(项目/任务/标签 CRUD) |
| [vanblog](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/vanblog) | Go | VanBlog 博客发布与管理 | | [vanblog](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/vanblog) | Go | VanBlog 博客发布与管理 |
| [deepsearch](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/deepsearch) | Go | 多轮深度检索(逐层聚焦 + 引用汇总) | | [deepsearch](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/deepsearch) | Go | 多轮深度检索(逐层聚焦 + 引用汇总) |
| [acp](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/acp) | Go | Agent Client Protocol 对接(外部编辑器/IDE 驱动本 agent) | | [acp](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/acp) | Go | Agent Client Protocol 对接(外部编辑器/IDE 驱动本 agent) |
| [recoverydiag](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/recoverydiag) | Go | 故障诊断五件套(分诊/sqlite 校验/日志签名/diff/结论排序),failback 模式的核心插件 | | [recoverydiag](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/recoverydiag) | Go | 故障诊断五件套(分诊/sqlite 校验/日志签名/diff/结论排序),failback 模式的核心插件 |
| [plugindev](https://github.com/JianFeeeee/homeagentsdk/tree/main/example/plugindev) | Go | 插件脚手架:生成工程、构建、安装,供 agent 自助开发插件 | | [plugindev](https://github.com/JianFeeeee/hologramagentsdk/tree/main/example/plugindev) | Go | 插件脚手架:生成工程、构建、安装,供 agent 自助开发插件 |
### 内置插件 ### 内置插件
@ -884,4 +890,4 @@ pmgr.ReloadPlugins() // 重载所有插件
*了解项目整体目标?查看 [OVERVIEW.md](OVERVIEW.md)。* *了解项目整体目标?查看 [OVERVIEW.md](OVERVIEW.md)。*
*了解技术架构?查看 [ARCHITECTURE.md](ARCHITECTURE.md)。* *了解技术架构?查看 [ARCHITECTURE.md](ARCHITECTURE.md)。*
*SDK 仓库与开发工具?查看 [homeagentsdk](https://github.com/JianFeeeee/homeagentsdk)。* *SDK 仓库与开发工具?查看 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk)。*

View File

@ -1,8 +1,8 @@
HomeAgent 是完全独立自研的新一代 Agent 框架。 HologramAgent 是完全独立自研的新一代 Agent 框架。
## 架构总览 ## 架构总览
HomeAgent 采用内核 + 插件双层架构: HologramAgent 采用内核 + 插件双层架构:
``` ```
外部输入(QQ/Web/CLI) 外部输入(QQ/Web/CLI)

View File

@ -2,7 +2,7 @@
name: commondev-discipline name: commondev-discipline
description: commondev 主管式多仓库开发流水线的使用与开发纪律。当你要为某个工程开一条分支、把工程派给一个下属 agent、或用 pi 拉起执行 agent 干活时必读。核心动作:为每个工程对话新建驻留式子 agent 并把该工程的一对通道划给它(否则所有工程都挤在根 agent 上)。**git 托管平台的操作(Gitea/GitHub/GitLab)不在插件里,而在本文** —— 用 enc_shell + 保存的凭据调 curl/CLI 完成。也含 hmap 插件开发与部署纪律。 description: commondev 主管式多仓库开发流水线的使用与开发纪律。当你要为某个工程开一条分支、把工程派给一个下属 agent、或用 pi 拉起执行 agent 干活时必读。核心动作:为每个工程对话新建驻留式子 agent 并把该工程的一对通道划给它(否则所有工程都挤在根 agent 上)。**git 托管平台的操作(Gitea/GitHub/GitLab)不在插件里,而在本文** —— 用 enc_shell + 保存的凭据调 curl/CLI 完成。也含 hmap 插件开发与部署纪律。
version: 2.0.0 version: 2.0.0
author: HomeAgent author: HologramAgent
--- ---
# commondev 纪律(主管模式 · 工程对话 · 平台操作) # commondev 纪律(主管模式 · 工程对话 · 平台操作)
@ -176,7 +176,7 @@ enc_shell(command="curl -s -X POST -H \"Authorization: token @{t}\" -H 'Content-
| 角色 | 是谁 | 干什么 | | 角色 | 是谁 | 干什么 |
|---|---|---| |---|---|---|
| **人** | 老板 | 只与主管对接 | | **人** | 老板 | 只与主管对接 |
| **主管** | 你(长期驻留的 HomeAgent 主 agent) | 拆任务、**定义判据**、复审、决定合入 | | **主管** | 你(长期驻留的 HologramAgent 主 agent) | 拆任务、**定义判据**、复审、决定合入 |
| **执行 agent** | 按工程拉起的一个 pi / dsh 实例 | 在某棵工作树里干活 | | **执行 agent** | 按工程拉起的一个 pi / dsh 实例 | 在某棵工作树里干活 |
| **commondev 插件** | 外部独立插件 | 仓库/凭据/分支/PR/测试,全部机械化操作 | | **commondev 插件** | 外部独立插件 | 仓库/凭据/分支/PR/测试,全部机械化操作 |

View File

@ -2,5 +2,5 @@
"name": "commondev-discipline", "name": "commondev-discipline",
"description": "commondev 主管式多仓库开发流水线的使用与开发纪律:为每个工程对话新建驻留子并划入其通道、投递必须落同一 session、复核必须含变异阶段;以及 hmap 插件开发纪律(plg.json、SDK 版本、产物形态、Serial/ParallelSafe、外部插件能力边界、凭据与孤儿清理)", "description": "commondev 主管式多仓库开发流水线的使用与开发纪律:为每个工程对话新建驻留子并划入其通道、投递必须落同一 session、复核必须含变异阶段;以及 hmap 插件开发纪律(plg.json、SDK 版本、产物形态、Serial/ParallelSafe、外部插件能力边界、凭据与孤儿清理)",
"version": "1.0.0", "version": "1.0.0",
"author": "HomeAgent" "author": "HologramAgent"
} }

View File

@ -1,16 +1,16 @@
--- ---
name: knowledge-base name: knowledge-base
description: 按分类树检索 HomeAgent 知识库。当需要查阅项目知识、架构约定、历史决策,或用户提到"知识库/knowledge/知识库有哪些/这个项目的约定是什么"时使用。支持树形导航、分类内检索、以图搜知识。 description: 按分类树检索 HologramAgent 知识库。当需要查阅项目知识、架构约定、历史决策,或用户提到"知识库/knowledge/知识库有哪些/这个项目的约定是什么"时使用。支持树形导航、分类内检索、以图搜知识。
version: 1.1.0 version: 1.1.0
author: HomeAgent author: HologramAgent
--- ---
## Usage ## Usage
先看树定位分类,再做定向检索。接口由 HomeAgent 的 `kbtree` 插件提供, 先看树定位分类,再做定向检索。接口由 HologramAgent 的 `kbtree` 插件提供,
只读、需 token。详见下方"接入"。 只读、需 token。详见下方"接入"。
HomeAgent 知识库按**分类树**组织(`tech/go/并发`、`life/sleep` …)。 HologramAgent 知识库按**分类树**组织(`tech/go/并发`、`life/sleep` …)。
### 何时用 ### 何时用
@ -102,7 +102,7 @@ curl -s -H "$AUTH" "$BASE/search?q=goroutine&limit=5"
若知识条目挂了图片,它在**多模态统一空间**里有向量,能被图片本身检索到。 若知识条目挂了图片,它在**多模态统一空间**里有向量,能被图片本身检索到。
前提是宿主已接入多模态向量 provider(否则只是记录了媒体,不参与召回)。 前提是宿主已接入多模态向量 provider(否则只是记录了媒体,不参与召回)。
判断是否就绪:HomeAgent 自身的知识库接口会返回稠密路状态 判断是否就绪:HologramAgent 自身的知识库接口会返回稠密路状态
(`dense.enabled` / `dense.ready` / `dense.total`)。若为未启用, (`dense.enabled` / `dense.ready` / `dense.total`)。若为未启用,
**不要承诺"能以图搜"**。 **不要承诺"能以图搜"**。
@ -132,13 +132,13 @@ curl -s -H "$AUTH" "$BASE/search?q=goroutine&limit=5"
- **分类不存在返回 404**,并在 `categories` 字段里附上现有分类 —— 用它自查 - **分类不存在返回 404**,并在 `categories` 字段里附上现有分类 —— 用它自查
拼写。 拼写。
- **`items=0` 只是不要正文**,`total_count` 仍准确,可用于判断规模。 - **`items=0` 只是不要正文**,`total_count` 仍准确,可用于判断规模。
- **本服务只读**。写方法返回 405。要写知识请用 HomeAgent 主 agent 的 - **本服务只读**。写方法返回 405。要写知识请用 HologramAgent 主 agent 的
`knowledge_create`(或 WebUI 界面),不要试图绕过它直接写这个接口。 `knowledge_create`(或 WebUI 界面),不要试图绕过它直接写这个接口。
- 知识名里不能有 `..`、空格(会被规范成 `_`)、点开头的段。 - 知识名里不能有 `..`、空格(会被规范成 `_`)、点开头的段。
### 写入 ### 写入
本服务不提供写入。若你在 HomeAgent 主 agent 内部,写入用内核工具: 本服务不提供写入。若你在 HologramAgent 主 agent 内部,写入用内核工具:
``` ```
knowledge_create name="tech/go/调度" content="正文..." knowledge_create name="tech/go/调度" content="正文..."

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# kbtree 客户端:按分类树检索 HomeAgent 知识库。 # kbtree 客户端:按分类树检索 HologramAgent 知识库。
# #
# 由 knowledge-base skill 调用(agent 读 SKILL.md 后按需调本脚本)。 # 由 knowledge-base skill 调用(agent 读 SKILL.md 后按需调本脚本)。
# 也可人工使用: # 也可人工使用:
@ -88,7 +88,7 @@ if ! (exec 3<>"/dev/tcp/${HOSTPORT%%:*}/${HOSTPORT##*:}") 2>/dev/null; then
cat >&2 <<EOF cat >&2 <<EOF
kbtree: 连不上 $BASE(端口未监听)。 kbtree: 连不上 $BASE(端口未监听)。
最常见原因——**服务还没上线**。kbtree 是 HomeAgent 的内置插件, 最常见原因——**服务还没上线**。kbtree 是 HologramAgent 的内置插件,
需要二进制里含它才会启动。若 homed 是在合入该插件之前构建/部署的, 需要二进制里含它才会启动。若 homed 是在合入该插件之前构建/部署的,
就一直没有这个服务。上线后验证: 就一直没有这个服务。上线后验证:

View File

@ -1,6 +1,6 @@
{ {
"name": "knowledge-base", "name": "knowledge-base",
"description": "按分类树检索 HomeAgent 知识库:树形导航、分类内检索、以图搜知识", "description": "按分类树检索 HologramAgent 知识库:树形导航、分类内检索、以图搜知识",
"version": "1.1.0", "version": "1.1.0",
"author": "HomeAgent" "author": "HologramAgent"
} }

View File

@ -1,4 +1,4 @@
# HomeAgent GUI 设计说明与外部借鉴 # HologramAgent GUI 设计说明与外部借鉴
本文件记录 GUI(`cmd/gui`)在设计过程中**明确借鉴的外部实现**,以及本仓自行做出的设计决策。 本文件记录 GUI(`cmd/gui`)在设计过程中**明确借鉴的外部实现**,以及本仓自行做出的设计决策。
@ -12,11 +12,11 @@
### PiDeck(`C:\Users\21989\AppData\Local\Programs\PiDeck`) ### PiDeck(`C:\Users\21989\AppData\Local\Programs\PiDeck`)
**PiDeck 是本机安装的一个第三方 Electron 应用**(其前端依赖 **PiDeck 是本机安装的一个第三方 Electron 应用**(其前端依赖
`@deepseek-ai/dsh-web-frontend`)。HomeAgent GUI 在交互设计上参考了它的以下 `@deepseek-ai/dsh-web-frontend`)。HologramAgent GUI 在交互设计上参考了它的以下
做法。参考方式为**阅读其打包产物 `app.asar` 中的 CSS 实现**,理解其设计意图后 做法。参考方式为**阅读其打包产物 `app.asar` 中的 CSS 实现**,理解其设计意图后
在 HomeAgent 的样式体系中独立重写 —— 未复制其代码,也未使用其资源文件。 在 HologramAgent 的样式体系中独立重写 —— 未复制其代码,也未使用其资源文件。
| 借鉴项 | PiDeck 的实现 | HomeAgent 的落地 | 动机 | | 借鉴项 | PiDeck 的实现 | HologramAgent 的落地 | 动机 |
|---|---|---|---| |---|---|---|---|
| **加载 / 思考动画** | `@keyframes _dsh-state-dot-chase`(三点透明度递减 `1 → .6 → .35 → .15`)与 `tool-activity-dot`(上浮 + 缩放) | `renderer/style.css` 的 `haDots` / `haDotFloat` | 旧实现是 `border + rotate` 的经典 spinner。旋转语义上暗示"在加载某个确定的东西",而 agent 思考本身没有进度可转;且在 15px 的消息气泡里转圈糊成一团 | | **加载 / 思考动画** | `@keyframes _dsh-state-dot-chase`(三点透明度递减 `1 → .6 → .35 → .15`)与 `tool-activity-dot`(上浮 + 缩放) | `renderer/style.css` 的 `haDots` / `haDotFloat` | 旧实现是 `border + rotate` 的经典 spinner。旋转语义上暗示"在加载某个确定的东西",而 agent 思考本身没有进度可转;且在 15px 的消息气泡里转圈糊成一团 |
| **运行态呼吸光晕** | `.turn-row--running:before` —— 一层几乎看不见的 accent 底色(`color-mix(... 3%)`)以 `2.2s` 周期做 `opacity: 0 → 1 → 0` 呼吸 | `.msg-bubble.msg-running::before` | 单看气泡里的几个点,视线要缩到一小块;光晕铺在整条消息上,余光就能感知"agent 正在回" | | **运行态呼吸光晕** | `.turn-row--running:before` —— 一层几乎看不见的 accent 底色(`color-mix(... 3%)`)以 `2.2s` 周期做 `opacity: 0 → 1 → 0` 呼吸 | `.msg-bubble.msg-running::before` | 单看气泡里的几个点,视线要缩到一小块;光晕铺在整条消息上,余光就能感知"agent 正在回" |
@ -24,20 +24,20 @@
| **组合键处理** | ——(PiDeck 为 Web 应用,不涉及系统级输入注入) | —— | —— | | **组合键处理** | ——(PiDeck 为 Web 应用,不涉及系统级输入注入) | —— | —— |
**关于分组卡的规则差异**:PiDeck 的分组策略我们理解为"折叠成组", **关于分组卡的规则差异**:PiDeck 的分组策略我们理解为"折叠成组",
HomeAgent 额外加了一条规则 —— **只有 1 个工具时保持单卡**(组卡没意义, HologramAgent 额外加了一条规则 —— **只有 1 个工具时保持单卡**(组卡没意义,
反而多一层点击)。这是本地决策,不是 PiDeck 的做法。 反而多一层点击)。这是本地决策,不是 PiDeck 的做法。
--- ---
## 本仓自行做出的设计(未借鉴) ## 本仓自行做出的设计(未借鉴)
以下几项与 PiDeck 无关,是为 HomeAgent 自身场景做的决定: 以下几项与 PiDeck 无关,是为 HologramAgent 自身场景做的决定:
- **SSE 取代 EventSource**:需要自定义 `X-API-Key` 与 `Last-Event-ID` 请求头, - **SSE 取代 EventSource**:需要自定义 `X-API-Key` 与 `Last-Event-ID` 请求头,
浏览器原生 `EventSource` 不支持。 浏览器原生 `EventSource` 不支持。
- **增量聊天渲染**(`applyIncrementalChatRender`):按 `data-msgkey` 复用 DOM - **增量聊天渲染**(`applyIncrementalChatRender`):按 `data-msgkey` 复用 DOM
节点。性能问题由本仓实测定位(旧实现 200 条消息单次全量重建 235ms)。 节点。性能问题由本仓实测定位(旧实现 200 条消息单次全量重建 235ms)。
- **设备令牌注入**(`isDeviceTokenPath` / `deviceApiKey`):HomeAgent 服务端 - **设备令牌注入**(`isDeviceTokenPath` / `deviceApiKey`):HologramAgent 服务端
`/api/v1/device/` 命名空间下并存两套鉴权(`requireAPI` 认 cookie、 `/api/v1/device/` 命名空间下并存两套鉴权(`requireAPI` 认 cookie、
`requireToken` 只认 `X-API-Key`),需要客户端主动带令牌。 `requireToken` 只认 `X-API-Key`),需要客户端主动带令牌。
- **静默启动的"创建后隐藏"**:不创建窗口会导致渲染进程不启动(SSE、设备桥 - **静默启动的"创建后隐藏"**:不创建窗口会导致渲染进程不启动(SSE、设备桥

View File

@ -166,7 +166,7 @@ check(
// ── 5) 页面不得再引用公网 CDN ─────────────────────────────────────── // ── 5) 页面不得再引用公网 CDN ───────────────────────────────────────
// //
// 与服务端 starmap_vendor_test.go 同一判据(那里钉 WebUI,这里钉 GUI)。 // 与服务端 starmap_vendor_test.go 同一判据(那里钉 WebUI,这里钉 GUI)。
// HomeAgent 支持离线/内网部署,而前端有 4 个硬依赖在公网上时, // HologramAgent 支持离线/内网部署,而前端有 4 个硬依赖在公网上时,
// 断网/出口受限环境下星图必坏、且用户无从修复。 // 断网/出口受限环境下星图必坏、且用户无从修复。
const htmlSrc = readFileSync(join(here, "renderer", "index.html"), "utf8"); const htmlSrc = readFileSync(join(here, "renderer", "index.html"), "utf8");

View File

@ -185,7 +185,7 @@ async function adoptSlSession(url) {
} }
// 带 Electron cookie jar 的 fetch(跟随外部网关 302 → login 域 → 回跳,最多 6 跳), // 带 Electron cookie jar 的 fetch(跟随外部网关 302 → login 域 → 回跳,最多 6 跳),
// 使 POST /api/v1/login 真正触达内层 HomeAgent 并取回 homeagent_session。 // 使 POST /api/v1/login 真正触达内层 HologramAgent 并取回 homeagent_session。
async function sessionFetch(initUrl, opts) { async function sessionFetch(initUrl, opts) {
const { session } = require("electron"); const { session } = require("electron");
let url = initUrl; let url = initUrl;
@ -430,7 +430,7 @@ function createWindow(show = true) {
height: 860, height: 860,
minWidth: 900, minWidth: 900,
minHeight: 600, minHeight: 600,
title: "HomeAgent", title: "HologramAgent",
frame: false, frame: false,
show: false, // 一律先不显示,由 ready-to-show 决定 show: false, // 一律先不显示,由 ready-to-show 决定
icon: path.join(__dirname, "icon.ico"), icon: path.join(__dirname, "icon.ico"),
@ -806,7 +806,7 @@ ipcMain.handle("webui:openLogin", (_, { url, username, password }) => {
parent: mainWindow, parent: mainWindow,
modal: true, modal: true,
autoHideMenuBar: true, autoHideMenuBar: true,
title: "HomeAgent - 网关联机登录", title: "HologramAgent - 网关联机登录",
webPreferences: { contextIsolation: true, nodeIntegration: false }, webPreferences: { contextIsolation: true, nodeIntegration: false },
}); });
const u = encodeURIComponent(username || ""); const u = encodeURIComponent(username || "");
@ -1456,7 +1456,7 @@ async function connectDeviceBridgeOnce(cfg) {
op: "hello", op: "hello",
device: { device: {
device_id: deviceBridgeId, device_id: deviceBridgeId,
name: "HomeAgent GUI", name: "HologramAgent GUI",
kind: "computer", kind: "computer",
authorized: authorized, authorized: authorized,
caps: [ caps: [
@ -1986,8 +1986,8 @@ function executeHomeagentCmd(capability, reqId) {
const h = Math.min(parseInt(area.height || 600, 10) - 40, 560); const h = Math.min(parseInt(area.height || 600, 10) - 40, 560);
const html = /<\/?[a-z][\s\S]*>/i.test(raw) const html = /<\/?[a-z][\s\S]*>/i.test(raw)
? raw ? raw
: "<div style='font-family:sans-serif;display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;padding:24px;box-sizing:border-box'><h1 style='margin:0 0 16px;color:#ff7fac'>HomeAgent</h1><p style='font-size:16px;line-height:1.6;white-space:pre-wrap;word-break:break-all'>" + : "<div style='font-family:sans-serif;display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;padding:24px;box-sizing:border-box'><h1 style='margin:0 0 16px;color:#ff7fac'>HologramAgent</h1><p style='font-size:16px;line-height:1.6;white-space:pre-wrap;word-break:break-all'>" +
String(raw || "HomeAgent 远程屏幕提示").replace(/</g, "&lt;") + String(raw || "HologramAgent 远程屏幕提示").replace(/</g, "&lt;") +
"</p></div>"; "</p></div>";
try { try {
if (screensueWin && !screensueWin.isDestroyed()) screensueWin.destroy(); if (screensueWin && !screensueWin.isDestroyed()) screensueWin.destroy();
@ -1999,7 +1999,7 @@ function executeHomeagentCmd(capability, reqId) {
alwaysOnTop: true, alwaysOnTop: true,
frame: false, frame: false,
resizable: true, resizable: true,
title: "HomeAgent · screensue", title: "HologramAgent · screensue",
webPreferences: { nodeIntegration: false, contextIsolation: true }, webPreferences: { nodeIntegration: false, contextIsolation: true },
}); });
screensueWin.setAlwaysOnTop(true, "screen-saver"); screensueWin.setAlwaysOnTop(true, "screen-saver");
@ -3223,7 +3223,7 @@ async function setDeviceAuthorized(authorized) {
op: "hello", op: "hello",
device: { device: {
device_id: deviceBridgeId, device_id: deviceBridgeId,
name: "HomeAgent GUI", name: "HologramAgent GUI",
kind: "computer", kind: "computer",
authorized: !!authorized, authorized: !!authorized,
caps: [ caps: [
@ -3291,7 +3291,7 @@ function buildTrayMenuTemplate() {
const electron = require("electron"); const electron = require("electron");
const TMenu = electron.Menu; const TMenu = electron.Menu;
const tpl = []; const tpl = [];
tpl.push({ label: "HomeAgent", enabled: true }); tpl.push({ label: "HologramAgent", enabled: true });
tpl.push({ type: "separator" }); tpl.push({ type: "separator" });
// 远程连接状态(缓存避免每次读文件) // 远程连接状态(缓存避免每次读文件)
const connLabel = trayConnCache.connLabel || "未连接"; const connLabel = trayConnCache.connLabel || "未连接";
@ -3404,7 +3404,7 @@ function initTray() {
img = nImg.createEmpty(); img = nImg.createEmpty();
} }
tray = new Tray(img); tray = new Tray(img);
tray.setToolTip("HomeAgent - 个人智能管家"); tray.setToolTip("HologramAgent - 个人智能管家");
rebuildTrayMenu(); rebuildTrayMenu();
tray.on("double-click", () => showMainWindow()); tray.on("double-click", () => showMainWindow());
console.log("[tray] READY: " + (tray ? "tray-created" : "null")); console.log("[tray] READY: " + (tray ? "tray-created" : "null"));
@ -3525,7 +3525,7 @@ app.whenReady().then(async () => {
// 「启动时不显示主窗口,驻留托盘后台运行」,但主进程从来没读过它 —— // 「启动时不显示主窗口,驻留托盘后台运行」,但主进程从来没读过它 ——
// whenReady 无条件 createWindow(),于是必然弹窗。 // whenReady 无条件 createWindow(),于是必然弹窗。
// //
// 实测(本机,prefs.silentStart=true):窗口标题 HomeAgent 照样出现。 // 实测(本机,prefs.silentStart=true):窗口标题 HologramAgent 照样出现。
// //
// 与 applyAutoLaunch 里的 openAsHidden 是两回事:那个是告诉 OS「开机自启 // 与 applyAutoLaunch 里的 openAsHidden 是两回事:那个是告诉 OS「开机自启
// 时如何启动」(仅 Windows/macOS 生效),这里是本进程自己决定要不要显示, // 时如何启动」(仅 Windows/macOS 生效),这里是本进程自己决定要不要显示,
@ -3605,7 +3605,7 @@ let notifSeq = 0;
ipcMain.handle("notify:show", (_, payload) => { ipcMain.handle("notify:show", (_, payload) => {
try { try {
const p = payload || {}; const p = payload || {};
const title = String(p.title || "HomeAgent"); const title = String(p.title || "HologramAgent");
const body = String(p.body || ""); const body = String(p.body || "");
if (!body) return { ok: false, error: "empty body" }; if (!body) return { ok: false, error: "empty body" };
if (!Notification.isSupported()) return { ok: false, error: "unsupported" }; if (!Notification.isSupported()) return { ok: false, error: "unsupported" };
@ -3721,10 +3721,10 @@ function saveGuiPrefs(p) {
} }
// Electron LoginItem 写入的注册表项名是「electron.app.<exe 名>」。 // Electron LoginItem 写入的注册表项名是「electron.app.<exe 名>」。
// 产物改名过(HomeAgent.exe → homeagent-gui.exe)就会多出一条旧项, // 产物改名过(HologramAgent.exe → homeagent-gui.exe)就会多出一条旧项,
// 且 setLoginItemSettings 不会清理它们 ⇒ 开机启动两次同一个 exe。 // 且 setLoginItemSettings 不会清理它们 ⇒ 开机启动两次同一个 exe。
// 实测本机 Run 键里确实同时存在: // 实测本机 Run 键里确实同时存在:
// electron.app.HomeAgent = "E:\program\homeagent\...\homeagent-gui.exe" // electron.app.HologramAgent = "E:\program\homeagent\...\homeagent-gui.exe"
// electron.app.homeagent-gui = E:\program\homeagent\...\homeagent-gui.exe // electron.app.homeagent-gui = E:\program\homeagent\...\homeagent-gui.exe
// 两条指向同一 exe,所以表面看是两个自启动,实际是同一份的重复。 // 两条指向同一 exe,所以表面看是两个自启动,实际是同一份的重复。
// //

View File

@ -3,7 +3,7 @@
"version": "1.4.0", "version": "1.4.0",
"author": "JianFeeeee <jianfeeeee@homeagent.local>", "author": "JianFeeeee <jianfeeeee@homeagent.local>",
"homepage": "https://github.com/JianFeeeee/HologramAgent", "homepage": "https://github.com/JianFeeeee/HologramAgent",
"description": "HomeAgent Desktop GUI - Multi-connection management dashboard", "description": "HologramAgent Desktop GUI - Multi-connection management dashboard",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"start": "electron . --no-sandbox", "start": "electron . --no-sandbox",
@ -25,7 +25,7 @@
}, },
"build": { "build": {
"appId": "com.homeagent.gui", "appId": "com.homeagent.gui",
"productName": "HomeAgent", "productName": "HologramAgent",
"linux": { "linux": {
"target": [ "target": [
"deb", "deb",

View File

@ -741,7 +741,7 @@ async function api(p, o) {
// 从服务端发现设备网关地址(自动链接的权威来源)。 // 从服务端发现设备网关地址(自动链接的权威来源)。
// //
// 失败不报错:老版本 HomeAgent 没有这个端点,回退到本地推导即可 // 失败不报错:老版本 HologramAgent 没有这个端点,回退到本地推导即可
// (见 renderDeviceChannel 里的 state.discoveredGateway || 旧口径)。 // (见 renderDeviceChannel 里的 state.discoveredGateway || 旧口径)。
// //
// 优先 url_portal(门户同源形态):GUI 主进程连 WS 走系统解析器, // 优先 url_portal(门户同源形态):GUI 主进程连 WS 走系统解析器,
@ -1083,7 +1083,7 @@ async function refreshAll() {
// 服务端列表未含本机(可能离线),仍展示本地状态 // 服务端列表未含本机(可能离线),仍展示本地状态
state.devices.push({ state.devices.push({
device_id: dbinfo.deviceId, device_id: dbinfo.deviceId,
name: "HomeAgent GUI", name: "HologramAgent GUI",
kind: "computer", kind: "computer",
authorized: dbinfo.authorized, authorized: dbinfo.authorized,
online: false, online: false,
@ -1555,7 +1555,7 @@ function renderOverview() {
b.commit && b.commit !== "unknown" ? String(b.commit).slice(0, 7) : ""; b.commit && b.commit !== "unknown" ? String(b.commit).slice(0, 7) : "";
return ( return (
"v" + escHtml(v) + "v" + escHtml(v) +
'<div class="stat-sub">' + escHtml(b.kernel_name || "HomeAgent") + '<div class="stat-sub">' + escHtml(b.kernel_name || "HologramAgent") +
(sha ? " · " + escHtml(sha) : "") + "</div>" (sha ? " · " + escHtml(sha) : "") + "</div>"
); );
})(), })(),
@ -1819,8 +1819,8 @@ function buildChatLayout() {
"</h2>" + "</h2>" +
"<p>" + "<p>" +
__( __(
"连接 HomeAgent 服务端后即可开始对话。请在设置中添加后端连接。", "连接 HologramAgent 服务端后即可开始对话。请在设置中添加后端连接。",
"Connect to a HomeAgent server to start chatting. Add a backend connection in Settings.", "Connect to a HologramAgent server to start chatting. Add a backend connection in Settings.",
) + ) +
"</p>" + "</p>" +
'<button class="btn btn-primary" onclick="goSettingsConn()">' + '<button class="btn btn-primary" onclick="goSettingsConn()">' +
@ -1853,7 +1853,7 @@ function buildChatLayout() {
if (state.messages.length === 0) { if (state.messages.length === 0) {
html += html +=
'<div class="empty-state" style="flex:1;display:flex;align-items:center;justify-content:center"><p>' + '<div class="empty-state" style="flex:1;display:flex;align-items:center;justify-content:center"><p>' +
__("开始与您的 HomeAgent 聊天吧", "Start chatting with your HomeAgent") + __("开始与您的 HologramAgent 聊天吧", "Start chatting with your HologramAgent") +
"</p></div>"; "</p></div>";
} }
html += html +=
@ -2229,7 +2229,7 @@ function renderChat() {
if (msgs.length === 0) { if (msgs.length === 0) {
html = html =
'<div class="empty-state" style="flex:1;display:flex;align-items:center;justify-content:center"><p>' + '<div class="empty-state" style="flex:1;display:flex;align-items:center;justify-content:center"><p>' +
__("开始与您的 HomeAgent 聊天吧", "Start chatting with your HomeAgent") + __("开始与您的 HologramAgent 聊天吧", "Start chatting with your HologramAgent") +
"</p></div>"; "</p></div>";
} else { } else {
msgs.forEach((m, i) => { msgs.forEach((m, i) => {
@ -5963,7 +5963,7 @@ function renderGuiPrefs() {
sw( sw(
"autoLaunch", "autoLaunch",
__("开机自启", "Auto launch on start"), __("开机自启", "Auto launch on start"),
__("登录后自动启动 HomeAgent(静默)", "Start silently at login"), __("登录后自动启动 HologramAgent(静默)", "Start silently at login"),
"saveGuiPrefs('autoLaunch', this.checked)", "saveGuiPrefs('autoLaunch', this.checked)",
) + ) +
sw( sw(
@ -6041,7 +6041,7 @@ function renderConnSection() {
"</h3>" + "</h3>" +
"<label>" + "<label>" +
__("名称", "Name") + __("名称", "Name") +
'</label><input id="conn-name" placeholder="My HomeAgent">' + '</label><input id="conn-name" placeholder="My HologramAgent">' +
"<label>" + "<label>" +
__("连接类型", "Type") + __("连接类型", "Type") +
'</label><select id="conn-type" onchange="toggleConnType()">' + '</label><select id="conn-type" onchange="toggleConnType()">' +
@ -6734,7 +6734,7 @@ async function connectFetchSSE(url) {
String(p.content || "").slice(0, 40); String(p.content || "").slice(0, 40);
notifyTurnOnce( notifyTurnOnce(
_sig, _sig,
"HomeAgent", "HologramAgent",
String(p.content || "").slice(0, 160) || __("(空回复)", "(empty reply)"), String(p.content || "").slice(0, 160) || __("(空回复)", "(empty reply)"),
); );
endChatTurn(); endChatTurn();
@ -7329,8 +7329,8 @@ function renderDevices() {
html += html +=
'<p style="color:var(--text-muted)">' + '<p style="color:var(--text-muted)">' +
__( __(
"暂无设备接入。设备通过 WebSocket 连接到设备网关(默认经 HomeAgent 反代到 devices.<基域名>,或直连 127.0.0.1:9890/api/v1/device/ws),携带 token 后 hello 登记、bind 授权。", "暂无设备接入。设备通过 WebSocket 连接到设备网关(默认经 HologramAgent 反代到 devices.<基域名>,或直连 127.0.0.1:9890/api/v1/device/ws),携带 token 后 hello 登记、bind 授权。",
"No devices yet. Devices connect via WebSocket (proxied by HomeAgent at devices.<base-domain>, or directly 127.0.0.1:9890/api/v1/device/ws), hello to register, bind to authorize.", "No devices yet. Devices connect via WebSocket (proxied by HologramAgent at devices.<base-domain>, or directly 127.0.0.1:9890/api/v1/device/ws), hello to register, bind to authorize.",
) + ) +
"</p>"; "</p>";
} else { } else {

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HomeAgent</title> <title>HologramAgent</title>
<link rel="icon" type="image/svg+xml" href="icon.svg" /> <link rel="icon" type="image/svg+xml" href="icon.svg" />
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css" />
<!-- <!--
@ -11,7 +11,7 @@
为什么(与服务端 webui 插件同一条理由,见 为什么(与服务端 webui 插件同一条理由,见
internal/plugins/webui/starmap_vendor_test.go 的注释): internal/plugins/webui/starmap_vendor_test.go 的注释):
HomeAgent 明确支持离线/内网部署,而前端原本有 4 个硬依赖在公网上 HologramAgent 明确支持离线/内网部署,而前端原本有 4 个硬依赖在公网上
(cdnjs/jsdelivr)。真实现象是「服务端 curl 同一批 URL 返回 200, (cdnjs/jsdelivr)。真实现象是「服务端 curl 同一批 URL 返回 200,
但用户点星图看到『3D 星图不可用(CDN 加载失败)』」——不是服务端的问题, 但用户点星图看到『3D 星图不可用(CDN 加载失败)』」——不是服务端的问题,
是客户端访问不到公共 CDN(内网/出口受限/断网)。 是客户端访问不到公共 CDN(内网/出口受限/断网)。
@ -108,7 +108,7 @@
<div id="app"> <div id="app">
<!-- 左侧图标栏:主页=对话,其余为二级页面 --> <!-- 左侧图标栏:主页=对话,其余为二级页面 -->
<aside class="rail" id="rail"> <aside class="rail" id="rail">
<div class="rail-logo"><img src="icon.svg" alt="HomeAgent" /></div> <div class="rail-logo"><img src="icon.svg" alt="HologramAgent" /></div>
<button <button
class="rail-btn active" class="rail-btn active"
id="rail-chat" id="rail-chat"
@ -318,7 +318,7 @@
<div class="main"> <div class="main">
<div class="topbar"> <div class="topbar">
<h1>HomeAgent</h1> <h1>HologramAgent</h1>
<span <span
class="conn-indicator" class="conn-indicator"
id="conn-indicator" id="conn-indicator"

View File

@ -6,7 +6,7 @@
// 文案写着「启动时不显示主窗口,驻留托盘后台运行」,但主进程从来没读过它 // 文案写着「启动时不显示主窗口,驻留托盘后台运行」,但主进程从来没读过它
// —— whenReady 无条件 createWindow(),于是必然弹窗。 // —— whenReady 无条件 createWindow(),于是必然弹窗。
// //
// 实测(本机,prefs.silentStart=true):窗口标题 HomeAgent 照样出现。 // 实测(本机,prefs.silentStart=true):窗口标题 HologramAgent 照样出现。
// //
// 本判据钉住三件事: // 本判据钉住三件事:
// 1. whenReady 真的读 silentStart 并据此决定 show // 1. whenReady 真的读 silentStart 并据此决定 show

View File

@ -296,7 +296,7 @@ func (s *MockServer) handlePlugins(w http.ResponseWriter, r *http.Request) {
func (s *MockServer) handleChatHistory(w http.ResponseWriter, r *http.Request) { func (s *MockServer) handleChatHistory(w http.ResponseWriter, r *http.Request) {
writeJSON(w, []ChatMessage{ writeJSON(w, []ChatMessage{
{Role: "user", Content: "你好"}, {Role: "user", Content: "你好"},
{Role: "assistant", Content: "你好!我是 HomeAgent,有什么可以帮你的?"}, {Role: "assistant", Content: "你好!我是 HologramAgent,有什么可以帮你的?"},
{Role: "user", Content: "测试消息"}, {Role: "user", Content: "测试消息"},
{Role: "assistant", Content: "这是模拟后端的测试回复,GUI 连接正常 ✅"}, {Role: "assistant", Content: "这是模拟后端的测试回复,GUI 连接正常 ✅"},
}) })
@ -361,7 +361,7 @@ func (s *MockServer) handleChatEvents(w http.ResponseWriter, r *http.Request) {
func (s *MockServer) handleMemoryGraph(w http.ResponseWriter, r *http.Request) { func (s *MockServer) handleMemoryGraph(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]interface{}{ writeJSON(w, map[string]interface{}{
"nodes": []map[string]interface{}{ "nodes": []map[string]interface{}{
{"id": "1", "label": "HomeAgent", "group": "system"}, {"id": "1", "label": "HologramAgent", "group": "system"},
{"id": "2", "label": "GUI 测试", "group": "user"}, {"id": "2", "label": "GUI 测试", "group": "user"},
}, },
"edges": []map[string]interface{}{ "edges": []map[string]interface{}{
@ -511,7 +511,7 @@ func main() {
server.registerRoutes(mux) server.registerRoutes(mux)
addr := ":9099" addr := ":9099"
log.Printf("=== HomeAgent Mock Server ====") log.Printf("=== HologramAgent Mock Server ====")
log.Printf("监听地址: http://0.0.0.0%s", addr) log.Printf("监听地址: http://0.0.0.0%s", addr)
log.Printf("API 基础路径: http://0.0.0.0%s/api/v1/", addr) log.Printf("API 基础路径: http://0.0.0.0%s/api/v1/", addr)
log.Printf("SSE 端点: http://0.0.0.0%s/api/v1/chat/events", addr) log.Printf("SSE 端点: http://0.0.0.0%s/api/v1/chat/events", addr)

View File

@ -349,7 +349,7 @@ func (s *MockServer) handleDeviceWS(w http.ResponseWriter, r *http.Request) {
ws.WriteJSON(client.CmdMsg{ ws.WriteJSON(client.CmdMsg{
Op: "cmd", Op: "cmd",
ReqID: "test_2_clipboardsue_" + fmt.Sprintf("%d", time.Now().UnixMilli()), ReqID: "test_2_clipboardsue_" + fmt.Sprintf("%d", time.Now().UnixMilli()),
Command: "homeagent-clipboardsue HomeAgent远程测试_" + fmt.Sprintf("%d", time.Now().UnixMilli()), Command: "homeagent-clipboardsue HologramAgent远程测试_" + fmt.Sprintf("%d", time.Now().UnixMilli()),
CmdType: "homeagent", CmdType: "homeagent",
}) })
@ -404,7 +404,7 @@ func (s *MockServer) handleDeviceWS(w http.ResponseWriter, r *http.Request) {
ws.WriteJSON(client.CmdMsg{ ws.WriteJSON(client.CmdMsg{
Op: "cmd", Op: "cmd",
ReqID: "test_6_screensue_" + fmt.Sprintf("%d", time.Now().UnixMilli()), ReqID: "test_6_screensue_" + fmt.Sprintf("%d", time.Now().UnixMilli()),
Command: `homeagent-screensue 10 <!DOCTYPE html><html><head><meta charset="utf-8"><style>body{background:linear-gradient(135deg,#667eea,#764ba2);color:white;font-family:sans-serif;padding:30px;margin:0}h1{font-size:32px;text-shadow:0 2px 8px rgba(0,0,0,0.3)}.card{background:rgba(255,255,255,0.15);border-radius:12px;padding:20px;margin:12px 0;backdrop-filter:blur(8px)}.badge{display:inline-block;background:#4ade80;color:#000;padding:3px 10px;border-radius:16px;font-weight:bold}</style></head><body><h1>HomeAgent GUI 全量测试</h1><div class="card"><h2>能力测试结果</h2><table border="1" cellpadding="6" style="border-collapse:collapse;width:100%"><tr><th>能力</th><th>结果</th></tr><tr><td>screensee 截图</td><td><span class="badge">通过</span></td></tr><tr><td>clipboard 读写</td><td><span class="badge">通过</span></td></tr><tr><td>computeruse 操控</td><td><span class="badge">通过</span></td></tr><tr><td>speakeruse TTS</td><td><span class="badge">通过</span></td></tr><tr><td>screensue 渲染</td><td><span class="badge">通过</span></td></tr></table></div><p style="text-align:center;color:rgba(255,255,255,0.7)">2026-08-23 19:50</p></body></html>`, Command: `homeagent-screensue 10 <!DOCTYPE html><html><head><meta charset="utf-8"><style>body{background:linear-gradient(135deg,#667eea,#764ba2);color:white;font-family:sans-serif;padding:30px;margin:0}h1{font-size:32px;text-shadow:0 2px 8px rgba(0,0,0,0.3)}.card{background:rgba(255,255,255,0.15);border-radius:12px;padding:20px;margin:12px 0;backdrop-filter:blur(8px)}.badge{display:inline-block;background:#4ade80;color:#000;padding:3px 10px;border-radius:16px;font-weight:bold}</style></head><body><h1>HologramAgent GUI 全量测试</h1><div class="card"><h2>能力测试结果</h2><table border="1" cellpadding="6" style="border-collapse:collapse;width:100%"><tr><th>能力</th><th>结果</th></tr><tr><td>screensee 截图</td><td><span class="badge">通过</span></td></tr><tr><td>clipboard 读写</td><td><span class="badge">通过</span></td></tr><tr><td>computeruse 操控</td><td><span class="badge">通过</span></td></tr><tr><td>speakeruse TTS</td><td><span class="badge">通过</span></td></tr><tr><td>screensue 渲染</td><td><span class="badge">通过</span></td></tr></table></div><p style="text-align:center;color:rgba(255,255,255,0.7)">2026-08-23 19:50</p></body></html>`,
CmdType: "homeagent", CmdType: "homeagent",
}) })

View File

@ -1,7 +1,7 @@
{ {
"app": { "app": {
"bundleName": "com.example.homeagent", "bundleName": "com.example.homeagent",
"vendor": "HomeAgent", "vendor": "HologramAgent",
"versionCode": 1004000, "versionCode": 1004000,
"versionName": "1.4.0", "versionName": "1.4.0",
// 分层图标:前景是字形,背景(沉淀色)在 base/ 与 dark/ 各一份,随系统主题切换。 // 分层图标:前景是字形,背景(沉淀色)在 base/ 与 dark/ 各一份,随系统主题切换。

View File

@ -2,7 +2,7 @@
"string": [ "string": [
{ {
"name": "app_name", "name": "app_name",
"value": "HomeAgent" "value": "HologramAgent"
} }
] ]
} }

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 989 B

After

Width:  |  Height:  |  Size: 989 B

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 988 B

After

Width:  |  Height:  |  Size: 988 B

View File

@ -1,7 +1,7 @@
{ {
"name": "entry", "name": "entry",
"version": "1.0.0", "version": "1.0.0",
"description": "HomeAgent HarmonyOS client entry module", "description": "HologramAgent HarmonyOS client entry module",
"main": "", "main": "",
"author": "", "author": "",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -45,7 +45,7 @@ export function applyChatSse(ev: SseEvent, sink: ChatStreamSink): void {
const frameType: string = frame['type'] as string ?? ''; const frameType: string = frame['type'] as string ?? '';
const type: string = ev.event.length > 0 ? ev.event : frameType; const type: string = ev.event.length > 0 ? ev.event : frameType;
// 诊断只记事件类型(内容可能含隐私,不落盘) // 诊断只记事件类型(内容可能含隐私,不落盘)
hilog.debug(0x0000, 'HomeAgent', 'sse %{public}s', type); hilog.debug(0x0000, 'HologramAgent', 'sse %{public}s', type);
if (type === 'agent_output') { if (type === 'agent_output') {
const content: string = payload['content'] as string ?? ''; const content: string = payload['content'] as string ?? '';

View File

@ -10,7 +10,7 @@ export class DeviceBridgeClient {
private url: string = ''; private url: string = '';
private token: string = ''; private token: string = '';
private deviceId: string = ''; private deviceId: string = '';
private name: string = 'HomeAgent OHOS'; private name: string = 'HologramAgent OHOS';
private kind: string = 'phone'; private kind: string = 'phone';
private caps: string[] = []; private caps: string[] = [];
private hostname: string = 'ohos'; private hostname: string = 'ohos';
@ -237,7 +237,7 @@ export class DeviceBridgeClient {
// 为何不报错回去:这些 op 不要求回执(服务端 pushToDevice 不等 // 为何不报错回去:这些 op 不要求回执(服务端 pushToDevice 不等
// device→server 的应答),回一个错误帧反而会被当成未知帧处理。 // device→server 的应答),回一个错误帧反而会被当成未知帧处理。
if (op.length > 0) { if (op.length > 0) {
hilog.debug(0x0000, 'HomeAgent', 'device bridge: 忽略不支持的 op %{public}s', op); hilog.debug(0x0000, 'HologramAgent', 'device bridge: 忽略不支持的 op %{public}s', op);
} }
return; return;
} }

View File

@ -30,7 +30,7 @@ function ensureBridgeStateTracking(): void {
* *
* 网关改造为子域反代后位于 devices.<基域名>,而基域名与子域标签都是**服务端 * 网关改造为子域反代后位于 devices.<基域名>,而基域名与子域标签都是**服务端
* 配置**,客户端拼不出来。正常路径是先调 discoverGateway() 问服务端; * 配置**,客户端拼不出来。正常路径是先调 discoverGateway() 问服务端;
* 本函数只在「服务端没有发现端点」(老版本 HomeAgent)时兜底, * 本函数只在「服务端没有发现端点」(老版本 HologramAgent)时兜底,
* 保留旧部署的可用性。 * 保留旧部署的可用性。
*/ */
export function deviceGatewayUrl(base: string): string { export function deviceGatewayUrl(base: string): string {

View File

@ -216,7 +216,7 @@ class StatusStore {
if (build !== undefined && build !== null) { if (build !== undefined && build !== null) {
const bVer: string = build['version'] as string ?? ''; const bVer: string = build['version'] as string ?? '';
const bCommit: string = build['commit'] as string ?? ''; const bCommit: string = build['commit'] as string ?? '';
const bName: string = build['kernel_name'] as string ?? 'HomeAgent'; const bName: string = build['kernel_name'] as string ?? 'HologramAgent';
const bSdk: string = build['sdk_compatible'] as string ?? ''; const bSdk: string = build['sdk_compatible'] as string ?? '';
const bTime: string = build['build_time'] as string ?? ''; const bTime: string = build['build_time'] as string ?? '';
lic = build['license'] as string ?? ''; lic = build['license'] as string ?? '';

View File

@ -11,11 +11,11 @@ import { ApiError } from './ApiClient';
* 约定: * 约定:
* - UI 只显示 userMessage() 返回的短句(人话、可行动、不含技术细节); * - UI 只显示 userMessage() 返回的短句(人话、可行动、不含技术细节);
* - 技术细节(原始 message / HTTP 状态码 / 响应体)只写进 hilog, * - 技术细节(原始 message / HTTP 状态码 / 响应体)只写进 hilog,
* 通过 `hdc shell hilog | grep HomeAgent` 排查,不进 UI。 * 通过 `hdc shell hilog | grep HologramAgent` 排查,不进 UI。
*/ */
const DOMAIN: number = 0xA0A0; const DOMAIN: number = 0xA0A0;
const TAG: string = 'HomeAgent'; const TAG: string = 'HologramAgent';
/** 网络类错误的统一提示:不暴露主机名、端口、curl 错误码。 */ /** 网络类错误的统一提示:不暴露主机名、端口、curl 错误码。 */
const MSG_UNREACHABLE: string = '连接不上后端服务,请检查网络与服务地址'; const MSG_UNREACHABLE: string = '连接不上后端服务,请检查网络与服务地址';

View File

@ -208,7 +208,7 @@ export struct ConnectionsPane {
.fontSize(12) .fontSize(12)
.fontColor(this.palette().textSecondary) .fontColor(this.palette().textSecondary)
.margin({ bottom: 10 }) .margin({ bottom: 10 })
TextInput({ placeholder: '名称 (如 HomeAgent)', text: this.editName }) TextInput({ placeholder: '名称 (如 HologramAgent)', text: this.editName })
.height(36).fontSize(13).fontColor(this.palette().textPrimary) .height(36).fontSize(13).fontColor(this.palette().textPrimary)
.placeholderColor(this.palette().textMuted).backgroundColor(this.palette().bgInput) .placeholderColor(this.palette().textMuted).backgroundColor(this.palette().bgInput)
.borderRadius(RADIUS_SM).border({ width: 1, color: this.palette().border }) .borderRadius(RADIUS_SM).border({ width: 1, color: this.palette().border })

View File

@ -69,7 +69,7 @@ export struct DeviceLocalPane {
}) { }) {
PlainCard({ caption: '基本信息' }) { PlainCard({ caption: '基本信息' }) {
DeviceKvRow({ label: '设备 ID', value: this.deviceId.length > 0 ? this.deviceId : '未注册' }) DeviceKvRow({ label: '设备 ID', value: this.deviceId.length > 0 ? this.deviceId : '未注册' })
DeviceKvRow({ label: '名称', value: 'HomeAgent OHOS' }) DeviceKvRow({ label: '名称', value: 'HologramAgent OHOS' })
DeviceKvRow({ label: '类型', value: 'phone' }) DeviceKvRow({ label: '类型', value: 'phone' })
} }

View File

@ -48,7 +48,7 @@ export struct GradientBackground {
// 只处理本地沙箱文件;http(s) 背景图不是本应用的场景(设置页只给图库选择) // 只处理本地沙箱文件;http(s) 背景图不是本应用的场景(设置页只给图库选择)
const path: string = src.startsWith('file://') ? src.substring(7) : src; const path: string = src.startsWith('file://') ? src.substring(7) : src;
if (!path.startsWith('/')) { if (!path.startsWith('/')) {
hilog.error(0x0000, 'HomeAgent', 'bg: unsupported source'); hilog.error(0x0000, 'HologramAgent', 'bg: unsupported source');
this.bgPixel = undefined; this.bgPixel = undefined;
return; return;
} }
@ -59,11 +59,11 @@ export struct GradientBackground {
await srcObj.release(); await srcObj.release();
fileIo.closeSync(f); fileIo.closeSync(f);
this.bgPixel = pm; this.bgPixel = pm;
hilog.info(0x0000, 'HomeAgent', 'bg decoded ok'); hilog.info(0x0000, 'HologramAgent', 'bg decoded ok');
} catch (e) { } catch (e) {
// 失败必须留痕:否则表现就是"设置了但没生效",无从定位 // 失败必须留痕:否则表现就是"设置了但没生效",无从定位
const err = e as BusinessError; const err = e as BusinessError;
hilog.error(0x0000, 'HomeAgent', 'bg decode failed code=%{public}d', err.code as number); hilog.error(0x0000, 'HologramAgent', 'bg decode failed code=%{public}d', err.code as number);
this.bgPixel = undefined; this.bgPixel = undefined;
} }
} }

View File

@ -21,7 +21,7 @@ function storedThemeMode(): string {
export default class EntryAbility extends UIAbility { export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('[HomeAgent] ability onCreate'); console.info('[HologramAgent] ability onCreate');
const sysDark: boolean = const sysDark: boolean =
this.context.config?.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK; this.context.config?.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
// Seed the OS color state first, then resolve the persisted mode. // Seed the OS color state first, then resolve the persisted mode.
@ -33,7 +33,7 @@ export default class EntryAbility extends UIAbility {
onDestroy(): void { onDestroy(): void {
stopForegroundBridge(); stopForegroundBridge();
console.info('[HomeAgent] ability onDestroy'); console.info('[HologramAgent] ability onDestroy');
} }
/** System dark/light switch — re-resolve when mode is 'system'. */ /** System dark/light switch — re-resolve when mode is 'system'. */
@ -75,7 +75,7 @@ export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void { onWindowStageCreate(windowStage: window.WindowStage): void {
const startUI = (): void => { const startUI = (): void => {
windowStage.loadContent('pages/Index', () => { windowStage.loadContent('pages/Index', () => {
console.info('[HomeAgent] main page loaded'); console.info('[HologramAgent] main page loaded');
}); });
}; };
@ -99,21 +99,21 @@ export default class EntryAbility extends UIAbility {
// 全屏沉浸:内容延伸到状态栏和导航栏区域 // 全屏沉浸:内容延伸到状态栏和导航栏区域
win.setWindowLayoutFullScreen(true); win.setWindowLayoutFullScreen(true);
} catch (e) { } catch (e) {
console.warn('[HomeAgent] setWindowLayoutFullScreen failed: ' + (e as Error).message); console.warn('[HologramAgent] setWindowLayoutFullScreen failed: ' + (e as Error).message);
} }
connStore.init(this.context).then(async () => { connStore.init(this.context).then(async () => {
const cur = connStore.getCurrentConnection(); const cur = connStore.getCurrentConnection();
if (cur !== null) { if (cur !== null) {
apiClient.setConnection(cur); apiClient.setConnection(cur);
} }
console.info('[HomeAgent] connStore initialized'); console.info('[HologramAgent] connStore initialized');
// init 之后持久化的主题模式才可读,这里按存量设置重新解析并刷新系统栏 // init 之后持久化的主题模式才可读,这里按存量设置重新解析并刷新系统栏
this.reapplyStoredTheme(); this.reapplyStoredTheme();
this.applySystemBar(); this.applySystemBar();
startForegroundBridge(this.context); startForegroundBridge(this.context);
startUI(); startUI();
}).catch((e: Error) => { }).catch((e: Error) => {
console.error('[HomeAgent] connStore init failed: ' + e.message); console.error('[HologramAgent] connStore init failed: ' + e.message);
startUI(); startUI();
}); });
}); });
@ -129,16 +129,16 @@ export default class EntryAbility extends UIAbility {
} }
onWindowStageDestroy(): void { onWindowStageDestroy(): void {
console.info('[HomeAgent] ability onWindowStageDestroy'); console.info('[HologramAgent] ability onWindowStageDestroy');
} }
onForeground(): void { onForeground(): void {
startForegroundBridge(this.context); startForegroundBridge(this.context);
console.info('[HomeAgent] ability onForeground'); console.info('[HologramAgent] ability onForeground');
} }
onBackground(): void { onBackground(): void {
stopForegroundBridge(); stopForegroundBridge();
console.info('[HomeAgent] ability onBackground'); console.info('[HologramAgent] ability onBackground');
} }
} }

View File

@ -207,7 +207,7 @@ export function normalizeBaseUrl(raw: string): string {
export function defaultConnection(): ConnectionConfig { export function defaultConnection(): ConnectionConfig {
const c: ConnectionConfig = { const c: ConnectionConfig = {
id: '', id: '',
name: 'HomeAgent', name: 'HologramAgent',
url: '', url: '',
apiKey: '', apiKey: '',
type: 'webui', type: 'webui',

Some files were not shown because too many files have changed in this diff Show More