dsh-continual-evolve 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +42 -115
  2. package/README.zh.md +33 -93
  3. package/client/client.js +253 -0
  4. package/cordis.patch.yml +2 -2
  5. package/lib/card-routes.d.ts +32 -0
  6. package/lib/card-routes.js +92 -0
  7. package/lib/index.d.ts +65 -217
  8. package/lib/index.js +74 -234
  9. package/lib/known-workspaces.d.ts +22 -0
  10. package/lib/known-workspaces.js +42 -0
  11. package/lib/memory-guide.d.ts +13 -14
  12. package/lib/memory-guide.js +34 -21
  13. package/lib/memory-section.d.ts +77 -0
  14. package/lib/memory-section.js +189 -0
  15. package/lib/memory-snapshot.d.ts +54 -0
  16. package/lib/memory-snapshot.js +125 -0
  17. package/package.json +8 -17
  18. package/lib/apply.d.ts +0 -22
  19. package/lib/apply.js +0 -157
  20. package/lib/approval.d.ts +0 -73
  21. package/lib/approval.js +0 -127
  22. package/lib/audit-log.d.ts +0 -19
  23. package/lib/audit-log.js +0 -52
  24. package/lib/auto.d.ts +0 -236
  25. package/lib/auto.js +0 -773
  26. package/lib/autocase.d.ts +0 -38
  27. package/lib/autocase.js +0 -76
  28. package/lib/benchmark-command.d.ts +0 -9
  29. package/lib/benchmark-command.js +0 -357
  30. package/lib/benchmark.d.ts +0 -151
  31. package/lib/benchmark.js +0 -250
  32. package/lib/command-util.d.ts +0 -70
  33. package/lib/command-util.js +0 -123
  34. package/lib/command.d.ts +0 -13
  35. package/lib/command.js +0 -134
  36. package/lib/consolidate.d.ts +0 -90
  37. package/lib/consolidate.js +0 -180
  38. package/lib/copy.d.ts +0 -37
  39. package/lib/copy.js +0 -58
  40. package/lib/declines.d.ts +0 -103
  41. package/lib/declines.js +0 -179
  42. package/lib/evaluate.d.ts +0 -74
  43. package/lib/evaluate.js +0 -271
  44. package/lib/evolve-event.d.ts +0 -40
  45. package/lib/evolve-event.js +0 -60
  46. package/lib/extraction-phase.d.ts +0 -132
  47. package/lib/extraction-phase.js +0 -236
  48. package/lib/failures.d.ts +0 -42
  49. package/lib/failures.js +0 -171
  50. package/lib/fate.d.ts +0 -130
  51. package/lib/fate.js +0 -350
  52. package/lib/goal-command.d.ts +0 -7
  53. package/lib/goal-command.js +0 -37
  54. package/lib/goal.d.ts +0 -72
  55. package/lib/goal.js +0 -72
  56. package/lib/inject.d.ts +0 -90
  57. package/lib/inject.js +0 -164
  58. package/lib/injection-caps.d.ts +0 -4
  59. package/lib/injection-caps.js +0 -4
  60. package/lib/injection-directory.d.ts +0 -29
  61. package/lib/injection-directory.js +0 -78
  62. package/lib/injection-rank.d.ts +0 -85
  63. package/lib/injection-rank.js +0 -161
  64. package/lib/listener.d.ts +0 -45
  65. package/lib/listener.js +0 -199
  66. package/lib/llm-text.d.ts +0 -104
  67. package/lib/llm-text.js +0 -140
  68. package/lib/logfile.d.ts +0 -71
  69. package/lib/logfile.js +0 -159
  70. package/lib/maintenance-commands.d.ts +0 -23
  71. package/lib/maintenance-commands.js +0 -136
  72. package/lib/memory-agent.d.ts +0 -93
  73. package/lib/memory-agent.js +0 -300
  74. package/lib/memory-apply.d.ts +0 -44
  75. package/lib/memory-apply.js +0 -202
  76. package/lib/memory-benchmark.d.ts +0 -76
  77. package/lib/memory-benchmark.js +0 -170
  78. package/lib/memory-index.d.ts +0 -114
  79. package/lib/memory-index.js +0 -197
  80. package/lib/memory-manifest.d.ts +0 -16
  81. package/lib/memory-manifest.js +0 -70
  82. package/lib/message-source.d.ts +0 -23
  83. package/lib/message-source.js +0 -12
  84. package/lib/mount-command.d.ts +0 -10
  85. package/lib/mount-command.js +0 -48
  86. package/lib/mount.d.ts +0 -42
  87. package/lib/mount.js +0 -212
  88. package/lib/notify.d.ts +0 -48
  89. package/lib/notify.js +0 -57
  90. package/lib/plan-command.d.ts +0 -12
  91. package/lib/plan-command.js +0 -38
  92. package/lib/plan.d.ts +0 -16
  93. package/lib/plan.js +0 -126
  94. package/lib/planner.d.ts +0 -61
  95. package/lib/planner.js +0 -144
  96. package/lib/pool.d.ts +0 -7
  97. package/lib/pool.js +0 -25
  98. package/lib/prefix-cache.d.ts +0 -79
  99. package/lib/prefix-cache.js +0 -168
  100. package/lib/project.d.ts +0 -28
  101. package/lib/project.js +0 -66
  102. package/lib/projection.d.ts +0 -22
  103. package/lib/projection.js +0 -112
  104. package/lib/promotion.d.ts +0 -94
  105. package/lib/promotion.js +0 -171
  106. package/lib/recall.d.ts +0 -85
  107. package/lib/recall.js +0 -142
  108. package/lib/record-language.d.ts +0 -85
  109. package/lib/record-language.js +0 -152
  110. package/lib/render.d.ts +0 -13
  111. package/lib/render.js +0 -80
  112. package/lib/review-scheduler.d.ts +0 -38
  113. package/lib/review-scheduler.js +0 -183
  114. package/lib/review.d.ts +0 -60
  115. package/lib/review.js +0 -145
  116. package/lib/rollback.d.ts +0 -9
  117. package/lib/rollback.js +0 -68
  118. package/lib/rubric.d.ts +0 -29
  119. package/lib/rubric.js +0 -119
  120. package/lib/runtime-commands.d.ts +0 -14
  121. package/lib/runtime-commands.js +0 -115
  122. package/lib/runtime.d.ts +0 -24
  123. package/lib/runtime.js +0 -87
  124. package/lib/score.d.ts +0 -64
  125. package/lib/score.js +0 -191
  126. package/lib/search.d.ts +0 -83
  127. package/lib/search.js +0 -136
  128. package/lib/service.d.ts +0 -63
  129. package/lib/service.js +0 -253
  130. package/lib/skill-render.d.ts +0 -23
  131. package/lib/skill-render.js +0 -69
  132. package/lib/skill.d.ts +0 -15
  133. package/lib/skill.js +0 -80
  134. package/lib/skillquality.d.ts +0 -85
  135. package/lib/skillquality.js +0 -320
  136. package/lib/source.d.ts +0 -30
  137. package/lib/source.js +0 -43
  138. package/lib/state.d.ts +0 -26
  139. package/lib/state.js +0 -151
  140. package/lib/store.d.ts +0 -61
  141. package/lib/store.js +0 -164
  142. package/lib/token-usage.d.ts +0 -68
  143. package/lib/token-usage.js +0 -211
  144. package/lib/tool.d.ts +0 -34
  145. package/lib/tool.js +0 -320
  146. package/lib/turn-snapshot.d.ts +0 -78
  147. package/lib/turn-snapshot.js +0 -252
  148. package/lib/types.d.ts +0 -218
  149. package/lib/types.js +0 -133
  150. package/lib/usage.d.ts +0 -36
  151. package/lib/usage.js +0 -98
  152. package/lib/validate.d.ts +0 -28
  153. package/lib/validate.js +0 -181
  154. package/lib/wrapup-candidates.d.ts +0 -127
  155. package/lib/wrapup-candidates.js +0 -161
  156. package/lib/wrapup-command.d.ts +0 -10
  157. package/lib/wrapup-command.js +0 -243
  158. package/lib/wrapup-proposals.d.ts +0 -101
  159. package/lib/wrapup-proposals.js +0 -281
  160. package/lib/wrapup.d.ts +0 -56
  161. package/lib/wrapup.js +0 -105
