@downcity/agent 1.1.6 → 1.1.8

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 (284) hide show
  1. package/bin/agent/AgentContext.d.ts.map +1 -1
  2. package/bin/agent/AgentContext.js +1 -0
  3. package/bin/agent/AgentContext.js.map +1 -1
  4. package/bin/agent/AgentContextTypes.d.ts +5 -1
  5. package/bin/agent/AgentContextTypes.d.ts.map +1 -1
  6. package/bin/agent/AgentRuntime.d.ts +6 -1
  7. package/bin/agent/AgentRuntime.d.ts.map +1 -1
  8. package/bin/agent/AgentRuntime.js +22 -3
  9. package/bin/agent/AgentRuntime.js.map +1 -1
  10. package/bin/agent/AgentRuntimeState.d.ts.map +1 -1
  11. package/bin/agent/AgentRuntimeState.js +58 -2
  12. package/bin/agent/AgentRuntimeState.js.map +1 -1
  13. package/bin/agent/AgentRuntimeTypes.d.ts +5 -1
  14. package/bin/agent/AgentRuntimeTypes.d.ts.map +1 -1
  15. package/bin/agent/project/AgentInitializer.d.ts +3 -2
  16. package/bin/agent/project/AgentInitializer.d.ts.map +1 -1
  17. package/bin/agent/project/AgentInitializer.js +34 -44
  18. package/bin/agent/project/AgentInitializer.js.map +1 -1
  19. package/bin/config/Config.d.ts.map +1 -1
  20. package/bin/config/Config.js +2 -21
  21. package/bin/config/Config.js.map +1 -1
  22. package/bin/config/Paths.d.ts +1 -5
  23. package/bin/config/Paths.d.ts.map +1 -1
  24. package/bin/config/Paths.js +2 -8
  25. package/bin/config/Paths.js.map +1 -1
  26. package/bin/host/daemon/ProjectSetup.d.ts +2 -1
  27. package/bin/host/daemon/ProjectSetup.d.ts.map +1 -1
  28. package/bin/host/daemon/ProjectSetup.js +14 -21
  29. package/bin/host/daemon/ProjectSetup.js.map +1 -1
  30. package/bin/host/runtime/AgentHostRuntime.d.ts.map +1 -1
  31. package/bin/host/runtime/AgentHostRuntime.js +1 -2
  32. package/bin/host/runtime/AgentHostRuntime.js.map +1 -1
  33. package/bin/host/runtime/CityPaths.d.ts +0 -3
  34. package/bin/host/runtime/CityPaths.d.ts.map +1 -1
  35. package/bin/host/runtime/CityPaths.js +0 -3
  36. package/bin/host/runtime/CityPaths.js.map +1 -1
  37. package/bin/host/sdk/Agent.d.ts +4 -0
  38. package/bin/host/sdk/Agent.d.ts.map +1 -1
  39. package/bin/host/sdk/Agent.js +71 -2
  40. package/bin/host/sdk/Agent.js.map +1 -1
  41. package/bin/host/sdk/AgentSdkTypes.d.ts +9 -0
  42. package/bin/host/sdk/AgentSdkTypes.d.ts.map +1 -1
  43. package/bin/host/sdk/SdkSessionSystemComposer.d.ts +11 -2
  44. package/bin/host/sdk/SdkSessionSystemComposer.d.ts.map +1 -1
  45. package/bin/host/sdk/SdkSessionSystemComposer.js +19 -2
  46. package/bin/host/sdk/SdkSessionSystemComposer.js.map +1 -1
  47. package/bin/host/sdk/Session.d.ts +10 -0
  48. package/bin/host/sdk/Session.d.ts.map +1 -1
  49. package/bin/host/sdk/Session.js +8 -0
  50. package/bin/host/sdk/Session.js.map +1 -1
  51. package/bin/http/Server.d.ts.map +1 -1
  52. package/bin/http/Server.js +1 -11
  53. package/bin/http/Server.js.map +1 -1
  54. package/bin/http/auth/AuthEnv.d.ts +0 -9
  55. package/bin/http/auth/AuthEnv.d.ts.map +1 -1
  56. package/bin/http/auth/AuthEnv.js +0 -9
  57. package/bin/http/auth/AuthEnv.js.map +1 -1
  58. package/bin/http/auth/CliAuthStateStore.d.ts +0 -4
  59. package/bin/http/auth/CliAuthStateStore.d.ts.map +1 -1
  60. package/bin/http/auth/CliAuthStateStore.js +0 -4
  61. package/bin/http/auth/CliAuthStateStore.js.map +1 -1
  62. package/bin/http/control/ModelRoutes.d.ts.map +1 -1
  63. package/bin/http/control/ModelRoutes.js +3 -8
  64. package/bin/http/control/ModelRoutes.js.map +1 -1
  65. package/bin/index.d.ts +2 -6
  66. package/bin/index.d.ts.map +1 -1
  67. package/bin/index.js +2 -6
  68. package/bin/index.js.map +1 -1
  69. package/bin/model/CreateModel.d.ts +2 -2
  70. package/bin/model/CreateModel.d.ts.map +1 -1
  71. package/bin/model/CreateModel.js +13 -12
  72. package/bin/model/CreateModel.js.map +1 -1
  73. package/bin/plugin/Activation.d.ts +4 -0
  74. package/bin/plugin/Activation.d.ts.map +1 -1
  75. package/bin/plugin/Activation.js +2 -2
  76. package/bin/plugin/Activation.js.map +1 -1
  77. package/bin/plugin/LocalExecution.d.ts.map +1 -1
  78. package/bin/plugin/LocalExecution.js +23 -2
  79. package/bin/plugin/LocalExecution.js.map +1 -1
  80. package/bin/plugin/PluginRegistry.js +2 -2
  81. package/bin/plugin/PluginRegistry.js.map +1 -1
  82. package/bin/plugins/asr/Plugin.d.ts.map +1 -1
  83. package/bin/plugins/asr/Plugin.js +4 -5
  84. package/bin/plugins/asr/Plugin.js.map +1 -1
  85. package/bin/plugins/auth/Plugin.d.ts.map +1 -1
  86. package/bin/plugins/auth/Plugin.js +1 -0
  87. package/bin/plugins/auth/Plugin.js.map +1 -1
  88. package/bin/plugins/auth/runtime/AuthorizationConfig.d.ts +4 -4
  89. package/bin/plugins/auth/runtime/AuthorizationConfig.d.ts.map +1 -1
  90. package/bin/plugins/auth/runtime/AuthorizationConfig.js +28 -26
  91. package/bin/plugins/auth/runtime/AuthorizationConfig.js.map +1 -1
  92. package/bin/plugins/auth/runtime/AuthorizationPolicy.d.ts +2 -0
  93. package/bin/plugins/auth/runtime/AuthorizationPolicy.d.ts.map +1 -1
  94. package/bin/plugins/auth/runtime/AuthorizationPolicy.js +3 -2
  95. package/bin/plugins/auth/runtime/AuthorizationPolicy.js.map +1 -1
  96. package/bin/plugins/auth/runtime/AuthorizationStore.d.ts +1 -1
  97. package/bin/plugins/auth/runtime/AuthorizationStore.d.ts.map +1 -1
  98. package/bin/plugins/auth/runtime/AuthorizationStore.js +3 -4
  99. package/bin/plugins/auth/runtime/AuthorizationStore.js.map +1 -1
  100. package/bin/plugins/skill/Plugin.js +2 -2
  101. package/bin/plugins/skill/Plugin.js.map +1 -1
  102. package/bin/plugins/tts/Plugin.d.ts.map +1 -1
  103. package/bin/plugins/tts/Plugin.js +4 -5
  104. package/bin/plugins/tts/Plugin.js.map +1 -1
  105. package/bin/plugins/web/Plugin.d.ts.map +1 -1
  106. package/bin/plugins/web/Plugin.js +4 -5
  107. package/bin/plugins/web/Plugin.js.map +1 -1
  108. package/bin/plugins/workboard/Plugin.js +2 -2
  109. package/bin/plugins/workboard/Plugin.js.map +1 -1
  110. package/bin/service/builtins/chat/accounts/ChannelAccountService.d.ts +4 -1
  111. package/bin/service/builtins/chat/accounts/ChannelAccountService.d.ts.map +1 -1
  112. package/bin/service/builtins/chat/accounts/ChannelAccountService.js +64 -91
  113. package/bin/service/builtins/chat/accounts/ChannelAccountService.js.map +1 -1
  114. package/bin/service/builtins/chat/runtime/ChatChannelActions.d.ts.map +1 -1
  115. package/bin/service/builtins/chat/runtime/ChatChannelActions.js +11 -18
  116. package/bin/service/builtins/chat/runtime/ChatChannelActions.js.map +1 -1
  117. package/bin/service/builtins/chat/runtime/ChatChannelCore.d.ts +1 -1
  118. package/bin/service/builtins/chat/runtime/ChatChannelCore.d.ts.map +1 -1
  119. package/bin/service/builtins/chat/runtime/ChatChannelCore.js +9 -17
  120. package/bin/service/builtins/chat/runtime/ChatChannelCore.js.map +1 -1
  121. package/bin/service/builtins/memory/Action.d.ts +1 -5
  122. package/bin/service/builtins/memory/Action.d.ts.map +1 -1
  123. package/bin/service/builtins/memory/Action.js +4 -42
  124. package/bin/service/builtins/memory/Action.js.map +1 -1
  125. package/bin/service/builtins/memory/MemoryService.d.ts.map +1 -1
  126. package/bin/service/builtins/memory/MemoryService.js +2 -32
  127. package/bin/service/builtins/memory/MemoryService.js.map +1 -1
  128. package/bin/service/builtins/memory/runtime/Search.d.ts +7 -3
  129. package/bin/service/builtins/memory/runtime/Search.d.ts.map +1 -1
  130. package/bin/service/builtins/memory/runtime/Search.js +220 -16
  131. package/bin/service/builtins/memory/runtime/Search.js.map +1 -1
  132. package/bin/service/builtins/memory/runtime/Store.d.ts +9 -50
  133. package/bin/service/builtins/memory/runtime/Store.d.ts.map +1 -1
  134. package/bin/service/builtins/memory/runtime/Store.js +10 -130
  135. package/bin/service/builtins/memory/runtime/Store.js.map +1 -1
  136. package/bin/service/builtins/memory/runtime/Writer.d.ts.map +1 -1
  137. package/bin/service/builtins/memory/runtime/Writer.js +1 -2
  138. package/bin/service/builtins/memory/runtime/Writer.js.map +1 -1
  139. package/bin/service/builtins/memory/types/Memory.d.ts +3 -57
  140. package/bin/service/builtins/memory/types/Memory.d.ts.map +1 -1
  141. package/bin/service/schedule/Store.d.ts +22 -25
  142. package/bin/service/schedule/Store.d.ts.map +1 -1
  143. package/bin/service/schedule/Store.js +172 -154
  144. package/bin/service/schedule/Store.js.map +1 -1
  145. package/bin/session/composer/system/default/SystemDomain.d.ts.map +1 -1
  146. package/bin/session/composer/system/default/SystemDomain.js +1 -0
  147. package/bin/session/composer/system/default/SystemDomain.js.map +1 -1
  148. package/bin/shared/types/AgentHost.d.ts +120 -4
  149. package/bin/shared/types/AgentHost.d.ts.map +1 -1
  150. package/bin/shared/types/Plugin.d.ts +5 -1
  151. package/bin/shared/types/Plugin.d.ts.map +1 -1
  152. package/package.json +1 -4
  153. package/src/agent/AgentContext.ts +1 -0
  154. package/src/agent/AgentContextTypes.ts +5 -0
  155. package/src/agent/AgentRuntime.ts +32 -3
  156. package/src/agent/AgentRuntimeState.ts +66 -2
  157. package/src/agent/AgentRuntimeTypes.ts +5 -0
  158. package/src/agent/project/AgentInitializer.ts +40 -42
  159. package/src/config/Config.ts +2 -17
  160. package/src/config/Paths.ts +2 -9
  161. package/src/host/daemon/ProjectSetup.ts +19 -21
  162. package/src/host/runtime/AgentHostRuntime.ts +0 -2
  163. package/src/host/runtime/CityPaths.ts +0 -3
  164. package/src/host/sdk/Agent.ts +67 -2
  165. package/src/host/sdk/AgentSdkTypes.ts +10 -0
  166. package/src/host/sdk/SdkSessionSystemComposer.ts +39 -2
  167. package/src/host/sdk/Session.ts +18 -0
  168. package/src/http/Server.ts +0 -13
  169. package/src/http/auth/AuthEnv.ts +0 -9
  170. package/src/http/auth/CliAuthStateStore.ts +0 -4
  171. package/src/http/control/ModelRoutes.ts +3 -9
  172. package/src/index.ts +2 -12
  173. package/src/model/CreateModel.ts +15 -13
  174. package/src/plugin/Activation.ts +6 -2
  175. package/src/plugin/LocalExecution.ts +24 -2
  176. package/src/plugin/PluginRegistry.ts +2 -2
  177. package/src/plugins/asr/Plugin.ts +4 -5
  178. package/src/plugins/auth/Plugin.ts +1 -0
  179. package/src/plugins/auth/runtime/AuthorizationConfig.ts +47 -37
  180. package/src/plugins/auth/runtime/AuthorizationPolicy.ts +5 -2
  181. package/src/plugins/auth/runtime/AuthorizationStore.ts +6 -5
  182. package/src/plugins/skill/Plugin.ts +2 -2
  183. package/src/plugins/tts/Plugin.ts +4 -5
  184. package/src/plugins/web/Plugin.ts +4 -5
  185. package/src/plugins/workboard/Plugin.ts +2 -2
  186. package/src/service/builtins/chat/accounts/ChannelAccountService.ts +42 -62
  187. package/src/service/builtins/chat/runtime/ChatChannelActions.ts +12 -18
  188. package/src/service/builtins/chat/runtime/ChatChannelCore.ts +9 -14
  189. package/src/service/builtins/memory/Action.ts +6 -47
  190. package/src/service/builtins/memory/MemoryService.ts +1 -33
  191. package/src/service/builtins/memory/runtime/Search.ts +256 -16
  192. package/src/service/builtins/memory/runtime/Store.ts +13 -185
  193. package/src/service/builtins/memory/runtime/Writer.ts +1 -2
  194. package/src/service/builtins/memory/types/Memory.ts +2 -59
  195. package/src/service/schedule/Store.ts +215 -175
  196. package/src/session/composer/system/default/SystemDomain.ts +1 -0
  197. package/src/shared/types/AgentHost.ts +138 -4
  198. package/src/shared/types/Plugin.ts +5 -0
  199. package/tsconfig.tsbuildinfo +1 -1
  200. package/bin/http/auth/AuthMiddleware.d.ts +0 -36
  201. package/bin/http/auth/AuthMiddleware.d.ts.map +0 -1
  202. package/bin/http/auth/AuthMiddleware.js +0 -37
  203. package/bin/http/auth/AuthMiddleware.js.map +0 -1
  204. package/bin/http/auth/AuthRoutes.d.ts +0 -17
  205. package/bin/http/auth/AuthRoutes.d.ts.map +0 -1
  206. package/bin/http/auth/AuthRoutes.js +0 -78
  207. package/bin/http/auth/AuthRoutes.js.map +0 -1
  208. package/bin/http/auth/AuthService.d.ts +0 -119
  209. package/bin/http/auth/AuthService.d.ts.map +0 -1
  210. package/bin/http/auth/AuthService.js +0 -307
  211. package/bin/http/auth/AuthService.js.map +0 -1
  212. package/bin/http/auth/AuthStore.d.ts +0 -165
  213. package/bin/http/auth/AuthStore.d.ts.map +0 -1
  214. package/bin/http/auth/AuthStore.js +0 -442
  215. package/bin/http/auth/AuthStore.js.map +0 -1
  216. package/bin/http/auth/RoutePolicy.d.ts +0 -30
  217. package/bin/http/auth/RoutePolicy.d.ts.map +0 -1
  218. package/bin/http/auth/RoutePolicy.js +0 -229
  219. package/bin/http/auth/RoutePolicy.js.map +0 -1
  220. package/bin/plugin/Lifecycle.d.ts +0 -33
  221. package/bin/plugin/Lifecycle.d.ts.map +0 -1
  222. package/bin/plugin/Lifecycle.js +0 -102
  223. package/bin/plugin/Lifecycle.js.map +0 -1
  224. package/bin/service/builtins/memory/runtime/Indexer.d.ts +0 -71
  225. package/bin/service/builtins/memory/runtime/Indexer.d.ts.map +0 -1
  226. package/bin/service/builtins/memory/runtime/Indexer.js +0 -345
  227. package/bin/service/builtins/memory/runtime/Indexer.js.map +0 -1
  228. package/bin/service/schedule/Schema.d.ts +0 -171
  229. package/bin/service/schedule/Schema.d.ts.map +0 -1
  230. package/bin/service/schedule/Schema.js +0 -26
  231. package/bin/service/schedule/Schema.js.map +0 -1
  232. package/bin/shared/utils/store/StoreChannelAccountRepository.d.ts +0 -34
  233. package/bin/shared/utils/store/StoreChannelAccountRepository.d.ts.map +0 -1
  234. package/bin/shared/utils/store/StoreChannelAccountRepository.js +0 -198
  235. package/bin/shared/utils/store/StoreChannelAccountRepository.js.map +0 -1
  236. package/bin/shared/utils/store/StoreEnvRepository.d.ts +0 -98
  237. package/bin/shared/utils/store/StoreEnvRepository.d.ts.map +0 -1
  238. package/bin/shared/utils/store/StoreEnvRepository.js +0 -334
  239. package/bin/shared/utils/store/StoreEnvRepository.js.map +0 -1
  240. package/bin/shared/utils/store/StoreModelRepository.d.ts +0 -61
  241. package/bin/shared/utils/store/StoreModelRepository.d.ts.map +0 -1
  242. package/bin/shared/utils/store/StoreModelRepository.js +0 -278
  243. package/bin/shared/utils/store/StoreModelRepository.js.map +0 -1
  244. package/bin/shared/utils/store/StoreSchema.d.ts +0 -13
  245. package/bin/shared/utils/store/StoreSchema.d.ts.map +0 -1
  246. package/bin/shared/utils/store/StoreSchema.js +0 -319
  247. package/bin/shared/utils/store/StoreSchema.js.map +0 -1
  248. package/bin/shared/utils/store/StoreSecureSettings.d.ts +0 -33
  249. package/bin/shared/utils/store/StoreSecureSettings.d.ts.map +0 -1
  250. package/bin/shared/utils/store/StoreSecureSettings.js +0 -91
  251. package/bin/shared/utils/store/StoreSecureSettings.js.map +0 -1
  252. package/bin/shared/utils/store/StoreShared.d.ts +0 -44
  253. package/bin/shared/utils/store/StoreShared.d.ts.map +0 -1
  254. package/bin/shared/utils/store/StoreShared.js +0 -40
  255. package/bin/shared/utils/store/StoreShared.js.map +0 -1
  256. package/bin/shared/utils/store/crypto.d.ts +0 -24
  257. package/bin/shared/utils/store/crypto.d.ts.map +0 -1
  258. package/bin/shared/utils/store/crypto.js +0 -101
  259. package/bin/shared/utils/store/crypto.js.map +0 -1
  260. package/bin/shared/utils/store/index.d.ts +0 -230
  261. package/bin/shared/utils/store/index.d.ts.map +0 -1
  262. package/bin/shared/utils/store/index.js +0 -360
  263. package/bin/shared/utils/store/index.js.map +0 -1
  264. package/bin/shared/utils/store/schema.d.ts +0 -690
  265. package/bin/shared/utils/store/schema.d.ts.map +0 -1
  266. package/bin/shared/utils/store/schema.js +0 -81
  267. package/bin/shared/utils/store/schema.js.map +0 -1
  268. package/src/http/auth/AuthMiddleware.ts +0 -61
  269. package/src/http/auth/AuthRoutes.ts +0 -100
  270. package/src/http/auth/AuthService.ts +0 -367
  271. package/src/http/auth/AuthStore.ts +0 -572
  272. package/src/http/auth/RoutePolicy.ts +0 -255
  273. package/src/plugin/Lifecycle.ts +0 -116
  274. package/src/service/builtins/memory/runtime/Indexer.ts +0 -466
  275. package/src/service/schedule/Schema.ts +0 -34
  276. package/src/shared/utils/store/StoreChannelAccountRepository.ts +0 -269
  277. package/src/shared/utils/store/StoreEnvRepository.ts +0 -452
  278. package/src/shared/utils/store/StoreModelRepository.ts +0 -324
  279. package/src/shared/utils/store/StoreSchema.ts +0 -344
  280. package/src/shared/utils/store/StoreSecureSettings.ts +0 -126
  281. package/src/shared/utils/store/StoreShared.ts +0 -67
  282. package/src/shared/utils/store/crypto.ts +0 -112
  283. package/src/shared/utils/store/index.ts +0 -497
  284. package/src/shared/utils/store/schema.ts +0 -103
