Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ dsh plugin --profile web add @hr98w/dsh-memory

Restart `dsh web` completely after installation.

The plugin adapts to the newer DSH Web RPC and Session persistence APIs. Restart Web after updating the plugin; refreshing the page alone is not enough.
The plugin targets the DSH 0.2.x Web Connection, RPC, and Session persistence APIs. After updating from 0.1.4 or earlier, fully restart Web; refreshing the page alone is not enough.

### Install from source

Expand Down Expand Up @@ -150,7 +150,7 @@ $DSH_HOME/memory/
└── <memory-name>.md # authoritative detailed memory
```

- Web management APIs are available only to the local machine;
- Web management is enabled only when the page is served from the local machine; the `/memory` channel uses the DSH Connection Host/Origin and browser-auth fence (loopback by default);
- Web memory-write requests never submit a memory root, absolute path, cwd, or internal Workspace key; the header shows the Host-reported local memory root for diagnostics only;
- Debug is off by default and does not record conversation evidence, memory/model proposal bodies, or API keys;
- `MEMORY.md` is a rebuildable index; detailed Markdown files are authoritative Workspace data.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ dsh plugin --profile web add @hr98w/dsh-memory

安装完成后请完全重启 `dsh web`。

插件已适配新版 DSH 的 Web RPC 与 Session persistence 接口;更新插件后也需重启 Web,不能只刷新页面。
插件适配 DSH 0.2.x 的 Web Connection、RPC 与 Session persistence 接口;从 0.1.4 或更早版本更新后必须完全重启 Web,不能只刷新页面。

### 从源码安装

Expand Down Expand Up @@ -150,7 +150,7 @@ $DSH_HOME/memory/
└── <memory-name>.md # 权威详细记忆
```

- Web 管理接口只允许本机访问;
- Web 管理页面只在本机访问时启用;`/memory` 通道沿用 DSH Connection 的 Host/Origin 与 browser-auth 准入(默认只接受 loopback);
- Web 的记忆写入请求不会提交记忆根目录、绝对路径、cwd 或内部 Workspace key;页面顶部仅为本机诊断显示 Host 返回的记忆根目录;
- Debug 默认关闭,不记录对话证据、记忆正文、模型提议正文或 API key;
- `MEMORY.md` 是可重建索引,详细 Markdown 文件才是 Workspace 权威数据。
Expand Down
7 changes: 4 additions & 3 deletions cordis.patch.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# dsh-memory contributes one Host plugin; its browser half is discovered through dsh.client.
# Standard DSH Web Connection needs webServer for generic RPC registrations.
# Preserve webRuntime used by its config; module-level dependencies still merge.
# The standard DSH Web Connection row reads `webStartup` for its config and still accesses
# `webServer` from its own provider context to register generic RPC channels; keep both
# provider dependencies. Module-level dependencies such as `credentials` still merge.
- id: connection
name: '@deepseek-ai/dsh-client-connection'
inject: [webRuntime, webServer]
inject: [webStartup, webServer]

