linger-skill 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/PRIVACY.md +31 -0
- package/README.md +300 -0
- package/README.zh-CN.md +300 -0
- package/SECURITY.md +25 -0
- package/dist/adapter-evidence.d.ts +31 -0
- package/dist/adapter-evidence.js +166 -0
- package/dist/adapter-evidence.js.map +1 -0
- package/dist/adapters.d.ts +20 -0
- package/dist/adapters.js +105 -0
- package/dist/adapters.js.map +1 -0
- package/dist/capture.d.ts +16 -0
- package/dist/capture.js +108 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +217 -0
- package/dist/cli.js.map +1 -0
- package/dist/control.d.ts +9 -0
- package/dist/control.js +38 -0
- package/dist/control.js.map +1 -0
- package/dist/decision-extraction.d.ts +4 -0
- package/dist/decision-extraction.js +63 -0
- package/dist/decision-extraction.js.map +1 -0
- package/dist/decisions.d.ts +60 -0
- package/dist/decisions.js +166 -0
- package/dist/decisions.js.map +1 -0
- package/dist/delete.d.ts +21 -0
- package/dist/delete.js +96 -0
- package/dist/delete.js.map +1 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.js +208 -0
- package/dist/doctor.js.map +1 -0
- package/dist/enrichment.d.ts +82 -0
- package/dist/enrichment.js +359 -0
- package/dist/enrichment.js.map +1 -0
- package/dist/eval-adversarial.d.ts +2 -0
- package/dist/eval-adversarial.js +20 -0
- package/dist/eval-adversarial.js.map +1 -0
- package/dist/eval-cli.d.ts +2 -0
- package/dist/eval-cli.js +83 -0
- package/dist/eval-cli.js.map +1 -0
- package/dist/eval-generator.d.ts +41 -0
- package/dist/eval-generator.js +36 -0
- package/dist/eval-generator.js.map +1 -0
- package/dist/eval-heldout.d.ts +2 -0
- package/dist/eval-heldout.js +56 -0
- package/dist/eval-heldout.js.map +1 -0
- package/dist/eval-io.d.ts +6 -0
- package/dist/eval-io.js +15 -0
- package/dist/eval-io.js.map +1 -0
- package/dist/eval-runner.d.ts +8 -0
- package/dist/eval-runner.js +56 -0
- package/dist/eval-runner.js.map +1 -0
- package/dist/eval-scorer.d.ts +27 -0
- package/dist/eval-scorer.js +28 -0
- package/dist/eval-scorer.js.map +1 -0
- package/dist/hook-cli.d.ts +2 -0
- package/dist/hook-cli.js +28 -0
- package/dist/hook-cli.js.map +1 -0
- package/dist/hook-command.d.ts +9 -0
- package/dist/hook-command.js +31 -0
- package/dist/hook-command.js.map +1 -0
- package/dist/hook-handler.d.ts +24 -0
- package/dist/hook-handler.js +93 -0
- package/dist/hook-handler.js.map +1 -0
- package/dist/hook-installer.d.ts +4 -0
- package/dist/hook-installer.js +120 -0
- package/dist/hook-installer.js.map +1 -0
- package/dist/hook-ownership.d.ts +8 -0
- package/dist/hook-ownership.js +51 -0
- package/dist/hook-ownership.js.map +1 -0
- package/dist/install-consent.d.ts +4 -0
- package/dist/install-consent.js +20 -0
- package/dist/install-consent.js.map +1 -0
- package/dist/installer.d.ts +33 -0
- package/dist/installer.js +140 -0
- package/dist/installer.js.map +1 -0
- package/dist/io.d.ts +7 -0
- package/dist/io.js +86 -0
- package/dist/io.js.map +1 -0
- package/dist/memory-events.d.ts +31 -0
- package/dist/memory-events.js +109 -0
- package/dist/memory-events.js.map +1 -0
- package/dist/memory-intent.d.ts +1 -0
- package/dist/memory-intent.js +4 -0
- package/dist/memory-intent.js.map +1 -0
- package/dist/paths.d.ts +15 -0
- package/dist/paths.js +24 -0
- package/dist/paths.js.map +1 -0
- package/dist/pending.d.ts +21 -0
- package/dist/pending.js +76 -0
- package/dist/pending.js.map +1 -0
- package/dist/processed-markdown.d.ts +3 -0
- package/dist/processed-markdown.js +29 -0
- package/dist/processed-markdown.js.map +1 -0
- package/dist/processing-policy.d.ts +19 -0
- package/dist/processing-policy.js +79 -0
- package/dist/processing-policy.js.map +1 -0
- package/dist/processing.d.ts +4 -0
- package/dist/processing.js +143 -0
- package/dist/processing.js.map +1 -0
- package/dist/purge.d.ts +7 -0
- package/dist/purge.js +16 -0
- package/dist/purge.js.map +1 -0
- package/dist/recall-sampling.d.ts +53 -0
- package/dist/recall-sampling.js +201 -0
- package/dist/recall-sampling.js.map +1 -0
- package/dist/recall.d.ts +19 -0
- package/dist/recall.js +70 -0
- package/dist/recall.js.map +1 -0
- package/dist/record-paths.d.ts +18 -0
- package/dist/record-paths.js +38 -0
- package/dist/record-paths.js.map +1 -0
- package/dist/repair.d.ts +4 -0
- package/dist/repair.js +83 -0
- package/dist/repair.js.map +1 -0
- package/dist/runtime-identity.d.ts +1 -0
- package/dist/runtime-identity.js +31 -0
- package/dist/runtime-identity.js.map +1 -0
- package/dist/runtime-installer.d.ts +7 -0
- package/dist/runtime-installer.js +43 -0
- package/dist/runtime-installer.js.map +1 -0
- package/dist/schema-validation.d.ts +26 -0
- package/dist/schema-validation.js +222 -0
- package/dist/schema-validation.js.map +1 -0
- package/dist/search.d.ts +16 -0
- package/dist/search.js +192 -0
- package/dist/search.js.map +1 -0
- package/dist/sensitivity.d.ts +6 -0
- package/dist/sensitivity.js +27 -0
- package/dist/sensitivity.js.map +1 -0
- package/dist/tag-registry.d.ts +21 -0
- package/dist/tag-registry.js +70 -0
- package/dist/tag-registry.js.map +1 -0
- package/dist/term-graph.d.ts +18 -0
- package/dist/term-graph.js +63 -0
- package/dist/term-graph.js.map +1 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vault-candidates.d.ts +1 -0
- package/dist/vault-candidates.js +57 -0
- package/dist/vault-candidates.js.map +1 -0
- package/dist/vault.d.ts +28 -0
- package/dist/vault.js +158 -0
- package/dist/vault.js.map +1 -0
- package/dist/windows-command.d.ts +6 -0
- package/dist/windows-command.js +9 -0
- package/dist/windows-command.js.map +1 -0
- package/package.json +53 -0
- package/scripts/release-readiness.mjs +58 -0
- package/skills/linger/SKILL.md +56 -0
- package/skills/linger/agents/openai.yaml +4 -0
- package/skills/linger/references/protocol.md +84 -0
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Linger
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/linger-skill)
|
|
4
|
+
[](https://github.com/arishana1969/linger-skill/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
[English](README.md) | **简体中文**
|
|
7
|
+
|
|
8
|
+
面向编程智能体的本地对话归档与决策轨迹系统。
|
|
9
|
+
|
|
10
|
+
> 念念不忘,必有回响。
|
|
11
|
+
>
|
|
12
|
+
> What lingers in mind will find its echo.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 项目状态
|
|
17
|
+
|
|
18
|
+
Linger v0.2.2 是面向 Claude Code 和 Codex 的实验性 MVP,以公开的 [`linger-skill`](https://www.npmjs.com/package/linger-skill) 包分发。Linger 不承诺完整的归档覆盖率或生产级支持。
|
|
19
|
+
|
|
20
|
+
## 缘起
|
|
21
|
+
|
|
22
|
+
这个项目的灵感来自 [Evis Drenova](https://x.com/evisdrenova) 的一条帖子:
|
|
23
|
+
|
|
24
|
+
> My ideal AI interface is a single never-ending chat thread. I don't want to think about sessions, context windows, worktrees, MCP servers, or anything else. The harness should automate everything transparently.
|
|
25
|
+
|
|
26
|
+
我们无法真正提供一个永不终止的对话线程——上下文窗口是真实的物理限制,会话会结束,机器会重启。但我们可以构建次优解:一个静默保存对话内容与决策过程的层,使你回来时,连续性仍然存在。
|
|
27
|
+
|
|
28
|
+
## 功能概述
|
|
29
|
+
|
|
30
|
+
Linger 将你与编程智能体之间的可见对话——讨论、显式笔记、决策及其可见推理——保存为你拥有的本地文件。
|
|
31
|
+
|
|
32
|
+
它不是通用 AI 记忆系统,而是一个 sidecar 连续性层。Linger 观察受支持的生命周期事件,但不禁用、替换或写入智能体自身的记忆系统。当智能体丢失了较早的上下文,Linger 仍保有记录;当你询问三周前某个决策的原因,Linger 能呈现证据及其演化过程。
|
|
33
|
+
|
|
34
|
+
### 你将获得
|
|
35
|
+
|
|
36
|
+
- 通过已验证的 lifecycle hooks 自动捕获对话,宿主不支持自动化时回退到显式 CLI。
|
|
37
|
+
- 可选的宿主模型整理:当前 Claude 或 Codex 模型可改进摘要、类型、标签和检索短语,无需额外的模型提供商或 API 密钥。
|
|
38
|
+
- 决策轨迹,保存结论的演化过程而非仅最终结果。
|
|
39
|
+
- 记事接口,支持记住、跳过、遗忘、纠正、审查和删除工作流。
|
|
40
|
+
- 本地优先存储,使用带版本的 JSON 和人类可读的 Markdown。
|
|
41
|
+
- 无数据库、无用户管理的 daemon、无遥测、无 embedding API、无云同步。
|
|
42
|
+
- 项目范围内的召回,返回来源、区分不确定性,在无可靠记忆时明确告知。
|
|
43
|
+
- 中断恢复、完整性检查、有界搜索和需确认的破坏性操作。
|
|
44
|
+
|
|
45
|
+
## 目标用户
|
|
46
|
+
|
|
47
|
+
Linger 面向在长期项目中使用 Claude Code 或 Codex 的开发者。
|
|
48
|
+
|
|
49
|
+
如果你曾重启对话,希望智能体还记得上周讨论的内容——或者某个设计决策为何更改——Linger 就是为你准备的。
|
|
50
|
+
|
|
51
|
+
## 安装
|
|
52
|
+
|
|
53
|
+
需要 Node.js 20 或更高版本。推荐的安装方式只需一条命令:
|
|
54
|
+
|
|
55
|
+
npx linger-skill install
|
|
56
|
+
|
|
57
|
+
交互式安装会展示本地/云端的数据边界,并要求输入准确的 YES 后才写入。非交互调用方须传入 --yes:
|
|
58
|
+
|
|
59
|
+
npx linger-skill install --yes
|
|
60
|
+
|
|
61
|
+
只安装单个适配器:
|
|
62
|
+
|
|
63
|
+
npx linger-skill install --adapters claude-code
|
|
64
|
+
npx linger-skill install --adapters codex
|
|
65
|
+
|
|
66
|
+
如需持久的全局 CLI:
|
|
67
|
+
|
|
68
|
+
npm install --global linger-skill
|
|
69
|
+
linger install
|
|
70
|
+
|
|
71
|
+
从源码安装用于开发:
|
|
72
|
+
|
|
73
|
+
git clone https://github.com/arishana1969/linger-skill.git
|
|
74
|
+
cd linger-skill
|
|
75
|
+
pnpm install --frozen-lockfile
|
|
76
|
+
pnpm build
|
|
77
|
+
node dist/cli.js install
|
|
78
|
+
|
|
79
|
+
## 工作原理
|
|
80
|
+
|
|
81
|
+
Linger 由 Skill、lifecycle hooks、辅助 CLI、事件驱动处理器和本地 Vault 协同组成。
|
|
82
|
+
|
|
83
|
+
1. Skill 告知智能体何时保存、何时检索,以及如何对待召回的记忆。
|
|
84
|
+
2. 受支持的 lifecycle hooks 捕获可见的用户和助手事件,捕获失败不阻塞对话。
|
|
85
|
+
3. 捕获过程暂存事件、写入不可变的原始记录,并添加一个持久化的 queue 项。
|
|
86
|
+
4. 事件驱动的 session-local 处理器串行消费 queue。它可在以下时机运行:用户显式记忆指令、达到体积阈值、后续事件观测到最大等待时间已过、会话启动时、或通过手动命令触发。
|
|
87
|
+
5. 确定性处理创建安全的可搜索基线:raw → processed memory → 标签与检索短语。
|
|
88
|
+
6. 当存在符合条件的待处理记录时,已安装的 Skill 可在用户主要任务完成后,请求 Claude Code 或 Codex 中正在运行的模型整理一个有界批次。
|
|
89
|
+
7. 经验证的 enrichment 作为派生 overlay 存储。它改进标签和召回质量,但不改写原始证据或确定性基线。
|
|
90
|
+
8. 召回搜索当前项目并返回有界的证据包,当 enrichment 缺失、过期或无效时回退到确定性基线。
|
|
91
|
+
|
|
92
|
+
活跃的智能体将该证据包与其当前对话及上下文中已有的宿主记忆合并。实质相同的事实属于同一底层证据,而非独立佐证;冲突应被显式呈现而非静默消解。
|
|
93
|
+
|
|
94
|
+
Linger 不安装 daemon,不修改宿主的记忆设置,也不保证空闲的长时间运行会话会在精确时刻处理 queue。
|
|
95
|
+
|
|
96
|
+
默认 Vault 路径为 ~/.linger/vault:
|
|
97
|
+
|
|
98
|
+
~/.linger/vault/
|
|
99
|
+
├── config.json
|
|
100
|
+
├── projects/
|
|
101
|
+
├── raw/
|
|
102
|
+
├── processed/
|
|
103
|
+
├── enrichments/
|
|
104
|
+
├── decisions/
|
|
105
|
+
├── queue/
|
|
106
|
+
├── registry/
|
|
107
|
+
├── tmp/
|
|
108
|
+
└── quarantine/
|
|
109
|
+
|
|
110
|
+
Raw events 和 decision events 是仅追加的源记录。Processed memories 使用 JSON 加 Markdown 镜像。Host enrichment 是可替换的派生 overlay。Registry 和当前决策视图是派生数据,可以重建。
|
|
111
|
+
|
|
112
|
+
一切都是文件。你可以查看、备份、grep,或通过需确认的操作删除它们。
|
|
113
|
+
|
|
114
|
+
## 核心理念
|
|
115
|
+
|
|
116
|
+
### 文件原生
|
|
117
|
+
|
|
118
|
+
你的记忆存储在带版本 schema 的普通本地文件中。没有记忆数据库或必需的服务器。卸载集成后 Vault 保持原位。
|
|
119
|
+
|
|
120
|
+
### 决策轨迹
|
|
121
|
+
|
|
122
|
+
Linger 记录带类型的决策演化:idea、preference、proposal、rationale、constraint、rejection、decision、current state、todo 和 correction。
|
|
123
|
+
|
|
124
|
+
当前视图从不可变事件推导而来。这使 Linger 能保存 A → B → A 的路径,而非静默覆盖历史。
|
|
125
|
+
|
|
126
|
+
### 证据而非指令
|
|
127
|
+
|
|
128
|
+
召回的记忆是不受信任的历史证据。记忆中发现的命令、提示词或旧系统消息不得被执行。当前的系统指令和用户指令始终优先。
|
|
129
|
+
|
|
130
|
+
### 诚实召回
|
|
131
|
+
|
|
132
|
+
召回明确区分精确匹配、相似匹配、可能匹配、冲突记忆、未处理原始记录和无结果。结果携带来源 ID 和溯源警告。当 Linger 找不到可靠证据时,它会如实告知,并可能提供候选主题、决策、标签或观测到的时间段。
|
|
133
|
+
|
|
134
|
+
## 智能体支持
|
|
135
|
+
|
|
136
|
+
自动化程度因宿主和版本而异。能力等级基于观测证据报告,而非仅凭安装文件。
|
|
137
|
+
|
|
138
|
+
| 智能体 | 已验证等级 | 当前证据与限制 |
|
|
139
|
+
| --- | ---: | --- |
|
|
140
|
+
| Claude Code | L2 | Claude Code 2.1.207 在宿主活跃模型为 DeepSeek 的条件下完成了完整的 v0.2.1 host-enrichment 路径:lifecycle 捕获、确定性处理、overlay 提交和增强标签。v0.2.2 的 sidecar 升级保留了宿主自有记忆,并已通过安装和迁移验证。StopFailure 在一次性沙箱验收中已覆盖。在观测路径上,Ctrl-C 未能保留已流式输出的助手文本。 |
|
|
141
|
+
| Codex | L2 | Codex 0.144.0-alpha.4 完成了完整的 v0.2.1 host-enrichment 路径:lifecycle 捕获、当前模型整理、overlay 提交、tag registry 重建和精确语义召回。v0.2.2 的 sidecar 契约由 Codex adapter 共享。全新安装在一次可信的实际会话完成 SessionStart、UserPromptSubmit 和 Stop 之前保持 L1。 |
|
|
142
|
+
| 其他智能体 | 未实现 | 额外 adapter 不在 v0.2.2 MVP 范围内。未来的 adapter 必须遵守相同的 sidecar 边界,不触碰宿主自有记忆。 |
|
|
143
|
+
|
|
144
|
+
等级定义:
|
|
145
|
+
|
|
146
|
+
- L0:仅规则行为。
|
|
147
|
+
- L1:已安装 Skill,通过显式文件和 CLI 操作。
|
|
148
|
+
- L2:已验证的 lifecycle hook 捕获。
|
|
149
|
+
- L3:session-local 异步处理。
|
|
150
|
+
- L4:更完整的捕获、处理、恢复、索引和召回自动化。
|
|
151
|
+
|
|
152
|
+
Linger 不会仅因集成文件存在就报告 L3 或 L4。
|
|
153
|
+
|
|
154
|
+
Host enrichment 遵循相同的支持边界。在 Codex 中使用 Codex 的活跃模型;在 Claude Code 中使用 Claude Code 的活跃模型,包括用户配置的兼容端点。Linger 不安装模型 SDK、不选择模型、不索要提供商凭据。如果宿主无法或未运行 enrichment,捕获和确定性召回照常继续。
|
|
155
|
+
|
|
156
|
+
## 自然语言工作流
|
|
157
|
+
|
|
158
|
+
Skill 帮助智能体将用户意图转化为 Linger 操作。具体行为取决于宿主的 Skill 和 lifecycle 支持程度。
|
|
159
|
+
|
|
160
|
+
捕获与召回是有意区分的。在已验证的 lifecycle hooks 下,可见的对话轮次在正常聊天期间自动捕获。召回是按需驱动的:Linger 不会在每一轮都搜索或注入整个 Vault。当用户用“之前”“上次”“继续”“为什么选了”“还记得吗”等短语提及早期工作时,智能体应搜索当前项目的 Linger 证据。对于重要的查找,用户可以说“用 Linger 查一下……”,无需运行 CLI 命令。
|
|
161
|
+
|
|
162
|
+
| 你说 | 预期行为 |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| “记住这个” | 作为高优先级的 user-explicit 证据捕获。 |
|
|
165
|
+
| “这个不要保存” | 在 Vault 初始化前,若 hook 支持则跳过当前用户事件。 |
|
|
166
|
+
| “忘掉那个决策” | 将 processed memory 从普通召回中撤回,但不删除原始历史。 |
|
|
167
|
+
| “那条记忆不对” | 追加一条带可见证据的纠正记录;不原地改写历史。 |
|
|
168
|
+
| “为什么当时选了 X?” | 搜索当前项目的决策轨迹并返回带来源的证据。 |
|
|
169
|
+
| “我们讨论过 Y 吗?” | 搜索当前项目的对话归档。 |
|
|
170
|
+
| “采样这次召回” | 为本次查询选择加入,返回一个 attempt ID,等待你的质量评判。 |
|
|
171
|
+
| “状态” | 报告 Vault、queue、待处理项和完整性健康状况。 |
|
|
172
|
+
|
|
173
|
+
普通捕获和 enrichment 不需要用户发出单独的 Linger 命令。Lifecycle hooks 捕获可见轮次。当宿主报告存在符合条件的待处理记录时,Skill 可在主要请求完成后至多整理一个有界批次。
|
|
174
|
+
|
|
175
|
+
Linger 是 sidecar 而非宿主记忆的替代品。安装时保留 Claude Code、Codex 及未来 adapter 的原生记忆设置和文件。宿主可能独立保留了 Linger 也已捕获的事实;在召回时,活跃模型应将重复项合并为同一底层事件,并披露冲突版本。Linger 绝不将重复的上下文视为额外的投票。
|
|
176
|
+
|
|
177
|
+
遗忘和删除是不同的操作。遗忘改变召回资格。删除移除一条已确认的记录。清除仅在双重确认后移除完整的 Linger 状态。
|
|
178
|
+
|
|
179
|
+
## 隐私
|
|
180
|
+
|
|
181
|
+
完整的数据边界和删除行为见 [PRIVACY.md](PRIVACY.md)。
|
|
182
|
+
|
|
183
|
+
Linger 将可见的对话内容存储在本地文件中。其运行时不回传数据、不收集遥测、不生成 embedding、不向 Linger 服务同步数据。
|
|
184
|
+
|
|
185
|
+
本地优先不意味着数据永远不离开你的机器。当智能体召回证据或整理有界 enrichment 批次时,该证据进入当前 Claude Code 或 Codex 的上下文,可能被发送至该宿主配置的模型提供商。Linger 不会将其发送至额外的模型服务。
|
|
186
|
+
|
|
187
|
+
项目包含敏感内容的安全基线:
|
|
188
|
+
|
|
189
|
+
- 高置信度的密钥模式在原始持久化前被脱敏。
|
|
190
|
+
- 密钥记录被排除在 processed memory 和普通召回之外。
|
|
191
|
+
- 类似联系方式的敏感记录默认被排除在普通召回之外。
|
|
192
|
+
- 仅正常敏感度、哈希已验证的记录有资格参与 host enrichment。
|
|
193
|
+
- 同一捕获边界适用于 hook 捕获、手动 CLI 捕获和编程接口捕获。
|
|
194
|
+
|
|
195
|
+
该检测器不是完整的数据防泄漏系统。未知的密钥格式可能被遗漏,被显式标记但未识别的密钥材料可能仍残留在原始文件中。请像对待任何本地对话记录一样保护 Vault。不要在需要审计合规控制的场景中使用这个实验性版本。
|
|
196
|
+
|
|
197
|
+
## 安全与完整性
|
|
198
|
+
|
|
199
|
+
信任边界和私密漏洞报告流程见 [SECURITY.md](SECURITY.md)。
|
|
200
|
+
|
|
201
|
+
- 召回默认仅限当前项目。
|
|
202
|
+
- 跨项目搜索永远不会被隐式启用。
|
|
203
|
+
- 搜索对文件数、片段数、返回字符数、原始片段大小和挂钟时间施加上界。
|
|
204
|
+
- 超时被报告为检索失败,而非虚假的“无记忆”结果。
|
|
205
|
+
- ID、enum 字段、时间戳、schema 和规范记录位置在运行时验证。
|
|
206
|
+
- 关键 Vault 路径拒绝指向真实 Vault 根目录之外的物理符号链接逃逸。
|
|
207
|
+
- 被篡改的原始来源被排除或显式降级。
|
|
208
|
+
- 可解析但无效的记录保持惰性,出现在 doctor 报告中,仅通过确认的修复操作迁移。
|
|
209
|
+
- 安装和卸载在写入前验证受管目标。
|
|
210
|
+
- Hook 捕获失败不阻塞智能体对话。
|
|
211
|
+
|
|
212
|
+
这些控制措施降低了风险,但不保证在恶意仓库、prompt injection、文件系统竞态、手动 Vault 修改或本地账户被入侵等情况下的绝对安全。
|
|
213
|
+
|
|
214
|
+
通过 [GitHub Security Advisories](https://github.com/arishana1969/linger-skill/security/advisories/new) 私密报告漏洞。请勿在普通 issue 中包含真实密钥或 Vault 文件。
|
|
215
|
+
|
|
216
|
+
## 配置
|
|
217
|
+
|
|
218
|
+
Linger 以保守的默认值初始化:
|
|
219
|
+
|
|
220
|
+
- 除非 Vault 被暂停,否则启用捕获。
|
|
221
|
+
- 仅当前项目召回。
|
|
222
|
+
- 启用敏感内容排除。
|
|
223
|
+
- 无 embedding 或额外模型 API。
|
|
224
|
+
- Host enrichment 为可选项,每次智能体轮次至多一个有界批次,使用 Claude Code 或 Codex 中已活跃的模型。
|
|
225
|
+
- 至多返回 8 个片段。
|
|
226
|
+
- 至多返回 12,000 个证据字符。
|
|
227
|
+
- 至多扫描 5,000 个文件。
|
|
228
|
+
- 每个原始片段至多 500 字符。
|
|
229
|
+
- 搜索截止时间 2 秒。
|
|
230
|
+
|
|
231
|
+
暂停和恢复可通过 CLI 操作。检索上界可在每次搜索或召回请求中指定。
|
|
232
|
+
|
|
233
|
+
真实使用的召回采样默认关闭。被显式采样的查询及其结果元数据保留在本地 Vault 中;高置信度密钥被脱敏。反馈为仅追加模式,可区分有用、部分有用、错误和遗漏的召回。
|
|
234
|
+
|
|
235
|
+
v0.2.2 中并非所有内部处理阈值都作为稳定的用户配置项暴露。配置文件经过 schema 验证;不受支持的版本或无效的上界会显式报错,而非被静默改写。
|
|
236
|
+
|
|
237
|
+
## 已知限制
|
|
238
|
+
|
|
239
|
+
- 这是一个 MVP,不承诺完美召回每一个字。
|
|
240
|
+
- 自动捕获依赖已验证的宿主 lifecycle 支持。
|
|
241
|
+
- Codex hooks 需要显式的宿主信任。安装或升级 Linger 后,需启动一个新的 Codex 会话并完成一次轮次,方可依赖自动捕获。
|
|
242
|
+
- Claude Code 在 Ctrl-C 中断时可能丢失已流式输出的助手文本。
|
|
243
|
+
- 处理是事件驱动和 session-local 的;没有持久的空闲定时器或 daemon。
|
|
244
|
+
- 确定性的摘要、标签、检索短语和决策主题生成可能遗漏复杂语义。Host enrichment 在可用时改进整理质量,但不保证在每一轮都运行。
|
|
245
|
+
- Host enrichment 目前改进 processed memory 的元数据;它不会自主推断或追加决策轨迹事件。
|
|
246
|
+
- 密钥检测器是有意限制的,不是加密方案。
|
|
247
|
+
- 多窗口使用不是强一致性事务系统。
|
|
248
|
+
- Linger 降低 prompt injection 和恶意仓库风险,但不消除它。
|
|
249
|
+
- 确定性评估套件是回归基线,不是普遍真实世界质量的证明。
|
|
250
|
+
|
|
251
|
+
## 开发与验证
|
|
252
|
+
|
|
253
|
+
需要 Node.js 20 或更高版本以及 pnpm。
|
|
254
|
+
|
|
255
|
+
贡献规则和完整的候选门禁见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
256
|
+
|
|
257
|
+
pnpm install --frozen-lockfile
|
|
258
|
+
pnpm test
|
|
259
|
+
node scripts/validate-skill.mjs skills/linger
|
|
260
|
+
pnpm eval:year
|
|
261
|
+
pnpm eval:adversarial
|
|
262
|
+
pnpm eval:heldout
|
|
263
|
+
pnpm verify:package
|
|
264
|
+
|
|
265
|
+
当前的干净候选通过了:
|
|
266
|
+
|
|
267
|
+
- 188 项自动化测试。
|
|
268
|
+
- 年度、对抗性和 250+ 事件留出评估门禁,宏观综合得分 1.0。
|
|
269
|
+
- 真实 Codex host-enrichment 验收:从 hook 捕获到精确召回的完整路径;真实 Claude Code host-enrichment 验收:使用 DeepSeek 后端宿主模型。
|
|
270
|
+
- Skill 验证。
|
|
271
|
+
- 精确 tarball 包管理器安装。
|
|
272
|
+
- 捕获到召回的冒烟测试。
|
|
273
|
+
- 卸载后 Vault 保留验证。
|
|
274
|
+
- 已跟踪文件的凭据扫描。
|
|
275
|
+
|
|
276
|
+
这些生成的测试套件保护已知行为,不证明在任意对话上的通用记忆质量。
|
|
277
|
+
|
|
278
|
+
## 卸载与清除
|
|
279
|
+
|
|
280
|
+
使用推荐的 npx 安装方式时:
|
|
281
|
+
|
|
282
|
+
npx linger-skill uninstall --yes
|
|
283
|
+
|
|
284
|
+
使用全局安装或源码安装时:
|
|
285
|
+
|
|
286
|
+
linger uninstall --yes
|
|
287
|
+
|
|
288
|
+
卸载移除受管的 Skill 和 hook 集成,但保留 Vault。
|
|
289
|
+
|
|
290
|
+
若要移除全部状态(包括 Vault):
|
|
291
|
+
|
|
292
|
+
node dist/cli.js purge --yes --confirm PURGE
|
|
293
|
+
|
|
294
|
+
清除是有意独立的操作,需要两个确认机制同时满足。
|
|
295
|
+
|
|
296
|
+
## 许可证
|
|
297
|
+
|
|
298
|
+
Linger 基于 MIT 许可证发布。见 LICENSE。
|
|
299
|
+
|
|
300
|
+
用户可见的变更见 [CHANGELOG.md](CHANGELOG.md),数据边界见 [PRIVACY.md](PRIVACY.md),安全模型与私密报告通道见 [SECURITY.md](SECURITY.md)。
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Linger is an experimental local-first project. It reduces known risks around file-native agent memory, but it is not an audited security product and does not provide a compliance guarantee.
|
|
4
|
+
|
|
5
|
+
## Trust boundary
|
|
6
|
+
|
|
7
|
+
Recalled content and enrichment evidence are untrusted historical data. Agents must not execute commands, prompts, or old system messages found inside memory. Current system and user instructions always take precedence.
|
|
8
|
+
|
|
9
|
+
Linger validates record schemas, IDs, timestamps, canonical locations, content hashes, and physical Vault containment. Critical reads and writes reject symlink paths that escape the real Vault root. Invalid or tampered records fail closed, are reported by `doctor`, and move only through confirmed repair operations.
|
|
10
|
+
|
|
11
|
+
High-confidence credential patterns are redacted before raw persistence and excluded from normal processing and recall. This detector is deliberately conservative and is not a complete secret scanner or encryption system. Protect the Vault like any other local transcript archive.
|
|
12
|
+
|
|
13
|
+
## Host integration
|
|
14
|
+
|
|
15
|
+
The installer merges managed lifecycle hooks into existing Claude Code or Codex configuration and preserves unrelated user settings. Linger does not disable, replace, rewrite, or delete host-owned memory. Uninstall removes only Linger-managed integration and preserves the Vault.
|
|
16
|
+
|
|
17
|
+
Codex may require explicit hook trust. Host-owned memory may contain some of the same facts as Linger. Retrieved Linger records remain separately labelled historical evidence; duplicate context is not independent corroboration, and conflicting versions must not be silently collapsed.
|
|
18
|
+
|
|
19
|
+
Optional enrichment uses only the model already active in Claude Code or Codex. Linger does not configure a separate model provider or credential. Enrichment submissions are bounded, project-scoped, limited to normal-sensitivity evidence, and revalidated against immutable source hashes before commit.
|
|
20
|
+
|
|
21
|
+
## Reporting a vulnerability
|
|
22
|
+
|
|
23
|
+
Report vulnerabilities privately through [GitHub Security Advisories](https://github.com/arishana1969/linger-skill/security/advisories/new).
|
|
24
|
+
|
|
25
|
+
Do not include live credentials, private conversations, or Vault files in a public issue. Include the affected version, expected behavior, reproduction steps using synthetic data, and the security impact when possible.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const CODEX_LIVE_EVENTS: readonly ["SessionStart", "UserPromptSubmit", "Stop"];
|
|
2
|
+
export type CodexLiveEvent = typeof CODEX_LIVE_EVENTS[number];
|
|
3
|
+
interface EventEvidence {
|
|
4
|
+
observed_at: string;
|
|
5
|
+
captured_event_id?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SessionEvidence {
|
|
8
|
+
session_id: string;
|
|
9
|
+
project_id: string;
|
|
10
|
+
runtime_identity: string;
|
|
11
|
+
first_observed_at: string;
|
|
12
|
+
last_observed_at: string;
|
|
13
|
+
events: Partial<Record<CodexLiveEvent, EventEvidence>>;
|
|
14
|
+
}
|
|
15
|
+
export interface CodexAdapterEvidence {
|
|
16
|
+
schema_version: 2;
|
|
17
|
+
adapter: "codex";
|
|
18
|
+
sessions: SessionEvidence[];
|
|
19
|
+
}
|
|
20
|
+
export declare function recordCodexLiveEvent(root: string, input: {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
projectId: string;
|
|
23
|
+
runtimeIdentity: string;
|
|
24
|
+
event: CodexLiveEvent;
|
|
25
|
+
capturedEventId?: string;
|
|
26
|
+
}, now?: Date): Promise<void>;
|
|
27
|
+
export declare function readCodexAdapterEvidence(root: string): Promise<CodexAdapterEvidence | undefined>;
|
|
28
|
+
export declare function verifiedCodexLiveSession(root: string, evidence: CodexAdapterEvidence | undefined, runtimeIdentities: string[]): Promise<SessionEvidence | undefined>;
|
|
29
|
+
export declare function assertCodexAdapterEvidence(value: unknown): asserts value is CodexAdapterEvidence;
|
|
30
|
+
export declare function assertCodexAdapterEvidencePath(root: string, file: string): void;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { assertReadableInside, assertWritableInside, atomicJson, readJson, withFileLock } from "./io.js";
|
|
4
|
+
import { assertSafeId, vaultPaths } from "./paths.js";
|
|
5
|
+
import { assertRawRecordPath } from "./record-paths.js";
|
|
6
|
+
import { assertRawEvent } from "./schema-validation.js";
|
|
7
|
+
export const CODEX_LIVE_EVENTS = ["SessionStart", "UserPromptSubmit", "Stop"];
|
|
8
|
+
export async function recordCodexLiveEvent(root, input, now = new Date()) {
|
|
9
|
+
const sessionId = assertSafeId(input.sessionId, "Codex evidence session id");
|
|
10
|
+
const projectId = assertSafeId(input.projectId, "Codex evidence project id");
|
|
11
|
+
if (!/^[a-f0-9]{64}$/.test(input.runtimeIdentity))
|
|
12
|
+
throw new Error("Invalid Codex runtime identity");
|
|
13
|
+
if (!CODEX_LIVE_EVENTS.includes(input.event))
|
|
14
|
+
throw new Error("Invalid Codex live event");
|
|
15
|
+
if (input.capturedEventId !== undefined)
|
|
16
|
+
assertSafeId(input.capturedEventId, "Codex captured event id");
|
|
17
|
+
const dir = path.join(vaultPaths(root).registry, "adapter-evidence");
|
|
18
|
+
const file = path.join(dir, "codex.json");
|
|
19
|
+
const lock = path.join(dir, ".codex.lock");
|
|
20
|
+
await assertWritableInside(root, file);
|
|
21
|
+
await assertWritableInside(root, lock);
|
|
22
|
+
await withFileLock(lock, async () => {
|
|
23
|
+
const evidence = await readEvidence(file);
|
|
24
|
+
const observed = now.toISOString();
|
|
25
|
+
let session = evidence.sessions.find(item => item.session_id === sessionId
|
|
26
|
+
&& item.project_id === projectId
|
|
27
|
+
&& item.runtime_identity === input.runtimeIdentity);
|
|
28
|
+
if (!session) {
|
|
29
|
+
session = {
|
|
30
|
+
session_id: sessionId,
|
|
31
|
+
project_id: projectId,
|
|
32
|
+
runtime_identity: input.runtimeIdentity,
|
|
33
|
+
first_observed_at: observed,
|
|
34
|
+
last_observed_at: observed,
|
|
35
|
+
events: {}
|
|
36
|
+
};
|
|
37
|
+
evidence.sessions.push(session);
|
|
38
|
+
}
|
|
39
|
+
session.last_observed_at = observed;
|
|
40
|
+
session.events[input.event] = {
|
|
41
|
+
observed_at: observed,
|
|
42
|
+
...(input.capturedEventId ? { captured_event_id: input.capturedEventId } : {})
|
|
43
|
+
};
|
|
44
|
+
evidence.sessions = evidence.sessions
|
|
45
|
+
.sort((a, b) => b.last_observed_at.localeCompare(a.last_observed_at))
|
|
46
|
+
.slice(0, 20);
|
|
47
|
+
await atomicJson(file, evidence);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export async function readCodexAdapterEvidence(root) {
|
|
51
|
+
const file = path.join(vaultPaths(root).registry, "adapter-evidence", "codex.json");
|
|
52
|
+
try {
|
|
53
|
+
await assertReadableInside(root, file);
|
|
54
|
+
const value = await readJson(file);
|
|
55
|
+
if (isLegacyEvidence(value))
|
|
56
|
+
return undefined;
|
|
57
|
+
assertCodexAdapterEvidence(value);
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error.code === "ENOENT")
|
|
62
|
+
return undefined;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function verifiedCodexLiveSession(root, evidence, runtimeIdentities) {
|
|
67
|
+
const allowed = new Set(runtimeIdentities.filter(identity => /^[a-f0-9]{64}$/.test(identity)));
|
|
68
|
+
if (!allowed.size)
|
|
69
|
+
return undefined;
|
|
70
|
+
for (const session of evidence?.sessions ?? []) {
|
|
71
|
+
if (!allowed.has(session.runtime_identity) || !session.events.SessionStart)
|
|
72
|
+
continue;
|
|
73
|
+
const user = session.events.UserPromptSubmit?.captured_event_id;
|
|
74
|
+
const assistant = session.events.Stop?.captured_event_id;
|
|
75
|
+
if (!user || !assistant)
|
|
76
|
+
continue;
|
|
77
|
+
if (await verifiedCapture(root, session, user, "user") && await verifiedCapture(root, session, assistant, "assistant"))
|
|
78
|
+
return session;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
async function verifiedCapture(root, session, eventId, role) {
|
|
83
|
+
const paths = vaultPaths(root);
|
|
84
|
+
const file = path.join(paths.raw, session.project_id, session.session_id, `${eventId}.json`);
|
|
85
|
+
try {
|
|
86
|
+
await assertReadableInside(root, file);
|
|
87
|
+
const event = await readJson(file);
|
|
88
|
+
assertRawEvent(event);
|
|
89
|
+
assertRawRecordPath(paths, file, event);
|
|
90
|
+
const hash = createHash("sha256").update(event.content).digest("hex");
|
|
91
|
+
return event.event_id === eventId
|
|
92
|
+
&& event.project_id === session.project_id
|
|
93
|
+
&& event.session_id === session.session_id
|
|
94
|
+
&& event.role === role
|
|
95
|
+
&& event.source_agent === "codex"
|
|
96
|
+
&& event.content_hash === hash;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function readEvidence(file) {
|
|
103
|
+
try {
|
|
104
|
+
const value = await readJson(file);
|
|
105
|
+
if (isLegacyEvidence(value))
|
|
106
|
+
return emptyEvidence();
|
|
107
|
+
assertCodexAdapterEvidence(value);
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code === "ENOENT")
|
|
112
|
+
return emptyEvidence();
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function emptyEvidence() { return { schema_version: 2, adapter: "codex", sessions: [] }; }
|
|
117
|
+
function isLegacyEvidence(value) {
|
|
118
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
119
|
+
return false;
|
|
120
|
+
const record = value;
|
|
121
|
+
return record.schema_version === 1 && record.adapter === "codex" && Array.isArray(record.sessions);
|
|
122
|
+
}
|
|
123
|
+
export function assertCodexAdapterEvidence(value) {
|
|
124
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
125
|
+
throw new Error("Invalid Codex adapter evidence");
|
|
126
|
+
const record = value;
|
|
127
|
+
if (record.schema_version !== 2 || record.adapter !== "codex" || !Array.isArray(record.sessions))
|
|
128
|
+
throw new Error("Invalid Codex adapter evidence");
|
|
129
|
+
for (const candidate of record.sessions) {
|
|
130
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
|
|
131
|
+
throw new Error("Invalid Codex adapter evidence session");
|
|
132
|
+
const session = candidate;
|
|
133
|
+
if (typeof session.session_id !== "string" || typeof session.project_id !== "string")
|
|
134
|
+
throw new Error("Invalid Codex adapter evidence session");
|
|
135
|
+
assertSafeId(session.session_id, "Codex evidence session id");
|
|
136
|
+
assertSafeId(session.project_id, "Codex evidence project id");
|
|
137
|
+
if (typeof session.runtime_identity !== "string" || !/^[a-f0-9]{64}$/.test(session.runtime_identity))
|
|
138
|
+
throw new Error("Invalid Codex adapter evidence runtime");
|
|
139
|
+
assertIsoTimestamp(session.first_observed_at);
|
|
140
|
+
assertIsoTimestamp(session.last_observed_at);
|
|
141
|
+
if (!session.events || typeof session.events !== "object" || Array.isArray(session.events))
|
|
142
|
+
throw new Error("Invalid Codex adapter evidence events");
|
|
143
|
+
for (const [event, detail] of Object.entries(session.events)) {
|
|
144
|
+
if (!CODEX_LIVE_EVENTS.includes(event) || !detail || typeof detail !== "object" || Array.isArray(detail))
|
|
145
|
+
throw new Error("Invalid Codex adapter evidence event");
|
|
146
|
+
const item = detail;
|
|
147
|
+
assertIsoTimestamp(item.observed_at);
|
|
148
|
+
if (item.captured_event_id !== undefined) {
|
|
149
|
+
if (typeof item.captured_event_id !== "string")
|
|
150
|
+
throw new Error("Invalid Codex adapter evidence event");
|
|
151
|
+
assertSafeId(item.captured_event_id, "Codex captured event id");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
export function assertCodexAdapterEvidencePath(root, file) {
|
|
157
|
+
const expected = path.join(vaultPaths(root).registry, "adapter-evidence", "codex.json");
|
|
158
|
+
if (path.resolve(file) !== path.resolve(expected))
|
|
159
|
+
throw new Error("Invalid Codex adapter evidence path");
|
|
160
|
+
}
|
|
161
|
+
function assertIsoTimestamp(value) {
|
|
162
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/.test(value) || !Number.isFinite(Date.parse(value))) {
|
|
163
|
+
throw new Error("Invalid Codex adapter evidence timestamp");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=adapter-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-evidence.js","sourceRoot":"","sources":["../src/adapter-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type CapabilityLevel = 0 | 1 | 2 | 3 | 4;
|
|
2
|
+
export type AdapterName = "claude-code" | "codex";
|
|
3
|
+
export interface AdapterCapability {
|
|
4
|
+
adapter: AdapterName;
|
|
5
|
+
detected: boolean;
|
|
6
|
+
installed: boolean;
|
|
7
|
+
level: CapabilityLevel;
|
|
8
|
+
capabilities: {
|
|
9
|
+
rules: boolean;
|
|
10
|
+
file_access: boolean;
|
|
11
|
+
lifecycle_hooks: boolean;
|
|
12
|
+
async_processing: boolean;
|
|
13
|
+
recovery: boolean;
|
|
14
|
+
};
|
|
15
|
+
evidence: string[];
|
|
16
|
+
limitations: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function capabilityReport(home: string): Promise<AdapterCapability[]>;
|
|
19
|
+
export declare function detectClaudeCode(home: string): Promise<AdapterCapability>;
|
|
20
|
+
export declare function detectCodex(home: string): Promise<AdapterCapability>;
|