@downcity/plugins 1.0.59 → 1.0.61

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 (101) hide show
  1. package/bin/BuiltinPlugins.d.ts +39 -2
  2. package/bin/BuiltinPlugins.d.ts.map +1 -1
  3. package/bin/BuiltinPlugins.js +24 -3
  4. package/bin/BuiltinPlugins.js.map +1 -1
  5. package/bin/contact/ContactPlugin.d.ts +6 -1
  6. package/bin/contact/ContactPlugin.d.ts.map +1 -1
  7. package/bin/contact/ContactPlugin.js +9 -8
  8. package/bin/contact/ContactPlugin.js.map +1 -1
  9. package/bin/contact/types/ContactPluginOptions.d.ts +29 -0
  10. package/bin/contact/types/ContactPluginOptions.d.ts.map +1 -0
  11. package/bin/contact/types/ContactPluginOptions.js +9 -0
  12. package/bin/contact/types/ContactPluginOptions.js.map +1 -0
  13. package/bin/index.d.ts +9 -1
  14. package/bin/index.d.ts.map +1 -1
  15. package/bin/index.js +4 -0
  16. package/bin/index.js.map +1 -1
  17. package/bin/memory/Action.d.ts +15 -10
  18. package/bin/memory/Action.d.ts.map +1 -1
  19. package/bin/memory/Action.js +233 -16
  20. package/bin/memory/Action.js.map +1 -1
  21. package/bin/memory/MemoryPlugin.d.ts +10 -4
  22. package/bin/memory/MemoryPlugin.d.ts.map +1 -1
  23. package/bin/memory/MemoryPlugin.js +79 -37
  24. package/bin/memory/MemoryPlugin.js.map +1 -1
  25. package/bin/memory/runtime/Search.d.ts +1 -1
  26. package/bin/memory/runtime/Search.d.ts.map +1 -1
  27. package/bin/memory/runtime/Search.js +11 -7
  28. package/bin/memory/runtime/Search.js.map +1 -1
  29. package/bin/memory/runtime/Store.d.ts +8 -23
  30. package/bin/memory/runtime/Store.d.ts.map +1 -1
  31. package/bin/memory/runtime/Store.js +28 -43
  32. package/bin/memory/runtime/Store.js.map +1 -1
  33. package/bin/memory/runtime/SystemProvider.d.ts +4 -8
  34. package/bin/memory/runtime/SystemProvider.d.ts.map +1 -1
  35. package/bin/memory/runtime/SystemProvider.js +55 -62
  36. package/bin/memory/runtime/SystemProvider.js.map +1 -1
  37. package/bin/memory/runtime/Writer.d.ts +48 -10
  38. package/bin/memory/runtime/Writer.d.ts.map +1 -1
  39. package/bin/memory/runtime/Writer.js +197 -60
  40. package/bin/memory/runtime/Writer.js.map +1 -1
  41. package/bin/memory/types/Memory.d.ts +222 -33
  42. package/bin/memory/types/Memory.d.ts.map +1 -1
  43. package/bin/memory/types/Memory.js +4 -3
  44. package/bin/memory/types/Memory.js.map +1 -1
  45. package/bin/shell/ShellPlugin.d.ts +2 -1
  46. package/bin/shell/ShellPlugin.d.ts.map +1 -1
  47. package/bin/shell/ShellPlugin.js +3 -3
  48. package/bin/shell/ShellPlugin.js.map +1 -1
  49. package/bin/shell/ShellRuntimeTypes.d.ts +7 -2
  50. package/bin/shell/ShellRuntimeTypes.d.ts.map +1 -1
  51. package/bin/shell/runtime/ShellActionRuntime.d.ts.map +1 -1
  52. package/bin/shell/runtime/ShellActionRuntime.js +6 -6
  53. package/bin/shell/runtime/ShellActionRuntime.js.map +1 -1
  54. package/bin/shell/runtime/ShellActionRuntimeSupport.d.ts +14 -5
  55. package/bin/shell/runtime/ShellActionRuntimeSupport.d.ts.map +1 -1
  56. package/bin/shell/runtime/ShellActionRuntimeSupport.js +58 -22
  57. package/bin/shell/runtime/ShellActionRuntimeSupport.js.map +1 -1
  58. package/bin/shell/runtime/ShellProcessEvents.js +3 -3
  59. package/bin/shell/runtime/ShellProcessEvents.js.map +1 -1
  60. package/bin/shell/types/ShellPluginOptions.d.ts +95 -0
  61. package/bin/shell/types/ShellPluginOptions.d.ts.map +1 -0
  62. package/bin/shell/types/ShellPluginOptions.js +10 -0
  63. package/bin/shell/types/ShellPluginOptions.js.map +1 -0
  64. package/bin/task/Scheduler.d.ts +8 -0
  65. package/bin/task/Scheduler.d.ts.map +1 -1
  66. package/bin/task/Scheduler.js +7 -9
  67. package/bin/task/Scheduler.js.map +1 -1
  68. package/bin/task/TaskPlugin.d.ts +18 -1
  69. package/bin/task/TaskPlugin.d.ts.map +1 -1
  70. package/bin/task/TaskPlugin.js +23 -1
  71. package/bin/task/TaskPlugin.js.map +1 -1
  72. package/bin/task/types/TaskPluginOptions.d.ts +22 -0
  73. package/bin/task/types/TaskPluginOptions.d.ts.map +1 -0
  74. package/bin/task/types/TaskPluginOptions.js +9 -0
  75. package/bin/task/types/TaskPluginOptions.js.map +1 -0
  76. package/package.json +2 -2
  77. package/src/BuiltinPlugins.ts +67 -3
  78. package/src/contact/ContactPlugin.ts +17 -9
  79. package/src/contact/types/ContactPluginOptions.ts +30 -0
  80. package/src/index.ts +41 -1
  81. package/src/memory/Action.ts +292 -25
  82. package/src/memory/MemoryPlugin.ts +82 -40
  83. package/src/memory/runtime/Search.ts +16 -9
  84. package/src/memory/runtime/Store.ts +52 -49
  85. package/src/memory/runtime/SystemProvider.ts +55 -69
  86. package/src/memory/runtime/Writer.ts +262 -81
  87. package/src/memory/types/Memory.ts +296 -35
  88. package/src/shell/ShellPlugin.ts +4 -3
  89. package/src/shell/ShellRuntimeTypes.ts +7 -2
  90. package/src/shell/runtime/ShellActionRuntime.ts +18 -9
  91. package/src/shell/runtime/ShellActionRuntimeSupport.ts +106 -21
  92. package/src/shell/runtime/ShellProcessEvents.ts +3 -3
  93. package/src/shell/types/ShellPluginOptions.ts +112 -0
  94. package/src/task/Scheduler.ts +15 -9
  95. package/src/task/TaskPlugin.ts +27 -1
  96. package/src/task/types/TaskPluginOptions.ts +22 -0
  97. package/bin/memory/runtime/Flush.d.ts +0 -15
  98. package/bin/memory/runtime/Flush.d.ts.map +0 -1
  99. package/bin/memory/runtime/Flush.js +0 -63
  100. package/bin/memory/runtime/Flush.js.map +0 -1
  101. package/src/memory/runtime/Flush.ts +0 -83
