dsh-experience 1.1.0 → 1.2.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/CHANGELOG.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## [1.2.0] — 2026-11-09 — 计划任务召回与已知问题清单
5
+
6
+ ### 修复
7
+ - **计划任务的轮次召回为空**:这类会话的第一条消息是 `[SCHEDULE REMINDER] … reminder_prompt_json: "…"`,
8
+ 整段被当成查询词,搜到的是 "schedule"。新增 `stripReminder()`,从包装里取出真实 prompt 再分词;
9
+ 原文保留在 `trace.queryRaw`。测试 T84。
10
+
11
+ ### 新增
12
+ - `docs/KNOWN-ISSUES.md`:八条已知问题,按"会不会让经验变错"排序,每条写症状/影响/现状/绕法
13
+ (含 shell 落盘不算写、`set_bundle` 不重载、召回只在第 1 步、纯词面检索、单机单用户等)。
14
+
15
+ ### 测试
16
+ - `selftest 94 pass / 0 fail`、`e2e 23 pass / 0 fail`。
17
+
18
+ ## [1.1.1] — 2026-11-09 — 构建标识修正
19
+
20
+ - `BUILD` 从 `r20-scope-tools` 改为 `r21-platform`。1.1.0 的代码已含平台隔离,但包内构建标识还是上一个版本号,
21
+ 排查问题时会让 `~/.dsh/experience/applied.json` 显示错误的构建。无行为变更。
22
+
4
23
  ## [1.1.0] — 2026-11-09 — 平台隔离与适用边界
5
24
 
6
25
  ### 新增
package/README.md CHANGED
@@ -150,6 +150,8 @@ plugin_manager install_bundle target=link:<PATH-TO-dsh-experience>
150
150
  - 淘汰只归档不删除,需要 `restore` 才能回到召回池;
151
151
  - 经验只描述「怎么做」,不包含业务代码本身。
152
152
 
153
+ 已知问题的完整清单(含症状、影响、绕法)见 [docs/KNOWN-ISSUES.md](docs/KNOWN-ISSUES.md)。
154
+
153
155
  ## 开发
154
156
 
155
157
  ```bash
package/index.js CHANGED
@@ -35,7 +35,7 @@ const PROBE_FILE = path.join(STORE_DIR, 'probe.json')
35
35
  const THRESHOLD = 0.15
36
36
  const MAX_EXPERIENCES = 2
37
37
  const MAX_CHARS = 1600
38
- const BUILD = 'r20-scope-tools'
38
+ const BUILD = 'r22-reminder'
39
39
 
40
40
  let CONFIG = { ...C.DEFAULT_CONFIG }
41
41
 
@@ -286,7 +286,8 @@ export function apply(ctx) {
286
286
  const step = payload.step
287
287
  if (!id || step !== 1) return
288
288
 
289
- const queryText = (payload.messages || []).map(textOfMessage).join('\n')
289
+ const rawQuery = (payload.messages || []).map(textOfMessage).join('\n')
290
+ const queryText = R.stripReminder(rawQuery)
290
291
  const queryTokens = tokens(queryText)
291
292
  const index = R.corpus(store.experiences)
292
293
  const scored = store.experiences
@@ -310,6 +311,7 @@ export function apply(ctx) {
310
311
  endedAt: null,
311
312
  steps: 0,
312
313
  query: queryText.slice(0, 400),
314
+ queryRaw: rawQuery === queryText ? undefined : rawQuery.slice(0, 400),
313
315
  queryTokens: [...queryTokens].slice(0, 40),
314
316
  candidates,
315
317
  threshold: threshold(),
package/lib/recall.js CHANGED
@@ -87,6 +87,29 @@ export function expand(text) {
87
87
  return out
88
88
  }
89
89
 
90
+ /**
91
+ * A scheduled turn starts with its delivery wrapper ("[SCHEDULE REMINDER] ...
92
+ * reminder_prompt_json: \"...\""). Searching that text finds the word
93
+ * "schedule" and nothing else, so recall reads the wrapped prompt instead.
94
+ */
95
+ export function stripReminder(text) {
96
+ const raw = String(text || '')
97
+ if (!raw.includes('[SCHEDULE REMINDER]')) return raw
98
+ const spoiled = /reminder_prompt_json:\s*("(?:[^"\\]|\\.)*")/s.exec(raw)
99
+ if (spoiled) {
100
+ try {
101
+ const prompt = JSON.parse(spoiled[1])
102
+ if (prompt) return String(prompt)
103
+ } catch {
104
+ /* keep the raw text when the JSON is not what we expect */
105
+ }
106
+ }
107
+ return raw
108
+ .split('\n')
109
+ .filter((line) => !/^\s*\[SCHEDULE REMINDER\]/.test(line) && !/^\s*(schedule_id_json|occurrence_at|reminder_prompt_json)\s*:/.test(line))
110
+ .join('\n')
111
+ }
112
+
90
113
  /** The searchable profile of a stored experience. */
91
114
  export function profileText(experience) {
92
115
  if (!experience) return ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-experience",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1198,6 +1198,16 @@ test('T83 a recipe from another OS is never merged and never recalled', () => {
1198
1198
  assert.equal(C.platformConflicts({}), false)
1199
1199
  })
1200
1200
 
1201
+ test('T84 a scheduled wrapper yields the task, not the wrapper', () => {
1202
+ const wrapped = '[SCHEDULE REMINDER]\nThis is a scheduled message from the user\nschedule_id_json: "schedule-1"\noccurrence_at: 2026-11-09T02:57:00.000Z\nreminder_prompt_json: "在 D:\\\\work\\\\platgate 下创建 platgate.js 并运行"'
1203
+ const task = R.stripReminder(wrapped)
1204
+ assert.equal(task, '在 D:\\work\\platgate 下创建 platgate.js 并运行')
1205
+ const tokens = R.expand(task)
1206
+ assert.ok(tokens.has('platgate'))
1207
+ assert.ok(!tokens.has('schedule'))
1208
+ assert.equal(R.stripReminder('普通任务'), '普通任务')
1209
+ })
1210
+
1201
1211
  // ---------------------------------------------------------------- summary
1202
1212
 
1203
1213
  const total = pass + failures.length