dsh-hypatia-auto-memory 0.4.0 → 1.0.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/README.md CHANGED
@@ -43,7 +43,8 @@ dsh plugin --profile web add dsh-hypatia-auto-memory
43
43
 
44
44
  Repeat for other profiles (desktop, dsh-tui). Restart the profile after install.
45
45
 
46
- The browser settings card is pre-built in `lib/client.js` and shipped with the
46
+ The browser half — the settings card and the conversation's Memory tab — is
47
+ pre-built in `lib/client.js` and shipped with the
47
48
  package. If you edit `src/client/*`, run `npm install && npm run build` in this
48
49
  directory to regenerate it; while `pnpm run dev:web` is active in the DSH
49
50
  checkout, client-plugin changes reload without a full page refresh.
@@ -253,10 +254,62 @@ agent/session-start ──▶ rules/taboos inject()
253
254
  in agent sessions — presets load disk skills in a nearer layer — so it is
254
255
  reported with its directory.
255
256
 
257
+ - **The conversation's Memory tab** is the read face of everything above. It sits
258
+ beside the **Chat** and **Trajectory** tabs (a `conversation.view` entry with
259
+ id `memory`, ordered after Trajectory) and shows, for the session it is bound
260
+ to, three things. **Status**: the logging and consolidation watermarks, the
261
+ pending token backlog, whether the `session-<id>` node exists, and any task
262
+ that exhausted its attempts together with the error that stopped it.
263
+ **Span summaries**: the `sum-<session>-<from>-<to>` entries consolidation
264
+ produced, with their archive tier. **Work units**: the `wu-*` entries derived
265
+ from those summaries, reached through the `derivedFrom` edges — their names are
266
+ content-addressed, so the edge is the only way to find them. Bodies render
267
+ through the shell's own `MarkdownText`, so a summary reads like assistant
268
+ Markdown elsewhere in the GUI.
269
+
270
+ Raw `msg-*` entries are deliberately **not** shown: they hold the conversation
271
+ verbatim, the reader just wrote them, and the agent is the right reader for
272
+ them.
273
+
274
+ The data arrives over a session-scoped, read-only HTTP route this plugin
275
+ registers on the DSH web server, `/api/dsh-hypatia-auto-memory/session`: status
276
+ from the in-memory state tables on every poll, the shelf content only when it
277
+ can have changed (on open, on a session switch, on a manual refresh, and when
278
+ consolidation advances the session's watermark), cached for 15 s in between.
279
+ The tab polls every five seconds while it is on screen, and a response is
280
+ bounded: the newest 40 summaries and 40 work units, 4000 characters per body
281
+ and 120 000 per response. JSE has no `ORDER BY`, so "newest" is decided only
282
+ after reading every candidate; when a scan reaches its cap the counts become
283
+ lower bounds, and the tab reports that response as truncated rather than
284
+ leaving a short list unexplained.
285
+ Two channels were rejected on the way here. A **session projection** would be
286
+ the best shape, but the event that would drive it cannot be written from
287
+ outside this repository: `Session.append` offers no way to set the envelope's
288
+ `ignorable` marker, and the persistence read path refuses a session log
289
+ carrying an unknown event type without it — so a custom event would break
290
+ session reload. A **settings namespace** was root-scoped even when it existed:
291
+ the Host cannot know which session the browser is showing, so it would have
292
+ to ship every session's data. (dsh 0.1.7 removed the Host-side settings
293
+ registration API entirely; the route family below is now the only channel.)
294
+
295
+ The route is not one of the composed app's authenticated routes — a route a
296
+ plugin registers never is — so it authenticates itself the way
297
+ `dsh-hypatia-ui`'s does: the socket must be loopback, the `Host` must be a
298
+ loopback name (`localhost`, `127.x`, `[::1]`), and the request same-origin. A
299
+ browser tab on this page passes all three; a page on another origin fails the
300
+ last even from the same machine, and a DNS-rebound page fails the `Host` check.
301
+ `~/.dsh/storages/hypatia_auto_memory.json` and
302
+ `hypatia_auto_memory_diag.json` remain the durable record; the tab is a view of
303
+ the same facts, not a replacement for them.
304
+
256
305
  ## Configuration
257
306
 
258
- Settings namespace `hypatia-auto-memory` (edit `settings.yaml` or Web
259
- settings; all fields optional, defaults shown):
307
+ The plugin's cordis `Config` is its settings tab (dsh ≥ 0.1.7 projects a
308
+ plugin's Config schema into Settings → Plugins; every field below applies on
309
+ save, without a profile reload). Stored as the plugin entry's `config:` in the
310
+ profile patch; a pre-0.1.7 `settings.yaml` section is imported once on first
311
+ launch. All fields optional, defaults shown. A blank consolidation route is
312
+ refused on save; a duplicate one is dropped at runtime with a warning:
260
313
 
