autosnippet 3.3.0 → 3.3.3

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 (245) hide show
  1. package/dashboard/dist/assets/icons-BJ2mUBi8.js +1 -0
  2. package/dashboard/dist/assets/index-B659K9t5.js +128 -0
  3. package/dashboard/dist/assets/index-NCm40PMD.css +1 -0
  4. package/dashboard/dist/index.html +3 -3
  5. package/dist/bin/cli.d.ts +1 -0
  6. package/dist/bin/cli.js +284 -142
  7. package/dist/lib/agent/context/ExplorationTracker.d.ts +2 -0
  8. package/dist/lib/agent/context/ExplorationTracker.js +21 -3
  9. package/dist/lib/agent/core/ToolExecutionPipeline.d.ts +3 -1
  10. package/dist/lib/agent/core/ToolExecutionPipeline.js +8 -1
  11. package/dist/lib/agent/forge/DynamicComposer.d.ts +58 -0
  12. package/dist/lib/agent/forge/DynamicComposer.js +99 -0
  13. package/dist/lib/agent/forge/SandboxRunner.d.ts +60 -0
  14. package/dist/lib/agent/forge/SandboxRunner.js +251 -0
  15. package/dist/lib/agent/forge/TemporaryToolRegistry.d.ts +76 -0
  16. package/dist/lib/agent/forge/TemporaryToolRegistry.js +154 -0
  17. package/dist/lib/agent/forge/ToolForge.d.ts +92 -0
  18. package/dist/lib/agent/forge/ToolForge.js +239 -0
  19. package/dist/lib/agent/forge/ToolRequirementAnalyzer.d.ts +44 -0
  20. package/dist/lib/agent/forge/ToolRequirementAnalyzer.js +119 -0
  21. package/dist/lib/agent/tools/ToolRegistry.d.ts +2 -0
  22. package/dist/lib/agent/tools/ToolRegistry.js +4 -0
  23. package/dist/lib/agent/tools/composite.js +0 -1
  24. package/dist/lib/agent/tools/index.d.ts +2 -50
  25. package/dist/lib/agent/tools/index.js +2 -3
  26. package/dist/lib/agent/tools/lifecycle.d.ts +1 -58
  27. package/dist/lib/agent/tools/lifecycle.js +2 -75
  28. package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
  29. package/dist/lib/cli/KnowledgeSyncService.js +33 -1
  30. package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
  31. package/dist/lib/cli/deploy/FileManifest.js +0 -11
  32. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +10 -0
  33. package/dist/lib/domain/knowledge/KnowledgeEntry.js +2 -0
  34. package/dist/lib/domain/knowledge/Lifecycle.d.ts +19 -2
  35. package/dist/lib/domain/knowledge/Lifecycle.js +32 -6
  36. package/dist/lib/domain/knowledge/UnifiedValidator.d.ts +1 -5
  37. package/dist/lib/domain/knowledge/UnifiedValidator.js +7 -44
  38. package/dist/lib/domain/knowledge/values/Stats.d.ts +29 -0
  39. package/dist/lib/domain/knowledge/values/Stats.js +41 -0
  40. package/dist/lib/external/mcp/McpServer.d.ts +19 -38
  41. package/dist/lib/external/mcp/McpServer.js +145 -117
  42. package/dist/lib/external/mcp/autoApproveInjector.js +0 -2
  43. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +26 -1
  44. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +41 -0
  45. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -0
  46. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +3 -0
  47. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +27 -0
  48. package/dist/lib/external/mcp/handlers/bootstrap/skills.js +1 -1
  49. package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -0
  50. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  51. package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
  52. package/dist/lib/external/mcp/handlers/browse.js +2 -1
  53. package/dist/lib/external/mcp/handlers/consolidated.d.ts +117 -6
  54. package/dist/lib/external/mcp/handlers/consolidated.js +251 -71
  55. package/dist/lib/external/mcp/handlers/guard.d.ts +150 -0
  56. package/dist/lib/external/mcp/handlers/guard.js +239 -5
  57. package/dist/lib/external/mcp/handlers/knowledge.d.ts +0 -29
  58. package/dist/lib/external/mcp/handlers/knowledge.js +1 -76
  59. package/dist/lib/external/mcp/handlers/panorama.d.ts +36 -0
  60. package/dist/lib/external/mcp/handlers/panorama.js +156 -0
  61. package/dist/lib/external/mcp/handlers/system.d.ts +2 -54
  62. package/dist/lib/external/mcp/handlers/system.js +3 -113
  63. package/dist/lib/external/mcp/handlers/task.d.ts +13 -24
  64. package/dist/lib/external/mcp/handlers/task.js +218 -557
  65. package/dist/lib/external/mcp/handlers/types.d.ts +91 -8
  66. package/dist/lib/external/mcp/handlers/types.js +18 -1
  67. package/dist/lib/external/mcp/handlers/wiki-external.d.ts +18 -1
  68. package/dist/lib/external/mcp/handlers/wiki-external.js +16 -1
  69. package/dist/lib/external/mcp/tools.d.ts +18 -24
  70. package/dist/lib/external/mcp/tools.js +132 -159
  71. package/dist/lib/http/HttpServer.js +52 -0
  72. package/dist/lib/http/middleware/validate.js +7 -3
  73. package/dist/lib/http/routes/audit.d.ts +8 -0
  74. package/dist/lib/http/routes/audit.js +51 -0
  75. package/dist/lib/http/routes/guardReport.d.ts +10 -0
  76. package/dist/lib/http/routes/guardReport.js +143 -0
  77. package/dist/lib/http/routes/knowledge.js +32 -1
  78. package/dist/lib/http/routes/panorama.d.ts +11 -0
  79. package/dist/lib/http/routes/panorama.js +322 -0
  80. package/dist/lib/http/routes/signals.d.ts +10 -0
  81. package/dist/lib/http/routes/signals.js +104 -0
  82. package/dist/lib/http/routes/task.d.ts +2 -3
  83. package/dist/lib/http/routes/task.js +17 -347
  84. package/dist/lib/http/routes/violations.js +1 -1
  85. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +6 -1
  86. package/dist/lib/infrastructure/audit/AuditLogger.js +14 -1
  87. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +202 -504
  88. package/dist/lib/infrastructure/database/drizzle/schema.js +38 -69
  89. package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.d.ts +8 -0
  90. package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.js +43 -0
  91. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
  92. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
  93. package/dist/lib/infrastructure/logging/Logger.d.ts +2 -0
  94. package/dist/lib/infrastructure/logging/Logger.js +34 -7
  95. package/dist/lib/infrastructure/monitoring/ErrorTracker.js +3 -1
  96. package/dist/lib/infrastructure/monitoring/PerformanceMonitor.d.ts +2 -2
  97. package/dist/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -10
  98. package/dist/lib/infrastructure/notification/LarkNotifier.d.ts +24 -0
  99. package/dist/lib/infrastructure/notification/LarkNotifier.js +97 -0
  100. package/dist/lib/infrastructure/report/ReportStore.d.ts +45 -0
  101. package/dist/lib/infrastructure/report/ReportStore.js +133 -0
  102. package/dist/lib/infrastructure/signal/SignalAggregator.d.ts +18 -0
  103. package/dist/lib/infrastructure/signal/SignalAggregator.js +84 -0
  104. package/dist/lib/infrastructure/signal/SignalBridge.d.ts +13 -0
  105. package/dist/lib/infrastructure/signal/SignalBridge.js +20 -0
  106. package/dist/lib/infrastructure/signal/SignalBus.d.ts +63 -0
  107. package/dist/lib/infrastructure/signal/SignalBus.js +106 -0
  108. package/dist/lib/infrastructure/signal/SignalTraceWriter.d.ts +36 -0
  109. package/dist/lib/infrastructure/signal/SignalTraceWriter.js +130 -0
  110. package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
  111. package/dist/lib/injection/ServiceContainer.js +8 -0
  112. package/dist/lib/injection/ServiceMap.d.ts +16 -10
  113. package/dist/lib/injection/modules/AgentModule.d.ts +1 -1
  114. package/dist/lib/injection/modules/AgentModule.js +7 -1
  115. package/dist/lib/injection/modules/AppModule.d.ts +1 -1
  116. package/dist/lib/injection/modules/AppModule.js +4 -13
  117. package/dist/lib/injection/modules/GuardModule.js +27 -2
  118. package/dist/lib/injection/modules/InfraModule.d.ts +0 -1
  119. package/dist/lib/injection/modules/InfraModule.js +9 -7
  120. package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
  121. package/dist/lib/injection/modules/KnowledgeModule.js +131 -0
  122. package/dist/lib/injection/modules/PanoramaModule.d.ts +18 -0
  123. package/dist/lib/injection/modules/PanoramaModule.js +76 -0
  124. package/dist/lib/injection/modules/SignalModule.d.ts +10 -0
  125. package/dist/lib/injection/modules/SignalModule.js +84 -0
  126. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +1 -0
  127. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +6 -0
  128. package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +3 -1
  129. package/dist/lib/service/bootstrap/BootstrapTaskManager.js +20 -1
  130. package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
  131. package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
  132. package/dist/lib/service/delivery/AgentInstructionsGenerator.js +4 -5
  133. package/dist/lib/service/delivery/CursorDeliveryPipeline.d.ts +3 -1
  134. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +13 -10
  135. package/dist/lib/service/delivery/RulesGenerator.js +3 -2
  136. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +114 -0
  137. package/dist/lib/service/evolution/ConsolidationAdvisor.js +542 -0
  138. package/dist/lib/service/evolution/ContradictionDetector.d.ts +54 -0
  139. package/dist/lib/service/evolution/ContradictionDetector.js +253 -0
  140. package/dist/lib/service/evolution/DecayDetector.d.ts +71 -0
  141. package/dist/lib/service/evolution/DecayDetector.js +244 -0
  142. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +38 -0
  143. package/dist/lib/service/evolution/EnhancementSuggester.js +220 -0
  144. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +82 -0
  145. package/dist/lib/service/evolution/KnowledgeMetabolism.js +167 -0
  146. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +53 -0
  147. package/dist/lib/service/evolution/RedundancyAnalyzer.js +210 -0
  148. package/dist/lib/service/evolution/StagingManager.d.ts +57 -0
  149. package/dist/lib/service/evolution/StagingManager.js +201 -0
  150. package/dist/lib/service/guard/ComplianceReporter.d.ts +42 -2
  151. package/dist/lib/service/guard/ComplianceReporter.js +43 -5
  152. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +54 -0
  153. package/dist/lib/service/guard/CoverageAnalyzer.js +149 -0
  154. package/dist/lib/service/guard/GuardCheckEngine.d.ts +42 -0
  155. package/dist/lib/service/guard/GuardCheckEngine.js +465 -14
  156. package/dist/lib/service/guard/GuardFeedbackLoop.d.ts +3 -0
  157. package/dist/lib/service/guard/GuardFeedbackLoop.js +9 -0
  158. package/dist/lib/service/guard/ReverseGuard.d.ts +73 -0
  159. package/dist/lib/service/guard/ReverseGuard.js +256 -0
  160. package/dist/lib/service/guard/RuleLearner.d.ts +12 -0
  161. package/dist/lib/service/guard/RuleLearner.js +38 -0
  162. package/dist/lib/service/guard/UncertaintyCollector.d.ts +83 -0
  163. package/dist/lib/service/guard/UncertaintyCollector.js +149 -0
  164. package/dist/lib/service/guard/ViolationsStore.d.ts +1 -0
  165. package/dist/lib/service/guard/ViolationsStore.js +33 -3
  166. package/dist/lib/service/knowledge/ConfidenceRouter.d.ts +13 -0
  167. package/dist/lib/service/knowledge/ConfidenceRouter.js +14 -0
  168. package/dist/lib/service/knowledge/KnowledgeService.js +22 -4
  169. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
  170. package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
  171. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +27 -0
  172. package/dist/lib/service/panorama/CouplingAnalyzer.js +192 -0
  173. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +28 -0
  174. package/dist/lib/service/panorama/DimensionAnalyzer.js +320 -0
  175. package/dist/lib/service/panorama/LayerInferrer.d.ts +19 -0
  176. package/dist/lib/service/panorama/LayerInferrer.js +182 -0
  177. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +24 -0
  178. package/dist/lib/service/panorama/ModuleDiscoverer.js +185 -0
  179. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +29 -0
  180. package/dist/lib/service/panorama/PanoramaAggregator.js +228 -0
  181. package/dist/lib/service/panorama/PanoramaScanner.d.ts +52 -0
  182. package/dist/lib/service/panorama/PanoramaScanner.js +188 -0
  183. package/dist/lib/service/panorama/PanoramaService.d.ts +125 -0
  184. package/dist/lib/service/panorama/PanoramaService.js +363 -0
  185. package/dist/lib/service/panorama/PanoramaTypes.d.ts +134 -0
  186. package/dist/lib/service/panorama/PanoramaTypes.js +6 -0
  187. package/dist/lib/service/panorama/RoleRefiner.d.ts +48 -0
  188. package/dist/lib/service/panorama/RoleRefiner.js +535 -0
  189. package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
  190. package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
  191. package/dist/lib/service/search/CoarseRanker.js +11 -10
  192. package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
  193. package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
  194. package/dist/lib/service/search/MultiSignalRanker.d.ts +3 -2
  195. package/dist/lib/service/search/MultiSignalRanker.js +17 -1
  196. package/dist/lib/service/search/SearchEngine.d.ts +9 -7
  197. package/dist/lib/service/search/SearchEngine.js +67 -10
  198. package/dist/lib/service/search/SearchTypes.d.ts +25 -3
  199. package/dist/lib/service/search/SearchTypes.js +6 -1
  200. package/dist/lib/service/signal/HitRecorder.d.ts +68 -0
  201. package/dist/lib/service/signal/HitRecorder.js +173 -0
  202. package/dist/lib/service/skills/SignalCollector.d.ts +3 -1
  203. package/dist/lib/service/skills/SignalCollector.js +31 -1
  204. package/dist/lib/service/task/IntentExtractor.d.ts +66 -0
  205. package/dist/lib/service/task/IntentExtractor.js +256 -0
  206. package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
  207. package/dist/lib/service/task/PrimeSearchPipeline.js +113 -0
  208. package/dist/lib/service/vector/VectorService.d.ts +3 -0
  209. package/dist/lib/service/vector/VectorService.js +38 -4
  210. package/dist/lib/shared/schemas/mcp-tools.d.ts +41 -96
  211. package/dist/lib/shared/schemas/mcp-tools.js +59 -119
  212. package/dist/scripts/analyze-signals.d.ts +20 -0
  213. package/dist/scripts/analyze-signals.js +155 -0
  214. package/dist/scripts/diagnose-mcp.js +1 -1
  215. package/package.json +1 -1
  216. package/skills/autosnippet-create/SKILL.md +98 -89
  217. package/skills/autosnippet-devdocs/SKILL.md +55 -57
  218. package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
  219. package/templates/cursor-hooks/hooks/session-start.sh +1 -1
  220. package/templates/guard-ci.yml +2 -2
  221. package/templates/instructions/agent-static.md +2 -1
  222. package/templates/instructions/conventions.md +5 -6
  223. package/templates/recipes-setup/README.md +1 -2
  224. package/templates/recipes-setup/_template.md +39 -39
  225. package/dashboard/dist/assets/icons-BofcEZ3f.js +0 -1
  226. package/dashboard/dist/assets/index-D0whuycy.css +0 -1
  227. package/dashboard/dist/assets/index-SiN1GChm.js +0 -128
  228. package/dist/lib/domain/task/Task.d.ts +0 -140
  229. package/dist/lib/domain/task/Task.js +0 -254
  230. package/dist/lib/domain/task/TaskDependency.d.ts +0 -23
  231. package/dist/lib/domain/task/TaskDependency.js +0 -34
  232. package/dist/lib/domain/task/TaskIdGenerator.d.ts +0 -40
  233. package/dist/lib/domain/task/TaskIdGenerator.js +0 -75
  234. package/dist/lib/domain/task/index.d.ts +0 -4
  235. package/dist/lib/domain/task/index.js +0 -4
  236. package/dist/lib/infrastructure/database/migrations/002_add_tasks.d.ts +0 -11
  237. package/dist/lib/infrastructure/database/migrations/002_add_tasks.js +0 -86
  238. package/dist/lib/repository/task/TaskRepository.impl.d.ts +0 -171
  239. package/dist/lib/repository/task/TaskRepository.impl.js +0 -347
  240. package/dist/lib/service/task/TaskGraphService.d.ts +0 -222
  241. package/dist/lib/service/task/TaskGraphService.js +0 -597
  242. package/dist/lib/service/task/TaskKnowledgeBridge.d.ts +0 -95
  243. package/dist/lib/service/task/TaskKnowledgeBridge.js +0 -298
  244. package/dist/lib/service/task/TaskReadyEngine.d.ts +0 -84
  245. package/dist/lib/service/task/TaskReadyEngine.js +0 -115