- insert:
- id: dsh-memory
Expand Down
8 changes: 4 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
```text
Agent step Web Browser
│ │
│ Prompt context / memory_update │ loopback RPC /memory
│ Prompt context / memory_update │ Connection RPC /memory
▼ ▼
┌──────────────────────────────────────────────────┐
│ Host Cordis plugin: @hr98w/dsh-memory │
Expand All @@ -29,7 +29,7 @@ Agent step Web Browser

| 文件 | 职责 |
|---|---|
| `src/index.ts` | Host Cordis entry;组合 Service、Prompt、Tool 和 loopback RPC Consumer |
| `src/index.ts` | Host Cordis entry;组合 Service、Prompt、Tool 和 Connection RPC Consumer |
| `src/memory-store.ts` | Markdown Provider;格式校验、revision、CAS、scope lock、原子发布和索引重建 |
| `src/memory-observation.ts` | 将每次动态 context 的 Host-only revision 绑定到 live Agent,并为模型写入提供 CAS 前置条件 |
| `src/protocol.ts` | Host/Browser 共用的 wire 类型和双侧运行时解析 |
Expand Down Expand Up @@ -111,12 +111,12 @@ memory_update(change without revision)

## Browser 请求流程

Bundle 对标准 `connection` row(同时匹配 id 与包名)设置 `inject: [webRuntime, webServer]`,保留其 Web 配置依赖,并修复 DSH Electron 合并后通用 RPC 注册缺少 `webServer` 依赖的问题。模块声明的 `credentials` 仍由 Loader 合并;不改认证或请求处理逻辑。Headless 没有该 row 时补丁警告并跳过,不创建 Connection。自定义 Connection row 的额外依赖需由后加载的用户 patch 保留,因为 Bundle patch 的 `inject` 字段是替换而非追加。背景与移除条件见 [兼容性决策](decisions/implemented/bug-fix/2026-09-10-connection-webserver-injection.md)。
Bundle 对标准 `connection` row(同时匹配 id 与包名)设置 `inject: [webStartup, webServer]`:当前 DSH 的该 row 从 `webStartup` 求值 `trustedHosts` 配置,而通用 RPC 注册仍从 provider context 读取 `webServer`;两者缺一都会让 Connection 或 memory RPC 子 fiber 失败。模块声明的 `credentials` 仍由 Loader 合并;不改认证或请求处理逻辑。当前 DSH 的 `connection.rpc.handle()` 没有 per-channel authority 参数,memory 通道沿用 Connection 的标准 Host/Origin 与 browser-auth 准入,Browser 仍在 `isLoopback` 为 false 时禁用 Memory 操作。Headless 没有该 row 时补丁警告并跳过,不创建 Connection。自定义 Connection row 的额外依赖需由后加载的用户 patch 保留,因为 Bundle patch 的 `inject` 字段是替换而非追加。背景与移除条件见 [兼容性决策](decisions/implemented/bug-fix/2026-09-10-connection-webserver-injection.md)。

```text
Memory Settings
-> ctx.connection.rpc.call('/memory', endpoint, payload)
-> Connection enforces loopback authority
-> Connection applies the Host/Origin and browser-auth fence
-> Host dsh-memory handler validates payload
-> MemoryStore
-> Host validates/constructs response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ The first implementation needed to prove that a standalone DSH Bundle can own du

## Decision

Build the first vertical slice around a Host `MemoryStore` Service. The Host entry mounts dynamic prompt context and `memory_update` through optional Cordis inject fibers, and mounts a dedicated loopback Connection RPC channel only when Web provides `connection`. The Browser entry registers an independent Settings section and parses every Host response again.
Build the first vertical slice around a Host `MemoryStore` Service. The Host entry mounts dynamic prompt context and `memory_update` through optional Cordis inject fibers, and mounts a dedicated Connection RPC channel only when Web provides `connection`. The Browser entry registers an independent Settings section, gates Memory operations on `connection.isLoopback`, and parses every Host response again.

The first UI milestone implements Status and Global editing. Workspaces and Sessions retain visible navigation seats but do not claim functional data flows until their Host endpoints and lifecycle tests exist.

## Alternatives considered

- Direct file access from the Browser was rejected because Browser and Host are separate runtimes and the Browser must not receive filesystem authority.
- A Typert `/api` Remote was rejected because the generic Gateway uses trusted-host authority, while memory administration requires loopback authority.
- A Typert `/api` Remote was rejected because it would expose memory administration through the shared Gateway surface instead of a dedicated channel the Browser gates on loopback.
- Building Session consolidation before the online read/write path was rejected because it would introduce a second writer before revision, validation, and index publication were proven.

## Consequences
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
# Restore the standard Web Connection registration dependency
# Restore the standard Web Connection provider dependencies

## Problem

DSH commit `2ef85b1e17591fb9fadc78549726413c56f38dfa`, merged into master by `ba05b7d49de63106afdd7efcf741cca1985fae18`, removed `webServer` from Connection's provider dependencies. Generic `connection.rpc.handle()` still accesses that service through the provider context. The memory RPC child fails with `cannot get property "webServer" without inject`, while the parent plugin remains active. Unmatched POST requests then reach the static fallback and return HTTP 405.
The current DSH Web bundle composes the standard `connection` row with `inject: [webStartup]`: that row evaluates its `trustedHosts` config from `webStartup`. The Connection module itself declares only `credentials`, while generic `connection.rpc.handle()` still reads `webServer` through the provider context to register an HTTP route.

Root-provided service mocks hide this failure. The reproduction requires sibling provider plugins and explicitly awaiting failed child fibers, not just the parent entry.
The v0.1.4 Bundle override set the row's entry-level `inject` to `[webRuntime, webServer]`. Current DSH no longer provides `webRuntime`, so Connection waits for a missing service and the Web startup aborts with `connection (required) missing webRuntime`. The same override also dropped `webStartup`, so the row config could not be evaluated. Removing the override entirely keeps Web booting but leaves the memory RPC child failing with `cannot get property "webServer" without inject`; unmatched POST requests then reach the static fallback and return HTTP 405.

Root-provided service mocks hide the registration failure. The reproduction requires sibling provider plugins and explicitly awaiting failed child fibers, not just the parent entry.

