From 49fa37889bd2916f260ec031b49d00c642c2bb0a Mon Sep 17 00:00:00 2001 From: ShiningRay Date: Fri, 23 Oct 2026 17:46:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(respire):=20=E6=96=B0=E5=A2=9E=20respire?= =?UTF-8?q?=20=E6=9C=AC=E5=9C=B0=E5=8A=A0=E5=AF=86=E8=AE=B0=E5=BF=86?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=EF=BC=88hooks=EF=BC=8Bskill=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/respire/icon.png | Bin 0 -> 103466 bytes .../respire/.zcode-plugin/plugin.json | 26 + external_plugins/respire/CHANGELOG.md | 15 + external_plugins/respire/LICENSE | 21 + external_plugins/respire/README.md | 76 +++ external_plugins/respire/README_CN.md | 76 +++ external_plugins/respire/hooks/hooks.json | 41 ++ .../respire/hooks/respire.config.example.json | 10 + external_plugins/respire/hooks/respire.py | 572 ++++++++++++++++++ .../respire/skills/respire/SKILL.md | 32 + external_plugins/respire/tests/test_hooks.sh | 229 +++++++ marketplace.json | 78 ++- 12 files changed, 1164 insertions(+), 12 deletions(-) create mode 100644 assets/respire/icon.png create mode 100644 external_plugins/respire/.zcode-plugin/plugin.json create mode 100644 external_plugins/respire/CHANGELOG.md create mode 100644 external_plugins/respire/LICENSE create mode 100644 external_plugins/respire/README.md create mode 100644 external_plugins/respire/README_CN.md create mode 100644 external_plugins/respire/hooks/hooks.json create mode 100644 external_plugins/respire/hooks/respire.config.example.json create mode 100755 external_plugins/respire/hooks/respire.py create mode 100644 external_plugins/respire/skills/respire/SKILL.md create mode 100755 external_plugins/respire/tests/test_hooks.sh diff --git a/assets/respire/icon.png b/assets/respire/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c6c8c461d59856712eeb8868b681d46e7f81bf GIT binary patch literal 103466 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