@@ -15,7 +15,6 @@ import { BaseService } from "@/service/builtins/BaseService.js";
15
15
  import {
16
16
  flushMemoryAction,
17
17
  getMemoryAction,
18
- indexMemoryAction,
19
18
  searchMemoryAction,
20
19
  statusMemoryAction,
21
20
  storeMemoryAction,
@@ -76,11 +75,6 @@ function readOptionalNumber(body: JsonObject, key: string): number | undefined {
76
75
  return typeof value === "number" ? value : undefined;
77
76
  }
78
77
 
79
- function readOptionalBoolean(body: JsonObject, key: string): boolean | undefined {
80
- const value = body[key];
81
- return typeof value === "boolean" ? value : undefined;
82
- }
83
-
84
78
  /**
85
79
  * Memory service 类实现。
86
80
  */
@@ -124,7 +118,7 @@ export class MemoryService extends BaseService {
124
118
  readonly actions: ServiceActions = {
125
119
  status: {
126
120
  command: {
127
- description: "查看 memory 状态(backend/files/chunks/dirty)",
121
+ description: "查看 memory 状态(backend/files/chunks)",
128
122
  mapInput() {
129
123
  return {};
130
124
  },
@@ -137,32 +131,6 @@ export class MemoryService extends BaseService {
137
131
  return await statusMemoryAction(params.context, state);
138
132
  },
139
133
  },
140
- index: {
141
- command: {
142
- description: "重建 memory 索引",
143
- configure(command: Command) {
144
- command.option("--force", "强制全量重建", false);
145
- },
146
- mapInput({ opts }) {
147
- return {
148
- force: opts.force === true,
149
- };
150
- },
151
- },
152
- api: {
153
- method: "POST",
154
- mapInput(ctx) {
155
- return ctx.req.json();
156
- },
157
- },
158
- execute: async (params) => {
159
- const body = readBodyObject(params.payload);
160
- const state = this.getOrCreateRuntimeState(params.context);
161
- return await indexMemoryAction(params.context, state, {
162
- force: readOptionalBoolean(body, "force"),
163
- });
164
- },
165
- },
166
134
  search: {
167
135
  command: {
168
136
  description: "检索记忆片段",
@@ -2,26 +2,219 @@
2
2
  * Memory Search 运行时。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 收敛检索默认参数与容错返回。
6
- * - 检索前确保索引同步(dirty 时自动补齐)。
5
+ * - 直接扫描 Markdown 文件,不依赖额外索引库。
6
+ * - 统一收敛检索、分块、打分与状态统计逻辑。
7
7
  */
8
8
 
9
+ import fs from "node:fs/promises";
9
10
  import type { AgentContext } from "@/agent/AgentContextTypes.js";
10
11
  import type {
11
12
  MemorySearchPayload,
12
13
  MemorySearchResponse,
14
+ MemorySearchResultItem,
15
+ MemorySourceStat,
16
+ MemoryStatusResponse,
13
17
  } from "@/service/builtins/memory/types/Memory.js";
14
18
  import {
19
+ listMemorySourceFiles,
15
20
  MEMORY_DEFAULTS,
16
- ensureMemoryIndexed,
17
21
  type MemoryRuntimeState,
18
22
  } from "./Store.js";
19
23
 
24
+ const SNIPPET_MAX_CHARS = 700;
25
+ const CHUNK_MAX_CHARS = 1600;
26
+ const CHUNK_OVERLAP_CHARS = 240;
27
+
20
28
  function clampNumber(value: number, min: number, max: number): number {
21
29
  if (!Number.isFinite(value)) return min;
22
30
  return Math.max(min, Math.min(max, value));
23
31
  }
24
32
 
33
+ function normalizeText(input: string): string {
34
+ return String(input || "").replace(/\r\n/g, "\n");
35
+ }
36
+
37
+ function truncateText(value: string, maxChars: number): string {
38
+ if (value.length <= maxChars) {
39
+ return value;
40
+ }
41
+ return value.slice(0, maxChars);
42
+ }
43
+
44
+ function tokenizeQuery(raw: string): string[] {
45
+ return String(raw || "")
46
+ .toLowerCase()
47
+ .replace(/[^\p{L}\p{N}_-]+/gu, " ")
48
+ .split(/\s+/)
49
+ .map((item) => item.trim())
50
+ .filter(Boolean)
51
+ .slice(0, 12);
52
+ }
53
+
54
+ function countTokenOccurrences(text: string, token: string): number {
55
+ if (!token) return 0;
56
+ let hits = 0;
57
+ let start = 0;
58
+ while (start < text.length) {
59
+ const index = text.indexOf(token, start);
60
+ if (index < 0) break;
61
+ hits += 1;
62
+ start = index + token.length;
63
+ }
64
+ return hits;
65
+ }
66
+
67
+ function buildSnippetScore(text: string, tokens: string[]): number {
68
+ if (tokens.length === 0) return 0;
69
+ const normalized = normalizeText(text).toLowerCase();
70
+ let matchedTokens = 0;
71
+ let totalHits = 0;
72
+ for (const token of tokens) {
73
+ const hits = countTokenOccurrences(normalized, token);
74
+ if (hits > 0) {
75
+ matchedTokens += 1;
76
+ totalHits += Math.min(hits, 4);
77
+ }
78
+ }
79
+ if (matchedTokens === 0) {
80
+ return 0;
81
+ }
82
+ const coverageScore = matchedTokens / tokens.length;
83
+ const densityScore = Math.min(totalHits, tokens.length * 3) / (tokens.length * 3);
84
+ return Number((coverageScore * 0.75 + densityScore * 0.25).toFixed(4));
85
+ }
86
+
87
+ function chunkMarkdown(content: string): Array<{
88
+ startLine: number;
89
+ endLine: number;
90
+ text: string;
91
+ }> {
92
+ const lines = normalizeText(content).split("\n");
93
+ if (lines.length === 0) {
94
+ return [];
95
+ }
96
+ const out: Array<{
97
+ startLine: number;
98
+ endLine: number;
99
+ text: string;
100
+ }> = [];
101
+ let bucket: Array<{ line: string; lineNo: number }> = [];
102
+ let chars = 0;
103
+
104
+ const flush = () => {
105
+ if (bucket.length === 0) return;
106
+ const startLine = bucket[0]?.lineNo ?? 1;
107
+ const endLine = bucket[bucket.length - 1]?.lineNo ?? startLine;
108
+ const text = bucket.map((item) => item.line).join("\n").trim();
109
+ if (!text) return;
110
+ out.push({
111
+ startLine,
112
+ endLine,
113
+ text,
114
+ });
115
+ };
116
+
117
+ const carryOverlap = () => {
118
+ if (bucket.length === 0 || CHUNK_OVERLAP_CHARS <= 0) {
119
+ bucket = [];
120
+ chars = 0;
121
+ return;
122
+ }
123
+ let acc = 0;
124
+ const next: Array<{ line: string; lineNo: number }> = [];
125
+ for (let i = bucket.length - 1; i >= 0; i -= 1) {
126
+ const row = bucket[i];
127
+ if (!row) continue;
128
+ acc += row.line.length + 1;
129
+ next.unshift(row);
130
+ if (acc >= CHUNK_OVERLAP_CHARS) break;
131
+ }
132
+ bucket = next;
133
+ chars = bucket.reduce((sum, item) => sum + item.line.length + 1, 0);
134
+ };
135
+
136
+ for (let i = 0; i < lines.length; i += 1) {
137
+ const line = lines[i] ?? "";
138
+ const rowSize = line.length + 1;
139
+ if (bucket.length > 0 && chars + rowSize > CHUNK_MAX_CHARS) {
140
+ flush();
141
+ carryOverlap();
142
+ }
143
+ bucket.push({ line, lineNo: i + 1 });
144
+ chars += rowSize;
145
+ }
146
+ flush();
147
+ return out;
148
+ }
149
+
150
+ async function readMemoryChunks(context: AgentContext): Promise<Array<{
151
+ path: string;
152
+ source: "longterm" | "daily" | "working";
153
+ startLine: number;
154
+ endLine: number;
155
+ text: string;
156
+ }>> {
157
+ const files = await listMemorySourceFiles(context.rootPath);
158
+ const out: Array<{
159
+ path: string;
160
+ source: "longterm" | "daily" | "working";
161
+ startLine: number;
162
+ endLine: number;
163
+ text: string;
164
+ }> = [];
165
+ for (const file of files) {
166
+ const content = String(await fs.readFile(file.absPath, "utf-8"));
167
+ for (const chunk of chunkMarkdown(content)) {
168
+ out.push({
169
+ path: file.relPath,
170
+ source: file.source,
171
+ startLine: chunk.startLine,
172
+ endLine: chunk.endLine,
173
+ text: chunk.text,
174
+ });
175
+ }
176
+ }
177
+ return out;
178
+ }
179
+
180
+ /**
181
+ * 收集当前 memory Markdown 状态。
182
+ */
183
+ export async function collectMemoryStatus(
184
+ context: AgentContext,
185
+ state: MemoryRuntimeState,
186
+ ): Promise<MemoryStatusResponse> {
187
+ const files = await listMemorySourceFiles(context.rootPath);
188
+ const sourceCounts: MemorySourceStat[] = [
189
+ { source: "longterm", files: 0, chunks: 0 },
190
+ { source: "daily", files: 0, chunks: 0 },
191
+ { source: "working", files: 0, chunks: 0 },
192
+ ];
193
+
194
+ let totalChunks = 0;
195
+ for (const file of files) {
196
+ const bucket = sourceCounts.find((item) => item.source === file.source);
197
+ if (bucket) {
198
+ bucket.files += 1;
199
+ }
200
+ const content = String(await fs.readFile(file.absPath, "utf-8"));
201
+ const chunks = chunkMarkdown(content).length;
202
+ totalChunks += chunks;
203
+ if (bucket) {
204
+ bucket.chunks += chunks;
205
+ }
206
+ }
207
+
208
+ return {
209
+ enabled: state.enabled,
210
+ backend: "builtin",
211
+ mode: "scan",
212
+ files: files.length,
213
+ chunks: totalChunks,
214
+ sourceCounts,
215
+ };
216
+ }
217
+
25
218
  /**
26
219
  * 执行检索。
27
220
  */
@@ -33,7 +226,7 @@ export async function searchMemory(
33
226
  if (!state.enabled) {
34
227
  return {
35
228
  backend: "builtin",
36
- mode: "fts",
229
+ mode: "scan",
37
230
  results: [],
38
231
  disabled: true,
39
232
  error: "memory service disabled",
@@ -45,7 +238,16 @@ export async function searchMemory(
45
238
  if (!query) {
46
239
  return {
47
240
  backend: "builtin",
48
- mode: "fts",
241
+ mode: "scan",
242
+ results: [],
243
+ };
244
+ }
245
+
246
+ const tokens = tokenizeQuery(query);
247
+ if (tokens.length === 0) {
248
+ return {
249
+ backend: "builtin",
250
+ mode: "scan",
49
251
  results: [],
50
252
  };
51
253
  }
@@ -64,27 +266,65 @@ export async function searchMemory(
64
266
  );
65
267
 
66
268
  try {
67
- await ensureMemoryIndexed(context, state, { reason: "search" });
68
- const results = state.indexer.search({
69
- query,
70
- maxResults,
71
- minScore,
72
- maxInjectedChars: MEMORY_DEFAULTS.maxInjectedChars,
73
- });
269
+ const results = (await readMemoryChunks(context))
270
+ .map((chunk) => {
271
+ const score = buildSnippetScore(chunk.text, tokens);
272
+ const citation =
273
+ chunk.startLine === chunk.endLine
274
+ ? `${chunk.path}#L${chunk.startLine}`
275
+ : `${chunk.path}#L${chunk.startLine}-L${chunk.endLine}`;
276
+ return {
277
+ path: chunk.path,
278
+ source: chunk.source,
279
+ startLine: chunk.startLine,
280
+ endLine: chunk.endLine,
281
+ score,
282
+ snippet: truncateText(chunk.text, SNIPPET_MAX_CHARS),
283
+ citation,
284
+ } satisfies MemorySearchResultItem;
285
+ })
286
+ .filter((item) => item.score >= minScore)
287
+ .sort((left, right) => {
288
+ if (right.score !== left.score) {
289
+ return right.score - left.score;
290
+ }
291
+ if (left.path !== right.path) {
292
+ return left.path.localeCompare(right.path);
293
+ }
294
+ return left.startLine - right.startLine;
295
+ })
296
+ .slice(0, maxResults * 3);
297
+
298
+ let remain = Math.max(0, MEMORY_DEFAULTS.maxInjectedChars);
299
+ const clamped: MemorySearchResultItem[] = [];
300
+ for (const item of results) {
301
+ if (remain <= 0 || clamped.length >= maxResults) break;
302
+ if (item.snippet.length <= remain) {
303
+ clamped.push(item);
304
+ remain -= item.snippet.length;
305
+ continue;
306
+ }
307
+ clamped.push({
308
+ ...item,
309
+ snippet: item.snippet.slice(0, remain),
310
+ });
311
+ break;
312
+ }
313
+
74
314
  return {
75
315
  backend: "builtin",
76
- mode: "fts",
77
- results,
316
+ mode: "scan",
317
+ results: clamped,
78
318
  };
79
319
  } catch (error) {
80
320
  const message = String(error);
81
321
  return {
82
322
  backend: "builtin",
83
- mode: "fts",
323
+ mode: "scan",
84
324
  results: [],
85
325
  disabled: true,
86
326
  error: message,
87
- action: "Run `city memory index --force` to rebuild local memory index.",
327
+ action: "Check the Markdown memory files under `.downcity/memory/`.",
88
328
  };
89
329
  }
90
330
  }
@@ -2,32 +2,25 @@
2
2
  * Memory Store(文件与运行态管理)。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 管理 Memory service 的运行时状态(dirty/sync/watcher)。
6
- * - 统一管理 memory 源文件枚举与路径白名单。
7
- * - 不承载检索算法,检索在 Search/Indexer 模块。
5
+ * - 管理 Memory service 的最小运行时状态。
6
+ * - 统一管理 memory 源文件枚举。
7
+ * - 不承载检索算法,检索在 Search 模块。
8
8
  * - 新版本不再使用 module-global state,状态归属 MemoryService 实例。
9
9
  */
10
10
 
11
- import fs from "node:fs";
11
+ import type { Dirent } from "node:fs";
12
12
  import fsp from "node:fs/promises";
13
13
  import path from "node:path";
14
- import type { FSWatcher } from "node:fs";
15
14
  import type { AgentContext } from "@/agent/AgentContextTypes.js";
16
15
  import type {
17
16
  MemoryDefaults,
18
17
  MemorySourceType,
19
18
  } from "@/service/builtins/memory/types/Memory.js";
20
- import {
21
- MemoryIndexer,
22
- type MemoryIndexSyncResult,
23
- } from "./Indexer.js";
24
19
 
25
20
  export const MEMORY_DEFAULTS: MemoryDefaults = {
26
21
  maxResults: 6,
27
22
  minScore: 0.35,
28
23
  maxInjectedChars: 4000,
29
- watchDebounceMs: 1500,
30
- intervalMinutes: 5,
31
24
  };
32
25
 
33
26
  export type MemorySourceFile = {
@@ -54,38 +47,6 @@ export type MemoryRuntimeState = {
54
47
  * Memory 是否启用。
55
48
  */
56
49
  enabled: boolean;
57
- /**
58
- * 索引器实例。
59
- */
60
- indexer: MemoryIndexer;
61
- /**
62
- * 当前是否 dirty。
63
- */
64
- dirty: boolean;
65
- /**
66
- * 最近一次同步时间戳。
67
- */
68
- lastSyncAt?: number;
69
- /**
70
- * 最近一次同步错误。
71
- */
72
- lastError?: string;
73
- /**
74
- * 当前同步中的 Promise(用于并发合并)。
75
- */
76
- syncing: Promise<void> | null;
77
- /**
78
- * watcher 列表。
79
- */
80
- watchers: FSWatcher[];
81
- /**
82
- * debounce timer。
83
- */
84
- watchDebounceTimer: NodeJS.Timeout | null;
85
- /**
86
- * interval timer。
87
- */
88
- intervalTimer: NodeJS.Timeout | null;
89
50
  };
90
51
 
91
52
  function normalizeRelPath(rootPath: string, absPath: string): string {
@@ -107,7 +68,7 @@ async function pathExists(absPath: string): Promise<boolean> {
107
68
 
108
69
  async function listMarkdownFilesRecursively(dirPath: string): Promise<string[]> {
109
70
  const out: string[] = [];
110
- let entries: fs.Dirent[] = [];
71
+ let entries: Dirent[] = [];
111
72
  try {
112
73
  entries = await fsp.readdir(dirPath, { withFileTypes: true });
113
74
  } catch {
@@ -156,7 +117,7 @@ export async function listMemorySourceFiles(
156
117
  }
157
118
 
158
119
  const sessionRootDir = path.join(rootPath, ".downcity", "session");
159
- let sessions: fs.Dirent[] = [];
120
+ let sessions: Dirent[] = [];
160
121
  try {
161
122
  sessions = await fsp.readdir(sessionRootDir, { withFileTypes: true });
162
123
  } catch {
@@ -206,64 +167,15 @@ export function createMemoryRuntimeState(
206
167
  return {
207
168
  rootPath: context.rootPath,
208
169
  enabled: isMemoryEnabled(context),
209
- indexer: new MemoryIndexer(context.rootPath),
210
- dirty: true,
211
- syncing: null,
212
- watchers: [],
213
- watchDebounceTimer: null,
214
- intervalTimer: null,
215
170
  };
216
171
  }
217
172
 
218
173
  /**
219
- * 标记 dirty 并触发 debounce 同步。
220
- */
221
- export function markMemoryDirty(
222
- context: AgentContext,
223
- state: MemoryRuntimeState,
224
- reason: string,
225
- ): void {
226
- state.dirty = true;
227
- if (state.watchDebounceTimer) {
228
- clearTimeout(state.watchDebounceTimer);
229
- }
230
- state.watchDebounceTimer = setTimeout(() => {
231
- state.watchDebounceTimer = null;
232
- void ensureMemoryIndexed(context, state, { reason: `watch:${reason}` });
233
- }, MEMORY_DEFAULTS.watchDebounceMs);
234
- if (typeof state.watchDebounceTimer.unref === "function") {
235
- state.watchDebounceTimer.unref();
236
- }
237
- }
238
-
239
- function registerWatcher(
240
- context: AgentContext,
241
- state: MemoryRuntimeState,
242
- watchPath: string,
243
- ): void {
244
- try {
245
- const watcher = fs.watch(
246
- watchPath,
247
- { recursive: true },
248
- (_event, fileName) => {
249
- const name = String(fileName || "").toLowerCase();
250
- if (!name.endsWith(".md")) {
251
- return;
252
- }
253
- markMemoryDirty(context, state, name || "unknown");
254
- },
255
- );
256
- state.watchers.push(watcher);
257
- } catch (error) {
258
- context.logger.warn("[memory] watcher init skipped", {
259
- watchPath,
260
- error: String(error),
261
- });
262
- }
263
- }
264
-
265
- /**
266
- * 启动 memory 运行时(watcher + interval)。
174
+ * 启动 memory 运行时。
175
+ *
176
+ * 关键点(中文)
177
+ * - Markdown-only 方案下不再维护后台索引同步。
178
+ * - 这里只负责根据配置刷新 enabled 状态。
267
179
  */
268
180
  export async function startMemoryRuntime(
269
181
  context: AgentContext,
@@ -274,97 +186,13 @@ export async function startMemoryRuntime(
274
186
  context.logger.info("[memory] disabled by config");
275
187
  return;
276
188
  }
277
-
278
- if (state.watchers.length === 0) {
279
- registerWatcher(context, state, path.join(context.rootPath, ".downcity"));
280
- }
281
- if (!state.intervalTimer) {
282
- state.intervalTimer = setInterval(() => {
283
- void ensureMemoryIndexed(context, state, { reason: "interval" });
284
- }, MEMORY_DEFAULTS.intervalMinutes * 60 * 1000);
285
- if (typeof state.intervalTimer.unref === "function") {
286
- state.intervalTimer.unref();
287
- }
288
- }
289
-
290
- await ensureMemoryIndexed(context, state, { reason: "startup" });
291
189
  }
292
190
 
293
191
  /**
294
192
  * 停止 memory 运行时。
295
193
  */
296
194
  export async function stopMemoryRuntime(
297
- state: MemoryRuntimeState,
195
+ _state: MemoryRuntimeState,
298
196
  ): Promise<void> {
299
- if (state.watchDebounceTimer) {
300
- clearTimeout(state.watchDebounceTimer);
301
- state.watchDebounceTimer = null;
302
- }
303
- if (state.intervalTimer) {
304
- clearInterval(state.intervalTimer);
305
- state.intervalTimer = null;
306
- }
307
- for (const watcher of state.watchers) {
308
- try {
309
- watcher.close();
310
- } catch {
311
- // ignore
312
- }
313
- }
314
- state.watchers = [];
315
-
316
- if (state.syncing) {
317
- try {
318
- await state.syncing;
319
- } catch {
320
- // ignore
321
- }
322
- }
323
- state.indexer.close();
324
- }
325
-
326
- /**
327
- * 确保索引已同步。
328
- */
329
- export async function ensureMemoryIndexed(
330
- context: AgentContext,
331
- state: MemoryRuntimeState,
332
- params?: { force?: boolean; reason?: string },
333
- ): Promise<MemoryIndexSyncResult | null> {
334
- if (!state.enabled) {
335
- return null;
336
- }
337
- const force = params?.force === true;
338
- if (!force && !state.dirty) {
339
- return null;
340
- }
341
- if (state.syncing) {
342
- await state.syncing;
343
- return null;
344
- }
345
- let syncResult: MemoryIndexSyncResult | null = null;
346
- state.syncing = (async () => {
347
- try {
348
- const files = await listMemorySourceFiles(context.rootPath);
349
- syncResult = await state.indexer.sync(files, { force });
350
- state.dirty = false;
351
- state.lastError = undefined;
352
- state.lastSyncAt = Date.now();
353
- context.logger.info("[memory] index synced", {
354
- reason: params?.reason || "manual",
355
- files: files.length,
356
- });
357
- } catch (error) {
358
- state.lastError = String(error);
359
- context.logger.error("[memory] index sync failed", {
360
- reason: params?.reason || "manual",
361
- error: state.lastError,
362
- });
363
- throw error;
364
- } finally {
365
- state.syncing = null;
366
- }
367
- })();
368
- await state.syncing;
369
- return syncResult;
197
+ void _state;
370
198
  }
@@ -17,7 +17,6 @@ import type {
17
17
  MemoryStoreResponse,
18
18
  } from "@/service/builtins/memory/types/Memory.js";
19
19
  import type { MemoryRuntimeState } from "./Store.js";
20
- import { markMemoryDirty } from "./Store.js";
21
20
 
22
21
  function nowIso(): string {
23
22
  return new Date().toISOString();
@@ -88,6 +87,7 @@ export async function storeMemory(
88
87
  state: MemoryRuntimeState,
89
88
  payload: MemoryStorePayload,
90
89
  ): Promise<MemoryStoreResponse> {
90
+ void state;
91
91
  const target: MemorySourceType = payload.target ?? "daily";
92
92
  const content = String(payload.content || "").trim();
93
93
  if (!content) {
@@ -104,7 +104,6 @@ export async function storeMemory(
104
104
  }
105
105
  const entry = formatEntry(content);
106
106
  await fs.appendFile(resolved.absPath, `\n${entry}`, "utf-8");
107
- markMemoryDirty(context, state, `store:${resolved.relPath}`);
108
107
  return {
109
108
  path: resolved.relPath,
110
109
  target,