261
314
  ```yaml
262
315
  hypatia-auto-memory:
@@ -311,19 +364,24 @@ The cordis config block on the bundle row only carries skill packaging:
311
364
  skillsDir: /abs/path # override the packaged skills/ directory
312
365
  ```
313
366
 
314
- `enabled` and `autoApprove` are read when the plugin starts, so changing either
315
- needs a profile reload; every other switch applies immediately.
367
+ `enabled`, `shelf` and `autoApprove` are read when the collector starts, so
368
+ saving a change to any of them restarts the collector (queue drained, state
369
+ re-opened); every other switch applies in place. The same restart is what
370
+ applies an imported pre-0.1.7 `settings.yaml`: dsh imports it only after every
371
+ plugin has started, and the collector itself waits for that point before it
372
+ starts, so it rarely runs on the defaults at all.
316
373
 
317
374
  ### Choosing the shelf
318
375
 
319
376
  `shelf` names the hypatia shelf every entry is written to and every lookup
320
377
  reads — logging, consolidation, the cascade, the rules/taboos preload, and the
321
378
  startup housekeeping. The settings card offers it as a dropdown of what
322
- `hypatia list` reports (refreshed about once a minute, and after every settings
323
- change), marking shelves that are registered but not connected.
379
+ `hypatia list` reports — read on demand when the tab opens; a successful
380
+ listing is cached for 60 s on the Host, a failed one never is — marking
381
+ shelves that are registered but not connected.
324
382
 
325
- - **Takes effect after a profile reload**, like `enabled`. A running queue keeps
326
- writing where it started; the log says so when the setting changes.
383
+ - **Takes effect on save**, like `enabled`: the collector restarts on the new
384
+ shelf, and the log says so.
327
385
  - **Progress is kept per shelf.** Watermarks and queued tasks are stored per
328
386
  shelf, so a newly chosen shelf starts every session from zero, and switching
329
387
  back resumes exactly where that shelf left off — nothing is re-logged or
@@ -343,16 +401,23 @@ change), marking shelves that are registered but not connected.
343
401
  (`hypatia connect <dir> --name <name>`). Over MCP, that failure restarts the
344
402
  server, so the retry after a `connect` reaches the shelf.
345
403
 
346
- The listing reaches the browser as a second, read-only settings namespace,
347
- `hypatia-auto-memory-shelves`, which the Host re-registers whenever the listing
348
- changes. No card claims it, so it renders nowhere; nothing ever writes to it.
404
+ The listing reaches the browser over the same route family as the Memory tab —
405
+ `GET /api/dsh-hypatia-auto-memory/shelves`, mounted before the startup
406
+ reconcile and backfill (and even while `enabled: false`), so the list is there
407
+ when a broken shelf needs replacing. (It rode a read-only settings namespace until dsh 0.1.7 removed that
408
+ Host-side API; a shelf listing is not configuration, and the settings domain
409
+ now only projects plugin Config forms.)
349
410
 
350
411
  ## Operations checklist
351
412
 
352
413
  1. After a turn ends, entries appear within seconds:
353
- `hypatia knowledge-get msg-<sessionId>-<n>` (`n` counts messages, from 0)
414
+ `hypatia knowledge-get msg-<sessionId>-<n>` (`n` counts messages, from 0).
415
+ The conversation's **Memory** tab answers the same question without a CLI,
416
+ and adds what the CLI cannot show: the watermarks themselves.
354
417
  2. Watermarks live in the state domain; failed tasks stay in the `tasks`
355
- table with their last error for inspection.
418
+ table with their last error for inspection. Both are what the Memory tab
419
+ reads; refresh it after changing the shelf, since the tab's snapshot is
420
+ keyed to the shelf this run is writing to.
356
421
  3. Restart mid-write is safe: messages already stored are skipped
357
422
  (get-before-create); uncovered ranges are re-enqueued from watermarks.
358
423
  4. To check the route warning: consolidation with no selected `models`
@@ -380,8 +445,9 @@ changes. No card claims it, so it renders nowhere; nothing ever writes to it.
380
445
 
381
446
  | Symptom | Cause / handling |
382
447
  |---|---|