## Decision

Add a Bundle override guarded by both the standard `connection` id and `@deepseek-ai/dsh-client-connection` package name. Set its entry-level `inject` to `[webRuntime, webServer]`: retain the standard Web config's dependency and restore HTTP registration access. Loader still merges module-level dependencies such as `credentials`. Do not modify DSH source, user configuration, authentication, RPC envelopes, or memory semantics.
Keep a Bundle override guarded by both the standard `connection` id and `@deepseek-ai/dsh-client-connection` package name, with entry-level `inject: [webStartup, webServer]`: retain the config service the current row needs and restore HTTP registration access. Loader still merges module-level dependencies such as `credentials`. Do not modify DSH source, user configuration, authentication, RPC envelopes, or memory semantics.

Adapt the affected Host APIs to their current shapes: `connection.rpc.handle(channel, handler)` has no per-channel authority argument, so the memory channel inherits Connection's standard Host/Origin and browser-auth fence; the Browser surface still gates Memory operations on `connection.isLoopback`. `installModelSelection(agentCtx)` takes only the Agent context and reads the step selection from DSH.

Keep a Cordis regression test and a separate real-DSH source-checkout check using the shipped YAML, official patch algorithm, actual HostConnectionService, and built memory entry. Cover missing/mismatched targets, credentials/config preservation, failing unpatched registration, successful patched registration, disposal, and Headless loading.
Keep a Cordis regression test and a separate real-DSH source-checkout check using the shipped YAML, official patch algorithm, actual `HostConnectionService`, and built memory entry. Cover missing/mismatched targets, credentials/config preservation, failing unpatched registration, successful patched registration, disposal, and Headless loading.

## Alternatives considered

- Adding `webServer` only to the memory consumer does not repair the provider-context access.
- Removing the Bundle override leaves generic registration broken under the current DSH baseline.
- Direct HTTP registration would duplicate transport and security responsibilities.
- An upstream fix is preferable long-term, but this repair must stay within this repository.

## Consequences

Restart Web to recompose the Bundle. A Headless composition without Connection emits a skipped-patch warning but gains no Web dependency. This workaround targets the standard Web row; a renamed row is not patched. Because entry `inject` is replaced, deployments with custom extra dependencies must retain them in a later user patch. Connection without a Web server is outside this compatibility override's supported use.
Restart Web to recompose the Bundle. A Headless composition without Connection emits a skipped-patch warning but gains no Web dependency. This workaround targets the standard Web row; a renamed row is not patched. Because entry `inject` is replaced, deployments with custom extra dependencies must retain them in a later user patch. The real-DSH check pins the patched `inject` to the current web-bundle dependencies plus `webServer`, so a future bundle dependency change fails the check instead of being silently dropped.

Remove the override once the supported DSH baseline fixes generic registration ownership and passes the same sibling-provider regression. Keep using the host's existing authentication; this workaround does not restore or change older host interpretations of the `authority` option.
Remove the override once the supported DSH baseline fixes generic registration ownership and passes the same sibling-provider regression. Keep using the host's existing authentication and admission policy; the plugin does not add a separate loopback check or change older host interpretations of an authority option.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Add a fourth Memory Settings tab named Model. The Host projects active providers

Persist only the selected provider and model in `$DSH_HOME/memory/settings.yml`. The settings file has a closed schema, revision-derived CAS, and atomic replacement. A successful selection updates the live consolidation worker for subsequent attempts; an attempt snapshots its route before starting.

Expose the catalog and mutation through loopback-only, runtime-validated `models/read` and `models/select` RPC endpoints. The Host rechecks that a selected route is active before committing it.
Expose the catalog and mutation through runtime-validated `models/read` and `models/select` Connection RPC endpoints; the Settings surface disables them when the page is not loopback. The Host rechecks that a selected route is active before committing it.

## Alternatives considered

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Detailed JSONL diagnostics were written for every consolidation attempt. They ar

Rename the Browser tab to Settings and persist a `debug` boolean beside the consolidation model route in plugin-owned `settings.yml`. Debug logging is disabled by default and applies to new attempts. When disabled, the debug Consumer performs no directory or file write; receipts and replayable worker Sessions remain unchanged.

Keep format-1 settings readable and interpret their missing debug field as disabled. New commits write format 2 and remain revision checked. The existing loopback settings mutation validates the boolean at runtime and can change Debug without requiring the DSH model catalog when the model route itself is unchanged.
Keep format-1 settings readable and interpret their missing debug field as disabled. New commits write format 2 and remain revision checked. The existing Connection settings mutation validates the boolean at runtime and can change Debug without requiring the DSH model catalog when the model route itself is unchanged.

