@jingyi0605/codingns 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/public/assets/{TerminalPage-4ulgBhv9.js → TerminalPage-BlbQuWi1.js} +1 -1
  2. package/dist/public/assets/gemini-D4G1NbrE.png +0 -0
  3. package/dist/public/assets/index-1VIm8lVL.css +1 -0
  4. package/dist/public/assets/index-Dti93O2S.js +109 -0
  5. package/dist/public/assets/kimi-BWNNSh7e.png +0 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/server/config/env.d.ts +7 -0
  8. package/dist/server/config/env.js +150 -1
  9. package/dist/server/config/env.js.map +1 -1
  10. package/dist/server/config/opencode-system-probe-helper-process.d.ts +24 -0
  11. package/dist/server/config/opencode-system-probe-helper-process.js +70 -5
  12. package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -1
  13. package/dist/server/modules/butler/butler-action-context-service.d.ts +30 -0
  14. package/dist/server/modules/butler/butler-action-context-service.js +108 -0
  15. package/dist/server/modules/butler/butler-action-context-service.js.map +1 -0
  16. package/dist/server/modules/butler/butler-auth-service.d.ts +17 -0
  17. package/dist/server/modules/butler/butler-auth-service.js +91 -0
  18. package/dist/server/modules/butler/butler-auth-service.js.map +1 -0
  19. package/dist/server/modules/butler/butler-control-action-service.d.ts +65 -0
  20. package/dist/server/modules/butler/butler-control-action-service.js +296 -0
  21. package/dist/server/modules/butler/butler-control-action-service.js.map +1 -0
  22. package/dist/server/modules/butler/butler-control-session-service.d.ts +55 -0
  23. package/dist/server/modules/butler/butler-control-session-service.js +367 -0
  24. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -0
  25. package/dist/server/modules/butler/butler-controller.d.ts +367 -0
  26. package/dist/server/modules/butler/butler-controller.js +475 -0
  27. package/dist/server/modules/butler/butler-controller.js.map +1 -0
  28. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.d.ts +34 -0
  29. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js +77 -0
  30. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js.map +1 -0
  31. package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +23 -0
  32. package/dist/server/modules/butler/butler-follow-up-scheduler.js +57 -0
  33. package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -0
  34. package/dist/server/modules/butler/butler-follow-up-service.d.ts +86 -0
  35. package/dist/server/modules/butler/butler-follow-up-service.js +948 -0
  36. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -0
  37. package/dist/server/modules/butler/butler-inbox-service.d.ts +35 -0
  38. package/dist/server/modules/butler/butler-inbox-service.js +136 -0
  39. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -0
  40. package/dist/server/modules/butler/butler-notification-service.d.ts +12 -0
  41. package/dist/server/modules/butler/butler-notification-service.js +45 -0
  42. package/dist/server/modules/butler/butler-notification-service.js.map +1 -0
  43. package/dist/server/modules/butler/butler-profile-service.d.ts +26 -0
  44. package/dist/server/modules/butler/butler-profile-service.js +529 -0
  45. package/dist/server/modules/butler/butler-profile-service.js.map +1 -0
  46. package/dist/server/modules/butler/butler-project-service.d.ts +48 -0
  47. package/dist/server/modules/butler/butler-project-service.js +253 -0
  48. package/dist/server/modules/butler/butler-project-service.js.map +1 -0
  49. package/dist/server/modules/butler/butler-session-service.d.ts +79 -0
  50. package/dist/server/modules/butler/butler-session-service.js +503 -0
  51. package/dist/server/modules/butler/butler-session-service.js.map +1 -0
  52. package/dist/server/modules/butler/butler-session-summary-service.d.ts +55 -0
  53. package/dist/server/modules/butler/butler-session-summary-service.js +382 -0
  54. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -0
  55. package/dist/server/modules/butler/context-aggregator.d.ts +187 -0
  56. package/dist/server/modules/butler/context-aggregator.js +807 -0
  57. package/dist/server/modules/butler/context-aggregator.js.map +1 -0
  58. package/dist/server/modules/butler/instruction-adapter.d.ts +28 -0
  59. package/dist/server/modules/butler/instruction-adapter.js +101 -0
  60. package/dist/server/modules/butler/instruction-adapter.js.map +1 -0
  61. package/dist/server/modules/butler/patrol-execution-service.d.ts +47 -0
  62. package/dist/server/modules/butler/patrol-execution-service.js +347 -0
  63. package/dist/server/modules/butler/patrol-execution-service.js.map +1 -0
  64. package/dist/server/modules/butler/patrol-plan-service.d.ts +54 -0
  65. package/dist/server/modules/butler/patrol-plan-service.js +272 -0
  66. package/dist/server/modules/butler/patrol-plan-service.js.map +1 -0
  67. package/dist/server/modules/butler/patrol-run-service.d.ts +60 -0
  68. package/dist/server/modules/butler/patrol-run-service.js +185 -0
  69. package/dist/server/modules/butler/patrol-run-service.js.map +1 -0
  70. package/dist/server/modules/butler/patrol-scheduler.d.ts +36 -0
  71. package/dist/server/modules/butler/patrol-scheduler.js +99 -0
  72. package/dist/server/modules/butler/patrol-scheduler.js.map +1 -0
  73. package/dist/server/modules/butler/project-memory-service.d.ts +30 -0
  74. package/dist/server/modules/butler/project-memory-service.js +103 -0
  75. package/dist/server/modules/butler/project-memory-service.js.map +1 -0
  76. package/dist/server/modules/butler/provider-adapter-registry.d.ts +61 -0
  77. package/dist/server/modules/butler/provider-adapter-registry.js +430 -0
  78. package/dist/server/modules/butler/provider-adapter-registry.js.map +1 -0
  79. package/dist/server/modules/butler/session-summary-instruction-adapter.d.ts +28 -0
  80. package/dist/server/modules/butler/session-summary-instruction-adapter.js +79 -0
  81. package/dist/server/modules/butler/session-summary-instruction-adapter.js.map +1 -0
  82. package/dist/server/modules/butler/session-summary-scheduler.d.ts +23 -0
  83. package/dist/server/modules/butler/session-summary-scheduler.js +57 -0
  84. package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -0
  85. package/dist/server/modules/butler/verification-run-service.d.ts +73 -0
  86. package/dist/server/modules/butler/verification-run-service.js +633 -0
  87. package/dist/server/modules/butler/verification-run-service.js.map +1 -0
  88. package/dist/server/modules/file/file-controller.d.ts +12 -1
  89. package/dist/server/modules/file/file-controller.js +72 -1
  90. package/dist/server/modules/file/file-controller.js.map +1 -1
  91. package/dist/server/modules/file/file-preview-link-service.d.ts +22 -0
  92. package/dist/server/modules/file/file-preview-link-service.js +160 -0
  93. package/dist/server/modules/file/file-preview-link-service.js.map +1 -0
  94. package/dist/server/modules/preferences/profile-service.js +8 -2
  95. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  96. package/dist/server/modules/sessions/claude-runtime-helper-process.js +1 -1
  97. package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -1
  98. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +7 -2
  99. package/dist/server/modules/sessions/codex-app-server-helper-client.js +113 -2
  100. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  101. package/dist/server/modules/sessions/codex-app-server-helper-process.js +106 -1
  102. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  103. package/dist/server/modules/sessions/session-controller.d.ts +24 -1
  104. package/dist/server/modules/sessions/session-controller.js +34 -3
  105. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  106. package/dist/server/modules/sessions/session-history-service.d.ts +47 -2
  107. package/dist/server/modules/sessions/session-history-service.js +881 -56
  108. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  109. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +30 -2
  110. package/dist/server/modules/sessions/session-live-runtime-service.js +584 -159
  111. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  112. package/dist/server/modules/sessions/session-provider-error-mapper.js +94 -0
  113. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  114. package/dist/server/modules/workbench/workbench-service.d.ts +7 -1
  115. package/dist/server/modules/workbench/workbench-service.js +31 -7
  116. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  117. package/dist/server/routes/butler.d.ts +3 -0
  118. package/dist/server/routes/butler.js +54 -0
  119. package/dist/server/routes/butler.js.map +1 -0
  120. package/dist/server/routes/files.js +2 -0
  121. package/dist/server/routes/files.js.map +1 -1
  122. package/dist/server/routes/sessions.js +1 -0
  123. package/dist/server/routes/sessions.js.map +1 -1
  124. package/dist/server/server/create-server.d.ts +65 -0
  125. package/dist/server/server/create-server.js +154 -5
  126. package/dist/server/server/create-server.js.map +1 -1
  127. package/dist/server/shared/utils/command-availability.d.ts +1 -0
  128. package/dist/server/shared/utils/command-availability.js +83 -0
  129. package/dist/server/shared/utils/command-availability.js.map +1 -0
  130. package/dist/server/storage/repositories/butler-control-event-repository.d.ts +8 -0
  131. package/dist/server/storage/repositories/butler-control-event-repository.js +78 -0
  132. package/dist/server/storage/repositories/butler-control-event-repository.js.map +1 -0
  133. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +11 -0
  134. package/dist/server/storage/repositories/butler-control-session-repository.js +86 -0
  135. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -0
  136. package/dist/server/storage/repositories/butler-follow-up-task-repository.d.ts +16 -0
  137. package/dist/server/storage/repositories/butler-follow-up-task-repository.js +252 -0
  138. package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -0
  139. package/dist/server/storage/repositories/butler-inbox-item-repository.d.ts +15 -0
  140. package/dist/server/storage/repositories/butler-inbox-item-repository.js +111 -0
  141. package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -0
  142. package/dist/server/storage/repositories/butler-notification-archive-repository.d.ts +9 -0
  143. package/dist/server/storage/repositories/butler-notification-archive-repository.js +48 -0
  144. package/dist/server/storage/repositories/butler-notification-archive-repository.js.map +1 -0
  145. package/dist/server/storage/repositories/butler-profile-repository.d.ts +9 -0
  146. package/dist/server/storage/repositories/butler-profile-repository.js +86 -0
  147. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -0
  148. package/dist/server/storage/repositories/butler-project-repository.d.ts +14 -0
  149. package/dist/server/storage/repositories/butler-project-repository.js +140 -0
  150. package/dist/server/storage/repositories/butler-project-repository.js.map +1 -0
  151. package/dist/server/storage/repositories/butler-session-repository.d.ts +11 -0
  152. package/dist/server/storage/repositories/butler-session-repository.js +106 -0
  153. package/dist/server/storage/repositories/butler-session-repository.js.map +1 -0
  154. package/dist/server/storage/repositories/butler-session-summary-state-repository.d.ts +8 -0
  155. package/dist/server/storage/repositories/butler-session-summary-state-repository.js +62 -0
  156. package/dist/server/storage/repositories/butler-session-summary-state-repository.js.map +1 -0
  157. package/dist/server/storage/repositories/patrol-plan-repository.d.ts +27 -0
  158. package/dist/server/storage/repositories/patrol-plan-repository.js +119 -0
  159. package/dist/server/storage/repositories/patrol-plan-repository.js.map +1 -0
  160. package/dist/server/storage/repositories/patrol-run-repository.d.ts +28 -0
  161. package/dist/server/storage/repositories/patrol-run-repository.js +121 -0
  162. package/dist/server/storage/repositories/patrol-run-repository.js.map +1 -0
  163. package/dist/server/storage/repositories/project-memory-repository.d.ts +15 -0
  164. package/dist/server/storage/repositories/project-memory-repository.js +150 -0
  165. package/dist/server/storage/repositories/project-memory-repository.js.map +1 -0
  166. package/dist/server/storage/repositories/session-checkpoint-repository.d.ts +9 -0
  167. package/dist/server/storage/repositories/session-checkpoint-repository.js +72 -0
  168. package/dist/server/storage/repositories/session-checkpoint-repository.js.map +1 -0
  169. package/dist/server/storage/repositories/session-fork-repository.d.ts +8 -0
  170. package/dist/server/storage/repositories/session-fork-repository.js +69 -0
  171. package/dist/server/storage/repositories/session-fork-repository.js.map +1 -0
  172. package/dist/server/storage/repositories/session-index-repository.js +40 -2
  173. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  174. package/dist/server/storage/repositories/session-message-origin-repository.d.ts +10 -0
  175. package/dist/server/storage/repositories/session-message-origin-repository.js +93 -0
  176. package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -0
  177. package/dist/server/storage/repositories/verification-run-repository.d.ts +29 -0
  178. package/dist/server/storage/repositories/verification-run-repository.js +125 -0
  179. package/dist/server/storage/repositories/verification-run-repository.js.map +1 -0
  180. package/dist/server/storage/sqlite/client.js +146 -0
  181. package/dist/server/storage/sqlite/client.js.map +1 -1
  182. package/dist/server/storage/sqlite/schema.sql +354 -0
  183. package/dist/server/types/domain.d.ts +286 -2
  184. package/dist/server/ws/ws-server.d.ts +2 -1
  185. package/dist/server/ws/ws-server.js +2 -1
  186. package/dist/server/ws/ws-server.js.map +1 -1
  187. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +4 -0
  188. package/node_modules/@codingns/session-sync-core/dist/index.js +4 -0
  189. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  190. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +18 -0
  191. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +659 -0
  192. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +1 -0
  193. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +11 -0
  194. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +72 -0
  195. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +1 -0
  196. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +8 -0
  197. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +89 -0
  198. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
  199. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +6 -1
  200. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +228 -7
  201. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  202. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +26 -1
  203. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +499 -3
  204. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  205. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +41 -0
  206. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +1175 -0
  207. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -0
  208. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +29 -0
  209. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +578 -0
  210. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -0
  211. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +5 -1
  212. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +271 -4
  213. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  214. package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
  215. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +147 -19
  216. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
  217. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +2 -0
  218. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +43 -5
  219. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  220. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +12 -0
  221. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +442 -71
  222. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  223. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +21 -0
  224. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +537 -0
  225. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -0
  226. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +38 -0
  227. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +911 -0
  228. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +1 -0
  229. package/node_modules/@codingns/session-sync-core/dist/services.d.ts +2 -1
  230. package/node_modules/@codingns/session-sync-core/dist/services.js +55 -8
  231. package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
  232. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +6 -0
  233. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +9 -0
  234. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -0
  235. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +27 -0
  236. package/node_modules/@codingns/session-sync-core/package.json +8 -0
  237. package/package.json +1 -1
  238. package/dist/public/assets/index-C5lu52cQ.css +0 -1
  239. package/dist/public/assets/index-WpdUo_Vs.js +0 -108
