dsh-hypatia-auto-memory 0.5.0 → 1.1.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 +110 -38
- package/README.zh-CN.md +82 -31
- package/cordis.patch.yml +6 -3
- package/lib/client.js +240 -116
- package/lib/client.js.map +1 -1
- package/package.json +22 -15
- package/src/cascade.js +9 -2
- package/src/client/SettingsCard.module.css +5 -37
- package/src/client/SettingsCard.tsx +408 -413
- package/src/client/index.tsx +59 -50
- package/src/client/locales.ts +6 -6
- package/src/client/memory-client.ts +48 -0
- package/src/client/plugin-card-seat.ts +159 -0
- package/src/client/shelves.ts +6 -38
- package/src/client/slot-contract.ts +13 -5
- package/src/collector.js +104 -5
- package/src/config.js +155 -78
- package/src/consolidator.js +51 -3
- package/src/housekeeping.js +111 -33
- package/src/index.js +142 -74
- package/src/memory-api.js +54 -3
- package/src/persisted-session.js +35 -6
- package/src/queue.js +38 -5
- package/src/shelf.js +5 -99
package/README.md
CHANGED
|
@@ -33,6 +33,17 @@ additions are used when the binary has them and skipped when it does not:
|
|
|
33
33
|
The first call logs which the binary offers
|
|
34
34
|
(`hypatia over mcp: no-embed yes, similar filters yes`).
|
|
35
35
|
|
|
36
|
+
The plugin also needs **dsh ≥ 0.1.7**, and dsh enforces that itself rather than
|
|
37
|
+
this README: the package declares
|
|
38
|
+
`peerDependencies["@deepseek-ai/dsh"]: ">=0.1.7-rc.1"` (the same range also
|
|
39
|
+
sits under `dsh.engines.dsh` for display), and dsh checks every
|
|
40
|
+
`@deepseek-ai/dsh*` peer against the running version when a bundle is installed
|
|
41
|
+
and again at startup — an older runtime refuses the install instead of loading a
|
|
42
|
+
plugin built for API it does not have, and `dsh plugin allow-version` grants an
|
|
43
|
+
exact-version exemption when the risk is accepted deliberately. The peer is
|
|
44
|
+
marked optional in `peerDependenciesMeta` so that neither npm nor a profile
|
|
45
|
+
installation pulls the runtime in as a dependency: the launcher supplies it.
|
|
46
|
+
|
|
36
47
|
```bash
|
|
37
48
|
# from a local checkout (development)
|
|
38
49
|
dsh plugin --profile web add link:/path/to/dsh-hypatia-auto-memory
|
|
@@ -85,8 +96,11 @@ session/event (emit)
|
|
|
85
96
|
│ └─ cascade ──▶ $not-summaried ──▶ sum<N>-* (summary N)
|
|
86
97
|
└─ session/title | compaction/summary ──▶ session-node ──▶ session-<sid> + belongTo
|
|
87
98
|
session/disposed ──▶ final flush + consolidation, thresholds waived (a session
|
|
88
|
-
closed while DSH keeps running
|
|
89
|
-
|
|
99
|
+
closed while DSH keeps running)
|
|
100
|
+
collect fiber dispose ──▶ the same flush for every session still open, persisted
|
|
101
|
+
before the queue stops; the model call finishes at the next start
|
|
102
|
+
(a restart, a config-change restart, or a crash: the startup
|
|
103
|
+
consolidation backfill)
|
|
90
104
|
agent/session-start ──▶ rules/taboos inject()
|
|
91
105
|
```
|
|
92
106
|
|
|
@@ -178,6 +192,19 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
178
192
|
and advances the watermark only that far, so the remainder is not silently
|
|
179
193
|
marked done.
|
|
180
194
|
|
|
195
|
+
The extraction call asks for **no reasoning** on a route that advertises it.
|
|
196
|
+
Extraction is a mechanical transform into a fixed JSON shape, and on a
|
|
197
|
+
thinking route the reasoning is charged to the SAME `maxOutputTokens` as the
|
|
198
|
+
answer: measured live, a `deepseek-flash` route resolving to `high` spent
|
|
199
|
+
~800–1400 of a 2000-token cap thinking and hit `max-tokens` with the JSON half
|
|
200
|
+
written (9.3 s, against 25 s for a completed run of the same kind on that
|
|
201
|
+
route). Where a route cannot be asked — or a provider ignores the control —
|
|
202
|
+
the budget carries a fixed thinking allowance instead, and a truncation is
|
|
203
|
+
**retried** rather than declared permanent: the retry asks for one work unit
|
|
204
|
+
instead of `maxWorkUnitsPerRun`, because the units are what make the reply
|
|
205
|
+
long. Cascade is the exception: it keeps thinking (it is compressing sixteen
|
|
206
|
+
summaries, not filling a shape) and pays for it in the same allowance.
|
|
207
|
+
|
|
181
208
|
- **Cascade** archives sixteen unarchived tier-N summaries into one tier-(N+1)
|
|
182
209
|
entry, using hypatia's own `$not-summaried` (which anti-joins on
|
|
183
210
|
`statement.tail` and orders `created_at ASC`, giving FIFO batching for free).
|
|
@@ -200,22 +227,39 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
200
227
|
re-log and a re-consolidation, so both passes act only on positive evidence:
|
|
201
228
|
a failed shelf query or an empty session listing changes nothing.
|
|
202
229
|
|
|
203
|
-
A third pass consolidates what a
|
|
204
|
-
|
|
205
|
-
`session/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
230
|
+
A third pass consolidates what a shutdown could only persist, and what a crash
|
|
231
|
+
or `kill -9` left behind. Nothing a closing session owes survives on its own:
|
|
232
|
+
`session/disposed` observers are fire-and-forget in DSH, the collect fiber is
|
|
233
|
+
torn down before the session store releases its sessions, and the queue dies
|
|
234
|
+
with the fiber — so an `enqueue` there was a silent no-op and a whole run's
|
|
235
|
+
tails were dropped. Two things fix that. The collect fiber's disposer now
|
|
236
|
+
**persists** both ranges for every session still open (registering the sweep
|
|
237
|
+
after the queue's own disposer, because cordis disposes a fiber's effects in
|
|
238
|
+
reverse order, so it runs while the queue can still write), and this pass
|
|
239
|
+
finishes the work at the next start. Only the final log write is waited for at
|
|
240
|
+
shutdown: a hypatia write is milliseconds, while the model call it may trigger
|
|
241
|
+
is measured in tens of seconds and could never fit inside DSH's 5 s shutdown
|
|
242
|
+
budget.
|
|
243
|
+
|
|
244
|
+
This pass deliberately ignores the `consolidation.minNewTokens` floor that
|
|
245
|
+
paces the live trigger. The floor is right while a conversation is running —
|
|
246
|
+
there is always a later turn — but at boot there is no later turn, and a tail
|
|
247
|
+
skipped here is skipped for good: measured on a live shelf, fifteen sessions
|
|
248
|
+
carried 1–1950 pending tokens, not one of them was ever consolidated, and
|
|
249
|
+
eight of nine sessions had messages with `lastConsolidatedSeq: 0`. Cost is
|
|
250
|
+
bounded by `housekeeping.consolidationBackfillPerScope` instead — at most that
|
|
251
|
+
many sessions **per project scope** per start, the largest tails first, so a
|
|
252
|
+
backlog drains over several starts rather than in one unbounded burst of model
|
|
253
|
+
calls. A range whose consolidate record is `failed` is taken first whatever its
|
|
254
|
+
size: `resumeKind` never re-schedules a failed row, and re-enqueueing it is the
|
|
255
|
+
only path back.
|
|
212
256
|
|
|
213
257
|
Those tasks then read their events from **storage**, not only from the live
|
|
214
258
|
store. `session/created` — the signal that wakes a deferred task — fires only
|
|
215
259
|
where an agent actually runs, so a finished session may never emit it again,
|
|
216
260
|
and a subagent session (which the sidebar does not even list) almost certainly
|
|
217
261
|
will not: one sat `deferred` across a restart and stayed deferred while its
|
|
218
|
-
transcript was open on screen. `sessionPersistence.
|
|
262
|
+
transcript was open on screen. `sessionPersistence.open(id, 'read')` returns the same
|
|
219
263
|
log without publishing anything, and the executors use only `snapshotEvents`,
|
|
220
264
|
`inheritedEventCount`, `seq` and `header`, so a tail nothing will reopen still
|
|
221
265
|
becomes knowledge. A composition without `sessionPersistence` behaves as
|
|
@@ -276,16 +320,21 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
276
320
|
from the in-memory state tables on every poll, the shelf content only when it
|
|
277
321
|
can have changed (on open, on a session switch, on a manual refresh, and when
|
|
278
322
|
consolidation advances the session's watermark), cached for 15 s in between.
|
|
323
|
+
The tab polls every five seconds while it is on screen, and a response is
|
|
324
|
+
bounded: the newest 40 summaries and 40 work units, 4000 characters per body
|
|
325
|
+
and 120 000 per response. JSE has no `ORDER BY`, so "newest" is decided only
|
|
326
|
+
after reading every candidate; when a scan reaches its cap the counts become
|
|
327
|
+
lower bounds, and the tab reports that response as truncated rather than
|
|
328
|
+
leaving a short list unexplained.
|
|
279
329
|
Two channels were rejected on the way here. A **session projection** would be
|
|
280
330
|
the best shape, but the event that would drive it cannot be written from
|
|
281
331
|
outside this repository: `Session.append` offers no way to set the envelope's
|
|
282
332
|
`ignorable` marker, and the persistence read path refuses a session log
|
|
283
333
|
carrying an unknown event type without it — so a custom event would break
|
|
284
|
-
session reload. A **settings namespace**
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
namespace and has no way to update a registered `base`.
|
|
334
|
+
session reload. A **settings namespace** was root-scoped even when it existed:
|
|
335
|
+
the Host cannot know which session the browser is showing, so it would have
|
|
336
|
+
to ship every session's data. (dsh 0.1.7 removed the Host-side settings
|
|
337
|
+
registration API entirely; the route family below is now the only channel.)
|
|
289
338
|
|
|
290
339
|
The route is not one of the composed app's authenticated routes — a route a
|
|
291
340
|
plugin registers never is — so it authenticates itself the way
|
|
@@ -299,8 +348,16 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
299
348
|
|
|
300
349
|
## Configuration
|
|
301
350
|
|
|
302
|
-
|
|
303
|
-
|
|
351
|
+
The plugin's cordis `Config` is its configuration form. dsh ≥ 0.1.7 projects a
|
|
352
|
+
plugin's Config schema onto the sidebar's **Plugins** page, and the card renders
|
|
353
|
+
on this bundle's own page there; a deployment whose profile renders no Plugins
|
|
354
|
+
page gets the same card as a tab under **Settings → Built-in plugins**. The card
|
|
355
|
+
takes whichever seat exists and moves between them without ever appearing twice
|
|
356
|
+
(`src/client/plugin-card-seat.ts`). Every field below applies on
|
|
357
|
+
save, without a profile reload. Stored as the plugin entry's `config:` in the
|
|
358
|
+
profile patch; a pre-0.1.7 `settings.yaml` section is imported once on first
|
|
359
|
+
launch. All fields optional, defaults shown. A blank consolidation route is
|
|
360
|
+
refused on save; a duplicate one is dropped at runtime with a warning:
|
|
304
361
|
|
|
305
362
|
```yaml
|
|
306
363
|
hypatia-auto-memory:
|
|
@@ -344,6 +401,7 @@ hypatia-auto-memory:
|
|
|
344
401
|
reconcileOnStartup: true # reset rows whose session has no msg-* left in the shelf
|
|
345
402
|
pruneVanishedSessions: true # drop rows and tasks of sessions DSH no longer has
|
|
346
403
|
backfillConsolidation: true # consolidate a logged tail the in-session trigger never reached
|
|
404
|
+
consolidationBackfillPerScope: 4 # sessions that pass may queue per scope, per start
|
|
347
405
|
```
|
|
348
406
|
|
|
349
407
|
The cordis config block on the bundle row only carries skill packaging:
|
|
@@ -355,19 +413,24 @@ The cordis config block on the bundle row only carries skill packaging:
|
|
|
355
413
|
skillsDir: /abs/path # override the packaged skills/ directory
|
|
356
414
|
```
|
|
357
415
|
|
|
358
|
-
`enabled` and `autoApprove` are read when the
|
|
359
|
-
|
|
416
|
+
`enabled`, `shelf` and `autoApprove` are read when the collector starts, so
|
|
417
|
+
saving a change to any of them restarts the collector (queue drained, state
|
|
418
|
+
re-opened); every other switch applies in place. The same restart is what
|
|
419
|
+
applies an imported pre-0.1.7 `settings.yaml`: dsh imports it only after every
|
|
420
|
+
plugin has started, and the collector itself waits for that point before it
|
|
421
|
+
starts, so it rarely runs on the defaults at all.
|
|
360
422
|
|
|
361
423
|
### Choosing the shelf
|
|
362
424
|
|
|
363
425
|
`shelf` names the hypatia shelf every entry is written to and every lookup
|
|
364
426
|
reads — logging, consolidation, the cascade, the rules/taboos preload, and the
|
|
365
427
|
startup housekeeping. The settings card offers it as a dropdown of what
|
|
366
|
-
`hypatia list` reports
|
|
367
|
-
|
|
428
|
+
`hypatia list` reports — read on demand when the tab opens; a successful
|
|
429
|
+
listing is cached for 60 s on the Host, a failed one never is — marking
|
|
430
|
+
shelves that are registered but not connected.
|
|
368
431
|
|
|
369
|
-
- **Takes effect
|
|
370
|
-
|
|
432
|
+
- **Takes effect on save**, like `enabled`: the collector restarts on the new
|
|
433
|
+
shelf, and the log says so.
|
|
371
434
|
- **Progress is kept per shelf.** Watermarks and queued tasks are stored per
|
|
372
435
|
shelf, so a newly chosen shelf starts every session from zero, and switching
|
|
373
436
|
back resumes exactly where that shelf left off — nothing is re-logged or
|
|
@@ -387,9 +450,12 @@ change), marking shelves that are registered but not connected.
|
|
|
387
450
|
(`hypatia connect <dir> --name <name>`). Over MCP, that failure restarts the
|
|
388
451
|
server, so the retry after a `connect` reaches the shelf.
|
|
389
452
|
|
|
390
|
-
The listing reaches the browser as
|
|
391
|
-
`hypatia-auto-memory
|
|
392
|
-
|
|
453
|
+
The listing reaches the browser over the same route family as the Memory tab —
|
|
454
|
+
`GET /api/dsh-hypatia-auto-memory/shelves`, mounted before the startup
|
|
455
|
+
reconcile and backfill (and even while `enabled: false`), so the list is there
|
|
456
|
+
when a broken shelf needs replacing. (It rode a read-only settings namespace until dsh 0.1.7 removed that
|
|
457
|
+
Host-side API; a shelf listing is not configuration, and the settings domain
|
|
458
|
+
now only projects plugin Config forms.)
|
|
393
459
|
|
|
394
460
|
## Operations checklist
|
|
395
461
|
|
|
@@ -428,22 +494,23 @@ changes. No card claims it, so it renders nowhere; nothing ever writes to it.
|
|
|
428
494
|
|
|
429
495
|
| Symptom | Cause / handling |
|
|
430
496
|
|---|---|
|
|
431
|
-
| No entries after chatting | `enabled: false`, missing `hypatia` binary, or the collect fiber PENDING (needs `sessions`, `storageDomain`, `subprocess
|
|
497
|
+
| 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 |
|
|
432
498
|
| 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 |
|
|
499
|
+
| 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` |
|
|
433
500
|
| 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 |
|
|
434
501
|
| Logging works, no summaries | `consolidation.models` is empty or invalid — one warning at first trigger |
|
|
435
502
|
| Summaries but no `sum2-*` | Fewer than `cascade.batchSize` unarchived tier-1 summaries in that project yet |
|
|
436
503
|
| Work units have no relationships | No embedding model on the shelf (`similar` fails), every candidate was beyond `dedupMaxDistance`, or the adjudication call itself produced no verdict — `model_calls` shows that as `incomplete` / `max-tokens`, which is what a thinking-enabled route does when it spends the whole output budget on reasoning before answering. The call now asks a route for `reasoningEffort: 'off'` once the adapter advertises it, and carries a 1024-token cap for routes that cannot honour it. Since hypatia #19 the local model is looked up in `~/.hypatia/models/<org>/<name>` (or the Hugging Face cache), no longer beside the shelf: a shelf that used to answer `similar` and now says `is not installed` needs `hypatia model install <model>`, or `hypatia model register <model> <dir>` pointing at the files it already has |
|
|
437
504
|
| `similar` still returns `msg-*` rows | They were written before this plugin opted the log layer out of embedding, or by a hypatia without `--no-embed`. Retract the knowledge vectors once with `embedding.skip_tags = ["message"]` in the shelf's `shelf.toml` followed by `hypatia backfill` (a binary older than the key refuses to open the shelf, so upgrade every binary sharing it first). Do not add `session` to that list: `skip_tags` matches any entry carrying the tag, and knowledge people write about sessions carries it too — one such entry lost its vector on the shelf this was tried on. The plugin's own `session-*` nodes are few and now opt out per write. Statements have no tags and no update command, so `belongTo` / `summary` edges written before keep their vectors |
|
|
438
|
-
| Task in `deferred` state | Neither the live store nor persistence could supply its session. Not an error: it spends no attempts and runs as soon as one of them can. Normally storage answers immediately — the state persists only when `sessionPersistence` is absent from the composition, or its read failed (look for `could not be read` in the log) |
|
|
505
|
+
| Task in `deferred` state | Neither the live store nor persistence could supply its session. Not an error: it spends no attempts and runs as soon as one of them can. Normally storage answers immediately — the state persists only when `sessionPersistence` is absent from the composition, or its read failed (look for `could not be read` in the log). A deferred consolidation is re-queued by the next startup's backfill, so it does not wait for its session to be opened again |
|
|
439
506
|
| Task in `failed` state | A hypatia or model error persisted after `maxAttempts`. Failed `log-message` records are pruned at the next startup, since the watermark re-derives their range; other kinds are kept for inspection — delete one to let the next trigger re-create it |
|
|
440
507
|
| 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 |
|
|
441
508
|
| 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 |
|
|
442
509
|
| 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` |
|
|
443
|
-
| 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 |
|
|
510
|
+
| 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 |
|
|
444
511
|
| 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 |
|
|
445
512
|
| 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 |
|
|
446
|
-
| The agent's own `hypatia` write still asks for approval | `autoApprove: false
|
|
513
|
+
| 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 |
|
|
447
514
|
| 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 |
|
|
448
515
|
| 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 |
|
|
449
516
|
| 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 |
|
|
@@ -515,9 +582,9 @@ Deliberate, and worth knowing before you rely on them:
|
|
|
515
582
|
the triples it actually reviews reach that limit.
|
|
516
583
|
|
|
517
584
|
Its `evals/evals.json` gains cases 9 and 10 for these two patches.
|
|
518
|
-
- **`enabled: false` at the top level is read
|
|
519
|
-
|
|
520
|
-
|
|
585
|
+
- **`enabled: false` at the top level is read when the collector starts**;
|
|
586
|
+
saving a change restarts the collector, while the per-feature switches apply
|
|
587
|
+
in place.
|
|
521
588
|
|
|
522
589
|
## Directory structure
|
|
523
590
|
|
|
@@ -530,8 +597,8 @@ dsh-hypatia-auto-memory/
|
|
|
530
597
|
├── tsdown.config.ts # browser CJS factory build
|
|
531
598
|
├── src/
|
|
532
599
|
│ ├── index.js # fiber composition (collect + optional children)
|
|
533
|
-
│ ├── config.js #
|
|
534
|
-
│ ├── shelf.js # per-shelf table views, `hypatia list
|
|
600
|
+
│ ├── config.js # cordis Config schema (volatile), defaults, live updates
|
|
601
|
+
│ ├── shelf.js # per-shelf table views, `hypatia list` parsing
|
|
535
602
|
│ ├── state.js # storageDomain spec + progress helpers
|
|
536
603
|
│ ├── collector.js # session/event filtering, ledger, backfill
|
|
537
604
|
│ ├── content-policy.js # redaction, dates, caps, slugs (pure)
|
|
@@ -543,6 +610,8 @@ dsh-hypatia-auto-memory/
|
|
|
543
610
|
│ ├── consolidator.js # thresholds, prompt, llm.stream, validation
|
|
544
611
|
│ ├── cascade.js # log₁₆(n) hierarchical summary archive
|
|
545
612
|
│ ├── model-log.js # bounded record of which model each attempt ran on
|
|
613
|
+
│ ├── memory-status.js # per-session fold of the two state tables (pure)
|
|
614
|
+
│ ├── memory-api.js # Memory tab + shelf listing: read-only route family
|
|
546
615
|
│ ├── recall.js # rules/taboos preload at session start
|
|
547
616
|
│ ├── auto-approve.js # approves the agent's own plain bash hypatia calls
|
|
548
617
|
│ ├── skills.js # bundled skill registration (never shadows another provider)
|
|
@@ -550,7 +619,10 @@ dsh-hypatia-auto-memory/
|
|
|
550
619
|
│ └── client/ # settings card + Memory tab
|
|
551
620
|
│ ├── index.tsx # client plugin entry + slot registration
|
|
552
621
|
│ ├── SettingsCard.tsx
|
|
553
|
-
│ ├──
|
|
622
|
+
│ ├── MemoryView.tsx # the Memory tab body
|
|
623
|
+
│ ├── memory-client.ts # its fetch + merge rules (pure)
|
|
624
|
+
│ ├── plugin-card-seat.ts # card seat: official Plugins page, else Settings tab
|
|
625
|
+
│ ├── shelves.ts # shelf dropdown choices (listing from the /shelves route)
|
|
554
626
|
│ └── slot-contract.ts
|
|
555
627
|
├── lib/
|
|
556
628
|
│ └── client.js # built browser factory (commit this)
|
package/README.zh-CN.md
CHANGED
|
@@ -28,6 +28,14 @@
|
|
|
28
28
|
检索。首次调用会记录二进制提供了哪些(`hypatia over mcp: no-embed yes, similar
|
|
29
29
|
filters yes`)。
|
|
30
30
|
|
|
31
|
+
本插件还需要 **dsh ≥ 0.1.7**,而且这个下限由 dsh 自己强制执行,不靠本 README:
|
|
32
|
+
包里声明了 `peerDependencies["@deepseek-ai/dsh"]: ">=0.1.7-rc.1"`(同样的范围也写在
|
|
33
|
+
`dsh.engines.dsh` 里,供展示),dsh 在安装 bundle 时、以及每次启动时,会把每个
|
|
34
|
+
`@deepseek-ai/dsh*` peer 与自身版本比对——运行时太旧会直接拒绝安装,而不是加载一个
|
|
35
|
+
面向它并不具备的 API 写成的插件;确实要接受风险时,用 `dsh plugin allow-version`
|
|
36
|
+
授予精确版本豁免。该 peer 在 `peerDependenciesMeta` 里标为 optional,这样 npm 和
|
|
37
|
+
profile 安装都不会把运行时本身拉成依赖:运行时由 launcher 提供。
|
|
38
|
+
|
|
31
39
|
```bash
|
|
32
40
|
# 从本地 checkout 安装(开发)
|
|
33
41
|
dsh plugin --profile web add link:/path/to/dsh-hypatia-auto-memory
|
|
@@ -75,7 +83,10 @@ session/event (emit)
|
|
|
75
83
|
│ └─ cascade ──▶ $not-summaried ──▶ sum<N>-* (summary N)
|
|
76
84
|
└─ session/title | compaction/summary ──▶ session-node ──▶ session-<sid> + belongTo
|
|
77
85
|
session/disposed ──▶ final flush + consolidation, thresholds waived (DSH 仍在运行时
|
|
78
|
-
|
|
86
|
+
关闭的会话)
|
|
87
|
+
collect fiber 拆除 ──▶ 对每个仍打开的会话做同样的收尾,并在队列停止前落盘;模型
|
|
88
|
+
调用留到下次启动完成(重启、配置变更重启,或崩溃:启动时的
|
|
89
|
+
consolidation backfill)
|
|
79
90
|
agent/session-start ──▶ rules/taboos inject()
|
|
80
91
|
```
|
|
81
92
|
|
|
@@ -143,6 +154,15 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
143
154
|
脱敏并绝对化日期,超预算的 span 只巩固其**最旧**的条目,水位也只推进到那里,剩下
|
|
144
155
|
的不会被打成已完成。
|
|
145
156
|
|
|
157
|
+
抽取调用在**声明支持的路由上关闭推理**。抽取是朝固定 JSON 形状的机械变换,而在开启
|
|
158
|
+
thinking 的路由上,推理与答案**共享同一个 `maxOutputTokens`**:实测中一条解析为
|
|
159
|
+
`high` 的 `deepseek-flash` 路由在 2000 的预算里花掉约 800–1400 思考,然后带着写到
|
|
160
|
+
一半的 JSON 撞上 `max-tokens`(9.3s,而同一条路由上同类成功调用要 25s)。路由无法
|
|
161
|
+
被要求关闭(或 provider 忽略该控制)时,预算改为携带固定的思考余量;而**截断不再
|
|
162
|
+
算永久失败**,而是重试——重试时只要 1 个工作单元而不是 `maxWorkUnitsPerRun`,因为
|
|
163
|
+
正是工作单元让回复变长。Cascade 是例外:它保留 thinking(它是在压缩十六条摘要,不是
|
|
164
|
+
在填一个形状),并用同一份余量来支付。
|
|
165
|
+
|
|
146
166
|
- **Cascade** 把十六个未归档的 tier-N 摘要归档为一条 tier-(N+1) 条目,用 hypatia 自带
|
|
147
167
|
的 `$not-summaried`(它对 `statement.tail` 做反连接并按 `created_at ASC` 排序,
|
|
148
168
|
免费得到 FIFO 分批)。这就是协议的 log₁₆(n) 归档:每一层只压缩上一层已经蒸馏过的
|
|
@@ -160,17 +180,28 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
160
180
|
DSH 已不存在的会话的行和任务。重置的代价是一次重新记录和一次重新巩固,所以两遍
|
|
161
181
|
都只在有正面证据时才行动:shelf 查询失败或空的会话列表都不做任何改动。
|
|
162
182
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
第三遍巩固那些关闭时只能落盘、以及崩溃或 `kill -9` 完全没留下的内容。关闭中的会话
|
|
184
|
+
所欠的东西本身活不下来:DSH 里 `session/disposed` 的观察者是 fire-and-forget,
|
|
185
|
+
collect fiber 先于 session store 释放会话就被拆除,而队列随 fiber 一起消失——所以在
|
|
186
|
+
那里 `enqueue` 是一次静默的空操作,一整轮的尾巴就这么丢了。两件事修好了它:collect
|
|
187
|
+
fiber 的 disposer 现在会为每个仍打开的会话**落盘**两段范围(把收尾 effect 注册在队列
|
|
188
|
+
自己的 disposer **之后**,因为 cordis 按注册逆序拆除 fiber 的 effects,于是它跑的时候
|
|
189
|
+
队列还能写),而这一遍在下一次启动把活干完。关闭时只等最后的日志写入:一次 hypatia
|
|
190
|
+
写入是毫秒级,而它可能触发的模型调用是几十秒,永远塞不进 DSH 5s 的关机预算。
|
|
191
|
+
|
|
192
|
+
这一遍**刻意不看**节流实时触发器的 `consolidation.minNewTokens` 下限。对话还在进行时
|
|
193
|
+
下限是对的——总还有下一轮——但启动时没有下一轮,在这里被跳过的尾部就是永久跳过:
|
|
194
|
+
实测一个 live shelf 上 15 个会话带着 1–1950 的 pending token,没有一个被巩固过,9 个
|
|
195
|
+
有消息的会话里 8 个 `lastConsolidatedSeq: 0`。成本改由
|
|
196
|
+
`housekeeping.consolidationBackfillPerScope` 封顶——每次启动**每个 project scope**
|
|
197
|
+
最多这么多个会话,尾巴最大的优先,于是积压分几次启动消化,而不是一次爆出无界的模型
|
|
198
|
+
调用。consolidate 记录处于 `failed` 的范围无论多小都优先:`resumeKind` 不会重排
|
|
199
|
+
failed 行,重新入队是它唯一的回头路。
|
|
169
200
|
|
|
170
201
|
这些任务随后从 **storage** 读取事件,而不只是从活 store。`session/created`——唤醒
|
|
171
202
|
被延迟任务的信号——只在 Agent 实际运行的地方触发,所以一个已结束的会话可能再也不
|
|
172
203
|
会发它,而 subagent 会话(侧栏甚至不列出)几乎肯定不会:有一个就曾跨重启停留在
|
|
173
|
-
`deferred` 状态,尽管它的 transcript 正开在屏幕上。`sessionPersistence.
|
|
204
|
+
`deferred` 状态,尽管它的 transcript 正开在屏幕上。`sessionPersistence.open(id, 'read')`
|
|
174
205
|
返回同样的日志而不发布任何东西,执行器只用 `snapshotEvents`、
|
|
175
206
|
`inheritedEventCount`、`seq` 和 `header`,所以一个不会再被重新打开的尾部仍能变成
|
|
176
207
|
知识。没有 `sessionPersistence` 的组合行为照旧:任务等待。
|
|
@@ -215,13 +246,16 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
215
246
|
数据经由本插件注册在 DSH web 服务器上的、会话作用域的只读 HTTP 路由
|
|
216
247
|
`/api/dsh-hypatia-auto-memory/session` 到达浏览器:状态每次轮询都从内存中的状态表
|
|
217
248
|
现算,shelf 内容只在可能变化时才读(打开时、切换会话时、手动刷新时,以及整合推进了
|
|
218
|
-
该会话水位时),其间缓存 15
|
|
249
|
+
该会话水位时),其间缓存 15 秒。标签页在屏幕上时每 5 秒轮询一次,响应是有界的:
|
|
250
|
+
最新的 40 条摘要与 40 条工作单元,每条正文 4000 字符、整份响应 120000 字符。
|
|
251
|
+
JSE 没有 `ORDER BY`,所以「最新」只能在读完所有候选之后判定;扫描触到上限时计数
|
|
252
|
+
变成下界,标签页会把该次响应标为已截断,而不是让短列表无从解释。路上否掉了两条
|
|
253
|
+
通道。**session projection** 形态最
|
|
219
254
|
合适,但驱动它的事件在本仓库之外写不出来:`Session.append` 没有任何途径设置信封的
|
|
220
255
|
`ignorable` 标记,而持久化读取路径会拒绝一个带着未知事件类型、又没有该标记的会话
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
已注册的 `base`。
|
|
256
|
+
日志——所以自定义事件会破坏会话重载。**设置命名空间**即便在还存在时也是根作用域的:
|
|
257
|
+
宿主无法知道浏览器正在看哪个会话,只能把每个会话的数据都发出去。(dsh 0.1.7 已整体移除
|
|
258
|
+
宿主侧的设置注册 API,下面的路由族现在是唯一通道。)
|
|
225
259
|
|
|
226
260
|
这条路由不属于组合应用自己那批已鉴权的路由——插件注册的路由从来都不是——所以它像
|
|
227
261
|
`dsh-hypatia-ui` 那样自鉴权:socket 必须是回环,`Host` 必须是回环名(`localhost`、
|
|
@@ -232,8 +266,14 @@ agent/session-start ──▶ rules/taboos inject()
|
|
|
232
266
|
|
|
233
267
|
## 配置
|
|
234
268
|
|
|
235
|
-
|
|
236
|
-
|
|
269
|
+
插件的 cordis `Config` 就是它的配置表单。dsh ≥ 0.1.7 把插件的 Config schema 投影到
|
|
270
|
+
侧边栏的**「插件」页**,卡片渲染在该 bundle 自己的页面上;如果某个部署的 profile
|
|
271
|
+
不渲染这个页面,同一张卡片会退到**「设置 → 内置插件」**里作为一个标签页。卡片落在
|
|
272
|
+
存在的那个位置上,并在两者之间迁移时绝不会同时出现(`src/client/plugin-card-seat.ts`)。
|
|
273
|
+
下面每个字段保存即生效,无需重载 profile。配置以插件条目 `config:`
|
|
274
|
+
的形式存进 profile patch;0.1.7 之前的 `settings.yaml` 段落会在首次启动时被一次性导入。
|
|
275
|
+
所有字段可选,展示的是默认值。空白的巩固路由在保存时就被拒绝;重复的路由在运行时被丢弃
|
|
276
|
+
并记一条警告:
|
|
237
277
|
|
|
238
278
|
```yaml
|
|
239
279
|
hypatia-auto-memory:
|
|
@@ -277,6 +317,7 @@ hypatia-auto-memory:
|
|
|
277
317
|
reconcileOnStartup: true # 重置 session 在 shelf 里已无 msg-* 的行
|
|
278
318
|
pruneVanishedSessions: true # 删除 DSH 已不存在的会话的行和任务
|
|
279
319
|
backfillConsolidation: true # 巩固会话内触发器从未到达的已记录尾部
|
|
320
|
+
consolidationBackfillPerScope: 4 # 每次启动每个 scope 允许排入的会话数
|
|
280
321
|
```
|
|
281
322
|
|
|
282
323
|
bundle 行上的 cordis config 块只承载技能打包:
|
|
@@ -288,17 +329,19 @@ bundle 行上的 cordis config 块只承载技能打包:
|
|
|
288
329
|
skillsDir: /abs/path # 覆盖打包的 skills/ 目录
|
|
289
330
|
```
|
|
290
331
|
|
|
291
|
-
`enabled` 和 `autoApprove`
|
|
292
|
-
|
|
332
|
+
`enabled`、`shelf` 和 `autoApprove` 在采集器启动时读取,所以保存其中任一项的改动都会
|
|
333
|
+
重启采集器(排空队列、重新打开状态);其它开关原地生效。0.1.7 之前的 `settings.yaml`
|
|
334
|
+
也是靠这次重启生效的:dsh 在所有插件启动之后才导入它,而采集器本身会等到那一刻才启动,
|
|
335
|
+
所以基本不会先按默认值跑起来。
|
|
293
336
|
|
|
294
337
|
### 选择 shelf
|
|
295
338
|
|
|
296
339
|
`shelf` 指定所有条目写入、所有查找读取的 hypatia shelf——记录、巩固、级联、
|
|
297
340
|
rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所报告内容的下拉框
|
|
298
|
-
|
|
341
|
+
——标签页打开时按需读取;成功的结果在宿主侧缓存 60 秒,失败的结果从不缓存——并标注
|
|
342
|
+
已注册但未连接的 shelf。
|
|
299
343
|
|
|
300
|
-
- **像 `enabled`
|
|
301
|
-
设置变更时日志会说明。
|
|
344
|
+
- **像 `enabled` 一样,保存即生效**:采集器会在新 shelf 上重启,日志会说明。
|
|
302
345
|
- **进度按 shelf 保存。** 水位和排队的任务按 shelf 存储,所以新选的 shelf 会让每个
|
|
303
346
|
会话从零开始,而切回去会恰好从该 shelf 上次停下的地方继续——不会把已有的内容重新
|
|
304
347
|
记录或重新巩固进某个 shelf。代价落在新 shelf 上:每个活会话从它的开头开始记录——
|
|
@@ -312,8 +355,10 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
|
|
|
312
355
|
重试,直到它被连接(`hypatia connect <dir> --name <name>`)。走 MCP 时,该失败会
|
|
313
356
|
重启 server,所以 `connect` 之后的重试能到达那个 shelf。
|
|
314
357
|
|
|
315
|
-
|
|
316
|
-
|
|
358
|
+
列表通过与 Memory 标签页相同的路由族到达浏览器——`GET /api/dsh-hypatia-auto-memory/shelves`,
|
|
359
|
+
在启动时的对账与回填之前就挂载(`enabled: false` 时也挂载),所以 shelf 坏掉、需要换一个
|
|
360
|
+
时列表就在那里。(0.1.7 之前它走一个只读设置命名空间,该宿主侧 API 已被移除;
|
|
361
|
+
shelf 清单本来就不是配置,而设置域现在只投影插件的 Config 表单。)
|
|
317
362
|
|
|
318
363
|
## 运维清单
|
|
319
364
|
|
|
@@ -346,22 +391,23 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
|
|
|
346
391
|
|
|
347
392
|
| 症状 | 原因 / 处理 |
|
|
348
393
|
|---|---|
|
|
349
|
-
| 聊完没有条目 | `enabled: false`、缺少 `hypatia` 二进制、或 collect fiber 处于 PENDING(需要基础组合里的 `sessions`、`storageDomain`、`subprocess
|
|
394
|
+
| 聊完没有条目 | `enabled: false`、缺少 `hypatia` 二进制、或 collect fiber 处于 PENDING(需要基础组合里的 `sessions`、`storageDomain`、`subprocess`)——查 profile 日志 |
|
|
350
395
|
| 重启后一整轮什么都没记录 | storage domain 因为某条已存记录不符合 schema 而拒绝打开。storage service 在读时校验、不在写时校验,所以一次坏写入只会在下次启动时暴露——而一条坏记录会拖垮整个 domain。在 profile 日志里找 `startup failed` / `does not match its schema`。缺 `error` 的任务行现在已默认化;其它坏行则停 DSH、从 `~/.dsh/storages/hypatia_auto_memory.json` 删除它再重启 |
|
|
396
|
+
| 「记忆」标签页始终读不到数据,或显示读取失败 | 页面不是通过**回环来源**访问的。该路由自鉴权——回环 socket、回环 `Host`、同源——所以从另一台设备读 DSH(LAN 地址,或绑定 `0.0.0.0`)会被按设计拒绝,隧道送来公网 `Host` 也一样。响应两半一起被拒,所以标签页报的是读取失败而不是部分数据。请用 `127.0.0.1` 或 `localhost` 打开 GUI |
|
|
351
397
|
| 条目只在 turn 结束后才出现 | 设计如此:span 在 `turn/end` 切分,或当 turn 跑得比 `flushWindowMs` 久时在第一个 `step/end` 切分,所以条目不会缺它自己的工具结果 |
|
|
352
398
|
| 有记录,没摘要 | `consolidation.models` 为空或无效——首次触发时告警一次 |
|
|
353
399
|
| 有摘要但没有 `sum2-*` | 该项目里未归档的 tier-1 摘要还不足 `cascade.batchSize` 条 |
|
|
354
400
|
| 工作单元没有关系 | shelf 没有嵌入模型(`similar` 失败),所有候选都超过了 `dedupMaxDistance`,或裁决调用本身没有产出裁决——`model_calls` 里表现为 `incomplete` / `max-tokens`,也就是开启了思考的路由把整个输出预算花在推理上、还没开始作答就撞了上限。现在裁决会先确认适配器声明了 `off`,再对该路由请求 `reasoningEffort: 'off'`;对无法接受它的路由则把上限提到 1024。自 hypatia #19 起,本地模型在 `~/.hypatia/models/<org>/<name>`(或 Hugging Face 缓存)里找,不再挨着 shelf:一个以前能回答 `similar`、现在说 `is not installed` 的 shelf,需要 `hypatia model install <model>`,或 `hypatia model register <model> <dir>` 指向它已有的文件 |
|
|
355
401
|
| `similar` 仍返回 `msg-*` 行 | 它们是在本插件让日志层退出嵌入之前写的,或由一个没有 `--no-embed` 的 hypatia 写的。在 shelf 的 `shelf.toml` 里用 `embedding.skip_tags = ["message"]` 加一次 `hypatia backfill` 一次性收回知识向量(比该 key 旧的二进制会拒绝打开 shelf,所以先升级所有共享它的二进制)。不要把 `session` 加进那个列表:`skip_tags` 匹配任何带该 tag 的条目,而人们写的关于会话的知识也带它——在试过的 shelf 上就有一条这样的条目丢了向量。插件自己的 `session-*` 节点很少,现在已逐次写入退出。三元组没有 tags 也没有 update 命令,所以之前写的 `belongTo` / `summary` 边保留向量 |
|
|
356
|
-
| 任务处于 `deferred` 状态 | live store 和 persistence 都没能提供它的会话。不是错误:它不消耗尝试次数,一旦二者之一能提供就运行。通常 storage 立即回答——只有组合里没有 `sessionPersistence`,或它的读取失败(日志里找 `could not be read
|
|
402
|
+
| 任务处于 `deferred` 状态 | live store 和 persistence 都没能提供它的会话。不是错误:它不消耗尝试次数,一旦二者之一能提供就运行。通常 storage 立即回答——只有组合里没有 `sessionPersistence`,或它的读取失败(日志里找 `could not be read`)时才会持久化这个状态。被 deferred 的巩固任务会在下次启动时被 backfill 重新入队,所以它不必等会话被重新打开 |
|
|
357
403
|
| 任务处于 `failed` 状态 | 一次 hypatia 或模型错误在 `maxAttempts` 之后持久化了。失败的 `log-message` 记录在下次启动时被修剪,因为水位会重新推导它们的范围;其它类型保留供检查——删掉一条好让下一个触发器重新创建它 |
|
|
358
404
|
| 水位说已记录,但 shelf 里没条目 | shelf 在记录后被重置或切换。启动时处理:`housekeeping.reconcileOnStartup` 重置任何 session 已无 `msg-*` 的行,该会话下次活跃时从头重新记录——并重新巩固。shelf 查询失败则不动该行。一个所有消息都被故意删除的会话无法区分,会被重新记录;如果这要紧,关掉这个开关 |
|
|
359
405
|
| 警告 `hypatia mcp unavailable: …; using the hypatia CLI until the profile reloads` | 二进制早于 `hypatia mcp`(消息会引用它的 `unrecognized subcommand`)、缺本插件调用的某工具、或握手返回错误。一切继续走 CLI;升级 hypatia 并重载 profile 以使用 MCP |
|
|
360
406
|
| 每次调用都失败 `hypatia mcp exited …` 或 `timed out` | 二进制能启动但它的 MCP server 起不来——`binaries` 里一个改了 clap 退出码或措辞的包装脚本不会被识别为旧二进制。设 `transport: cli` |
|
|
361
|
-
| 改了 `shelf` 之后每次写入都失败 | shelf 没注册或没连接——启动时记录 `shelf "<name>" is not registered`。连接它(`hypatia connect <dir> --name <name
|
|
407
|
+
| 改了 `shelf` 之后每次写入都失败 | shelf 没注册或没连接——启动时记录 `shelf "<name>" is not registered`。连接它(`hypatia connect <dir> --name <name>`)后重新保存设置(或重载 profile),或换一个 |
|
|
362
408
|
| Agent 在 `default` 里搜,而插件写在别处 | 一份磁盘上的 `hypatia-memory`(见下)替换了随包技能,或 recall 被禁用,所以没有东西告诉 Agent 用哪个 shelf |
|
|
363
409
|
| 手动乱改后出现重复 `msg-*` | 在 hypatia 里删掉该条目,并在 state domain 里把该会话的 `lastLoggedSeq` 调低——回填会重新创建它一次 |
|
|
364
|
-
| Agent 自己的 `hypatia` 写入仍要审批 | `autoApprove: false
|
|
410
|
+
| Agent 自己的 `hypatia` 写入仍要审批 | `autoApprove: false`、命令在引号外有管道/重定向/串联、或首词不是 `binaries` 之一——按设计只回答纯调用。读取根本不会走到审批,所以那里没有要修的 |
|
|
365
411
|
| Agent 拿到了一份教它手动记录消息的记忆协议 | 另一个插件先注册了 `hypatia-memory`(profile 里还有 `dsh-hypatia`),或磁盘上有 `hypatia-memory`——通常是 `hypatia skill install --agent codex` 写的 `~/.agents/skills/hypatia-memory`。Agent 预设会在比插件更近的一层加载磁盘技能,所以它在会话里胜出,尽管技能中心可能列出本插件。删掉启动告警指出的那份拷贝 |
|
|
366
412
|
| 不知道实际用了哪个模型 | 设计上就是轮流的: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` 行表示调用仍在进行中,或进程在调用中途死了:记录在调用前先落盘、调用结束后回填 |
|
|
367
413
|
| 启动告警从不出现在终端 | `dsh web` 不挂日志导出器,所以插件任何级别的日志行都没地方去;控制台导出器的默认阈值也会丢掉警告(warn 是 2 级,高于 info 的 1 级)。临时挂一个 logger,如 `dsh-logbook` 或 `dsh-boot-doctor` 来读它们 |
|
|
@@ -412,8 +458,8 @@ rules/taboos 预载、启动清理。设置卡片把它做成 `hypatia list` 所
|
|
|
412
458
|
行上限里,它们会让技能在真正审阅的三元组远未达到上限前就停在截断检查上。
|
|
413
459
|
|
|
414
460
|
它的 `evals/evals.json` 为这两处补丁新增 case 9 和 10。
|
|
415
|
-
- **顶层的 `enabled: false`
|
|
416
|
-
|
|
461
|
+
- **顶层的 `enabled: false` 在采集器启动时读取**;保存改动会重启采集器,而各特性
|
|
462
|
+
开关原地生效。
|
|
417
463
|
|
|
418
464
|
## 目录结构
|
|
419
465
|
|
|
@@ -426,8 +472,8 @@ dsh-hypatia-auto-memory/
|
|
|
426
472
|
├── tsdown.config.ts # 浏览器 CJS 工厂构建
|
|
427
473
|
├── src/
|
|
428
474
|
│ ├── index.js # fiber 组合(collect + 可选子模块)
|
|
429
|
-
│ ├── config.js #
|
|
430
|
-
│ ├── shelf.js # 每 shelf 表视图、`hypatia list
|
|
475
|
+
│ ├── config.js # cordis Config schema(volatile)、默认值、热更新
|
|
476
|
+
│ ├── shelf.js # 每 shelf 表视图、`hypatia list` 解析
|
|
431
477
|
│ ├── state.js # storageDomain 规格 + 进度助手
|
|
432
478
|
│ ├── collector.js # session/event 过滤、台账、回填
|
|
433
479
|
│ ├── content-policy.js # 脱敏、日期、上限、slug(纯函数)
|
|
@@ -439,6 +485,8 @@ dsh-hypatia-auto-memory/
|
|
|
439
485
|
│ ├── consolidator.js # 阈值、prompt、llm.stream、校验
|
|
440
486
|
│ ├── cascade.js # log₁₆(n) 分层摘要归档
|
|
441
487
|
│ ├── model-log.js # 每次尝试实际用了哪个模型的有界记录
|
|
488
|
+
│ ├── memory-status.js # 两张状态表按会话折叠(纯函数)
|
|
489
|
+
│ ├── memory-api.js # 记忆标签页与 shelf 清单:只读路由族 + JSE 读取
|
|
442
490
|
│ ├── recall.js # 会话启动时的 rules/taboos 预载
|
|
443
491
|
│ ├── auto-approve.js # 批准 Agent 自己的纯 bash hypatia 调用
|
|
444
492
|
│ ├── skills.js # 随包技能注册(从不遮蔽其它提供者)
|
|
@@ -446,7 +494,10 @@ dsh-hypatia-auto-memory/
|
|
|
446
494
|
│ └── client/ # 设置卡片 + 记忆标签页
|
|
447
495
|
│ ├── index.tsx # client 插件入口 + slot 注册
|
|
448
496
|
│ ├── SettingsCard.tsx
|
|
449
|
-
│ ├──
|
|
497
|
+
│ ├── MemoryView.tsx # 记忆标签页主体
|
|
498
|
+
│ ├── memory-client.ts # 它的请求与合并规则(纯函数)
|
|
499
|
+
│ ├── plugin-card-seat.ts # 卡片落座:官方「插件」页,否则设置标签页
|
|
500
|
+
│ ├── shelves.ts # shelf 下拉选项(清单来自 /shelves 路由)
|
|
450
501
|
│ └── slot-contract.ts
|
|
451
502
|
├── lib/
|
|
452
503
|
│ └── client.js # 构建好的浏览器工厂(提交此文件)
|
package/cordis.patch.yml
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
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
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# Behavior is configured through this entry's `config:` (edited live on the
|
|
7
|
+
# bundle's own page of the sidebar's Plugins page, which dsh ≥ 0.1.7 projects
|
|
8
|
+
# the plugin's Config schema onto; a deployment that renders no Plugins page
|
|
9
|
+
# gets the same card under Settings → Built-in plugins).
|
|
10
|
+
# The cordis-level knobs below are the non-volatile ones — changing them
|
|
11
|
+
# remounts the plugin:
|
|
9
12
|
#
|
|
10
13
|
# - id: hypatia-auto-memory
|
|
11
14
|
# config:
|