@@ -1,151 +1,180 @@
1
1
  /**
2
- * MCP Handler — autosnippet_task (Unified Task & Decision Management)
2
+ * MCP Handler — autosnippet_task (Intent Lifecycle + Signal Collection)
3
3
  *
4
- * Operations:
5
- * Session: prime (session entry loads decisions + ready tasks + stats)
6
- * Tasks: create / ready / claim / close / fail / defer / progress
7
- * show / list / stats / blocked / decompose / dep_add / dep_tree
8
- * Decisions: record_decision / revise_decision / unpin_decision / list_decisions
4
+ * 5 Operations:
5
+ * prime Load knowledge context + initialize intent
6
+ * create Create in-memory task anchor (generates ID)
7
+ * close — Complete task + persist intent chain + trigger Guard
8
+ * fail — Abandon task + persist intent chain
9
+ * record_decision — Record user preference signal
10
+ *
11
+ * Architecture: Zero DB. Pure memory (IntentState) + SignalBus → JSONL signals.
9
12
  */
13
+ import { notifyTaskProgress } from '#infra/notification/LarkNotifier.js';
14
+ import { extract as extractIntent } from '#service/task/IntentExtractor.js';
10
15
  import { envelope } from '../envelope.js';
16
+ import { createIdleIntent } from './types.js';
17
+ // ─── In-memory task ID counter ───────────────────────────
18
+ let _taskCounter = 0;
19
+ function _generateTaskId() {
20
+ _taskCounter++;
21
+ return `asd-${Date.now().toString(36)}-${_taskCounter}`;
22
+ }
23
+ // ─── Task Rules Reminder ─────────────────────────────────
24
+ const _taskRules = {
25
+ reminder: [
26
+ '📋 TASK RULES (MANDATORY):',
27
+ '🔑 YOU are the task operator — user speaks naturally, you translate to task operations.',
28
+ '• MUST prime on EVERY message BEFORE anything else',
29
+ '• MUST create task for non-trivial work (≥2 files OR ≥10 lines)',
30
+ '• MUST close when done with meaningful reason',
31
+ '• When user agrees/disagrees → record_decision immediately',
32
+ '• NEVER tell user to run task commands',
33
+ ].join('\n'),
34
+ translationHint: [
35
+ 'User Says → You Run:',
36
+ '"fix bug"/"implement" → create→code→close',
37
+ '"continue" → resume in-progress→close',
38
+ '"pause"/"abandon" → fail(id, reason)',
39
+ '"agreed"/"disagree" → record_decision',
40
+ 'Quick question → No task. Just answer.',
41
+ ].join('\n'),
42
+ };
11
43
  /**
12
- * 统一入口
13
- * @param ctx { container }
14
- * @param args { operation, ...params }
44
+ * Unified entry point
15
45
  */