@@ -1,21 +1,28 @@
1
1
  /**
2
- * Memory Service Action 逻辑。
2
+ * Memory Plugin action 逻辑。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 只处理业务动作,不包含 CLI/API 参数映射细节。
6
- * - 所有动作统一返回结构化结果,失败不抛给上层。
5
+ * - action 面向 agent 的记忆语义:search/read/remember/digest/revise。
6
+ * - 原始证据先进入 sources,长期知识进入 wiki。
7
+ * - LLM digest/revise 能力由 MemoryPlugin constructor 注入。
7
8
  */
8
9
 
10
+ import type { UIDataTypes, UIMessagePart, UITools } from "ai";
11
+ import { isTextUIPart } from "ai";
9
12
  import type { PluginActionResult } from "@downcity/agent/internal/plugin/types/Plugin.js";
10
13
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
11
14
  import type { JsonValue } from "@downcity/agent/internal/types/common/Json.js";
12
15
  import type {
13
- MemoryFlushPayload,
14
- MemoryGetPayload,
16
+ MemoryDigestPayload,
17
+ MemoryDigestResponse,
18
+ MemoryPluginOptions,
19
+ MemoryReadPayload,
20
+ MemoryRememberPayload,
21
+ MemoryRememberResponse,
22
+ MemoryRevisePayload,
23
+ MemoryReviseResponse,
15
24
  MemorySearchPayload,
16
- MemoryStorePayload,
17
25
  } from "@/memory/types/Memory.js";
