dsh-prime-memory 0.25.0 → 0.26.1

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.en.md CHANGED
@@ -7,6 +7,43 @@
7
7
 
8
8
  This file covers the **0.12.0** release notes and the current **unreleased** changes in English. For the full history, see [CHANGELOG.md](./CHANGELOG.md) (Chinese).
9
9
 
10
+ ## [0.26.1] — 2026-10-15
11
+
12
+ ### Added
13
+
14
+ - **Backfill panel: auto-resumed progress + scan progress + honest summary.** The three backfill endpoints are trigger-plus-status hybrids, so a bare call while idle **starts a run**. New request body `BackfillTriggerRequest { probe?: boolean }` — `probe:true` returns the current snapshot and never starts anything; `BackfillSection` probes all three on mount and re-attaches polling if one is running (leaving and returning to the overview no longer needs a manual re-click). `BackfillProgress` gains `scanned/scanTotal` ("scanning 1252/1252 rows…"), and a 0-candidate scan now reports "no records missing keywords, nothing to backfill" instead of "0 updated", which read as a failure.
15
+ - **Cancel switch for the three batch actions (bounded hard-stop).** The contract gains `cancel?: boolean` and `BackfillProgress.cancelled`; each module exports `cancelXBackfill()`. Cancellation is **bounded**: after the signal a one-shot grace timer fires `runAbort.abort()` to cut in-flight requests. The hard bound is clamped by `backfill.cancelGraceMs` (schema max 30_000, default 15s) — no YAML value can exceed it.
16
+ - **Skill tool-group seal `toolSkills`.** The four `memory_skill_*` tools now go through the host seal mechanism (`reg('skills', …)`, ANDed with the `skillsEnabled` feature switch); both hot-apply on `loader/volatile-update` (toggling in the panel registers/unregisters immediately, no restart). Previously they gated once at registration, sat unconditionally in the model tool list once enabled, and did not hot-update on toggle.
17
+
18
+ ### Changed
19
+
20
+ - **All LLM batch sites unified to one-record-at-a-time serial.** The remaining three merged batches — wing (`labelWingOne`/`tagOne`), `pipeline/relabel.ts`, `room-review.ts` (`roomCandidateOne`) — were converted; all five batch sites now send exactly one memory per request and await it before the next (`concurrency` defaults to 1). Rationale: a single 35B record measures ~2.6s, while a multi-record batch loses the whole batch to one truncation or empty output.
21
+ - **Reshape exclusion now keyed by a version stamp.** `dsh_reshape_ver` (=2) with `isReshapedAtCurrentVersion(meta, content)` — the stamp is written **only on a successful write-back**; a record left unchanged gets **no marker** and stays retryable next run (the legacy boolean `dsh_reshape_tried` is read only for legacy compatibility). Bumping the schema version re-runs earlier outputs automatically.
22
+ - **`backfill.maxRecords` defaults to 300** (was wrongly set to 40, so a real 251-record target only advanced 40 per run).
23
+
24
+ ### Fixed
25
+
26
+ - **Bare-call polling hot loop (the real cause of "it doesn't start").** The UI tick called the trigger+status hybrid endpoints bare, so while idle it started a new run every 3s and the panel stayed on "scanning". The tick now sends `{ probe: true }`.
27
+ - **Batch scans returning empty output: thinking models used as direct-answer models.** `LlmCallOptions` gains a **call-level `reasoningEffort` override** and all five batch labelers pass `'off'`. With the route effort at `""` (auto) a thinking model defaulted to thinking on, the budget went to reasoning and the body came back at 0 characters (156 of 162 real failures were `local-9b` empty output).
28
+ - **`client/src/ui/MarkdownBody.tsx` typecheck failure.** The four `string | undefined` regex captures brought in with 0.26.0 failed under `noUncheckedIndexedAccess`; added non-null assertions.
29
+
30
+ ## [0.26.0] — 2026-10-14
31
+
32
+ ### Added
33
+
34
+ - **Existing-memory re-distillation reshaping (`redistill`)**: new independent batch scan `src/redistill-backfill.ts` (mirrors wing/keyword backfill: single-flight / background / bounded / failures leave records untouched) rewrites the body of **active, skeleton-incomplete** L1 records into the G2 per-type "cause→effect" sectioned form, and produces `metadata.dsh_keywords` / `metadata.dsh_usage_scenario` in the same pass. New endpoint `dsh-memory/redistill-backfill` (returns `{started,running,enabled,updated,skipped,failed}` immediately); a "Re-distill & Reshape" button in the WingWheel panel. Config `redistill.enabled` defaults **off** — off means no reshaping and content/FTS/projection byte-for-byte unchanged (zero drift).
35
+ - **Quality gate**: writes back only when the reshaped body's `scoreSkeleton` hit count is **strictly better**, and stamps the reshape version `dsh_reshape_ver` (excluded from later runs); otherwise it keeps the original **unstamped** so it stays eligible for a retry — never fakes a label or fabricates content just to "look structured".
36
+ - **Active only**: retired/superseded records are not touched.
37
+ - **Durable write-back for content edits**: reshaping writes via `L1Store.appendNew`, which in one shot appends to the JSONL fact source, recomputes the embedding, re-indexes `l1_fts`, and overwrites the `.memory.md` projection (the exporter re-renders from the DB and overwrites on any difference). Entry requires `vectorsReady()` — the whole run is skipped when embeddings aren't ready, to avoid silently dropping a record's vector.
38
+ - **`dsh-memory/records-meta` endpoint**: batch lookup (≤50) by `record_id` returning full metadata *and* body (name/type/priority/content/maturity/createdAt/updatedAt/scene/scope/workspaceId/keywords/usageScenario/filePath/retired) straight from SQLite via `stores.l1.getByIds`. The memory viewer therefore **no longer reads `.memory.md` files** — the earlier fileViewer approach (a fake `<id>.memory` path) was resolved by the host's EditorHost as a physical file and always hit ENOENT. Contract lives in `contract.ts`; endpoint count 62→63 (`tests/contract-keys.test.ts` pins it).
39
+ - **Display titles stop guessing from the body**: `memoryRecall` and slot `refViews` prefer the record's `name` (the short title the name-distillation produces) and only fall back to the first body line. `firstLine()` is now markdown-aware: H1 (`# xxx`) is a document title and is kept, H2+ (`## Findings`) is a section marker and is skipped — previously `## Findings` was rendered as the memory's "name" although it is plainly body text.
40
+ - **Single source of truth for projection paths, `relPathFor()`**: `renderRecord` (disk write), `records-meta`'s `filePath` and slot `refViews.filePath` now share one algorithm. The projection had copied its own sanitizing regex, so a single record could yield two different file names — the address the projection reports would not match the file on disk, which is exactly what those three consumers exist to cross-check. `refViews` also carry `filePath`/`workspaceId` so callers can build `<workspaceId>/.agents/memory/<filePath>` (workspace scope) or resolve against the projection root (global).
41
+ - **Markdown-rendered bodies in the records panel** (client): expanded record bodies go through the new `client/src/ui/MarkdownBody.tsx` (heading levels / bold / inline code / lists, zero external deps), so `## Findings` is no longer a bare line of text.
42
+
43
+ ### Fixed
44
+
45
+ - **`client-session-recalled-bundle` regression guard**: after 0.25.0 handed the pill's "this-session recalled" rendering to dsh-brief-sidebar, this test still asserted the pill contained the recalled strings (missed in that release). Now asserts the prime-memory pill does **not** re-render recalled, keeping the projection-wiring assertions.
46
+
10
47
  ## [0.25.0] — 2026-10-14