-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%0ATFgu)MxL=1) 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 ar~g0HNlq0g*@VXc0000 Store on inhale, recall on exhale — local encrypted memory for ZCode. + +`respire` (CLI: `rsrs`) is a standard ZCode plugin that wires the respire memory engine into the agent loop with command hooks: title-tier recall injected every turn, one-prefix storing, and a Stop storage check. Local-first, end-side encrypted, cloud LWW sync. + +Sibling builds for other hosts: [respire-dimcode](https://github.com/risense-ai/respire-dimcode) (Dim) · [respire-qoder](https://github.com/risense-ai/respire-qoder) (Qoder) · [respire-workbuddy](https://github.com/risense-ai/respire-workbuddy) (WorkBuddy) · [respire-zcode](https://github.com/risense-ai/respire-zcode) (this plugin's standalone repo). + +## Fully usable without the model (degradation is a first-class citizen) + +- **Registration, login, sync and recall never depend on a local model** — the plugin works right after install; without the model the engine automatically falls back to the lexical tier. +- BGE-M3 is a **pure optional boost**: for better semantic recall run `rsrs model install && rsrs model activate m3`. The plugin notices it is missing and mentions it once in-session; installing or not never blocks anything. + +## What happens after install + +| When | Behavior | +| --- | --- | +| `SessionStart` | Injects a short memory playbook (when to recall, how to store, red lines); **one probe, three states** — signed-out gets registration guidance, an empty store (<10 records) gets first-day seeding/import guidance, a missing semantic model gets the **optional** install hint; zero interruption otherwise | +| `UserPromptSubmit` | Automatic `rsrs recall --titles`: **title-tier injection** — score + 8-char short id + title only; the agent fetches bodies on demand with `rsrs show `. Default is **direct output without threshold filtering**; noise is bounded by the engine's own skip gate + limit 5 + title tier | +| Prompt starts with `记:` / `存:` / `remember:` | Stores directly via `rsrs remember … --importance important` and returns a receipt; when the dedup judge finds a similar record nothing is written — candidates are relayed for the model to decide (`--parent` attach or `--force` write); the receipt requires the model to extract a "subject + action + result" fact sentence and `retitle` | +| `Stop` (agent finished) | **Storage check**: `{"decision":"block","reason":…}` continues one round, nudging the model to store new facts/decisions/agreements/lessons/pitfalls; pure Q&A or chat ends directly. Loop-safe: the continuation round carries `stop_hook_active=true` and passes through (the host caps continuations at three; this layer nudges at most once per turn); throttle `store_nudge_every` (1 = every turn, N = every N turns, 0 = off); if the store saw a write in the last 10 minutes (engine `list`), it stays silent | +| Prompt starts with `/nomem` | Skips recall for that turn | +| `skills/respire` | On-demand playbook (recall/remember/show/forget/sync and envelope parsing notes); **the skill is documentation — actions are hook-driven** | + +## Install + +Install directly from the official plugin market (**Settings → Plugins**, search respire). Alternatively add the standalone repo as a custom market: clone [risense-ai/respire-zcode](https://github.com/risense-ai/respire-zcode), then **Plugin Marketplace → Add → Add Plugin Marketplace** with the repo directory, and install from "Personal → risense-zcode". + +## First use · three things + +1. **Register & sign in** (works out of the box, no model needed): register at [rsrs.rs](https://rsrs.rs) → run `rsrs register` locally; day-to-day `rsrs login` via browser; autosync is always on, `rsrs sync` for manual sync, `rsrs status` for state. +2. **Import existing rules and memory** (one-shot store, LLM extracts): paste this into a new ZCode task — + + > Please import my existing rules and memory into respire: read one by one — `~/.zcode/AGENTS.md` (home rules, if present), workspace-root and per-project `AGENTS.md`, `~/.agents/skills/*/SKILL.md`; extract reusable facts, agreements, lessons and red lines (never passwords, ID numbers, card numbers, private keys — record only where they are stored), dedupe, and store them one by one with `rsrs remember` (`--importance important`, fact-sentence titles; on dedup candidates decide to attach or merge), then give me a list. + +3. **Local model (optional boost)**: `rsrs model install && rsrs model activate m3` — fully usable without it; install for better semantic recall. + +## Tuning (optional) + +`hooks/respire.config.json` inside the installed plugin directory (missing = all defaults; new sessions pick it up; **never touches login or account**; template: `hooks/respire.config.example.json`): + +| Field | Default | Meaning | +| --- | --- | --- | +| `rsrs` | `""` | Engine path; empty = auto-detect (PATH → `~/.local/bin` → `~/.rsrs/bin`) | +| `recall_threshold` | `0.0` | Recall floor; **0 = off by default (engine direct output)** | +| `recall_limit` | `5` | Top N per turn | +| `min_prompt_chars` | `2` | Skip 1-char prompts | +| `session_rules` | `true` | Inject the SessionStart playbook | +| `budget_chars` | `1500` | Per-turn injection budget | +| `enabled` | `true` | **Master switch**: false = fully silent | +| `store_nudge_every` | `1` | Stop-check throttle: 1 = every turn, N = every N turns, 0 = off | +| `skip_prefixes` | `[]` | Host-internal prompts matching these prefixes are neither recalled nor stored | +| `log` | `true` | `hook.log` trace in the data dir (rotated at 256KB) | + +## Dependencies, network and side effects + +- **Required dependency**: the respire engine (`rsrs` CLI, npm package [`@rsrsai/cli`](https://www.npmjs.com/package/@rsrsai/cli), MIT, by risense-ai — the same vendor as this plugin). When missing, the plugin injects the install command (`npm i -g @rsrsai/cli@latest`) for the agent to run in-session; auto-detected at PATH, `~/.local/bin`, `~/.rsrs/bin`. +- **Network**: installing the engine uses npm; `rsrs sync` talks to the respire cloud (end-side encrypted; account optional — local-only mode works). No other network access. +- **Model/API**: optional BGE-M3 sidecar model downloaded via `rsrs model install`; no external LLM API is called by this plugin. +- **Command execution**: hooks invoke `rsrs` (`recall`/`remember`/`list`/`status`) as subprocesses. +- **File writes**: `hook.log` and `.nudge_counter` in the plugin data dir (or the hooks directory as fallback); nothing else. +- **Hooks**: `SessionStart`, `UserPromptSubmit`, `Stop` — all fail-open (empty output + exit 0 on any error, never blocks a session). +- **No MCP server**, no telemetry. +- **Assets**: `assets/respire/icon.png` is the project logo of risense-ai's respire (MIT). Third-party services referenced: rsrs.rs (account/sync, optional), npm registry (engine install). + +## Self-test + +```bash +python3 ../../scripts/validate.py # marketplace structure (repo root) +bash tests/test_hooks.sh # read-only behavior assertions against a real engine +bash tests/test_hooks.sh --store # adds a write probe (writes the real store, then rsrs forget cleanup) +``` + +## License + +MIT diff --git a/external_plugins/respire/README_CN.md b/external_plugins/respire/README_CN.md new file mode 100644 index 0000000..a100ea5 --- /dev/null +++ b/external_plugins/respire/README_CN.md @@ -0,0 +1,76 @@ +# respire + +> 吸即存,呼即取——给 ZCode 装上本地加密记忆。 + +`respire`(CLI 名 `rsrs`)是 ZCode 标准插件:以命令钩子把 respire 记忆引擎挂进 agent 循环——每轮标题档自动召回、「记:」一前缀即落库、Stop 存储检查。本地优先、端侧加密、云端 LWW 同步。 + +其他宿主的姊妹版本:[respire-dimcode](https://github.com/risense-ai/respire-dimcode)(Dim)· [respire-qoder](https://github.com/risense-ai/respire-qoder)(Qoder)· [respire-workbuddy](https://github.com/risense-ai/respire-workbuddy)(WorkBuddy)· [respire-zcode](https://github.com/risense-ai/respire-zcode)(本插件的独立源仓)。 + +## 不装模型完全可用(降级是一等公民) + +- **注册、登录、同步、召回全不依赖端侧模型**——装完插件即可用,未装模型时引擎自动走词面档。 +- BGE-M3 是**纯可选增强**:想要更好的语义召回再 `rsrs model install && rsrs model activate m3`,插件检测到未装会在会话内提示一句,装与不装都不报错、不拦路。 + +## 装上后发生什么 + +| 时机 | 行为 | +| --- | --- | +| `SessionStart` | 注入记忆层简则(何时翻忆、如何落库、红线);**一次探测三态**——未登录附注册引导、库空(<10 条)附首日存料与导入引导、缺语义模型附**可选**装法;常态零打扰 | +| `UserPromptSubmit` | 自动 `rsrs recall --titles`:**标题档注入**——只给分数+8 位短 id+标题,正文由 agent `rsrs show ` 按需取;默认**直出不过滤**,噪音边界=rsrs skip 门+limit 5+标题档 | +| `prompt` 以「记:」「存:」「remember:」开头 | 直接 `rsrs remember … --importance important` 落库并回执;判后即存判为相似时**不写入**、回执转述候选(`--parent` 挂靠或 `--force` 直写),由模型决断;回执硬约束 AI 抽「主语+动作+结果」事实句 `retitle` 替换暂拟题 | +| `Stop`(AI 干完活) | **存储检查**:`{"decision":"block","reason":…}` 续跑一轮,提示模型把本轮新的**事实/决定/约定/教训/踩坑**分条 `rsrs remember`;闲聊无新事实则直接结束。防循环:续跑轮 stdin 带 `stop_hook_active=true` 即放行(宿主限续跑至多三次,本层每轮至多催一次);节流 `store_nudge_every`(默认 1=每轮,N=每 N 轮,0=关);近 10 分钟本库有过落库(问引擎 `list`)则不再催 | +| `prompt` 以 `/nomem` 开头 | 本轮跳过召回 | +| `skills/respire` | 按需加载的操作法(recall/remember/show/forget/sync 与信封解析要点);**注意技能只是说明书,动作全由钩子驱动** | + +## 安装 + +官方插件市场直接安装(**设置 → 插件** 搜索 respire)。也可自建市场源安装:克隆 [risense-ai/respire-zcode](https://github.com/risense-ai/respire-zcode) 后,在 **插件市场 → 添加 → 添加插件市场** 粘贴仓目录,再在「个人 → risense-zcode」安装。 + +## 首次使用 · 三件事 + +1. **注册登录**(开箱即用,不依赖模型):[rsrs.rs](https://rsrs.rs) 注册 → 本机 `rsrs register`;日常 `rsrs login` 浏览器授权;autosync 常开,`rsrs sync` 手动同步,`rsrs status` 查态。 +2. **导入既有记忆与规则(一次成库,LLM 代抽)**:开个 ZCode 新任务粘贴下面整段—— + + > 请把本机的既有规则与记忆导入 respire:逐个读取——`~/.zcode/AGENTS.md`(家目录规则,若在)、工作区根与各项目的 `AGENTS.md`、`~/.agents/skills/*/SKILL.md`;抽出可复用的事实、约定、教训与红线(口令、证件号、卡号、私钥一律排除、只记位置),去重后用 `rsrs remember` 分条存入(`--importance important`、标题按事实句;判重给候选先决断挂靠或合并),存完给我一份清单。 + +3. **端侧模型(可选增强)**:`rsrs model install && rsrs model activate m3`——不装完全可用,想要更好语义召回再装。 + +## 调优(可选) + +插件安装目录下 `hooks/respire.config.json`(缺失即全默认,改完新会话生效;**不涉及登录与账号**;模板见 `hooks/respire.config.example.json`): + +| 字段 | 默认 | 含义 | +| --- | --- | --- | +| `rsrs` | `""` | 引擎路径;空 = 自动探测(PATH → `~/.local/bin` → `~/.rsrs/bin`) | +| `recall_threshold` | `0.0` | 召回下限;**0=默认关(rsrs 直出)**,设正数才启用过滤 | +| `recall_limit` | `5` | 取前 N 条 | +| `min_prompt_chars` | `2` | 1 字才跳,2 字起即查 | +| `session_rules` | `true` | 是否注入 SessionStart 简则 | +| `budget_chars` | `1500` | 单轮注入总预算 | +| `enabled` | `true` | **总开关**:false=全线静默 | +| `store_nudge_every` | `1` | Stop 存储检查节流:1=每轮、N=每 N 轮、0=关 | +| `skip_prefixes` | `[]` | 宿主内部提示白名单:命中前缀的 prompt 不召回不落库 | +| `log` | `true` | 数据目录 `hook.log` 留痕(超 256KB 清轮) | + +## 依赖、网络与副作用 + +- **必需依赖**:respire 引擎(`rsrs` CLI,npm 包 [`@rsrsai/cli`](https://www.npmjs.com/package/@rsrsai/cli),MIT,与本插件同属 risense-ai)。缺失时插件会在会话内注入装法(`npm i -g @rsrsai/cli@latest`)由 agent 代装;探测顺序 PATH → `~/.local/bin` → `~/.rsrs/bin`。 +- **网络**:装引擎走 npm;`rsrs sync` 连 respire 云端(端侧加密;账号可选,纯本机模式可用)。除此之外无网络访问。 +- **模型/API**:可选 BGE-M3 端侧模型经 `rsrs model install` 下载;本插件不调用任何外部 LLM API。 +- **命令执行**:钩子以子进程调用 `rsrs`(`recall`/`remember`/`list`/`status`)。 +- **文件写入**:插件数据目录(回退 hooks 目录)的 `hook.log` 与 `.nudge_counter`;无其他写入。 +- **钩子**:`SessionStart`、`UserPromptSubmit`、`Stop`——全部 fail-open(任何异常空输出+exit 0,绝不拦会话)。 +- **无 MCP server**、无遥测。 +- **素材**:`assets/respire/icon.png` 为 risense-ai respire 项目 logo(MIT)。引用的第三方服务:rsrs.rs(账号/同步,可选)、npm registry(引擎安装)。 + +## 自测 + +```bash +python3 ../../scripts/validate.py # 市场结构(仓根) +bash tests/test_hooks.sh # 行为只读断言(需真机 rsrs 引擎) +bash tests/test_hooks.sh --store # 加写探针(写真库→回执→rsrs forget 自清理) +``` + +## License + +MIT diff --git a/external_plugins/respire/hooks/hooks.json b/external_plugins/respire/hooks/hooks.json new file mode 100644 index 0000000..36c41f0 --- /dev/null +++ b/external_plugins/respire/hooks/hooks.json @@ -0,0 +1,41 @@ +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|resume|clear|compact", + "hooks": [ + { + "type": "command", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" SessionStart || python \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" SessionStart", + "timeout": 20, + "statusMessage": "respire: injecting memory playbook" + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" UserPromptSubmit || python \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" UserPromptSubmit", + "timeout": 20, + "statusMessage": "respire: recalling memory" + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" Stop || python \"${CLAUDE_PLUGIN_ROOT}/hooks/respire.py\" Stop", + "timeout": 20, + "statusMessage": "respire: checking storage" + } + ] + } + ] + } +} diff --git a/external_plugins/respire/hooks/respire.config.example.json b/external_plugins/respire/hooks/respire.config.example.json new file mode 100644 index 0000000..9c625ea --- /dev/null +++ b/external_plugins/respire/hooks/respire.config.example.json @@ -0,0 +1,10 @@ +{ + "rsrs": "", + "recall_threshold": 0.0, + "recall_limit": 5, + "min_prompt_chars": 2, + "session_rules": true, + "budget_chars": 1500, + "enabled": true, + "skip_prefixes": [] +} diff --git a/external_plugins/respire/hooks/respire.py b/external_plugins/respire/hooks/respire.py new file mode 100755 index 0000000..461d779 --- /dev/null +++ b/external_plugins/respire/hooks/respire.py @@ -0,0 +1,572 @@ +#!/usr/bin/env python3 +# respire —— respire(rsrs) × ZCode 记忆插件(hook 端单入口) +# 与 respire-qoder / respire-dimcode / respire-workbuddy 同源方案: +# SessionStart 注简则、UserPromptSubmit 每轮召回、Stop 存储检查。 +# 用法: python3 respire.py (事件 JSON 走 stdin) +# ZCode 协议差异(已核 ZCode runtime 源码): +# 1. stdout 不认纯文本——只有以 { 开头且过严格 schema 的 JSON 才被消费, +# 故一切注入走 {"additionalContext": "..."} 顶层键(三事件均注入); +# 输出非法 JSON 会被判 hook 失败,纯文本被静默丢弃。 +# 2. Stop 阻塞续跑的决策枚举是 approve|block——{"decision":"block","reason":...}; +# 其他宿主用的 "deny" 在 ZCode 会过不了 schema。 +# 3. stdin 全量带事件字段(prompt / stop_hook_active / hook_event_name)。 +# 铁律: fail-open —— 任何异常 → 空 stdout、exit 0(协议里即「无决策、放行」),绝不拦会话。 +from __future__ import annotations + +import datetime +import json +import os +import re +import shutil +import subprocess +import sys +import time + +VERSION = "0.1.0" + +# 超时窗(秒):须盖住 rsrs runtime 冷启动(实测 doctor 模型加载 7s+),否则首轮静默、热后即灵 +RECALL_T = 14 +PROBE_T = 10 +STORE_T = 18 +LIST_T = 6 # Stop 查「最近一条落库时间」的窗口(实测 list ~0.9s,热后即回) +LOG_MAX = 256 * 1024 # hook.log 超此整文件清轮 + +DEFAULTS = { + "rsrs": "", # respire 引擎(rsrs)路径;空 = 自动探测 + "recall_threshold": 0.0, # 召回下限,0=默认关(rsrs 直出):skip 门管无关、limit 限流、标题档廉价;设正数才启用过滤 + "recall_limit": 5, # 取前 N 条 + "min_prompt_chars": 2, # 过短不检索(1 字才跳;2 字起即查,rsrs 自带 skip 门、本地 100–400ms) + "session_rules": True, # SessionStart 是否注入用法简则 + "budget_chars": 1500, # 单轮注入总字数预算 + "log": True, # hook.log 轻量留痕(每事件一行,复盘时灵时不灵的抓手) + "enabled": True, # 总开关:false=记忆层全线静默(ZCode 插件页亦可逐组件关 hook) + "store_nudge_every": 1, # Stop 存储检查节流:1=每轮、N=每 N 轮一次、0=关闭(每轮=多一轮模型小调用) + "skip_prefixes": [], # 宿主内部提示白名单:命中这些前缀的 prompt 不召回也不落库(如宿主给自动化任务写的 Recap;默认空,按需配置) +} + +# 显式落库前缀:prompt 以「记:」等开头 → 直接入库并回执 +STORE_PREFIX = re.compile(r"^(?:记|存|记录|记住|remember|store)\s*[::]\s*", re.I) +# 本轮跳过召回 +SKIP_PREFIX = re.compile(r"^(?:/nomem|#nomem)\b") +# 技能指认前缀:$skill: 名称 后才是真任务——技能名会词面污染召回 query,须剥掉 +SKILL_PREFIX = re.compile(r"^\$\s*skill\s*:\s*\S+\s*", re.I) + +RULES = """【respire 记忆层 · 已挂接】本机 respire(rsrs)记忆库随本会话生效;数据本地加密、云端同步(账号注册与登录不归本层管,见 rsrs.rs)。 +1. 答前先翻忆:rsrs recall "项目名+关键词"(--json 结构化、--titles 只要 id+标题);报错、异常先按报错原文翻忆。 +2. 收尾可存:rsrs remember "内容" --title "事实句" --importance important(判后即存:相似会给候选,先决断再发)。**title 是召回与判重的门面,须你从内容抽取「主语+动作+结果」的完整事实句,禁主题分类名、禁硬截断**;prompt 以「记:」开头时由本层先落库保底——回执带暂拟标题与短 id,**你须抽取更准的事实句并 rsrs retitle <短id> --title 替换**。 +3. 每轮自动召回已随 prompt 注入;**未见召回注入、命中不贴题、或话题涉及此前的项目、报错、约定、日期时,换词 rsrs recall 补查再答(同义词、上位词、英文对应词各试一路),可见地调用**;仅当已有注入确实相关方才勿重复。本轮加 /nomem 前缀可关闭自动召回。 +4. 口令、证件号、手机号、卡号、私钥禁入记忆正文与查询词,只写存放位置——库虽加密,密钥泄露即全裸。 +5. 同步 rsrs sync(autosync 常开)、rsrs status 查态、rsrs prompt 读完整工作流;深用法见 respire 技能。""" + +NO_RSRS = """【respire·引擎未装】插件已就绪,只差引擎。请立即代为执行安装,无需等用户: + npm i -g @rsrsai/cli@latest +(无全局写权限时先试:npm config set prefix ~/.npm-global && npm i -g @rsrsai/cli@latest) +装完跑 rsrs --version 确认——本层每轮实时探测,装完同会话内即恢复召回与「记:」落库。 +配置下一步(你顺手引导即可):rsrs status 查登录态;未登录则请用户去 https://rsrs.rs 注册后本机跑 rsrs register --user <名>(登录密码本人设≥8位;**超级密码绝不自造、绝不传 --super**——系统自动生成 A3-… 恢复码并在输出中醒目列出,**务必抄下保存**,丢失则任何设备都解不开记忆);日常 rsrs login;语义模型可选,想要更好召回再 rsrs model install && rsrs model activate m3。若已误填自定义超级密码,跑 rsrs super-reset 重发 A3 码。""" + +LOGIN_NUDGE = """注意:本机 respire 尚未登录——记忆只存本机、不同步、不跨设备。引导用户:去 rsrs.rs 注册后跑 rsrs register(登录密码本人设;**超级密码由系统自动生成 A3-… 恢复码,不要自造,务必保存**);日常登录可由你代跑 rsrs login --oauth --no-open,把授权链接交给用户点击。""" + +EMPTY_NUDGE = """注意:本机 respire 记忆库还是空的——没有记忆可召回,自动召回无动静是正常而非故障。引导:用「记:〈事实〉」由本层落库,或让 agent 先把既有项目的约定、报错教训、关键结论 rsrs remember 存上几条;既有规则与记忆可一次成库——让 agent 读 ~/.zcode/AGENTS.md、工作区根与项目的 AGENTS.md、~/.agents/skills/*/SKILL.md,分析抽条后分条存入(口令证件卡号排除);有货之后每轮召回才有内容。""" + +NO_MODEL = """注意:respire 语义模型(BGE-M3)未装——现为词面档。**完全可用**:注册、登录、同步与召回都不依赖模型;想要更好效果再装:rsrs model install 下载模型,再 rsrs model activate m3 建索引激活召回升为语义档(可代跑,建索引可后台挂起)。""" + +PROBE_FAIL = """注意:respire 状态探测失败(rsrs status 未回)——若是全新机器尚未注册,先引导用户去 rsrs.rs 注册并跑 rsrs register(首设备);否则可能 runtime 冷启动中,首轮召回空转稍候即通,持续无回跑 rsrs doctor 诊断并转告用户。""" + + +_REDACT = [ + (re.compile(r"(?i)(password|passwd|pwd|密码|口令)\s*[:=:]\s*\S+"), r"\1=[REDACTED]"), + (re.compile(r"\b(sk|ghp|gho|github_pat|AKIA|xox)[A-Za-z0-9_-]{8,}\b"), "[REDACTED]"), + (re.compile(r"\b\d{11,19}\b"), "[NUM]"), +] + + +def redact(s: str) -> str: + """日志脱敏:口令式赋值、token 前缀、11-19 位长数字(手机/卡号)。 + 日志只用于诊断,绝不落明文凭据。""" + try: + for pat, rep in _REDACT: + s = pat.sub(rep, s) + except Exception: + pass + return s + + +def data_dir() -> str: + """运行时数据目录(hook.log / .nudge_counter):显式环境变量 → ZCode 插件数据目录 → 脚本目录回退。""" + for env in ("RESPITE_DATA", "ZCODE_PLUGIN_DATA"): + d = os.environ.get(env, "") + if d: + try: + os.makedirs(d, exist_ok=True) + return d + except Exception: + pass + return os.path.dirname(os.path.abspath(__file__)) + + +def log_event(cfg: dict, event: str, outcome: str, detail: str = "", ms=None): + """轻量留痕:hook.log 单行 JSONL、超 256KB 整文件清轮;日志失败绝不影响主链。 + config log:false 关闭。字段:ts/ev/out/ms(耗时毫秒)/d(摘要)。""" + if not cfg.get("log", True): + return + try: + path = os.environ.get("RESPITE_LOG") or os.path.join(data_dir(), "hook.log") + try: + if os.path.exists(path) and os.path.getsize(path) > LOG_MAX: + os.remove(path) + except Exception: + pass + rec = {"ts": time.strftime("%Y-%m-%dT%H:%M:%S"), "ev": event, "out": outcome} + if ms is not None: + rec["ms"] = int(ms) + if detail: + rec["d"] = redact(detail)[:160] + with open(path, "a", encoding="utf-8") as f: + f.write(json.dumps(rec, ensure_ascii=False) + "\n") + except Exception: + pass + + +def _typed(val, ref) -> bool: + if isinstance(ref, bool): + return isinstance(val, bool) + if isinstance(ref, (int, float)): + return isinstance(val, (int, float)) and not isinstance(val, bool) + return isinstance(val, type(ref)) + + +def load_config() -> dict: + cfg = dict(DEFAULTS) + candidates = [os.environ.get("RESPITE_CONFIG") or "", + os.path.join(data_dir(), "respire.config.json"), + os.path.join(os.path.dirname(os.path.abspath(__file__)), "respire.config.json")] + for path in candidates: + if not path: + continue + try: + with open(path, encoding="utf-8") as f: + doc = json.load(f) + if isinstance(doc, dict): + for key, ref in DEFAULTS.items(): + if key in doc and _typed(doc[key], ref): + cfg[key] = doc[key] + break + except Exception: + continue # 配置缺失/损坏 → 用默认值,绝不拦 + return cfg + + +def find_rsrs(cfg: dict): + """引擎探测链:显式配置 → 环境变量 → PATH → 常见安装位。 + 顺序即优先级;rsrs 可能多处并存(npm-global / ~/.local/bin / 自编译 ~/.rsrs/bin),显式路径优先。""" + home = os.path.expanduser("~") + cands = [cfg.get("rsrs") or "", + os.environ.get("RESPITE_RSRS") or "", + os.environ.get("RESPIRE_BIN") or "", + shutil.which("rsrs") or "", + shutil.which("respire") or "", + f"{home}/.local/bin/respire", f"{home}/.local/bin/rsrs", + f"{home}/.rsrs/bin/rsrs", "/usr/local/bin/rsrs", "/usr/bin/rsrs"] + seen = set() + for c in cands: + if not c or c in seen: + continue + seen.add(c) + if os.path.isfile(c) and os.access(c, os.X_OK): + return c + return None + + +def run_rsrs(bin_path: str, args: list, timeout: int): + # rsrs 的 --json 模式下进度写 stderr,capture 须分离,只解析 stdout + return subprocess.run([bin_path] + args, capture_output=True, text=True, + encoding="utf-8", errors="replace", timeout=timeout) + + +def extract_title(content: str) -> str: + """无 LLM 下尽量抽完整事实句:整句优先 → 退到分隔符 → 绝不在词中硬截(上限 70 字)。 + 引擎不代抽题(retitle --title 必填),抽毕由回执引导 agent 用 retitle 精修。""" + s = re.sub(r"\s+", " ", content.strip()) + if not s: + return "" + m = re.match(r"^(.{6,}?[。!?!?])", s) # 第一个 ≥6 字的完整句 + if m: + return m.group(1)[:70].strip() + head = s[:70] + cuts = [head.rfind(c) for c in ",;、,;"] # 退到分隔符,不切半词 + cut = max(cuts) + if cut >= 20: + return head[:cut + 1].rstrip(",,;; ").strip() + return head # 无标点长串才落到 70 字上界 + + +def do_store(bin_path: str, content: str, cfg: dict) -> str: + """显式落库:判后即存三态——ok 直写 / pending 给候选待决 / 其余为失败,皆回执不静默。""" + if not content: + log_event(cfg, "store", "empty_content") + return "" + title = extract_title(content) or "respire 快存" + t0 = time.time() + try: + r = run_rsrs(bin_path, ["remember", content, "--title", title, + "--importance", "important", "--json"], STORE_T) + except Exception as e: + log_event(cfg, "store", "timeout_or_error", f"{type(e).__name__}: {e}", (time.time() - t0) * 1000) + return "【respire·落库】respire 超时未回,请改手动 rsrs remember 核实是否入库。" + ms = (time.time() - t0) * 1000 + try: + doc = json.loads(r.stdout) if r.stdout else {} + except Exception: + doc = {} + if not isinstance(doc, dict): + doc = {} + status = str(doc.get("status") or "") + if status == "ok": + sid = str((doc.get("summary") or {}).get("id") or "") + log_event(cfg, "store", "ok", sid[:8], ms) + tail = f"({sid[:8]})" if sid else "" + return (f"【respire·落库】已存入:{title}{tail}。标题系规则暂拟——" + "请从内容抽取「主语+动作+结果」的完整事实句," + f"rsrs retitle {sid[:8]} --title 替换(title 是召回与判重的门面)。") + if status == "pending": + # 判后即存判定疑似重复/可挂靠,未写入(written:false)——转述候选,模型接续决断 + log_event(cfg, "store", "pending", f"cand={len(doc.get('items') or [])}", ms) + lines = ["【respire·落库待决】respire 判定本条与既有记忆相似,未写入。候选:"] + for it in (doc.get("items") or [])[:5]: + if isinstance(it, dict): + lines.append(f"- [{it.get('name', '?')}] {it.get('value', '')}") + lines.append("请决断:挂靠则 rsrs remember 同文 --parent ;确属独立则加 --force 直写。") + return "\n".join(lines) + errs = doc.get("errors") or [] + msg = json.dumps(errs, ensure_ascii=False)[:200] if errs else f"status={status or r.returncode}" + log_event(cfg, "store", "fail", msg, ms) + return f"【respire·落库失败】respire 未收:{msg}" + + +def do_recall(bin_path: str, prompt: str, cfg: dict) -> str: + """每轮召回:--titles 标题档(respire 设计——只注分数+短 id+标题,正文由 agent rsrs show 按需取)。 + rsrs 自带相关性门(无关→skip 零命中),本层再按阈值滤掉碰瓷命中。""" + t0 = time.time() + try: + r = run_rsrs(bin_path, ["recall", prompt, "--limit", + str(int(cfg["recall_limit"])), "--titles", "--json"], RECALL_T) + except Exception as e: + log_event(cfg, "recall", "timeout_or_error", f"{type(e).__name__}", (time.time() - t0) * 1000) + return "" + ms = (time.time() - t0) * 1000 + if r.returncode != 0: + log_event(cfg, "recall", "rc_nonzero", f"rc={r.returncode} {r.stderr[:80] if r.stderr else ''}", ms) + return "" + try: + doc = json.loads(r.stdout) + except Exception: + log_event(cfg, "recall", "parse_error", (r.stdout or "")[:80], ms) + return "" + if not isinstance(doc, dict) or doc.get("status") not in ("ok", "skip"): + log_event(cfg, "recall", "status_fail", str(doc.get("status") if isinstance(doc, dict) else "?"), ms) + return "" # fail 静默(fail-open);skip = 自带门判为无关 + details = doc.get("details") or [] + if not isinstance(details, list): + log_event(cfg, "recall", "bad_details", "", ms) + return "" + thr = float(cfg["recall_threshold"]) # 0=直出不过滤;噪音边界交给 rsrs 自带 skip 门+limit+标题档 + keep = [] + max_score = 0.0 + for d in details: + if not isinstance(d, dict): + continue + score = d.get("score") + if isinstance(score, (int, float)): + max_score = max(max_score, float(score)) + if isinstance(score, (int, float)) and float(score) >= thr: + keep.append((float(score), d)) + if len(keep) >= int(cfg["recall_limit"]): + break + if not keep: + log_event(cfg, "recall", + "skip" if doc.get("status") == "skip" else "below_thr", + f"max={max_score:.3f} thr={thr:g} q={prompt[:40]}", ms) + return "" + budget = int(cfg["budget_chars"]) + lines, used = [], 90 # 90 留给头行上界,砍点据此提前 + for score, d in keep: + title = str(d.get("title") or "").strip() or "(无题)" + eid = str(d.get("id") or "")[:8] + stale = " →已被取代" if d.get("superseded_by") else "" + line = f"- [{score:.2f}] {title}({eid}){stale}" + if lines and used + len(line) + 1 > budget: + break + lines.append(line) + used += len(line) + 1 + if not lines: + log_event(cfg, "recall", "budget_empty", "", ms) + return "" + gate = f"阈值 {thr:g}," if thr > 0 else "rsrs 直出," + head = (f"【respire·自动召回】命中 {len(lines)} 条({gate}标题档," + "正文 rsrs show <短id> 按需读;分数是相关度不是可信度):") + log_event(cfg, "recall", "hit", f"n={len(lines)} max={max_score:.3f} q={prompt[:40]}", ms) + return "\n".join([head] + lines) + + +def probe_status(bin_path: str): + """SessionStart 一次 status 三态共用(登录态+库量+探测成败;开销实测热态约 190ms)。 + 返回 (logged, total, probe_ok):logged True/False/None;total int/None; + probe_ok False=status 未回(冷启动/异常),注入 PROBE_FAIL 段。 + 后门:RESPITE_FAKE_LOGGED_OUT=1 / RESPITE_FAKE_EMPTY=1 / RESPITE_FAKE_PROBE_FAIL=1。""" + fake_out = os.environ.get("RESPITE_FAKE_LOGGED_OUT") == "1" + fake_empty = os.environ.get("RESPITE_FAKE_EMPTY") == "1" + if os.environ.get("RESPITE_FAKE_PROBE_FAIL") == "1": + log_event(DEFAULTS, "probe", "fake_fail") + return (None, None, False) + t0 = time.time() + try: + r = run_rsrs(bin_path, ["status", "--json"], PROBE_T) + doc = json.loads(r.stdout) if r.stdout else {} + if not isinstance(doc, dict) or doc.get("status") != "ok": + log_event(DEFAULTS, "probe", "status_not_ok", + str(doc.get("status") if isinstance(doc, dict) else "?"), + (time.time() - t0) * 1000) + return (None, None, False) + summ = doc.get("summary") or {} + sess = summ.get("session") or {} + logged = bool(sess.get("has_token")) and bool(sess.get("user")) + total = summ.get("local_total") + total = int(total) if isinstance(total, int) else None + except Exception as e: + log_event(DEFAULTS, "probe", "error", f"{type(e).__name__}: {e}", + (time.time() - t0) * 1000) + return (None, None, False) + ms = (time.time() - t0) * 1000 + if fake_out: + logged = False + if fake_empty: + total = 0 + log_event(DEFAULTS, "probe", "ok", f"logged={logged} total={total}", ms) + return (logged, total, True) + + +def check_model(): + """语义模型态(SessionStart 专用):~/.rsrs/models/bge-m3 目录存在即已装(stat 零开销)。 + 未装时引擎自带词面回退(源码 indexed==0 → lexical_only),即未配嵌入的引擎效果。 + RESPITE_FAKE_NO_MODEL=1 为自测后门。""" + if os.environ.get("RESPITE_FAKE_NO_MODEL") == "1": + return False + try: + return os.path.isdir(os.path.expanduser("~/.rsrs/models/bge-m3")) + except Exception: + return None # 探测失败不打扰 + + +STORE_NUDGE = ("记忆检查(自动一次):只做一件事——若你本轮产生了新的**事实、决定、约定、教训或踩坑**," + "用 rsrs remember 分条存入(--importance important;标题按「主语+动作+结果」事实句;" + "判重给候选先决断:--parent 挂靠或 --force 直写);" + "若本轮只是问答闲聊而无新事实,直接结束、勿重复作答。存完或无需存即结束。") + + +def _epoch(rfc3339): + """'2026-10-22T15:06:38.926Z' → epoch 秒;解析失败返回 None(不猜时区)。""" + if not isinstance(rfc3339, str) or not rfc3339.strip(): + return None + s = rfc3339.strip() + if s.endswith(("Z", "z")): + s = s[:-1] + "+00:00" + try: + dt = datetime.datetime.fromisoformat(s) + except Exception: + return None + if dt.tzinfo is None: + dt = dt.replace(tzinfo=datetime.timezone.utc) # 无偏移一律按 UTC(rsrs 输出恒带 Z) + return dt.timestamp() + + +def engine_last_store(engine, timeout: int = LIST_T): + """问引擎最近一条记录的落库时间(秒级 epoch),取 top3 的 updated/created 最大值。 + agent 直接跑 rsrs remember 不写本层 hook.log,节流必须问库才看得见。失败返回 None(放行给上层催)。""" + r = run_rsrs(engine, ["list", "--limit", "3", "--json"], timeout) + doc = json.loads(r.stdout) if r.stdout else None + dets = doc.get("details") if isinstance(doc, dict) else None + best = None + for d in (dets or []): + if not isinstance(d, dict): + continue + for k in ("updated_at", "created_at"): + v = _epoch(d.get(k)) + if v is not None and (best is None or v > best): + best = v + return best + + +def on_stop(data: dict, cfg: dict, engine): + """Stop 存储检查:干完活的存储提示。ZCode 协议(已核 runtime 源码): + {"decision":"block","reason":...} → blockRequested+续跑一轮,reason 注入上下文; + 续跑轮 stdin 带 stop_hook_active=true → 放行。返回 ""(放行)或 dict(阻塞 JSON)。""" + if data.get("stop_hook_active") is True: + log_event(cfg, "stop", "active_skip") + return "" + if not engine: + return "" + every = int(cfg.get("store_nudge_every", 1)) + if every <= 0: + return "" + try: # 节流计数(失败则不拦) + state = os.environ.get("RESPITE_NUDGE_STATE") or os.path.join(data_dir(), ".nudge_counter") + n = 0 + try: + with open(state, encoding="utf-8") as f: + n = int((f.read() or "0").strip()) + except Exception: + n = 0 + n += 1 + with open(state, "w", encoding="utf-8") as f: + f.write(str(n)) + if n % every != 0: + log_event(cfg, "stop", "every_skip", f"n={n} every={every}") + return "" + except Exception: + pass + now = time.time() + try: # 近 10 分钟本层已落库过 → 不再催(记忆活跃):钩子侧「记:」路径写本层日志 + logp = os.environ.get("RESPITE_LOG") or os.path.join(data_dir(), "hook.log") + if os.path.exists(logp): + with open(logp, encoding="utf-8", errors="replace") as f: + for ln in f.readlines()[-50:]: + try: + r = json.loads(ln) + except Exception: + continue + if r.get("ev") == "store" and r.get("out") == "ok": + try: + ts = time.mktime(time.strptime(r.get("ts", ""), "%Y-%m-%dT%H:%M:%S")) + except Exception: + continue + if now - ts < 600: + log_event(cfg, "stop", "stored_recent_skip", "src=hooklog") + return "" + except Exception: + pass + try: # 近 10 分钟引擎侧有落库(agent 直接 rsrs remember/update)→ 同样不催,问库而非看日志 + if engine: + t1 = time.time() + ts = engine_last_store(engine) + if ts is not None and now - ts < 600: + log_event(cfg, "stop", "stored_recent_skip", "src=engine", (time.time() - t1) * 1000) + return "" + except Exception: + pass + log_event(cfg, "stop", "nudge") + return {"decision": "block", "reason": STORE_NUDGE} + + +def on_prompt(data: dict, cfg: dict, engine) -> str: + prompt = data.get("prompt") + if not isinstance(prompt, str): + log_event(cfg, "prompt", "bad_prompt", "非字符串") + return "" + prompt = prompt.strip() + if not prompt or SKIP_PREFIX.match(prompt): + log_event(cfg, "prompt", "nomem_or_empty", prompt[:40]) + return "" + for pat in (cfg.get("skip_prefixes") or []): # 宿主内部提示白名单:不召回不落库 + if isinstance(pat, str) and pat and prompt[:len(pat)].lower() == pat.lower(): + log_event(cfg, "prompt", "internal_skip", pat) + return "" + m = STORE_PREFIX.match(prompt) + if m: + rest = prompt[m.end():].strip() + if not engine: + log_event(cfg, "store", "engine_missing", prompt[:40]) + return NO_RSRS + return do_store(engine, rest, cfg) + if len(prompt) < int(cfg["min_prompt_chars"]): + log_event(cfg, "prompt", "too_short", prompt[:40]) + return "" + if not engine: + log_event(cfg, "recall", "engine_missing", prompt[:40]) + return "" + query = SKILL_PREFIX.sub("", prompt).strip() or prompt # 技能指认轮剥前缀,防技能名污染 query + return do_recall(engine, query, cfg) + + +def main() -> None: + try: + event = (sys.argv[1] if len(sys.argv) > 1 else "") or os.environ.get("ZCODE_HOOK_EVENT", "") + try: + # 事件 JSON 恒为 UTF-8;Windows 非 UTF-8 区域(如 cp1252)下 read() 会按本地码页误解码 + raw = "" if sys.stdin.isatty() else sys.stdin.buffer.read().decode("utf-8", "replace") + except Exception: + try: + raw = "" if sys.stdin.isatty() else sys.stdin.read() + except Exception: + raw = "" + try: + data = json.loads(raw) if raw.strip() else {} + except Exception: + data = {} + if not isinstance(data, dict): + data = {} + if not event: + event = str(data.get("hook_event_name") or "") + except Exception: + data, event = {}, "" + + try: + cfg = load_config() + if not cfg.get("enabled", True): # 总开关:关闭=全线静默(管理器档) + log_event(cfg, event or "unknown", "disabled") + raise SystemExit(0) + engine = find_rsrs(cfg) + if event == "SessionStart": + if not cfg["session_rules"]: + out = "" + elif not engine: + out = NO_RSRS + log_event(cfg, "session", "engine_missing") + else: + out = RULES + logged, total, probe_ok = probe_status(engine) # 一次 status:登录+库量+成败 + segs = [] + if not probe_ok: + out += "\n" + PROBE_FAIL # 探测失败可见(冷启动/异常) + segs.append("probe_fail") + else: + if logged is False: # 仅未登录才附引导,已登录零打扰 + out += "\n" + LOGIN_NUDGE + segs.append("login") + if total is not None and total < 10: # 仅空库首日才附引导 + out += "\n" + EMPTY_NUDGE + segs.append("empty") + if check_model() is False: # 仅缺模型才附装法,已装零打扰 + out += "\n" + NO_MODEL + segs.append("no_model") + log_event(cfg, "session", "ok", "segs=" + (",".join(segs) or "none")) + elif event == "UserPromptSubmit": + out = on_prompt(data, cfg, engine) + elif event == "Stop": + out = on_stop(data, cfg, engine) + else: + out = "" + except Exception as e: + out = "" # fail-open——但必须留痕,否则「时灵时不灵」无从复盘 + try: + log_event(DEFAULTS, event or "unknown", "fatal", f"{type(e).__name__}: {e}") + except Exception: + pass + + if out: + try: # ZCode 只认 JSON:文本 → additionalContext 顶层键;Stop 阻塞已是 dict,原样发 + payload = out if isinstance(out, dict) else {"additionalContext": out} + buf = getattr(sys.stdout, "buffer", None) + data = json.dumps(payload, ensure_ascii=False).encode("utf-8") + if buf is None: + sys.stdout.write(data.decode("utf-8")) + sys.stdout.flush() + else: + buf.write(data) + buf.flush() + except Exception: + pass + sys.exit(0) # 恒 exit 0:合法 JSON 即被消费,无输出即无决策,绝不以退出码拦会话 + + +if __name__ == "__main__": + main() diff --git a/external_plugins/respire/skills/respire/SKILL.md b/external_plugins/respire/skills/respire/SKILL.md new file mode 100644 index 0000000..5677d63 --- /dev/null +++ b/external_plugins/respire/skills/respire/SKILL.md @@ -0,0 +1,32 @@ +--- +name: respire +description: respire(rsrs)本地加密记忆操作法。每轮自动召回已随 prompt 注入;**未见注入、命中不贴题、或涉旧(项目、报错、约定、日期)都须换词 rsrs recall 可见补查**(同义词/上位词/英文词分路试),仅当已有注入确实相关方才勿重复;手动翻忆 recall、落库 remember(判后即存)、读全文 show/chain、删除 forget、同步 sync、查态 status;prompt 以「记:」开头由插件直接落库,「/nomem」前缀关闭本轮召回。触发词:记忆、翻忆、recall、remember、存记忆、忘掉、同步。 +--- + +# respire · 记忆操作法(ZCode) + +本插件行为:SessionStart 注入简则;每轮 UserPromptSubmit 自动召回(rsrs 自带无关过滤(零命中即 skip),本层再按阈值兜底);prompt 以「记:」「存:」「remember:」开头由插件先落库保底并回执——**回执带暂拟标题与短 id 时,你须从事实抽取「主语+动作+结果」完整句 `rsrs retitle <短id> --title` 替换(硬约束,title 是召回与判重的门面)**;`/nomem` 关本轮;Stop 时若本轮有新事实而近 10 分钟库无落库,插件催存一轮。 + +## 命令 + +- `rsrs recall "项目名+关键词" --limit 5 --json` —— **答前必查、可见地调**(注入为空绝不等于无记忆,须自行补查);报错先按报错原文翻忆;`--titles` 只要短 id+标题 +- `rsrs remember "内容" --title "事实句" --importance important` —— 一事一条,先判后存 +- `rsrs show <短id>`(前 8 位)读全文;`rsrs chain` 一次取整条因果链 +- `rsrs forget ` —— 墓碑删除,随同步传播 +- `rsrs candidates <内容>` —— 存前候选账(合并/挂靠建议) +- `rsrs sync` 双向同步(autosync 常开);`rsrs status` 查态;`rsrs prompt` 读完整记忆工作流 +- `rsrs tree/resort/retitle` —— 整理因果树 + +## 解析要点(CLI 信封) + +- 一切命令 `--json` 走统一信封:`status`、`summary`、`items`、`details`、`errors`;**失败也是 HTTP 200+`status:"fail"`+`errors[]`**,须查 status 不查退出码 +- recall 的分数与条目在 `details[].score`、`details[].id`(全 id,取前 8 位当短 id)、`details[].title`、`details[].superseded_by`;`items` 只是「分数 标题」行 +- 进度写 stderr,勿 `2>&1` 混进 JSON +- remember 判后即存:`status:"ok"`=已写入(summary.id);`status:"pending"`+`written:false`=判为相似**未写入**,须 `--parent ` 挂靠或 `--force` 直写 +- 默认 `trivial` 档进日记区、不进召回主区且 title 被改「活动轨迹 日期」——事实类落库用 `--importance important` + +## 红线 + +- 口令、证件号、手机号、卡号、私钥禁入记忆正文与查询词,只写存放位置——库虽加密,密钥泄露即全裸 +- 召回分数是相关度不是可信度;存前 candidates 判重,改>并>存 +- 账号与同步配置不归本插件管:注册去 rsrs.rs,本机 `rsrs register`/`rsrs login`,插件不代办 diff --git a/external_plugins/respire/tests/test_hooks.sh b/external_plugins/respire/tests/test_hooks.sh new file mode 100755 index 0000000..f6e09af --- /dev/null +++ b/external_plugins/respire/tests/test_hooks.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash +# respire(zcode) 自测 —— 模拟 stdin 事件 JSON,逐分支断言。 +# ZCode 输出契约:一切输出为单行 JSON——文本走 {"additionalContext":...},Stop 阻塞走 {"decision":"block",...}。 +# rsrs 无 data-dir 隔离(runtime 单例+全局 client.json),故默认只读分支不碰库; +# 加 --store 才跑写探针(写真库→回执断言→rsrs forget 自清理)。 +set -u +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PY3="$(command -v python3)" +RSRS="$(command -v rsrs || true)" +[ -z "$RSRS" ] && RSRS="$HOME/.local/bin/rsrs" +[ -z "$RSRS" ] && RSRS="$HOME/.rsrs/bin/rsrs" +PASS=0; FAIL=0 +WITH_STORE=0 +[ "${1:-}" = "--store" ] && WITH_STORE=1 + +if [ -z "$RSRS" ] || [ ! -x "$RSRS" ]; then + echo "SKIP:未找到 rsrs(respire 引擎),无法自测。"; exit 0 +fi + +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT +CFG_ON="$TMP/config-on.json" +echo '{"session_rules": true}' > "$CFG_ON" +CFG_OFF="$TMP/config-off.json" +echo '{"session_rules": false}' > "$CFG_OFF" + +# run [prompt] [config] +run() { + local ev="$1" pr="${2-}" cfg="${3:-$CFG_ON}" + "$PY3" - "$ev" "$pr" <<'PY' | RESPITE_CONFIG="$cfg" RESPITE_LOG="$TMP/hook.log" RESPITE_NUDGE_STATE="$TMP/nudge" "$PY3" "$ROOT/hooks/respire.py" "$ev" +import json, sys +ev, pr = sys.argv[1], sys.argv[2] +d = {"hook_event_name": ev, "cwd": "/tmp", "session_id": "selftest"} +if ev == "UserPromptSubmit": + d["prompt"] = pr +if ev == "Stop": + d["stop_hook_active"] = False +sys.stdout.write(json.dumps(d, ensure_ascii=False)) +PY +} + +check() { # check <名字> <实际输出> <模式: 非空|空|含:xxx|不含:xxx> + local name="$1" got="$2" want="$3" ok=0 + case "$want" in + 非空) [ -n "$got" ] && ok=1 ;; + 空) [ -z "$got" ] && ok=1 ;; + 不含:*) printf '%s' "$got" | grep -qF -e "${want#不含:}" && ok=0 || ok=1 ;; + 含:*) printf '%s' "$got" | grep -qF -e "${want#含:}" && ok=1 ;; + esac + if [ "$ok" = 1 ]; then + PASS=$((PASS+1)); echo "PASS $name" + else + FAIL=$((FAIL+1)); echo "FAIL $name 期望[$want] 实际[$(printf '%s' "$got" | head -c 200)]" + fi +} + +# ---- 只读分支(真库查询,不写) ---- +out=$(run SessionStart) +check "SessionStart 注入简则" "$out" "含:respire 记忆层" +check "简则点明引擎与同步" "$out" "含:rsrs.rs" +check "输出为 JSON 且走 additionalContext" "$out" "含:{\"additionalContext\"" +check "输出单行无裸换行" "$( [ "$(printf '%s' "$out" | wc -l | tr -d ' ')" = "0" ] && echo SINGLE || echo MULTI)" "含:SINGLE" +check "additionalContext 可解析且含简则" "$(printf '%s' "$out" | python3 -c ' +import json,sys +d=json.loads(sys.stdin.read()) +print("OK" if "respire 记忆层" in d.get("additionalContext","") else "BAD")')" "含:OK" +check "已登录不带登录引导" "$(run SessionStart)" "不含:尚未登录" +check "未登录注入登录引导" "$(RESPITE_FAKE_LOGGED_OUT=1 run SessionStart)" "含:尚未登录" +check "未登录引导给出注册与代登法" "$(RESPITE_FAKE_LOGGED_OUT=1 run SessionStart)" "含:rsrs login --oauth --no-open" +check "session_rules=false 静默" "$(run SessionStart "" "$CFG_OFF")" "空" +check "已装模型不带提示" "$(run SessionStart)" "不含:语义模型(BGE-M3)未装" +check "缺模型注入装法" "$(RESPITE_FAKE_NO_MODEL=1 run SessionStart)" "含:rsrs model activate m3" +check "缺模型点明词面回退" "$(RESPITE_FAKE_NO_MODEL=1 run SessionStart)" "含:词面档" +check "有货库不带空库引导" "$(run SessionStart)" "不含:记忆库还是空的" +check "空库注入首日引导" "$(RESPITE_FAKE_EMPTY=1 run SessionStart)" "含:记忆库还是空的" +check "空库引导含导入线索" "$(RESPITE_FAKE_EMPTY=1 run SessionStart)" "含:~/.zcode/AGENTS.md" +check "探测失败注入冷启诊断段" "$(RESPITE_FAKE_PROBE_FAIL=1 run SessionStart)" "含:状态探测失败" +check "README 载三件事(模型可选)" "$(cat "$ROOT/README.md")" "含:不装模型完全可用" +check "README 载市场装法" "$(cat "$ROOT/README.md")" "含:添加插件市场" +check "包内无自定义安装脚本" "$( { [ -e "$ROOT/install.sh" ] || [ -e "$ROOT/uninstall.sh" ]; } && echo PRESENT || echo absent )" "含:absent" +# 日志留痕:任一事件后 hook.log 应有本行 +run SessionStart >/dev/null +run UserPromptSubmit "我的密码是 password: hunter2 这算敏感吗" >/dev/null +check "日志脱敏口令" "$(grep -q 'hunter2' "$TMP/hook.log" && echo LEAK || echo SAFE)" "含:SAFE" +check "日志脱敏留痕" "$(grep -q 'REDACTED' "$TMP/hook.log" && echo YES)" "含:YES" +check "hook.log 留痕" "$(grep -q '"ev": "session"' "$TMP/hook.log" 2>/dev/null && echo YES)" "含:YES" + +# 引擎缺失分支:单元注入 monkeypatch find_rsrs→None(真机造不出「全路径皆缺」) +run_engineless() { + ROOT="$ROOT" "$PY3" - <<'PY' +import io, json, os, sys, importlib.util +spec = importlib.util.spec_from_file_location("respire", os.path.join(os.environ["ROOT"], "hooks/respire.py")) +m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m) +m.find_rsrs = lambda cfg: None +sys.argv = ["respire.py", "SessionStart"] +sys.stdin = io.StringIO('{"hook_event_name":"SessionStart"}') +try: + m.main() +except SystemExit: + pass +PY +} +check "引擎缺失注入装法" "$(run_engineless)" "含:npm i -g @rsrsai/cli" +check "引导禁自造超级密码" "$(run_engineless)" "含:绝不传 --super" +check "引导要求保存恢复码" "$(run_engineless)" "含:务必抄下保存" +check "引擎缺失含注册站点" "$(run_engineless)" "含:rsrs.rs" +check "probe失败含未注册分支" "$(RESPITE_FAKE_PROBE_FAIL=1 run SessionStart)" "含:尚未注册" + +# Stop 存储检查节流分支的引擎桩:只回 list --json,fresh=刚落库 / stale=陈旧 +# (真机库随时有人写,不能拿真引擎断言催/不催) +STUB_FRESH="$TMP/rsrs-fresh"; STUB_STALE="$TMP/rsrs-stale" +FRESH_TS="$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" +cat > "$STUB_FRESH" < "$STUB_STALE" <<'EOF' +#!/usr/bin/env bash +[ "${1:-}" = list ] && printf '{"command":"list","status":"ok","details":[{"id":"bbbbbbbbbbbbbbbb","created_at":"2020-01-01T00:00:00.000Z","updated_at":"2020-01-01T00:00:00.000Z","title":"stale","content":"x"}]}' +exit 0 +EOF +chmod +x "$STUB_FRESH" "$STUB_STALE" +CFG_STALE="$TMP/config-stale.json" +echo '{"session_rules": true, "rsrs": "'"$STUB_STALE"'"}' > "$CFG_STALE" +CFG_FRESH="$TMP/config-fresh.json" +echo '{"session_rules": true, "rsrs": "'"$STUB_FRESH"'"}' > "$CFG_FRESH" + +# Stop 存储检查:(节流断言一律走 stale 引擎桩,保证「仍催」确定性) +run_stop() { + "$PY3" - "$1" <<'PY' | RESPITE_CONFIG="$CFG_STALE" RESPITE_LOG="$TMP/hook.log" RESPITE_NUDGE_STATE="$TMP/nudge" "$PY3" "$ROOT/hooks/respire.py" Stop +import json, sys +print(json.dumps({"hook_event_name": "Stop", "stop_hook_active": sys.argv[1] == "true"})) +PY +} +check "Stop 存储检查续跑(decision block)" "$(run_stop false)" '含:"decision": "block"' +check "Stop 阻塞输出可过 JSON 解析" "$(run_stop false | python3 -c ' +import json,sys +d=json.loads(sys.stdin.read()) +print("OK" if d.get("decision")=="block" and "rsrs remember" in d.get("reason","") else "BAD")')" "含:OK" +check "Stop 提示含存储指令" "$(run_stop false)" "含:rsrs remember" +check "Stop 无 deny 决策(ZCode 枚举外)" "$(run_stop false | grep -qF '"deny"' && echo HAS || echo CLEAN)" "含:CLEAN" +check "Stop 防重入放行" "$(run_stop true)" "空" +CFG_NUDGE0="$TMP/config-nudge0.json" +echo '{"store_nudge_every": 0}' > "$CFG_NUDGE0" +run_stop_cfg() { # $1=config + local cfg="$1" + "$PY3" - <<'PY' | RESPITE_CONFIG="$cfg" RESPITE_LOG="$TMP/hook.log" RESPITE_NUDGE_STATE="$TMP/nudge" "$PY3" "$ROOT/hooks/respire.py" Stop +import json +print(json.dumps({"hook_event_name": "Stop", "stop_hook_active": False})) +PY +} +check "Stop 节流关闭静默" "$(run_stop_cfg "$CFG_NUDGE0")" "空" +check "Stop 引擎近期落库不催" "$(run_stop_cfg "$CFG_FRESH")" "空" +check "Stop 引擎陈旧落库仍催" "$(run_stop false)" '含:"decision": "block"' +check "引擎放行留痕 src=engine" "$(grep -q 'stored_recent_skip.*src=engine' "$TMP/hook.log" && echo YES)" "含:YES" +printf '%s\n' '{"ts": "'"$(date +%Y-%m-%dT%H:%M:%S)"'", "ev": "store", "out": "ok", "d": "feed"}' >> "$TMP/hook.log" +check "Stop 已存放行放行" "$(run_stop false)" "空" +CFG_OFFE="$TMP/config-disabled.json" +echo '{"enabled": false}' > "$CFG_OFFE" +check "enabled=false 全线静默" "$(run SessionStart "" "$CFG_OFFE")" "空" +check "enabled=false 落库也静默" "$(run UserPromptSubmit "记:关断时不应落库" "$CFG_OFFE")" "空" + +out=$(run UserPromptSubmit "rsrs 信封 JSON 结构解析取哪层") +check "相关 prompt 触发召回" "$out" "非空" +check "召回也走 additionalContext" "$out" "含:{\"additionalContext\"" +check "注入为标题档无正文" "$out" "不含:|" +check "默认直出无阈值拦截" "$out" "含:rsrs 直出" +check "召回头行与条数一致" "$(printf '%s' "$out" | python3 -c ' +import re,json,sys +s=json.loads(sys.stdin.read())["additionalContext"]; head=s.split("\n",1)[0] +n=sum(1 for l in s.split("\n") if l.startswith("- [")) +m=re.search(r"命中 (\d+) 条",head) +print("OK" if m and int(m.group(1))==n else "BAD")')" "含:OK" + +check "无关 prompt 不注入" "$(run UserPromptSubmit "今晚吃什么菜好呢")" "空" +check "单字 prompt 跳过" "$(run UserPromptSubmit "嗯")" "空" +check "/nomem 关闭召回" "$(run UserPromptSubmit "/nomem rsrs 信封结构")" "空" +check "技能指认轮剥前缀仍召回" "$(run UserPromptSubmit '$skill: 笔痕 rsrs 信封 JSON 结构解析取哪层')" "含:自动召回" +echo '{"session_rules": true, "skip_prefixes": ["你正在为宿主写 Recap"]}' > "$TMP/config-skip.json" +check "skip_prefixes 白名单跳过" "$(run UserPromptSubmit "你正在为宿主写 Recap:无事" "$TMP/config-skip.json")" "空" +check "坏 stdin 静默" "$(printf 'not-json{{{' | RESPITE_CONFIG="$CFG_ON" "$PY3" "$ROOT/hooks/respire.py" UserPromptSubmit)" "空" +check "未知事件静默" "$(run PreToolUse "rsrs 信封")" "空" + +# ---- title 规则抽取(纯函数,零污染) ---- +check "整句优先取完整句" "$(python3 -c ' +import sys,importlib.util +spec=importlib.util.spec_from_file_location("r","'"$ROOT"'/hooks/respire.py"); m=importlib.util.module_from_spec(spec); spec.loader.exec_module(m) +print(m.extract_title("respire 插件路径迁至 respire-zcode 下。后续以仓库为准。"))')" "含:respire 插件路径迁至 respire-zcode 下。" +check "无句号退分隔符不切半词" "$(python3 -c ' +import sys,importlib.util +spec=importlib.util.spec_from_file_location("r","'"$ROOT"'/hooks/respire.py"); m=importlib.util.module_from_spec(spec); spec.loader.exec_module(m) +t=m.extract_title("这是一条没有句号的长事实句,讲清楚何时谁做了什么事,以及结果如何,后面还有别的内容") +print(t)')" "含:讲清楚何时谁做了什么事," +check "长串不超70字" "$(python3 -c ' +import sys,importlib.util +spec=importlib.util.spec_from_file_location("r","'"$ROOT"'/hooks/respire.py"); m=importlib.util.module_from_spec(spec); spec.loader.exec_module(m) +print(len(m.extract_title("默" * 200)))')" "非空" + +# ---- 写探针(--store 显式开启;写真库→断言→forget 清理) ---- +if [ "$WITH_STORE" = 1 ]; then + # 内容须与全库零语义关联:纯随机无义串(域词会撞相似走待决态) + MARK="zq$(date +%s)$RANDOM kw7f2 随机标记无义串" + out=$(run UserPromptSubmit "记:$MARK") + check "落库成功回执" "$out" "含:已存入" + check "回执约束AI抽事实句retitle" "$out" "含:rsrs retitle" + SID=$(printf '%s' "$out" | sed -n 's/.*(\([0-9a-f]\{8\}\)).*/\1/p') + check "回执带短 id" "$SID" "非空" + if [ -n "$SID" ]; then + rc=$(rsrs forget "$SID" --json 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin).get("status","err"))') + check "探针清理(status=ok)" "$rc" "含:ok" + fi + # 判重待决:复述库中稳定条目 → 大概率撞高相似 → pending 未写入。 + # 断言依赖实时库内容(教训:零命中必 FAIL)——判直写则兜底清理后记 SKIP,不硬失败。 + out=$(run UserPromptSubmit "记:1memory 更名 respire:吸即存呼即取,材料全线改(复述既有条目触发判重待决)") + if printf '%s' "$out" | grep -qF "已存入"; then + BID=$(printf '%s' "$out" | sed -n 's/.*(\([0-9a-f]\{8\}\)).*/\1/p') + [ -n "$BID" ] && rsrs forget "$BID" --json >/dev/null 2>&1 + PASS=$((PASS+1)); echo "SKIP 判重待决转述候选(库况变化判为直写,已清理 $BID)" + else + check "判重待决转述候选" "$out" "含:落库待决" + check "待决列出候选与决断法" "$out" "含:--force" + fi +else + echo "SKIP 写探针(默认只读;加 --store 开启,写真库并自动 forget 清理)" +fi + +echo "----" +echo "PASS=$PASS FAIL=$FAIL" +[ "$FAIL" -eq 0 ] diff --git a/marketplace.json b/marketplace.json index 350c942..13efd73 100644 --- a/marketplace.json +++ b/marketplace.json @@ -10,7 +10,10 @@ "url": "https://z.ai" }, "unlistedPlugins": [ - { "name": "openpencil", "source": "./external_plugins/openpencil" } + { + "name": "openpencil", + "source": "./external_plugins/openpencil" + } ], "plugins": [ { @@ -793,7 +796,9 @@ }, { "name": "excalidraw", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "Excalidraw", "displayName_i18n": { "en": "Excalidraw", @@ -816,7 +821,9 @@ "name": "photocraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/photocraft/icon.png", "source": "./external_plugins/photocraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "PhotoCraft", "displayName_i18n": { "en": "PhotoCraft", @@ -843,7 +850,9 @@ "name": "vectorcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/vectorcraft/icon.png", "source": "./external_plugins/vectorcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "VectorCraft", "displayName_i18n": { "en": "VectorCraft", @@ -870,7 +879,9 @@ "name": "pdfcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/pdfcraft/icon.png", "source": "./external_plugins/pdfcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "PdfCraft", "displayName_i18n": { "en": "PdfCraft", @@ -897,7 +908,9 @@ "name": "designcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/designcraft/icon.png", "source": "./external_plugins/designcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "DesignCraft", "displayName_i18n": { "en": "DesignCraft", @@ -924,7 +937,9 @@ "name": "wordcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/wordcraft/icon.png", "source": "./external_plugins/wordcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "WordCraft", "displayName_i18n": { "en": "WordCraft", @@ -951,7 +966,9 @@ "name": "gridcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/gridcraft/icon.png", "source": "./external_plugins/gridcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "GridCraft", "displayName_i18n": { "en": "GridCraft", @@ -978,7 +995,9 @@ "name": "deckcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/deckcraft/icon.png", "source": "./external_plugins/deckcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "DeckCraft", "displayName_i18n": { "en": "DeckCraft", @@ -1005,7 +1024,9 @@ "name": "cadcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/cadcraft/icon.png", "source": "./external_plugins/cadcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "CADCraft", "displayName_i18n": { "en": "CADCraft", @@ -1032,7 +1053,9 @@ "name": "soundcraft", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/soundcraft/icon.png", "source": "./external_plugins/soundcraft", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "SoundCraft", "displayName_i18n": { "en": "SoundCraft", @@ -1115,7 +1138,9 @@ }, "category": "developer-tools", "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/kicad/icon.png", - "componentTypes": ["ui"], + "componentTypes": [ + "ui" + ], "displayName": "KiCad", "displayName_i18n": { "en": "KiCad", @@ -1386,6 +1411,35 @@ "unity-cli", "skills" ] + }, + { + "name": "respire", + "displayName": "respire", + "displayName_i18n": { + "en": "respire", + "zh-CN": "respire 记忆" + }, + "source": "./external_plugins/respire", + "icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/respire/icon.png", + "description": "respire local encrypted memory: every-round title-tier recall, one-prefix store, Stop storage check. Fully usable without the sidecar model.", + "description_i18n": { + "en": "respire local encrypted memory: every-round title-tier recall, one-prefix store, Stop storage check. Fully usable without the sidecar model.", + "zh-CN": "respire 本地加密记忆:每轮标题档自动召回、一句「记:」落库、Stop 存储检查,不装模型也完全可用。" + }, + "version": "0.1.0", + "author": { + "name": "risense-ai", + "url": "https://github.com/risense-ai" + }, + "homepage": "https://github.com/risense-ai/respire-zcode", + "category": "productivity", + "keywords": [ + "memory", + "recall", + "hooks", + "local-first", + "rsrs" + ] } ] }