18
- import { flushMemory } from "./runtime/Flush.js";
19
26
  import {
20
27
  collectMemoryStatus,
21
28
  searchMemory,
@@ -24,7 +31,93 @@ import {
24
31
  MEMORY_DEFAULTS,
25
32
  type MemoryRuntimeState,
26
33
  } from "./runtime/Store.js";
27
- import { getMemory, storeMemory } from "./runtime/Writer.js";
34
+ import {
35
+ appendManualSource,
36
+ appendMemoryRevision,
37
+ appendWikiPage,
38
+ readMemory,
39
+ readWikiIndex,
40
+ writeSessionSource,
41
+ writeWikiPage,
42
+ } from "./runtime/Writer.js";
43
+
44
+ type AnyUiMessagePart = UIMessagePart<UIDataTypes, UITools>;
45
+
46
+ function toUiParts(message: { parts?: AnyUiMessagePart[] } | null | undefined): AnyUiMessagePart[] {
47
+ return Array.isArray(message?.parts) ? message.parts : [];
48
+ }
49
+
50
+ function extractReadableLine(message: {
51
+ role?: string;
52
+ parts?: AnyUiMessagePart[];
53
+ }): string {
54
+ const role = String(message.role || "").toLowerCase() === "user" ? "User" : "Assistant";
55
+ const text = toUiParts(message)
56
+ .filter(isTextUIPart)
57
+ .map((part) => String(part.text || "").trim())
58
+ .filter(Boolean)
59
+ .join("\n")
60
+ .trim();
61
+ if (!text) {
62
+ return "";
63
+ }
64
+ return `${role}: ${text}`;
65
+ }
66
+
67
+ function readDigestPages(result: Awaited<ReturnType<NonNullable<MemoryPluginOptions["digest"]>>>): {
68
+ pages: Array<{ path?: string; title?: string; content: string; tags?: string[] }>;
69
+ summary?: string;
70
+ } {
71
+ if (typeof result === "string") {
72
+ return {
73
+ pages: [{ title: "Memory Digest", content: result, tags: ["memory", "digest"] }],
74
+ };
75
+ }
76
+ return {
77
+ pages: result.pages,
78
+ summary: result.summary,
79
+ };
80
+ }
81
+
82
+ function readReviseResult(
83
+ result: Awaited<ReturnType<NonNullable<MemoryPluginOptions["revise"]>>>,
84
+ fallbackPath: string,
85
+ ): { path: string; content: string; summary?: string } {
86
+ if (typeof result === "string") {
87
+ return {
88
+ path: fallbackPath,
89
+ content: result,
90
+ };
91
+ }
92
+ return {
93
+ path: result.path || fallbackPath,
94
+ content: result.content,
95
+ summary: result.summary,
96
+ };
97
+ }
98
+
99
+ function slugify(value: string): string {
100
+ const text = String(value || "")
101
+ .trim()
102
+ .toLowerCase()
103
+ .replace(/[^\p{L}\p{N}]+/gu, "-")
104
+ .replace(/^-+|-+$/g, "")
105
+ .slice(0, 80);
106
+ return text || "inbox";
107
+ }
108
+
109
+ function toWikiMemoryPath(value: string): string {
110
+ const clean = String(value || "").replace(/\\/g, "/").replace(/^\/+/, "").trim();
111
+ if (!clean) return ".downcity/memory/wiki/inbox.md";
112
+ if (clean.startsWith(".downcity/memory/wiki/")) {
113
+ return clean.toLowerCase().endsWith(".md") ? clean : `${clean}.md`;
114
+ }
115
+ const withoutPrefix = clean.replace(/^wiki\//, "");
116
+ const withExt = withoutPrefix.toLowerCase().endsWith(".md")
117
+ ? withoutPrefix
118
+ : `${withoutPrefix}.md`;
119
+ return `.downcity/memory/wiki/${withExt}`;
120
+ }
28
121
 
29
122
  /**
30
123
  * status action。
@@ -70,14 +163,14 @@ export async function searchMemoryAction(
70
163
  }
71
164
 
72
165
  /**
73
- * get action。
166
+ * read action。
74
167
  */
75
- export async function getMemoryAction(
168
+ export async function readMemoryAction(
76
169
  context: AgentContext,
77
- payload: MemoryGetPayload,
170
+ payload: MemoryReadPayload,
78
171
  ): Promise<PluginActionResult<JsonValue>> {
79
172
  try {
80
- const data = await getMemory(context, payload);
173
+ const data = await readMemory(context, payload);
81
174
  return { success: true, data: data as unknown as JsonValue };
82
175
  } catch (error) {
83
176
  return {
@@ -88,16 +181,65 @@ export async function getMemoryAction(
88
181
  }
89
182
 
90
183
  /**
91
- * store action。
184
+ * remember action。
92
185
  */
93
- export async function storeMemoryAction(
186
+ export async function rememberMemoryAction(
94
187
  context: AgentContext,
95
- state: MemoryRuntimeState,
96
- payload: MemoryStorePayload,
188
+ options: MemoryPluginOptions,
189
+ payload: MemoryRememberPayload,
97
190
  ): Promise<PluginActionResult<JsonValue>> {
98
191
  try {
99
- const data = await storeMemory(context, state, payload);
100
- return { success: true, data: data as unknown as JsonValue };
192
+ const content = String(payload.content || "").trim();
193
+ if (!content) {
194
+ throw new Error("content is required");
195
+ }
196
+ const source = await appendManualSource(context, content, payload.source);
197
+ const targetPath = toWikiMemoryPath(payload.path || slugify(payload.topic || "inbox"));
198
+
199
+ if (options.revise) {
200
+ const current = await readMemory(context, { path: targetPath }).catch(() => ({
201
+ path: targetPath,
202
+ text: "",
203
+ }));
204
+ const revised = readReviseResult(
205
+ await options.revise({
206
+ rootPath: context.rootPath,
207
+ path: targetPath,
208
+ currentContent: current.text,
209
+ instruction: "Integrate this new memory into the wiki page. Deduplicate and keep it concise.",
210
+ evidence: `${content}\n\nSource: ${source.path}`,
211
+ }),
212
+ targetPath,
213
+ );
214
+ const written = await writeWikiPage(context, {
215
+ path: revised.path,
216
+ title: payload.topic,
217
+ content: revised.content,
218
+ tags: ["memory"],
219
+ });
220
+ const response: MemoryRememberResponse = {
221
+ sourcePath: source.path,
222
+ wikiPath: written.path,
223
+ mode: "revised",
224
+ writtenChars: written.writtenChars,
225
+ summary: revised.summary,
226
+ };
227
+ return { success: true, data: response as unknown as JsonValue };
228
+ }
229
+
230
+ const written = await appendWikiPage(context, {
231
+ path: targetPath,
232
+ title: payload.topic || "Memory Inbox",
233
+ content,
234
+ sourcePath: source.path,
235
+ });
236
+ const response: MemoryRememberResponse = {
237
+ sourcePath: source.path,
238
+ wikiPath: written.path,
239
+ mode: "appended",
240
+ writtenChars: written.writtenChars,
241
+ };
242
+ return { success: true, data: response as unknown as JsonValue };
101
243
  } catch (error) {
102
244
  return {
103
245
  success: false,
@@ -107,16 +249,135 @@ export async function storeMemoryAction(
107
249
  }
108
250
 
109
251
  /**
110
- * flush action。
252
+ * digest action。
111
253
  */
112
- export async function flushMemoryAction(
254
+ export async function digestMemoryAction(
113
255
  context: AgentContext,
114
- state: MemoryRuntimeState,
115
- payload: MemoryFlushPayload,
116
- ): Promise<PluginActionResult> {
256
+ options: MemoryPluginOptions,
257
+ payload: MemoryDigestPayload,
258
+ ): Promise<PluginActionResult<JsonValue>> {
117
259
  try {
118
- const data = await flushMemory(context, state, payload);
119
- return { success: true, data: data as unknown as JsonValue };
260
+ const sessionId = String(payload.sessionId || "").trim();
261
+ if (!sessionId) {
262
+ throw new Error("sessionId is required");
263
+ }
264
+ const maxMessages = Number.isFinite(payload.maxMessages)
265
+ ? Math.max(1, Math.floor(payload.maxMessages as number))
266
+ : 30;
267
+ const historyStore = context.session.get(sessionId).getHistoryStore();
268
+ const total = await historyStore.size();
269
+ const start = Math.max(0, total - maxMessages);
270
+ const messages = await historyStore.slice(start, total);
271
+ const lines = messages
272
+ .map((msg) => extractReadableLine(msg))
273
+ .filter((line) => line.length > 0);
274
+ const transcript =
275
+ lines.length > 0
276
+ ? lines.join("\n\n")
277
+ : "本次 digest 未找到可写入的用户/助手文本内容。";
278
+ const sourceText = [
279
+ `Window: ${start}-${Math.max(start, total - 1)}`,
280
+ "",
281
+ transcript,
282
+ ].join("\n");
283
+ const source = await writeSessionSource(context, sessionId, sourceText);
284
+
285
+ if (options.digest) {
286
+ const wikiIndex = await readWikiIndex(context);
287
+ const digested = readDigestPages(
288
+ await options.digest({
289
+ rootPath: context.rootPath,
290
+ sourceText,
291
+ sourcePath: source.path,
292
+ sessionId,
293
+ wikiIndex,
294
+ }),
295
+ );
296
+ const wikiPaths: string[] = [];
297
+ for (const page of digested.pages) {
298
+ const written = await writeWikiPage(context, page);
299
+ wikiPaths.push(written.path);
300
+ }
301
+ const response: MemoryDigestResponse = {
302
+ sourcePath: source.path,
303
+ wikiPaths,
304
+ messageCount: lines.length,
305
+ mode: "digested",
306
+ summary: digested.summary,
307
+ };
308
+ return { success: true, data: response as unknown as JsonValue };
309
+ }
310
+
311
+ const written = await appendWikiPage(context, {
312
+ path: "session-digests",
313
+ title: "Session Digests",
314
+ content: sourceText,
315
+ sourcePath: source.path,
316
+ });
317
+ const response: MemoryDigestResponse = {
318
+ sourcePath: source.path,
319
+ wikiPaths: [written.path],
320
+ messageCount: lines.length,
321
+ mode: "archived",
322
+ };
323
+ return { success: true, data: response as unknown as JsonValue };
324
+ } catch (error) {
325
+ return {
326
+ success: false,
327
+ error: String(error),
328
+ };
329
+ }
330
+ }
331
+
332
+ /**
333
+ * revise action。
334
+ */
335
+ export async function reviseMemoryAction(
336
+ context: AgentContext,
337
+ options: MemoryPluginOptions,
338
+ payload: MemoryRevisePayload,
339
+ ): Promise<PluginActionResult<JsonValue>> {
340
+ try {
341
+ const targetPath = toWikiMemoryPath(String(payload.path || "").trim());
342
+ if (!targetPath) {
343
+ throw new Error("path is required");
344
+ }
345
+ const instruction = String(payload.instruction || "").trim();
346
+ if (!instruction) {
347
+ throw new Error("instruction is required");
348
+ }
349
+
350
+ if (options.revise) {
351
+ const current = await readMemory(context, { path: targetPath }).catch(() => ({
352
+ path: targetPath,
353
+ text: "",
354
+ }));
355
+ const revised = readReviseResult(
356
+ await options.revise({
357
+ rootPath: context.rootPath,
358
+ path: targetPath,
359
+ currentContent: current.text,
360
+ instruction,
361
+ evidence: String(payload.evidence || ""),
362
+ }),
363
+ targetPath,
364
+ );
365
+ const written = await writeWikiPage(context, {
366
+ path: revised.path,
367
+ content: revised.content,
368
+ tags: ["memory"],
369
+ });
370
+ const response: MemoryReviseResponse = {
371
+ path: written.path,
372
+ mode: "revised",
373
+ writtenChars: written.writtenChars,
374
+ summary: revised.summary,
375
+ };
376
+ return { success: true, data: response as unknown as JsonValue };
377
+ }
378
+
379
+ const response = await appendMemoryRevision(context, payload);
380
+ return { success: true, data: response as unknown as JsonValue };
120
381
  } catch (error) {
121
382
  return {
122
383
  success: false,
@@ -143,5 +404,11 @@ export function toSearchPayload(input: Record<string, unknown>): MemorySearchPay
143
404
  : typeof input.minScore === "string"
144
405
  ? Number(input.minScore)
145
406
  : MEMORY_DEFAULTS.minScore,
407
+ includeSources:
408
+ typeof input.includeSources === "boolean"
409
+ ? input.includeSources
410
+ : typeof input.includeSources === "string"
411
+ ? input.includeSources === "true"
412
+ : undefined,
146
413
  };
147
414
  }
@@ -1,10 +1,10 @@
1
1
  /**
2
- * MemoryPlugin:memory plugin 的类实现。
2
+ * MemoryPlugin:agent 的长期记忆 plugin
3
3
  *
4
4
  * 关键点(中文)
5
- * - memory plugin state 现在归属于 plugin 实例。
6
- * - agent 持有 MemoryPlugin 实例,从而天然形成 per-agent 状态边界。
7
- * - 运行态只在实例内部缓存,不再放到模块级 Map。
5
+ * - 对外仍然是 MemoryPlugin,内部使用 LLM Wiki 方式组织知识。
6
+ * - constructor 注入 digest/revise 能力,plugin 不绑定具体 LLM 服务。
7
+ * - action 面向 agent 语义,而不是暴露底层文件写入细节。
8
8
  */
9
9
 
10
10
  import type { Command } from "commander";
@@ -13,20 +13,20 @@ import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/
13
13
  import type { PluginActions } from "@downcity/agent/internal/plugin/types/Plugin.js";
14
14
  import { BasePlugin } from "@downcity/agent/internal/plugin/core/BasePlugin.js";
15
15
  import {
16
- flushMemoryAction,
17
- getMemoryAction,
16
+ digestMemoryAction,
17
+ readMemoryAction,
18
+ rememberMemoryAction,
19
+ reviseMemoryAction,
18
20
  searchMemoryAction,
19
21
  statusMemoryAction,
20
- storeMemoryAction,
21
22
  } from "./Action.js";
22
23
  import {
23
24
  createMemoryRuntimeState,
24
- startMemoryRuntime,
25
- stopMemoryRuntime,
26
25
  type MemoryRuntimeState,
27
26
  } from "./runtime/Store.js";
28
27
  import { buildMemoryPluginSystemText } from "./runtime/SystemProvider.js";
29
28
  import { ensureMemoryDirectories } from "./runtime/Writer.js";
29
+ import type { MemoryPluginOptions } from "./types/Memory.js";
30
30
 
31
31
  function parsePositiveInteger(value: string): number {
32
32
  const text = String(value || "").trim();
@@ -74,6 +74,11 @@ function readOptionalNumber(body: JsonObject, key: string): number | undefined {
74
74
  return typeof value === "number" ? value : undefined;
75
75
  }
76
76
 
77
+ function readOptionalBoolean(body: JsonObject, key: string): boolean | undefined {
78
+ const value = body[key];
79
+ return typeof value === "boolean" ? value : undefined;
80
+ }
81
+
77
82
  /**
78
83
  * Memory plugin 类实现。
79
84
  */
@@ -88,6 +93,13 @@ export class MemoryPlugin extends BasePlugin {
88
93
  */
89
94
  public runtimeState: MemoryRuntimeState | null = null;
90
95
 
96
+ /**
97
+ * 创建 MemoryPlugin。
98
+ */
99
+ constructor(private readonly options: MemoryPluginOptions = {}) {
100
+ super();
101
+ }
102
+
91
103
  /**
92
104
  * 当前 plugin 的 system 文本提供器。
93
105
  */
@@ -101,12 +113,9 @@ export class MemoryPlugin extends BasePlugin {
101
113
  readonly lifecycle = {
102
114
  start: async (context: AgentContext): Promise<void> => {
103
115
  await ensureMemoryDirectories(context.rootPath);
104
- const state = this.getOrCreateRuntimeState(context);
105
- await startMemoryRuntime(context, state);
116
+ this.getOrCreateRuntimeState(context);
106
117
  },
107
118
  stop: async (): Promise<void> => {
108
- if (!this.runtimeState) return;
109
- await stopMemoryRuntime(this.runtimeState);
110
119
  this.runtimeState = null;
111
120
  },
112
121
  };
@@ -117,7 +126,7 @@ export class MemoryPlugin extends BasePlugin {
117
126
  readonly actions: PluginActions = {
118
127
  status: {
119
128
  command: {
120
- description: "查看 memory 状态(backend/files/chunks)",
129
+ description: "查看 memory wiki 状态(wiki/source/working)",
121
130
  mapInput() {
122
131
  return {};
123
132
  },
@@ -129,12 +138,13 @@ export class MemoryPlugin extends BasePlugin {
129
138
  },
130
139
  search: {
131
140
  command: {
132
- description: "检索记忆片段",
141
+ description: "检索 memory wiki",
133
142
  configure(command: Command) {
134
143
  command
135
144
  .argument("<query>")
136
145
  .option("--max-results <number>", "返回条数上限", parsePositiveInteger)
137
- .option("--min-score <number>", "最小相关分数", parseNumber);
146
+ .option("--min-score <number>", "最小相关分数", parseNumber)
147
+ .option("--include-sources", "同时检索原始 source 层");
138
148
  },
139
149
  mapInput({ args, opts }) {
140
150
  const payload: JsonObject = {
@@ -146,6 +156,9 @@ export class MemoryPlugin extends BasePlugin {
146
156
  if (typeof opts.minScore === "number") {
147
157
  payload.minScore = opts.minScore;
148
158
  }
159
+ if (opts.includeSources === true) {
160
+ payload.includeSources = true;
161
+ }
149
162
  return payload;
150
163
  },
151
164
  },
@@ -156,12 +169,13 @@ export class MemoryPlugin extends BasePlugin {
156
169
  query: readString(body, "query"),
157
170
  maxResults: readOptionalNumber(body, "maxResults"),
158
171
  minScore: readOptionalNumber(body, "minScore"),
172
+ includeSources: readOptionalBoolean(body, "includeSources"),
159
173
  });
160
174
  },
161
175
  },
162
- get: {
176
+ read: {
163
177
  command: {
164
- description: "读取记忆文件片段",
178
+ description: "读取 memory wiki/source 文件片段",
165
179
  configure(command: Command) {
166
180
  command
167
181
  .argument("<memoryPath>", "记忆文件路径(相对项目根目录)")
@@ -183,52 +197,52 @@ export class MemoryPlugin extends BasePlugin {
183
197
  },
184
198
  execute: async (params) => {
185
199
  const body = readBodyObject(params.payload);
186
- return await getMemoryAction(params.context, {
200
+ return await readMemoryAction(params.context, {
187
201
  path: readString(body, "path"),
188
202
  from: readOptionalNumber(body, "from"),
189
203
  lines: readOptionalNumber(body, "lines"),
190
204
  });
191
205
  },
192
206
  },
193
- store: {
207
+ remember: {
194
208
  command: {
195
- description: "显式写入 memory(longterm/daily/working)",
209
+ description: "把事实/偏好/决策记入 memory wiki",
196
210
  configure(command: Command) {
197
211
  command
198
- .requiredOption("--content <text>", "写入内容")
199
- .option("--target <target>", "写入层(longterm|daily|working)")
200
- .option("--session-id <sessionId>", "working 目标必填");
212
+ .requiredOption("--content <text>", "需要记住的内容")
213
+ .option("--topic <topic>", "记忆主题")
214
+ .option("--wiki-path <path>", "目标 wiki page 路径")
215
+ .option("--source <source>", "来源说明");
201
216
  },
202
217
  mapInput({ opts }) {
203
218
  const payload: JsonObject = {
204
219
  content: String(opts.content || ""),
205
220
  };
206
- if (typeof opts.target === "string") {
207
- payload.target = String(opts.target).trim();
221
+ if (typeof opts.topic === "string") {
222
+ payload.topic = String(opts.topic).trim();
208
223
  }
209
- if (typeof opts.sessionId === "string") {
210
- payload.sessionId = String(opts.sessionId).trim();
224
+ if (typeof opts.wikiPath === "string") {
225
+ payload.path = String(opts.wikiPath).trim();
226
+ }
227
+ if (typeof opts.source === "string") {
228
+ payload.source = String(opts.source).trim();
211
229
  }
212
230
  return payload;
213
231
  },
214
232
  },
215
233
  execute: async (params) => {
216
234
  const body = readBodyObject(params.payload);
217
- const target = readOptionalString(body, "target");
218
- const state = this.getOrCreateRuntimeState(params.context);
219
- return await storeMemoryAction(params.context, state, {
235
+ return await rememberMemoryAction(params.context, this.options, {
220
236
  content: readString(body, "content"),
221
- target:
222
- target === "longterm" || target === "daily" || target === "working"
223
- ? target
224
- : undefined,
225
- sessionId: readOptionalString(body, "sessionId"),
237
+ topic: readOptionalString(body, "topic"),
238
+ path: readOptionalString(body, "path"),
239
+ source: readOptionalString(body, "source"),
226
240
  });
227
241
  },
228
242
  },
229
- flush: {
243
+ digest: {
230
244
  command: {
231
- description: "将当前会话最近消息刷写到 daily memory",
245
+ description: " session 提炼进 memory wiki",
232
246
  configure(command: Command) {
233
247
  command
234
248
  .requiredOption("--session-id <sessionId>", "会话 ID")
@@ -246,13 +260,41 @@ export class MemoryPlugin extends BasePlugin {
246
260
  },
247
261
  execute: async (params) => {
248
262
  const body = readBodyObject(params.payload);
249
- const state = this.getOrCreateRuntimeState(params.context);
250
- return await flushMemoryAction(params.context, state, {
263
+ return await digestMemoryAction(params.context, this.options, {
251
264
  sessionId: readString(body, "sessionId"),
252
265
  maxMessages: readOptionalNumber(body, "maxMessages"),
253
266
  });
254
267
  },
255
268
  },
269
+ revise: {
270
+ command: {
271
+ description: "基于新证据修订 memory wiki page",
272
+ configure(command: Command) {
273
+ command
274
+ .argument("<memoryPath>", "目标 wiki page 路径")
275
+ .requiredOption("--instruction <text>", "修订指令")
276
+ .option("--evidence <text>", "新证据");
277
+ },
278
+ mapInput({ args, opts }) {
279
+ const payload: JsonObject = {
280
+ path: String(args[0] || ""),
281
+ instruction: String(opts.instruction || ""),
282
+ };
283
+ if (typeof opts.evidence === "string") {
284
+ payload.evidence = String(opts.evidence).trim();
285
+ }
286
+ return payload;
287
+ },
288
+ },
289
+ execute: async (params) => {
290
+ const body = readBodyObject(params.payload);
291
+ return await reviseMemoryAction(params.context, this.options, {
292
+ path: readString(body, "path"),
293
+ instruction: readString(body, "instruction"),
294
+ evidence: readOptionalString(body, "evidence"),
295
+ });
296
+ },
297
+ },
256
298
  };
257
299
 
258
300
  /**
@@ -2,7 +2,7 @@
2
2
  * Memory Search 运行时。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 直接扫描 Markdown 文件,不依赖额外索引库。
5
+ * - 直接扫描 LLM Wiki Markdown 文件,不依赖额外索引库。
6
6
  * - 统一收敛检索、分块、打分与状态统计逻辑。
7
7
  */
8
8
 
@@ -147,17 +147,20 @@ function chunkMarkdown(content: string): Array<{
147
147
  return out;
148
148
  }
149
149
 
150
- async function readMemoryChunks(context: AgentContext): Promise<Array<{
150
+ async function readMemoryChunks(
151
+ context: AgentContext,
152
+ options: { includeSources?: boolean },
153
+ ): Promise<Array<{
151
154
  path: string;
152
- source: "longterm" | "daily" | "working";
155
+ source: "wiki" | "source" | "working";
153
156
  startLine: number;
154
157
  endLine: number;
155
158
  text: string;
156
159
  }>> {
157
- const files = await listMemorySourceFiles(context.rootPath);
160
+ const files = await listMemorySourceFiles(context.rootPath, options);
158
161
  const out: Array<{
159
162
  path: string;
160
- source: "longterm" | "daily" | "working";
163
+ source: "wiki" | "source" | "working";
161
164
  startLine: number;
162
165
  endLine: number;
163
166
  text: string;
@@ -185,10 +188,12 @@ export async function collectMemoryStatus(
185
188
  state: MemoryRuntimeState,
186
189
  ): Promise<MemoryStatusResponse> {
187
190
  void state;
188
- const files = await listMemorySourceFiles(context.rootPath);
191
+ const files = await listMemorySourceFiles(context.rootPath, {
192
+ includeSources: true,
193
+ });
189
194
  const sourceCounts: MemorySourceStat[] = [
190
- { source: "longterm", files: 0, chunks: 0 },
191
- { source: "daily", files: 0, chunks: 0 },
195
+ { source: "wiki", files: 0, chunks: 0 },
196
+ { source: "source", files: 0, chunks: 0 },
192
197
  { source: "working", files: 0, chunks: 0 },
193
198
  ];
194
199
 
@@ -257,7 +262,9 @@ export async function searchMemory(
257
262
  );
258
263
 
259
264
  try {
260
- const results = (await readMemoryChunks(context))
265
+ const results = (await readMemoryChunks(context, {
266
+ includeSources: payload.includeSources,
267
+ }))
261
268
  .map((chunk) => {
262
269
  const score = buildSnippetScore(chunk.text, tokens);
263
270
  const citation =