dsh-loci 2.10.1 → 2.11.0
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 +28 -0
- package/docs/PRIOR-ART.md +2 -2
- package/package.json +1 -1
- package/src/host/index.js +1 -1
- package/src/host/scope.js +16 -6
- package/src/mcp/store-facade.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) from 1.0.0.
|
|
6
6
|
|
|
7
|
+
## [2.11.0] — 2026-11-15
|
|
8
|
+
|
|
9
|
+
One fix, made because it is the last thing that could put another project's facts into a
|
|
10
|
+
conversation.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **A session with no `cwd` borrowed the Host process directory as its scope.**
|
|
15
|
+
`resolveScopes` fell back to `process.cwd()` whenever the host did not supply
|
|
16
|
+
`session.header.cwd`. Every such session and every panel request therefore shared one
|
|
17
|
+
workspace key — the directory the Host process was started in, which for a packaged
|
|
18
|
+
install is the install directory. Two consequences: memories written there belonged to no
|
|
19
|
+
project, and any two cwd-less sessions could read each other's entries.
|
|
20
|
+
|
|
21
|
+
A missing cwd now yields an explicit `workspace:unresolved` / `project:unresolved`. It
|
|
22
|
+
collides with no real workspace, appears in no other session's read set, and the panel
|
|
23
|
+
labels it "unresolved workspace" — visible rather than quiet.
|
|
24
|
+
|
|
25
|
+
The trade is deliberate: a cwd-less session loses access to workspace memories instead of
|
|
26
|
+
being handed someone else's. That is the direction the I4′ invariant points.
|
|
27
|
+
|
|
28
|
+
### Docs
|
|
29
|
+
|
|
30
|
+
- `PRIOR-ART.md` I4′ and its expiry table said the process-cwd fallback was "a known risk,
|
|
31
|
+
not a guarantee". It is now a stated behaviour with a test, and the remaining half of that
|
|
32
|
+
risk (project-scope collapse past 12 ancestor directories, no `doctor` check on scope
|
|
33
|
+
identity) is named as still open.
|
|
34
|
+
|
|
7
35
|
## [2.10.1] — 2026-11-15
|
|
8
36
|
|
|
9
37
|
Three more ways a scope mistake could have answered with the wrong data. Found by checking
|
package/docs/PRIOR-ART.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
| **I1** | 注入闸门 | 只有 `status='verified'` 且未过期、未删除的条目进入提示词。`status` 由 store 重新派生,模型无法通过工具断言它。verified 来自**面板/CLI 的人工操作**,或**用户自己消息里逐字存在且与主张有实质关联的引文**(§2.1 的两问闸门)。 |
|
|
19
19
|
| **I2′** | 前缀稳定 | **没有任何写入会重排或改写已有行**;重求值只发生在会话/日边界,绝不在步内。尾部(`as of` 日期、`+N more`、待审提示)允许随写入与 UTC 日期变化。 |
|
|
20
20
|
| **I3** | 默认零网络 | 默认配置下不发任何请求。三条可选出网路径:更新检查、HTTP 向量通道、会话结束的抽取(经宿主 `ctx.llm`,非本进程直连)。前两条默认关闭,第三条默认 `off`。 |
|
|
21
|
-
| **I4′** | 作用域显式 |
|
|
21
|
+
| **I4′** | 作用域显式 | **会话内的每一项读取都绑定显式键集**,不使用通配;包括两个按 id 寻址的工具(`loci_get`/`loci_forget`)和全部按 id 寻址的写操作。面板/导出枚举全部键,那是**明确的行政通道**(管理员视图),不是会话读取。作用域键由宿主 `session.header.cwd` 派生;**该字段缺失时返回显式的 `workspace:unresolved`,不再回落进程 cwd**。 |
|
|
22
22
|
|
|
23
23
|
> **旧版 I2 / I4 为什么被替换**:I2 原文"无写入时字节级不变"在 UTC 零点自破(尾部日期),且在 TTL 到期时于会话中途静默改变内容;I4 原文"读永不越界"与 `allScopeKeys()` 的通配枚举矛盾。把它们改宽不是妥协,是把判据改到能被代码验证的精度。
|
|
24
24
|
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
| I1 | 结构(数据层)/ 语义依赖 | 宿主持续发 `session/event` 让引文缓冲存在 | 缓冲缺失 → 闸门**更严**(fail-safe,全部变候选);引文门槛或 `autoVerifyCaptured` 默认值改动 → 闸门**更松** | **不会**。所有单测都桩掉验证器,真实函数只在手动 e2e 里跑过 |
|
|
32
32
|
| I2′ | 结构(排序)/ 经济性依赖 | provider 的 prefix cache 粒度 | provider 改为按段缓存或取消前缀缓存 → 经济理由消失(安全性理由仍在) | **不会**。仓库里没有任何 cache 命中量;`request_stat` 记的是字节数 |
|
|
33
33
|
| I3 | 结构(无直连)/ 计数依赖 | 三个默认布尔值 + 枚举完整性 | 新增经宿主服务的出网(如 `ctx.llm` 的另一处调用)→ grep 门看不见 | **部分**。直接 `fetch` 会挂 CI,宿主中介出网不会 |
|
|
34
|
-
| I4′ | 结构(查询)/ 派生依赖 | `session.header.cwd` 存在性 | 会话创建时不带 cwd →
|
|
34
|
+
| I4′ | 结构(查询)/ 派生依赖 | `session.header.cwd` 存在性 | 会话创建时不带 cwd → 2.11.0 起落到显式的 `workspace:unresolved`(不泄漏,但该会话看不到工作区记忆);深目录超过 12 层 → 项目作用域塌成工作区 | **部分**。无 cwd 已由测试固定并与面板标签可见;12 层上限与作用域身份仍无 doctor 检查 |
|
|
35
35
|
|
|
36
36
|
**这套机制本身是这次审计最有价值的产出**:判据要带失效条件,否则它只是当时当地的偏好,被后人当成真理。任何新增不变量必须同时写它的失效信号。
|
|
37
37
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-loci",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Layered long-term memory for DeepSeek Harness: the prompt sees only a stable index, full text is one lookup away. Bi-temporal facts, strict scope isolation, verified-only injection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/host/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { registerCommands } from './commands.js';
|
|
|
23
23
|
import { runDoctor } from './doctor.js';
|
|
24
24
|
|
|
25
25
|
/** Version stamped into the database and diagnostics. */
|
|
26
|
-
export const VERSION = '2.
|
|
26
|
+
export const VERSION = '2.11.0';
|
|
27
27
|
|
|
28
28
|
/** Optional collaborators; without them the plugin still serves tools. */
|
|
29
29
|
export const inject = ['systemPrompt', 'tools'];
|
package/src/host/scope.js
CHANGED
|
@@ -80,21 +80,31 @@ function pathKey(prefix, path) {
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Build the scope identity of one session.
|
|
83
|
+
*
|
|
84
|
+
* When the host does not supply a cwd, the identity is **explicitly unresolved** rather
|
|
85
|
+
* than borrowed from `process.cwd()`. Borrowing it meant every cwd-less session and every
|
|
86
|
+
* panel request shared one scope keyed to wherever the Host process happened to be
|
|
87
|
+
* installed — a silent cross-project bucket, and the opposite of what the explicit-scope
|
|
88
|
+
* invariant promises. An unresolved key appears in no other session's read set, so the
|
|
89
|
+
* failure is visible (the panel labels it "unresolved workspace") instead of a quiet leak.
|
|
90
|
+
*
|
|
83
91
|
* @param options - `sessionId`, `cwd`.
|
|
84
92
|
* @returns `{session, workspace, project, global, labels, cwd, projectRoot}`.
|
|
85
93
|
*/
|
|
86
94
|
export function resolveScopes({ sessionId, cwd }) {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
95
|
+
const requested = typeof cwd === 'string' && cwd.length > 0 ? cwd : undefined;
|
|
96
|
+
const dir = requested === undefined ? undefined : canonicalDir(requested);
|
|
97
|
+
const projectRoot = dir === undefined ? undefined : findProjectRoot(dir);
|
|
98
|
+
const unresolved = dir === undefined;
|
|
89
99
|
return {
|
|
90
100
|
session: `session:${sessionId ?? 'unknown'}`,
|
|
91
|
-
workspace: pathKey('workspace', dir),
|
|
92
|
-
project: pathKey('project', projectRoot),
|
|
101
|
+
workspace: unresolved ? 'workspace:unresolved' : pathKey('workspace', dir),
|
|
102
|
+
project: unresolved ? 'project:unresolved' : pathKey('project', projectRoot),
|
|
93
103
|
global: 'global:',
|
|
94
104
|
labels: {
|
|
95
105
|
session: sessionId ? `session ${sessionId}` : 'session',
|
|
96
|
-
workspace: basename(dir) || dir,
|
|
97
|
-
project: basename(projectRoot) || projectRoot,
|
|
106
|
+
workspace: unresolved ? 'unresolved workspace' : basename(dir) || dir,
|
|
107
|
+
project: unresolved ? 'unresolved project' : basename(projectRoot) || projectRoot,
|
|
98
108
|
global: 'global',
|
|
99
109
|
},
|
|
100
110
|
cwd: dir,
|
package/src/mcp/store-facade.js
CHANGED