11
48
 
12
49
  ### Added
package/CHANGELOG.ja.md CHANGED
@@ -9,6 +9,44 @@
9
9
 
10
10
  本ファイルは **0.12.0** リリースノートと現在の**未リリース**変更の日本語版です。全履歴は [CHANGELOG.md](./CHANGELOG.md)(中文)を参照してください。
11
11
 
12
+ ## [0.26.1] — 2026-10-15
13
+
14
+ ### 追加
15
+
16
+ - **回填パネル「進行の自動復帰 + スキャン進捗 + 正直な要約」**:3 つの回填エンドポイントは「トリガー + 状態照会」の二合一で、アイドル時の裸呼び出しは**本当に走り出す**。新リクエストボディ `BackfillTriggerRequest { probe?: boolean }`——`probe:true` は現スナップショットのみ返し絶対に起動しない。`BackfillSection` はマウント時に resume で 3 つを偵察し、走っていればポーリングを再接続(概要区を離れて戻っても手動クリック不要)。`BackfillProgress` に `scanned/scanTotal` を追加(「ストック 1252/1252 行をスキャン中…」);候補 0 のときは「キーワード欠落の記録なし、回填不要」のような**正直な要約**を返す(「成功 0 件」が失敗と読まれる問題を解消)。
17
+ - **3 つのバッチ操作に「中止」スイッチ(有界強制停止)**:契約に `cancel?: boolean` と `BackfillProgress.cancelled` を追加;各モジュールが `cancelXBackfill()`。中止は**有界**:信号後に一度きりの猶予タイマーが発火し `runAbort.abort()` で在飛リクエストを即断。ハード境界は `backfill.cancelGraceMs`(schema max 30_000、既定 15s)でクランプ——YAML に何を書いても越えられない。
18
+ - **スキルツール群の封印 `toolSkills`**:`memory_skill_*` の四件を宿主封印機構(`reg('skills', …)`)へ統合し、機能スイッチ `skillsEnabled` と AND。両方とも `loader/volatile-update` で**ホット反映**(パネルの切り替えが即時登録/解除、再起動不要)。従来は登録時に一度だけ判定し、有効化後は無条件にモデルのツール一覧へ常駐、切り替えてもホット更新されなかった。
19
+
20
+ ### 変更
21
+
22
+ - **全 LLM バッチを「逐条直列」に統一**:残る 3 つの合併バッチ——wing(`labelWingOne`/`tagOne`)、`pipeline/relabel.ts`、`room-review.ts`(`roomCandidateOne`)——を変換;5 つのバッチ地点すべてが「1 リクエストに 1 件のみ、完了してから次」(`concurrency` 既定 1)。根拠:単条 35B は実測 ~2.6s、一方で複数条バッチは一度の截断/空出力でバッチ全体を失う。
23
+ - **整形の除外判定を「バージョン刻印」へ**:`dsh_reshape_ver`(=2)+ `isReshapedAtCurrentVersion(meta, content)`——刻印は**書き戻し成功時のみ**打つ;閘を通らなかった記録は原文のまま**マークなし**で次回も再試行可能(旧ブール `dsh_reshape_tried` はストック互換の読み取りのみ)。整形ルール更新時にバージョンを上げれば旧産物を自動再実行。
24
+ - **`backfill.maxRecords` 既定 300**(以前誤って 40 にしており、実機 251 件の対象が一回 40 件しか進まなかった)。
25
+
26
+ ### 修正
27
+
28
+ - **ポーリング裸呼び出しの死循環(「開始しない」の真因)**:UI の tick が「トリガー + 状態照会」二合一エンドポイントを裸で呼び、アイドル時に 3 秒ごとに新一轮を開始、パネルが「スキャン中」のまま。tick を `{ probe: true }` に変更。
29
+ - **バッチスキャンの全空出力の真因:思考モデルを直答モデルとして使用**:`LlmCallOptions` に**呼び出し級 `reasoningEffort` オーバーライド**を追加し、5 つのバッチラベラが `'off'` を明示。従来は経路の effort が `""`(自動)だと思考モデルが既定で思考オン、予算を reasoning に食われ本文 0 文字(実機 162 回の失敗中 156 回が `local-9b` の空出力)。
30
+ - **`client/src/ui/MarkdownBody.tsx` の typecheck 赤**:0.26.0 が持ち込んだ 4 箇所の `string | undefined`(正規表現キャプチャ)が `noUncheckedIndexedAccess` でエラー、非 null アサーションを追加。
31
+
32
+ ## [0.26.0] — 2026-10-14
33
+
34
+ ### 追加
35
+
36
+ - **`dsh-memory/records-meta` 端点**:`record_id` の一括照会(≤50)で単条記憶の全量メタデータ+本文(name/type/priority/content/maturity/createdAt/updatedAt/scene/scope/workspaceId/keywords/usageScenario/filePath/retired)を `stores.l1.getByIds` で SQLite から直接返す。記憶ビューアはこれで **`.memory.md` を読まなくてよい**——以前の fileViewer 方式(`<id>.memory` を偽装)は宿主 EditorHost に物理パスとして解決され必ず ENOENT。契約は `contract.ts`、端点数 62→63(`tests/contract-keys.test.ts` が固定)。
37
+ - **表示タイトルを本文から推測しない**:`memoryRecall` と槽位 `refViews` の title は `name`(名称蒸留の短題)を優先し、無い時だけ本文先頭行へ回退。`firstLine()` は markdown 対応になり、H1(`# xxx`)は文書タイトルとして保持、H2+(`## 結論/事実`)は分節マーカーとしてスキップ——以前は明らかに本文の `## 結論/事実` が記憶「名称」として表示されていた。
38
+ - **投射パスの単一事実源 `relPathFor()`**:`renderRecord`(ディスク書込)、`records-meta` の `filePath`、槽位 `refViews.filePath` の三箇所が同一アルゴリズムに集約。以前は投射側が独自のサニタイズ正規表現を写しており、同一記憶から二つの異なるファイル名が生じ得た——投射が示すアドレスとディスク上の実ファイルが一致せず、まさにこの三箇所が照合すべき事実そのもの。`refViews` は `filePath`/`workspaceId` も透出し、消費側は `<workspaceId>/.agents/memory/<filePath>`(workspace 範囲)または投射ルート(global)で絶対パスを組む。
39
+ - **検索パネルの本文を markdown 化**(client):RecordsTab の展開本文は新し `client/src/ui/MarkdownBody.tsx`(見出し階層 / 太字 / インラインコード / リスト、外部依存ゼロ)を経由し、`## 結論/事実` は裸のテキスト行ではなくなる。
40
+
41
+ - **既存記憶の再蒸留整形(`redistill`)**:独立バッチスキャン `src/redistill-backfill.ts`(wing/keyword 回填を镜像:単飛/後台/有界/失敗時原記録不変)が、**アクティブかつ骨格不全**の L1 本文を G2 類型別の「前因後果」分節形式に再編し、同時に `metadata.dsh_keywords` / `metadata.dsh_usage_scenario` を産出。新端点 `dsh-memory/redistill-backfill`(`{started,running,enabled,updated,skipped,failed}` を即返)、パネル(WingWheel)に「再蒸留整形」ボタン。設定 `redistill.enabled` はデフォルト**关**——关時は整形せず content/FTS/投射が逐字不変(ゼロドリフト)。
42
+ - **品質ゲート**:整形後 `scoreSkeleton` の命中数が**厳密に優れる**時だけ書き戻し、整形バージョン `dsh_reshape_ver` を刻印(次回以降は対象外);未達なら原文を残し**刻印せず**、次回も再試行可能——「見かけ上フォーマット付き」のために偽ラベルを貼ったり捏造したりは一切しない。
43
+ - **アクティブのみ**:退場/取代会済みは対象外。
44
+ - **本文変更の永続書込経路**:整形の書込は `L1Store.appendNew` を経由し、JSONL 事実源追記 + 埋め込み再計算 + `l1_fts` 再インデックス + `.memory.md` 投射上書き(エクスポータは库から再レンダし差分があれば上書き)を一度に完了。入口は `vectorsReady()` を要求——埋め込み未就绪時はラン全体をスキップし、ベクトルの静默消失を防ぐ。
45
+
46
+ ### 修正
47
+
48
+ - **`client-session-recalled-bundle` 回帰ガード**:0.25.0 で「本会話召回」の pill レンダリングを dsh-brief-sidebar へ移管した後、このテストは依然 pill に召回文字列があると主張していた(リリース時の更新漏れ)。現在は prime-memory pill が召回を**重複レンダリングしない**ことを主張し、投影接線の主張は保持。
49
+
12
50
  ## [0.25.0] — 2026-10-14