package/lib/index.js CHANGED
@@ -1,127 +1,38 @@
1
1
  /**
2
- * dsh-continual-evolve — plugin entry (Phase 2: auto review gate).
2
+ * dsh-continual-evolve — plugin entry.
3
3
  *
4
- * Mounts the evolution engine, registers the model-facing evolve_* tools,
5
- * the human-facing /evolve command, the system-prompt guidance section, and
6
- * the dedicated Memory Agent listener. The listener is always registered so
7
- * an install works without profile edits; `autoReview` only supplies the
8
- * initial default when `evolve/runtime.json` does not exist yet, and
9
- * `/evolve pause|resume` own it afterwards. Generic review/planner/fate are
10
- * not reachable from this listener.
4
+ * After the 2026-10-04 ZCode-alignment teardown the plugin is one thing: a
5
+ * session-start system-prompt section that injects the workspace memory
6
+ * index (`<cwd>/.evolve/memory/MEMORY.md`), hands out the store path, and
7
+ * teaches the when-to-save guide. The model reads and writes the markdown
8
+ * files with its native tools; there are no plugin tools, no commands, no
9
+ * background extraction, and no governance. See
10
+ * .agents/notes/implemented/architecture/2026-10-04-zcode-alignment-teardown.md.
11
11
  *
12
- * evolve v2 registers a third section (`evolve:memory-index`) that injects
13
- * memory content plus the when_to_save guide, frozen per session — see
14
- * memory-index.ts.
12
+ * The 2026-10-04 plugin-management card adds a read-only surface: the
13
+ * workspace roots the section has served feed an in-memory allowlist, and a
14
+ * `webServer` route pair projects any known workspace's store for the card
15
+ * in the official plugin manager (`plugins.bundle.config`, client bundle in
16
+ * client/client.js). The card is a viewer — see
17
+ * .agents/notes/implemented/feature/2026-10-04-plugin-management-card.md.
18
+ *
19
+ * The host API surface is declared locally (the minimal shape this plugin
20
+ * touches) instead of importing the DSH type packages — the section call is
21
+ * the same one every prior release used, verified in production.
15
22
  */