@@ -0,0 +1,807 @@
1
+ import path from "node:path";
2
+ import { hashContent } from "../../shared/utils/hash.js";
3
+ import { nowIso } from "../../shared/utils/time.js";
4
+ const MAX_PROJECT_RISKS = 5;
5
+ const MAX_PROJECT_ACTIONS = 5;
6
+ const MAX_GLOBAL_ITEMS = 8;
7
+ const MAX_OVERVIEW_PROJECTS = 5;
8
+ const MAX_OVERVIEW_SESSIONS = 5;
9
+ const MAX_OVERVIEW_RUNS = 5;
10
+ const MAX_PROMPT_PROJECTS = 3;
11
+ const MAX_PROMPT_ITEMS = 3;
12
+ export class ButlerContextAggregator {
13
+ butlerProfileService;
14
+ butlerProjectService;
15
+ butlerSessionService;
16
+ butlerInboxService;
17
+ projectMemoryService;
18
+ patrolRunService;
19
+ verificationRunService;
20
+ sessionCheckpointRepository;
21
+ constructor(butlerProfileService, butlerProjectService, butlerSessionService, butlerInboxService, projectMemoryService, patrolRunService, verificationRunService, sessionCheckpointRepository) {
22
+ this.butlerProfileService = butlerProfileService;
23
+ this.butlerProjectService = butlerProjectService;
24
+ this.butlerSessionService = butlerSessionService;
25
+ this.butlerInboxService = butlerInboxService;
26
+ this.projectMemoryService = projectMemoryService;
27
+ this.patrolRunService = patrolRunService;
28
+ this.verificationRunService = verificationRunService;
29
+ this.sessionCheckpointRepository = sessionCheckpointRepository;
30
+ }
31
+ async getOverview(userId) {
32
+ const snapshot = await this.getSnapshot(userId);
33
+ return {
34
+ version: snapshot.version,
35
+ generatedAt: snapshot.generatedAt,
36
+ global: snapshot.global,
37
+ projects: snapshot.projects.slice(0, MAX_OVERVIEW_PROJECTS),
38
+ sessions: snapshot.sessions.slice(0, MAX_OVERVIEW_SESSIONS),
39
+ inboxItems: snapshot.inboxItems.slice(0, MAX_OVERVIEW_SESSIONS),
40
+ patrols: snapshot.patrols.slice(0, MAX_OVERVIEW_RUNS),
41
+ verifications: snapshot.verifications.slice(0, MAX_OVERVIEW_RUNS)
42
+ };
43
+ }
44
+ async getSnapshot(userId) {
45
+ const generatedAt = nowIso();
46
+ const projectContexts = await this.collectProjectContexts(userId);
47
+ const projects = projectContexts.map((item) => item.digest);
48
+ const sessions = projectContexts.flatMap((item) => item.sessions);
49
+ const memories = projectContexts.flatMap((item) => item.memories);
50
+ const inboxItems = projectContexts.flatMap((item) => item.inboxItems);
51
+ const patrols = projectContexts.flatMap((item) => item.patrols);
52
+ const verifications = projectContexts.flatMap((item) => item.verifications);
53
+ const global = buildGlobalDigest(projectContexts);
54
+ const version = buildSnapshotVersion({
55
+ global,
56
+ projects,
57
+ sessions,
58
+ memories,
59
+ inboxItems,
60
+ patrols,
61
+ verifications
62
+ });
63
+ return {
64
+ version,
65
+ generatedAt,
66
+ global,
67
+ projects,
68
+ sessions,
69
+ memories,
70
+ inboxItems,
71
+ patrols,
72
+ verifications
73
+ };
74
+ }
75
+ async getProjectContext(projectId, userId) {
76
+ const project = this.getProjectOrThrow(projectId);
77
+ const generatedAt = nowIso();
78
+ await this.butlerSessionService.ensureProjectSessionsSynced(project.id, userId);
79
+ const context = this.buildProjectContext(project, userId);
80
+ const version = buildSnapshotVersion({
81
+ project: context.digest,
82
+ sessions: context.sessions,
83
+ memories: context.memories,
84
+ inboxItems: context.inboxItems,
85
+ patrols: context.patrols,
86
+ verifications: context.verifications
87
+ });
88
+ return {
89
+ version,
90
+ generatedAt,
91
+ project: context.digest,
92
+ sessions: context.sessions,
93
+ memories: context.memories,
94
+ inboxItems: context.inboxItems,
95
+ patrols: context.patrols,
96
+ verifications: context.verifications,
97
+ topRisks: context.digest.topRisks,
98
+ nextActions: context.digest.nextActions
99
+ };
100
+ }
101
+ async resolvePromptContext(userId, userMessage) {
102
+ const projectId = this.resolveProjectIdFromMessage(userMessage);
103
+ if (projectId) {
104
+ const context = await this.getProjectContext(projectId, userId);
105
+ const searchResult = await this.searchSummaries(userId, userMessage ?? "", {
106
+ projectId
107
+ });
108
+ return {
109
+ version: context.version,
110
+ generatedAt: context.generatedAt,
111
+ scope: "project",
112
+ projectId,
113
+ prompt: renderProjectPrompt(context, searchResult)
114
+ };
115
+ }
116
+ const overview = await this.getOverview(userId);
117
+ const searchResult = await this.searchSummaries(userId, userMessage ?? "");
118
+ return {
119
+ version: overview.version,
120
+ generatedAt: overview.generatedAt,
121
+ scope: "global",
122
+ projectId: null,
123
+ prompt: renderOverviewPrompt(overview, searchResult)
124
+ };
125
+ }
126
+ async searchSummaries(userId, query, options = {}) {
127
+ const normalizedQuery = query.trim();
128
+ const generatedAt = nowIso();
129
+ if (!normalizedQuery) {
130
+ return {
131
+ version: buildSnapshotVersion({
132
+ query: "",
133
+ items: []
134
+ }),
135
+ generatedAt,
136
+ query: "",
137
+ items: []
138
+ };
139
+ }
140
+ const projectContexts = await this.collectProjectContexts(userId, {
141
+ includeArchived: options.includeArchived ?? false
142
+ });
143
+ const filteredContexts = options.projectId
144
+ ? projectContexts.filter((context) => context.project.id === options.projectId)
145
+ : projectContexts;
146
+ const items = filteredContexts
147
+ .flatMap((context) => buildSearchHits(context, normalizedQuery))
148
+ .sort(compareSearchHits)
149
+ .slice(0, MAX_OVERVIEW_SESSIONS);
150
+ return {
151
+ version: buildSnapshotVersion({
152
+ query: normalizedQuery,
153
+ items
154
+ }),
155
+ generatedAt,
156
+ query: normalizedQuery,
157
+ items
158
+ };
159
+ }
160
+ async collectProjectContexts(userId, options) {
161
+ const focusProjectIds = new Set(this.butlerProfileService.getProfile()?.focus.projectIds ?? []);
162
+ const projects = this.butlerProjectService.list();
163
+ await Promise.all(projects.map((project) => this.butlerSessionService.ensureProjectSessionsSynced(project.id, userId, {
164
+ includeArchived: options?.includeArchived ?? false
165
+ })));
166
+ const contexts = projects.map((project) => this.buildProjectContext(project, userId, {
167
+ includeArchived: options?.includeArchived ?? false
168
+ }));
169
+ return contexts.sort((left, right) => compareProjectContexts(left, right, focusProjectIds));
170
+ }
171
+ buildProjectContext(project, userId, options) {
172
+ const sessions = this.butlerSessionService
173
+ .listByProject(project.id, userId, {
174
+ includeArchived: options?.includeArchived ?? false
175
+ })
176
+ .map((session) => {
177
+ const checkpoint = this.sessionCheckpointRepository.listByButlerSessionId(session.id, 1)[0] ?? null;
178
+ return {
179
+ id: session.id,
180
+ projectId: session.projectId,
181
+ sessionId: session.sessionId,
182
+ provider: session.provider,
183
+ title: session.title,
184
+ isArchived: session.isArchived,
185
+ role: session.role,
186
+ ownershipMode: session.ownershipMode,
187
+ status: session.status,
188
+ runningState: session.runningState,
189
+ lastSummary: session.lastSummary,
190
+ lastCheckpointAt: session.lastCheckpointAt,
191
+ progressState: checkpoint?.progressState ?? "unknown",
192
+ riskFlags: checkpoint?.riskFlags ?? [],
193
+ nextActions: checkpoint?.nextActions ?? [],
194
+ updatedAt: session.updatedAt,
195
+ createdAt: session.createdAt
196
+ };
197
+ })
198
+ .sort((left, right) => compareIso(right.updatedAt, left.updatedAt));
199
+ const memories = this.projectMemoryService
200
+ .listMemories(project.id)
201
+ .map((memory) => ({
202
+ id: memory.id,
203
+ projectId: memory.projectId,
204
+ title: memory.title,
205
+ memoryType: memory.memoryType,
206
+ status: memory.status,
207
+ scopePath: memory.scopePath,
208
+ tags: memory.tags,
209
+ confidence: memory.confidence,
210
+ updatedAt: memory.updatedAt,
211
+ createdAt: memory.createdAt
212
+ }))
213
+ .sort((left, right) => compareIso(right.updatedAt, left.updatedAt));
214
+ const inboxItems = this.butlerInboxService
215
+ .listItems({
216
+ projectId: project.id
217
+ })
218
+ .map((item) => mapInboxItem(item))
219
+ .sort((left, right) => compareInboxItems(left, right));
220
+ const patrols = this.patrolRunService
221
+ .listRuns(project.id)
222
+ .map((run) => mapPatrolRun(run))
223
+ .sort((left, right) => compareIso(right.createdAt, left.createdAt));
224
+ const verifications = this.verificationRunService
225
+ .listRuns(project.id)
226
+ .map((run) => mapVerificationRun(run))
227
+ .sort((left, right) => compareIso(right.createdAt, left.createdAt));
228
+ const topRisks = buildProjectTopRisks(project, sessions, patrols, verifications);
229
+ const nextActions = buildProjectNextActions(project, sessions, patrols, verifications);
230
+ const lastActivityAt = [
231
+ project.updatedAt,
232
+ sessions[0]?.updatedAt,
233
+ memories[0]?.updatedAt,
234
+ inboxItems[0]?.updatedAt,
235
+ patrols[0]?.finishedAt ?? patrols[0]?.startedAt ?? patrols[0]?.createdAt,
236
+ verifications[0]?.finishedAt ?? verifications[0]?.startedAt ?? verifications[0]?.createdAt
237
+ ].filter((value) => Boolean(value)).sort(compareIsoDesc)[0] ?? project.updatedAt;
238
+ return {
239
+ project,
240
+ sessions,
241
+ memories,
242
+ inboxItems,
243
+ patrols,
244
+ verifications,
245
+ digest: {
246
+ id: project.id,
247
+ workspaceId: project.workspaceId,
248
+ name: project.name,
249
+ repoRoot: project.repoRoot,
250
+ lifecycleStatus: project.lifecycleStatus,
251
+ riskLevel: project.riskLevel,
252
+ activeSessionCount: sessions.filter((item) => item.status === "running").length,
253
+ sessionCount: sessions.length,
254
+ memoryCount: memories.length,
255
+ failedPatrolCount: patrols.filter((item) => item.status === "failed").length,
256
+ failedVerificationCount: verifications.filter((item) => item.status === "failed").length,
257
+ latestSessionSummary: sessions[0]?.lastSummary ?? null,
258
+ latestPatrolSummary: patrols[0]?.summary ?? null,
259
+ latestVerificationSummary: verifications[0]?.summary ?? null,
260
+ topRisks,
261
+ nextActions,
262
+ lastActivityAt,
263
+ updatedAt: project.updatedAt
264
+ }
265
+ };
266
+ }
267
+ resolveProjectIdFromMessage(userMessage) {
268
+ const normalized = userMessage?.trim().toLocaleLowerCase();
269
+ if (!normalized) {
270
+ return null;
271
+ }
272
+ const projects = this.butlerProjectService.list();
273
+ const focusedProjectIds = this.butlerProfileService.getProfile()?.focus.projectIds ?? [];
274
+ if (focusedProjectIds.length === 1 && /(这个项目|当前项目|该项目)/u.test(normalized)) {
275
+ const focusedProjectId = focusedProjectIds[0];
276
+ if (projects.some((project) => project.id === focusedProjectId)) {
277
+ return focusedProjectId;
278
+ }
279
+ }
280
+ for (const project of projects) {
281
+ const candidates = [
282
+ project.id,
283
+ project.name,
284
+ path.basename(project.repoRoot)
285
+ ]
286
+ .map((item) => item.trim().toLocaleLowerCase())
287
+ .filter((item) => item.length >= 2);
288
+ if (candidates.some((candidate) => normalized.includes(candidate))) {
289
+ return project.id;
290
+ }
291
+ }
292
+ return null;
293
+ }
294
+ getProjectOrThrow(projectId) {
295
+ return this.butlerProjectService.getById(projectId);
296
+ }
297
+ }
298
+ function mapPatrolRun(run) {
299
+ return {
300
+ id: run.id,
301
+ projectId: run.projectId,
302
+ planId: run.planId,
303
+ triggeredBy: run.triggeredBy,
304
+ status: run.status,
305
+ riskLevel: run.riskLevel,
306
+ summary: run.summary,
307
+ suggestions: run.suggestions,
308
+ startedAt: run.startedAt,
309
+ finishedAt: run.finishedAt,
310
+ createdAt: run.createdAt
311
+ };
312
+ }
313
+ function mapInboxItem(item) {
314
+ return {
315
+ id: item.id,
316
+ projectId: item.projectId,
317
+ workspaceId: item.workspaceId,
318
+ projectName: item.projectName,
319
+ itemType: item.itemType,
320
+ title: item.title,
321
+ content: item.content,
322
+ priority: item.priority,
323
+ status: item.status,
324
+ updatedAt: item.updatedAt,
325
+ createdAt: item.createdAt,
326
+ closedAt: item.closedAt
327
+ };
328
+ }
329
+ function mapVerificationRun(run) {
330
+ return {
331
+ id: run.id,
332
+ projectId: run.projectId,
333
+ verificationType: run.verificationType,
334
+ status: run.status,
335
+ targetRef: run.targetRef,
336
+ summary: run.summary,
337
+ startedAt: run.startedAt,
338
+ finishedAt: run.finishedAt,
339
+ createdAt: run.createdAt
340
+ };
341
+ }
342
+ function compareInboxItems(left, right) {
343
+ const statusPriority = getInboxStatusPriority(left.status) - getInboxStatusPriority(right.status);
344
+ if (statusPriority !== 0) {
345
+ return statusPriority;
346
+ }
347
+ const priorityOrder = getInboxPriorityOrder(left.priority) - getInboxPriorityOrder(right.priority);
348
+ if (priorityOrder !== 0) {
349
+ return priorityOrder;
350
+ }
351
+ return compareIso(right.updatedAt, left.updatedAt);
352
+ }
353
+ function getInboxStatusPriority(status) {
354
+ switch (status) {
355
+ case "in_progress":
356
+ return 0;
357
+ case "pending":
358
+ return 1;
359
+ case "closed":
360
+ return 2;
361
+ default:
362
+ return 3;
363
+ }
364
+ }
365
+ function getInboxPriorityOrder(priority) {
366
+ switch (priority) {
367
+ case "high":
368
+ return 0;
369
+ case "medium":
370
+ return 1;
371
+ case "low":
372
+ return 2;
373
+ default:
374
+ return 3;
375
+ }
376
+ }
377
+ function compareProjectContexts(left, right, focusedProjectIds) {
378
+ const focusedDiff = Number(focusedProjectIds.has(right.project.id)) - Number(focusedProjectIds.has(left.project.id));
379
+ if (focusedDiff !== 0) {
380
+ return focusedDiff;
381
+ }
382
+ const riskDiff = riskWeight(right.project.riskLevel) - riskWeight(left.project.riskLevel);
383
+ if (riskDiff !== 0) {
384
+ return riskDiff;
385
+ }
386
+ const blockerDiff = Number(hasProjectBlocker(right)) - Number(hasProjectBlocker(left));
387
+ if (blockerDiff !== 0) {
388
+ return blockerDiff;
389
+ }
390
+ return compareIso(right.digest.lastActivityAt, left.digest.lastActivityAt);
391
+ }
392
+ function riskWeight(riskLevel) {
393
+ switch (riskLevel) {
394
+ case "high":
395
+ return 3;
396
+ case "medium":
397
+ return 2;
398
+ default:
399
+ return 1;
400
+ }
401
+ }
402
+ function buildProjectTopRisks(project, sessions, patrols, verifications) {
403
+ const risks = [];
404
+ if (project.lifecycleStatus === "paused") {
405
+ risks.push("项目当前处于暂停状态,新的推进动作可能被搁置。");
406
+ }
407
+ if (project.riskLevel === "high") {
408
+ risks.push("项目已被标记为高风险,需要优先处理。");
409
+ }
410
+ for (const session of sessions) {
411
+ if (session.progressState === "blocked") {
412
+ risks.push(session.title
413
+ ? `会话《${session.title}》当前阻塞:${joinItems(session.riskFlags, "未提供阻塞细节")}`
414
+ : `项目会话当前阻塞:${joinItems(session.riskFlags, "未提供阻塞细节")}`);
415
+ }
416
+ else if (session.status === "failed") {
417
+ risks.push(session.title
418
+ ? `会话《${session.title}》执行失败,需要人工确认。`
419
+ : "项目存在失败会话,需要人工确认。");
420
+ }
421
+ }
422
+ for (const patrol of patrols) {
423
+ if (patrol.status === "failed") {
424
+ risks.push(`最近巡视失败:${truncateText(patrol.summary, 80, "巡视失败,尚无摘要")}`);
425
+ }
426
+ }
427
+ for (const verification of verifications) {
428
+ if (verification.status === "failed") {
429
+ risks.push(`最近验证失败:${truncateText(verification.summary, 80, "验证失败,尚无摘要")}`);
430
+ }
431
+ }
432
+ return uniqueItems(risks, MAX_PROJECT_RISKS);
433
+ }
434
+ function buildProjectNextActions(project, sessions, patrols, verifications) {
435
+ const actions = [];
436
+ for (const session of sessions) {
437
+ actions.push(...session.nextActions);
438
+ }
439
+ for (const patrol of patrols) {
440
+ if (patrol.status === "failed") {
441
+ actions.push(...patrol.suggestions);
442
+ actions.push("复查最近一次失败巡视,确认是否需要重新发起。");
443
+ }
444
+ }
445
+ for (const verification of verifications) {
446
+ if (verification.status === "failed") {
447
+ actions.push(verification.targetRef
448
+ ? `优先复查目标 ${verification.targetRef} 的失败验证。`
449
+ : "优先复查最近一次失败验证。");
450
+ }
451
+ }
452
+ if (sessions.length === 0) {
453
+ actions.push("当前项目还没有纳管会话,优先导入或启动一个项目会话。");
454
+ }
455
+ if (patrols.length === 0) {
456
+ actions.push("当前项目还没有巡视记录,考虑先发起一次巡视。");
457
+ }
458
+ if (verifications.length === 0) {
459
+ actions.push("当前项目还没有验证记录,考虑补一轮基础验证。");
460
+ }
461
+ if (project.lifecycleStatus === "paused") {
462
+ actions.push("先确认项目是否应该恢复为 active,再继续执行动作。");
463
+ }
464
+ return uniqueItems(actions, MAX_PROJECT_ACTIONS);
465
+ }
466
+ function buildGlobalDigest(projectContexts) {
467
+ return {
468
+ projectCount: projectContexts.length,
469
+ activeProjectCount: projectContexts.filter((item) => item.project.lifecycleStatus === "active").length,
470
+ blockedProjectCount: projectContexts.filter((item) => hasProjectBlocker(item)).length,
471
+ highRiskProjectCount: projectContexts.filter((item) => item.project.riskLevel === "high").length,
472
+ topRisks: uniqueItems(projectContexts.flatMap((item) => item.digest.topRisks), MAX_GLOBAL_ITEMS),
473
+ nextActions: uniqueItems(projectContexts.flatMap((item) => item.digest.nextActions), MAX_GLOBAL_ITEMS)
474
+ };
475
+ }
476
+ function hasProjectBlocker(projectContext) {
477
+ return (projectContext.project.lifecycleStatus === "paused"
478
+ || projectContext.sessions.some((item) => item.status === "blocked" || item.status === "failed" || item.progressState === "blocked")
479
+ || projectContext.patrols.some((item) => item.status === "failed")
480
+ || projectContext.verifications.some((item) => item.status === "failed"));
481
+ }
482
+ function renderOverviewPrompt(overview, searchResult) {
483
+ const lines = [
484
+ "# 代码助手当前上下文",
485
+ "",
486
+ `- 作用域:全局总览`,
487
+ `- 上下文版本:${overview.version}`,
488
+ `- 生成时间:${overview.generatedAt}`,
489
+ "- 使用规则:先基于下面摘要回答;如果仍然缺信息,明确指出缺口并要求宿主系统下钻,不要编造。",
490
+ "",
491
+ "## 全局摘要",
492
+ `- 项目总数:${overview.global.projectCount}`,
493
+ `- 活跃项目:${overview.global.activeProjectCount}`,
494
+ `- 有阻塞或失败信号的项目:${overview.global.blockedProjectCount}`,
495
+ `- 高风险项目:${overview.global.highRiskProjectCount}`,
496
+ `- 最高优先风险:${joinItems(overview.global.topRisks, "暂无明显风险")}`,
497
+ `- 建议优先动作:${joinItems(overview.global.nextActions, "暂无待办动作")}`,
498
+ "",
499
+ "## 优先项目"
500
+ ];
501
+ if (overview.projects.length === 0) {
502
+ lines.push("- 当前还没有纳管项目。");
503
+ }
504
+ else {
505
+ for (const project of overview.projects.slice(0, MAX_PROMPT_PROJECTS)) {
506
+ lines.push(`- ${project.name}(${project.id}):风险=${project.riskLevel},活跃会话=${project.activeSessionCount},主要风险=${joinItems(project.topRisks, "暂无")},下一步=${joinItems(project.nextActions, "暂无")}`);
507
+ }
508
+ }
509
+ if (overview.sessions.length > 0) {
510
+ lines.push("", "## 最近会话");
511
+ for (const session of overview.sessions.slice(0, MAX_PROMPT_ITEMS)) {
512
+ lines.push(`- ${session.title ?? session.sessionId}:状态=${session.status}/${session.progressState},摘要=${truncateText(session.lastSummary, 80, "暂无摘要")}`);
513
+ }
514
+ }
515
+ if (overview.inboxItems.length > 0) {
516
+ lines.push("", "## 收件箱");
517
+ for (const item of overview.inboxItems.slice(0, MAX_PROMPT_ITEMS)) {
518
+ lines.push(`- ${describeInboxItem(item)}`);
519
+ }
520
+ }
521
+ if (overview.verifications.length > 0) {
522
+ lines.push("", "## 最近验证");
523
+ for (const verification of overview.verifications.slice(0, MAX_PROMPT_ITEMS)) {
524
+ lines.push(`- ${verification.verificationType}:状态=${verification.status},摘要=${truncateText(verification.summary, 80, "暂无摘要")}`);
525
+ }
526
+ }
527
+ if (overview.patrols.length > 0) {
528
+ lines.push("", "## 最近巡视");
529
+ for (const patrol of overview.patrols.slice(0, MAX_PROMPT_ITEMS)) {
530
+ lines.push(`- ${patrol.id}:状态=${patrol.status},风险=${patrol.riskLevel ?? "unknown"},摘要=${truncateText(patrol.summary, 80, "暂无摘要")}`);
531
+ }
532
+ }
533
+ appendSearchResultLines(lines, searchResult);
534
+ return lines.join("\n");
535
+ }
536
+ function renderProjectPrompt(context, searchResult) {
537
+ const lines = [
538
+ "# 代码助手当前上下文",
539
+ "",
540
+ `- 作用域:项目 ${context.project.name}(${context.project.id})`,
541
+ `- 上下文版本:${context.version}`,
542
+ `- 生成时间:${context.generatedAt}`,
543
+ "- 使用规则:先回答这个项目的当前情况;如果用户追问更细事实,再要求宿主系统补查具体对象。",
544
+ "",
545
+ "## 项目摘要",
546
+ `- 生命周期:${context.project.lifecycleStatus}`,
547
+ `- 风险级别:${context.project.riskLevel}`,
548
+ `- 活跃会话:${context.project.activeSessionCount}/${context.project.sessionCount}`,
549
+ `- 主要风险:${joinItems(context.topRisks, "暂无明显风险")}`,
550
+ `- 建议下一步:${joinItems(context.nextActions, "暂无待办动作")}`
551
+ ];
552
+ if (context.inboxItems.length > 0) {
553
+ lines.push("", "## 当前代办");
554
+ for (const item of context.inboxItems.slice(0, MAX_PROMPT_ITEMS)) {
555
+ lines.push(`- ${describeInboxItem(item)}`);
556
+ }
557
+ }
558
+ if (context.sessions.length > 0) {
559
+ lines.push("", "## 项目会话");
560
+ for (const session of context.sessions.slice(0, MAX_PROMPT_ITEMS)) {
561
+ lines.push(`- ${session.title ?? session.sessionId}:状态=${session.status}/${session.progressState},风险=${joinItems(session.riskFlags, "暂无")},摘要=${truncateText(session.lastSummary, 80, "暂无摘要")}`);
562
+ }
563
+ }
564
+ if (context.memories.length > 0) {
565
+ lines.push("", "## 相关记忆");
566
+ for (const memory of context.memories.slice(0, MAX_PROMPT_ITEMS)) {
567
+ lines.push(`- ${memory.title}:类型=${memory.memoryType},状态=${memory.status},标签=${joinItems(memory.tags, "无")}`);
568
+ }
569
+ }
570
+ if (context.verifications.length > 0) {
571
+ lines.push("", "## 最近验证");
572
+ for (const verification of context.verifications.slice(0, MAX_PROMPT_ITEMS)) {
573
+ lines.push(`- ${verification.verificationType}:状态=${verification.status},摘要=${truncateText(verification.summary, 80, "暂无摘要")}`);
574
+ }
575
+ }
576
+ if (context.patrols.length > 0) {
577
+ lines.push("", "## 最近巡视");
578
+ for (const patrol of context.patrols.slice(0, MAX_PROMPT_ITEMS)) {
579
+ lines.push(`- ${patrol.id}:状态=${patrol.status},风险=${patrol.riskLevel ?? "unknown"},摘要=${truncateText(patrol.summary, 80, "暂无摘要")}`);
580
+ }
581
+ }
582
+ appendSearchResultLines(lines, searchResult);
583
+ return lines.join("\n");
584
+ }
585
+ function appendSearchResultLines(lines, searchResult) {
586
+ if (!searchResult || !searchResult.query || searchResult.items.length === 0) {
587
+ return;
588
+ }
589
+ lines.push("", `## 摘要命中(优先回答这些命中的摘要)`, `- 当前查询:${searchResult.query}`);
590
+ for (const item of searchResult.items.slice(0, MAX_PROMPT_ITEMS)) {
591
+ lines.push(`- ${describeSearchHit(item)}:${truncateText(item.summary, 120, "暂无摘要")}`);
592
+ }
593
+ }
594
+ function describeSearchHit(item) {
595
+ switch (item.kind) {
596
+ case "project":
597
+ return `项目 ${item.title}`;
598
+ case "session":
599
+ return `会话 ${item.title}`;
600
+ case "memory":
601
+ return `记忆 ${item.title}`;
602
+ case "patrol":
603
+ return `巡视 ${item.title}`;
604
+ case "verification":
605
+ return `验证 ${item.title}`;
606
+ default:
607
+ return item.title;
608
+ }
609
+ }
610
+ function describeInboxItem(item) {
611
+ return `${item.projectName} · ${item.title}:状态=${item.status},优先级=${item.priority},类型=${item.itemType},内容=${truncateText(item.content, 80, "暂无内容")}`;
612
+ }
613
+ function buildSearchHits(context, query) {
614
+ const hits = [];
615
+ const projectBaseTime = context.digest.lastActivityAt;
616
+ pushSearchHit(hits, {
617
+ kind: "project",
618
+ id: context.project.id,
619
+ sessionId: null,
620
+ projectId: context.project.id,
621
+ workspaceId: context.project.workspaceId,
622
+ title: context.project.name,
623
+ isArchived: context.project.lifecycleStatus === "archived",
624
+ summary: [
625
+ `风险=${context.digest.riskLevel}`,
626
+ `主要风险=${joinItems(context.digest.topRisks, "暂无")}`,
627
+ `下一步=${joinItems(context.digest.nextActions, "暂无")}`,
628
+ `最近会话摘要=${context.digest.latestSessionSummary ?? "暂无"}`
629
+ ].join(";"),
630
+ updatedAt: projectBaseTime
631
+ }, query, [
632
+ context.project.id,
633
+ context.project.name,
634
+ path.basename(context.project.repoRoot),
635
+ context.digest.latestSessionSummary ?? "",
636
+ context.digest.topRisks.join(" "),
637
+ context.digest.nextActions.join(" ")
638
+ ]);
639
+ for (const session of context.sessions) {
640
+ pushSearchHit(hits, {
641
+ kind: "session",
642
+ id: session.id,
643
+ sessionId: session.sessionId,
644
+ projectId: session.projectId,
645
+ workspaceId: context.project.workspaceId,
646
+ title: session.title ?? session.sessionId,
647
+ isArchived: session.isArchived,
648
+ summary: [
649
+ `状态=${session.status}/${session.progressState}`,
650
+ `风险=${joinItems(session.riskFlags, "暂无")}`,
651
+ `下一步=${joinItems(session.nextActions, "暂无")}`,
652
+ `摘要=${session.lastSummary ?? "暂无"}`
653
+ ].join(";"),
654
+ updatedAt: session.updatedAt
655
+ }, query, [
656
+ session.id,
657
+ session.sessionId,
658
+ session.title ?? "",
659
+ session.lastSummary ?? "",
660
+ session.riskFlags.join(" "),
661
+ session.nextActions.join(" ")
662
+ ]);
663
+ }
664
+ for (const memory of context.memories) {
665
+ pushSearchHit(hits, {
666
+ kind: "memory",
667
+ id: memory.id,
668
+ sessionId: null,
669
+ projectId: memory.projectId,
670
+ workspaceId: context.project.workspaceId,
671
+ title: memory.title,
672
+ isArchived: false,
673
+ summary: `类型=${memory.memoryType};状态=${memory.status};标签=${joinItems(memory.tags, "无")}`,
674
+ updatedAt: memory.updatedAt
675
+ }, query, [memory.title, memory.memoryType, memory.status, memory.tags.join(" ")]);
676
+ }
677
+ for (const patrol of context.patrols) {
678
+ pushSearchHit(hits, {
679
+ kind: "patrol",
680
+ id: patrol.id,
681
+ sessionId: null,
682
+ projectId: patrol.projectId,
683
+ workspaceId: context.project.workspaceId,
684
+ title: patrol.id,
685
+ isArchived: false,
686
+ summary: `状态=${patrol.status};风险=${patrol.riskLevel ?? "unknown"};摘要=${patrol.summary ?? "暂无"};建议=${joinItems(patrol.suggestions, "暂无")}`,
687
+ updatedAt: patrol.finishedAt ?? patrol.startedAt ?? patrol.createdAt
688
+ }, query, [patrol.id, patrol.summary ?? "", patrol.suggestions.join(" "), patrol.status, patrol.riskLevel ?? ""]);
689
+ }
690
+ for (const verification of context.verifications) {
691
+ pushSearchHit(hits, {
692
+ kind: "verification",
693
+ id: verification.id,
694
+ sessionId: null,
695
+ projectId: verification.projectId,
696
+ workspaceId: context.project.workspaceId,
697
+ title: verification.verificationType,
698
+ isArchived: false,
699
+ summary: `状态=${verification.status};目标=${verification.targetRef ?? "未指定"};摘要=${verification.summary ?? "暂无"}`,
700
+ updatedAt: verification.finishedAt ?? verification.startedAt ?? verification.createdAt
701
+ }, query, [
702
+ verification.id,
703
+ verification.verificationType,
704
+ verification.status,
705
+ verification.targetRef ?? "",
706
+ verification.summary ?? ""
707
+ ]);
708
+ }
709
+ return hits;
710
+ }
711
+ function pushSearchHit(hits, base, query, fields) {
712
+ const score = scoreSearch(fields, query);
713
+ if (score <= 0) {
714
+ return;
715
+ }
716
+ hits.push({
717
+ ...base,
718
+ score
719
+ });
720
+ }
721
+ function scoreSearch(fields, query) {
722
+ const normalizedFields = fields
723
+ .map((field) => normalizeSearchText(field))
724
+ .filter((field) => field.length > 0);
725
+ if (normalizedFields.length === 0) {
726
+ return 0;
727
+ }
728
+ const normalizedQuery = normalizeSearchText(query);
729
+ const terms = extractSearchTerms(normalizedQuery);
730
+ let score = 0;
731
+ for (const field of normalizedFields) {
732
+ if (field.includes(normalizedQuery)) {
733
+ score += 10;
734
+ }
735
+ for (const term of terms) {
736
+ if (field.includes(term)) {
737
+ score += term.length >= 4 ? 4 : 2;
738
+ }
739
+ }
740
+ }
741
+ return score;
742
+ }
743
+ function extractSearchTerms(query) {
744
+ const terms = new Set();
745
+ const asciiTerms = query.match(/[a-z0-9_-]{2,}/g) ?? [];
746
+ for (const term of asciiTerms) {
747
+ terms.add(term);
748
+ }
749
+ const hanTerms = query.match(/\p{Script=Han}+/gu) ?? [];
750
+ for (const term of hanTerms) {
751
+ terms.add(term);
752
+ if (term.length <= 4) {
753
+ continue;
754
+ }
755
+ for (let size = 2; size <= 4; size += 1) {
756
+ for (let index = 0; index <= term.length - size; index += 1) {
757
+ terms.add(term.slice(index, index + size));
758
+ }
759
+ }
760
+ }
761
+ return Array.from(terms).filter((term) => term.trim().length >= 2).slice(0, 16);
762
+ }
763
+ function normalizeSearchText(value) {
764
+ return value.trim().toLocaleLowerCase();
765
+ }
766
+ function compareSearchHits(left, right) {
767
+ if (left.score !== right.score) {
768
+ return right.score - left.score;
769
+ }
770
+ return compareIso(right.updatedAt, left.updatedAt);
771
+ }
772
+ function buildSnapshotVersion(payload) {
773
+ return `ctx_${hashContent(JSON.stringify(payload)).slice(0, 16)}`;
774
+ }
775
+ function uniqueItems(items, limit) {
776
+ const deduped = [];
777
+ const seen = new Set();
778
+ for (const item of items) {
779
+ const normalized = item.trim();
780
+ if (!normalized || seen.has(normalized)) {
781
+ continue;
782
+ }
783
+ seen.add(normalized);
784
+ deduped.push(normalized);
785
+ if (deduped.length >= limit) {
786
+ break;
787
+ }
788
+ }
789
+ return deduped;
790
+ }
791
+ function truncateText(value, maxLength, fallback) {
792
+ const normalized = value?.trim();
793
+ if (!normalized) {
794
+ return fallback;
795
+ }
796
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 3)}...` : normalized;
797
+ }
798
+ function joinItems(items, fallback) {
799
+ return items.length > 0 ? items.join(";") : fallback;
800
+ }
801
+ function compareIso(left, right) {
802
+ return left.localeCompare(right);
803
+ }
804
+ function compareIsoDesc(left, right) {
805
+ return compareIso(right, left);
806
+ }
807
+ //# sourceMappingURL=context-aggregator.js.map