383
- | No entries after chatting | `enabled: false`, missing `hypatia` binary, or the collect fiber PENDING (needs `sessions`, `storageDomain`, `subprocess`, `settings` from the base composition) — check profile logs |
448
+ | No entries after chatting | `enabled: false`, missing `hypatia` binary, or the collect fiber PENDING (needs `sessions`, `storageDomain`, `subprocess` from the base composition) — check profile logs |
384
449
  | Nothing logged for a whole run after a restart | The storage domain refused to open because a stored record failed its schema. The storage service validates on read, not on write, so a bad write only surfaces at the next startup — and one bad row fails the whole domain. Look for `startup failed` / `does not match its schema` in the profile log. Task rows missing `error` are now defaulted; for any other bad row, stop DSH, remove it from `~/.dsh/storages/hypatia_auto_memory.json`, and restart |
450
+ | Memory tab never shows data, or shows a read failure | The page is not reached over a **loopback origin**. The route authenticates itself — loopback socket, loopback `Host`, same-origin — so reading DSH from another device (a LAN address, or a `0.0.0.0` binding) is refused by design, and so is a tunnel presenting a public `Host`. Both halves of the payload are refused together, so the tab reports a load failure rather than partial data. Open the GUI at `127.0.0.1` or `localhost` |
385
451
  | Entries appear only after a turn finishes | By design: spans are cut at `turn/end`, or at the first `step/end` once a turn has run longer than `flushWindowMs`, so an entry never lacks its own tool results |
386
452
  | Logging works, no summaries | `consolidation.models` is empty or invalid — one warning at first trigger |
387
453
  | Summaries but no `sum2-*` | Fewer than `cascade.batchSize` unarchived tier-1 summaries in that project yet |
@@ -392,10 +458,10 @@ changes. No card claims it, so it renders nowhere; nothing ever writes to it.
392
458
  | Watermark says logged, but the shelf has no entries | The shelf was reset or switched after logging. Handled at startup: `housekeeping.reconcileOnStartup` resets any row whose session has no `msg-*` left, and that session is re-logged — and re-consolidated — from the start on its next activity. A shelf query that fails leaves the row untouched. A session whose messages were all deleted on purpose is indistinguishable and is logged again; turn the switch off if that matters |
393
459
  | Warning `hypatia mcp unavailable: …; using the hypatia CLI until the profile reloads` | The binary predates `hypatia mcp` (the message quotes its `unrecognized subcommand`), lacks a tool the plugin calls, or answered the handshake with an error. Everything keeps working over the CLI; upgrade hypatia and reload the profile to use MCP |
394
460
  | Every call fails with `hypatia mcp exited …` or `timed out` | The binary starts but its MCP server does not come up — a wrapper script in `binaries` that changes clap's exit code or wording is not recognised as an old binary. Set `transport: cli` |
395
- | Every write fails right after changing `shelf` | The shelf is not registered or not connected — startup logs `shelf "<name>" is not registered`. Connect it (`hypatia connect <dir> --name <name>`) and reload the profile, or pick another |
461
+ | Every write fails right after changing `shelf` | The shelf is not registered or not connected — startup logs `shelf "<name>" is not registered`. Connect it (`hypatia connect <dir> --name <name>`) and save the setting again (or reload the profile), or pick another |
396
462
  | The agent searches `default` while the plugin writes elsewhere | A disk copy of `hypatia-memory` (see below) replaced the bundled skill, or recall is disabled, so nothing told the agent which shelf to use |
397
463
  | Duplicate `msg-*` after weird manual edits | Delete the entry in hypatia and lower `lastLoggedSeq` for that session in the state domain — backfill recreates it once |
398
- | The agent's own `hypatia` write still asks for approval | `autoApprove: false` (needs a profile reload to change), the command pipes/redirects/chains outside quotes, or its first word is not one of `binaries` — only plain calls are answered, by design. Reads never reach approval at all, so nothing to fix there |
464
+ | The agent's own `hypatia` write still asks for approval | `autoApprove: false`, the command pipes/redirects/chains outside quotes, or its first word is not one of `binaries` — only plain calls are answered, by design. Reads never reach approval at all, so nothing to fix there |
399
465
  | The agent got a memory protocol that tells it to log messages by hand | Another plugin registered `hypatia-memory` first (`dsh-hypatia` still in the profile), or a `hypatia-memory` exists on disk — typically `~/.agents/skills/hypatia-memory`, written by `hypatia skill install --agent codex`. Agent presets load disk skills in a layer nearer than plugins, so it wins in sessions even though the skill center may list this plugin. Remove the copy the startup warning names |
400
466
  | Can't tell which model actually ran | By design the choice rotates: one process-local cursor is shared by span summaries, adjudication and cascade, so consecutive calls alternate through `consolidation.models`. Each attempt lands in `~/.dsh/storages/hypatia_auto_memory_diag.json` (`model_calls`) with purpose, provider/model, outcome and duration. The usage ledger cannot answer this — it folds agent turns (`assistant/message`) and never sees a plugin's direct `llm.stream`. A `pending` row means the call is still in flight, or the process died mid-call: the row is written before the call and settled after it |
