@yolk_vat-y/dsh-project-memory 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,51 @@
1
1
  # Changelog
2
2
 
3
- ## 0.5.0 (unreleased)
3
+ ## 0.5.2 (2026-09-09)
4
+
5
+ ### Changed (file hotspot + resume info)
6
+
7
+ - **Task files now stay in "hot" order (writes first)**: `task.files` is kept sorted by recency-weighted activity — written/edited files always precede read-only ones, ordered by last-write then last-read time; a read never outranks a written file. Every surface reads `task.files` in order, so `select_task` resume, `query_memory` task rows, `/tasks` and the task panel all surface the hottest files first. Per-file metadata (`fileMeta`: `lastWriteAt` / `lastReadAt` / touch count `n`) is additive and old tasks stay compatible.
8
+ - **Resident task card shows ≤3 "editing now" files**: the silent-injection task block appends `编辑中: a.ts, b.ts` for recently written files (`autoContext.editedMax`, default 3).
9
+ - **No echo of the task card when the model maintains its own task list**: when the most recent progress is the model's own `todo_write` with no newer human message (`lastTodoAt > lastHumanAt`), the resident task card is suppressed (saves tokens) while relevant project/global insight injection is kept. `autoContext.skipEchoSelfTodo` toggles it (default on).
10
+
11
+ ### Changed (cold-index I/O)
12
+
13
+ - **Single-read indexing — cold index ~4× faster**: every indexing path read each file twice (`await sha256OfFile()` for the hash, then `readFileSync()` for the body) and paid a per-file `async readFile` thread-pool round-trip. New `readFileForIndex()` does one synchronous read and hands the same buffer to both `createHash('sha256')` and `toString('utf8')`; `index_repo`, `watch`, and lazy read-on-observe all use it. Unchanged files are still hashed without decoding. Synthetic 5,000-file corpus (WSL2, Node 24): cold index 1,093 ms → 272 ms (4.0×); the `read+hash` phase drops from 70% to 11% of the pipeline. Tests green.
14
+
15
+ ### Fixed (host-contract + watch robustness)
16
+
17
+ - **`agent/pre-step` could still crash every step** — the listener called `next()` outside any guard, so a missing/renamed continuation rejected it (`next is not a function`, the exact 0.5.1 failure mode); and when a downstream listener returned `undefined`, the plugin forwarded that `undefined`, making the host read `decision.kind` off `undefined` and abort the step. Both now degrade to a valid `enter` decision built from `payload.messages`; genuine downstream errors still propagate. Regression test `test/host-contract.test.mjs`.
18
+ - **Watch poll storms** — `start(NaN)` / `start(undefined)` produced `setInterval(fn, NaN)`, which Node silently rewrites to a 1 ms interval, saturating the event loop so the agent stopped responding; invalid intervals now fall back to 15 s. `poll()` had no re-entrancy guard, so a slow poll (large repo, or doc LLM summaries) overlapped the next tick and stacked concurrent indexing; a re-entrancy flag now skips overlapping ticks.
19
+ - **`WatchManager.restorePersisted()` dropped persisted watch roots** — it ignored `load()`'s return value, so on a `storeCache` hit it iterated the empty new instance instead of the loaded one; it now uses the returned store.
20
+
21
+ ### Changed (injection + cache hygiene)
22
+
23
+ - **Removed the unused `wrapLlmStream` helper** — silent injection is wired solely through the host's `agent/pre-step` seam; the `llm.stream` wrapper could never intercept the host's internal reference, so it was dead code (and its tests went with it).
24
+ - **Per-session injection fingerprint** — the "already injected" fingerprint was one shared value, so concurrent sessions could suppress each other's injection; it is now keyed per session (bounded).
25
+ - **Injection no longer re-fires on hotspot churn** — the dedupe fingerprint now covers only stable content (task title/steps/insights plus matched insights); the volatile `编辑中` file list still appears in the injected block but no longer triggers a re-injection on every file write.
26
+ - **Bounded store cache, synchronous effect** — `ProjectMemoryStore`'s cross-project cache is capped so long-running multi-project use cannot grow without bound; the optional `autoIndexOnFirstUse` effect is now a proper synchronous disposer instead of an async callback.
27
+
28
+ ### Files
29
+ - src/setup/taskbridge.js, src/auto-inject.js, src/util/fs.js, src/store.js, src/index.js, src/tools/index-repo.js, src/watch.js, src/lazy.js, test/auto-inject.test.mjs, test/host-contract.test.mjs, package.json, README.md, README.zh-CN.md
30
+
31
+ ## 0.5.1 (2026-09-08)
32
+
33
+ ### Fixed
34
+
35
+ - **Silent injection could crash every agent step (`next is not a function`)** — the `agent/pre-step` listener registered with the host waterfall arguments reversed (it consumed the event payload as the continuation, then called it). Signature corrected to the host contract `(payload, next)`; project root and session id now come from `payload.agent.session` (`header.cwd` / `id`).
36
+ - **Silent injection crashed with `Cannot read properties of undefined (reading 'kind')`** — injected `[Memory Inject]` messages were bare `{ role, content }` objects, but the host reads `message.source.kind` while assembling requests. Injection now builds full host messages via `createUserMessage` with a plugin `source` (`form: notice`, the same pattern as the host's own plan-mode narration).
37
+ - Both paths stay inert on any error or missing session cwd: they return the host's default decision and never break the request.
38
+
39
+ ### Changed (task panel editing)
40
+
41
+ - **Click vs. double-click discrimination on card heads**: single-click expand/collapse waits ~250 ms to rule out a double-click, so double-click-to-rename no longer fights layout shifts (the first click no longer toggles before the second click lands, and a double-click no longer toggles twice).
42
+ - **Auto-growing inline editors**: step and task-title editing switched from single-line inputs to content-sized textareas (capped, then internal scroll); Enter commits, Shift+Enter inserts a newline, Esc cancels.
43
+ - Step content renders with `white-space: pre-wrap`, so committed multi-line text stays readable.
44
+
45
+ ### Files
46
+ - src/auto-inject.js, src/client/TaskComponents.tsx, src/client/TaskPanel.module.css, client/client.js (+ map), package.json
47
+
48
+ ## 0.5.0 (2026-09-07)
4
49
 
5
50
  ### Added — v0.5 tiered insight memory (lessons / decisions / procedures)
6
51
 
package/README.md CHANGED
@@ -16,7 +16,7 @@ The workflow panel is collapsible, automatically adapts to dsh and theme plugin
16
16
  ![alt text](docs/images/image-4.png)
17
17
  ## Features
18
18
 
19
- - **TaskBridge: cross-session development tasks** — the plugin watches each session's live todo list (`todo_write` events) and file reads (`tool/call`): progress snapshots (`steps`) and touched files sync into durable per-project task entities. An unbound session that writes a todo auto-creates a task. New sessions continue by `list_tasks` → `select_task` (bind / rename / unarchive); `query_memory` gains `type: 'task'` and appends a task-count hint to `type: 'all'` results. The user-side `/tasks` command shows the task stack, step progress, involved files, and the current session binding. Titles are chosen by the model via `select_task(title=…)` (fallback: the part of your message after the last colon). Capacity is project-size adaptive (`fileCount/20`, clamped 5–100). Storage: `.dsh-project-memory/tasks.json` + `binding.json`. Auto-sync requires a dsh build with session events + `todo_write` (verified on 0.1.2-alpha.x); on older hosts the task tools still work as a plain record list.
19
+ - **TaskBridge: cross-session development tasks** — the plugin watches each session's live todo list (`todo_write` events) and file reads (`tool/call`): progress snapshots (`steps`) and touched files sync into durable per-project task entities. An unbound session that writes a todo auto-creates a task. Associated files are kept in **recency-weighted order (written/edited first; a read never outranks a written file)** so a resumed session sees at a glance where to look. New sessions continue by `list_tasks` → `select_task` (bind / rename / unarchive); `query_memory` gains `type: 'task'` and appends a task-count hint to `type: 'all'` results. The user-side `/tasks` command shows the task stack, step progress, involved files, and the current session binding. Titles are chosen by the model via `select_task(title=…)` (fallback: the part of your message after the last colon). Capacity is project-size adaptive (`fileCount/20`, clamped 5–100). Storage: `.dsh-project-memory/tasks.json` + `binding.json`. Auto-sync requires a dsh build with session events + `todo_write` (verified on 0.1.2-alpha.x); on older hosts the task tools still work as a plain record list.
20
20
  - **Task Panel (v0.4.2+): Floating task panel in dsh web** — built on the real dsh web 0.1.2-rc.1 client plugin contract (cordis inject + apply, registered into host `shell.overlay` slot). Draggable cards show steps/files (click to copy path); collapse to a draggable mini-bar; hide completely (summon with `/task` / `/tasks`). Render errors have error boundaries — panel crash no longer takes down the host.
21
21
  - **Task Panel Behavior** —
22
22
  - **Default hidden**: panel does not show on dsh web startup
@@ -38,7 +38,7 @@ The workflow panel is collapsible, automatically adapts to dsh and theme plugin
38
38
  - **blindSpots-aware recall** — document summaries carry a `blindSpots` field (what the summary explicitly does NOT cover). When a query hits a blind spot, `query_memory` appends a warning pointing the model to read the source file, preventing hallucination from partial summaries.
39
39
  - **Experience notes** — problems → solutions; similar problems supersede instead of duplicating, and notes are returned only when a search matches. The note store is bounded: capacity scales with project size (clamped to 100–2000), and the oldest notes are pruned when the limit is exceeded. **Supersede tightened to bidirectional 0.7 overlap** (was 0.6); **experience `problem` field now participates in CJK phrase boost** for long-tail query recall.
40
40
  - **v0.5 tiered insight memory (lessons / decisions / procedures)** — one `insight` entity across three scopes: `task` (private drafts in `tasks.json`), `project` (`.dsh-project-memory/insights.json`), `global` (`~/.config/dsh-project-memory/global.json`). `save_lesson` writes any scope; dedupe is bidirectional token overlap ≥ 0.7 (merge) with a 0.65–0.7 reinforce band; **promotion is a scope change, not a copy** — 2 tasks hitting the same insight promote it to project, 3+ to global. Archive is soft (`archived`), decay/capacity prune archived entries only; writes are filtered for secret/token-shaped content. LLM **reflection is off by default** and only ever writes task-level drafts (`source: reflect`) on task switch-away/archive. Panel gains a Task / Project / Global memory view with approve, promote/demote, archive/restore, delete, edit and a create form (procedures can carry an “as Skill” trigger). Old `experience.json` notes are imported into `insights.json` once, non-destructively. Defaults & rationale: `PLAN-v0.5.0.md`.
41
- - **Streaming TF + IDF caching** — query path caches IDF (term inverse frequency) per store version; on cache hit, single-pass streaming scores 20k entries in ~8 ms (5k files) / ~1 ms (1k files) with zero intermediate objects; write path is O(1) version bump.
41
+ - **Streaming TF + IDF caching** — query path caches IDF (term inverse frequency) per store version; on cache hit, single-pass streaming scores 20k entries in ~3 ms (5k files) / ~0.6 ms (1k files) with zero intermediate objects; write path is O(1) version bump.
42
42
  - **Lock-free sync transactions** — all writes (index / watch / remember / forget / watch_repo) go through synchronous transactions `store.commit(fn)`; fn succeeds then atomic write; JS single-threaded event loop guarantees no interleaving; `remember`/`forget` never blocked by watch re-indexing.
43
43
  - **Minimal dependencies** — pure JavaScript; the only runtime dependency is `pdfjs-dist` (PDF text extraction), no native builds required.
44
44
  - **Negligible overhead** — pure in-process operation; cold start <100 ms (5k files), typical project query median 2–3 ms (p99 < 7 ms); bottleneck is LLM summarization and PDF parsing, not the plugin.
@@ -49,16 +49,16 @@ The workflow panel is collapsible, automatically adapts to dsh and theme plugin
49
49
 
50
50
  | Scenario | Scale | Measured |
51
51
  |----------|-------|----------|
52
- | Full cold index | 5,000 files / 20k entries | 353 ms |
53
- | Cold load | 5,000 files | 82 ms |
52
+ | Full cold index | 5,000 files / 20k entries | 272 ms |
53
+ | Cold load | 5,000 files | 43 ms |
54
54
  | Hot lazy re-index (single file) | 5k files | median 2.3 ms / max 4.0 ms |
55
- | query_memory (cached) | 5k files / 20k entries | median 9.3 ms / p95 12.6 ms |
56
- | query_memory (cached) | 1k files / 4k entries | median 1.0 ms / p95 2.0 ms |
55
+ | query_memory (cached) | 5k files / 20k entries | median 3.0 ms / p95 5.9 ms |
56
+ | query_memory (cached) | 1k files / 4k entries | median 0.6 ms / p95 1.7 ms |
57
57
  | Full cold index | 10,000 files / 40k entries | 637 ms |
58
- | Cold load | 10,000 files | 144 ms |
58
+ | Cold load | 10,000 files | 108 ms |
59
59
  | Hot lazy re-index (single file) | 10k files | median 4.5 ms / max 10.2 ms |
60
60
 
61
- > Synthetic benchmark: generated code (~8 symbols/file), Node 24, Linux native FS, SSD. Measures pure indexing overhead without LLM calls. query_memory benchmark uses IDF cache + precomputed searchText; first query after write rebuilds IDF (~150 ms), subsequent queries hit cache.
61
+ > Synthetic benchmark: generated code (~4–5 symbols/file), Node 24, Linux, SSD. Measures pure indexing overhead without LLM calls. query_memory benchmark uses IDF cache + precomputed searchText; first query after write rebuilds IDF (~150 ms), subsequent queries hit cache.
62
62
 
63
63
  ### Real Project Storage
64
64
 
@@ -265,7 +265,7 @@ These are deliberate scope choices.
265
265
  | `enableTypeScript` | true | set `false` to disable L2 TS enhancement entirely (L1 regex only) |
266
266
  | `insight.*` | dedupOverlap `0.7` · reinforceBand `0.65` · maxProject `100` · maxGlobalProcedures `200` · promoteConfidence `0.7` · globalPromoteTasks `3` · decayDays `90` · `globalFile` (auto) | v0.5 insight dedupe / reinforce / promotion / capacity / archive settings |
267
267
  | `reflection.enabled` | false | v0.5 LLM reflection, **draft-only at task level** (fires on task switch-away / archive). `cooldownMs` `1800000`, `maxLessonsPerReflect` `3`, `maxDecisionsPerReflect` `2` |
268
- | `autoContext.enabled` | true | v0.5 silent injection wrapper (entry block + relevance). Inert (full passthrough) until the host exposes a resolvable session cwd; `maxTokens` `400` |
268
+ | `autoContext.enabled` | true | v0.5 silent injection wrapper (entry block + relevance). Inert (full passthrough) until the host exposes a resolvable session cwd; `maxTokens` `400`, `editedMax` `3` (how many recently-written "editing now" files the resident task card shows), `skipEchoSelfTodo` `true` (don't echo the task card back when the model itself maintains the task list with no newer human message; relevant insights still inject) |
269
269
 
270
270
  ### Toggling features
271
271
 
@@ -301,7 +301,7 @@ These commands are for **maintaining the plugin code** — regular users do not
301
301
 
302
302
  ```bash
303
303
  npm install
304
- npm test # 211 tests (166 core + 11 TaskBridge + 11 insight-store + 5 reflection + 9 auto-inject + 9 insight-actions)
304
+ npm test # 215 tests (166 core + 11 TaskBridge + 11 insight-store + 5 reflection + 7 auto-inject + 9 insight-actions + 6 host-contract)
305
305
  ```
306
306
 
307
307
  ## License
package/README.zh-CN.md CHANGED
@@ -17,7 +17,7 @@
17
17
  ![alt text](docs/images/image-4.png)
18
18
  ## 特性
19
19
 
20
- - **TaskBridge:跨会话开发任务** — 监听会话内宿主 `todo_write` 维护的任务清单与 `tool/call` 读文件:进度快照(steps)与触碰文件自动同步进跨会话的任务实体。未绑定会话写 todo 时自动建档。新会话通过 `list_tasks` → `select_task`(绑定/改名/解归档)续接;`query_memory` 新增 `type:'task'`,`type:'all'` 结果尾部附任务计数提示。用户侧 `/tasks` 命令展示任务栈、步骤进度、涉及文件与当前会话绑定。标题由模型经 `select_task(title=…)` 命名(回退:取消息最后一个「:」后的任务段)。容量随项目体积自适应(fileCount/20,clamp 5–100)。存储:`.dsh-project-memory/tasks.json` + `binding.json`。自动同步需含会话事件与 `todo_write` 的 dsh(0.1.2-alpha.x 实测);旧宿主下降级为纯记录。
20
+ - **TaskBridge:跨会话开发任务** — 监听会话内宿主 `todo_write` 维护的任务清单与 `tool/call` 读文件:进度快照(steps)与触碰文件自动同步进跨会话的任务实体。未绑定会话写 todo 时自动建档。关联文件按**最近活跃排序(写过/编辑的排最前,任何读取不越过写过文件)**,续接时一眼看到该看哪些文件。新会话通过 `list_tasks` → `select_task`(绑定/改名/解归档)续接;`query_memory` 新增 `type:'task'`,`type:'all'` 结果尾部附任务计数提示。用户侧 `/tasks` 命令展示任务栈、步骤进度、涉及文件与当前会话绑定。标题由模型经 `select_task(title=…)` 命名(回退:取消息最后一个「:」后的任务段)。容量随项目体积自适应(fileCount/20,clamp 5–100)。存储:`.dsh-project-memory/tasks.json` + `binding.json`。自动同步需含会话事件与 `todo_write` 的 dsh(0.1.2-alpha.x 实测);旧宿主下降级为纯记录。
21
21
  - **Task Panel(v0.4.2+):dsh web 浮动任务面板** — 按 dsh web 0.1.2-rc.1 真实 client 插件契约落地(cordis inject + apply,注册进宿主 `shell.overlay` 槽)。卡片可拖拽、展开查看步骤/文件(点击复制路径);折叠为可拖拽顶部迷你条;可彻底隐藏(输入 `/task` / `/tasks` 唤起)。渲染错误有边界兜底,面板崩溃不再拖垮宿主。
22
22
  - **任务面板行为** —
23
23
  - **默认隐藏**:dsh web 启动时面板不显示
@@ -39,7 +39,7 @@
39
39
  - **blindSpots 感知召回** — 文档摘要携带 `blindSpots` 字段(明确说明摘要未覆盖的内容)。查询命中盲区时,`query_memory` 追加提示引导模型去读原文,防止半截摘要误导。
40
40
  - **经验笔记** — 记录问题 → 方案;相似问题覆盖而非重复;笔记仅在检索命中时返回。笔记数量有界:容量随项目规模伸缩(钳制在 100–2000),超限时淘汰最旧的笔记。**覆盖阈值收紧为双向 0.7 重叠**(原 0.6);**经验 `problem` 字段现参与 CJK 短语加分**,提升长尾问句召回。
41
41
  - **v0.5 分层 insight 记忆(教训 / 决策 / 流程)** — 一个 `insight` 实体贯穿三级:`task`(任务私有草稿,存 `tasks.json`)、`project`(`.dsh-project-memory/insights.json`)、`global`(`~/.config/dsh-project-memory/global.json`)。`save_lesson` 三级可写;去重采用双向 token overlap ≥ 0.7(合并)外加 0.65–0.7 近重复强化带;**提升 = scope 字段变更而非复制**——同一 insight 被 2 个任务命中升 project、3+ 升 global。归档为软删(`archived`),容量/衰减只清归档区;写盘前过滤密钥/token 形态内容。LLM **反思默认关闭**,且只产任务级草稿(`source: reflect`,触发于任务切走/归档时)。面板新增 Task / Project / Global 记忆视图:审核、提升/降级、归档/恢复、删除、编辑与新建表单(procedure 可带"作为 Skill"触发关键词)。旧 `experience.json` 笔记**非破坏**导入 `insights.json` 一次。默认值与设计说明见 `PLAN-v0.5.0.md`。
42
- - **流式 TF + IDF 缓存** — 查询路径按存储版本缓存 IDF(词逆频率);命中时单次流式遍历 20k 条目仅需 ~8 ms(5k 文件) / ~1 ms(1k 文件),零中间对象;写入路径仅 O(1) 版本号递增。
42
+ - **流式 TF + IDF 缓存** — 查询路径按存储版本缓存 IDF(词逆频率);命中时单次流式遍历 20k 条目仅需 ~3 ms(5k 文件) / ~0.6 ms(1k 文件),零中间对象;写入路径仅 O(1) 版本号递增。
43
43
  - **无锁同步事务** — 不采用锁:所有写入(index / watch / remember / forget / watch_repo)统一走同步事务 `store.commit(fn)`,fn 成功后才一次落盘;JS 单线程事件循环保证事务间不交错,`remember`/`forget` 不会被 watch 重索引阻塞排队。多实例并发写入同一项目存储时,得益于 CAS 幂等更新与原子提交,自然具备幂等性,无数据损坏风险。
44
44
  - **依赖极简** — 纯 JavaScript;唯一运行时依赖是 `pdfjs-dist`(PDF 文本提取),无需原生构建。
45
45
  - **开销可忽略** — 纯进程内操作;冷启动 <100 ms(5k 文件),典型项目查询中位数 2–3 ms(p99 < 7 ms);瓶颈在 LLM 摘要与 PDF 解析,插件本身不阻塞。
@@ -50,16 +50,16 @@
50
50
 
51
51
  | 场景 | 规模 | 实测 |
52
52
  |------|------|------|
53
- | 批量冷记忆构建 | 5,000 文件 / 20k 条目 | 353 ms |
54
- | 冷加载 | 5,000 文件 | 82 ms |
53
+ | 批量冷记忆构建 | 5,000 文件 / 20k 条目 | 272 ms |
54
+ | 冷加载 | 5,000 文件 | 43 ms |
55
55
  | 热路径懒记忆 | 单文件重记忆+落盘 | 中位数 2.3 ms / 最大 4.0 ms (5k) |
56
- | query_memory (缓存命中) | 5k 文件 / 20k 条目 | 中位数 9.3 ms / p95 12.6 ms |
57
- | query_memory (缓存命中) | 1k 文件 / 4k 条目 | 中位数 1.0 ms / p95 2.0 ms |
56
+ | query_memory (缓存命中) | 5k 文件 / 20k 条目 | 中位数 3.0 ms / p95 5.9 ms |
57
+ | query_memory (缓存命中) | 1k 文件 / 4k 条目 | 中位数 0.6 ms / p95 1.7 ms |
58
58
  | 批量冷记忆构建 | 10,000 文件 / 40k 条目 | 637 ms |
59
- | 冷加载 | 10,000 文件 | 144 ms |
59
+ | 冷加载 | 10,000 文件 | 108 ms |
60
60
  | 热路径懒记忆 | 单文件重记忆+落盘 | 中位数 4.5 ms / 最大 10.2 ms (10k) |
61
61
 
62
- > 合成基准:生成代码(~8 符号/文件),Node 24,Linux 文件系统,SSD。测量纯索引开销,不含 LLM 调用。query_memory 基准使用 IDF 缓存 + 预计算 searchText;写入后首次查询重建 IDF(~150 ms),后续查询命中缓存。
62
+ > 合成基准:生成代码(~4–5 符号/文件),Node 24,Linux 文件系统,SSD。测量纯索引开销,不含 LLM 调用。query_memory 基准使用 IDF 缓存 + 预计算 searchText;写入后首次查询重建 IDF(~150 ms),后续查询命中缓存。
63
63
 
64
64
  ### 真实项目存储体积
65
65
 
@@ -264,7 +264,7 @@ TaskPanel (Container)
264
264
  | `enableTypeScript` | true | 设为 `false` 彻底禁用 L2 TS 增强(仅保留 L1 正则) |
265
265
  | `insight.*` | dedupOverlap `0.7` · reinforceBand `0.65` · maxProject `100` · maxGlobalProcedures `200` · promoteConfidence `0.7` · globalPromoteTasks `3` · decayDays `90` · `globalFile`(自动) | v0.5 insight 去重/强化/提升/容量/归档设置 |
266
266
  | `reflection.enabled` | false | v0.5 LLM 反思,**只写任务级草稿**(触发于任务切走/归档)。`cooldownMs` `1800000`、`maxLessonsPerReflect` `3`、`maxDecisionsPerReflect` `2` |
267
- | `autoContext.enabled` | true | v0.5 静默注入包装(entry 常驻块 + relevance)。宿主无法解析会话 cwd 时完全透传(零副作用);`maxTokens` `400` |
267
+ | `autoContext.enabled` | true | v0.5 静默注入包装(entry 常驻块 + relevance)。宿主无法解析会话 cwd 时完全透传(零副作用);`maxTokens` `400`、`editedMax` `3`(resident 任务卡显示最近"编辑中"文件数)、`skipEchoSelfTodo` `true`(模型自己写/维护任务清单后、无新人类消息时不回声任务卡,省 token;相关 insights 仍注入) |
268
268
 
269
269
  ### 功能开关
270
270
 
@@ -300,7 +300,7 @@ dsh web --patch ./config.yml
300
300
 
301
301
  ```bash
302
302
  npm install
303
- npm test # 211 项测试(核心 166 + TaskBridge 11 + insight-store 11 + reflection 5 + auto-inject 9 + insight-actions 9)
303
+ npm test # 215 项测试(核心 166 + TaskBridge 11 + insight-store 11 + reflection 5 + auto-inject 7 + insight-actions 9 + host-contract 6)
304
304
  ```
305
305
 
306
306
  ## 许可证
package/client/client.js CHANGED
@@ -560,8 +560,8 @@ window.__ModuleLoader__.load({
560
560
  };
561
561
  }
562
562
  //#endregion
563
- //#region \0dsh-css:/home/sxt/project/dsh-project-memory/src/client/TaskPanel.module.css.mjs
564
- const css$1 = ".vZSZnG_panel{pointer-events:auto;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));border:1px solid var(--dsw-alias-border-l2,#7f7f7f4d);border-radius:14px;flex-direction:column;width:380px;max-width:calc(100vw - 24px);max-height:min(70vh,640px);font-family:inherit;animation:.18s ease-out vZSZnG_panelIn;display:flex;position:fixed;overflow:hidden;box-shadow:0 12px 32px #00000029,0 2px 8px #00000014}@keyframes vZSZnG_panelIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}.vZSZnG_dragHandle{cursor:grab;z-index:1;justify-content:center;align-items:center;height:12px;display:flex;position:absolute;top:0;left:0;right:0}.vZSZnG_dragHandle:active{cursor:grabbing}.vZSZnG_handleGrip{background:var(--dsw-alias-separator-primary,#7f7f7f59);opacity:.8;border-radius:2px;width:32px;height:3px}.vZSZnG_header{border-bottom:1px solid var(--dsw-alias-border-l1,#7f7f7f2e);-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;gap:8px;padding:10px 10px 8px 14px;display:flex}.vZSZnG_headerLeft{align-items:center;gap:6px;min-width:0;display:flex}.vZSZnG_headerIcon{color:var(--dsw-alias-label-secondary);flex:none}.vZSZnG_headerTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:13px;font-weight:600;line-height:20px}.vZSZnG_headerRight{align-items:center;gap:2px;margin-left:auto;display:flex}.vZSZnG_counts{color:var(--dsw-alias-label-tertiary);white-space:nowrap;margin-right:4px;font-size:11px;line-height:16px}.vZSZnG_boundBadge{color:var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-button-primary-fill,#3a6ef5);white-space:nowrap;border-radius:7px;flex:none;padding:0 6px;font-size:10px;line-height:14px}.vZSZnG_notice{color:var(--dsw-alias-state-warn-label,#b7791f);background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1a);word-break:break-all;border-radius:8px;margin:8px 12px 0;padding:6px 10px;font-size:12px;line-height:18px}.vZSZnG_taskList{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,transparent) transparent;flex-direction:column;gap:6px;padding:8px;display:flex;overflow-y:auto}.vZSZnG_emptyState{text-align:center;color:var(--dsw-alias-label-secondary);padding:36px 24px 30px}.vZSZnG_emptyTitle{color:var(--dsw-alias-label-primary);margin:0 0 6px;font-size:13px;font-weight:600}.vZSZnG_emptyDesc{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:20px}.vZSZnG_syncHint{color:var(--dsw-alias-label-dimmed);justify-content:center;align-items:center;gap:6px;margin:10px 0 0;font-size:11px;display:flex}.vZSZnG_card{border:1px solid var(--dsw-alias-border-l1,#7f7f7f29);background:var(--dsw-alias-bg-base,transparent);border-radius:10px;transition:border-color .15s;overflow:hidden}.vZSZnG_cardBound{border-color:var(--dsw-alias-border-l3,#7f7f7f59);box-shadow:inset 0 0 0 .5px var(--dsw-alias-border-l3,transparent)}.vZSZnG_cardHead{cursor:pointer;text-align:left;background:0 0;border:none;justify-content:space-between;align-items:center;gap:8px;width:100%;padding:8px 10px 7px;display:flex}.vZSZnG_cardHead:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_cardTitleRow{align-items:center;gap:6px;min-width:0;display:flex}.vZSZnG_cardTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;line-height:20px;overflow:hidden}.vZSZnG_cardMeta{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:8px;display:flex}.vZSZnG_progressText{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}.vZSZnG_updated{color:var(--dsw-alias-label-dimmed);font-size:10px;line-height:14px}.vZSZnG_chevron{color:var(--dsw-alias-label-tertiary);display:inline-flex}.vZSZnG_track{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f);border-radius:1px;height:2px;margin:0 10px 6px;overflow:hidden}.vZSZnG_trackFill{background:var(--dsw-alias-label-primary-bluish,var(--dsw-alias-button-primary-fill,#3a6ef5));border-radius:1px;height:100%;transition:width .2s}.vZSZnG_cardBody{border-top:1px solid var(--dsw-alias-border-l1,#7f7f7f24);overscroll-behavior:contain;max-height:300px;padding:8px 10px 10px;overflow-y:auto}.vZSZnG_section{margin-bottom:8px}.vZSZnG_section:last-child{margin-bottom:0}.vZSZnG_sectionLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;margin-bottom:4px;font-size:11px;line-height:16px;display:flex}.vZSZnG_sectionCount{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1a);color:var(--dsw-alias-label-tertiary);border-radius:6px;padding:0 5px;font-size:10px;line-height:14px}.vZSZnG_stepsList{flex-direction:column;gap:1px;margin:0;padding:0;list-style:none;display:flex}.vZSZnG_stepRow{align-items:flex-start;gap:7px;padding:3px 2px;display:flex}.vZSZnG_stepRow>svg{flex:none;margin-top:2px}.vZSZnG_stepDone{color:var(--dsw-alias-label-dimmed)}.vZSZnG_stepRun{color:var(--dsw-alias-label-primary-bluish,#3a6ef5)}.vZSZnG_stepPending{background:var(--dsw-alias-border-l3,#7f7f7f80);border-radius:50%;flex:none;width:8px;height:8px;margin:5px 3px}.vZSZnG_stepContent{color:var(--dsw-alias-label-secondary);word-break:break-word;font-size:12px;line-height:20px}.vZSZnG_stepContentDone{color:var(--dsw-alias-label-dimmed);text-decoration:line-through}.vZSZnG_stepContentRun{color:var(--dsw-alias-label-primary)}.vZSZnG_muted{color:var(--dsw-alias-label-dimmed);font-size:11px;line-height:18px}.vZSZnG_filesList{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.vZSZnG_fileRow{cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;align-items:center;gap:6px;width:100%;padding:3px 4px;display:flex}.vZSZnG_fileRow:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_fileDot{background:var(--dsw-alias-border-l3,#7f7f7f80);border-radius:50%;flex:none;width:5px;height:5px}.vZSZnG_filePath{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;text-align:left;direction:rtl;font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:11px;line-height:18px;overflow:hidden}.vZSZnG_fileLine{color:var(--dsw-alias-label-dimmed);flex:none;font-size:10px;line-height:18px}.vZSZnG_cardFooter{gap:6px;margin-top:8px;display:flex}@media (prefers-reduced-motion:reduce){.vZSZnG_panel{animation:none}.vZSZnG_trackFill,.vZSZnG_card{transition:none}}.vZSZnG_miniBar{border:1px solid var(--dsw-alias-border-l3,#7f7f7f4d);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));max-width:min(46vw,340px);height:34px;color:var(--dsw-alias-label-primary);cursor:grab;-webkit-user-select:none;user-select:none;pointer-events:auto;border-radius:999px;align-items:center;gap:7px;padding:0 12px;font-size:12px;line-height:18px;transition:background .15s,border-color .15s;display:inline-flex;position:fixed;box-shadow:0 6px 20px #00000024,0 1px 4px #00000014}.vZSZnG_miniBar:active{cursor:grabbing}.vZSZnG_miniBar:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_miniIcon{color:var(--dsw-alias-label-secondary);flex:none}.vZSZnG_miniText{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.vZSZnG_miniChevron{color:var(--dsw-alias-label-tertiary);flex:none}.vZSZnG_taskList{flex:auto;min-height:0}.vZSZnG_card{flex-direction:column;display:flex}.vZSZnG_boundaryFallback{border:1px solid var(--dsw-alias-border-l3,#7f7f7f4d);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary);cursor:pointer;pointer-events:auto;border-radius:999px;padding:4px 10px;font-size:12px;line-height:18px;position:fixed;top:64px;right:16px}.vZSZnG_stepToggle{cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;align-items:center;margin-top:2px;padding:0;display:inline-flex}.vZSZnG_stepToggle:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f)}.vZSZnG_stepToggle:disabled{cursor:default;opacity:.5}.vZSZnG_stepEditable{cursor:text;border-radius:3px}.vZSZnG_stepEditable:hover{outline:1px dashed var(--dsw-alias-border-l3,#7f7f7f66);outline-offset:1px}.vZSZnG_stepInput{box-sizing:border-box;width:auto;min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base,transparent);border:1px solid var(--dsw-alias-border-l3,#7f7f7f73);border-radius:5px;flex:auto;padding:0 5px;font-size:12px;line-height:20px}.vZSZnG_stepDragging{opacity:.3;background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_cardTitleEditable{cursor:text;border-radius:3px}.vZSZnG_cardTitleEditable:hover{outline:1px dashed var(--dsw-alias-border-l3,#7f7f7f66);outline-offset:1px}.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:color-mix(in srgb, var(--dsw-alias-bg-layer-2,#fff) 90%, transparent);-webkit-backdrop-filter:blur(18px)saturate(1.2);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2,#7f7f7f4d) 80%, transparent)}.vZSZnG_panel[data-theme=glass] .vZSZnG_card{background:color-mix(in srgb, var(--dsw-alias-bg-base,transparent) 84%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-border-l1,#7f7f7f29) 60%, transparent)}.vZSZnG_panel[data-theme=glass] .vZSZnG_cardBody{border-top-color:color-mix(in srgb, var(--dsw-alias-border-l1,#7f7f7f24) 60%, transparent)}@media (prefers-reduced-transparency:reduce){.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:var(--dsw-alias-bg-layer-2,#fff);-webkit-backdrop-filter:none;backdrop-filter:none}.vZSZnG_panel[data-theme=glass] .vZSZnG_card{background:var(--dsw-alias-bg-base,transparent)}}@supports not ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:var(--dsw-alias-bg-layer-2,#fff)}}.vZSZnG_panel[data-theme=brutal]{box-shadow:none;border-width:1px;border-radius:6px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_card{border:1px solid var(--dsw-alias-border-l3,#7f7f7f6b);background:var(--dsw-alias-bg-base,transparent);box-shadow:none;border-radius:3px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardHead{padding:7px 10px 6px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardTitle{letter-spacing:.01em;font-weight:650}.vZSZnG_panel[data-theme=brutal] .vZSZnG_headerTitle{letter-spacing:.03em}.vZSZnG_panel[data-theme=brutal] .vZSZnG_boundBadge{border-radius:2px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_track{border-radius:0;height:3px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_trackFill{border-radius:0}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardBody{border-top:1px solid var(--dsw-alias-border-l2,#7f7f7f3d)}.vZSZnG_panel[data-theme=mono] .vZSZnG_headerTitle,.vZSZnG_panel[data-theme=mono] .vZSZnG_counts,.vZSZnG_panel[data-theme=mono] .vZSZnG_cardTitle,.vZSZnG_panel[data-theme=mono] .vZSZnG_progressText,.vZSZnG_panel[data-theme=mono] .vZSZnG_stepContent,.vZSZnG_panel[data-theme=mono] .vZSZnG_filePath{font-family:ui-monospace,SF Mono,Menlo,Consolas,Liberation Mono,monospace}.vZSZnG_panel[data-theme=mono] .vZSZnG_cardTitle{font-size:12px}.vZSZnG_panel[data-theme=mono] .vZSZnG_stepContent{font-size:11.5px}.vZSZnG_panel[data-theme=mono] .vZSZnG_card{border-radius:5px}.vZSZnG_panel[data-theme=mono] .vZSZnG_trackFill{border-radius:0}.vZSZnG_panel[data-theme=mono] .vZSZnG_stepPending{border-radius:1px}.vZSZnG_panel[data-theme=mono] .vZSZnG_updated{letter-spacing:.02em}.vZSZnG_headerIconBtn{color:inherit;cursor:pointer;background:0 0;border:none;border-radius:0;flex:none;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.vZSZnG_headerIcon{color:var(--dsw-alias-label-secondary);flex:none;display:block}.vZSZnG_headerLeft{min-width:0}.vZSZnG_headerTitle{text-overflow:ellipsis;flex:0 auto;min-width:0;overflow:hidden}.vZSZnG_headerRight{min-width:0}.vZSZnG_counts{text-overflow:ellipsis;flex:none;max-width:45%;margin-left:auto;overflow:hidden}.vZSZnG_dragGhost{pointer-events:none;z-index:9999;transition:left 50ms,top 50ms;position:fixed;transform:translate(-50%,-50%)}.vZSZnG_dragGhostCard{background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));border:1px solid var(--dsw-alias-border-l3,#7f7f7f59);color:var(--dsw-alias-label-primary);white-space:nowrap;border-radius:8px;align-items:center;gap:8px;max-width:280px;padding:8px 12px;font-size:12px;line-height:20px;display:inline-flex;transform:scale(.9);box-shadow:0 8px 24px #0000002e,0 2px 8px #0000001a}.vZSZnG_dragGhostContent{text-overflow:ellipsis;word-break:break-word;flex:1;min-width:0;overflow:hidden}.vZSZnG_dragGhostClose{width:20px;height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:.6;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;margin-left:8px;font-size:16px;line-height:1;transition:opacity .15s,background .15s,color .15s;display:inline-flex}.vZSZnG_dragGhostClose:hover{opacity:1;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f)}.vZSZnG_dropTargetBefore{position:relative}.vZSZnG_dropTargetBefore:before{content:\"\";background:var(--dsw-alias-button-primary-fill,#3a6ef5);height:2px;box-shadow:0 0 4px var(--dsw-alias-button-primary-fill,#3a6ef5);border-radius:1px;animation:.8s ease-in-out infinite vZSZnG_dropPulse;position:absolute;top:-1px;left:0;right:0}.vZSZnG_dropTargetAfter{position:relative}.vZSZnG_dropTargetAfter:after{content:\"\";background:var(--dsw-alias-button-primary-fill,#3a6ef5);height:2px;box-shadow:0 0 4px var(--dsw-alias-button-primary-fill,#3a6ef5);border-radius:1px;animation:.8s ease-in-out infinite vZSZnG_dropPulse;position:absolute;bottom:-1px;left:0;right:0}@keyframes vZSZnG_dropPulse{0%,to{opacity:.6}50%{opacity:1}}.vZSZnG_memoryPane{flex-direction:column;gap:6px;max-height:480px;padding:8px 10px 10px;display:flex;overflow:auto}.vZSZnG_memToolbar{justify-content:space-between;align-items:center;gap:8px;display:flex}.vZSZnG_memCount{opacity:.75;font-size:12px;font-weight:600}.vZSZnG_memList{flex-direction:column;gap:6px;display:flex}.vZSZnG_memRow{border:1px solid var(--dsw-border-weak,#80808040);background:color-mix(in srgb, var(--dsw-bg-secondary,#f5f5f5) 45%, transparent);border-radius:6px;flex-direction:column;gap:4px;padding:6px 8px;display:flex}.vZSZnG_memRowArchived{opacity:.55}.vZSZnG_memRowMain{flex-direction:column;gap:2px;display:flex}.vZSZnG_memTitle{word-break:break-word;align-items:flex-start;gap:6px;font-size:13px;line-height:1.35;display:flex}.vZSZnG_kindBadge{background:var(--dsw-accent-soft,#5078ff26);color:var(--dsw-accent,#4d6bfe);border-radius:4px;flex:none;padding:0 5px;font-size:11px;line-height:1.5}.vZSZnG_memMeta{flex-wrap:wrap;align-items:center;gap:4px;display:flex}.vZSZnG_badge{background:color-mix(in srgb, var(--dsw-fg,#222) 8%, transparent);border-radius:4px;padding:0 5px;font-size:11px;line-height:1.5}.vZSZnG_badgeDraft{color:#b26a00;background:#ffb40029}.vZSZnG_badgeArchived{opacity:.8}.vZSZnG_memBody{opacity:.8;word-break:break-word;font-size:12px}.vZSZnG_memActs{flex-wrap:wrap;gap:4px;display:flex}.vZSZnG_memToolbarActs{align-items:center;gap:6px;display:flex}.vZSZnG_memNew{border:1px solid var(--dsw-border-weak,#80808040);border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.vZSZnG_memNewRow{align-items:center;gap:8px;display:flex}.vZSZnG_memLabel{opacity:.8;align-items:center;gap:6px;font-size:12px;display:inline-flex}.vZSZnG_memNew select,.vZSZnG_memNew textarea{box-sizing:border-box;width:100%}";
563
+ //#region \0dsh-css:src/client/TaskPanel.module.css.mjs
564
+ const css$1 = ".vZSZnG_panel{pointer-events:auto;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));border:1px solid var(--dsw-alias-border-l2,#7f7f7f4d);border-radius:14px;flex-direction:column;width:380px;max-width:calc(100vw - 24px);max-height:min(70vh,640px);font-family:inherit;animation:.18s ease-out vZSZnG_panelIn;display:flex;position:fixed;overflow:hidden;box-shadow:0 12px 32px #00000029,0 2px 8px #00000014}@keyframes vZSZnG_panelIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}.vZSZnG_dragHandle{cursor:grab;z-index:1;justify-content:center;align-items:center;height:12px;display:flex;position:absolute;top:0;left:0;right:0}.vZSZnG_dragHandle:active{cursor:grabbing}.vZSZnG_handleGrip{background:var(--dsw-alias-separator-primary,#7f7f7f59);opacity:.8;border-radius:2px;width:32px;height:3px}.vZSZnG_header{border-bottom:1px solid var(--dsw-alias-border-l1,#7f7f7f2e);-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;gap:8px;padding:10px 10px 8px 14px;display:flex}.vZSZnG_headerLeft{align-items:center;gap:6px;min-width:0;display:flex}.vZSZnG_headerIcon{color:var(--dsw-alias-label-secondary);flex:none}.vZSZnG_headerTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:13px;font-weight:600;line-height:20px}.vZSZnG_headerRight{align-items:center;gap:2px;margin-left:auto;display:flex}.vZSZnG_counts{color:var(--dsw-alias-label-tertiary);white-space:nowrap;margin-right:4px;font-size:11px;line-height:16px}.vZSZnG_boundBadge{color:var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-button-primary-fill,#3a6ef5);white-space:nowrap;border-radius:7px;flex:none;padding:0 6px;font-size:10px;line-height:14px}.vZSZnG_notice{color:var(--dsw-alias-state-warn-label,#b7791f);background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1a);word-break:break-all;border-radius:8px;margin:8px 12px 0;padding:6px 10px;font-size:12px;line-height:18px}.vZSZnG_taskList{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,transparent) transparent;flex-direction:column;gap:6px;padding:8px;display:flex;overflow-y:auto}.vZSZnG_emptyState{text-align:center;color:var(--dsw-alias-label-secondary);padding:36px 24px 30px}.vZSZnG_emptyTitle{color:var(--dsw-alias-label-primary);margin:0 0 6px;font-size:13px;font-weight:600}.vZSZnG_emptyDesc{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:20px}.vZSZnG_syncHint{color:var(--dsw-alias-label-dimmed);justify-content:center;align-items:center;gap:6px;margin:10px 0 0;font-size:11px;display:flex}.vZSZnG_card{border:1px solid var(--dsw-alias-border-l1,#7f7f7f29);background:var(--dsw-alias-bg-base,transparent);border-radius:10px;transition:border-color .15s;overflow:hidden}.vZSZnG_cardBound{border-color:var(--dsw-alias-border-l3,#7f7f7f59);box-shadow:inset 0 0 0 .5px var(--dsw-alias-border-l3,transparent)}.vZSZnG_cardHead{cursor:pointer;text-align:left;background:0 0;border:none;justify-content:space-between;align-items:center;gap:8px;width:100%;padding:8px 10px 7px;display:flex}.vZSZnG_cardHead:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_cardTitleRow{align-items:center;gap:6px;min-width:0;display:flex}.vZSZnG_cardTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;line-height:20px;overflow:hidden}.vZSZnG_cardMeta{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:8px;display:flex}.vZSZnG_progressText{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}.vZSZnG_updated{color:var(--dsw-alias-label-dimmed);font-size:10px;line-height:14px}.vZSZnG_chevron{color:var(--dsw-alias-label-tertiary);display:inline-flex}.vZSZnG_track{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f);border-radius:1px;height:2px;margin:0 10px 6px;overflow:hidden}.vZSZnG_trackFill{background:var(--dsw-alias-label-primary-bluish,var(--dsw-alias-button-primary-fill,#3a6ef5));border-radius:1px;height:100%;transition:width .2s}.vZSZnG_cardBody{border-top:1px solid var(--dsw-alias-border-l1,#7f7f7f24);overscroll-behavior:contain;max-height:300px;padding:8px 10px 10px;overflow-y:auto}.vZSZnG_section{margin-bottom:8px}.vZSZnG_section:last-child{margin-bottom:0}.vZSZnG_sectionLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;margin-bottom:4px;font-size:11px;line-height:16px;display:flex}.vZSZnG_sectionCount{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1a);color:var(--dsw-alias-label-tertiary);border-radius:6px;padding:0 5px;font-size:10px;line-height:14px}.vZSZnG_stepsList{flex-direction:column;gap:1px;margin:0;padding:0;list-style:none;display:flex}.vZSZnG_stepRow{align-items:flex-start;gap:7px;padding:3px 2px;display:flex}.vZSZnG_stepRow>svg{flex:none;margin-top:2px}.vZSZnG_stepDone{color:var(--dsw-alias-label-dimmed)}.vZSZnG_stepRun{color:var(--dsw-alias-label-primary-bluish,#3a6ef5)}.vZSZnG_stepPending{background:var(--dsw-alias-border-l3,#7f7f7f80);border-radius:50%;flex:none;width:8px;height:8px;margin:5px 3px}.vZSZnG_stepContent{color:var(--dsw-alias-label-secondary);word-break:break-word;white-space:pre-wrap;font-size:12px;line-height:20px}.vZSZnG_stepContentDone{color:var(--dsw-alias-label-dimmed);text-decoration:line-through}.vZSZnG_stepContentRun{color:var(--dsw-alias-label-primary)}.vZSZnG_muted{color:var(--dsw-alias-label-dimmed);font-size:11px;line-height:18px}.vZSZnG_filesList{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.vZSZnG_fileRow{cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;align-items:center;gap:6px;width:100%;padding:3px 4px;display:flex}.vZSZnG_fileRow:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_fileDot{background:var(--dsw-alias-border-l3,#7f7f7f80);border-radius:50%;flex:none;width:5px;height:5px}.vZSZnG_filePath{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;text-align:left;direction:rtl;font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:11px;line-height:18px;overflow:hidden}.vZSZnG_fileLine{color:var(--dsw-alias-label-dimmed);flex:none;font-size:10px;line-height:18px}.vZSZnG_cardFooter{gap:6px;margin-top:8px;display:flex}@media (prefers-reduced-motion:reduce){.vZSZnG_panel{animation:none}.vZSZnG_trackFill,.vZSZnG_card{transition:none}}.vZSZnG_miniBar{border:1px solid var(--dsw-alias-border-l3,#7f7f7f4d);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));max-width:min(46vw,340px);height:34px;color:var(--dsw-alias-label-primary);cursor:grab;-webkit-user-select:none;user-select:none;pointer-events:auto;border-radius:999px;align-items:center;gap:7px;padding:0 12px;font-size:12px;line-height:18px;transition:background .15s,border-color .15s;display:inline-flex;position:fixed;box-shadow:0 6px 20px #00000024,0 1px 4px #00000014}.vZSZnG_miniBar:active{cursor:grabbing}.vZSZnG_miniBar:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_miniIcon{color:var(--dsw-alias-label-secondary);flex:none}.vZSZnG_miniText{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.vZSZnG_miniChevron{color:var(--dsw-alias-label-tertiary);flex:none}.vZSZnG_taskList{flex:auto;min-height:0}.vZSZnG_card{flex-direction:column;display:flex}.vZSZnG_boundaryFallback{border:1px solid var(--dsw-alias-border-l3,#7f7f7f4d);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary);cursor:pointer;pointer-events:auto;border-radius:999px;padding:4px 10px;font-size:12px;line-height:18px;position:fixed;top:64px;right:16px}.vZSZnG_stepToggle{cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;align-items:center;margin-top:2px;padding:0;display:inline-flex}.vZSZnG_stepToggle:hover{background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f)}.vZSZnG_stepToggle:disabled{cursor:default;opacity:.5}.vZSZnG_stepEditable{cursor:text;border-radius:3px}.vZSZnG_stepEditable:hover{outline:1px dashed var(--dsw-alias-border-l3,#7f7f7f66);outline-offset:1px}.vZSZnG_stepInput{box-sizing:border-box;width:auto;min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base,transparent);border:1px solid var(--dsw-alias-border-l3,#7f7f7f73);border-radius:5px;flex:auto;padding:0 5px;font-size:12px;line-height:20px}.vZSZnG_editorArea{box-sizing:border-box;min-width:0;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base,transparent);border:1px solid var(--dsw-alias-border-l3,#7f7f7f73);resize:none;vertical-align:middle;cursor:text;white-space:pre-wrap;word-break:break-word;border-radius:5px;flex:1;margin:0;padding:2px 5px;font-family:inherit;font-size:12px;line-height:20px;overflow:hidden}.vZSZnG_editorArea:focus{border-color:var(--dsw-alias-border-l3,#7f7f7f99);outline:none}.vZSZnG_stepDragging{opacity:.3;background:var(--dsw-alias-interactive-bg-hover,#7f7f7f14)}.vZSZnG_cardTitleEditable{cursor:text;border-radius:3px}.vZSZnG_cardTitleEditable:hover{outline:1px dashed var(--dsw-alias-border-l3,#7f7f7f66);outline-offset:1px}.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:color-mix(in srgb, var(--dsw-alias-bg-layer-2,#fff) 90%, transparent);-webkit-backdrop-filter:blur(18px)saturate(1.2);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2,#7f7f7f4d) 80%, transparent)}.vZSZnG_panel[data-theme=glass] .vZSZnG_card{background:color-mix(in srgb, var(--dsw-alias-bg-base,transparent) 84%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-border-l1,#7f7f7f29) 60%, transparent)}.vZSZnG_panel[data-theme=glass] .vZSZnG_cardBody{border-top-color:color-mix(in srgb, var(--dsw-alias-border-l1,#7f7f7f24) 60%, transparent)}@media (prefers-reduced-transparency:reduce){.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:var(--dsw-alias-bg-layer-2,#fff);-webkit-backdrop-filter:none;backdrop-filter:none}.vZSZnG_panel[data-theme=glass] .vZSZnG_card{background:var(--dsw-alias-bg-base,transparent)}}@supports not ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){.vZSZnG_panel[data-theme=glass],.vZSZnG_miniBar[data-theme=glass]{background:var(--dsw-alias-bg-layer-2,#fff)}}.vZSZnG_panel[data-theme=brutal]{box-shadow:none;border-width:1px;border-radius:6px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_card{border:1px solid var(--dsw-alias-border-l3,#7f7f7f6b);background:var(--dsw-alias-bg-base,transparent);box-shadow:none;border-radius:3px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardHead{padding:7px 10px 6px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardTitle{letter-spacing:.01em;font-weight:650}.vZSZnG_panel[data-theme=brutal] .vZSZnG_headerTitle{letter-spacing:.03em}.vZSZnG_panel[data-theme=brutal] .vZSZnG_boundBadge{border-radius:2px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_track{border-radius:0;height:3px}.vZSZnG_panel[data-theme=brutal] .vZSZnG_trackFill{border-radius:0}.vZSZnG_panel[data-theme=brutal] .vZSZnG_cardBody{border-top:1px solid var(--dsw-alias-border-l2,#7f7f7f3d)}.vZSZnG_panel[data-theme=mono] .vZSZnG_headerTitle,.vZSZnG_panel[data-theme=mono] .vZSZnG_counts,.vZSZnG_panel[data-theme=mono] .vZSZnG_cardTitle,.vZSZnG_panel[data-theme=mono] .vZSZnG_progressText,.vZSZnG_panel[data-theme=mono] .vZSZnG_stepContent,.vZSZnG_panel[data-theme=mono] .vZSZnG_filePath{font-family:ui-monospace,SF Mono,Menlo,Consolas,Liberation Mono,monospace}.vZSZnG_panel[data-theme=mono] .vZSZnG_cardTitle{font-size:12px}.vZSZnG_panel[data-theme=mono] .vZSZnG_stepContent{font-size:11.5px}.vZSZnG_panel[data-theme=mono] .vZSZnG_card{border-radius:5px}.vZSZnG_panel[data-theme=mono] .vZSZnG_trackFill{border-radius:0}.vZSZnG_panel[data-theme=mono] .vZSZnG_stepPending{border-radius:1px}.vZSZnG_panel[data-theme=mono] .vZSZnG_updated{letter-spacing:.02em}.vZSZnG_headerIconBtn{color:inherit;cursor:pointer;background:0 0;border:none;border-radius:0;flex:none;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.vZSZnG_headerIcon{color:var(--dsw-alias-label-secondary);flex:none;display:block}.vZSZnG_headerLeft{min-width:0}.vZSZnG_headerTitle{text-overflow:ellipsis;flex:0 auto;min-width:0;overflow:hidden}.vZSZnG_headerRight{min-width:0}.vZSZnG_counts{text-overflow:ellipsis;flex:none;max-width:45%;margin-left:auto;overflow:hidden}.vZSZnG_dragGhost{pointer-events:none;z-index:9999;transition:left 50ms,top 50ms;position:fixed;transform:translate(-50%,-50%)}.vZSZnG_dragGhostCard{background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));border:1px solid var(--dsw-alias-border-l3,#7f7f7f59);color:var(--dsw-alias-label-primary);white-space:nowrap;border-radius:8px;align-items:center;gap:8px;max-width:280px;padding:8px 12px;font-size:12px;line-height:20px;display:inline-flex;transform:scale(.9);box-shadow:0 8px 24px #0000002e,0 2px 8px #0000001a}.vZSZnG_dragGhostContent{text-overflow:ellipsis;word-break:break-word;flex:1;min-width:0;overflow:hidden}.vZSZnG_dragGhostClose{width:20px;height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:.6;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;margin-left:8px;font-size:16px;line-height:1;transition:opacity .15s,background .15s,color .15s;display:inline-flex}.vZSZnG_dragGhostClose:hover{opacity:1;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover,#7f7f7f1f)}.vZSZnG_dropTargetBefore{position:relative}.vZSZnG_dropTargetBefore:before{content:\"\";background:var(--dsw-alias-button-primary-fill,#3a6ef5);height:2px;box-shadow:0 0 4px var(--dsw-alias-button-primary-fill,#3a6ef5);border-radius:1px;animation:.8s ease-in-out infinite vZSZnG_dropPulse;position:absolute;top:-1px;left:0;right:0}.vZSZnG_dropTargetAfter{position:relative}.vZSZnG_dropTargetAfter:after{content:\"\";background:var(--dsw-alias-button-primary-fill,#3a6ef5);height:2px;box-shadow:0 0 4px var(--dsw-alias-button-primary-fill,#3a6ef5);border-radius:1px;animation:.8s ease-in-out infinite vZSZnG_dropPulse;position:absolute;bottom:-1px;left:0;right:0}@keyframes vZSZnG_dropPulse{0%,to{opacity:.6}50%{opacity:1}}.vZSZnG_memoryPane{flex-direction:column;gap:6px;max-height:480px;padding:8px 10px 10px;display:flex;overflow:auto}.vZSZnG_memToolbar{justify-content:space-between;align-items:center;gap:8px;display:flex}.vZSZnG_memCount{opacity:.75;font-size:12px;font-weight:600}.vZSZnG_memList{flex-direction:column;gap:6px;display:flex}.vZSZnG_memRow{border:1px solid var(--dsw-border-weak,#80808040);background:color-mix(in srgb, var(--dsw-bg-secondary,#f5f5f5) 45%, transparent);border-radius:6px;flex-direction:column;gap:4px;padding:6px 8px;display:flex}.vZSZnG_memRowArchived{opacity:.55}.vZSZnG_memRowMain{flex-direction:column;gap:2px;display:flex}.vZSZnG_memTitle{word-break:break-word;align-items:flex-start;gap:6px;font-size:13px;line-height:1.35;display:flex}.vZSZnG_kindBadge{background:var(--dsw-accent-soft,#5078ff26);color:var(--dsw-accent,#4d6bfe);border-radius:4px;flex:none;padding:0 5px;font-size:11px;line-height:1.5}.vZSZnG_memMeta{flex-wrap:wrap;align-items:center;gap:4px;display:flex}.vZSZnG_badge{background:color-mix(in srgb, var(--dsw-fg,#222) 8%, transparent);border-radius:4px;padding:0 5px;font-size:11px;line-height:1.5}.vZSZnG_badgeDraft{color:#b26a00;background:#ffb40029}.vZSZnG_badgeArchived{opacity:.8}.vZSZnG_memBody{opacity:.8;word-break:break-word;font-size:12px}.vZSZnG_memActs{flex-wrap:wrap;gap:4px;display:flex}.vZSZnG_memToolbarActs{align-items:center;gap:6px;display:flex}.vZSZnG_memNew{border:1px solid var(--dsw-border-weak,#80808040);border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.vZSZnG_memNewRow{align-items:center;gap:8px;display:flex}.vZSZnG_memLabel{opacity:.8;align-items:center;gap:6px;font-size:12px;display:inline-flex}.vZSZnG_memNew select,.vZSZnG_memNew textarea{box-sizing:border-box;width:100%}";
565
565
  const tagId$1 = "@yolk_vat-y/dsh-project-memory/TaskPanel.module.css";
566
566
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
567
567
  const tag = document.createElement("style");
@@ -571,90 +571,91 @@ window.__ModuleLoader__.load({
571
571
  document.head.appendChild(tag);
572
572
  }
573
573
  var TaskPanel_module_css_default = {
574
- "cardTitleRow": "vZSZnG_cardTitleRow",
575
- "memMeta": "vZSZnG_memMeta",
574
+ "stepToggle": "vZSZnG_stepToggle",
575
+ "stepDone": "vZSZnG_stepDone",
576
+ "miniIcon": "vZSZnG_miniIcon",
577
+ "headerIconBtn": "vZSZnG_headerIconBtn",
578
+ "cardBody": "vZSZnG_cardBody",
579
+ "sectionCount": "vZSZnG_sectionCount",
580
+ "progressText": "vZSZnG_progressText",
581
+ "cardBound": "vZSZnG_cardBound",
582
+ "chevron": "vZSZnG_chevron",
583
+ "memList": "vZSZnG_memList",
584
+ "kindBadge": "vZSZnG_kindBadge",
585
+ "cardFooter": "vZSZnG_cardFooter",
586
+ "trackFill": "vZSZnG_trackFill",
576
587
  "cardTitle": "vZSZnG_cardTitle",
577
- "dragGhostClose": "vZSZnG_dragGhostClose",
578
- "syncHint": "vZSZnG_syncHint",
579
- "updated": "vZSZnG_updated",
588
+ "stepEditable": "vZSZnG_stepEditable",
589
+ "counts": "vZSZnG_counts",
580
590
  "section": "vZSZnG_section",
591
+ "stepInput": "vZSZnG_stepInput",
592
+ "handleGrip": "vZSZnG_handleGrip",
593
+ "cardTitleEditable": "vZSZnG_cardTitleEditable",
594
+ "dragGhostContent": "vZSZnG_dragGhostContent",
595
+ "dragGhostClose": "vZSZnG_dragGhostClose",
596
+ "dropTargetBefore": "vZSZnG_dropTargetBefore",
597
+ "stepDragging": "vZSZnG_stepDragging",
598
+ "cardMeta": "vZSZnG_cardMeta",
599
+ "panel": "vZSZnG_panel",
600
+ "dragGhost": "vZSZnG_dragGhost",
601
+ "dropTargetAfter": "vZSZnG_dropTargetAfter",
602
+ "stepPending": "vZSZnG_stepPending",
603
+ "fileDot": "vZSZnG_fileDot",
604
+ "memTitle": "vZSZnG_memTitle",
605
+ "badge": "vZSZnG_badge",
581
606
  "emptyTitle": "vZSZnG_emptyTitle",
582
- "stepRow": "vZSZnG_stepRow",
583
- "sectionLabel": "vZSZnG_sectionLabel",
584
- "emptyDesc": "vZSZnG_emptyDesc",
607
+ "stepContentRun": "vZSZnG_stepContentRun",
608
+ "stepRun": "vZSZnG_stepRun",
585
609
  "fileLine": "vZSZnG_fileLine",
586
- "boundaryFallback": "vZSZnG_boundaryFallback",
587
- "fileDot": "vZSZnG_fileDot",
610
+ "dropPulse": "vZSZnG_dropPulse",
588
611
  "memRow": "vZSZnG_memRow",
589
- "stepContent": "vZSZnG_stepContent",
590
- "fileRow": "vZSZnG_fileRow",
612
+ "filesList": "vZSZnG_filesList",
613
+ "sectionLabel": "vZSZnG_sectionLabel",
591
614
  "cardHead": "vZSZnG_cardHead",
592
- "stepContentDone": "vZSZnG_stepContentDone",
593
- "dropTargetAfter": "vZSZnG_dropTargetAfter",
594
- "stepDragging": "vZSZnG_stepDragging",
615
+ "fileRow": "vZSZnG_fileRow",
616
+ "notice": "vZSZnG_notice",
617
+ "emptyDesc": "vZSZnG_emptyDesc",
618
+ "panelIn": "vZSZnG_panelIn",
619
+ "headerLeft": "vZSZnG_headerLeft",
620
+ "headerIcon": "vZSZnG_headerIcon",
595
621
  "memNew": "vZSZnG_memNew",
596
- "taskList": "vZSZnG_taskList",
597
- "memCount": "vZSZnG_memCount",
598
- "stepToggle": "vZSZnG_stepToggle",
599
- "trackFill": "vZSZnG_trackFill",
600
622
  "memToolbar": "vZSZnG_memToolbar",
601
- "stepPending": "vZSZnG_stepPending",
623
+ "memActs": "vZSZnG_memActs",
624
+ "badgeDraft": "vZSZnG_badgeDraft",
625
+ "headerRight": "vZSZnG_headerRight",
626
+ "card": "vZSZnG_card",
627
+ "syncHint": "vZSZnG_syncHint",
628
+ "updated": "vZSZnG_updated",
602
629
  "memBody": "vZSZnG_memBody",
603
- "memRowMain": "vZSZnG_memRowMain",
630
+ "memLabel": "vZSZnG_memLabel",
631
+ "dragHandle": "vZSZnG_dragHandle",
632
+ "track": "vZSZnG_track",
633
+ "memoryPane": "vZSZnG_memoryPane",
604
634
  "badgeArchived": "vZSZnG_badgeArchived",
605
- "header": "vZSZnG_header",
606
- "dragGhost": "vZSZnG_dragGhost",
607
- "card": "vZSZnG_card",
608
- "cardTitleEditable": "vZSZnG_cardTitleEditable",
609
- "panelIn": "vZSZnG_panelIn",
610
- "headerRight": "vZSZnG_headerRight",
611
- "emptyState": "vZSZnG_emptyState",
612
- "headerIconBtn": "vZSZnG_headerIconBtn",
613
- "progressText": "vZSZnG_progressText",
635
+ "muted": "vZSZnG_muted",
636
+ "editorArea": "vZSZnG_editorArea",
637
+ "memMeta": "vZSZnG_memMeta",
614
638
  "boundBadge": "vZSZnG_boundBadge",
615
- "headerIcon": "vZSZnG_headerIcon",
616
- "chevron": "vZSZnG_chevron",
617
- "track": "vZSZnG_track",
618
- "kindBadge": "vZSZnG_kindBadge",
619
- "memLabel": "vZSZnG_memLabel",
620
- "cardBound": "vZSZnG_cardBound",
621
- "stepsList": "vZSZnG_stepsList",
622
- "dragGhostContent": "vZSZnG_dragGhostContent",
623
- "miniIcon": "vZSZnG_miniIcon",
624
- "stepEditable": "vZSZnG_stepEditable",
625
- "memNewRow": "vZSZnG_memNewRow",
626
- "panel": "vZSZnG_panel",
639
+ "stepRow": "vZSZnG_stepRow",
640
+ "miniBar": "vZSZnG_miniBar",
641
+ "stepContent": "vZSZnG_stepContent",
627
642
  "filePath": "vZSZnG_filePath",
628
- "badge": "vZSZnG_badge",
629
- "memTitle": "vZSZnG_memTitle",
630
- "cardFooter": "vZSZnG_cardFooter",
631
- "headerLeft": "vZSZnG_headerLeft",
632
- "notice": "vZSZnG_notice",
643
+ "memNewRow": "vZSZnG_memNewRow",
644
+ "emptyState": "vZSZnG_emptyState",
645
+ "memCount": "vZSZnG_memCount",
646
+ "memToolbarActs": "vZSZnG_memToolbarActs",
633
647
  "headerTitle": "vZSZnG_headerTitle",
634
- "miniBar": "vZSZnG_miniBar",
635
- "stepDone": "vZSZnG_stepDone",
636
- "stepContentRun": "vZSZnG_stepContentRun",
637
- "cardBody": "vZSZnG_cardBody",
638
- "filesList": "vZSZnG_filesList",
639
- "handleGrip": "vZSZnG_handleGrip",
640
- "memoryPane": "vZSZnG_memoryPane",
641
- "memRowArchived": "vZSZnG_memRowArchived",
642
- "dragHandle": "vZSZnG_dragHandle",
648
+ "memRowMain": "vZSZnG_memRowMain",
649
+ "stepContentDone": "vZSZnG_stepContentDone",
643
650
  "miniChevron": "vZSZnG_miniChevron",
644
- "badgeDraft": "vZSZnG_badgeDraft",
645
- "memList": "vZSZnG_memList",
646
- "memActs": "vZSZnG_memActs",
647
- "memToolbarActs": "vZSZnG_memToolbarActs",
648
- "cardMeta": "vZSZnG_cardMeta",
649
- "stepRun": "vZSZnG_stepRun",
650
- "stepInput": "vZSZnG_stepInput",
651
+ "taskList": "vZSZnG_taskList",
652
+ "boundaryFallback": "vZSZnG_boundaryFallback",
651
653
  "dragGhostCard": "vZSZnG_dragGhostCard",
652
- "sectionCount": "vZSZnG_sectionCount",
653
- "muted": "vZSZnG_muted",
654
- "counts": "vZSZnG_counts",
655
- "dropTargetBefore": "vZSZnG_dropTargetBefore",
656
- "dropPulse": "vZSZnG_dropPulse",
657
- "miniText": "vZSZnG_miniText"
654
+ "header": "vZSZnG_header",
655
+ "cardTitleRow": "vZSZnG_cardTitleRow",
656
+ "memRowArchived": "vZSZnG_memRowArchived",
657
+ "miniText": "vZSZnG_miniText",
658
+ "stepsList": "vZSZnG_stepsList"
658
659
  };
659
660
  //#endregion
660
661
  //#region src/client/TaskComponents.tsx
@@ -685,6 +686,52 @@ window.__ModuleLoader__.load({
685
686
  if (status === "in_progress") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { className: TaskPanel_module_css_default.stepRun });
686
687
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: TaskPanel_module_css_default.stepPending });
687
688
  }
689
+ /**
690
+ * 双击判定窗口(ms)。
691
+ * 单击(如卡片展开/收起)会延迟该时长执行:若期间收到第二次点击则按“双击”处理并取消单击,
692
+ * 从而避免“第一下先展开/收起、布局位移后第二下点不到目标”的问题。
693
+ * 数值是可感知与可靠性的折中:太小慢速双击会失效,太大单击会显“肉”。
694
+ */
695
+ const CLICK_SINGLE_DELAY_MS = 250;
696
+ function InlineEditor({ value, className, maxRows = 8, onValueChange, onCommit, onCancel }) {
697
+ const ref = (0, react.useRef)(null);
698
+ (0, react.useLayoutEffect)(() => {
699
+ const el = ref.current;
700
+ if (!el) return;
701
+ el.style.height = "auto";
702
+ const cs = getComputedStyle(el);
703
+ const lineHeight = parseFloat(cs.lineHeight) || 20;
704
+ const padV = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
705
+ const borderV = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
706
+ const cap = maxRows * lineHeight + padV + borderV;
707
+ const height = Math.min(el.scrollHeight, cap);
708
+ el.style.height = `${height}px`;
709
+ el.style.overflowY = el.scrollHeight > cap ? "auto" : "hidden";
710
+ }, [value, maxRows]);
711
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
712
+ ref,
713
+ className,
714
+ rows: 1,
715
+ value,
716
+ autoFocus: true,
717
+ spellCheck: false,
718
+ onChange: (e) => onValueChange(e.target.value),
719
+ onFocus: (e) => {
720
+ const len = e.target.value.length;
721
+ e.target.setSelectionRange(len, len);
722
+ },
723
+ onBlur: onCommit,
724
+ onKeyDown: (e) => {
725
+ if (e.key === "Enter" && !e.shiftKey) {
726
+ e.preventDefault();
727
+ onCommit();
728
+ } else if (e.key === "Escape") {
729
+ e.preventDefault();
730
+ onCancel();
731
+ }
732
+ }
733
+ });
734
+ }
688
735
  function MiniBar({ label, hint, position, theme, onMove, open }) {
689
736
  const barRef = (0, react.useRef)(null);
690
737
  const down = (0, react.useRef)(null);
@@ -763,33 +810,57 @@ window.__ModuleLoader__.load({
763
810
  setTitleEdit(null);
764
811
  onRename(value);
765
812
  };
813
+ const headClickTimer = (0, react.useRef)(null);
814
+ const suppressHeadClick = (0, react.useRef)(false);
815
+ (0, react.useEffect)(() => () => {
816
+ if (headClickTimer.current !== null) window.clearTimeout(headClickTimer.current);
817
+ }, []);
818
+ const handleHeadClick = (e) => {
819
+ if (suppressHeadClick.current) {
820
+ suppressHeadClick.current = false;
821
+ return;
822
+ }
823
+ if (titleEdit !== null) return;
824
+ const target = e.target;
825
+ const onTitle = isBound && target.closest("[data-card-title]") !== null;
826
+ if (headClickTimer.current !== null) {
827
+ window.clearTimeout(headClickTimer.current);
828
+ headClickTimer.current = null;
829
+ if (onTitle) setTitleEdit({ value: String(task.title ?? "") });
830
+ else onToggleExpand();
831
+ return;
832
+ }
833
+ headClickTimer.current = window.setTimeout(() => {
834
+ headClickTimer.current = null;
835
+ onToggleExpand();
836
+ }, CLICK_SINGLE_DELAY_MS);
837
+ };
766
838
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
767
839
  "data-task-id": task.id,
768
840
  className: `${TaskPanel_module_css_default.card}${isBound ? ` ${TaskPanel_module_css_default.cardBound}` : ""}`,
769
841
  children: [
770
842
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
771
843
  className: TaskPanel_module_css_default.cardHead,
772
- onClick: () => {
773
- if (titleEdit !== null) return;
774
- onToggleExpand();
844
+ onClick: handleHeadClick,
845
+ onMouseDownCapture: () => {
846
+ if (titleEdit !== null) suppressHeadClick.current = true;
775
847
  },
848
+ onDoubleClick: (e) => e.preventDefault(),
776
849
  "aria-expanded": expanded,
777
850
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
778
851
  className: TaskPanel_module_css_default.cardTitleRow,
779
852
  children: [isBound && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
780
853
  className: TaskPanel_module_css_default.boundBadge,
781
854
  children: t("task.current")
782
- }), isBound && titleEdit !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
783
- className: TaskPanel_module_css_default.stepInput,
855
+ }), isBound && titleEdit !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineEditor, {
856
+ className: TaskPanel_module_css_default.editorArea,
784
857
  value: titleEdit.value,
785
- autoFocus: true,
786
- onChange: (e) => setTitleEdit({ value: e.target.value }),
787
- onBlur: commitTitleEdit,
788
- onKeyDown: (e) => {
789
- if (e.key === "Enter") commitTitleEdit();
790
- else if (e.key === "Escape") setTitleEdit(null);
791
- }
858
+ maxRows: 4,
859
+ onValueChange: (value) => setTitleEdit({ value }),
860
+ onCommit: commitTitleEdit,
861
+ onCancel: () => setTitleEdit(null)
792
862
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
863
+ "data-card-title": true,
793
864
  className: `${TaskPanel_module_css_default.cardTitle}${isBound ? ` ${TaskPanel_module_css_default.cardTitleEditable}` : ""}`,
794
865
  title: isBound ? t("task.title-edit") : void 0,
795
866
  onDoubleClick: (e) => {
@@ -865,19 +936,16 @@ window.__ModuleLoader__.load({
865
936
  },
866
937
  title: `${t("step.completed")}/${t("step.in-progress")}/${t("step.pending")}`,
867
938
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepIcon, { status: step.status })
868
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepIcon, { status: step.status }), stepIsEditing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
869
- className: TaskPanel_module_css_default.stepInput,
939
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepIcon, { status: step.status }), stepIsEditing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineEditor, {
940
+ className: TaskPanel_module_css_default.editorArea,
870
941
  value: stepEdit.value,
871
- autoFocus: true,
872
- onChange: (e) => setStepEdit({
942
+ maxRows: 10,
943
+ onValueChange: (value) => setStepEdit({
873
944
  index: i,
874
- value: e.target.value
945
+ value
875
946
  }),
876
- onBlur: () => commitStepEdit(i),
877
- onKeyDown: (e) => {
878
- if (e.key === "Enter") commitStepEdit(i);
879
- else if (e.key === "Escape") setStepEdit(null);
880
- }
947
+ onCommit: () => commitStepEdit(i),
948
+ onCancel: () => setStepEdit(null)
881
949
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
882
950
  className: `${TaskPanel_module_css_default.stepContent}${step.status === "completed" ? ` ${TaskPanel_module_css_default.stepContentDone}` : ""}${step.status === "in_progress" ? ` ${TaskPanel_module_css_default.stepContentRun}` : ""}${isBound ? ` ${TaskPanel_module_css_default.stepEditable}` : ""}`,
883
951
  title: isBound && showHints ? t("step.edit-hint") : void 0,
@@ -1937,7 +2005,7 @@ window.__ModuleLoader__.load({
1937
2005
  });
1938
2006
  }
1939
2007
  //#endregion
1940
- //#region \0dsh-css:/home/sxt/project/dsh-project-memory/src/client/TaskCommandNode.module.css.mjs
2008
+ //#region \0dsh-css:src/client/TaskCommandNode.module.css.mjs
1941
2009
  const css = ".TPe-xa_row{max-width:100%;color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;align-items:center;gap:6px;padding:2px 0;font-size:12px;line-height:20px;display:inline-flex;overflow:hidden}.TPe-xa_row[data-variant=ok]{color:var(--dsw-alias-label-secondary)}.TPe-xa_row[data-variant=error]{color:var(--dsw-alias-state-warn-label,#b7791f)}.TPe-xa_row[data-variant=running]{color:var(--dsw-alias-label-dimmed)}.TPe-xa_icon{color:var(--dsw-alias-label-tertiary);flex:none}";
1942
2010
  const tagId = "@yolk_vat-y/dsh-project-memory/TaskCommandNode.module.css";
1943
2011
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
@@ -1948,8 +2016,8 @@ window.__ModuleLoader__.load({
1948
2016
  document.head.appendChild(tag);
1949
2017
  }
1950
2018
  var TaskCommandNode_module_css_default = {
1951
- "row": "TPe-xa_row",
1952
- "icon": "TPe-xa_icon"
2019
+ "icon": "TPe-xa_icon",
2020
+ "row": "TPe-xa_row"
1953
2021
  };
1954
2022
  //#endregion
1955
2023
  //#region src/client/TaskCommandNode.tsx