13
51
 
14
52
  ### 追加
package/CHANGELOG.ko.md CHANGED
@@ -9,6 +9,44 @@
9
9
 
10
10
  이 파일은 **0.12.0** 릴리스 노트와 현재 **미출시** 변경 사항의 한국어판입니다. 전체 이력은 [CHANGELOG.md](./CHANGELOG.md)(中文)를 참조하세요.
11
11
 
12
+ ## [0.26.1] — 2026-10-15
13
+
14
+ ### 추가
15
+
16
+ - **백필 패널 「진행 자동 복원 + 스캔 진행 + 정직한 요약」**: 3개 백필 엔드포인트는 「트리거 + 상태 조회」 이합일(二合一)이라, 유휴 시 베어 호출은 **실제로 한 회차를 시작**한다. 새 요청 바디 `BackfillTriggerRequest { probe?: boolean }`——`probe:true`는 현재 스냅샷만 반환하고 절대 시작하지 않는다. `BackfillSection`은 마운트 시 resume로 3개를 탐지해 돌고 있으면 폴링을 재연결(개요 구역을 떠났다 돌아와도 수동 재클릭 불필요). `BackfillProgress`에 `scanned/scanTotal` 추가(「스톡 1252/1252 행 스캔 중…」); 후보 0이면 「키워드가 없는 기록이 없어 백필 불필요」 같은 **정직한 요약**을 반환(「성공 0건」이 실패로 읽히던 문제 해소).
17
+ - **3개 배치 동작에 「중지」 스위치(유계 강제 정지)**: 계약에 `cancel?: boolean`과 `BackfillProgress.cancelled` 추가; 각 모듈이 `cancelXBackfill()` 제공. 중지는 **유계**: 신호 후 일회성 유예 타이머가 `runAbort.abort()`로 비행 중 요청을 즉시 절단. 하드 경계는 `backfill.cancelGraceMs`(schema max 30_000, 기본 15s)로 클램프——YAML에 무엇을 써도 넘을 수 없다.
18
+ - **스킬 도구군 봉인 `toolSkills`**: `memory_skill_*` 네 개를 호스트 봉인 메커니즘(`reg('skills', …)`)에 통합하고 기능 스위치 `skillsEnabled`와 AND. 둘 다 `loader/volatile-update`에서 **핫 반영**(패널 토글이 즉시 등록/해제, 재시작 불필요). 이전에는 등록 시 한 번만 판정했고, 활성화 후에는 무조건 모델 도구 목록에 상주했으며 토글해도 핫 갱신되지 않았다.
19
+
20
+ ### 변경
21
+
22
+ - **모든 LLM 배치를 「건별 직렬」로 통일**: 남은 3개 병합 배치——wing(`labelWingOne`/`tagOne`), `pipeline/relabel.ts`, `room-review.ts`(`roomCandidateOne`)——를 변환; 5개 배치 지점 모두 「요청당 정확히 1건, 완료 후 다음」(`concurrency` 기본 1). 근거: 단건 35B 실측 ~2.6s, 반면 다건 배치는 한 번의 절단/빈 출력으로 배치 전체를 잃는다.
23
+ - **재정형 제외 판정을 「버전 각인」으로**: `dsh_reshape_ver`(=2) + `isReshapedAtCurrentVersion(meta, content)`——각인은 **쓰기 성공 시에만** 찍는다; 게이트를 못 넘은 기록은 원문 그대로 **마커 없음**으로 다음 회차에도 재시도 가능(구 불리언 `dsh_reshape_tried`는 레거시 호환 읽기 전용). 재정형 규칙 갱신 시 버전을 올리면 이전 산출물을 자동 재실행.
24
+ - **`backfill.maxRecords` 기본 300**(이전에 잘못 40으로 설정되어 실기 251건 대상이 한 회차에 40건만 진행).
25
+
26
+ ### 수정
27
+
28
+ - **폴링 베어 호출 무한 루프(「시작하지 않음」의 진짜 원인)**: UI tick이 「트리거 + 상태 조회」 이합일 엔드포인트를 베어로 호출해 유휴 시 3초마다 새 회차를 시작, 패널이 「스캔 중」에 머묾. tick을 `{ probe: true }`로 변경.
29
+ - **배치 스캔 전부 빈 출력의 진짜 원인: 사고 모델을 직답 모델로 사용**: `LlmCallOptions`에 **호출급 `reasoningEffort` 오버라이드** 추가, 5개 배치 라벨러가 `'off'`를 명시. 이전에는 경로 effort가 `""`(자동)이면 사고 모델이 기본으로 사고 ON, 예산을 reasoning이 먹어 본문 0자(실기 162회 실패 중 156회가 `local-9b` 빈 출력).
30
+ - **`client/src/ui/MarkdownBody.tsx` typecheck 적색**: 0.26.0이 들여온 4곳의 `string | undefined`(정규식 캡처)가 `noUncheckedIndexedAccess`에서 오류, 비-null 단언 추가.
31
+
32
+ ## [0.26.0] — 2026-10-14
33
+
34
+ ### 신규
35
+
36
+ - **`dsh-memory/records-meta` 엔드포인트**: `record_id` 일괄 조회(≤50)로 단일 기억의 전량 메타데이터와 본문(name/type/priority/content/maturity/createdAt/updatedAt/scene/scope/workspaceId/keywords/usageScenario/filePath/retired)을 `stores.l1.getByIds` 로 SQLite 에서 직접 반환。기억 뷰어는 이로써 **`.memory.md` 를 읽지 않아도 됨**——이전의 fileViewer 방식(`<id>.memory` 위장)은 호스트 EditorHost 가 물리 경로로 해석해 반드시 ENOENT。계약은 `contract.ts`, 엔드포인트 수 62→63(`tests/contract-keys.test.ts` 고정)。
37
+ - **표시 제목을 본문에서 추정하지 않음**: `memoryRecall` 과 슬롯 `refViews` 의 title 은 `name`(명칭 증류가 만든 짧은 제목)을 우선하고, 없을 때만 본문 첫 줄로 회퇴。`firstLine()` 은 markdown 인지 방식으로 바뀌어 H1(`# xxx`)은 문서 제목으로 보존, H2+(`## 결론/사실`)는 분절 마커로 건너뜀——이전에는 명백히 본문인 `## 결론/사실` 이 기억 “이름”으로 표시됨。
38
+ - **투사 경로의 단일 사실원 `relPathFor()`**: `renderRecord`(디스크 기록), `records-meta` 의 `filePath`, 슬롯 `refViews.filePath` 세 곳이 동일 알고리즘으로 수렴。이전은 투사 측이 자체 정제 정규식을 옮겨 같은 기억에서 두 개의 다른 파일명이 나올 수 있었음——투사가 알려준 주소와 디스크의 실제 파일이 맞지 않는데, 이 세 곳이야말로 사람을 위해 같은 사실을 대조하는 자리다。`refViews` 는 `filePath`/`workspaceId` 도 노출하여 소비자는 `<workspaceId>/.agents/memory/<filePath>`(workspace 범위) 또는 투사 루트(global)로 절대 경로를 조립。
39
+ - **검색 패널 본문의 markdown 렌더**(client): RecordsTab 펼침 본문은 새로 생긴 `client/src/ui/MarkdownBody.tsx`(제목 계층 / 굵게 / 인라인 코드 / 목록, 외부 의존성 없음)를 거치며, `## 결론/사실` 이 맨 텍스트 줄로 보이지 않음。
40
+
41
+ - **기존 기억 재증류 성형(`redistill`)**: 독립 일괄 스캔 `src/redistill-backfill.ts`(wing/keyword 백필 미러: 단일비행/후대/유계/실패시 원 기록불변)가 **활성이고 골격 불비**인 L1 본문을 G2 유형별 “전인후과” 분절 형식으로 재배열하고, 동시에 `metadata.dsh_keywords` / `metadata.dsh_usage_scenario` 를 산출。新 엔드 `dsh-memory/redistill-backfill`(`{started,running,enabled,updated,skipped,failed` 즉시 반환), 패널(WingWheel)에 “재증류 성형” 버튼。설정 `redistill.enabled` **기본关**——关 시 성형 없음、content/FTS/투사 글자 그대로 동일(영 드리프트)。
42
+ - **품질 게이트**: 성형 후 `scoreSkeleton` 적중 수가 **엄격히 우수**할 때만 기입하고 성형 버전 `dsh_reshape_ver` 를 찍어(이후 회차 제외), 미달이면 원문을 보존하고 표시를 남기지 않아 다음 회차에 재시도 가능——“겉보기 형식”을 위해 거짓 라벨이나 날조를 일체 하지 않음。
43
+ - **활성만**: 퇴장/대체됨 기록은 대상 외。
44
+ - **본문 변경의 지속 기입 경로**: 성형 기입은 `L1Store.appendNew` 를 경유해 JSONL 사실원 추기 + 임베딩 재계산 + `l1_fts` 재인덱싱 + `.memory.md` 투사 덮어쓰기(exporter 는库에서 재렌더, 다르면 덮어씀)를 한 번에 완료。입구는 `vectorsReady()` 를 요구——임베딩 미준비 시 런 전체를 건너뛰어 벡터의 조용한 소실을 방지。
45
+
46
+ ### 수정
47
+
48
+ - **`client-session-recalled-bundle` 회귀 가드**: 0.25.0 에서 “본 대화召回” pill 렌더링을 dsh-brief-sidebar 에 넘긴 후, 이 테스트는 여전히 pill 에召回 문자열이 있다고 주장(발판 시 누락)。이제는 prime-memory pill 이召回를 **중복 렌더링하지 않음**을 주장하고, 투사 배선 주장은 보존。
49
+
12
50
  ## [0.25.0] — 2026-10-14