401
467
  | Startup warnings never appear in the terminal | `dsh web` mounts no log exporter, so plugin log lines of any level go nowhere; the console exporter's default threshold would also drop warnings (warn is level 2, above info's 1). Mount a logger such as `dsh-logbook` or `dsh-boot-doctor` temporarily to read them |
@@ -467,9 +533,9 @@ Deliberate, and worth knowing before you rely on them:
467
533
  the triples it actually reviews reach that limit.
468
534
 
469
535
  Its `evals/evals.json` gains cases 9 and 10 for these two patches.
470
- - **`enabled: false` at the top level is read at plugin startup**; toggling it
471
- live requires a profile reload, while the per-feature switches apply
472
- immediately.
536
+ - **`enabled: false` at the top level is read when the collector starts**;
537
+ saving a change restarts the collector, while the per-feature switches apply
538
+ in place.
473
539
 
474
540
  ## Directory structure
475
541
 
@@ -482,8 +548,8 @@ dsh-hypatia-auto-memory/
482
548
  ├── tsdown.config.ts # browser CJS factory build
483
549
  ├── src/
484
550
  │ ├── index.js # fiber composition (collect + optional children)
485
- │ ├── config.js # settings namespace, defaults, live updates
486
- │ ├── shelf.js # per-shelf table views, `hypatia list`, shelf inventory
551
+ │ ├── config.js # cordis Config schema (volatile), defaults, live updates
552
+ │ ├── shelf.js # per-shelf table views, `hypatia list` parsing
487
553
  │ ├── state.js # storageDomain spec + progress helpers
488
554
  │ ├── collector.js # session/event filtering, ledger, backfill
489
555
  │ ├── content-policy.js # redaction, dates, caps, slugs (pure)
@@ -495,13 +561,17 @@ dsh-hypatia-auto-memory/
495
561
  │ ├── consolidator.js # thresholds, prompt, llm.stream, validation
496
562
  │ ├── cascade.js # log₁₆(n) hierarchical summary archive
497
563
  │ ├── model-log.js # bounded record of which model each attempt ran on
564
+ │ ├── memory-status.js # per-session fold of the two state tables (pure)
565
+ │ ├── memory-api.js # Memory tab + shelf listing: read-only route family
498
566
  │ ├── recall.js # rules/taboos preload at session start
499
567
  │ ├── auto-approve.js # approves the agent's own plain bash hypatia calls
500
568
  │ ├── skills.js # bundled skill registration (never shadows another provider)
501
569
  │ ├── status.js # counters + structured logging
502
- │ └── client/ # browser settings card
570
+ │ └── client/ # settings card + Memory tab
503
571
  │ ├── index.tsx # client plugin entry + slot registration
504
572
  │ ├── SettingsCard.tsx
573
+ │ ├── MemoryView.tsx # the Memory tab body
574
+ │ ├── memory-client.ts # its fetch + merge rules (pure)
505
575
  │ ├── shelves.ts # shelf dropdown choices from the inventory namespace
506
576
  │ └── slot-contract.ts
507
577
  ├── lib/
package/README.zh-CN.md CHANGED
@@ -38,9 +38,10 @@ dsh plugin --profile web add dsh-hypatia-auto-memory
38
38
 
39
39
  对其它 profile(desktop、dsh-tui)重复以上步骤。安装后重启 profile。
40
40
 
41
- 浏览器设置卡片预构建在 `lib/client.js` 中,随包发布。如果你改了 `src/client/*`,
42
- 在本目录运行 `npm install && npm run build` 重新生成它;当 DSH checkout 里
43
- `pnpm run dev:web` 处于运行状态时,client-plugin 的改动无需整页刷新即可热更新。
41
+ 浏览器半——设置卡片与会话里的记忆标签页——预构建在 `lib/client.js` 中,随包发布。如果
42
+ 你改了 `src/client/*`,在本目录运行 `npm install && npm run build` 重新生成它;当 DSH
43
+ checkout 里 `pnpm run dev:web` 处于运行状态时,client-plugin 的改动无需整页刷新即可
44
+ 热更新。
44
45
 
45
46
  **替换 `dsh-hypatia`:** 把它从 profile 移除并重启。
46
47
 
@@ -199,10 +200,46 @@ agent/session-start ──▶ rules/taboos inject()
199
200
  登记上去但在 Agent 会话里仍胜出——预设会在更近的一层加载磁盘技能——所以会连同目录