16
46
  export async function taskHandler(ctx, args) {
17
- const taskService = ctx.container.get('taskGraphService');
18
47
  let result;
19
48
  switch (args.operation) {
20
- // ── Session ──
21
49
  case 'prime':
22
- return _prime(taskService, args);
23
- // ── Task CRUD ──
50
+ return _prime(ctx, args);
24
51
  case 'create':
25
- result = await _create(taskService, args);
26
- break;
27
- case 'ready':
28
- return _ready(taskService, args);
29
- case 'claim':
30
- result = await _claim(taskService, args);
52
+ result = await _create(ctx, args);
31
53
  break;
32
54
  case 'close':
33
- result = await _close(ctx, taskService, args);
55
+ result = await _close(ctx, args);
34
56
  break;
35
57
  case 'fail':
36
- result = await _fail(taskService, args);
37
- break;
38
- case 'defer':
39
- result = await _defer(taskService, args);
58
+ result = await _fail(ctx, args);
40
59
  break;
41
- case 'progress':
42
- result = await _progress(taskService, args);
43
- break;
44
- case 'decompose':
45
- result = await _decompose(taskService, args);
46
- break;
47
- case 'show':
48
- return _show(taskService, args);
49
- case 'list':
50
- return _list(taskService, args);
51
- case 'blocked':
52
- return _blocked(taskService);
53
- case 'dep_add':
54
- return _depAdd(taskService, args);
55
- case 'dep_tree':
56
- return _depTree(taskService, args);
57
- case 'stats':
58
- return _stats(taskService);
59
- // ── Decisions ──
60
60
  case 'record_decision':
61
- result = await _recordDecision(taskService, args);
62
- break;
63
- case 'revise_decision':
64
- result = await _reviseDecision(taskService, args);
65
- break;
66
- case 'unpin_decision':
67
- result = await _unpinDecision(taskService, args);
61
+ result = await _recordDecision(ctx, args);
68
62
  break;
69
- case 'list_decisions':
70
- return _listDecisions(taskService);
71
63
  default:
72
64
  return envelope({
73
65
  success: false,
74
- message: `Unknown operation: ${args.operation}. Valid: prime, ready, create, claim, close, fail, defer, progress, decompose, dep_add, dep_tree, stats, list, record_decision, revise_decision, unpin_decision, list_decisions.`,
66
+ message: `Unknown operation: ${args.operation}. Valid: prime, create, close, fail, record_decision.`,
75
67
  meta: { tool: 'autosnippet_task' },
76
68
  });
77
69
  }
78
- // ── 飞书任务进度通知(异步非阻塞)──
79
- _notifyTaskProgress(args.operation, args, result).catch((err) => {
80
- process.stderr.write(`[MCP/Task] Notify error: ${err?.message}\n`);
70
+ // ── Lark notification (async, non-blocking) ──
71
+ notifyTaskProgress(args.operation, args, result).catch((err) => {
72
+ process.stderr.write(`[MCP/Task] Notify error: ${err instanceof Error ? err.message : String(err)}\n`);
81
73
  });
82
74
  return result;
83
75
  }
84
- // ── create ──
85
- async function _create(svc, args) {
86
- if (!args.title) {
87
- return envelope({
88
- success: false,
89
- message: 'title is required',
90
- meta: { tool: 'autosnippet_task' },
91
- });
76
+ // ═══ prime ═══════════════════════════════════════════════
77
+ async function _prime(ctx, args) {
78
+ const intent = ctx.session?.intent;
79
+ // If there is an active intent, persist it as abandoned before starting fresh
80
+ if (intent && intent.phase === 'active') {
81
+ _persistIntentChain(ctx, intent, 'abandoned', 'New prime received');
82
+ }
83
+ // ─── Intake: extract intent signals ───
84
+ const extracted = extractIntent(args.userQuery || '', args.activeFile, args.language);
85
+ // ─── Enrichment: multi-query search via PrimeSearchPipeline ───
86
+ const pipeline = _getPipeline(ctx.container);
87
+ let searchResult = null;
88
+ if (pipeline && extracted.queries[0]?.trim()) {
89
+ try {
90
+ searchResult = await pipeline.search(extracted);
91
+ }
92
+ catch {
93
+ // search failure is non-fatal
94
+ }
95
+ }
96
+ // ─── Lifecycle: initialize IntentState ───
97
+ const freshIntent = createIdleIntent();
98
+ freshIntent.phase = 'active';
99
+ freshIntent.primeQuery = args.userQuery || '';
100
+ freshIntent.primeActiveFile = args.activeFile;
101
+ freshIntent.primeLanguage = extracted.language;
102
+ freshIntent.primeModule = extracted.module;
103
+ freshIntent.primeScenario = extracted.scenario;
104
+ freshIntent.primeAt = Date.now();
105
+ if (searchResult) {
106
+ freshIntent.primeRecipeIds = [...searchResult.relatedKnowledge, ...searchResult.guardRules]
107
+ .map((r) => r.id)
108
+ .filter(Boolean);
109
+ freshIntent.searchMeta = {
110
+ queries: searchResult.searchMeta.queries,
111
+ resultCount: searchResult.searchMeta.resultCount,
112
+ filteredCount: searchResult.searchMeta.filteredCount,
113
+ };
114
+ }
115
+ // Bind intent to session
116
+ if (ctx.session) {
117
+ ctx.session.intent = freshIntent;
118
+ }
119
+ // ─── Delivery: build response ───
120
+ const relatedCount = searchResult?.relatedKnowledge.length ?? 0;
121
+ const ruleCount = searchResult?.guardRules.length ?? 0;
122
+ const lines = [];
123
+ if (relatedCount > 0 || ruleCount > 0) {
124
+ lines.push(`📋 Found ${relatedCount} recipe(s), ${ruleCount} guard rule(s).`);
125
+ for (const r of searchResult.relatedKnowledge) {
126
+ const hint = r.actionHint ? ` — ${r.actionHint}` : '';
127
+ const refs = r.sourceRefs?.length ? `\n 📍 ${r.sourceRefs.join(', ')}` : '';
128
+ lines.push(` • ${r.trigger || r.title}${hint}${refs}`);
129
+ }
130
+ for (const r of searchResult.guardRules) {
131
+ lines.push(` • [rule] ${r.trigger || r.title}`);
132
+ }
133
+ }
134
+ else {
135
+ lines.push('No matching recipes found.');
92
136
  }
93
- const { task, isDuplicate } = await svc.create({
94
- title: args.title,
95
- description: args.description || '',
96
- design: args.design || '',
97
- acceptance: args.acceptance || '',
98
- priority: args.priority ?? 2,
99
- taskType: args.taskType || 'task',
100
- parentId: args.parentId || null,
101
- });
102
- return envelope({
103
- success: true,
104
- data: task.toJSON(),
105
- message: isDuplicate
106
- ? `⚠ Duplicate detected: ${task.id} already exists`
107
- : `Created ${task.id}: ${task.title}`,
108
- meta: { tool: 'autosnippet_task' },
109
- });
110
- }
111
- // ── ready ──
112
- async function _ready(svc, args) {
113
- const tasks = await svc.ready({
114
- limit: args.limit || 10,
115
- withKnowledge: args.withKnowledge !== false,
116
- userQuery: args.userQuery,
117
- activeFile: args.activeFile,
118
- language: args.language,
119
- });
120
137
  return envelope({
121
138
  success: true,
122
- data: tasks.map((t) => (t.toJSON ? t.toJSON() : t)),
123
- message: `${tasks.length} task(s) ready`,
139
+ data: {
140
+ knowledge: searchResult
141
+ ? {
142
+ relatedKnowledge: searchResult.relatedKnowledge,
143
+ guardRules: searchResult.guardRules,
144
+ }
145
+ : null,
146
+ searchMeta: searchResult?.searchMeta ?? null,
147
+ _taskRules,
148
+ },
149
+ message: lines.join('\n'),
124
150
  meta: { tool: 'autosnippet_task' },
125
151
  });
126
152
  }
127
- // ── claim ──
128
- async function _claim(svc, args) {
129
- if (!args.id) {
153
+ // ═══ create ═════════════════════════════════════════════
154
+ async function _create(ctx, args) {
155
+ if (!args.title) {
130
156
  return envelope({
131
157
  success: false,
132
- message: 'id is required',
158
+ message: 'title is required',
133
159
  meta: { tool: 'autosnippet_task' },
134
160
  });
135
161
  }
136
- const knowledgeOptions = args.userQuery || args.activeFile || args.language
137
- ? { userQuery: args.userQuery, activeFile: args.activeFile, language: args.language }
138
- : undefined;
139
- const task = await svc.claim(args.id, 'agent', knowledgeOptions);
162
+ const taskId = _generateTaskId();
163
+ const intent = ctx.session?.intent;
164
+ // Bind task ID to current intent
165
+ if (intent && intent.phase === 'active') {
166
+ intent.taskId = taskId;
167
+ intent.taskTitle = args.title;
168
+ }
140
169
  return envelope({
141
170
  success: true,
142
- data: task.toJSON(),
143
- message: `Claimed ${args.id}`,
171
+ data: { id: taskId, title: args.title },
172
+ message: `📌 Created: ${taskId} — ${args.title}`,
144
173
  meta: { tool: 'autosnippet_task' },
145
174
  });
146
175
  }
147
- // ── close ──
148
- async function _close(ctx, svc, args) {
176
+ // ═══ close ══════════════════════════════════════════════
177
+ async function _close(ctx, args) {
149
178
  if (!args.id) {
150
179
  return envelope({
151
180
  success: false,
@@ -153,21 +182,23 @@ async function _close(ctx, svc, args) {
153
182
  meta: { tool: 'autosnippet_task' },
154
183
  });
155
184
  }
156
- const { task, newlyReady } = await svc.close(args.id, args.reason || 'Completed');
157
- const lines = [`✅ Closed: ${args.id} ${task.title}`];
158
- if (args.reason && args.reason !== 'Completed') {
159
- lines.push(`Reason: ${args.reason}`);
185
+ const intent = ctx.session?.intent;
186
+ const reason = args.reason || 'Completed';
187
+ // Persist intent chain via SignalBus
188
+ if (intent && intent.phase === 'active') {
189
+ _persistIntentChain(ctx, intent, 'completed', reason);
160
190
  }
161
- if (newlyReady.length > 0) {
162
- lines.push(`→ ${newlyReady.length} task(s) newly ready`);
191
+ // Reset intent to idle
192
+ if (ctx.session) {
193
+ ctx.session.intent = createIdleIntent();
163
194
  }
195
+ const lines = [`✅ Closed: ${args.id} — ${reason}`];
164
196
  lines.push('');
165
- lines.push('⚠️ REQUIRED: You MUST call autosnippet_guard (no args) NOW to review changed files for compliance violations. Do NOT proceed to the next task until guard review passes.');
197
+ lines.push('⚠️ REQUIRED: You MUST call autosnippet_guard (no args) NOW to review changed files for compliance violations.');
166
198
  return envelope({
167
199
  success: true,
168
200
  data: {
169
- closed: task.toJSON(),
170
- newlyReady,
201
+ closed: { id: args.id, reason, closedAt: Date.now() },
171
202
  nextAction: {
172
203
  tool: 'autosnippet_guard',
173
204
  args: {},
@@ -179,150 +210,8 @@ async function _close(ctx, svc, args) {
179
210
  meta: { tool: 'autosnippet_task' },
180
211
  });
181
212
  }
182
- // ── fail ──
183
- async function _fail(svc, args) {
184
- if (!args.id) {
185
- return envelope({
186
- success: false,
187
- message: 'id is required',
188
- meta: { tool: 'autosnippet_task' },
189
- });
190
- }
191
- const task = await svc.fail(args.id, args.reason || 'Agent execution failed');
192
- return envelope({
193
- success: true,
194
- data: task.toJSON(),
195
- message: `Failed ${args.id} (attempt #${task.failCount}): ${task.lastFailReason}`,
196
- meta: { tool: 'autosnippet_task' },
197
- });
198
- }
199
- // ── defer ──
200
- async function _defer(svc, args) {
201
- if (!args.id) {
202
- return envelope({
203
- success: false,
204
- message: 'id is required',
205
- meta: { tool: 'autosnippet_task' },
206
- });
207
- }
208
- const task = await svc.defer(args.id, args.reason || '');
209
- return envelope({
210
- success: true,
211
- data: task.toJSON(),
212
- message: `Deferred ${args.id}`,
213
- meta: { tool: 'autosnippet_task' },
214
- });
215
- }
216
- // ── progress ──
217
- async function _progress(svc, args) {
218
- if (!args.id) {
219
- return envelope({
220
- success: false,
221
- message: 'id is required',
222
- meta: { tool: 'autosnippet_task' },
223
- });
224
- }
225
- const note = args.reason || args.description || '';
226
- const task = await svc.progress(args.id, note);
227
- return envelope({
228
- success: true,
229
- data: task.toJSON(),
230
- message: `Progress updated for ${args.id}`,
231
- meta: { tool: 'autosnippet_task' },
232
- });
233
- }
234
- // ── decompose ──
235
- async function _decompose(svc, args) {
236
- if (!args.id) {
237
- return envelope({
238
- success: false,
239
- message: 'Epic id is required',
240
- meta: { tool: 'autosnippet_task' },
241
- });
242
- }
243
- if (!args.subtasks || !Array.isArray(args.subtasks) || args.subtasks.length === 0) {
244
- return envelope({
245
- success: false,
246
- message: 'subtasks array is required',
247
- meta: { tool: 'autosnippet_task' },
248
- });
249
- }
250
- const tasks = await svc.decompose(args.id, args.subtasks);
251
- return envelope({
252
- success: true,
253
- data: tasks.map((t) => (t.toJSON ? t.toJSON() : t)),
254
- message: `Decomposed ${args.id} into ${tasks.length} subtasks`,
255
- meta: { tool: 'autosnippet_task' },
256
- });
257
- }
258
- // ── show ──
259
- async function _show(svc, args) {
260
- if (!args.id) {
261
- return envelope({
262
- success: false,
263
- message: 'id is required',
264
- meta: { tool: 'autosnippet_task' },
265
- });
266
- }
267
- const task = await svc.show(args.id);
268
- if (!task) {
269
- return envelope({
270
- success: false,
271
- message: `Task not found: ${args.id}`,
272
- meta: { tool: 'autosnippet_task' },
273
- });
274
- }
275
- return envelope({
276
- success: true,
277
- data: task.toJSON(),
278
- meta: { tool: 'autosnippet_task' },
279
- });
280
- }
281
- // ── list ──
282
- async function _list(svc, args) {
283
- const filters = {};
284
- if (args.status) {
285
- filters.status = args.status;
286
- }
287
- if (args.taskType) {
288
- filters.taskType = args.taskType;
289
- }
290
- const tasks = await svc.list(filters, { limit: args.limit || 20 });
291
- return envelope({
292
- success: true,
293
- data: tasks.map((t) => t.toJSON()),
294
- message: `${tasks.length} task(s)`,
295
- meta: { tool: 'autosnippet_task' },
296
- });
297
- }
298
- // ── blocked ──
299
- async function _blocked(svc) {
300
- const tasks = await svc.blocked();
301
- return envelope({
302
- success: true,
303
- data: tasks,
304
- message: `${tasks.length} blocked task(s)`,
305
- meta: { tool: 'autosnippet_task' },
306
- });
307
- }
308
- // ── dep_add ──
309
- async function _depAdd(svc, args) {
310
- if (!args.id || !args.dependsOn) {
311
- return envelope({
312
- success: false,
313
- message: 'id and dependsOn are required',
314
- meta: { tool: 'autosnippet_task' },
315
- });
316
- }
317
- await svc.addDependency(args.id, args.dependsOn, args.depType || 'blocks');
318
- return envelope({
319
- success: true,
320
- message: `${args.id} ${args.depType || 'blocks'} ${args.dependsOn}`,
321
- meta: { tool: 'autosnippet_task' },
322
- });
323
- }
324
- // ── dep_tree ──
325
- async function _depTree(svc, args) {
213
+ // ═══ fail ═══════════════════════════════════════════════
214
+ async function _fail(ctx, args) {
326
215
  if (!args.id) {
327
216
  return envelope({
328
217
  success: false,
@@ -330,104 +219,27 @@ async function _depTree(svc, args) {
330
219
  meta: { tool: 'autosnippet_task' },
331
220
  });
332
221
  }
333
- const tree = await svc.depTree(args.id);
334
- return envelope({
335
- success: true,
336
- data: tree,
337
- message: `${tree.length} node(s) in dependency tree`,
338
- meta: { tool: 'autosnippet_task' },
339
- });
340
- }
341
- // ── stats ──
342
- async function _stats(svc) {
343
- const stats = await svc.stats();
344
- return envelope({
345
- success: true,
346
- data: stats,
347
- meta: { tool: 'autosnippet_task' },
348
- });
349
- }
350
- // ═══ Session (prime) ═══════════════════════════════════
351
- async function _prime(svc, args) {
352
- const result = await svc.prime({
353
- limit: args.limit || 10,
354
- withKnowledge: args.withKnowledge !== false,
355
- userQuery: args.userQuery,
356
- activeFile: args.activeFile,
357
- language: args.language,
358
- });
359
- const decisionCount = (result.decisions || []).length;
360
- const staleCount = (result.staleDecisions || []).length;
361
- const decisionTitles = (result.decisions || []).map((d) => d.title).join('; ');
362
- const statsLine = `${result.inProgress.length} in-progress, ${result.ready.length} ready, ${result.stats.total} total`;
363
- // ── Behavioral Rules Reminder (survives compaction) ──
364
- result._taskRules = {
365
- reminder: [
366
- '📋 TASK RULES (MANDATORY):',
367
- '🔑 YOU are the task operator — user speaks naturally, you translate to task operations. NEVER tell user to run task commands.',
368
- '• MUST prime on EVERY message BEFORE anything else',
369
- '• MUST create task for non-trivial work (≥2 files OR ≥10 lines)',
370
- '• MUST claim before coding, close when done with meaningful reason',
371
- '• MUST handle unfinished tasks before starting new work (ask user: Continue/Defer/Abandon)',
372
- '• NEVER skip prime, NEVER start new work with open in_progress tasks',
373
- '• NEVER leave tasks in in_progress when session ends — close or defer ALL',
374
- '• When in doubt → create a task. When idle → ready()',
375
- '• Session end → close all tasks, defer incomplete, verify zero in_progress',
376
- ].join('\n'),
377
- translationHint: [
378
- 'User Says → You Run:',
379
- '"fix bug"/"implement" → create→claim→code→close',
380
- '"continue" → resume in-progress→close',
381
- '"pause" → defer | "abandon" → fail | "break down" → decompose',
382
- '"what\'s next" → ready() | "agreed" → record_decision',
383
- 'Quick question → No task. Just answer.',
384
- ].join('\n'),
385
- };
386
- let message;
387
- if (decisionCount > 0) {
388
- const stalePart = staleCount > 0 ? ` ${staleCount} stale.` : '';
389
- message = `⚠️ ${decisionCount} ACTIVE DECISION(S): [${decisionTitles}].${stalePart} ${statsLine}.`;
222
+ const intent = ctx.session?.intent;
223
+ const reason = args.reason || 'Agent execution failed';
224
+ // Persist intent chain via SignalBus
225
+ if (intent && intent.phase === 'active') {
226
+ _persistIntentChain(ctx, intent, 'failed', reason);
390
227
  }
391
- else {
392
- message = `${statsLine}.`;
393
- }
394
- // ── Resume Prompt: 有 inProgress 任务时,提示 Agent 让用户选择 ──
395
- if (result.inProgress.length > 0) {
396
- const taskList = result.inProgress
397
- .map((t) => {
398
- const age = t.updatedAt
399
- ? `${Math.floor((Date.now() / 1000 - t.updatedAt) / 86400)}d ago`
400
- : '';
401
- return `• **${t.id}** — ${t.title}${age ? ` (${age})` : ''}`;
402
- })
403
- .join('\n');
404
- result._resumePrompt = {
405
- instruction: [
406
- 'There are unfinished tasks. You MUST present these options to the user BEFORE doing anything else:',
407
- '',
408
- '**Unfinished tasks:**',
409
- taskList,
410
- '',
411
- 'Ask the user to choose:',
412
- '1. **Continue** — resume the unfinished task(s)',
413
- '2. **Defer** — pause it and work on something else',
414
- '3. **Abandon** — close/fail it and start fresh',
415
- '',
416
- "Wait for the user's answer. Do NOT auto-resume.",
417
- ].join('\n'),
418
- taskIds: result.inProgress.map((t) => t.id),
419
- };
420
- message += ` ⏸️ ${result.inProgress.length} unfinished task(s) — ask user before resuming.`;
228
+ // Reset intent to idle
229
+ if (ctx.session) {
230
+ ctx.session.intent = createIdleIntent();
421
231
  }
422
232
  return envelope({
423
233
  success: true,
424
- data: result,
425
- message,
234
+ data: {
235
+ failed: { id: args.id, reason, failedAt: Date.now() },
236
+ },
237
+ message: `❌ Failed: ${args.id} — ${reason}`,
426
238
  meta: { tool: 'autosnippet_task' },
427
239
  });
428
240
  }
429
- // ═══ Decisions ═══════════════════════════════════════
430
- async function _recordDecision(svc, args) {
241
+ // ═══ record_decision ════════════════════════════════════
242
+ async function _recordDecision(ctx, args) {
431
243
  if (!args.title) {
432
244
  return envelope({
433
245
  success: false,
@@ -442,229 +254,78 @@ async function _recordDecision(svc, args) {
442
254
  meta: { tool: 'autosnippet_task' },
443
255
  });
444
256
  }
445
- const { task, isDuplicate } = await svc.recordDecision({
257
+ const decisionId = `dec-${Date.now().toString(36)}`;
258
+ const decision = {
259
+ id: decisionId,
446
260
  title: args.title,
447
261
  description: args.description,
448
- rationale: args.rationale || '',
449
- tags: args.tags || [],
450
- relatedTaskId: args.relatedTaskId || null,
451
- });
452
- return envelope({
453
- success: true,
454
- data: task.toJSON(),
455
- message: isDuplicate
456
- ? `⚠ Decision already recorded: ${task.id}`
457
- : `✅ Decision pinned: ${task.id} — "${args.title}"`,
458
- meta: { tool: 'autosnippet_task' },
459
- });
460
- }
461
- async function _reviseDecision(svc, args) {
462
- if (!args.id) {
463
- return envelope({
464
- success: false,
465
- message: 'id of old decision is required',
466
- meta: { tool: 'autosnippet_task' },
467
- });
468
- }
469
- if (!args.title) {
470
- return envelope({
471
- success: false,
472
- message: 'title of new decision is required',
473
- meta: { tool: 'autosnippet_task' },
474
- });
475
- }
476
- if (!args.description) {
477
- return envelope({
478
- success: false,
479
- message: 'description of new decision is required',
480
- meta: { tool: 'autosnippet_task' },
481
- });
482
- }
483
- const result = await svc.reviseDecision({
484
- oldDecisionId: args.id,
485
- title: args.title,
486
- description: args.description,
487
- rationale: args.rationale || '',
488
- reason: args.reason || '',
489
- });
490
- return envelope({
491
- success: true,
492
- data: {
493
- newDecision: result.newDecision.toJSON(),
494
- superseded: result.oldDecisionId,
495
- },
496
- message: `✅ Decision revised: ${result.oldDecisionId} → ${result.newDecision.id}`,
497
- meta: { tool: 'autosnippet_task' },
498
- });
499
- }
500
- async function _unpinDecision(svc, args) {
501
- if (!args.id) {
502
- return envelope({
503
- success: false,
504
- message: 'id is required',
505
- meta: { tool: 'autosnippet_task' },
506
- });
262
+ rationale: args.rationale,
263
+ tags: args.tags,
264
+ recordedAt: Date.now(),
265
+ };
266
+ // Push to current intent's decisions
267
+ const intent = ctx.session?.intent;
268
+ if (intent && intent.phase === 'active') {
269
+ intent.decisions.push(decision);
507
270
  }
508
- const task = await svc.unpinDecision(args.id, args.reason || '');
509
- return envelope({
510
- success: true,
511
- data: task.toJSON(),
512
- message: `Decision ${args.id} unpinned and closed`,
513
- meta: { tool: 'autosnippet_task' },
514
- });
515
- }
516
- async function _listDecisions(svc) {
517
- const decisions = await svc.list({ status: 'pinned', taskType: 'decision' }, { limit: 50 });
518
271
  return envelope({
519
272
  success: true,
520
- data: decisions.map((d) => d.toJSON()),
521
- message: `${decisions.length} active decision(s)`,
273
+ data: { decision: { id: decisionId, title: args.title } },
274
+ message: `📌 Decision recorded: ${args.title}`,
522
275
  meta: { tool: 'autosnippet_task' },
523
276
  });
524
277
  }
525
- // ═══ 飞书任务进度通知(通过 API Server 中转)═══════════════
526
- //
527
- // MCP Server 与 API Server 是独立进程。
528
- // 飞书 WSClient 连接在 API Server 中,因此通知需 HTTP 中转。
529
- // ═══════════════════════════════════════════════════════════
530
- const PRIORITY_LABELS = ['P0 紧急', 'P1 高', 'P2 中', 'P3 低', 'P4 微'];
531
- /** 通过 API Server 的 /api/v1/remote/notify 发送飞书通知 */
532
- async function _sendLarkViaApi(text) {
278
+ // ═══ Intent Chain Persistence (via SignalBus) ═══════════
279
+ function _persistIntentChain(ctx, intent, outcome, reason) {
280
+ const now = Date.now();
281
+ const chain = {
282
+ sessionId: ctx.session?.id || 'unknown',
283
+ taskId: intent.taskId,
284
+ outcome,
285
+ primeQuery: intent.primeQuery,
286
+ primeActiveFile: intent.primeActiveFile,
287
+ primeRecipeIds: intent.primeRecipeIds,
288
+ primeAt: intent.primeAt || now,
289
+ primeLanguage: intent.primeLanguage ?? null,
290
+ primeModule: intent.primeModule ?? null,
291
+ primeScenario: intent.primeScenario ?? 'search',
292
+ searchMeta: intent.searchMeta,
293
+ toolCalls: intent.toolCalls,
294
+ searchQueries: intent.searchQueries,
295
+ mentionedFiles: intent.mentionedFiles,
296
+ decisions: intent.decisions,
297
+ driftEvents: intent.driftEvents,
298
+ driftScore: _computeDriftScore(intent),
299
+ closeReason: outcome === 'completed' ? reason : undefined,
300
+ failReason: outcome !== 'completed' ? reason : undefined,
301
+ startedAt: intent.primeAt || now,
302
+ endedAt: now,
303
+ duration: now - (intent.primeAt || now),
304
+ };
305
+ // Emit via SignalBus — subscribers handle JSONL persistence
533
306
  try {
534
- const port = process.env.PORT || 3000;
535
- const resp = await fetch(`http://localhost:${port}/api/v1/remote/notify`, {
536
- method: 'POST',
537
- headers: { 'Content-Type': 'application/json' },
538
- body: JSON.stringify({ text }),
539
- signal: AbortSignal.timeout(5000),
307
+ const signalBus = ctx.container.get('signalBus');
308
+ signalBus.send('intent', 'TaskHandler', _computeDriftScore(intent), {
309
+ target: intent.taskId ?? null,
310
+ metadata: { chain },
540
311
  });
541
- if (!resp.ok) {
542
- process.stderr.write(`[MCP/Task] Lark notify HTTP ${resp.status}\n`);
543
- return false;
544
- }
545
- const body = (await resp.json());
546
- return body.success === true;
547
312
  }
548
- catch (err) {
549
- process.stderr.write(`[MCP/Task] Lark notify failed: ${err instanceof Error ? err.message : String(err)}\n`);
550
- return false;
313
+ catch {
314
+ // signalBus unavailable silent failure, non-blocking
551
315
  }
552
316
  }
553
- /**
554
- * 通过 API Server 截取 IDE 窗口截图并发送到飞书
555
- * @param [caption] 可选文字说明
556
- */
557
- async function _sendScreenshotViaApi(caption = '') {
558
- try {
559
- const port = process.env.PORT || 3000;
560
- const resp = await fetch(`http://localhost:${port}/api/v1/remote/screenshot`, {
561
- method: 'POST',
562
- headers: { 'Content-Type': 'application/json' },
563
- body: JSON.stringify({ caption }),
564
- signal: AbortSignal.timeout(15000),
565
- });
566
- if (!resp.ok) {
567
- process.stderr.write(`[MCP/Task] Screenshot HTTP ${resp.status}\n`);
568
- return false;
569
- }
570
- const body = (await resp.json());
571
- return body.success === true;
572
- }
573
- catch (err) {
574
- process.stderr.write(`[MCP/Task] Screenshot failed: ${err instanceof Error ? err.message : String(err)}\n`);
575
- return false;
317
+ function _computeDriftScore(intent) {
318
+ if (intent.driftEvents.length === 0) {
319
+ return 0;
576
320
  }
321
+ const sum = intent.driftEvents.reduce((acc, d) => acc + (1 - d.primeOverlap), 0);
322
+ return sum / intent.driftEvents.length;
577
323
  }
578
- /**
579
- * 根据任务操作向飞书发送进度通知(异步非阻塞)
580
- * result 是 envelope() 返回的 { success, data, message, meta }
581
- */
582
- async function _notifyTaskProgress(operation, args, result) {
583
- if (!result || result.success === false) {
584
- return;
585
- }
586
- const data = result.data;
587
- let text = '';
588
- switch (operation) {
589
- case 'create': {
590
- const title = data?.title || args.title || '';
591
- const id = data?.id || '';
592
- const type = data?.taskType || args.taskType || 'task';
593
- const pri = PRIORITY_LABELS[(data?.priority ?? args.priority ?? 2)] || 'P2';
594
- const dup = result.message?.includes('Duplicate') || result.message?.startsWith('⚠') ? ' (重复)' : '';
595
- text = `📋 新任务${dup}: ${id}\n${title}\n类型: ${type} | ${pri}`;
596
- break;
597
- }
598
- case 'claim': {
599
- const id = data?.id || args.id;
600
- const title = data?.title || '';
601
- text = `🔨 开始执行: ${id}\n${title}`;
602
- break;
603
- }
604
- case 'close': {
605
- const closed = (data?.closed || data);
606
- const title = closed?.title || '';
607
- const id = closed?.id || args.id;
608
- const reason = closed?.closeReason || args.reason || '';
609
- const readyCount = Array.isArray(data?.newlyReady) ? data.newlyReady.length : 0;
610
- const readyInfo = readyCount > 0 ? `\n→ ${readyCount} 个任务新就绪` : '';
611
- text = `✅ 完成: ${id}\n${title}\n原因: ${reason}${readyInfo}`;
612
- break;
613
- }
614
- case 'fail': {
615
- const title = data?.title || '';
616
- const id = data?.id || args.id;
617
- const reason = data?.lastFailReason || args.reason || '未知';
618
- const count = Number(data?.failCount || 0);
619
- text = `❌ 失败: ${id}\n${title}\n原因: ${reason}${count > 1 ? ` (第${count}次)` : ''}`;
620
- break;
621
- }
622
- case 'defer': {
623
- const id = data?.id || args.id;
624
- const title = data?.title || '';
625
- text = `⏸️ 暂缓: ${id} — ${title}`;
626
- break;
627
- }
628
- case 'progress': {
629
- const id = data?.id || args.id;
630
- const note = args.reason || args.description || '';
631
- text = note ? `📝 进度: ${id}\n${note.slice(0, 200)}` : `📝 进度: ${id}`;
632
- break;
633
- }
634
- case 'decompose': {
635
- const epicId = args.id;
636
- const count = Array.isArray(data) ? data.length : 0;
637
- const subTitles = Array.isArray(data)
638
- ? data
639
- .slice(0, 5)
640
- .map((t, i) => ` ${i + 1}. ${t.title || t.id}`)
641
- .join('\n')
642
- : '';
643
- text = `📂 拆解: ${epicId} → ${count} 个子任务${subTitles ? `\n${subTitles}` : ''}`;
644
- break;
645
- }
646
- case 'record_decision': {
647
- const title = data?.title || args.title || '';
648
- text = `📌 决策: ${title}`;
649
- break;
650
- }
651
- case 'revise_decision': {
652
- const oldId = data?.superseded || args.id;
653
- const newTitle = data?.newDecision?.title || args.title;
654
- text = `🔄 决策更新: ${oldId} → ${newTitle}`;
655
- break;
656
- }
657
- case 'unpin_decision': {
658
- const id = data?.id || args.id;
659
- text = `🔓 决策取消: ${id}`;
660
- break;
661
- }
662
- default:
663
- return;
324
+ function _getPipeline(container) {
325
+ try {
326
+ return container.get('primeSearchPipeline');
664
327
  }
665
- if (text) {
666
- await _sendLarkViaApi(text);
667
- // 发送文字通知后,附带 IDE 窗口截图
668
- await _sendScreenshotViaApi();
328
+ catch {
329
+ return null;
669
330
  }
670
331
  }