13
51
 
14
52
  ### 신규
package/CHANGELOG.md CHANGED
@@ -17,6 +17,45 @@
17
17
  - **建议状态(`longtask-hint-get`)以纯端点提供**:判定 = 上下文占用达 `contextThresholdPct` 或 todo 漂移达 `driftThreshold`(部署配置可调);配合 `longtask-compress-tail` 作手动压缩入口。
18
18
  - **撤下浮动球 UI(`client/src/longtask-fab.ts`;beta.1 起曾随包发布)**:悬浮建议面板的归属本在 context-compression 侧(「建议启动压缩」是其领域),pm 属误载——2026-10-02 用户裁定撤下。撤下不违座位契约(settings.section 恰一个 + 输入栏 pill,body 单例不占座位),但全应用级 overlay 会与其他插件的悬浮 UI 抢占屏幕角落;长任务开关与手动压缩入口待长任务定版时落记忆设置分节。
19
19
  - **`session-mode-set` 的 `mode` 转为可选**:与 `longTask` 二选一必传——纯切长任务开关不再被迫发送档位(旧客户端恒传 mode,行为不变)。响应 `mode` 改回当前生效值。
20
+
21
+ ## [0.26.1] — 2026-10-15
22
+
23
+ ### 新增
24
+
25
+ - **回填面板「自动恢复进度 + 扫描进度 + 诚实摘要」**:三个回填端点同时是「触发器 + 状态查询」,空闲时裸调用会**真的开跑**。新增请求体 `BackfillTriggerRequest { probe?: boolean }`——`probe:true` 只回当前快照、绝不启动;`BackfillSection` 挂载时以 resume 探测三个端点,在跑就接回轮询(离开概览区再回来不再需要手动重点)。`BackfillProgress` 加 `scanned/scanTotal`,扫描段显示「正在扫描存量 1252/1252 行…」;候选 0 时报「没有缺关键词的记录,无需回填」等**诚实摘要**(不再报“成功 0 条”被读成失败)。
26
+ - **三个批量动作「中止」开关(有界强杀)**:契约加 `cancel?: boolean`、`BackfillProgress.cancelled`;三个模块各带 `cancelXBackfill()`。中止**有界**:置信号后起一次性宽限表,到期 `runAbort.abort()` 当场捻断在飞请求;硬界由 `backfill.cancelGraceMs`(schema max 30_000、默认 15s)钳住——无论 YAML 怎么写都越不过。
27
+ - **技能工具组封印 `toolSkills`**:`memory_skill_*` 四件并入宿主封印机制(`reg('skills', …)`),与功能总开关 `skillsEnabled` 取 AND;两者都随 `loader/volatile-update` **热更**(面板拨开关即时注册/反注册,无需重启)。此前技能工具只在注册期判一次,开启后无条件常驻模型工具列表,且拨开关不热更。
28
+
29
+ ### 变更
30
+
31
+ - **全部 LLM 批处理统一「逐条串行」**:补齐 wing(`labelWingOne`/`tagOne`)、`pipeline/relabel.ts`、`room-review.ts`(`roomCandidateOne`)三处合并批——至此 5 个批处理站点全部「一个请求只装一条记忆、跑完一条再发下一条」(`concurrency` 默认 1)。依据:单条 35B 实测 ~2.6s,而一批多条一旦截断/空输出就带走整批。
32
+ - **整形排除判据改为「版本戳」**:`dsh_reshape_ver`(=2)+ `isReshapedAtCurrentVersion(meta, content)`——**仅成功写回**时盖戳;未过闸留原文**不打标记**、下轮仍可重试(旧布尔 `dsh_reshape_tried` 只作存量兼容读取)。整形规则升级时递增版本号即自动重跑旧产物。
33
+ - **`backfill.maxRecords` 默认 300**(此前误设 40,导致真机 251 条目标一次只动 40)。
34
+
35
+ ### 修复
36
+
37
+ - **轮询裸调用死循环(“不开始”真因)**:UI 的 tick 对「触发器 + 状态查询」二合一端点用裸调用 → 空闲时每 3s 又开一轮,面板永远停在“正在扫描”。tick 改 `{ probe: true }`。
38
+ - **批扫全部空输出真因:把思考模型当直答模型用**:`LlmCallOptions` 加**调用级 `reasoningEffort` 覆盖**,五个批扫标注器显式传 `'off'`——此前路由档位为 `""`(自动)时思考模型默认开思考,预算被 reasoning 吃光、正文 0 字符(真机 162 次失败中 156 次是 `local-9b` 空输出)。
39
+ - **`client/src/ui/MarkdownBody.tsx` typecheck 红**:0.26.0 带入的 4 处 `string | undefined`(正则捕获组)在 `noUncheckedIndexedAccess` 下报错,加非空断言。
40
+
41
+ ## [0.26.0] — 2026-10-14
42
+
43
+ ### 新增
44
+
45
+ - **存量记忆重蒸馏整形(`redistill`)**:新增独立回填批扫 `src/redistill-backfill.ts`(镜像 wing/keyword 回填:单飞/后台/有界/失败原记录不动),把**活跃且骨架不齐备**的存量 L1 正文按 G2 分类型「前因后果」骨架重排,并顺带产 `metadata.dsh_keywords` / `metadata.dsh_usage_scenario`。新端点 `dsh-memory/redistill-backfill`(立即返回 `{started,running,enabled,updated,skipped,failed}`)、面板(WingWheel)加「重蒸馏整形」按钮。配置 `redistill.enabled` **默认关**——关时不整形、content/FTS/投射逐字不变(零漂移)。
46
+ - **质量闸**:只有整形后 `scoreSkeleton` 骨架命中数**严格更优**才写回,并盖整形版本戳 `dsh_reshape_ver`(下轮不再入列);未过闸的留原文**不打标记**、下轮仍可重试——绝不为「看起来有格式」贴假标签或编造。
47
+ - **只碰活跃**:已退场/被取代的记录不补做优化。
48
+ - **改正文的持久写回路径**:整形写回走 `L1Store.appendNew`,一次完成 JSONL 事实源追加 + 重算 embedding + `l1_fts` 重索引 + `.memory.md` 投射覆盖(导出器按库重渲染、不同即覆盖),保证「库改了、检索/向量/磁盘同步跟上」。入口要求 `vectorsReady()`——嵌入未就绪时整轮跳过,避免改正文静默丢向量。
49
+
50
+ - **`dsh-memory/records-meta` 端点**:按 `record_id` 批量(≤50)取单条记忆的全量元数据与正文(name/type/priority/content/maturity/createdAt/updatedAt/scene/scope/workspaceId/keywords/usageScenario/filePath/retired),走 `stores.l1.getByIds` 直读 SQLite。记忆查看器因此**不需要读 `.memory.md` 文件**——旧方案(伪装文件名的 fileViewer)会被宿主 EditorHost 当物理路径 fsRead,必报 ENOENT。契约入 `contract.ts`,端点集 62→63(`tests/contract-keys.test.ts` 同步钉数)。
51
+ - **展示标题不再从正文猜**:召回投影 `memoryRecall` 与槽位 `refViews` 的 title 优先取 `name`(名称蒸馏产出的短标题),无 `name` 才回落正文首行。`firstLine()` 改为 markdown 感知:H1(`# xxx`)是文档标题保留作标题,H2+(`## 结论/事实`)是分节标记一律跳过——此前 `## 结论/事实` 会被当成「记忆名称」显示,而它明显是正文。
52
+ - **投射路径单一事实源 `relPathFor()`**:`renderRecord` 写盘、`records-meta` 的 `filePath`、槽位 `refViews.filePath` 三处收敛到同一算法。此前投影里另抄了一份清洗正则,同一条记忆会算出两个不同文件名——「投影给的地址」与「磁盘上的真文件」对不上,而这三处本就是给人核对同一事实的。`refViews` 同时透出 `filePath`/`workspaceId`,消费方按 `<workspaceId>/.agents/memory/<filePath>`(workspace 范围)或投射根(global)拼绝对地址。
53
+ - **检索面板正文 markdown 化**(client):RecordsTab 展开详情走新增的 `client/src/ui/MarkdownBody.tsx`(heading 层级 / 粗体 / 行内代码 / 列表,零外部依赖),`## 结论/事实` 不再是一行裸文本。
54
+
55
+ ### 修复
56
+
57
+ - **`client-session-recalled-bundle` 回归守卫**:0.25.0 把「本会话召回」pill 渲染移交 dsh-brief-sidebar 后,该测试仍断言 pill 含召回串(发版时漏更)。改为断言 prime-memory pill **不**再重复渲染召回,保留投影接线断言。
58
+
20
59
  ## [0.25.0] — 2026-10-14
21
60
 
22
61
  ### 新增