200
201
  一起报告。
201
202
 
203
+ - **会话里的「记忆」标签页**是上述一切的读取面。它和 **对话**、**轨迹** 并排(一个
204
+ `conversation.view` 注册项,id 为 `memory`,排序在轨迹之后),对当前会话显示三块
205
+ 内容。**状态**:记录水位、整合水位、待整合 token、`session-<id>` 节点是否存在,以及
206
+ 用尽重试次数的任务和拦住它的那条错误。**跨度摘要**:整合产出的
207
+ `sum-<session>-<from>-<to>` 条目及其归档层级。**工作单元**:由这些摘要派生的 `wu-*`
208
+ 条目——它们通过 `derivedFrom` 边找到,因为名字是内容寻址的,不携带会话信息。正文用
209
+ 外壳自带的 `MarkdownText` 渲染,所以摘要读起来和 GUI 里其它地方的助手 Markdown
210
+ 一致。
211
+
212
+ 原始 `msg-*` 条目**故意不显示**:它是对话原文,读的人刚刚写过,而正确的读者是
213
+ Agent。
214
+
215
+ 数据经由本插件注册在 DSH web 服务器上的、会话作用域的只读 HTTP 路由
216
+ `/api/dsh-hypatia-auto-memory/session` 到达浏览器:状态每次轮询都从内存中的状态表
217
+ 现算,shelf 内容只在可能变化时才读(打开时、切换会话时、手动刷新时,以及整合推进了
218
+ 该会话水位时),其间缓存 15 秒。标签页在屏幕上时每 5 秒轮询一次,响应是有界的:
219
+ 最新的 40 条摘要与 40 条工作单元,每条正文 4000 字符、整份响应 120000 字符。
220
+ JSE 没有 `ORDER BY`,所以「最新」只能在读完所有候选之后判定;扫描触到上限时计数
221
+ 变成下界,标签页会把该次响应标为已截断,而不是让短列表无从解释。路上否掉了两条
222
+ 通道。**session projection** 形态最
223
+ 合适,但驱动它的事件在本仓库之外写不出来:`Session.append` 没有任何途径设置信封的
224
+ `ignorable` 标记,而持久化读取路径会拒绝一个带着未知事件类型、又没有该标记的会话
225
+ 日志——所以自定义事件会破坏会话重载。**设置命名空间**即便在还存在时也是根作用域的:
226
+ 宿主无法知道浏览器正在看哪个会话,只能把每个会话的数据都发出去。(dsh 0.1.7 已整体移除
227
+ 宿主侧的设置注册 API,下面的路由族现在是唯一通道。)
228
+
229
+ 这条路由不属于组合应用自己那批已鉴权的路由——插件注册的路由从来都不是——所以它像
230
+ `dsh-hypatia-ui` 那样自鉴权:socket 必须是回环,`Host` 必须是回环名(`localhost`、
231
+ `127.x`、`[::1]`),请求必须同源。本页面里的浏览器标签三条都满足;另一来源的页面
232
+ 即便同机也会在最后一条上失败,DNS 重绑定的页面会在 `Host` 检查上失败。
233
+ `~/.dsh/storages/hypatia_auto_memory.json` 和 `hypatia_auto_memory_diag.json` 仍是
234
+ 持久记录;标签页是同一批事实的视图,不是它们的替代品。
235
+
202
236
  ## 配置
203
237
 
204
- 设置命名空间 `hypatia-auto-memory`(编辑 `settings.yaml` 或 Web 设置;所有字段可选,
205
- 展示的是默认值):
238
+ 插件的 cordis `Config` 就是它的设置页(dsh ≥ 0.1.7 把插件的 Config schema 投影到
239
+ 「设置 → 插件」;下面每个字段保存即生效,无需重载 profile)。配置以插件条目 `config:`
240
+ 的形式存进 profile patch;0.1.7 之前的 `settings.yaml` 段落会在首次启动时被一次性导入。
241
+ 所有字段可选,展示的是默认值。空白的巩固路由在保存时就被拒绝;重复的路由在运行时被丢弃
242
+ 并记一条警告:
206
243
 
207
244
  ```yaml
208
245
  hypatia-auto-memory:
@@ -257,17 +294,19 @@ bundle 行上的 cordis config 块只承载技能打包:
257
294
  skillsDir: /abs/path # 覆盖打包的 skills/ 目录
258
295
  ```
259
296
 