16
- import { join } from "node:path";
17
23
  import z from "@deepseek-ai/schemastery";
18
- import { expandHomePath, resolveDshHome } from "@deepseek-ai/dsh-home-paths";
19
- import { createEvolutionEngine } from "./service.js";
20
- import { registerEvolveTools } from "./tool.js";
21
- import { registerEvolveCommand } from "./command.js";
22
- import { registerAutoReview } from "./listener.js";
23
- import { syncSkillsFromResult } from "./skill.js";
24
- import { entriesSectionText } from "./inject.js";
25
- import { createFrozenMemorySection, DEFAULT_MEMORY_INDEX_MAX_CHARS, DEFAULT_MEMORY_SECTION_ORDER, MEMORY_SECTION_NAME, memoryIndexSectionText, } from "./memory-index.js";
26
- import { resolveRubricKey } from "./rubric.js";
27
- import { restoreMounted } from "./mount.js";
28
- import { registerFileLogger } from "./logfile.js";
29
- import { resolvePromotionPolicy } from "./promotion.js";
24
+ import { mountCardRoutes } from "./card-routes.js";
25
+ import { createKnownWorkspaces } from "./known-workspaces.js";
26
+ import { createFrozenMemorySection, cwdOf, DEFAULT_MAX_CHARS, DEFAULT_MEMORY_SECTION_ORDER, MEMORY_SECTION_NAME, memorySectionText, } from "./memory-section.js";
30
27
  export const name = "continual-evolve";