Only show a debug path for an attempt whose response says logging was enabled. Do not keep debug paths on every historical Session card.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The Browser needs to start one consolidation attempt and show its durable result

## Decision

Expose `sessions/consolidate` on the existing loopback-only `/memory` channel. The Browser submits exactly one non-empty Session id, and the Host resolves all source and target state through `SessionPersistence`, the live Agent registry, and `MemoryStore`.
Expose `sessions/consolidate` on the existing `/memory` Connection channel. The Browser submits exactly one non-empty Session id, and the Host resolves all source and target state through `SessionPersistence`, the live Agent registry, and `MemoryStore`.

Assemble the runner only in an optional Cordis child fiber that requires `sessionPersistence`, `agents`, and `sessions`. The first version waits for one attempt and propagates the Connection abort signal instead of introducing a background job system. Its closed response contains only terminal state, recovery flag, review id, attempt number, and change count. Both Host and Browser parse request and response values at runtime.

Expand All @@ -21,7 +21,7 @@ The Session list exposes only a receipt status summary. It marks whether that re

## Consequences

- Manual consolidation remains loopback-only and Host-authoritative.
- Manual consolidation remains Host-authoritative; the Settings surface enables it only on a loopback page, while the channel follows Connection admission.
- Missing Session services disable only consolidation while other Memory Consumers continue to load.
- A completed action can refresh the list and distinguish current-review results from receipts for an older Session revision.
- If real Web testing shows Connection timeouts are unsuitable, a later decision may replace the synchronous response with a Host-owned job protocol.
2 changes: 1 addition & 1 deletion docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Memory 是 DSH Settings 中独立 section,包含:

页面顶部另有只读状态摘要,展示本机 memory root、字节数和 Workspace 数量;错误与最近操作结果在当前页面就地显示。

Browser 与 Host 是两个 Cordis tree。Browser 只能通过 `/memory` loopback Connection RPC 使用受校验的业务值,不能直接访问 Host `ctx`、文件系统、`MemoryStore` 或 `SessionPersistence`。
Browser 与 Host 是两个 Cordis tree。Browser 只能通过 `/memory` Connection RPC 使用受校验的业务值(页面在非 loopback 访问时禁用 Memory 操作),不能直接访问 Host `ctx`、文件系统、`MemoryStore` 或 `SessionPersistence`。

目标 endpoint:

Expand Down
8 changes: 4 additions & 4 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pnpm run build
node scripts/check-memory-rpc.mjs /absolute/path/to/deepseek-harness
```

脚本使用真实补丁算法和 Connection,检查同级服务提供方的 RPC 注册、子 fiber 失败、路由卸载,以及缺少 Connection 的 Headless 组合。它只创建临时记忆目录并在退出时清理,不启动 HTTP 服务或验证浏览器认证。真实页面仍需按下面步骤人工验证。标准 Headless 没有 `connection` row 时会提示跳过该补丁,不影响启动。
脚本使用真实补丁算法和 Connection,检查当前 web-app `connection` row 的 `webStartup` 配置依赖、`webServer` 注册、未打补丁时的子 fiber 失败、路由卸载,以及缺少 Connection 的 Headless 组合。它只创建临时记忆目录并在退出时清理,不启动 HTTP 服务或验证浏览器认证。真实页面仍需按下面步骤人工验证。标准 Headless 没有 `connection` row 时会提示跳过该补丁,不影响启动。

```sh
pnpm run build
Expand All @@ -65,11 +65,11 @@ dsh web --no-open

确认:

1. dump config 包含 `id: dsh-memory`;
2. Web boot 没有 `plugin tree failed to load`;
1. dump config 包含 `id: dsh-memory`,且 `connection` row 的 `inject` 同时包含 `webStartup` 与 `webServer`;
2. Web boot 打印访问 URL,且没有 `startup failed`、`plugin tree failed to load` 或 missing-service 列表;
3. `__DSH_BOOT__` 包含 `@hr98w/dsh-memory` Client;
4. 启动页插件清单中的 `/plugins/@hr98w/dsh-memory/client.js?rev=...` 返回 Browser bundle,而不是 SPA fallback;
5. Settings 导航出现 Memory;
5. Settings 导航出现 Memory,页面显示“已启用”和根目录,而不是 `transport failure ... HTTP 405`;
6. Global 保存后 revision 变化,旧页面保存得到 conflict;
7. Headless 不包含 `connection` 时仍能加载 Prompt 和 Tool。

Expand Down
Loading
Loading