260
- `enabled` 和 `autoApprove` 在插件启动时读取,改动二者需要重载 profile;其它开关
261
- 立即生效。
297
+ `enabled`、`shelf` 和 `autoApprove` 在采集器启动时读取,所以保存其中任一项的改动都会
298
+ 重启采集器(排空队列、重新打开状态);其它开关原地生效。0.1.7 之前的 `settings.yaml`
299
+ 也是靠这次重启生效的:dsh 在所有插件启动之后才导入它,而采集器本身会等到那一刻才启动,
300
+ 所以基本不会先按默认值跑起来。
262
301
 
263
302
  ### 选择 shelf
264
303
 
265
304
  `shelf` 指定所有条目写入、所有查找读取的 hypatia shelf——记录、巩固、级联、
266
305
  rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所报告内容的下拉框
267
- (约每分钟刷新一次,每次设置变更后也刷新),并标注已注册但未连接的 shelf。
306
+ ——标签页打开时按需读取;成功的结果在宿主侧缓存 60 秒,失败的结果从不缓存——并标注
307
+ 已注册但未连接的 shelf。
268
308
 
269
- - **像 `enabled` 一样,profile 重载后才生效。** 运行中的队列会继续写到它开始的地方;
270
- 设置变更时日志会说明。
309
+ - **像 `enabled` 一样,保存即生效**:采集器会在新 shelf 上重启,日志会说明。
271
310
  - **进度按 shelf 保存。** 水位和排队的任务按 shelf 存储,所以新选的 shelf 会让每个
272
311
  会话从零开始,而切回去会恰好从该 shelf 上次停下的地方继续——不会把已有的内容重新
273
312
  记录或重新巩固进某个 shelf。代价落在新 shelf 上:每个活会话从它的开头开始记录——
@@ -281,14 +320,20 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
281
320
  重试,直到它被连接(`hypatia connect <dir> --name <name>`)。走 MCP 时,该失败会
282
321
  重启 server,所以 `connect` 之后的重试能到达那个 shelf。
283
322
 
284
- 列表以第二个只读设置命名空间 `hypatia-auto-memory-shelves` 到达浏览器,宿主在列表
285
- 变化时重新注册它。没有卡片认领它,所以它不渲染在任何地方;也从不写入它。
323
+ 列表通过与 Memory 标签页相同的路由族到达浏览器——`GET /api/dsh-hypatia-auto-memory/shelves`,
324
+ 在启动时的对账与回填之前就挂载(`enabled: false` 时也挂载),所以 shelf 坏掉、需要换一个
325
+ 时列表就在那里。(0.1.7 之前它走一个只读设置命名空间,该宿主侧 API 已被移除;
326
+ shelf 清单本来就不是配置,而设置域现在只投影插件的 Config 表单。)
286
327
 
287
328
  ## 运维清单
288
329
 
289
330
  1. 一个 turn 结束后,条目在几秒内出现:
290
- `hypatia knowledge-get msg-<sessionId>-<n>`(`n` 从 0 开始数消息)
291
- 2. 水位在 state domain 里;失败的任务连同最后一次错误留在 `tasks` 表里供检查。
331
+ `hypatia knowledge-get msg-<sessionId>-<n>`(`n` 从 0 开始数消息)。
332
+ 会话里的**记忆**标签页不用敲 CLI 就能回答同一个问题,并且能给出 CLI 看不到的东西:
333
+ 水位本身。
334
+ 2. 水位在 state domain 里;失败的任务连同最后一次错误留在 `tasks` 表里供检查。这两者
335
+ 就是记忆标签页读的内容;换过 shelf 后要刷新它,因为它读的快照对应本次运行正在写入
336
+ 的那个 shelf。
292
337
  3. 写入中途重启是安全的:已存储的消息被跳过(get-before-create);未覆盖的范围从
293
338
  水位重新排队。
294
339
  4. 检查路由告警:没选 `models` 的巩固只记录一次告警,其余时间静默。
@@ -311,8 +356,9 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
311
356
 
312
357
  | 症状 | 原因 / 处理 |
313
358
  |---|---|
314
- | 聊完没有条目 | `enabled: false`、缺少 `hypatia` 二进制、或 collect fiber 处于 PENDING(需要基础组合里的 `sessions`、`storageDomain`、`subprocess`、`settings`)——查 profile 日志 |
359
+ | 聊完没有条目 | `enabled: false`、缺少 `hypatia` 二进制、或 collect fiber 处于 PENDING(需要基础组合里的 `sessions`、`storageDomain`、`subprocess`)——查 profile 日志 |
315
360
  | 重启后一整轮什么都没记录 | storage domain 因为某条已存记录不符合 schema 而拒绝打开。storage service 在读时校验、不在写时校验,所以一次坏写入只会在下次启动时暴露——而一条坏记录会拖垮整个 domain。在 profile 日志里找 `startup failed` / `does not match its schema`。缺 `error` 的任务行现在已默认化;其它坏行则停 DSH、从 `~/.dsh/storages/hypatia_auto_memory.json` 删除它再重启 |