31
- /** Service key under which the evolution engine is published. */
32
- export const EVOLUTION_SERVICE = "evolution";
33
- export const inject = ["tools", "commands", "systemPrompt", "llm", "sessionQuery", "agents", "userQuestions", "subagents"];
28
+ export const inject = ["systemPrompt"];
34
29
  export const Config = z.object({
35
- /** Root for evolution stores; defaults to the resolved DSH home. */
36
- baseDir: z.string(),
37
- /** System-prompt section order for the evolution guidance. */
38
- sectionOrder: z.natural().default(118),
39
- /** Initial Memory Agent default when no `evolve/runtime.json` exists; not a registration gate. */
40
- autoReview: z.boolean().default(false),
41
- /** ZCode-style minimum lexical words in one direct user text part. */
42
- memoryMinUserWords: z.natural().default(3),
43
- /**
44
- * Session-close bounded drain for in-flight extraction, in milliseconds.
45
- * Absent → 15s; 0 restores the legacy immediate abort on dispose.
46
- */
47
- sessionCloseDrainMs: z.natural().default(15000),
48
- /** Trajectory slice handed to the extractor, in characters. */
49
- maxReviewInputChars: z.natural().default(40000),
50
- /** Output budget for the cheap gate call. */
51
- reviewBudgetTokens: z.natural().default(4096),
52
- /**
53
- * Planner prefix-cache routing for the gate and planner inputs: `auto`
54
- * prepends a session-derived message prefix when the session shows
55
- * cache-read evidence (dropping the redundant flat trajectory text),
56
- * `session` always prefixes, `off` keeps the legacy flat-text input.
57
- */
58
- plannerPrefixCache: z.union([z.const("auto"), z.const("session"), z.const("off")]).default("auto"),
59
- /** Session-prefix budget for Route A planning inputs, in characters. */
60
- plannerPrefixMaxChars: z.natural().default(12000),
61
- /** After an approved gate run with applied edits, queue a visible follow-up notice. */
62
- notifyOnAutoReview: z.boolean().default(true),
63
- /** Cross-session (global) edits require an explicit human approval. */
64
- requireGlobalApproval: z.boolean().default(true),
65
- /**
66
- * Authoring language for evolution records (refinement summaries,
67
- * rationales, entry titles/content, review and wrap-up verdicts).
68
- * `auto` follows the DSH client language (durable `locale.preference`,
69
- * then trajectory detection, then `en`); `zh`/`en` pin it.
70
- */
71
- recordLanguage: z.union([z.const("auto"), z.const("zh"), z.const("en")]).default("auto"),
72
- /** Skills root for materialized skill entries; defaults to <dshHome>/skills. */
73
- skillsDir: z.string(),
74
- /** Passphrase for rubric encryption; falls back to DSH_EVOLVE_RUBRIC_KEY, then a local key file. */
75
- rubricKey: z.string(),
76
- /** Write all cordis log messages to <baseDir>/evolve/plugin.log (JSONL). */
77
- logToFile: z.boolean().default(true),
78
- /** File log level: 0=error, 1=info, 2=warn, 3=debug. */
79
- logLevel: z.natural().default(1),
80
- /** Rotate the file log when it exceeds this many bytes. */
81
- logMaxBytes: z.natural().default(5 * 1024 * 1024),
82
- /** After a benchmark decision rejects a candidate, roll the refinement back automatically. */
83
- autoRollbackOnReject: z.boolean().default(true),
84
- /**
85
- * P1 auto-case capture: failed evolution attempts (benchmark-rejected
86
- * candidates, gate proposals without consent) land as draft cases in the
87
- * auto-regression container benchmark, seeding the regression loop.
88
- */
89
- autoCase: z.boolean().default(true),
90
- /**
91
- * Optional model override for the memory extractor (cheaper model).
92
- * Format: "provider/model" or just "model" (same provider as the agent).
93
- * When absent, the extractor uses the agent's own provider/model.
94
- */
95
- reviewModel: z.string(),
96
- /**
97
- * Goal-blocked moment: after this many CONSECUTIVE idle probes that
98
- * observe the session goal in phase "blocked", run one memory extraction
99
- * so the stuck encounter is distilled. 0 disables.
100
- */
101
- goalBlockedWrapupTurns: z.natural().min(0).default(3),
102
- /**
103
- * Promotion policy (2026-08-22): regex sources whose match in a
104
- * candidate's title/content marks it project-scoped — such entries are
105
- * never promoted to the cross-session global store. Replaces the built-in
106
- * defaults when set.
107
- */
108
30
  /**
109
- * Promotion policy (2026-08-22): regex sources whose match in a
110
- * candidate's title/content marks it project-scoped — such entries are
111
- * never promoted to the cross-session global store. Replaces the built-in
112
- * defaults when set.
113
- */
114
- promotionBlockPatterns: z.array(z.string()),
115
- /** Whole promotions below this content length stay local (chars). */
116
- promotionMinChars: z.natural().default(100),
117
- /** Entry-directory lines injected per build before folding into a counter. */
118
- injectionDirectoryLines: z.natural().default(15),
119
- /**
120
- * evolve v2 memory index: the section that injects memory CONTENT (not just
121
- * an id/title directory) at session start, frozen per session to keep the
122
- * system prompt byte-stable (prompt cache). `enabled`/`guide`/`order`/
123
- * `maxChars` default to true/true/400/6000; with `guide` off and an empty
124
- * store the section renders to "" and costs no tokens.
31
+ * The memory section: injects the workspace memory index at session
32
+ * start, frozen per session to keep the system prompt byte-stable
33
+ * (prompt cache). `enabled`/`guide`/`order`/`maxChars` default to
34
+ * true/true/400/6000; with `guide` off and an empty store the section
35
+ * renders to "" and costs no tokens.
125
36
  */
126
37
  memoryIndex: z.object({
127
38
  enabled: z.boolean(),
@@ -130,130 +41,59 @@ export const Config = z.object({
130
41
  maxChars: z.natural(),
131
42
  }),
132
43
  /**
133
- * Storage hygiene (#20): how much append-only past each write keeps.
134
- * `snapshots` = full-state copies per store, `refinements` = tail lines
135
- * per store history, `reviews` = tail lines of the shared audit trail,
136
- * `tokenUsage` = tail lines of the direct-call token ledger.
137
- * Absent fields fall back to the store defaults (20 / 500 / 500 / 500) —
138
- * rollback needs recent snapshots, readers need a recent window.
44
+ * The read-only memory card in the official plugin manager
45
+ * (`plugins.bundle.config`). Defaults to on; hosts without the
46
+ * `webServer` service or the `plugins.bundle.config` slot degrade to no
47
+ * card, with the memory section unaffected.
139
48
  */
140
- historyRetain: z.object({
141
- snapshots: z.natural(),
142
- refinements: z.natural(),
143
- reviews: z.natural(),
144
- tokenUsage: z.natural(),
49
+ memoryCard: z.object({
50
+ enabled: z.boolean(),
145
51
  }),
146
52
  });
147
- export function apply(ctx, config) {
148
- const baseDir = resolveDshHome(config.baseDir);
149
- const skillsRoot = config.skillsDir ? expandHomePath(config.skillsDir) : join(baseDir, "skills");
150
- const engine = createEvolutionEngine(baseDir, {
151
- onApplied: (result) => {
152
- try {
153
- const warnings = syncSkillsFromResult(skillsRoot, result);
154
- for (const warning of warnings) {
155
- ctx.logger("continual-evolve").warn(warning);
156
- }
157
- }
158
- catch (cause) {
159
- ctx
160
- .logger("continual-evolve")
161
- .warn(`skill materialization failed for ${result.id}: ${cause instanceof Error ? cause.message : String(cause)}`);
162
- }
163
- },
164
- }, config.historyRetain ? { historyRetain: { ...config.historyRetain } } : {});
165
- ctx.provide(EVOLUTION_SERVICE, { engine, baseDir });
166
- ctx.systemPrompt.section({
167
- name: "tool:continual-evolve",
168
- order: config.sectionOrder ?? 118,
169
- text: "You have a continual harness: versioned, persistent prompt notes, memories, skills, and subagent specs. Memories are ONE FACT per entry, typed user/feedback/project/reference (feedback = pitfall + Why + How to apply). Stores: local (this session's staging), project (this project's cross-session store), global (cross-project). Prompt notes and delegation specs are injected below; use evolve_list for the full state. Create an entry (evolve_add) after a repeated failure, a reusable tactic, a durable fact or preference, a repeated procedure, or a repeated delegation role. Keep edits small and evidence-backed; prefer local scope, use project for repo-specific lessons, global: true only for stable cross-project lessons. Update or delete (evolve_update / evolve_delete) when an entry is wrong or obsolete; roll back faulty refinements with evolve_rollback. Every edit is snapshotted, versioned, and recorded — no edit can be silently lost.",
170
- });
171
- // Phase 2: make prompt entries real system-prompt content and subagent
172
- // entries real delegation specs. The text is a provider evaluated at every
173
- // assembly with the assembling agent; a store without prompt/subagent
174
- // entries renders to "" and the prompt renderer drops the section.
175
- //
176
- // evolve v2: when the memory index section carries memory content, the
177
- // directory stops listing memories so the same entries are never paid for
178
- // twice (one fact, one home).
179
- const memoryIndexEnabled = config.memoryIndex?.enabled ?? true;
180
- ctx.systemPrompt.section({
181
- name: "tool:continual-evolve:entries",
182
- order: (config.sectionOrder ?? 118) + 1,
183
- text: (context) => entriesSectionText(engine, context.agent, undefined, {
184
- directoryLines: config.injectionDirectoryLines ?? 15,
185
- includeMemoryDirectory: !memoryIndexEnabled,
186
- }),
187
- });
188
- // evolve v2 passive read path: memory content in the prompt at session
189
- // start, frozen per session (see memory-index.ts) so the whole system
190
- // prompt stays byte-stable within a session and prompt-cache reads keep
191
- // hitting. Registered in the empty 1–499 order slot — the two sections
192
- // above sit at 118/119, upstream's named slots start at PLAN_POLICY=500.
193
- if (memoryIndexEnabled) {
194
- const frozenMemories = createFrozenMemorySection();
195
- const memoryIndexOrder = config.memoryIndex?.order ?? DEFAULT_MEMORY_SECTION_ORDER;
196
- const memoryIndexMaxChars = config.memoryIndex?.maxChars ?? DEFAULT_MEMORY_INDEX_MAX_CHARS;
197
- const memoryIndexGuide = config.memoryIndex?.guide ?? true;
198
- ctx.systemPrompt.section({
199
- name: MEMORY_SECTION_NAME,
200
- order: memoryIndexOrder,
201
- text: (context) => frozenMemories.textFor(context.agent, (agent) => memoryIndexSectionText(engine, agent, { maxChars: memoryIndexMaxChars, guide: memoryIndexGuide })),
202
- });
53
+ /**
54
+ * Mount the card routes on the host web server through a nested inject, so
55
+ * the plugin stays mountable on hosts without that service. Called only when
56
+ * the card is enabled.
57
+ */
58
+ function mountCardWhenAvailable(host, workspaces) {
59
+ const nested = host;
60
+ if (typeof nested.inject !== "function") {
61
+ host.logger("continual-evolve").warn("host context exposes no inject — memory card routes not mounted (memory section unaffected)");
62
+ return;
203
63
  }
204
- const gate = {
205
- requireGlobalApproval: config.requireGlobalApproval ?? true,
206
- ...(config.recordLanguage !== undefined ? { recordLanguage: config.recordLanguage } : {}),
207
- };
208
- const promotionPolicy = resolvePromotionPolicy({
209
- blockPatterns: config.promotionBlockPatterns,
210
- minPromoteChars: config.promotionMinChars,
211
- });
212
- registerEvolveTools(ctx, engine, gate);
213
- const rubricKey = resolveRubricKey(baseDir, config.rubricKey, process.env, (m) => ctx.logger("continual-evolve").warn(m));
214
- registerEvolveCommand(ctx, engine, gate, {
215
- rubricKey,
216
- autoRollbackOnReject: config.autoRollbackOnReject ?? true,
217
- autoCase: config.autoCase ?? true,
218
- promotionPolicy,
219
- autoReview: config.autoReview ?? false,
220
- ...(config.recordLanguage !== undefined ? { recordLanguage: config.recordLanguage } : {}),
64
+ nested.inject(["webServer"], (scoped) => {
65
+ const webServer = scoped.webServer;
66
+ if (webServer === undefined) {
67
+ host.logger("continual-evolve").warn("webServer service present but empty — memory card routes not mounted (memory section unaffected)");
68
+ return;
69
+ }
70
+ mountCardRoutes(webServer, workspaces);
221
71
  });
222
- // Plugin-owned file logging: every cordis log message lands in
223
- // <baseDir>/evolve/plugin.log regardless of how dsh web was launched —
224
- // no extra component to install, no startup-script dependency.
225
- if (config.logToFile !== false) {
226
- registerFileLogger(ctx, baseDir, {
227
- logLevel: config.logLevel ?? 1,
228
- ...(config.logMaxBytes !== undefined ? { logMaxBytes: config.logMaxBytes } : {}),
229
- });
72
+ }
73
+ export function apply(host, config) {
74
+ if (config.memoryIndex?.enabled === false) {
75
+ return;
230
76
  }
231
- // v2 optional: restore hot-mounted skill plugins after a restart.
232
- void restoreMounted(ctx, baseDir).catch((cause) => {
233
- ctx.logger("continual-evolve").warn(`mount restore failed: ${cause instanceof Error ? cause.message : String(cause)}`);
234
- });
235
- // The memory extraction listener is always registered: an install must
236
- // work without profile edits, so `autoReview` is only the initial default
237
- // when no `evolve/runtime.json` exists yet — `/evolve pause|resume` own
238
- // the switch afterwards. Extraction is moment-driven: compaction,
239
- // goal-blocked streaks, the session-close drain, and manual
240
- // `/evolve wrapup` — successful turns never cost an LLM call.
241
- registerAutoReview(ctx, engine, {
242
- enabledByDefault: config.autoReview ?? false,
243
- memoryMinUserWords: config.memoryMinUserWords ?? 3,
244
- sessionCloseDrainMs: config.sessionCloseDrainMs ?? 15000,
245
- maxInputChars: config.maxReviewInputChars ?? 40000,
246
- budgetTokens: config.reviewBudgetTokens ?? 4096,
247
- notifyOnAutoReview: config.notifyOnAutoReview ?? true,
248
- goalBlockedWrapupTurns: config.goalBlockedWrapupTurns ?? 3,
249
- ...(config.historyRetain?.reviews !== undefined ? { reviewsRetain: config.historyRetain.reviews } : {}),
250
- ...(config.reviewModel ? { reviewModel: config.reviewModel } : {}),
251
- requireGlobalApproval: config.requireGlobalApproval ?? true,
252
- ...(config.plannerPrefixCache ? { prefixCacheMode: config.plannerPrefixCache } : {}),
253
- ...(config.plannerPrefixMaxChars !== undefined ? { prefixMaxChars: config.plannerPrefixMaxChars } : {}),
254
- ...(config.recordLanguage !== undefined ? { recordLanguage: config.recordLanguage } : {}),
77
+ const workspaces = createKnownWorkspaces();
78
+ const frozen = createFrozenMemorySection();
79
+ host.systemPrompt.section({
80
+ name: MEMORY_SECTION_NAME,
81
+ order: config.memoryIndex?.order ?? DEFAULT_MEMORY_SECTION_ORDER,
82
+ text: (context) => {
83
+ const cwd = cwdOf(context.agent);
84
+ if (cwd !== undefined) {
85
+ workspaces.remember(cwd);
86
+ }
87
+ return frozen.textFor(context.agent, (agent) => memorySectionText(agent, {
88
+ maxChars: config.memoryIndex?.maxChars ?? DEFAULT_MAX_CHARS,
89
+ guide: config.memoryIndex?.guide ?? true,
90
+ }));
91
+ },
255
92
  });
256
- ctx.logger("continual-evolve").info("continual-evolve moment-driven memory extraction listener registered (compaction / goal-blocked / session-close drain; manual wrapup via /evolve wrapup)");
257
- ctx.logger("continual-evolve").info(`continual-evolve mounted (baseDir=${baseDir})`);
93
+ host.logger("continual-evolve").info("continual-evolve memory section registered (workspace .evolve/memory, native file read/write)");
94
+ if (config.memoryCard?.enabled === false) {
95
+ return;
96
+ }
97
+ mountCardWhenAvailable(host, workspaces);
258
98
  }
259
99
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,22 @@
1
+ /** One known workspace: the resolved root and when it was last served. */
2
+ export interface KnownWorkspace {
3
+ root: string;
4
+ /** ISO timestamp of the most recent section assembly seen for this root. */
5
+ lastSeen: string;
6
+ }
7
+ /** The allowlist the card routes read through. */
8
+ export interface KnownWorkspaces {
9
+ /**
10
+ * Record (or refresh) a workspace root. A relative or blank cwd is a
11
+ * silent no-op — the caller is the injection path, where a bad cwd
12
+ * already renders an empty section and must not turn into a throw.
13
+ */
14
+ remember(cwd: string): void;
15
+ /** Known roots, most recently served first. */
16
+ list(): KnownWorkspace[];
17
+ }
18
+ /** How many recent workspaces the card tracks. */
19
+ export declare const DEFAULT_KNOWN_WORKSPACE_LIMIT = 8;
20
+ /** Create the in-memory allowlist; `limit` is the LRU bound. */
21
+ export declare function createKnownWorkspaces(limit?: number): KnownWorkspaces;
22
+ //# sourceMappingURL=known-workspaces.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The workspace roots the card may look at, in memory only.
3
+ *
4
+ * Populated as a side effect of section assembly (the plugin already knows
5
+ * each session's cwd there), this list is the card's read allowlist: the
6
+ * memory API refuses any root the running process has not served a session
7
+ * in, so the endpoint cannot be used to probe the filesystem. Bounded LRU,
8
+ * never persisted — a restart starts empty and one session in a workspace
9
+ * puts it back. No second store on disk.
10
+ */
11
+ import { isAbsolute, resolve } from "node:path";
12
+ /** How many recent workspaces the card tracks. */
13
+ export const DEFAULT_KNOWN_WORKSPACE_LIMIT = 8;
14
+ /** Create the in-memory allowlist; `limit` is the LRU bound. */
15
+ export function createKnownWorkspaces(limit = DEFAULT_KNOWN_WORKSPACE_LIMIT) {
16
+ const max = Math.max(1, limit);
17
+ // Insertion order encodes recency: the most recently served root is last.
18
+ const recent = new Map();
19
+ return {
20
+ remember(cwd) {
21
+ const trimmed = cwd.trim();
22
+ if (trimmed.length === 0 || !isAbsolute(trimmed)) {
23
+ return;
24
+ }
25
+ recent.delete(resolve(trimmed));
26
+ recent.set(resolve(trimmed), new Date().toISOString());
27
+ while (recent.size > max) {
28
+ const oldest = recent.keys().next();
29
+ if (oldest.done === true) {
30
+ break;
31
+ }
32
+ recent.delete(oldest.value);
33
+ }
34
+ },
35
+ list() {
36
+ return [...recent.entries()]
37
+ .reverse()
38
+ .map(([root, lastSeen]) => ({ root, lastSeen }));
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=known-workspaces.js.map
@@ -1,19 +1,18 @@
1
1
  /**
2
- * The persistent-memory guide injected as part of the `evolve:memory-index`
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 v2 plan: it teaches the model when a
6
- * durable memory is worth creating, which recall type it belongs to, what body
7
- * structure each type owes, and that the main session may write directly with
8
- * `evolve_add` instead of waiting for the background pipeline.
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: `docs/research/evolve-v2-passive-read-write-plan.md` §改动二
11
- * (Chinese full text, adapted from ZCode's persistent-memory prompt). Kept in
12
- * its own module so the wording is a single home — `memory-index.ts` only
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 const MEMORY_GUIDE_INTRO = "# \u6301\u4E45\u8BB0\u5FC6\n\n\u4F60\u6709\u4E00\u4E2A\u8DE8\u4F1A\u8BDD\u7684\u6301\u4E45\u8BB0\u5FC6\u7CFB\u7EDF\u3002\u4EE5\u4E0B\u662F\u5F53\u524D\u8BB0\u5FC6\uFF08\u5DF2\u76F4\u63A5\u6CE8\u5165\uFF0C\u65E0\u9700\u67E5\u8BE2\u5373\u53EF\u4F7F\u7528\uFF1B\u9700\u8981\u68C0\u7D22\u5B8C\u6574\u5185\u5BB9\u6216\u786E\u8BA4\u662F\u5426\u6709\u76F8\u5173\u8BB0\u5FC6\u65F6\uFF0C\u8C03\u7528 evolve_recall\uFF09\u3002\u672C\u4F1A\u8BDD\u5185\u53EF\u4EE5\u76F4\u63A5\u7528 evolve_add \u843D\u76D8\uFF0C\u4E0D\u5FC5\u7B49\u540E\u53F0\u6D41\u7A0B\u2014\u2014\u9ED8\u8BA4\u5199 local\uFF0C\u53EA\u6709\u7A33\u5B9A\u7684\u8DE8\u9879\u76EE\u6559\u8BAD\u624D\u5199 global\uFF08global \u5199\u5165\u4F1A\u5F39\u4EBA\u5DE5\u6279\u51C6\uFF09\u3002";
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\u6761\u76EE\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\u6761\u76EE\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\u6761\u76EE\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- \u66F4\u65B0\u4F18\u5148\u4E8E\u65B0\u5EFA\uFF1A\u72B6\u6001\u53D8\u4E86\u6539\u539F\u6761\u76EE\uFF0C\u4E0D\u53E6\u8D77\u4E00\u6761\uFF08\u65B0\u65E7\u5E76\u5B58\u6BD4\u6CA1\u6709\u8BB0\u5FC6\u66F4\u7CDF\uFF09\u3002\n- \u6BCF\u6761\u7684 description \u662F\u7ED9\u672A\u6765\u4F1A\u8BDD\u7684\u76F8\u5173\u6027 hook\u2014\u2014\u5199\"\u51B3\u5B9A\u4E86\u4E00\u6761\u8BB0\u5FC6\u4F1A\u4E0D\u4F1A\n \u88AB\u60F3\u8D77\"\u7684\u4E00\u53E5\u8BDD\uFF0C\u4E0D\u5199\u6D41\u6C34\u8D26\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";
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- \u5F15\u7528\u8BB0\u5FC6\u524D\u82E5\u53D1\u73B0\u4E0E\u73B0\u5B9E\u4E0D\u7B26\uFF0C\u5F53\u573A\u4FEE\u6B63\u6216\u5220\u9664\u8BE5\u6587\u4EF6\u4E0E\u7D22\u5F15\u884C\u2014\u2014\u574F\u8BB0\u5FC6\u7684\u5371\u5BB3\u968F\u5F15\u7528\u6269\u6563\uFF0C\n \u9000\u5F79\u4E0D\u7559\u7ED9\u7528\u6237\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
@@ -1,29 +1,41 @@
1
1
  /**
2
- * The persistent-memory guide injected as part of the `evolve:memory-index`
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 v2 plan: it teaches the model when a
6
- * durable memory is worth creating, which recall type it belongs to, what body
7
- * structure each type owes, and that the main session may write directly with
8
- * `evolve_add` instead of waiting for the background pipeline.
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: `docs/research/evolve-v2-passive-read-write-plan.md` §改动二
11
- * (Chinese full text, adapted from ZCode's persistent-memory prompt). Kept in
12
- * its own module so the wording is a single home — `memory-index.ts` only
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 const MEMORY_GUIDE_INTRO = `# 持久记忆
14
+ /** Heading and framing before the `<memories>` block. Takes the absolute memory dir. */
15
+ export function memoryGuideIntro(memoryDir) {
16
+ return `# 持久记忆
17
17
 
18
- 你有一个跨会话的持久记忆系统。以下是当前记忆(已直接注入,无需查询即可使用;需要检索完整内容或确认是否有相关记忆时,调用 evolve_recall)。本会话内可以直接用 evolve_add 落盘,不必等后台流程——默认写 local,只有稳定的跨项目教训才写 global(global 写入会弹人工批准)。`;
19
- /** The when_to_save / how_to_use rules and maintenance discipline. */
20
- export const MEMORY_GUIDE_RULES = `## 何时写入记忆
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
- 这类记忆衰减快,状态变化时更新它,别堆叠新条目。</when_to_save>
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,9 @@ export const MEMORY_GUIDE_RULES = `## 何时写入记忆
66
78
 
67
79
  ## 如何维护
68
80
 
69
- - 更新优先于新建:状态变了改原条目,不另起一条(新旧并存比没有记忆更糟)。
70
- - 每条的 description 是给未来会话的相关性 hook——写"决定了一条记忆会不会
71
- 被想起"的一句话,不写流水账。
81
+ - 写完记忆文件后立即在 MEMORY.md 追加或更新一行索引:\`- [标题](文件名.md) — 钩子\`;状态变化时原文与索引行一起改,别让索引说谎。
82
+ - 更新优先于新建:状态变了改原文件,不另起一条(新旧并存比没有记忆更糟)。
83
+ - 引用记忆前若发现与现实不符,当场修正或删除该文件与索引行——坏记忆的危害随引用扩散,
84
+ 退役不留给用户。
72
85
  - [[名字]] 互相引用相关记忆;引用还不存在的名字是合法的,它标记"值得将来补写"。`;
73
86
  //# 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