dsh-plan-completion 0.1.4 → 0.1.5
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 +12 -0
- package/cordis.patch.yml +16 -16
- package/docs/publish-assessment.md +4 -1
- package/index.js +29 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning: SemVer.
|
|
5
5
|
|
|
6
|
+
## [0.1.5] - 2026-11-02
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- Detect a plan that stopped tracking the conversation: after **two consecutive `op=none`**
|
|
10
|
+
generation outcomes (e.g. an edited plan whose coarse step needs splitting), the published
|
|
11
|
+
suggestion becomes `/replan 按最近进展重排计划,把已完成项标记完成` and the plan is marked
|
|
12
|
+
stale, so the user can fix the plan with one Tab fill instead of seeing a repeated stale hint.
|
|
13
|
+
- `/reset` and forced `/replan` clear the none-streak counter.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `npm test` now reports **133/133** tests.
|
|
17
|
+
|
|
6
18
|
## [0.1.4] - 2026-11-02
|
|
7
19
|
|
|
8
20
|
### Fixed
|
package/cordis.patch.yml
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
- insert:
|
|
2
|
-
- id: plan-completion
|
|
3
|
-
name: 'dsh-plan-completion'
|
|
4
|
-
config:
|
|
5
|
-
evidenceChars: 1500
|
|
6
|
-
maxTokensPatch: 400
|
|
7
|
-
maxTokensRewrite: 1200
|
|
8
|
-
warmBackfill: true
|
|
9
|
-
modelCalls: true # false: heuristic-only; no plugin model requests
|
|
10
|
-
# Plans + last suggestion are persisted per session so a DSH restart restores the UI.
|
|
11
|
-
persistPlans: true
|
|
12
|
-
storageDir: '~/.dsh/plan-completion-data'
|
|
13
|
-
# Debug endpoints (debug/diag/theme/experiment/autojob/steps/reset) stay off in production.
|
|
14
|
-
enableDebugRoutes: false
|
|
15
|
-
# Optional: set a local directory to persist debug logs / experiment data.
|
|
16
|
-
# debugDir: '<local debug directory>'
|
|
1
|
+
- insert:
|
|
2
|
+
- id: plan-completion
|
|
3
|
+
name: 'dsh-plan-completion'
|
|
4
|
+
config:
|
|
5
|
+
evidenceChars: 1500
|
|
6
|
+
maxTokensPatch: 400
|
|
7
|
+
maxTokensRewrite: 1200
|
|
8
|
+
warmBackfill: true
|
|
9
|
+
modelCalls: true # false: heuristic-only; no plugin model requests
|
|
10
|
+
# Plans + last suggestion are persisted per session so a DSH restart restores the UI.
|
|
11
|
+
persistPlans: true
|
|
12
|
+
storageDir: '~/.dsh/plan-completion-data'
|
|
13
|
+
# Debug endpoints (debug/diag/theme/experiment/autojob/steps/reset) stay off in production.
|
|
14
|
+
enableDebugRoutes: false
|
|
15
|
+
# Optional: set a local directory to persist debug logs / experiment data.
|
|
16
|
+
# debugDir: '<local debug directory>'
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# GitHub 发布评估 · 会话计划补全插件(2026-11-02)
|
|
2
2
|
|
|
3
|
-
> 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。**当前最新为 v0.1.
|
|
3
|
+
> 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。**当前最新为 v0.1.4**(建议文本保真 + 核对进度 + 本地计划持久化 + 鉴权/状态一致性/隐私边界修复),详见 [security.md](security.md) 与 [CHANGELOG](../CHANGELOG.md);旧记录中的“无鉴权”、原始诊断字段与独立评审不代表当前实现。
|
|
4
4
|
>
|
|
5
|
+
> v0.1.4 发布记录(2026-11-02):建议文本保真修复(标识符/路径/命令保留 + 语义化截断)。tag `v0.1.4` + [Release](https://github.com/Xumi0920/dsh-plan-completion/releases/tag/v0.1.4);GitHub Actions [run 41389845733](https://github.com/Xumi0920/dsh-plan-completion/actions/runs/41389845733)(四 job 全绿,132/132 tests);npm `dsh-plan-completion@0.1.4`(31 files,shasum `a6f8953445e330081243f9f9d6b429fd4402b341`)。
|
|
6
|
+
>
|
|
5
7
|
> v0.1.3 发布记录(2026-11-02):tag `v0.1.3` + [Release](https://github.com/Xumi0920/dsh-plan-completion/releases/tag/v0.1.3);GitHub Actions [run 41368609382](https://github.com/Xumi0920/dsh-plan-completion/actions/runs/41368609382)(Ubuntu/Windows × Node20/22 四 job 全绿,126/126 tests);npm `dsh-plan-completion@0.1.3`(31 files,shasum `11adee0fe9acff199a180be511ca8bc600afdf6f`);canary 验证 `/plan-completion/verify` 未认证 401、客户端鉴权请求正常。
|
|
6
8
|
>
|
|
7
9
|
> v0.1.1 发布记录(2026-11-02):tag `v0.1.1` + [Release](https://github.com/Xumi0920/dsh-plan-completion/releases/tag/v0.1.1);GitHub Actions [run 41366651557](https://github.com/Xumi0920/dsh-plan-completion/actions/runs/41366651557)(Ubuntu/Windows × Node20/22 四 job 全绿);npm `dsh-plan-completion@0.1.1`(31 files,shasum `18989b0f50b72d5c8c332f3fdee7f43ccb5fd91a`)。
|
|
@@ -105,3 +107,4 @@ git tag v0.1.0 && git push --tags
|
|
|
105
107
|
> 建议:**先把 P0 B1~B8 全部打勾,再公开仓库**;否则许可证与数据问题会成为不可逆的历史提交。
|
|
106
108
|
|
|
107
109
|
|
|
110
|
+
|
package/index.js
CHANGED
|
@@ -215,7 +215,7 @@ const HOST_GLOBAL_KEY = '__dsh_plan_completion_host__'
|
|
|
215
215
|
* up with two live handler sets answering different state.
|
|
216
216
|
*/
|
|
217
217
|
function hostGlobal() {
|
|
218
|
-
const fallback = { states: new Map(), sessions: new Map(), handledTurns: new Map(), warmAttempts: new Map(), disposers: [] }
|
|
218
|
+
const fallback = { states: new Map(), sessions: new Map(), handledTurns: new Map(), warmAttempts: new Map(), noneStreaks: new Map(), disposers: [] }
|
|
219
219
|
if (typeof globalThis === 'undefined') return { ...fallback, store: createMemoryStore() }
|
|
220
220
|
if (!globalThis[HOST_GLOBAL_KEY] || typeof globalThis[HOST_GLOBAL_KEY] !== 'object') {
|
|
221
221
|
globalThis[HOST_GLOBAL_KEY] = fallback
|
|
@@ -225,6 +225,7 @@ function hostGlobal() {
|
|
|
225
225
|
if (!(shared.sessions instanceof Map)) shared.sessions = new Map()
|
|
226
226
|
if (!(shared.handledTurns instanceof Map)) shared.handledTurns = new Map()
|
|
227
227
|
if (!(shared.warmAttempts instanceof Map)) shared.warmAttempts = new Map()
|
|
228
|
+
if (!(shared.noneStreaks instanceof Map)) shared.noneStreaks = new Map()
|
|
228
229
|
if (!Array.isArray(shared.disposers)) shared.disposers = []
|
|
229
230
|
if (!shared.store || typeof shared.store.get !== 'function' || typeof shared.store.put !== 'function') {
|
|
230
231
|
shared.store = createMemoryStore()
|
|
@@ -754,12 +755,22 @@ export function apply(ctx, config = {}) {
|
|
|
754
755
|
const withinSuggestionDeadline = elapsed <= suggestionDeadline
|
|
755
756
|
const finalSuggestion = withinSuggestionDeadline ? suggestion ?? provisionalSuggestion : provisionalSuggestion
|
|
756
757
|
|
|
758
|
+
// A long `op=none` streak means the visible plan stopped tracking the conversation
|
|
759
|
+
// (edited-by-user plans cannot be rewritten automatically, and one coarse step may need
|
|
760
|
+
// splitting). Surface an actionable replan suggestion instead of repeating a stale one.
|
|
761
|
+
const priorNoneStreak = noneStreaks.get(sessionId) ?? 0
|
|
762
|
+
const nextNoneStreak = force ? 0 : outcome === 'none' && nextPlan ? priorNoneStreak + 1 : 0
|
|
763
|
+
const replanSuggested = nextNoneStreak >= 2 && Boolean(nextPlan)
|
|
764
|
+
const publishedSuggestion = replanSuggested
|
|
765
|
+
? { text: '/replan 按最近进展重排计划,把已完成项标记完成', alternates: [] }
|
|
766
|
+
: finalSuggestion
|
|
767
|
+
|
|
757
768
|
if (nextPlan) {
|
|
758
769
|
try {
|
|
759
770
|
// Check CAS even for an unchanged plan; an old snapshot is never an
|
|
760
771
|
// unconditional write. null also protects the first commit from races.
|
|
761
772
|
// The last suggestion is persisted in the same write so a restart can restore it.
|
|
762
|
-
const committed = await store.put(nextPlan, { cas: storedVersion, suggestion:
|
|
773
|
+
const committed = await store.put(nextPlan, { cas: storedVersion, suggestion: publishedSuggestion })
|
|
763
774
|
if (!committed) {
|
|
764
775
|
debugLog({ phase: 'store-cas-miss', sessionId })
|
|
765
776
|
const winner = typeof store.getState === 'function'
|
|
@@ -779,13 +790,23 @@ export function apply(ctx, config = {}) {
|
|
|
779
790
|
}
|
|
780
791
|
}
|
|
781
792
|
|
|
793
|
+
noneStreaks.set(sessionId, nextNoneStreak)
|
|
782
794
|
publishState(sessionId, {
|
|
783
795
|
plan: nextPlan,
|
|
784
|
-
suggestion:
|
|
785
|
-
stale:
|
|
796
|
+
suggestion: publishedSuggestion,
|
|
797
|
+
stale: replanSuggested,
|
|
786
798
|
})
|
|
787
|
-
debug.lastJob = {
|
|
788
|
-
|
|
799
|
+
debug.lastJob = {
|
|
800
|
+
sessionId,
|
|
801
|
+
turn,
|
|
802
|
+
elapsed,
|
|
803
|
+
outcome,
|
|
804
|
+
suggestion: Boolean(suggestion),
|
|
805
|
+
withinSuggestionDeadline,
|
|
806
|
+
noneStreak: nextNoneStreak,
|
|
807
|
+
replanSuggested,
|
|
808
|
+
}
|
|
809
|
+
debugLog({ phase: 'job-done', sessionId, turn, elapsed, outcome, withinSuggestionDeadline, replanSuggested })
|
|
789
810
|
} catch (error) {
|
|
790
811
|
if (!isCurrent()) return
|
|
791
812
|
debugLog({ phase: 'job-failed', sessionId, turn, error: String(error) })
|
|
@@ -800,6 +821,7 @@ export function apply(ctx, config = {}) {
|
|
|
800
821
|
|
|
801
822
|
// ── Session event subscription: one plan job per completed turn ──────────
|
|
802
823
|
const handledTurns = shared.handledTurns // sessionId -> last handled turn number
|
|
824
|
+
const noneStreaks = shared.noneStreaks // sessionId -> consecutive op=none outcomes
|
|
803
825
|
|
|
804
826
|
function triggerTurnEnd(session, seq, turn, source) {
|
|
805
827
|
if (!session?.id || disposed) return
|
|
@@ -1161,6 +1183,7 @@ export function apply(ctx, config = {}) {
|
|
|
1161
1183
|
states.delete(sessionId)
|
|
1162
1184
|
warmAttempts.delete(sessionId)
|
|
1163
1185
|
handledTurns.delete(sessionId)
|
|
1186
|
+
noneStreaks.delete(sessionId)
|
|
1164
1187
|
debugLog({ phase: 'state-reset', sessionId, existed })
|
|
1165
1188
|
jsonResponse(res, 200, { ok: true, sessionId, existed })
|
|
1166
1189
|
return
|