361
+ | 「记忆」标签页始终读不到数据,或显示读取失败 | 页面不是通过**回环来源**访问的。该路由自鉴权——回环 socket、回环 `Host`、同源——所以从另一台设备读 DSH(LAN 地址,或绑定 `0.0.0.0`)会被按设计拒绝,隧道送来公网 `Host` 也一样。响应两半一起被拒,所以标签页报的是读取失败而不是部分数据。请用 `127.0.0.1` 或 `localhost` 打开 GUI |
316
362
  | 条目只在 turn 结束后才出现 | 设计如此:span 在 `turn/end` 切分,或当 turn 跑得比 `flushWindowMs` 久时在第一个 `step/end` 切分,所以条目不会缺它自己的工具结果 |
317
363
  | 有记录,没摘要 | `consolidation.models` 为空或无效——首次触发时告警一次 |
318
364
  | 有摘要但没有 `sum2-*` | 该项目里未归档的 tier-1 摘要还不足 `cascade.batchSize` 条 |
@@ -323,10 +369,10 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
323
369
  | 水位说已记录,但 shelf 里没条目 | shelf 在记录后被重置或切换。启动时处理:`housekeeping.reconcileOnStartup` 重置任何 session 已无 `msg-*` 的行,该会话下次活跃时从头重新记录——并重新巩固。shelf 查询失败则不动该行。一个所有消息都被故意删除的会话无法区分,会被重新记录;如果这要紧,关掉这个开关 |
324
370
  | 警告 `hypatia mcp unavailable: …; using the hypatia CLI until the profile reloads` | 二进制早于 `hypatia mcp`(消息会引用它的 `unrecognized subcommand`)、缺本插件调用的某工具、或握手返回错误。一切继续走 CLI;升级 hypatia 并重载 profile 以使用 MCP |
325
371
  | 每次调用都失败 `hypatia mcp exited …` 或 `timed out` | 二进制能启动但它的 MCP server 起不来——`binaries` 里一个改了 clap 退出码或措辞的包装脚本不会被识别为旧二进制。设 `transport: cli` |
326
- | 改了 `shelf` 之后每次写入都失败 | shelf 没注册或没连接——启动时记录 `shelf "<name>" is not registered`。连接它(`hypatia connect <dir> --name <name>`)并重载 profile,或换一个 |
372
+ | 改了 `shelf` 之后每次写入都失败 | shelf 没注册或没连接——启动时记录 `shelf "<name>" is not registered`。连接它(`hypatia connect <dir> --name <name>`)后重新保存设置(或重载 profile),或换一个 |
327
373
  | Agent 在 `default` 里搜,而插件写在别处 | 一份磁盘上的 `hypatia-memory`(见下)替换了随包技能,或 recall 被禁用,所以没有东西告诉 Agent 用哪个 shelf |
328
374
  | 手动乱改后出现重复 `msg-*` | 在 hypatia 里删掉该条目,并在 state domain 里把该会话的 `lastLoggedSeq` 调低——回填会重新创建它一次 |
329
- | Agent 自己的 `hypatia` 写入仍要审批 | `autoApprove: false`(改动需重载 profile)、命令在引号外有管道/重定向/串联、或首词不是 `binaries` 之一——按设计只回答纯调用。读取根本不会走到审批,所以那里没有要修的 |
375
+ | Agent 自己的 `hypatia` 写入仍要审批 | `autoApprove: false`、命令在引号外有管道/重定向/串联、或首词不是 `binaries` 之一——按设计只回答纯调用。读取根本不会走到审批,所以那里没有要修的 |
330
376
  | Agent 拿到了一份教它手动记录消息的记忆协议 | 另一个插件先注册了 `hypatia-memory`(profile 里还有 `dsh-hypatia`),或磁盘上有 `hypatia-memory`——通常是 `hypatia skill install --agent codex` 写的 `~/.agents/skills/hypatia-memory`。Agent 预设会在比插件更近的一层加载磁盘技能,所以它在会话里胜出,尽管技能中心可能列出本插件。删掉启动告警指出的那份拷贝 |
331
377
  | 不知道实际用了哪个模型 | 设计上就是轮流的:span 摘要、裁决、cascade 共用一个进程内游标,所以相邻调用在 `consolidation.models` 里交替。每次尝试都落在 `~/.dsh/storages/hypatia_auto_memory_diag.json`(`model_calls`),带 purpose、provider/model、outcome 和耗时。usage ledger 答不了这个问题——它只折算 Agent 的 turn(`assistant/message`),看不见插件直连的 `llm.stream`。`pending` 行表示调用仍在进行中,或进程在调用中途死了:记录在调用前先落盘、调用结束后回填 |
