dsh-continual-evolve 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -115
- package/README.zh.md +26 -93
- package/cordis.patch.yml +2 -2
- package/lib/index.d.ts +44 -225
- package/lib/index.js +30 -239
- package/lib/memory-guide.d.ts +13 -14
- package/lib/memory-guide.js +32 -21
- package/lib/memory-section.d.ts +77 -0
- package/lib/memory-section.js +189 -0
- package/package.json +2 -17
- package/lib/apply.d.ts +0 -22
- package/lib/apply.js +0 -157
- package/lib/approval.d.ts +0 -73
- package/lib/approval.js +0 -127
- package/lib/audit-log.d.ts +0 -19
- package/lib/audit-log.js +0 -52
- package/lib/auto.d.ts +0 -236
- package/lib/auto.js +0 -773
- package/lib/autocase.d.ts +0 -38
- package/lib/autocase.js +0 -76
- package/lib/benchmark-command.d.ts +0 -9
- package/lib/benchmark-command.js +0 -357
- package/lib/benchmark.d.ts +0 -151
- package/lib/benchmark.js +0 -250
- package/lib/command-util.d.ts +0 -70
- package/lib/command-util.js +0 -123
- package/lib/command.d.ts +0 -13
- package/lib/command.js +0 -134
- package/lib/consolidate.d.ts +0 -90
- package/lib/consolidate.js +0 -180
- package/lib/copy.d.ts +0 -37
- package/lib/copy.js +0 -58
- package/lib/declines.d.ts +0 -103
- package/lib/declines.js +0 -179
- package/lib/evaluate.d.ts +0 -74
- package/lib/evaluate.js +0 -271
- package/lib/evolve-event.d.ts +0 -40
- package/lib/evolve-event.js +0 -60
- package/lib/extraction-phase.d.ts +0 -132
- package/lib/extraction-phase.js +0 -236
- package/lib/failures.d.ts +0 -42
- package/lib/failures.js +0 -171
- package/lib/fate.d.ts +0 -130
- package/lib/fate.js +0 -350
- package/lib/goal-command.d.ts +0 -7
- package/lib/goal-command.js +0 -37
- package/lib/goal.d.ts +0 -72
- package/lib/goal.js +0 -72
- package/lib/inject.d.ts +0 -90
- package/lib/inject.js +0 -164
- package/lib/injection-caps.d.ts +0 -4
- package/lib/injection-caps.js +0 -4
- package/lib/injection-directory.d.ts +0 -29
- package/lib/injection-directory.js +0 -78
- package/lib/injection-rank.d.ts +0 -85
- package/lib/injection-rank.js +0 -161
- package/lib/listener.d.ts +0 -45
- package/lib/listener.js +0 -199
- package/lib/llm-text.d.ts +0 -104
- package/lib/llm-text.js +0 -140
- package/lib/logfile.d.ts +0 -71
- package/lib/logfile.js +0 -159
- package/lib/maintenance-commands.d.ts +0 -23
- package/lib/maintenance-commands.js +0 -136
- package/lib/memory-agent.d.ts +0 -93
- package/lib/memory-agent.js +0 -300
- package/lib/memory-apply.d.ts +0 -44
- package/lib/memory-apply.js +0 -202
- package/lib/memory-benchmark.d.ts +0 -76
- package/lib/memory-benchmark.js +0 -170
- package/lib/memory-index.d.ts +0 -114
- package/lib/memory-index.js +0 -197
- package/lib/memory-manifest.d.ts +0 -16
- package/lib/memory-manifest.js +0 -70
- package/lib/message-source.d.ts +0 -23
- package/lib/message-source.js +0 -12
- package/lib/mount-command.d.ts +0 -10
- package/lib/mount-command.js +0 -48
- package/lib/mount.d.ts +0 -42
- package/lib/mount.js +0 -212
- package/lib/notify.d.ts +0 -48
- package/lib/notify.js +0 -57
- package/lib/plan-command.d.ts +0 -12
- package/lib/plan-command.js +0 -38
- package/lib/plan.d.ts +0 -16
- package/lib/plan.js +0 -126
- package/lib/planner.d.ts +0 -61
- package/lib/planner.js +0 -144
- package/lib/pool.d.ts +0 -7
- package/lib/pool.js +0 -25
- package/lib/prefix-cache.d.ts +0 -79
- package/lib/prefix-cache.js +0 -168
- package/lib/project.d.ts +0 -28
- package/lib/project.js +0 -66
- package/lib/projection.d.ts +0 -22
- package/lib/projection.js +0 -112
- package/lib/promotion.d.ts +0 -94
- package/lib/promotion.js +0 -171
- package/lib/recall.d.ts +0 -85
- package/lib/recall.js +0 -142
- package/lib/record-language.d.ts +0 -85
- package/lib/record-language.js +0 -152
- package/lib/render.d.ts +0 -13
- package/lib/render.js +0 -80
- package/lib/review-scheduler.d.ts +0 -38
- package/lib/review-scheduler.js +0 -183
- package/lib/review.d.ts +0 -60
- package/lib/review.js +0 -145
- package/lib/rollback.d.ts +0 -9
- package/lib/rollback.js +0 -68
- package/lib/rubric.d.ts +0 -29
- package/lib/rubric.js +0 -119
- package/lib/runtime-commands.d.ts +0 -14
- package/lib/runtime-commands.js +0 -115
- package/lib/runtime.d.ts +0 -24
- package/lib/runtime.js +0 -87
- package/lib/score.d.ts +0 -64
- package/lib/score.js +0 -191
- package/lib/search.d.ts +0 -83
- package/lib/search.js +0 -136
- package/lib/service.d.ts +0 -63
- package/lib/service.js +0 -253
- package/lib/skill-render.d.ts +0 -23
- package/lib/skill-render.js +0 -69
- package/lib/skill.d.ts +0 -15
- package/lib/skill.js +0 -80
- package/lib/skillquality.d.ts +0 -85
- package/lib/skillquality.js +0 -320
- package/lib/source.d.ts +0 -30
- package/lib/source.js +0 -43
- package/lib/state.d.ts +0 -26
- package/lib/state.js +0 -151
- package/lib/store.d.ts +0 -61
- package/lib/store.js +0 -164
- package/lib/token-usage.d.ts +0 -68
- package/lib/token-usage.js +0 -211
- package/lib/tool.d.ts +0 -34
- package/lib/tool.js +0 -320
- package/lib/turn-snapshot.d.ts +0 -78
- package/lib/turn-snapshot.js +0 -252
- package/lib/types.d.ts +0 -218
- package/lib/types.js +0 -133
- package/lib/usage.d.ts +0 -36
- package/lib/usage.js +0 -98
- package/lib/validate.d.ts +0 -28
- package/lib/validate.js +0 -181
- package/lib/wrapup-candidates.d.ts +0 -127
- package/lib/wrapup-candidates.js +0 -161
- package/lib/wrapup-command.d.ts +0 -10
- package/lib/wrapup-command.js +0 -243
- package/lib/wrapup-proposals.d.ts +0 -101
- package/lib/wrapup-proposals.js +0 -281
- package/lib/wrapup.d.ts +0 -56
- package/lib/wrapup.js +0 -105
package/lib/memory-guide.d.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The persistent-memory guide injected as part of the
|
|
3
|
-
* system-prompt section (evolve v2, change two).
|
|
2
|
+
* The persistent-memory guide injected as part of the memory section.
|
|
4
3
|
*
|
|
5
|
-
* This text is the write-path half of the
|
|
6
|
-
* durable memory is worth creating, which
|
|
7
|
-
* structure each type owes, and that
|
|
8
|
-
*
|
|
4
|
+
* This text is the write-path half of the ZCode-aligned design: it teaches
|
|
5
|
+
* the model when a durable memory is worth creating, which type it belongs
|
|
6
|
+
* to, what body structure each type owes, and that memories are plain
|
|
7
|
+
* markdown files written directly with the native file tools — the plugin
|
|
8
|
+
* registers no tools and no commands.
|
|
9
9
|
*
|
|
10
|
-
* Provenance:
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
* composes it with the `<memories>` block.
|
|
10
|
+
* Provenance: the when_to_save taxonomy is adapted from ZCode's persistent-
|
|
11
|
+
* memory prompt (introduced here in evolve v2); the write-path wording now
|
|
12
|
+
* targets native file operations on `<workspace>/.evolve/memory/`.
|
|
14
13
|
*/
|
|
15
|
-
/** Heading and framing before the `<memories>` block. */
|
|
16
|
-
export declare
|
|
17
|
-
/** The when_to_save / how_to_use rules and maintenance discipline. */
|
|
18
|
-
export declare const MEMORY_GUIDE_RULES = "## \u4F55\u65F6\u5199\u5165\u8BB0\u5FC6\n\n\u7528\u6237\u660E\u786E\u8981\u6C42\u8BB0\u4F4F\u65F6\uFF0C\u7ACB\u5373\u5B58\u4E3A\u6700\u5408\u9002\u7684\u7C7B\u578B\uFF1B\u8981\u6C42\u5FD8\u8BB0\u65F6\uFF0C\u627E\u5230\u5E76\u5220\u9664\u76F8\u5173\
|
|
14
|
+
/** Heading and framing before the `<memories>` block. Takes the absolute memory dir. */
|
|
15
|
+
export declare function memoryGuideIntro(memoryDir: string): string;
|
|
16
|
+
/** The file format, when_to_save / how_to_use rules, and maintenance discipline. */
|
|
17
|
+
export declare const MEMORY_GUIDE_RULES = "## \u8BB0\u5FC6\u6587\u4EF6\u7684\u683C\u5F0F\n\n---\nname: <\u77ED\u6A2A\u7EBF\u5C0F\u5199\u6807\u8BC6>\ndescription: <\u4E00\u53E5\u8BDD\u76F8\u5173\u6027\u94A9\u5B50\u2014\u2014\u51B3\u5B9A\u672A\u6765\u4F1A\u8BDD\u4F1A\u4E0D\u4F1A\u60F3\u8D77\u8FD9\u6761\u8BB0\u5FC6>\nmetadata:\n type: user | feedback | project | reference\n---\n\n<\u6B63\u6587>\n\n## \u4F55\u65F6\u5199\u5165\u8BB0\u5FC6\n\n\u7528\u6237\u660E\u786E\u8981\u6C42\u8BB0\u4F4F\u65F6\uFF0C\u7ACB\u5373\u5B58\u4E3A\u6700\u5408\u9002\u7684\u7C7B\u578B\uFF1B\u8981\u6C42\u5FD8\u8BB0\u65F6\uFF0C\u627E\u5230\u5E76\u5220\u9664\u76F8\u5173\u6587\u4EF6\u4E0E\u7D22\u5F15\u884C\u3002\n\u9664\u6B64\u4E4B\u5916\uFF0C\u5728\u5BF9\u8BDD\u4E2D\u81EA\u7136\u9047\u5230\u4EE5\u4E0B\u4FE1\u53F7\u65F6\u4E3B\u52A8\u5199\u5165\u3002\u5B58\u4E4B\u524D\u5148\u8FC7\u4E00\u9053\u7B5B\u5B50\uFF1A\n**\u80FD\u4ECE\u4EE3\u7801\u3001git \u5386\u53F2\u3001\u4ED3\u5E93\u6587\u4EF6\u91CC\u91CD\u65B0\u63A8\u5BFC\u51FA\u6765\u7684\u4E8B\u5B9E\u4E0D\u5B58**\u2014\u2014\u53EA\u5B58\u91CD\u63A8\u5BFC\u4E0D\u51FA\u6765\u7684\n\u80CC\u666F\u3001\u51B3\u7B56\u548C\u504F\u597D\u3002\u76F8\u5BF9\u65E5\u671F\u4E00\u5F8B\u8F6C\u6210\u7EDD\u5BF9\u65E5\u671F\uFF08\"\u5468\u56DB\"\u2192\"2026-10-08\"\uFF09\uFF0C\u5426\u5219\u672A\u6765\n\u7684\u4F1A\u8BDD\u65E0\u6CD5\u89E3\u8BFB\u3002\u4E00\u6761\u4E8B\u5B9E\u4E00\u4E2A\u6587\u4EF6\uFF1B\u4E0D\u786E\u5B9A\u503C\u4E0D\u503C\u5F97\u5B58\u65F6\uFF0C\u95EE\u81EA\u5DF1\"\u8FD9\u6761\u7684\u4FE1\u606F\u91CF\n\u662F\u5426\u5728'\u4E3A\u4EC0\u4E48'\u91CC\uFF0C\u800C\u4E0D\u5728'\u662F\u4EC0\u4E48'\u91CC\"\u3002\n\n<type name=\"user\">\n <when_to_save>\u5F97\u77E5\u7528\u6237\u7684\u89D2\u8272\u3001\u6280\u672F\u6808\u3001\u7ECF\u9A8C\u6C34\u5E73\u3001\u504F\u597D\u6216\u77E5\u8BC6\u80CC\u666F\u7684\u4EFB\u4F55\u7EC6\u8282\u65F6\u3002\n \u4F8B\u5982\u7528\u6237\u81EA\u8FF0\"\u6211\u662F\u6570\u636E\u79D1\u5B66\u5BB6\uFF0C\u5728\u67E5\u65E5\u5FD7\u7CFB\u7EDF\"\u2014\u2014\u5B58\uFF1A\u7528\u6237\u662F\u6570\u636E\u79D1\u5B66\u5BB6\uFF0C\n \u5F53\u524D\u5173\u6CE8\u53EF\u89C2\u6D4B\u6027/\u65E5\u5FD7\u3002</when_to_save>\n <how_to_use>\u540E\u7EED\u89E3\u91CA\u548C\u5EFA\u8BAE\u6309\u7528\u6237\u7684\u80CC\u666F\u5B9A\u5236\u6DF1\u6D45\u4E0E\u7C7B\u6BD4\uFF1B\u5BF9\u8D44\u6DF1\u5DE5\u7A0B\u5E08\n \u4E0D\u89E3\u91CA\u57FA\u7840\u6982\u5FF5\uFF0C\u5BF9\u67D0\u9886\u57DF\u7684\u65B0\u624B\u7528\u5176\u719F\u6089\u9886\u57DF\u7684\u7C7B\u6BD4\u5207\u5165\u3002</how_to_use>\n</type>\n\n<type name=\"feedback\">\n <when_to_save>\u4E24\u6761\u4FE1\u53F7\uFF0C\u7F3A\u4E00\u4E0D\u53EF\uFF1A\n \uFF081\uFF09\u7EA0\u6B63\u2014\u2014\u7528\u6237\u8BF4\"\u4E0D\u662F\u8FD9\u6837\"\"\u522B\"\"\u505C\u4E0B\"\u65F6\uFF0C\u5B58\u88AB\u7EA0\u6B63\u7684\u505A\u6CD5\u4E0E\u6B63\u786E\u505A\u6CD5\uFF1B\n \uFF082\uFF09\u786E\u8BA4\u2014\u2014\u8FD9\u662F\u66F4\u5B89\u9759\u3001\u66F4\u5BB9\u6613\u6F0F\u7684\u4FE1\u53F7\uFF1A\u7528\u6237\u63A5\u53D7\u4E86\u4F60\u4E0D\u5BFB\u5E38\u7684\u9009\u62E9\u800C\u6CA1\u6709\u5F02\u8BAE\uFF0C\n \u6216\u660E\u786E\u8BF4\"\u5BF9\uFF0C\u5C31\u8FD9\u6837\"\"\u4FDD\u6301\u8FD9\u6837\"\u3002\u786E\u8BA4\u8FC7\u7684\u5224\u65AD\u662F\u5DF2\u9A8C\u8BC1\u7684\u65B9\u6CD5\u8BBA\uFF0C\u53EA\u5B58\u7EA0\u9519\n \u4F1A\u8BA9\u4F60\u7684\u884C\u4E3A\u8D8A\u6765\u8D8A\u4FDD\u5B88\u3001\u504F\u79BB\u7528\u6237\u5DF2\u8BA4\u53EF\u7684\u8DEF\u3002\n \u4F8B\uFF1A\"\u6D4B\u8BD5\u522B mock \u6570\u636E\u5E93\uFF0C\u4E0A\u6B21 mock \u8FC7\u4E86\u7EBF\u4E0A\u8FC1\u79FB\u6302\u4E86\"\u2014\u2014\u5B58\u89C4\u5219\u5E76\u5E26\u4E0A\u4E8B\u6545\u539F\u56E0\u3002\n \u4F8B\uFF1A\"\u7ED3\u5C3E\u4E0D\u7528\u603B\u7ED3\uFF0Cdiff \u6211\u81EA\u5DF1\u4F1A\u770B\"\u2014\u2014\u5B58\uFF1A\u8BE5\u7528\u6237\u4E0D\u8981\u5C3E\u968F\u6027\u603B\u7ED3\u3002</when_to_save>\n <how_to_use>\u8BA9\u7528\u6237\u4E0D\u5FC5\u628A\u540C\u4E00\u6761\u6307\u5BFC\u8BF4\u7B2C\u4E8C\u904D\u3002</how_to_use>\n <body_structure>\u89C4\u5219\u5148\u884C\uFF0C\u968F\u540E\u5FC5\u5E26\u4E24\u884C\uFF1A**Why:**\uFF08\u7528\u6237\u7ED9\u7684\u7406\u7531\uFF0C\u5E38\u662F\u67D0\u6B21\n \u4E8B\u6545\u6216\u5F3A\u504F\u597D\u2014\u2014\u77E5\u9053\u4E3A\u4EC0\u4E48\u624D\u80FD\u5224\u65AD\u8FB9\u7F18\u60C5\u51B5\u8BE5\u4E0D\u8BE5\u7834\u4F8B\uFF09\u548C **How to apply:**\n \uFF08\u4F55\u65F6\u4F55\u5730\u751F\u6548\uFF09\u3002feedback \u7C7B\u578B\u7F3A\u8FD9\u4E24\u884C\u89C6\u4E3A\u4E0D\u5B8C\u6574\u3002</body_structure>\n</type>\n\n<type name=\"project\">\n <when_to_save>\u5F97\u77E5\u8FDB\u884C\u4E2D\u7684\u5DE5\u4F5C\u3001\u76EE\u6807\u3001\u51B3\u7B56\u3001\u622A\u6B62\u671F\u6216\u4E8B\u6545\u7684\u80CC\u666F\u52A8\u673A\u65F6\u2014\u2014\n \u51E1\u662F\"\u8C01\u5728\u505A\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u3001\u5230\u4EC0\u4E48\u65F6\u5019\"\u4E14\u65E0\u6CD5\u4ECE\u4EE3\u7801\u6216 git \u63A8\u5BFC\u7684\u4FE1\u606F\u3002\n \u8FD9\u7C7B\u8BB0\u5FC6\u8870\u51CF\u5FEB\uFF0C\u72B6\u6001\u53D8\u5316\u65F6\u66F4\u65B0\u5B83\uFF0C\u522B\u5806\u53E0\u65B0\u6587\u4EF6\u3002</when_to_save>\n <how_to_use>\u7528\u5B83\u8865\u5168\u8BF7\u6C42\u80CC\u540E\u7684\u4E0A\u4E0B\u6587\u4E0E\u52A8\u673A\uFF0C\u8BA9\u5EFA\u8BAE\u8D34\u4F4F\u771F\u5B9E\u7EA6\u675F\u3002\n \u4F8B\uFF1A\"\u5468\u56DB\u8D77\u51BB\u7ED3\u5408\u5E76\"\u2014\u2014\u5B58\uFF1A2026-03-05 \u8D77\u51BB\u7ED3\u5408\u5E76\uFF0C\u975E\u5173\u952E PR \u63D0\u524D\u6807\u8BB0\u3002</how_to_use>\n <body_structure>\u4E8B\u5B9E/\u51B3\u7B56\u5148\u884C + **Why:**\uFF08\u52A8\u673A\uFF1A\u7EA6\u675F\u3001deadline\u3001\u5E72\u7CFB\u4EBA\u8981\u6C42\uFF09\n + **How to apply:**\uFF08\u8FD9\u6761\u5982\u4F55\u5F71\u54CD\u540E\u7EED\u5EFA\u8BAE\uFF09\u3002</body_structure>\n</type>\n\n<type name=\"reference\">\n <when_to_save>\u7528\u6237\u63D0\u5230\u672A\u6765\u4F1A\u7528\u5230\u7684\u5916\u90E8\u8D44\u6E90\u6307\u9488\uFF1AURL\u3001\u4EEA\u8868\u76D8\u3001\u5DE5\u5355\u3001\n \u6587\u6863\u4F4D\u7F6E\u3002</when_to_save>\n <how_to_use>\u9700\u8981\u65F6\u76F4\u63A5\u53D6\u7528\uFF0C\u4E0D\u91CD\u65B0\u68C0\u7D22\u3002</how_to_use>\n</type>\n\n## \u5982\u4F55\u7EF4\u62A4\n\n- \u5199\u5B8C\u8BB0\u5FC6\u6587\u4EF6\u540E\u7ACB\u5373\u5728 MEMORY.md \u8FFD\u52A0\u6216\u66F4\u65B0\u4E00\u884C\u7D22\u5F15\uFF1A`- [\u6807\u9898](\u6587\u4EF6\u540D.md) \u2014 \u94A9\u5B50`\uFF1B\u72B6\u6001\u53D8\u5316\u65F6\u539F\u6587\u4E0E\u7D22\u5F15\u884C\u4E00\u8D77\u6539\uFF0C\u522B\u8BA9\u7D22\u5F15\u8BF4\u8C0E\u3002\n- \u66F4\u65B0\u4F18\u5148\u4E8E\u65B0\u5EFA\uFF1A\u72B6\u6001\u53D8\u4E86\u6539\u539F\u6587\u4EF6\uFF0C\u4E0D\u53E6\u8D77\u4E00\u6761\uFF08\u65B0\u65E7\u5E76\u5B58\u6BD4\u6CA1\u6709\u8BB0\u5FC6\u66F4\u7CDF\uFF09\u3002\n- [[\u540D\u5B57]] \u4E92\u76F8\u5F15\u7528\u76F8\u5173\u8BB0\u5FC6\uFF1B\u5F15\u7528\u8FD8\u4E0D\u5B58\u5728\u7684\u540D\u5B57\u662F\u5408\u6CD5\u7684\uFF0C\u5B83\u6807\u8BB0\"\u503C\u5F97\u5C06\u6765\u8865\u5199\"\u3002";
|
|
19
18
|
//# sourceMappingURL=memory-guide.d.ts.map
|
package/lib/memory-guide.js
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The persistent-memory guide injected as part of the
|
|
3
|
-
* system-prompt section (evolve v2, change two).
|
|
2
|
+
* The persistent-memory guide injected as part of the memory section.
|
|
4
3
|
*
|
|
5
|
-
* This text is the write-path half of the
|
|
6
|
-
* durable memory is worth creating, which
|
|
7
|
-
* structure each type owes, and that
|
|
8
|
-
*
|
|
4
|
+
* This text is the write-path half of the ZCode-aligned design: it teaches
|
|
5
|
+
* the model when a durable memory is worth creating, which type it belongs
|
|
6
|
+
* to, what body structure each type owes, and that memories are plain
|
|
7
|
+
* markdown files written directly with the native file tools — the plugin
|
|
8
|
+
* registers no tools and no commands.
|
|
9
9
|
*
|
|
10
|
-
* Provenance:
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
* composes it with the `<memories>` block.
|
|
10
|
+
* Provenance: the when_to_save taxonomy is adapted from ZCode's persistent-
|
|
11
|
+
* memory prompt (introduced here in evolve v2); the write-path wording now
|
|
12
|
+
* targets native file operations on `<workspace>/.evolve/memory/`.
|
|
14
13
|
*/
|
|
15
|
-
/** Heading and framing before the `<memories>` block. */
|
|
16
|
-
export
|
|
14
|
+
/** Heading and framing before the `<memories>` block. Takes the absolute memory dir. */
|
|
15
|
+
export function memoryGuideIntro(memoryDir) {
|
|
16
|
+
return `# 持久记忆
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
你有一个跨会话的持久记忆系统,存放在 \`${memoryDir}/\`:一事一文,\`MEMORY.md\` 是索引,其内容已注入在下方。没有专用命令或工具——直接用你的文件读写工具查看、新建、修改、删除记忆文件,并在同一会话内顺手落盘。`;
|
|
19
|
+
}
|
|
20
|
+
/** The file format, when_to_save / how_to_use rules, and maintenance discipline. */
|
|
21
|
+
export const MEMORY_GUIDE_RULES = `## 记忆文件的格式
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
---
|
|
24
|
+
name: <短横线小写标识>
|
|
25
|
+
description: <一句话相关性钩子——决定未来会话会不会想起这条记忆>
|
|
26
|
+
metadata:
|
|
27
|
+
type: user | feedback | project | reference
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<正文>
|
|
31
|
+
|
|
32
|
+
## 何时写入记忆
|
|
33
|
+
|
|
34
|
+
用户明确要求记住时,立即存为最合适的类型;要求忘记时,找到并删除相关文件与索引行。
|
|
23
35
|
除此之外,在对话中自然遇到以下信号时主动写入。存之前先过一道筛子:
|
|
24
36
|
**能从代码、git 历史、仓库文件里重新推导出来的事实不存**——只存重推导不出来的
|
|
25
37
|
背景、决策和偏好。相对日期一律转成绝对日期("周四"→"2026-10-08"),否则未来
|
|
26
|
-
|
|
38
|
+
的会话无法解读。一条事实一个文件;不确定值不值得存时,问自己"这条的信息量
|
|
27
39
|
是否在'为什么'里,而不在'是什么'里"。
|
|
28
40
|
|
|
29
41
|
<type name="user">
|
|
@@ -51,7 +63,7 @@ export const MEMORY_GUIDE_RULES = `## 何时写入记忆
|
|
|
51
63
|
<type name="project">
|
|
52
64
|
<when_to_save>得知进行中的工作、目标、决策、截止期或事故的背景动机时——
|
|
53
65
|
凡是"谁在做什么、为什么、到什么时候"且无法从代码或 git 推导的信息。
|
|
54
|
-
|
|
66
|
+
这类记忆衰减快,状态变化时更新它,别堆叠新文件。</when_to_save>
|
|
55
67
|
<how_to_use>用它补全请求背后的上下文与动机,让建议贴住真实约束。
|
|
56
68
|
例:"周四起冻结合并"——存:2026-03-05 起冻结合并,非关键 PR 提前标记。</how_to_use>
|
|
57
69
|
<body_structure>事实/决策先行 + **Why:**(动机:约束、deadline、干系人要求)
|
|
@@ -66,8 +78,7 @@ export const MEMORY_GUIDE_RULES = `## 何时写入记忆
|
|
|
66
78
|
|
|
67
79
|
## 如何维护
|
|
68
80
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
被想起"的一句话,不写流水账。
|
|
81
|
+
- 写完记忆文件后立即在 MEMORY.md 追加或更新一行索引:\`- [标题](文件名.md) — 钩子\`;状态变化时原文与索引行一起改,别让索引说谎。
|
|
82
|
+
- 更新优先于新建:状态变了改原文件,不另起一条(新旧并存比没有记忆更糟)。
|
|
72
83
|
- [[名字]] 互相引用相关记忆;引用还不存在的名字是合法的,它标记"值得将来补写"。`;
|
|
73
84
|
//# sourceMappingURL=memory-guide.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Name of the injected section (unique — duplicate names throw upstream). */
|
|
2
|
+
export declare const MEMORY_SECTION_NAME = "evolve:memory-index";
|
|
3
|
+
/** Default prompt-section order (empty 1–499 slot; upstream named slots start at 500). */
|
|
4
|
+
export declare const DEFAULT_MEMORY_SECTION_ORDER = 400;
|
|
5
|
+
/** Default hard character budget for the injected index. */
|
|
6
|
+
export declare const DEFAULT_MAX_CHARS = 6000;
|
|
7
|
+
/** Sessions whose frozen section text is kept before LRU eviction. */
|
|
8
|
+
export declare const DEFAULT_FROZEN_SESSIONS = 32;
|
|
9
|
+
/** The assembling agent's validated absolute cwd, or undefined when absent. */
|
|
10
|
+
export declare function cwdOf(agent: unknown): string | undefined;
|
|
11
|
+
/** Absolute memory directory for a workspace root. */
|
|
12
|
+
export declare function memoryDirFor(cwd: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Ensure `.evolve/` is ignored when the workspace is a git repository, so
|
|
15
|
+
* the memory store never shows up in git status. Best-effort hygiene: a
|
|
16
|
+
* non-git workspace (no `.git` entry at the cwd) is left untouched — no
|
|
17
|
+
* `.gitignore` is created — and any write failure is swallowed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ensureGitIgnored(cwd: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Ensure the store exists (creating the directory and a starter index on
|
|
22
|
+
* first use), then return the trimmed index text. Any filesystem failure
|
|
23
|
+
* degrades to "" — injection must never break an assembly.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readMemoryIndex(dir: string, bootstrap?: boolean): string;
|
|
26
|
+
/** What fitting the index into the budget produced. */
|
|
27
|
+
export interface IndexFit {
|
|
28
|
+
/** Index text actually shown (possibly truncated to a line boundary). */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Characters dropped by the budget, 0 when the index fit whole. */
|
|
31
|
+
dropped: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whole-line-preserving truncation of the index to `maxChars` characters.
|
|
35
|
+
* Only index rows that fit whole are shown — a partial markdown row would
|
|
36
|
+
* corrupt the line the model reads. When not even one row fits, `text` is
|
|
37
|
+
* "" and `dropped` carries the full length.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fitIndex(index: string, maxChars: number): IndexFit;
|
|
40
|
+
/** Options for {@link memorySectionText}. */
|
|
41
|
+
export interface MemorySectionOptions {
|
|
42
|
+
/** Hard character budget for the injected index (default 6000). */
|
|
43
|
+
maxChars?: number;
|
|
44
|
+
/** Include the when_to_save guide (default true). */
|
|
45
|
+
guide?: boolean;
|
|
46
|
+
/** Skip store bootstrap (tests). */
|
|
47
|
+
bootstrap?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compose the full memory section for one assembling agent: the guide
|
|
51
|
+
* wrapped around the budget-bounded index, with the absolute store path
|
|
52
|
+
* handed out so the model can read and write the files directly. With the
|
|
53
|
+
* guide off and an empty index the result is "" — the prompt renderer then
|
|
54
|
+
* drops the section, so an empty workspace costs zero tokens.
|
|
55
|
+
*/
|
|
56
|
+
export declare function memorySectionText(agent: unknown, opts?: MemorySectionOptions): string;
|
|
57
|
+
/** A per-session frozen view of the memory section text. */
|
|
58
|
+
export interface FrozenMemorySection {
|
|
59
|
+
/**
|
|
60
|
+
* The section text for this agent: built on first use per agent id and
|
|
61
|
+
* reused byte-for-byte afterwards. An agent without an id is rebuilt on
|
|
62
|
+
* every call (nothing to key the cache on).
|
|
63
|
+
*/
|
|
64
|
+
textFor(agent: unknown, build: (agent: unknown) => string): string;
|
|
65
|
+
/** Sessions currently frozen (test/observability hook). */
|
|
66
|
+
size(): number;
|
|
67
|
+
/** Drop every frozen session (tests; a restart rebuilds naturally). */
|
|
68
|
+
clear(): void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Freeze the section text per agent id. The frozen value is what keeps the
|
|
72
|
+
* system prompt stable across a session's assemblies (prompt-cache guard);
|
|
73
|
+
* bounded by `maxSessions` with insertion-order (LRU) eviction, so a
|
|
74
|
+
* long-lived process cannot grow without limit.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createFrozenMemorySection(maxSessions?: number): FrozenMemorySection;
|
|
77
|
+
//# sourceMappingURL=memory-section.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole runtime of the plugin after the 2026-10-04 ZCode-alignment
|
|
3
|
+
* teardown: the session-start memory section.
|
|
4
|
+
*
|
|
5
|
+
* The store is plain markdown inside the workspace — `<cwd>/.evolve/memory/`
|
|
6
|
+
* with a `MEMORY.md` index and one file per fact — so the model reads and
|
|
7
|
+
* writes it with its native file tools (DSH fences only out-of-workspace
|
|
8
|
+
* writes; dot-directories inside the workspace pass both ways). This module
|
|
9
|
+
* hands out the absolute path, injects the index content, and wraps the
|
|
10
|
+
* when-to-save guide around it. There are no tools, no scopes, and no
|
|
11
|
+
* governance: the files are the store, and a bad memory is a visible file
|
|
12
|
+
* the user deletes.
|
|
13
|
+
*
|
|
14
|
+
* Session-freeze contract (the prompt-cache guard): the section is computed
|
|
15
|
+
* once per agent id and reused byte-for-byte afterwards, so the system
|
|
16
|
+
* prompt stays stable within a session and cache reads keep hitting. A
|
|
17
|
+
* memory written mid-session becomes visible in the NEXT session; the model
|
|
18
|
+
* can always read the directory directly for the immediate need.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
22
|
+
import { MEMORY_GUIDE_RULES, memoryGuideIntro } from "./memory-guide.js";
|
|
23
|
+
/** Name of the injected section (unique — duplicate names throw upstream). */
|
|
24
|
+
export const MEMORY_SECTION_NAME = "evolve:memory-index";
|
|
25
|
+
/** Default prompt-section order (empty 1–499 slot; upstream named slots start at 500). */
|
|
26
|
+
export const DEFAULT_MEMORY_SECTION_ORDER = 400;
|
|
27
|
+
/** Default hard character budget for the injected index. */
|
|
28
|
+
export const DEFAULT_MAX_CHARS = 6000;
|
|
29
|
+
/** Sessions whose frozen section text is kept before LRU eviction. */
|
|
30
|
+
export const DEFAULT_FROZEN_SESSIONS = 32;
|
|
31
|
+
const MEMORY_DIR_SEGMENTS = [".evolve", "memory"];
|
|
32
|
+
const INDEX_FILE = "MEMORY.md";
|
|
33
|
+
/** Starter index written on first use; the model maintains every line after. */
|
|
34
|
+
const STARTER_INDEX = "# 记忆索引\n\n<!-- 一行一条:- [标题](文件名.md) — 一句话相关性钩子;写新记忆后在此追加一行 -->\n";
|
|
35
|
+
/** Appended to the workspace .gitignore on bootstrap (git workspaces only). */
|
|
36
|
+
const GITIGNORE_ENTRY = "# workspace memory (dsh-continual-evolve)\n.evolve/\n";
|
|
37
|
+
/** The assembling agent's validated absolute cwd, or undefined when absent. */
|
|
38
|
+
export function cwdOf(agent) {
|
|
39
|
+
try {
|
|
40
|
+
const header = agent?.session?.header;
|
|
41
|
+
const raw = header?.cwd ?? header?.meta?.cwd;
|
|
42
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const cwd = raw.trim();
|
|
46
|
+
if (!isAbsolute(cwd)) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return resolve(cwd);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Absolute memory directory for a workspace root. */
|
|
56
|
+
export function memoryDirFor(cwd) {
|
|
57
|
+
return join(resolve(cwd), ...MEMORY_DIR_SEGMENTS);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Ensure `.evolve/` is ignored when the workspace is a git repository, so
|
|
61
|
+
* the memory store never shows up in git status. Best-effort hygiene: a
|
|
62
|
+
* non-git workspace (no `.git` entry at the cwd) is left untouched — no
|
|
63
|
+
* `.gitignore` is created — and any write failure is swallowed.
|
|
64
|
+
*/
|
|
65
|
+
export function ensureGitIgnored(cwd) {
|
|
66
|
+
try {
|
|
67
|
+
if (!existsSync(join(cwd, ".git"))) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const gitignore = join(cwd, ".gitignore");
|
|
71
|
+
const existing = existsSync(gitignore) ? readFileSync(gitignore, "utf8") : "";
|
|
72
|
+
if (existing.split("\n").some((line) => line.trim() === ".evolve/")) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
76
|
+
writeFileSync(gitignore, `${existing}${prefix}${GITIGNORE_ENTRY}`, "utf8");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Never blocks the store; worst case is a visible .evolve/ in git status.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Ensure the store exists (creating the directory and a starter index on
|
|
84
|
+
* first use), then return the trimmed index text. Any filesystem failure
|
|
85
|
+
* degrades to "" — injection must never break an assembly.
|
|
86
|
+
*/
|
|
87
|
+
export function readMemoryIndex(dir, bootstrap = true) {
|
|
88
|
+
try {
|
|
89
|
+
if (bootstrap && !existsSync(dir)) {
|
|
90
|
+
mkdirSync(dir, { recursive: true });
|
|
91
|
+
writeFileSync(join(dir, INDEX_FILE), STARTER_INDEX, "utf8");
|
|
92
|
+
ensureGitIgnored(resolve(dir, "..", ".."));
|
|
93
|
+
}
|
|
94
|
+
return readFileSync(join(dir, INDEX_FILE), "utf8").trim();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whole-line-preserving truncation of the index to `maxChars` characters.
|
|
102
|
+
* Only index rows that fit whole are shown — a partial markdown row would
|
|
103
|
+
* corrupt the line the model reads. When not even one row fits, `text` is
|
|
104
|
+
* "" and `dropped` carries the full length.
|
|
105
|
+
*/
|
|
106
|
+
export function fitIndex(index, maxChars) {
|
|
107
|
+
const budget = Math.max(0, maxChars);
|
|
108
|
+
if (index.length <= budget) {
|
|
109
|
+
return { text: index, dropped: 0 };
|
|
110
|
+
}
|
|
111
|
+
const cut = index.slice(0, budget);
|
|
112
|
+
const lastLine = cut.lastIndexOf("\n");
|
|
113
|
+
if (lastLine <= 0) {
|
|
114
|
+
return { text: "", dropped: index.length };
|
|
115
|
+
}
|
|
116
|
+
const text = cut.slice(0, lastLine);
|
|
117
|
+
return { text, dropped: index.length - text.length };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Compose the full memory section for one assembling agent: the guide
|
|
121
|
+
* wrapped around the budget-bounded index, with the absolute store path
|
|
122
|
+
* handed out so the model can read and write the files directly. With the
|
|
123
|
+
* guide off and an empty index the result is "" — the prompt renderer then
|
|
124
|
+
* drops the section, so an empty workspace costs zero tokens.
|
|
125
|
+
*/
|
|
126
|
+
export function memorySectionText(agent, opts) {
|
|
127
|
+
const guide = opts?.guide ?? true;
|
|
128
|
+
const maxChars = opts?.maxChars ?? DEFAULT_MAX_CHARS;
|
|
129
|
+
const cwd = cwdOf(agent);
|
|
130
|
+
if (!cwd) {
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
const dir = memoryDirFor(cwd);
|
|
134
|
+
const index = readMemoryIndex(dir, opts?.bootstrap ?? true);
|
|
135
|
+
const { text, dropped } = fitIndex(index, maxChars);
|
|
136
|
+
const parts = [];
|
|
137
|
+
if (index.length === 0) {
|
|
138
|
+
parts.push("<memories>\n(暂无记忆)\n</memories>");
|
|
139
|
+
}
|
|
140
|
+
else if (text.length > 0) {
|
|
141
|
+
parts.push(`<memories>\n${text}\n</memories>`);
|
|
142
|
+
if (dropped > 0) {
|
|
143
|
+
parts.push(`(索引超出 ${maxChars} 字符预算,已截断——其余记忆直接读取 \`${dir}/\` 目录)`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
parts.push(`(索引超出 ${maxChars} 字符预算,一行都放不下——记忆直接读取 \`${dir}/\` 目录)`);
|
|
148
|
+
}
|
|
149
|
+
if (!guide) {
|
|
150
|
+
return index.length === 0 ? "" : parts.join("\n\n");
|
|
151
|
+
}
|
|
152
|
+
return [memoryGuideIntro(dir), ...parts, MEMORY_GUIDE_RULES].join("\n\n");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Freeze the section text per agent id. The frozen value is what keeps the
|
|
156
|
+
* system prompt stable across a session's assemblies (prompt-cache guard);
|
|
157
|
+
* bounded by `maxSessions` with insertion-order (LRU) eviction, so a
|
|
158
|
+
* long-lived process cannot grow without limit.
|
|
159
|
+
*/
|
|
160
|
+
export function createFrozenMemorySection(maxSessions = DEFAULT_FROZEN_SESSIONS) {
|
|
161
|
+
const limit = Math.max(1, maxSessions);
|
|
162
|
+
const frozen = new Map();
|
|
163
|
+
return {
|
|
164
|
+
textFor(agent, build) {
|
|
165
|
+
const id = agent?.id;
|
|
166
|
+
const key = typeof id === "string" && id.length > 0 ? id : undefined;
|
|
167
|
+
if (key === undefined) {
|
|
168
|
+
return build(agent);
|
|
169
|
+
}
|
|
170
|
+
const cached = frozen.get(key);
|
|
171
|
+
if (cached !== undefined) {
|
|
172
|
+
return cached;
|
|
173
|
+
}
|
|
174
|
+
const text = build(agent);
|
|
175
|
+
frozen.set(key, text);
|
|
176
|
+
while (frozen.size > limit) {
|
|
177
|
+
const oldest = frozen.keys().next();
|
|
178
|
+
if (oldest.done === true) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
frozen.delete(oldest.value);
|
|
182
|
+
}
|
|
183
|
+
return text;
|
|
184
|
+
},
|
|
185
|
+
size: () => frozen.size,
|
|
186
|
+
clear: () => frozen.clear(),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=memory-section.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-continual-evolve",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "ZCode-style workspace memory for DeepSeek Harness: a per-workspace markdown store (<workspace>/.evolve/memory/) with session-start index injection — the model reads and writes the files with its native tools.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -19,10 +19,6 @@
|
|
|
19
19
|
"types": "./lib/index.d.ts",
|
|
20
20
|
"default": "./lib/index.js"
|
|
21
21
|
},
|
|
22
|
-
"./types": {
|
|
23
|
-
"types": "./lib/types.d.ts",
|
|
24
|
-
"default": "./lib/types.js"
|
|
25
|
-
},
|
|
26
22
|
"./package.json": "./package.json"
|
|
27
23
|
},
|
|
28
24
|
"files": [
|
|
@@ -51,7 +47,6 @@
|
|
|
51
47
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.scripts.json --noEmit",
|
|
52
48
|
"test": "vitest run",
|
|
53
49
|
"test:coverage": "vitest run --coverage",
|
|
54
|
-
"coverage:gaps": "tsx scripts/coverage-gaps.ts",
|
|
55
50
|
"test:watch": "vitest",
|
|
56
51
|
"lint": "oxlint src test",
|
|
57
52
|
"clean": "rm -rf lib",
|
|
@@ -60,19 +55,9 @@
|
|
|
60
55
|
"check:governance": "tsx scripts/verify-governance.ts"
|
|
61
56
|
},
|
|
62
57
|
"peerDependencies": {
|
|
63
|
-
"@deepseek-ai/cordis": "^4.0.1",
|
|
64
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
|
|
65
|
-
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
|
|
66
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
|
|
67
58
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
68
59
|
},
|
|
69
60
|
"devDependencies": {
|
|
70
|
-
"@deepseek-ai/cordis": "4.0.4",
|
|
71
|
-
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
72
|
-
"@deepseek-ai/dsh-commands": "0.2.0-rc.2",
|
|
73
|
-
"@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
|
|
74
|
-
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
75
|
-
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
76
61
|
"@deepseek-ai/schemastery": "3.18.4",
|
|
77
62
|
"@types/node": "^22.10.0",
|
|
78
63
|
"@vitest/coverage-v8": "^3.2.0",
|
package/lib/apply.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The apply pass: turn a validated proposal into state changes with full
|
|
3
|
-
* per-edit accounting. Every failure is recorded per edit — a bad edit never
|
|
4
|
-
* invalidates the whole proposal — and optimistic-concurrency checks reject
|
|
5
|
-
* edits whose target entry changed while planning was in flight.
|
|
6
|
-
*/
|
|
7
|
-
import type { EntrySource, HarnessScope, HarnessState, RefinementProposal, RefinementResult } from "./types.js";
|
|
8
|
-
export interface ApplyOptions {
|
|
9
|
-
id: string;
|
|
10
|
-
scope?: HarnessScope;
|
|
11
|
-
rollbackOf?: string;
|
|
12
|
-
/** State captured before planning; used to reject conflicting edits. */
|
|
13
|
-
baselineState?: HarnessState;
|
|
14
|
-
/**
|
|
15
|
-
* Trajectory citation stamped into newly created entries' metadata
|
|
16
|
-
* (sourceSession + sourceSeqs); updates keep whatever the entry already
|
|
17
|
-
* carries. Omitted entirely when the caller cannot determine it.
|
|
18
|
-
*/
|
|
19
|
-
source?: EntrySource;
|
|
20
|
-
}
|
|
21
|
-
export declare function applyRefinementProposal(state: HarnessState, proposal: RefinementProposal, options: ApplyOptions): RefinementResult;
|
|
22
|
-
//# sourceMappingURL=apply.d.ts.map
|
package/lib/apply.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { SOURCE_SESSION_KEY, SOURCE_SEQS_KEY, ARCHIVED_AT_KEY, cloneEntry, isArchived, slug } from "./types.js";
|
|
2
|
-
import { entryChangedSince } from "./state.js";
|
|
3
|
-
import { validateEdit } from "./validate.js";
|
|
4
|
-
export function applyRefinementProposal(state, proposal, options) {
|
|
5
|
-
const appliedEdits = [];
|
|
6
|
-
const touched = new Set();
|
|
7
|
-
const now = new Date().toISOString();
|
|
8
|
-
for (const edit of proposal.edits) {
|
|
9
|
-
// Store-prefix hygiene: planner edits are written against the MERGED
|
|
10
|
-
// view, where colliding project/local ids carry a `project:` /
|
|
11
|
-
// `local:` / `global:` prefix. A CREATE must never bake that view
|
|
12
|
-
// prefix into a permanent id (observed: global entries literally
|
|
13
|
-
// named "local:handoff_todo_…").
|
|
14
|
-
// Non-create edits address existing entries: the literal id wins —
|
|
15
|
-
// legacy stores contain entries whose literal id carries a colon
|
|
16
|
-
// prefix, and stripping unconditionally would resolve them to a
|
|
17
|
-
// sibling that does not exist ("entry not found", forcing file-level
|
|
18
|
-
// surgery). Only when the literal id is absent does the stripped
|
|
19
|
-
// form serve as the merged-view fallback.
|
|
20
|
-
// (Review audit 2026-08-28 S7 origin; literal-first fix 2026-09-24.)
|
|
21
|
-
const strippedId = edit.id?.replace(/^(?:local|project|global):/, "");
|
|
22
|
-
const requestedId = edit.action === "create"
|
|
23
|
-
? strippedId
|
|
24
|
-
: edit.id !== undefined && state.entries[edit.kind]?.[edit.id] !== undefined
|
|
25
|
-
? edit.id
|
|
26
|
-
: edit.id && strippedId;
|
|
27
|
-
const computedId = requestedId ?? (edit.action === "create" ? slug(edit.title ?? edit.kind, edit.kind) : undefined);
|
|
28
|
-
const id = computedId ?? "";
|
|
29
|
-
// Unknown kinds fail per-edit — a malformed proposal must never crash
|
|
30
|
-
// the whole pass (review audit 2026-08-28 S1).
|
|
31
|
-
const records = state.entries[edit.kind];
|
|
32
|
-
if (!records) {
|
|
33
|
-
appliedEdits.push({ ...edit, id, applied: false, error: `unsupported kind ${String(edit.kind)}` });
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
const before = cloneEntry(records[id]);
|
|
37
|
-
const entryKey = `${edit.kind}:${id}`;
|
|
38
|
-
if (options.baselineState && !touched.has(entryKey) && entryChangedSince(options.baselineState, state, edit.kind, id)) {
|
|
39
|
-
appliedEdits.push({
|
|
40
|
-
...edit,
|
|
41
|
-
id,
|
|
42
|
-
...(before ? { before } : {}),
|
|
43
|
-
applied: false,
|
|
44
|
-
error: "entry changed during planning",
|
|
45
|
-
});
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
// Validation sees the current entry so update rules can distinguish
|
|
49
|
-
// "carrying the persisted value" from "changing it" (skill_kind).
|
|
50
|
-
const validationError = validateEdit(edit, computedId, options.scope, before);
|
|
51
|
-
if (validationError) {
|
|
52
|
-
appliedEdits.push({ ...edit, id, applied: false, error: validationError });
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (edit.action === "delete") {
|
|
56
|
-
if (!before) {
|
|
57
|
-
appliedEdits.push({ ...edit, id, applied: false, error: "entry not found" });
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
delete records[id];
|
|
61
|
-
touched.add(entryKey);
|
|
62
|
-
appliedEdits.push({ ...edit, id, before, applied: true });
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (edit.action === "archive") {
|
|
66
|
-
// Archive hides an entry from injection but never deletes it:
|
|
67
|
-
// metadata.archivedAt is stamped through the normal apply path, so
|
|
68
|
-
// the edit gets a before/after snapshot, a version bump, and a
|
|
69
|
-
// rollback inverse like any other edit (restoring the snapshot
|
|
70
|
-
// clears the stamp). Idempotency is explicit: re-archiving an
|
|
71
|
-
// already-archived entry is an error, not a silent no-op.
|
|
72
|
-
if (!before) {
|
|
73
|
-
appliedEdits.push({ ...edit, id, applied: false, error: "entry not found" });
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (isArchived(before)) {
|
|
77
|
-
appliedEdits.push({ ...edit, id, before, applied: false, error: "entry already archived" });
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const after = {
|
|
81
|
-
...before,
|
|
82
|
-
metadata: { ...before.metadata, [ARCHIVED_AT_KEY]: now },
|
|
83
|
-
updated_at: now,
|
|
84
|
-
version: before.version + 1,
|
|
85
|
-
};
|
|
86
|
-
records[id] = after;
|
|
87
|
-
touched.add(entryKey);
|
|
88
|
-
appliedEdits.push({ ...edit, id, before, after: cloneEntry(after) ?? after, applied: true });
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (edit.action === "create" && before) {
|
|
92
|
-
appliedEdits.push({ ...edit, id, before, applied: false, error: "entry already exists" });
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (edit.action === "update" && !before) {
|
|
96
|
-
appliedEdits.push({ ...edit, id, applied: false, error: "entry not found" });
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
// Trajectory citation: stamped on create (the distillation moment),
|
|
100
|
-
// never re-stamped on update (the entry keeps its original source).
|
|
101
|
-
// Model-supplied metadata wins on key collision.
|
|
102
|
-
const sourceMetadata = !before && options.source
|
|
103
|
-
? {
|
|
104
|
-
[SOURCE_SESSION_KEY]: options.source.sessionId,
|
|
105
|
-
...(options.source.seqs && options.source.seqs.length > 0
|
|
106
|
-
? { [SOURCE_SEQS_KEY]: options.source.seqs }
|
|
107
|
-
: {}),
|
|
108
|
-
}
|
|
109
|
-
: {};
|
|
110
|
-
const skillKind = edit.skill_kind ?? before?.skill_kind;
|
|
111
|
-
const after = {
|
|
112
|
-
id,
|
|
113
|
-
kind: edit.kind,
|
|
114
|
-
title: edit.title ?? before?.title ?? id,
|
|
115
|
-
content: edit.content ?? before?.content ?? "",
|
|
116
|
-
path: edit.path ?? before?.path ?? "general",
|
|
117
|
-
scope: before?.scope ?? options.scope ?? "local",
|
|
118
|
-
reference: edit.reference ?? before?.reference ?? {},
|
|
119
|
-
arguments: edit.arguments ?? before?.arguments ?? {},
|
|
120
|
-
...(skillKind !== undefined ? { skill_kind: skillKind } : {}),
|
|
121
|
-
metadata: { ...sourceMetadata, ...(edit.metadata ?? before?.metadata ?? {}) },
|
|
122
|
-
source: "evolve",
|
|
123
|
-
created_at: before?.created_at ?? now,
|
|
124
|
-
updated_at: now,
|
|
125
|
-
version: before ? before.version + 1 : 1,
|
|
126
|
-
};
|
|
127
|
-
records[id] = after;
|
|
128
|
-
touched.add(entryKey);
|
|
129
|
-
appliedEdits.push({
|
|
130
|
-
...edit,
|
|
131
|
-
id,
|
|
132
|
-
...(before ? { before } : {}),
|
|
133
|
-
after: cloneEntry(after) ?? after,
|
|
134
|
-
applied: true,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
const changes = appliedEdits.filter((edit) => edit.applied).map((edit) => `${edit.action} ${edit.kind}:${edit.id}`);
|
|
138
|
-
state.refinements.push({
|
|
139
|
-
id: options.id,
|
|
140
|
-
trigger: proposal.summary,
|
|
141
|
-
changes,
|
|
142
|
-
evidence: proposal.rationale,
|
|
143
|
-
outcome: proposal.expectedOutcome,
|
|
144
|
-
created_at: now,
|
|
145
|
-
});
|
|
146
|
-
return {
|
|
147
|
-
id: options.id,
|
|
148
|
-
summary: proposal.summary,
|
|
149
|
-
rationale: proposal.rationale,
|
|
150
|
-
expectedOutcome: proposal.expectedOutcome,
|
|
151
|
-
appliedEdits,
|
|
152
|
-
harnessStatePath: "",
|
|
153
|
-
...(options.rollbackOf ? { rollbackOf: options.rollbackOf } : {}),
|
|
154
|
-
...(options.scope ? { scope: options.scope } : {}),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
//# sourceMappingURL=apply.js.map
|