332
378
  | 启动告警从不出现在终端 | `dsh web` 不挂日志导出器,所以插件任何级别的日志行都没地方去;控制台导出器的默认阈值也会丢掉警告(warn 是 2 级,高于 info 的 1 级)。临时挂一个 logger,如 `dsh-logbook` 或 `dsh-boot-doctor` 来读它们 |
@@ -377,8 +423,8 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
377
423
  行上限里,它们会让技能在真正审阅的三元组远未达到上限前就停在截断检查上。
378
424
 
379
425
  它的 `evals/evals.json` 为这两处补丁新增 case 9 和 10。
380
- - **顶层的 `enabled: false` 在插件启动时读取**;实时切换需要重载 profile,而各特性
381
- 开关立即生效。
426
+ - **顶层的 `enabled: false` 在采集器启动时读取**;保存改动会重启采集器,而各特性
427
+ 开关原地生效。
382
428
 
383
429
  ## 目录结构
384
430
 
@@ -391,8 +437,8 @@ dsh-hypatia-auto-memory/
391
437
  ├── tsdown.config.ts # 浏览器 CJS 工厂构建
392
438
  ├── src/
393
439
  │ ├── index.js # fiber 组合(collect + 可选子模块)
394
- │ ├── config.js # 设置命名空间、默认值、热更新
395
- │ ├── shelf.js # 每 shelf 表视图、`hypatia list`、shelf 清单
440
+ │ ├── config.js # cordis Config schema(volatile)、默认值、热更新
441
+ │ ├── shelf.js # 每 shelf 表视图、`hypatia list` 解析
396
442
  │ ├── state.js # storageDomain 规格 + 进度助手
397
443
  │ ├── collector.js # session/event 过滤、台账、回填
398
444
  │ ├── content-policy.js # 脱敏、日期、上限、slug(纯函数)
@@ -404,14 +450,18 @@ dsh-hypatia-auto-memory/
404
450
  │ ├── consolidator.js # 阈值、prompt、llm.stream、校验
405
451
  │ ├── cascade.js # log₁₆(n) 分层摘要归档
406
452
  │ ├── model-log.js # 每次尝试实际用了哪个模型的有界记录
453
+ │ ├── memory-status.js # 两张状态表按会话折叠(纯函数)
454
+ │ ├── memory-api.js # 记忆标签页与 shelf 清单:只读路由族 + JSE 读取
407
455
  │ ├── recall.js # 会话启动时的 rules/taboos 预载
408
456
  │ ├── auto-approve.js # 批准 Agent 自己的纯 bash hypatia 调用
409
457
  │ ├── skills.js # 随包技能注册(从不遮蔽其它提供者)
410
458
  │ ├── status.js # 计数器 + 结构化日志
411
- │ └── client/ # 浏览器设置卡片
459
+ │ └── client/ # 设置卡片 + 记忆标签页
412
460
  │ ├── index.tsx # client 插件入口 + slot 注册
413
461
  │ ├── SettingsCard.tsx
414
- │ ├── shelves.ts # 来自清单命名空间的 shelf 下拉选项
462
+ │ ├── MemoryView.tsx # 记忆标签页主体
463
+ │ ├── memory-client.ts # 它的请求与合并规则(纯函数)
464
+ │ ├── shelves.ts # shelf 下拉选项(清单来自 /shelves 路由)
415
465
  │ └── slot-contract.ts
416
466
  ├── lib/
417
467
  │ └── client.js # 构建好的浏览器工厂(提交此文件)
package/cordis.patch.yml CHANGED
@@ -3,9 +3,10 @@
3
3
  # dsh plugin --profile <name> add dsh-hypatia-auto-memory (registry)
4
4
  # dsh plugin --profile <name> add link:/path/to/dsh-hypatia-auto-memory
5
5
  #
6
- # Behavior is configured through the `hypatia-auto-memory` settings namespace
7
- # (settings.yaml / Web settings), NOT through this config block. The only
8
- # cordis-level knobs are the bundled skill:
6
+ # Behavior is configured through this entry's `config:` (edited live in
7
+ # Settings → Plugins; dsh ≥ 0.1.7 projects the plugin's Config schema there).
8
+ # The cordis-level knobs below are the non-volatile ones — changing them
9
+ # remounts the plugin:
9
10
  #
10
11
  # - id: hypatia-auto-memory
11
12
  # config: